diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InlineAsmLowering.h b/llvm/include/llvm/CodeGen/GlobalISel/InlineAsmLowering.h --- a/llvm/include/llvm/CodeGen/GlobalISel/InlineAsmLowering.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InlineAsmLowering.h @@ -20,6 +20,7 @@ namespace llvm { class CallBase; class MachineIRBuilder; +class MachineOperand; class Register; class TargetLowering; class Value; @@ -30,10 +31,23 @@ virtual void anchor(); public: + /// Lower the given inline asm call instruction + /// \p GetOrCreateVRegs is a callback to materialize a register for the + /// input and output operands of the inline asm + /// \return True if the lowering succeeds, false otherwise. bool lowerInlineAsm(MachineIRBuilder &MIRBuilder, const CallBase &CB, std::function(const Value &Val)> GetOrCreateVRegs) const; + /// Lower the specified operand into the Ops vector. + /// \p Val is the IR input value to be lowered + /// \p Constraint is the user supplied constraint string + /// \p Ops is the vector to be filled with the lowered operands + /// \return True if the lowering succeeds, false otherwise. + virtual bool lowerAsmOperandForConstraint(Value *Val, StringRef Constraint, + std::vector &Ops, + MachineIRBuilder &MIRBuilder) const; + protected: /// Getter for generic TargetLowering class. const TargetLowering *getTLI() const { return TLI; } 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 @@ -252,6 +252,7 @@ TLI->ParseConstraints(DL, TRI, Call); ExtraFlags ExtraInfo(Call); + unsigned ArgNo = 0; // ArgNo - The argument of the CallInst. unsigned ResNo = 0; // ResNo - The result number of the next output. for (auto &T : TargetConstraints) { ConstraintOperands.push_back(GISelAsmOperandInfo(T)); @@ -261,9 +262,32 @@ if (OpInfo.Type == InlineAsm::isInput || (OpInfo.Type == InlineAsm::isOutput && OpInfo.isIndirect)) { - LLVM_DEBUG(dbgs() << "Input operands and indirect output operands are " - "not supported yet\n"); - return false; + OpInfo.CallOperandVal = const_cast(Call.getArgOperand(ArgNo++)); + + if (const auto *BB = dyn_cast(OpInfo.CallOperandVal)) { + LLVM_DEBUG(dbgs() << "Basic block input operands not supported yet\n"); + return false; + } + + Type *OpTy = OpInfo.CallOperandVal->getType(); + + // If this is an indirect operand, the operand is a pointer to the + // accessed type. + if (OpInfo.isIndirect) { + PointerType *PtrTy = dyn_cast(OpTy); + if (!PtrTy) + report_fatal_error("Indirect operand for inline asm not a pointer!"); + OpTy = PtrTy->getElementType(); + } + + // FIXME: Support aggregate input operands + if (!OpTy->isSingleValueType()) { + LLVM_DEBUG( + dbgs() << "Aggregate input operands are not supported yet\n"); + return false; + } + + OpInfo.ConstraintVT = TLI->getValueType(DL, OpTy, true).getSimpleVT(); } else if (OpInfo.Type == InlineAsm::isOutput && !OpInfo.isIndirect) { assert(!Call.getType()->isVoidTy() && "Bad inline asm!"); @@ -363,8 +387,97 @@ } break; - case InlineAsm::isInput: - return false; + case InlineAsm::isInput: { + if (OpInfo.isMatchingInputConstraint()) { + LLVM_DEBUG(dbgs() << "Tied input operands not supported yet\n"); + return false; + } + + if (OpInfo.ConstraintType == TargetLowering::C_Other && + OpInfo.isIndirect) { + LLVM_DEBUG(dbgs() << "Indirect input operands with unknown constraint " + "not supported yet\n"); + return false; + } + + if (OpInfo.ConstraintType == TargetLowering::C_Immediate || + OpInfo.ConstraintType == TargetLowering::C_Other) { + + std::vector Ops; + if (!lowerAsmOperandForConstraint(OpInfo.CallOperandVal, + OpInfo.ConstraintCode, Ops, + MIRBuilder)) { + LLVM_DEBUG(dbgs() << "Don't support constraint: " + << OpInfo.ConstraintCode << " yet\n"); + return false; + } + + assert(Ops.size() > 0 && + "Expected constraint to be lowered to at least one operand"); + + // Add information to the INLINEASM node to know about this input. + unsigned OpFlags = + InlineAsm::getFlagWord(InlineAsm::Kind_Imm, Ops.size()); + Inst.addImm(OpFlags); + Inst.add(Ops); + break; + } + + if (OpInfo.ConstraintType == TargetLowering::C_Memory) { + assert(OpInfo.isIndirect && "Operand must be indirect to be a mem!"); + + unsigned ConstraintID = + TLI->getInlineAsmMemConstraint(OpInfo.ConstraintCode); + unsigned OpFlags = InlineAsm::getFlagWord(InlineAsm::Kind_Mem, 1); + OpFlags = InlineAsm::getFlagWordForMem(OpFlags, ConstraintID); + Inst.addImm(OpFlags); + ArrayRef SourceRegs = + GetOrCreateVRegs(*OpInfo.CallOperandVal); + assert( + SourceRegs.size() == 1 && + "Expected the memory input to fit into a single virtual register"); + Inst.addReg(SourceRegs[0]); + break; + } + + assert((OpInfo.ConstraintType == TargetLowering::C_RegisterClass || + OpInfo.ConstraintType == TargetLowering::C_Register) && + "Unknown constraint type!"); + + if (OpInfo.isIndirect) { + LLVM_DEBUG(dbgs() << "Can't handle indirect register inputs yet " + "for constraint '" + << OpInfo.ConstraintCode << "'\n"); + return false; + } + + // Copy the input into the appropriate registers. + if (OpInfo.Regs.empty()) { + LLVM_DEBUG( + dbgs() + << "Couldn't allocate input register for register constraint\n"); + return false; + } + + unsigned NumRegs = OpInfo.Regs.size(); + ArrayRef SourceRegs = GetOrCreateVRegs(*OpInfo.CallOperandVal); + assert(NumRegs == SourceRegs.size() && + "Expected the number of input registers to match the number of " + "source registers"); + + if (NumRegs > 1) { + LLVM_DEBUG(dbgs() << "Input operands with multiple input registers are " + "not supported yet\n"); + return false; + } + + unsigned Flag = InlineAsm::getFlagWord(InlineAsm::Kind_RegUse, NumRegs); + Inst.addImm(Flag); + MIRBuilder.buildCopy(OpInfo.Regs[0], SourceRegs[0]); + Inst.addReg(OpInfo.Regs[0]); + break; + } + case InlineAsm::isClobber: { unsigned NumRegs = OpInfo.Regs.size(); @@ -441,3 +554,27 @@ return true; } + +bool InlineAsmLowering::lowerAsmOperandForConstraint( + Value *Val, StringRef Constraint, std::vector &Ops, + MachineIRBuilder &MIRBuilder) const { + if (Constraint.size() > 1) + return false; + + char ConstraintLetter = Constraint[0]; + switch (ConstraintLetter) { + default: + return false; + case 'i': // Simple Integer or Relocatable Constant + if (ConstantInt *CI = dyn_cast(Val)) { + assert(CI->getBitWidth() <= 64 && + "expected immediate to fit into 64-bits"); + // Boolean constants should be zero-extended, others are sign-extended + bool IsBool = CI->getBitWidth() == 1; + int64_t ExtVal = IsBool ? CI->getZExtValue() : CI->getSExtValue(); + Ops.push_back(MachineOperand::CreateImm(ExtVal)); + return true; + } + return false; + } +} diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll --- a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll +++ b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-inline-asm.ll @@ -132,3 +132,66 @@ %2 = extractelement <2 x float> %1, i32 0 ret float %2 } + +define void @test_input_register_imm() { + ; CHECK-LABEL: name: test_input_register_imm + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 42 + ; CHECK: [[COPY:%[0-9]+]]:gpr64common = COPY [[C]](s64) + ; CHECK: INLINEASM &"mov x0, $0", 1 /* sideeffect attdialect */, 9 /* reguse */, [[COPY]] + ; CHECK: RET_ReallyLR + call void asm sideeffect "mov x0, $0", "r"(i64 42) + ret void +} + +; Make sure that boolean immediates are properly (zero) extended. +define i32 @test_boolean_imm_ext() { + ; CHECK-LABEL: name: test_boolean_imm_ext + ; CHECK: bb.1.entry: + ; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 1 + ; CHECK: INLINEASM &"#TEST 42 + ${0:c} - .\0A\09", 9 /* sideeffect mayload attdialect */, 13 /* imm */, 1 + ; CHECK: $w0 = COPY [[C]](s32) + ; CHECK: RET_ReallyLR implicit $w0 +entry: + tail call void asm sideeffect "#TEST 42 + ${0:c} - .\0A\09", "i"(i1 true) + ret i32 1 +} + +define void @test_input_imm() { + ; CHECK-LABEL: name: test_input_imm + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: INLINEASM &"mov x0, $0", 9 /* sideeffect mayload attdialect */, 13 /* imm */, 42 + ; CHECK: RET_ReallyLR + call void asm sideeffect "mov x0, $0", "i"(i64 42) + ret void +} + +define zeroext i8 @test_input_register(i8* %src) nounwind { + ; CHECK-LABEL: name: test_input_register + ; CHECK: bb.1.entry: + ; CHECK: liveins: $x0 + ; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0 + ; CHECK: [[COPY1:%[0-9]+]]:gpr64common = COPY [[COPY]](p0) + ; CHECK: INLINEASM &"ldtrb ${0:w}, [$1]", 0 /* attdialect */, 655370 /* regdef:GPR32common */, def %1, 9 /* reguse */, [[COPY1]] + ; CHECK: [[COPY2:%[0-9]+]]:_(s32) = COPY %1 + ; CHECK: [[TRUNC:%[0-9]+]]:_(s8) = G_TRUNC [[COPY2]](s32) + ; CHECK: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[TRUNC]](s8) + ; CHECK: $w0 = COPY [[ZEXT]](s32) + ; CHECK: RET_ReallyLR implicit $w0 +entry: + %0 = tail call i8 asm "ldtrb ${0:w}, [$1]", "=r,r"(i8* %src) nounwind + ret i8 %0 +} + +define i32 @test_memory_constraint(i32* %a) nounwind { + ; CHECK-LABEL: name: test_memory_constraint + ; CHECK: bb.1 (%ir-block.0): + ; CHECK: liveins: $x0 + ; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0 + ; CHECK: INLINEASM &"ldr $0, $1", 8 /* mayload attdialect */, 655370 /* regdef:GPR32common */, def %1, 196622 /* mem:m */, [[COPY]](p0) + ; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY %1 + ; CHECK: $w0 = COPY [[COPY1]](s32) + ; CHECK: RET_ReallyLR implicit $w0 + %1 = tail call i32 asm "ldr $0, $1", "=r,*m"(i32* %a) + ret i32 %1 +}