[InlineAsm] Add support for address operands ("p").
authorJonas Paulsson <paulsson@linux.vnet.ibm.com>
Tue, 22 Mar 2022 09:39:07 +0000 (10:39 +0100)
committerJonas Paulsson <paulsson@linux.vnet.ibm.com>
Wed, 13 Apr 2022 10:50:21 +0000 (12:50 +0200)
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

15 files changed:
clang/lib/Basic/Targets/SystemZ.h
clang/lib/Basic/Targets/X86.cpp
clang/test/CodeGen/SystemZ/systemz-inline-asm-03.c [new file with mode: 0644]
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
llvm/test/CodeGen/SystemZ/inline-asm-addr.ll [new file with mode: 0644]
llvm/test/CodeGen/X86/inline-asm-p-constraint.ll [new file with mode: 0644]

index 92cefeea5d264df2b6bae3086085caaa7c35bde1..f0306642a6656a1b8724ed3363c23ba17990081d 100644 (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 "";
index 1ec2bb9c249f026f8e8c4ffdcb33ad8236d20206..b83b3517ddf908a9ac08ee527a114453d7d4af86 100644 (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.
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 (file)
index 0000000..3157d0e
--- /dev/null
@@ -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;
+}
index 99bcccd33d82525cb125ec50edf8743cdf00707d..ec7e6d1556cb65c6ebace763eed7d0d3f828d705 100644 (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)
+}
index 1a218c0a5c756c6eb63778742c9458ec153fbcbd..98e90a65073ad59ffb580a8ff4d22960840b6061 100644 (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.
index 5a383244fef9807d503ee0347db505d0fc925962..dfc0af04b7590904b48de299a558290359790aff 100644 (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;
   }
 
index cf6b7af96980e7b7e68c0c0e64ffdc865a45c51a..343306de44abd34ce061679017a198a76ea266aa 100644 (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");
     }
index cbc14cd72843ec76c6aed3f97a3de51b40991703..4eeaf3d808ac4a7c97eda7d3a207f46fb6ea8c12 100644 (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++);
index 8d9e1a5c65acfd2778d7a7f2c92e15bdec9f6257..95ae8383b6faaffd862781a93331a1ac361516d2 100644 (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;
index 12827a84249cefccfdf15e87bb05f47fdefbea2d..ae5ffd863f8c290c914492a5c468ec18f08985ef 100644 (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");
       }
index 65a5eba422de3f6d7ed976862080ef6935051d62..bf26ad5a628d252627e142b53d90b4b6a3933ae5 100644 (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");
index cf55318d328df2e9c4bbcd8fdf7bea88c690f9f0..3de59b9f75e1c6781c80297ecfea85bce8f7a4db 100644 (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"
index a9fa64e488ee90b0458b53461a0a6eb3d6371268..c1303780613407fa24d601a6380abbd51436ede0 100644 (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;
diff --git a/llvm/test/CodeGen/SystemZ/inline-asm-addr.ll b/llvm/test/CodeGen/SystemZ/inline-asm-addr.ll
new file mode 100644 (file)
index 0000000..474e92b
--- /dev/null
@@ -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 (file)
index 0000000..55c8113
--- /dev/null
@@ -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
+}