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 "";
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.
--- /dev/null
+// 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;
+}
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)
+}
- ``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.
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.
return InlineAsm::Constraint_o;
if (ConstraintCode == "X")
return InlineAsm::Constraint_X;
+ if (ConstraintCode == "p")
+ return InlineAsm::Constraint_p;
return InlineAsm::Constraint_Unknown;
}
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,
Constraint_Z,
Constraint_ZC,
Constraint_Zy,
- Constraints_Max = Constraint_Zy,
+
+ // Address constraints
+ Constraint_p,
+
+ Constraints_Max = Constraint_p,
Constraints_ShiftAmount = 16,
Flag_MatchingOperand = 0x80000000
return "ZC";
case InlineAsm::Constraint_Zy:
return "Zy";
+ case InlineAsm::Constraint_p:
+ return "p";
default:
llvm_unreachable("Unknown memory constraint");
}
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))
// 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++);
case TargetLowering::C_RegisterClass:
return 2;
case TargetLowering::C_Memory:
+ case TargetLowering::C_Address:
return 3;
}
llvm_unreachable("Invalid constraint type");
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;
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
// 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
}
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:
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");
break;
case TargetLowering::C_Memory:
break; // Already handled.
+ case TargetLowering::C_Address:
+ break; // Silence warning.
case TargetLowering::C_Unknown:
assert(false && "Unexpected unknown constraint");
}
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':
case TargetLowering::C_RegisterClass:
return 2;
case TargetLowering::C_Memory:
+ case TargetLowering::C_Address:
return 3;
}
llvm_unreachable("Invalid constraint type");
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"
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;
--- /dev/null
+; 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
+}
--- /dev/null
+; 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
+}