[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:
Jonas Paulsson 2022-03-22 10:39:07 +01:00
parent 6d3224d93f
commit 46f83caebc
15 changed files with 168 additions and 12 deletions

View File

@ -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 "";

View File

@ -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.

View File

@ -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;
}

View File

@ -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)
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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++);

View File

@ -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;

View File

@ -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");
}

View File

@ -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");

View File

@ -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"

View File

@ -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;

View File

@ -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
}

View File

@ -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
}