Index: lib/CodeGen/GlobalISel/InstructionSelector.cpp =================================================================== --- lib/CodeGen/GlobalISel/InstructionSelector.cpp +++ lib/CodeGen/GlobalISel/InstructionSelector.cpp @@ -43,6 +43,11 @@ if (TRI.isPhysicalRegister(MO.getReg())) continue; + // Predicate operands and optional defs don't need to be constrained. + MCOperandInfo OpInfo = I.getDesc().OpInfo[OpI]; + if (OpInfo.isPredicate() || OpInfo.isOptionalDef()) + continue; + const TargetRegisterClass *RC = TII.getRegClass(I.getDesc(), OpI, &TRI, MF); assert(RC && "Selected inst should have regclass operand"); Index: lib/Target/ARM/ARMCallLowering.cpp =================================================================== --- lib/Target/ARM/ARMCallLowering.cpp +++ lib/Target/ARM/ARMCallLowering.cpp @@ -29,13 +29,31 @@ ARMCallLowering::ARMCallLowering(const ARMTargetLowering &TLI) : CallLowering(&TLI) {} +static bool isSupportedType(const DataLayout DL, const ARMTargetLowering &TLI, + Type *T) { + EVT VT = TLI.getValueType(DL, T); + return VT.isSimple() && VT.getSimpleVT().getSizeInBits() == 32; +} + bool ARMCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, unsigned VReg) const { - // We're currently only handling void returns - if (Val != nullptr) + assert(!Val == !VReg && "Return value without a vreg"); + + if (!Val) { + AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); + return true; + } + + auto DL = MIRBuilder.getMF().getDataLayout(); + auto &TLI = *getTLI(); + if (!isSupportedType(DL, TLI, Val->getType())) return false; - AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); + auto PhysReg = ARM::R0; // TODO: use CCState instead of hardcoding + MIRBuilder.buildCopy(PhysReg, VReg); + + auto Ret = AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); + Ret.addUse(PhysReg, RegState::Implicit); return true; } @@ -43,5 +61,37 @@ bool ARMCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, ArrayRef VRegs) const { - return F.arg_empty(); + // Quick exit if there aren't any args + if (F.arg_empty()) + return true; + + // Stick to only 4 arguments for now + if (F.arg_size() > 4) + return false; + + if (F.isVarArg()) + return false; + + auto DL = MIRBuilder.getMF().getDataLayout(); + auto &TLI = *getTLI(); + + auto &Args = F.getArgumentList(); + for (auto &Arg : Args) + if (!isSupportedType(DL, TLI, Arg.getType())) + return false; + + // TODO: use CCState instead of hardcoding + MCPhysReg PhysRegs[] = {ARM::R0, ARM::R1, ARM::R2, ARM::R3}; + + unsigned Idx = 0; + for (auto &Arg : Args) { + (void)Arg; + auto VirtReg = VRegs[Idx]; + auto PhysReg = PhysRegs[Idx]; + MIRBuilder.getMBB().addLiveIn(PhysReg); + MIRBuilder.buildCopy(VirtReg, PhysReg); + Idx++; + } + + return true; } Index: lib/Target/ARM/ARMInstructionSelector.cpp =================================================================== --- lib/Target/ARM/ARMInstructionSelector.cpp +++ lib/Target/ARM/ARMInstructionSelector.cpp @@ -15,6 +15,7 @@ #include "ARMRegisterBankInfo.h" #include "ARMSubtarget.h" #include "ARMTargetMachine.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "arm-isel" @@ -30,6 +31,59 @@ : InstructionSelector(), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()), RBI(RBI) {} -bool ARMInstructionSelector::select(llvm::MachineInstr &I) const { - return !isPreISelGenericOpcode(I.getOpcode()); +static bool selectCopy(MachineInstr &I, const TargetInstrInfo &TII, + MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) { + + unsigned DstReg = I.getOperand(0).getReg(); + if (TargetRegisterInfo::isPhysicalRegister(DstReg)) { + return true; + } + + const RegisterBank *RegBank = RBI.getRegBank(DstReg, MRI, TRI); + assert(RegBank && "Can't get reg bank for virtual register"); + + const unsigned DstSize = MRI.getType(DstReg).getSizeInBits(); + unsigned SrcReg = I.getOperand(1).getReg(); + const unsigned SrcSize = RBI.getSizeInBits(SrcReg, MRI, TRI); + (void)SrcSize; + assert(DstSize == SrcSize && "Copy with different width?!"); + + assert(RegBank->getID() == ARM::GPRRegBankID && "Unsupported reg bank"); + const TargetRegisterClass *RC = &ARM::GPRRegClass; + + // No need to constrain SrcReg. It will get constrained when + // we hit another of its uses or its defs. + // Copies do not have constraints. + if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) { + DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << " operand\n"); + return false; + } + return true; +} + +bool ARMInstructionSelector::select(MachineInstr &I) const { + assert(I.getParent() && "Instruction should be in a basic block!"); + assert(I.getParent()->getParent() && "Instruction should be in a function!"); + + auto &MBB = *I.getParent(); + auto &MF = *MBB.getParent(); + auto &MRI = MF.getRegInfo(); + + if (!isPreISelGenericOpcode(I.getOpcode())) { + if (I.isCopy()) { + return selectCopy(I, TII, MRI, TRI, RBI); + } + + return true; + } + + if (I.getOpcode() == TargetOpcode::G_ADD) { + I.setDesc(TII.get(ARM::ADDrr)); + AddDefaultCC(AddDefaultPred(MachineInstrBuilder(MF, I))); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + + return false; } Index: lib/Target/ARM/ARMLegalizerInfo.cpp =================================================================== --- lib/Target/ARM/ARMLegalizerInfo.cpp +++ lib/Target/ARM/ARMLegalizerInfo.cpp @@ -24,5 +24,10 @@ #endif ARMLegalizerInfo::ARMLegalizerInfo() { + using namespace TargetOpcode; + const LLT s32 = LLT::scalar(32); + + setAction({G_ADD, s32}, Legal); + computeTables(); } Index: lib/Target/ARM/ARMRegisterBankInfo.h =================================================================== --- lib/Target/ARM/ARMRegisterBankInfo.h +++ lib/Target/ARM/ARMRegisterBankInfo.h @@ -20,10 +20,22 @@ class TargetRegisterInfo; +namespace ARM { +enum { + GPRRegBankID = 0, // General purpose registers + NumRegisterBanks, +}; +} // end namespace ARM + /// This class provides the information for the target register banks. class ARMRegisterBankInfo final : public RegisterBankInfo { public: ARMRegisterBankInfo(const TargetRegisterInfo &TRI); + + const RegisterBank & + getRegBankFromRegClass(const TargetRegisterClass &RC) const override; + + InstructionMapping getInstrMapping(const MachineInstr &MI) const override; }; } // End llvm namespace. #endif Index: lib/Target/ARM/ARMRegisterBankInfo.cpp =================================================================== --- lib/Target/ARM/ARMRegisterBankInfo.cpp +++ lib/Target/ARM/ARMRegisterBankInfo.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "ARMRegisterBankInfo.h" +#include "ARMInstrInfo.h" // For the register classes #include "llvm/CodeGen/GlobalISel/RegisterBank.h" #include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -23,5 +24,93 @@ #error "You shouldn't build this" #endif +// FIXME: TableGen this. +// If it grows too much and TableGen still isn't ready to do the job, extract it +// into an ARMGenRegisterBankInfo.def (similar to AArch64). +namespace llvm { +namespace ARM { +RegisterBank GPRRegBank; +RegisterBank *RegBanks[] = {&GPRRegBank}; + +RegisterBankInfo::PartialMapping GPRPartialMapping{0, 32, GPRRegBank}; + +RegisterBankInfo::ValueMapping ValueMappings[] = { + {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}}; + +} // end namespace arm +} // end namespace llvm + ARMRegisterBankInfo::ARMRegisterBankInfo(const TargetRegisterInfo &TRI) - : RegisterBankInfo(nullptr, 0) {} + : RegisterBankInfo(ARM::RegBanks, ARM::NumRegisterBanks) { + static bool AlreadyInit = false; + // We have only one set of register banks, whatever the subtarget + // is. Therefore, the initialization of the RegBanks table should be + // done only once. Indeed the table of all register banks + // (ARM::RegBanks) is unique in the compiler. At some point, it + // will get tablegen'ed and the whole constructor becomes empty. + if (AlreadyInit) + return; + AlreadyInit = true; + + // Initialize the GPR bank. + createRegisterBank(ARM::GPRRegBankID, "GPRrb"); + + addRegBankCoverage(ARM::GPRRegBankID, ARM::GPRRegClassID, TRI); + addRegBankCoverage(ARM::GPRRegBankID, ARM::GPRwithAPSRRegClassID, TRI); + const RegisterBank &RBGPR = getRegBank(ARM::GPRRegBankID); + (void)RBGPR; + assert(&ARM::GPRRegBank == &RBGPR && "The order in RegBanks is messed up"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRRegClassID)) && + "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRwithAPSRRegClassID)) && + "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRnopcRegClassID)) && + "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::rGPRRegClassID)) && + "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPRRegClassID)) && + "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::tcGPRRegClassID)) && + "Subclass not added?"); + assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPR_and_tcGPRRegClassID)) && + "Subclass not added?"); + assert(RBGPR.getSize() == 32 && "GPRs should hold up to 32-bit"); +} + +const RegisterBank &ARMRegisterBankInfo::getRegBankFromRegClass( + const TargetRegisterClass &RC) const { + using namespace ARM; + + switch (RC.getID()) { + case GPRRegClassID: + case tGPR_and_tcGPRRegClassID: + return getRegBank(ARM::GPRRegBankID); + default: + llvm_unreachable("Unsupported register kind"); + } + + llvm_unreachable("Switch should handle all register classes"); +} + +RegisterBankInfo::InstructionMapping +ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { + auto Opc = MI.getOpcode(); + + // Try the default logic for non-generic instructions that are either copies + // or already have some operands assigned to banks. + if (!isPreISelGenericOpcode(Opc)) { + RegisterBankInfo::InstructionMapping Mapping = getInstrMappingImpl(MI); + if (Mapping.isValid()) + return Mapping; + } + + if (Opc == TargetOpcode::G_ADD) { + unsigned Cost = 1; + unsigned NumOperands = MI.getNumOperands(); + ValueMapping *OperandsMapping = &ARM::ValueMappings[0]; + return InstructionMapping{DefaultMappingID, Cost, OperandsMapping, + NumOperands}; + } + + llvm_unreachable("Unsupported opcode"); +} Index: test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir =================================================================== --- /dev/null +++ test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir @@ -0,0 +1,34 @@ +# RUN: llc -O0 -mtriple arm-- -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s +--- | + define void @test_adds32() { ret void } +... +--- +name: test_adds32 +# CHECK-LABEL: name: test_adds32 +legalized: true +regBankSelected: true +selected: false +# CHECK: selected: true +registers: + - { id: 0, class: gprrb } + - { id: 1, class: gprrb } + - { id: 2, class: gprrb } +body: | + bb.0: + liveins: %r0, %r1 + + %0(s32) = COPY %r0 + ; CHECK: [[VREGX:%[0-9]+]] = COPY %r0 + + %1(s32) = COPY %r1 + ; CHECK: [[VREGY:%[0-9]+]] = COPY %r1 + + %2(s32) = G_ADD %0, %1 + ; CHECK: [[VREGSUM:%[0-9]+]] = ADDrr [[VREGX]], [[VREGY]], 14, _, _ + + %r0 = COPY %2(s32) + ; CHECK: %r0 = COPY [[VREGSUM]] + + BX_RET 14, _, implicit %r0 + ; CHECK: BX_RET 14, _, implicit %r0 +... Index: test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll +++ test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll @@ -7,3 +7,15 @@ ret void } +define i32 @test_add(i32 %x, i32 %y) { +; CHECK-LABEL: name: test_add +; CHECK: liveins: %r0, %r1 +; CHECK: [[VREGX:%[0-9]+]]{{.*}} = COPY %r0 +; CHECK: [[VREGY:%[0-9]+]]{{.*}} = COPY %r1 +; CHECK: [[SUM:%[0-9]+]]{{.*}} = G_ADD [[VREGX]], [[VREGY]] +; CHECK: %r0 = COPY [[SUM]] +; CHECK: BX_RET 14, _, implicit %r0 +entry: + %sum = add i32 %x, %y + ret i32 %sum +} Index: test/CodeGen/ARM/GlobalISel/arm-isel.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/GlobalISel/arm-isel.ll @@ -0,0 +1,17 @@ +; RUN: llc -mtriple arm-unknown -global-isel %s -o - | FileCheck %s + +define void @test_void_return() { +; CHECK-LABEL: test_void_return: +; CHECK: bx lr +entry: + ret void +} + +define i32 @test_add(i32 %x, i32 %y) { +; CHECK-LABEL: test_add: +; CHECK: add r0, r0, r1 +; CHECK: bx lr +entry: + %sum = add i32 %x, %y + ret i32 %sum +} Index: test/CodeGen/ARM/GlobalISel/arm-legalizer.mir =================================================================== --- /dev/null +++ test/CodeGen/ARM/GlobalISel/arm-legalizer.mir @@ -0,0 +1,29 @@ +# RUN: llc -mtriple arm-- -global-isel -run-pass=legalizer %s -o - | FileCheck %s +--- | + define void @test_adds32() { ret void } +... +--- +name: test_adds32 +# CHECK-LABEL: name: test_adds32 +legalized: false +# CHECK: legalized: true +regBankSelected: false +selected: false +tracksRegLiveness: true +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } +body: | + bb.0: + liveins: %r0, %r1 + + %0(s32) = COPY %r0 + %1(s32) = COPY %r1 + %2(s32) = G_ADD %0, %1 + ; G_ADD with s32 is legal, so we should find it unchanged in the output + ; CHECK: {{%[0-9]+}}(s32) = G_ADD {{%[0-9]+, %[0-9]+}} + %r0 = COPY %2(s32) + BX_RET 14, _, implicit %r0 + +... Index: test/CodeGen/ARM/GlobalISel/arm-regbankselect.mir =================================================================== --- /dev/null +++ test/CodeGen/ARM/GlobalISel/arm-regbankselect.mir @@ -0,0 +1,30 @@ +# RUN: llc -mtriple arm-- -global-isel -run-pass=regbankselect %s -o - | FileCheck %s +--- | + define void @test_adds32() { ret void } +... +--- +name: test_adds32 +# CHECK-LABEL: name: test_adds32 +legalized: true +regBankSelected: false +selected: false +# CHECK: registers: +# CHECK: - { id: 0, class: gprrb } +# CHECK: - { id: 1, class: gprrb } +# CHECK: - { id: 2, class: gprrb } + +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } +body: | + bb.0: + liveins: %r0, %r1 + + %0(s32) = COPY %r0 + %1(s32) = COPY %r1 + %2(s32) = G_ADD %0, %1 + %r0 = COPY %2(s32) + BX_RET 14, _, implicit %r0 + +...