Author: Jonas Paulsson Date: 2022-04-13T12:50:21+02:00 New Revision: 46f83caebc8f2329bcf16b3edf6785dff480c5e3
URL: https://github.com/llvm/llvm-project/commit/46f83caebc8f2329bcf16b3edf6785dff480c5e3 DIFF: https://github.com/llvm/llvm-project/commit/46f83caebc8f2329bcf16b3edf6785dff480c5e3.diff LOG: [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 Added: clang/test/CodeGen/SystemZ/systemz-inline-asm-03.c llvm/test/CodeGen/SystemZ/inline-asm-addr.ll llvm/test/CodeGen/X86/inline-asm-p-constraint.ll Modified: clang/lib/Basic/Targets/SystemZ.h clang/lib/Basic/Targets/X86.cpp clang/test/CodeGen/asm.c llvm/docs/LangRef.rst llvm/include/llvm/CodeGen/TargetLowering.h llvm/include/llvm/IR/InlineAsm.h llvm/lib/CodeGen/CodeGenPrepare.cpp llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp llvm/lib/Target/X86/X86ISelDAGToDAG.cpp Removed: ################################################################################ diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h index 92cefeea5d264..f0306642a6656 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -82,6 +82,16 @@ class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo { 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 ""; diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp index 1ec2bb9c249f0..b83b3517ddf90 100644 --- a/clang/lib/Basic/Targets/X86.cpp +++ b/clang/lib/Basic/Targets/X86.cpp @@ -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. diff --git a/clang/test/CodeGen/SystemZ/systemz-inline-asm-03.c b/clang/test/CodeGen/SystemZ/systemz-inline-asm-03.c new file mode 100644 index 0000000000000..3157d0ef62416 --- /dev/null +++ b/clang/test/CodeGen/SystemZ/systemz-inline-asm-03.c @@ -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; +} diff --git a/clang/test/CodeGen/asm.c b/clang/test/CodeGen/asm.c index 99bcccd33d825..ec7e6d1556cb6 100644 --- a/clang/test/CodeGen/asm.c +++ b/clang/test/CodeGen/asm.c @@ -274,3 +274,13 @@ int t32(int cond) 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) +} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 1a218c0a5c756..98e90a65073ad 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -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. diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index 5a383244fef98..dfc0af04b7590 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -4336,6 +4336,7 @@ class TargetLowering : public TargetLoweringBase { 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 @@ class TargetLowering : public TargetLoweringBase { return InlineAsm::Constraint_o; if (ConstraintCode == "X") return InlineAsm::Constraint_X; + if (ConstraintCode == "p") + return InlineAsm::Constraint_p; return InlineAsm::Constraint_Unknown; } diff --git a/llvm/include/llvm/IR/InlineAsm.h b/llvm/include/llvm/IR/InlineAsm.h index cf6b7af96980e..343306de44abd 100644 --- a/llvm/include/llvm/IR/InlineAsm.h +++ b/llvm/include/llvm/IR/InlineAsm.h @@ -240,12 +240,15 @@ class InlineAsm final : public Value { 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 diff erence is that they are not used to actaully + // access memory by the instruction. Constraint_Unknown = 0, Constraint_es, Constraint_i, @@ -268,7 +271,11 @@ class InlineAsm final : public Value { 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 @@ class InlineAsm final : public Value { return "ZC"; case InlineAsm::Constraint_Zy: return "Zy"; + case InlineAsm::Constraint_p: + return "p"; default: llvm_unreachable("Unknown memory constraint"); } diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp index cbc14cd72843e..4eeaf3d808ac4 100644 --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -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++); diff --git a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp index 8d9e1a5c65acf..95ae8383b6faa 100644 --- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp @@ -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; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 12827a84249ce..ae5ffd863f8c2 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -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"); } diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 65a5eba422de3..bf26ad5a628d2 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -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"); diff --git a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp index cf55318d328df..3de59b9f75e1c 100644 --- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp @@ -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" diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp index a9fa64e488ee9..c130378061340 100644 --- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp +++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp @@ -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; diff --git a/llvm/test/CodeGen/SystemZ/inline-asm-addr.ll b/llvm/test/CodeGen/SystemZ/inline-asm-addr.ll new file mode 100644 index 0000000000000..474e92b52a033 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/inline-asm-addr.ll @@ -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 +} diff --git a/llvm/test/CodeGen/X86/inline-asm-p-constraint.ll b/llvm/test/CodeGen/X86/inline-asm-p-constraint.ll new file mode 100644 index 0000000000000..55c811332d89d --- /dev/null +++ b/llvm/test/CodeGen/X86/inline-asm-p-constraint.ll @@ -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 +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits