Skip to content

Commit 91063cf

Browse files
[GlobalISel][InlineAsm] Add support for basic input operand constraints
Reviewers: arsenm, dsanders, aemerson, volkan, t.p.northover, paquette Reviewed By: arsenm Subscribers: gargaroff, wdng, rovka, hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D78319
1 parent 104e38c commit 91063cf

File tree

3 files changed

+219
-5
lines changed

3 files changed

+219
-5
lines changed

llvm/include/llvm/CodeGen/GlobalISel/InlineAsmLowering.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
namespace llvm {
2121
class CallBase;
2222
class MachineIRBuilder;
23+
class MachineOperand;
2324
class Register;
2425
class TargetLowering;
2526
class Value;
@@ -30,10 +31,23 @@ class InlineAsmLowering {
3031
virtual void anchor();
3132

3233
public:
34+
/// Lower the given inline asm call instruction
35+
/// \p GetOrCreateVRegs is a callback to materialize a register for the
36+
/// input and output operands of the inline asm
37+
/// \return True if the lowering succeeds, false otherwise.
3338
bool lowerInlineAsm(MachineIRBuilder &MIRBuilder, const CallBase &CB,
3439
std::function<ArrayRef<Register>(const Value &Val)>
3540
GetOrCreateVRegs) const;
3641

42+
/// Lower the specified operand into the Ops vector.
43+
/// \p Val is the IR input value to be lowered
44+
/// \p Constraint is the user supplied constraint string
45+
/// \p Ops is the vector to be filled with the lowered operands
46+
/// \return True if the lowering succeeds, false otherwise.
47+
virtual bool lowerAsmOperandForConstraint(Value *Val, StringRef Constraint,
48+
std::vector<MachineOperand> &Ops,
49+
MachineIRBuilder &MIRBuilder) const;
50+
3751
protected:
3852
/// Getter for generic TargetLowering class.
3953
const TargetLowering *getTLI() const { return TLI; }

llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ bool InlineAsmLowering::lowerInlineAsm(
252252
TLI->ParseConstraints(DL, TRI, Call);
253253

254254
ExtraFlags ExtraInfo(Call);
255+
unsigned ArgNo = 0; // ArgNo - The argument of the CallInst.
255256
unsigned ResNo = 0; // ResNo - The result number of the next output.
256257
for (auto &T : TargetConstraints) {
257258
ConstraintOperands.push_back(GISelAsmOperandInfo(T));
@@ -261,9 +262,32 @@ bool InlineAsmLowering::lowerInlineAsm(
261262
if (OpInfo.Type == InlineAsm::isInput ||
262263
(OpInfo.Type == InlineAsm::isOutput && OpInfo.isIndirect)) {
263264

264-
LLVM_DEBUG(dbgs() << "Input operands and indirect output operands are "
265-
"not supported yet\n");
266-
return false;
265+
OpInfo.CallOperandVal = const_cast<Value *>(Call.getArgOperand(ArgNo++));
266+
267+
if (const auto *BB = dyn_cast<BasicBlock>(OpInfo.CallOperandVal)) {
268+
LLVM_DEBUG(dbgs() << "Basic block input operands not supported yet\n");
269+
return false;
270+
}
271+
272+
Type *OpTy = OpInfo.CallOperandVal->getType();
273+
274+
// If this is an indirect operand, the operand is a pointer to the
275+
// accessed type.
276+
if (OpInfo.isIndirect) {
277+
PointerType *PtrTy = dyn_cast<PointerType>(OpTy);
278+
if (!PtrTy)
279+
report_fatal_error("Indirect operand for inline asm not a pointer!");
280+
OpTy = PtrTy->getElementType();
281+
}
282+
283+
// FIXME: Support aggregate input operands
284+
if (!OpTy->isSingleValueType()) {
285+
LLVM_DEBUG(
286+
dbgs() << "Aggregate input operands are not supported yet\n");
287+
return false;
288+
}
289+
290+
OpInfo.ConstraintVT = TLI->getValueType(DL, OpTy, true).getSimpleVT();
267291

268292
} else if (OpInfo.Type == InlineAsm::isOutput && !OpInfo.isIndirect) {
269293
assert(!Call.getType()->isVoidTy() && "Bad inline asm!");
@@ -363,8 +387,97 @@ bool InlineAsmLowering::lowerInlineAsm(
363387
}
364388

365389
break;
366-
case InlineAsm::isInput:
367-
return false;
390+
case InlineAsm::isInput: {
391+
if (OpInfo.isMatchingInputConstraint()) {
392+
LLVM_DEBUG(dbgs() << "Tied input operands not supported yet\n");
393+
return false;
394+
}
395+
396+
if (OpInfo.ConstraintType == TargetLowering::C_Other &&
397+
OpInfo.isIndirect) {
398+
LLVM_DEBUG(dbgs() << "Indirect input operands with unknown constraint "
399+
"not supported yet\n");
400+
return false;
401+
}
402+
403+
if (OpInfo.ConstraintType == TargetLowering::C_Immediate ||
404+
OpInfo.ConstraintType == TargetLowering::C_Other) {
405+
406+
std::vector<MachineOperand> Ops;
407+
if (!lowerAsmOperandForConstraint(OpInfo.CallOperandVal,
408+
OpInfo.ConstraintCode, Ops,
409+
MIRBuilder)) {
410+
LLVM_DEBUG(dbgs() << "Don't support constraint: "
411+
<< OpInfo.ConstraintCode << " yet\n");
412+
return false;
413+
}
414+
415+
assert(Ops.size() > 0 &&
416+
"Expected constraint to be lowered to at least one operand");
417+
418+
// Add information to the INLINEASM node to know about this input.
419+
unsigned OpFlags =
420+
InlineAsm::getFlagWord(InlineAsm::Kind_Imm, Ops.size());
421+
Inst.addImm(OpFlags);
422+
Inst.add(Ops);
423+
break;
424+
}
425+
426+
if (OpInfo.ConstraintType == TargetLowering::C_Memory) {
427+
assert(OpInfo.isIndirect && "Operand must be indirect to be a mem!");
428+
429+
unsigned ConstraintID =
430+
TLI->getInlineAsmMemConstraint(OpInfo.ConstraintCode);
431+
unsigned OpFlags = InlineAsm::getFlagWord(InlineAsm::Kind_Mem, 1);
432+
OpFlags = InlineAsm::getFlagWordForMem(OpFlags, ConstraintID);
433+
Inst.addImm(OpFlags);
434+
ArrayRef<Register> SourceRegs =
435+
GetOrCreateVRegs(*OpInfo.CallOperandVal);
436+
assert(
437+
SourceRegs.size() == 1 &&
438+
"Expected the memory input to fit into a single virtual register");
439+
Inst.addReg(SourceRegs[0]);
440+
break;
441+
}
442+
443+
assert((OpInfo.ConstraintType == TargetLowering::C_RegisterClass ||
444+
OpInfo.ConstraintType == TargetLowering::C_Register) &&
445+
"Unknown constraint type!");
446+
447+
if (OpInfo.isIndirect) {
448+
LLVM_DEBUG(dbgs() << "Can't handle indirect register inputs yet "
449+
"for constraint '"
450+
<< OpInfo.ConstraintCode << "'\n");
451+
return false;
452+
}
453+
454+
// Copy the input into the appropriate registers.
455+
if (OpInfo.Regs.empty()) {
456+
LLVM_DEBUG(
457+
dbgs()
458+
<< "Couldn't allocate input register for register constraint\n");
459+
return false;
460+
}
461+
462+
unsigned NumRegs = OpInfo.Regs.size();
463+
ArrayRef<Register> SourceRegs = GetOrCreateVRegs(*OpInfo.CallOperandVal);
464+
assert(NumRegs == SourceRegs.size() &&
465+
"Expected the number of input registers to match the number of "
466+
"source registers");
467+
468+
if (NumRegs > 1) {
469+
LLVM_DEBUG(dbgs() << "Input operands with multiple input registers are "
470+
"not supported yet\n");
471+
return false;
472+
}
473+
474+
unsigned Flag = InlineAsm::getFlagWord(InlineAsm::Kind_RegUse, NumRegs);
475+
Inst.addImm(Flag);
476+
MIRBuilder.buildCopy(OpInfo.Regs[0], SourceRegs[0]);
477+
Inst.addReg(OpInfo.Regs[0]);
478+
break;
479+
}
480+
368481
case InlineAsm::isClobber: {
369482

370483
unsigned NumRegs = OpInfo.Regs.size();
@@ -441,3 +554,27 @@ bool InlineAsmLowering::lowerInlineAsm(
441554

442555
return true;
443556
}
557+
558+
bool InlineAsmLowering::lowerAsmOperandForConstraint(
559+
Value *Val, StringRef Constraint, std::vector<MachineOperand> &Ops,
560+
MachineIRBuilder &MIRBuilder) const {
561+
if (Constraint.size() > 1)
562+
return false;
563+
564+
char ConstraintLetter = Constraint[0];
565+
switch (ConstraintLetter) {
566+
default:
567+
return false;
568+
case 'i': // Simple Integer or Relocatable Constant
569+
if (ConstantInt *CI = dyn_cast<ConstantInt>(Val)) {
570+
assert(CI->getBitWidth() <= 64 &&
571+
"expected immediate to fit into 64-bits");
572+
// Boolean constants should be zero-extended, others are sign-extended
573+
bool IsBool = CI->getBitWidth() == 1;
574+
int64_t ExtVal = IsBool ? CI->getZExtValue() : CI->getSExtValue();
575+
Ops.push_back(MachineOperand::CreateImm(ExtVal));
576+
return true;
577+
}
578+
return false;
579+
}
580+
}

llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,66 @@ define float @test_vector_output() nounwind {
132132
%2 = extractelement <2 x float> %1, i32 0
133133
ret float %2
134134
}
135+
136+
define void @test_input_register_imm() {
137+
; CHECK-LABEL: name: test_input_register_imm
138+
; CHECK: bb.1 (%ir-block.0):
139+
; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 42
140+
; CHECK: [[COPY:%[0-9]+]]:gpr64common = COPY [[C]](s64)
141+
; CHECK: INLINEASM &"mov x0, $0", 1 /* sideeffect attdialect */, 9 /* reguse */, [[COPY]]
142+
; CHECK: RET_ReallyLR
143+
call void asm sideeffect "mov x0, $0", "r"(i64 42)
144+
ret void
145+
}
146+
147+
; Make sure that boolean immediates are properly (zero) extended.
148+
define i32 @test_boolean_imm_ext() {
149+
; CHECK-LABEL: name: test_boolean_imm_ext
150+
; CHECK: bb.1.entry:
151+
; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
152+
; CHECK: INLINEASM &"#TEST 42 + ${0:c} - .\0A\09", 9 /* sideeffect mayload attdialect */, 13 /* imm */, 1
153+
; CHECK: $w0 = COPY [[C]](s32)
154+
; CHECK: RET_ReallyLR implicit $w0
155+
entry:
156+
tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i"(i1 true)
157+
ret i32 1
158+
}
159+
160+
define void @test_input_imm() {
161+
; CHECK-LABEL: name: test_input_imm
162+
; CHECK: bb.1 (%ir-block.0):
163+
; CHECK: INLINEASM &"mov x0, $0", 9 /* sideeffect mayload attdialect */, 13 /* imm */, 42
164+
; CHECK: RET_ReallyLR
165+
call void asm sideeffect "mov x0, $0", "i"(i64 42)
166+
ret void
167+
}
168+
169+
define zeroext i8 @test_input_register(i8* %src) nounwind {
170+
; CHECK-LABEL: name: test_input_register
171+
; CHECK: bb.1.entry:
172+
; CHECK: liveins: $x0
173+
; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0
174+
; CHECK: [[COPY1:%[0-9]+]]:gpr64common = COPY [[COPY]](p0)
175+
; CHECK: INLINEASM &"ldtrb ${0:w}, [$1]", 0 /* attdialect */, 655370 /* regdef:GPR32common */, def %1, 9 /* reguse */, [[COPY1]]
176+
; CHECK: [[COPY2:%[0-9]+]]:_(s32) = COPY %1
177+
; CHECK: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC [[COPY2]](s32)
178+
; CHECK: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[TRUNC]](s8)
179+
; CHECK: $w0 = COPY [[ZEXT]](s32)
180+
; CHECK: RET_ReallyLR implicit $w0
181+
entry:
182+
%0 = tail call i8 asm "ldtrb ${0:w}, [$1]", "=r,r"(i8* %src) nounwind
183+
ret i8 %0
184+
}
185+
186+
define i32 @test_memory_constraint(i32* %a) nounwind {
187+
; CHECK-LABEL: name: test_memory_constraint
188+
; CHECK: bb.1 (%ir-block.0):
189+
; CHECK: liveins: $x0
190+
; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0
191+
; CHECK: INLINEASM &"ldr $0, $1", 8 /* mayload attdialect */, 655370 /* regdef:GPR32common */, def %1, 196622 /* mem:m */, [[COPY]](p0)
192+
; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY %1
193+
; CHECK: $w0 = COPY [[COPY1]](s32)
194+
; CHECK: RET_ReallyLR implicit $w0
195+
%1 = tail call i32 asm "ldr $0, $1", "=r,*m"(i32* %a)
196+
ret i32 %1
197+
}

0 commit comments

Comments
 (0)