[InlineAsm] Add support for address operands ("p").
This patch adds support for inline assembly address operands using the "p" constraint on X86 and SystemZ. This was in fact broken on X86 (see example at https://reviews.llvm.org/D110267, Nov 23). These operands should probably be treated the same as memory operands by CodeGenPrepare, which have been commented with "TODO" there. Review: Xiang Zhang and Ulrich Weigand Differential Revision: https://reviews.llvm.org/D122220
This commit is contained in:
parent
6d3224d93f
commit
46f83caebc
|
@ -82,6 +82,16 @@ public:
|
|||
bool validateAsmConstraint(const char *&Name,
|
||||
TargetInfo::ConstraintInfo &info) const override;
|
||||
|
||||
std::string convertConstraint(const char *&Constraint) const override {
|
||||
switch (Constraint[0]) {
|
||||
case 'p': // Keep 'p' constraint.
|
||||
return std::string("p");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TargetInfo::convertConstraint(Constraint);
|
||||
}
|
||||
|
||||
const char *getClobbers() const override {
|
||||
// FIXME: Is this really right?
|
||||
return "";
|
||||
|
|
|
@ -1490,8 +1490,8 @@ std::string X86TargetInfo::convertConstraint(const char *&Constraint) const {
|
|||
return std::string("{si}");
|
||||
case 'D':
|
||||
return std::string("{di}");
|
||||
case 'p': // address
|
||||
return std::string("im");
|
||||
case 'p': // Keep 'p' constraint (address).
|
||||
return std::string("p");
|
||||
case 't': // top of floating point stack.
|
||||
return std::string("{st}");
|
||||
case 'u': // second from top of floating point stack.
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// RUN: %clang_cc1 -no-opaque-pointers -triple s390x-linux-gnu -O2 -emit-llvm \
|
||||
// RUN: -o - %s 2>&1 | FileCheck %s
|
||||
// REQUIRES: systemz-registered-target
|
||||
|
||||
long *A;
|
||||
long Idx;
|
||||
unsigned long Addr;
|
||||
|
||||
unsigned long fun_BD12_p() {
|
||||
// CHECK-LABEL: define{{.*}} i64 @fun_BD12_p()
|
||||
// CHECK: call i64 asm "lay $0, $1", "=r,p"(i64* nonnull %arrayidx)
|
||||
asm("lay %0, %1" : "=r" (Addr) : "p" (&A[100]));
|
||||
return Addr;
|
||||
}
|
||||
|
||||
unsigned long fun_BDX12_p() {
|
||||
// CHECK-LABEL: define{{.*}} i64 @fun_BDX12_p()
|
||||
// CHECK: call i64 asm "lay $0, $1", "=r,p"(i64* %arrayidx)
|
||||
asm("lay %0, %1" : "=r" (Addr) : "p" (&A[Idx + 100]));
|
||||
return Addr;
|
||||
}
|
||||
|
||||
unsigned long fun_BD20_p() {
|
||||
// CHECK-LABEL: define{{.*}} i64 @fun_BD20_p()
|
||||
// CHECK: call i64 asm "lay $0, $1", "=r,p"(i64* nonnull %arrayidx)
|
||||
asm("lay %0, %1" : "=r" (Addr) : "p" (&A[1000]));
|
||||
return Addr;
|
||||
}
|
||||
|
||||
unsigned long fun_BDX20_p() {
|
||||
// CHECK-LABEL: define{{.*}} i64 @fun_BDX20_p()
|
||||
// CHECK: call i64 asm "lay $0, $1", "=r,p"(i64* %arrayidx)
|
||||
asm("lay %0, %1" : "=r" (Addr) : "p" (&A[Idx + 1000]));
|
||||
return Addr;
|
||||
}
|
|
@ -274,3 +274,13 @@ loop:
|
|||
label_true:
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *t33(void *ptr)
|
||||
{
|
||||
void *ret;
|
||||
asm ("lea %1, %0" : "=r" (ret) : "p" (ptr));
|
||||
return ret;
|
||||
|
||||
// CHECK: @t33
|
||||
// CHECK: %1 = call i8* asm "lea $1, $0", "=r,p,~{dirflag},~{fpsr},~{flags}"(i8* %0)
|
||||
}
|
||||
|
|
|
@ -4658,6 +4658,8 @@ Some constraint codes are typically supported by all targets:
|
|||
- ``m``: A memory address operand. It is target-specific what addressing modes
|
||||
are supported, typical examples are register, or register + register offset,
|
||||
or register + immediate offset (of some target-specific size).
|
||||
- ``p``: An address operand. Similar to ``m``, but used by "load address"
|
||||
type instructions without touching memory.
|
||||
- ``i``: An integer constant (of target-specific width). Allows either a simple
|
||||
immediate, or a relocatable value.
|
||||
- ``n``: An integer constant -- *not* including relocatable values.
|
||||
|
|
|
@ -4336,6 +4336,7 @@ public:
|
|||
C_Register, // Constraint represents specific register(s).
|
||||
C_RegisterClass, // Constraint represents any of register(s) in class.
|
||||
C_Memory, // Memory constraint.
|
||||
C_Address, // Address constraint.
|
||||
C_Immediate, // Requires an immediate.
|
||||
C_Other, // Something else.
|
||||
C_Unknown // Unsupported constraint.
|
||||
|
@ -4440,6 +4441,8 @@ public:
|
|||
return InlineAsm::Constraint_o;
|
||||
if (ConstraintCode == "X")
|
||||
return InlineAsm::Constraint_X;
|
||||
if (ConstraintCode == "p")
|
||||
return InlineAsm::Constraint_p;
|
||||
return InlineAsm::Constraint_Unknown;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,12 +240,15 @@ public:
|
|||
Kind_RegDefEarlyClobber = 3, // Early-clobber output register, "=&r".
|
||||
Kind_Clobber = 4, // Clobbered register, "~r".
|
||||
Kind_Imm = 5, // Immediate.
|
||||
Kind_Mem = 6, // Memory operand, "m".
|
||||
Kind_Mem = 6, // Memory operand, "m", or an address, "p".
|
||||
|
||||
// Memory constraint codes.
|
||||
// These could be tablegenerated but there's little need to do that since
|
||||
// there's plenty of space in the encoding to support the union of all
|
||||
// constraint codes for all targets.
|
||||
// Addresses are included here as they need to be treated the same by the
|
||||
// backend, the only difference is that they are not used to actaully
|
||||
// access memory by the instruction.
|
||||
Constraint_Unknown = 0,
|
||||
Constraint_es,
|
||||
Constraint_i,
|
||||
|
@ -268,7 +271,11 @@ public:
|
|||
Constraint_Z,
|
||||
Constraint_ZC,
|
||||
Constraint_Zy,
|
||||
Constraints_Max = Constraint_Zy,
|
||||
|
||||
// Address constraints
|
||||
Constraint_p,
|
||||
|
||||
Constraints_Max = Constraint_p,
|
||||
Constraints_ShiftAmount = 16,
|
||||
|
||||
Flag_MatchingOperand = 0x80000000
|
||||
|
@ -453,6 +460,8 @@ public:
|
|||
return "ZC";
|
||||
case InlineAsm::Constraint_Zy:
|
||||
return "Zy";
|
||||
case InlineAsm::Constraint_p:
|
||||
return "p";
|
||||
default:
|
||||
llvm_unreachable("Unknown memory constraint");
|
||||
}
|
||||
|
|
|
@ -4835,7 +4835,7 @@ static bool IsOperandAMemoryOperand(CallInst *CI, InlineAsm *IA, Value *OpVal,
|
|||
TLI.ComputeConstraintToUse(OpInfo, SDValue());
|
||||
|
||||
// If this asm operand is our Value*, and if it isn't an indirect memory
|
||||
// operand, we can't fold it!
|
||||
// operand, we can't fold it! TODO: Also handle C_Address?
|
||||
if (OpInfo.CallOperandVal == OpVal &&
|
||||
(OpInfo.ConstraintType != TargetLowering::C_Memory ||
|
||||
!OpInfo.isIndirect))
|
||||
|
@ -5618,6 +5618,7 @@ bool CodeGenPrepare::optimizeInlineAsmInst(CallInst *CS) {
|
|||
// Compute the constraint code and ConstraintType to use.
|
||||
TLI->ComputeConstraintToUse(OpInfo, SDValue());
|
||||
|
||||
// TODO: Also handle C_Address?
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Memory &&
|
||||
OpInfo.isIndirect) {
|
||||
Value *OpVal = CS->getArgOperand(ArgNo++);
|
||||
|
|
|
@ -145,6 +145,7 @@ static unsigned getConstraintGenerality(TargetLowering::ConstraintType CT) {
|
|||
case TargetLowering::C_RegisterClass:
|
||||
return 2;
|
||||
case TargetLowering::C_Memory:
|
||||
case TargetLowering::C_Address:
|
||||
return 3;
|
||||
}
|
||||
llvm_unreachable("Invalid constraint type");
|
||||
|
@ -644,6 +645,8 @@ bool InlineAsmLowering::lowerInlineAsm(
|
|||
return false;
|
||||
case TargetLowering::C_Memory:
|
||||
break; // Already handled.
|
||||
case TargetLowering::C_Address:
|
||||
break; // Silence warning.
|
||||
case TargetLowering::C_Unknown:
|
||||
LLVM_DEBUG(dbgs() << "Unexpected unknown constraint\n");
|
||||
return false;
|
||||
|
|
|
@ -8506,8 +8506,9 @@ getRegistersForValue(SelectionDAG &DAG, const SDLoc &DL,
|
|||
SmallVector<unsigned, 4> Regs;
|
||||
const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();
|
||||
|
||||
// No work to do for memory operations.
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Memory)
|
||||
// No work to do for memory/address operands.
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Memory ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Address)
|
||||
return None;
|
||||
|
||||
// If this is a constraint for a single physreg, or a constraint for a
|
||||
|
@ -8765,8 +8766,9 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
|
|||
// Compute the constraint code and ConstraintType to use.
|
||||
TLI.ComputeConstraintToUse(OpInfo, OpInfo.CallOperand, &DAG);
|
||||
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Memory &&
|
||||
OpInfo.Type == InlineAsm::isClobber)
|
||||
if ((OpInfo.ConstraintType == TargetLowering::C_Memory &&
|
||||
OpInfo.Type == InlineAsm::isClobber) ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Address)
|
||||
continue;
|
||||
|
||||
// If this is a memory input, and if the operand is not indirect, do what we
|
||||
|
@ -8841,6 +8843,10 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
|
|||
}
|
||||
return false;
|
||||
};
|
||||
assert((OpInfo.ConstraintType != TargetLowering::C_Address ||
|
||||
(OpInfo.Type == InlineAsm::isInput &&
|
||||
!OpInfo.isMatchingInputConstraint())) &&
|
||||
"Only address as input operand is allowed.");
|
||||
|
||||
switch (OpInfo.Type) {
|
||||
case InlineAsm::isOutput:
|
||||
|
@ -8973,8 +8979,11 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
|
|||
break;
|
||||
}
|
||||
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Memory) {
|
||||
assert(OpInfo.isIndirect && "Operand must be indirect to be a mem!");
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Memory ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Address) {
|
||||
assert((OpInfo.isIndirect ||
|
||||
OpInfo.ConstraintType != TargetLowering::C_Memory) &&
|
||||
"Operand must be indirect to be a mem!");
|
||||
assert(InOperandVal.getValueType() ==
|
||||
TLI.getPointerTy(DAG.getDataLayout()) &&
|
||||
"Memory operands expect pointer values");
|
||||
|
@ -9112,6 +9121,8 @@ void SelectionDAGBuilder::visitInlineAsm(const CallBase &Call,
|
|||
break;
|
||||
case TargetLowering::C_Memory:
|
||||
break; // Already handled.
|
||||
case TargetLowering::C_Address:
|
||||
break; // Silence warning.
|
||||
case TargetLowering::C_Unknown:
|
||||
assert(false && "Unexpected unknown constraint");
|
||||
}
|
||||
|
|
|
@ -4908,13 +4908,14 @@ TargetLowering::getConstraintType(StringRef Constraint) const {
|
|||
case 'o': // offsetable
|
||||
case 'V': // not offsetable
|
||||
return C_Memory;
|
||||
case 'p': // Address.
|
||||
return C_Address;
|
||||
case 'n': // Simple Integer
|
||||
case 'E': // Floating Point Constant
|
||||
case 'F': // Floating Point Constant
|
||||
return C_Immediate;
|
||||
case 'i': // Simple Integer or Relocatable Constant
|
||||
case 's': // Relocatable Constant
|
||||
case 'p': // Address.
|
||||
case 'X': // Allow ANY value.
|
||||
case 'I': // Target registers.
|
||||
case 'J':
|
||||
|
@ -5288,6 +5289,7 @@ static unsigned getConstraintGenerality(TargetLowering::ConstraintType CT) {
|
|||
case TargetLowering::C_RegisterClass:
|
||||
return 2;
|
||||
case TargetLowering::C_Memory:
|
||||
case TargetLowering::C_Address:
|
||||
return 3;
|
||||
}
|
||||
llvm_unreachable("Invalid constraint type");
|
||||
|
|
|
@ -1700,6 +1700,7 @@ SelectInlineAsmMemoryOperand(const SDValue &Op,
|
|||
case InlineAsm::Constraint_T:
|
||||
case InlineAsm::Constraint_m:
|
||||
case InlineAsm::Constraint_o:
|
||||
case InlineAsm::Constraint_p:
|
||||
// Accept an address with a long displacement and an index.
|
||||
// m works the same as T, as this is the most general case.
|
||||
// We don't really have any special handling of "offsettable"
|
||||
|
|
|
@ -6176,6 +6176,7 @@ SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
|
|||
case InlineAsm::Constraint_v: // not offsetable ??
|
||||
case InlineAsm::Constraint_m: // memory
|
||||
case InlineAsm::Constraint_X:
|
||||
case InlineAsm::Constraint_p: // address
|
||||
if (!selectAddr(nullptr, Op, Op0, Op1, Op2, Op3, Op4))
|
||||
return true;
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
; RUN: llc -mtriple=s390x-linux-gnu < %s | FileCheck %s
|
||||
|
||||
@Addr = global i64 0, align 8
|
||||
@A = global i64* null, align 8
|
||||
@Idx = global i64 0, align 8
|
||||
|
||||
define i64 @fun_BD12_p() {
|
||||
; CHECK-LABEL: fun_BD12_p:
|
||||
; CHECK: #APP
|
||||
; CHECK: lay %r2, 800(%r1)
|
||||
entry:
|
||||
%0 = load i64*, i64** @A
|
||||
%arrayidx = getelementptr inbounds i64, i64* %0, i64 100
|
||||
%1 = tail call i64 asm "lay $0, $1", "=r,p"(i64* nonnull %arrayidx)
|
||||
store i64 %1, i64* @Addr
|
||||
ret i64 %1
|
||||
}
|
||||
|
||||
define i64 @fun_BDX12_p() {
|
||||
; CHECK-LABEL: fun_BDX12_p:
|
||||
; CHECK: #APP
|
||||
; CHECK: lay %r2, 800(%r1,%r2)
|
||||
entry:
|
||||
%0 = load i64*, i64** @A
|
||||
%1 = load i64, i64* @Idx
|
||||
%add = add nsw i64 %1, 100
|
||||
%arrayidx = getelementptr inbounds i64, i64* %0, i64 %add
|
||||
%2 = tail call i64 asm "lay $0, $1", "=r,p"(i64* %arrayidx)
|
||||
store i64 %2, i64* @Addr
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @fun_BD20_p() {
|
||||
; CHECK-LABEL: fun_BD20_p:
|
||||
; CHECK: #APP
|
||||
; CHECK: lay %r2, 8000(%r1)
|
||||
entry:
|
||||
%0 = load i64*, i64** @A
|
||||
%arrayidx = getelementptr inbounds i64, i64* %0, i64 1000
|
||||
%1 = tail call i64 asm "lay $0, $1", "=r,p"(i64* nonnull %arrayidx)
|
||||
store i64 %1, i64* @Addr
|
||||
ret i64 %1
|
||||
}
|
||||
|
||||
define i64 @fun_BDX20_p() {
|
||||
; CHECK-LABEL: fun_BDX20_p:
|
||||
; CHECK: #APP
|
||||
; CHECK: lay %r2, 8000(%r1,%r2)
|
||||
entry:
|
||||
%0 = load i64*, i64** @A
|
||||
%1 = load i64, i64* @Idx
|
||||
%add = add nsw i64 %1, 1000
|
||||
%arrayidx = getelementptr inbounds i64, i64* %0, i64 %add
|
||||
%2 = tail call i64 asm "lay $0, $1", "=r,p"(i64* %arrayidx)
|
||||
store i64 %2, i64* @Addr
|
||||
ret i64 %2
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
; RUN: llc -mtriple=x86_64-unknown-unknown -no-integrated-as < %s 2>&1 | FileCheck %s
|
||||
|
||||
define i8* @foo(i8* %ptr) {
|
||||
; CHECK-LABEL: foo:
|
||||
%1 = tail call i8* asm "lea $1, $0", "=r,p,~{dirflag},~{fpsr},~{flags}"(i8* %ptr)
|
||||
; CHECK: #APP
|
||||
; CHECK-NEXT: lea (%rdi), %rax
|
||||
; CHECK-NEXT: #NO_APP
|
||||
ret i8* %1
|
||||
; CHECK-NEXT: retq
|
||||
}
|
Loading…
Reference in New Issue