[llvm] 46f83ca - [InlineAsm] Add support for address operands ("p").

Jonas Paulsson via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 13 03:52:04 PDT 2022


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
+
+ at Addr = global i64 0, align 8
+ at A = global i64* null, align 8
+ at 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
+}


        


More information about the llvm-commits mailing list