diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -82,6 +82,16 @@ 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 --- a/clang/lib/Basic/Targets/X86.cpp +++ b/clang/lib/Basic/Targets/X86.cpp @@ -1490,8 +1490,8 @@ 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 --- /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 --- a/clang/test/CodeGen/asm.c +++ b/clang/test/CodeGen/asm.c @@ -274,3 +274,13 @@ 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 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -4658,6 +4658,8 @@ - ``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 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -4336,6 +4336,7 @@ 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 @@ 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 --- a/llvm/include/llvm/IR/InlineAsm.h +++ b/llvm/include/llvm/IR/InlineAsm.h @@ -240,12 +240,15 @@ 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 @@ 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 @@ 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 --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -4835,7 +4835,7 @@ 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 @@ // 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 --- a/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp @@ -145,6 +145,7 @@ 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 @@ 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 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -8506,8 +8506,9 @@ SmallVector 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 @@ // 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 @@ } 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 @@ 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 @@ 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 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -4908,13 +4908,14 @@ 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 @@ 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 --- a/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp @@ -1700,6 +1700,7 @@ 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 --- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp +++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp @@ -6176,6 +6176,7 @@ 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 --- /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 --- /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 +}