mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
Emit diagnostic if an inline asm constraint requires an immediate
Summary: An inline asm call can result in an immediate after inlining. Therefore emit a diagnostic here if constraint requires an immediate but one isn't supplied. Reviewers: joerg, mgorny, efriedma, rsmith Reviewed By: joerg Subscribers: asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, s.egerton, MaskRay, jyknight, dylanmckay, javed.absar, fedor.sergeev, jrtc27, Jim, krytarowski, eraman, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D60942 llvm-svn: 367750
This commit is contained in:
parent
91d943cb89
commit
fa94d17420
@ -3742,6 +3742,7 @@ public:
|
||||
C_Register, // Constraint represents specific register(s).
|
||||
C_RegisterClass, // Constraint represents any of register(s) in class.
|
||||
C_Memory, // Memory constraint.
|
||||
C_Immediate, // Requires an immediate.
|
||||
C_Other, // Something else.
|
||||
C_Unknown // Unsupported constraint.
|
||||
};
|
||||
|
@ -8049,6 +8049,14 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
|
||||
// Compute the constraint code and ConstraintType to use.
|
||||
TLI.ComputeConstraintToUse(T, SDValue());
|
||||
|
||||
if (T.ConstraintType == TargetLowering::C_Immediate &&
|
||||
OpInfo.CallOperand && !isa<ConstantSDNode>(OpInfo.CallOperand))
|
||||
// We've delayed emitting a diagnostic like the "n" constraint because
|
||||
// inlining could cause an integer showing up.
|
||||
return emitInlineAsmError(
|
||||
CS, "constraint '" + Twine(T.ConstraintCode) + "' expects an "
|
||||
"integer constant expression");
|
||||
|
||||
ExtraInfo.update(T);
|
||||
}
|
||||
|
||||
@ -8133,7 +8141,8 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
|
||||
switch (OpInfo.Type) {
|
||||
case InlineAsm::isOutput:
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Memory ||
|
||||
(OpInfo.ConstraintType == TargetLowering::C_Other &&
|
||||
((OpInfo.ConstraintType == TargetLowering::C_Immediate ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Other) &&
|
||||
OpInfo.isIndirect)) {
|
||||
unsigned ConstraintID =
|
||||
TLI.getInlineAsmMemConstraint(OpInfo.ConstraintCode);
|
||||
@ -8147,13 +8156,14 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
|
||||
MVT::i32));
|
||||
AsmNodeOperands.push_back(OpInfo.CallOperand);
|
||||
break;
|
||||
} else if ((OpInfo.ConstraintType == TargetLowering::C_Other &&
|
||||
} else if (((OpInfo.ConstraintType == TargetLowering::C_Immediate ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Other) &&
|
||||
!OpInfo.isIndirect) ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Register ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_RegisterClass) {
|
||||
// Otherwise, this outputs to a register (directly for C_Register /
|
||||
// C_RegisterClass, and a target-defined fashion for C_Other). Find a
|
||||
// register that we can use.
|
||||
// C_RegisterClass, and a target-defined fashion for
|
||||
// C_Immediate/C_Other). Find a register that we can use.
|
||||
if (OpInfo.AssignedRegs.Regs.empty()) {
|
||||
emitInlineAsmError(
|
||||
CS, "couldn't allocate output register for constraint '" +
|
||||
@ -8233,15 +8243,24 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
|
||||
}
|
||||
|
||||
// Treat indirect 'X' constraint as memory.
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Other &&
|
||||
if ((OpInfo.ConstraintType == TargetLowering::C_Immediate ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Other) &&
|
||||
OpInfo.isIndirect)
|
||||
OpInfo.ConstraintType = TargetLowering::C_Memory;
|
||||
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Other) {
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Immediate ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Other) {
|
||||
std::vector<SDValue> Ops;
|
||||
TLI.LowerAsmOperandForConstraint(InOperandVal, OpInfo.ConstraintCode,
|
||||
Ops, DAG);
|
||||
if (Ops.empty()) {
|
||||
if (OpInfo.ConstraintType == TargetLowering::C_Immediate)
|
||||
if (isa<ConstantSDNode>(InOperandVal)) {
|
||||
emitInlineAsmError(CS, "value out of range for constraint '" +
|
||||
Twine(OpInfo.ConstraintCode) + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
emitInlineAsmError(CS, "invalid operand for inline asm constraint '" +
|
||||
Twine(OpInfo.ConstraintCode) + "'");
|
||||
return;
|
||||
@ -8278,7 +8297,8 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
|
||||
}
|
||||
|
||||
assert((OpInfo.ConstraintType == TargetLowering::C_RegisterClass ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Register) &&
|
||||
OpInfo.ConstraintType == TargetLowering::C_Register ||
|
||||
OpInfo.ConstraintType == TargetLowering::C_Immediate) &&
|
||||
"Unknown constraint type!");
|
||||
|
||||
// TODO: Support this.
|
||||
@ -8384,6 +8404,7 @@ void SelectionDAGBuilder::visitInlineAsm(ImmutableCallSite CS) {
|
||||
Val = OpInfo.AssignedRegs.getCopyFromRegs(
|
||||
DAG, FuncInfo, getCurSDLoc(), Chain, &Flag, CS.getInstruction());
|
||||
break;
|
||||
case TargetLowering::C_Immediate:
|
||||
case TargetLowering::C_Other:
|
||||
Val = TLI.LowerAsmOutputForConstraint(Chain, Flag, getCurSDLoc(),
|
||||
OpInfo, DAG);
|
||||
|
@ -3873,15 +3873,17 @@ TargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
if (S == 1) {
|
||||
switch (Constraint[0]) {
|
||||
default: break;
|
||||
case 'r': return C_RegisterClass;
|
||||
case 'r':
|
||||
return C_RegisterClass;
|
||||
case 'm': // memory
|
||||
case 'o': // offsetable
|
||||
case 'V': // not offsetable
|
||||
return C_Memory;
|
||||
case 'i': // Simple Integer or Relocatable Constant
|
||||
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.
|
||||
@ -4256,6 +4258,7 @@ TargetLowering::ParseConstraints(const DataLayout &DL,
|
||||
/// Return an integer indicating how general CT is.
|
||||
static unsigned getConstraintGenerality(TargetLowering::ConstraintType CT) {
|
||||
switch (CT) {
|
||||
case TargetLowering::C_Immediate:
|
||||
case TargetLowering::C_Other:
|
||||
case TargetLowering::C_Unknown:
|
||||
return 0;
|
||||
@ -4375,11 +4378,12 @@ static void ChooseConstraint(TargetLowering::AsmOperandInfo &OpInfo,
|
||||
TargetLowering::ConstraintType CType =
|
||||
TLI.getConstraintType(OpInfo.Codes[i]);
|
||||
|
||||
// If this is an 'other' constraint, see if the operand is valid for it.
|
||||
// For example, on X86 we might have an 'rI' constraint. If the operand
|
||||
// is an integer in the range [0..31] we want to use I (saving a load
|
||||
// of a register), otherwise we must use 'r'.
|
||||
if (CType == TargetLowering::C_Other && Op.getNode()) {
|
||||
// If this is an 'other' or 'immediate' constraint, see if the operand is
|
||||
// valid for it. For example, on X86 we might have an 'rI' constraint. If
|
||||
// the operand is an integer in the range [0..31] we want to use I (saving a
|
||||
// load of a register), otherwise we must use 'r'.
|
||||
if ((CType == TargetLowering::C_Other ||
|
||||
CType == TargetLowering::C_Immediate) && Op.getNode()) {
|
||||
assert(OpInfo.Codes[i].size() == 1 &&
|
||||
"Unhandled multi-letter 'other' constraint");
|
||||
std::vector<SDValue> ResultOps;
|
||||
|
@ -5686,8 +5686,6 @@ AArch64TargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
switch (Constraint[0]) {
|
||||
default:
|
||||
break;
|
||||
case 'z':
|
||||
return C_Other;
|
||||
case 'x':
|
||||
case 'w':
|
||||
return C_RegisterClass;
|
||||
@ -5695,6 +5693,16 @@ AArch64TargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
// currently handle addresses it is the same as 'r'.
|
||||
case 'Q':
|
||||
return C_Memory;
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
return C_Immediate;
|
||||
case 'z':
|
||||
case 'S': // A symbolic address
|
||||
return C_Other;
|
||||
}
|
||||
|
@ -14976,7 +14976,8 @@ const char *ARMTargetLowering::LowerXConstraint(EVT ConstraintVT) const {
|
||||
/// constraint it is for this target.
|
||||
ARMTargetLowering::ConstraintType
|
||||
ARMTargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
if (Constraint.size() == 1) {
|
||||
unsigned S = Constraint.size();
|
||||
if (S == 1) {
|
||||
switch (Constraint[0]) {
|
||||
default: break;
|
||||
case 'l': return C_RegisterClass;
|
||||
@ -14984,12 +14985,12 @@ ARMTargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
case 'h': return C_RegisterClass;
|
||||
case 'x': return C_RegisterClass;
|
||||
case 't': return C_RegisterClass;
|
||||
case 'j': return C_Other; // Constant for movw.
|
||||
// An address with a single base register. Due to the way we
|
||||
// currently handle addresses it is the same as an 'r' memory constraint.
|
||||
case 'j': return C_Immediate; // Constant for movw.
|
||||
// An address with a single base register. Due to the way we
|
||||
// currently handle addresses it is the same as an 'r' memory constraint.
|
||||
case 'Q': return C_Memory;
|
||||
}
|
||||
} else if (Constraint.size() == 2) {
|
||||
} else if (S == 2) {
|
||||
switch (Constraint[0]) {
|
||||
default: break;
|
||||
case 'T': return C_RegisterClass;
|
||||
|
@ -1689,6 +1689,8 @@ AVRTargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
if (Constraint.size() == 1) {
|
||||
// See http://www.nongnu.org/avr-libc/user-manual/inline_asm.html
|
||||
switch (Constraint[0]) {
|
||||
default:
|
||||
break;
|
||||
case 'a': // Simple upper registers
|
||||
case 'b': // Base pointer registers pairs
|
||||
case 'd': // Upper register
|
||||
@ -1715,9 +1717,7 @@ AVRTargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
case 'O': // Integer constant (Range: 8, 16, 24)
|
||||
case 'P': // Integer constant (Range: 1)
|
||||
case 'R': // Integer constant (Range: -6 to 5)x
|
||||
return C_Other;
|
||||
default:
|
||||
break;
|
||||
return C_Immediate;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2407,6 +2407,10 @@ RISCVTargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
break;
|
||||
case 'f':
|
||||
return C_RegisterClass;
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
return C_Immediate;
|
||||
}
|
||||
}
|
||||
return TargetLowering::getConstraintType(Constraint);
|
||||
|
@ -3183,7 +3183,7 @@ SparcTargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
case 'e':
|
||||
return C_RegisterClass;
|
||||
case 'I': // SIMM13
|
||||
return C_Other;
|
||||
return C_Immediate;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -956,7 +956,7 @@ SystemZTargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
case 'K': // Signed 16-bit constant
|
||||
case 'L': // Signed 20-bit displacement (on all targets we support)
|
||||
case 'M': // 0x7fffffff
|
||||
return C_Other;
|
||||
return C_Immediate;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -44875,10 +44875,11 @@ X86TargetLowering::getConstraintType(StringRef Constraint) const {
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'G':
|
||||
case 'L':
|
||||
case 'M':
|
||||
return C_Immediate;
|
||||
case 'C':
|
||||
case 'e':
|
||||
case 'Z':
|
||||
|
@ -2,7 +2,7 @@
|
||||
; RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
|
||||
|
||||
; Check for at least one invalid constant.
|
||||
; CHECK-ERRORS: error: invalid operand for inline asm constraint 'I'
|
||||
; CHECK-ERRORS: error: value out of range for constraint 'I'
|
||||
|
||||
define i32 @constraint_I(i32 %i, i32 %j) nounwind ssp {
|
||||
entry:
|
||||
|
@ -2,7 +2,7 @@
|
||||
; RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
|
||||
|
||||
; Check for at least one invalid constant.
|
||||
; CHECK-ERRORS: error: invalid operand for inline asm constraint 'J'
|
||||
; CHECK-ERRORS: error: value out of range for constraint 'J'
|
||||
|
||||
define i32 @constraint_J(i32 %i, i32 %j) nounwind ssp {
|
||||
entry:
|
||||
|
@ -2,7 +2,7 @@
|
||||
; RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
|
||||
|
||||
; Check for at least one invalid constant.
|
||||
; CHECK-ERRORS: error: invalid operand for inline asm constraint 'K'
|
||||
; CHECK-ERRORS: error: value out of range for constraint 'K'
|
||||
|
||||
define i32 @constraint_K(i32 %i, i32 %j) nounwind {
|
||||
entry:
|
||||
|
@ -2,7 +2,7 @@
|
||||
; RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
|
||||
|
||||
; Check for at least one invalid constant.
|
||||
; CHECK-ERRORS: error: invalid operand for inline asm constraint 'L'
|
||||
; CHECK-ERRORS: error: value out of range for constraint 'L'
|
||||
|
||||
define i32 @constraint_L(i32 %i, i32 %j) nounwind {
|
||||
entry:
|
||||
|
@ -2,7 +2,7 @@
|
||||
; RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
|
||||
|
||||
; Check for at least one invalid constant.
|
||||
; CHECK-ERRORS: error: invalid operand for inline asm constraint 'M'
|
||||
; CHECK-ERRORS: error: value out of range for constraint 'M'
|
||||
|
||||
define i32 @constraint_M(i32 %i, i32 %j) nounwind {
|
||||
entry:
|
||||
|
@ -2,7 +2,7 @@
|
||||
; RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
|
||||
|
||||
; Check for at least one invalid constant.
|
||||
; CHECK-ERRORS: error: invalid operand for inline asm constraint 'N'
|
||||
; CHECK-ERRORS: error: value out of range for constraint 'N'
|
||||
|
||||
define i32 @constraint_N(i32 %i, i32 %j) nounwind {
|
||||
entry:
|
||||
|
@ -2,23 +2,23 @@
|
||||
; RUN: not llc -mtriple=riscv64 < %s 2>&1 | FileCheck %s
|
||||
|
||||
define void @constraint_I() {
|
||||
; CHECK: error: invalid operand for inline asm constraint 'I'
|
||||
; CHECK: error: value out of range for constraint 'I'
|
||||
tail call void asm sideeffect "addi a0, a0, $0", "I"(i32 2048)
|
||||
; CHECK: error: invalid operand for inline asm constraint 'I'
|
||||
; CHECK: error: value out of range for constraint 'I'
|
||||
tail call void asm sideeffect "addi a0, a0, $0", "I"(i32 -2049)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @constraint_J() {
|
||||
; CHECK: error: invalid operand for inline asm constraint 'J'
|
||||
; CHECK: error: value out of range for constraint 'J'
|
||||
tail call void asm sideeffect "addi a0, a0, $0", "J"(i32 1)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @constraint_K() {
|
||||
; CHECK: error: invalid operand for inline asm constraint 'K'
|
||||
; CHECK: error: value out of range for constraint 'K'
|
||||
tail call void asm sideeffect "csrwi mstatus, $0", "K"(i32 32)
|
||||
; CHECK: error: invalid operand for inline asm constraint 'K'
|
||||
; CHECK: error: value out of range for constraint 'K'
|
||||
tail call void asm sideeffect "csrwi mstatus, $0", "K"(i32 -1)
|
||||
ret void
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
@x = global i32 0, align 4
|
||||
|
||||
;CHECK: error: invalid operand for inline asm constraint 'n'
|
||||
; CHECK: error: constraint 'n' expects an integer constant expression
|
||||
define void @foo() {
|
||||
%a = getelementptr i32, i32* @x, i32 1
|
||||
call void asm sideeffect "foo $0", "n"(i32* %a) nounwind
|
||||
|
17
test/CodeGen/X86/inline-asm-e-constraint.ll
Normal file
17
test/CodeGen/X86/inline-asm-e-constraint.ll
Normal file
@ -0,0 +1,17 @@
|
||||
; RUN: not llc -mtriple=x86_64-unknown-unknown -no-integrated-as < %s 2>&1 | FileCheck %s
|
||||
|
||||
%struct.s = type { i32, i32 }
|
||||
|
||||
@pr40890.s = internal global %struct.s zeroinitializer, align 4
|
||||
|
||||
; CHECK: error: invalid operand for inline asm constraint 'e'
|
||||
; CHECK: error: invalid operand for inline asm constraint 'e'
|
||||
|
||||
define void @pr40890() {
|
||||
entry:
|
||||
; This pointer cannot be used as an integer constant expression.
|
||||
tail call void asm sideeffect "\0A#define GLOBAL_A abcd$0\0A", "e,~{dirflag},~{fpsr},~{flags}"(i32* getelementptr inbounds (%struct.s, %struct.s* @pr40890.s, i64 0, i32 0))
|
||||
; Floating-point is also not okay.
|
||||
tail call void asm sideeffect "\0A#define PI abcd$0\0A", "e,~{dirflag},~{fpsr},~{flags}"(float 0x40091EB860000000)
|
||||
ret void
|
||||
}
|
7
test/CodeGen/X86/inline-asm-imm-out-of-range.ll
Normal file
7
test/CodeGen/X86/inline-asm-imm-out-of-range.ll
Normal file
@ -0,0 +1,7 @@
|
||||
; RUN: not llc -mtriple=i686-- -no-integrated-as < %s 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK: error: value out of range for constraint 'I'
|
||||
define void @foo() {
|
||||
call void asm sideeffect "foo $0", "I"(i32 42)
|
||||
ret void
|
||||
}
|
13
test/CodeGen/X86/inline-asm-n-constraint.ll
Normal file
13
test/CodeGen/X86/inline-asm-n-constraint.ll
Normal file
@ -0,0 +1,13 @@
|
||||
; RUN: llc -mtriple=x86_64-unknown-unknown -no-integrated-as < %s 2>&1 | FileCheck %s
|
||||
|
||||
@x = global i32 0, align 4
|
||||
|
||||
define void @foo() {
|
||||
; CHECK-LABEL: foo:
|
||||
call void asm sideeffect "foo $0", "n"(i32 42) nounwind
|
||||
; CHECK: #APP
|
||||
; CHECK-NEXT: foo $42
|
||||
; CHECK-NEXT: #NO_APP
|
||||
ret void
|
||||
; CHECK-NEXT: retq
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user