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; + // Register operands with a value of 0 (e.g. predicate operands) don't need + // to be constrained. + if (MO.getReg() == 0) + 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,19 +29,139 @@ 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.isInteger() && + VT.getSimpleVT().getSizeInBits() == 32; +} + +namespace { +struct FuncReturnHandler : public CallLowering::ValueHandler { + FuncReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB) + : ValueHandler(MIRBuilder, MRI), MIB(MIB) {} + + unsigned getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + llvm_unreachable("Don't know how to get a stack address yet"); + } + + void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + CCValAssign &VA) override { + assert(VA.isRegLoc() && "Value shouldn't be assigned to reg"); + assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?"); + + assert(VA.getValVT().getSizeInBits() == 32 && "Unsupported value size"); + assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size"); + + MIRBuilder.buildCopy(PhysReg, ValVReg); + MIB.addUse(PhysReg, RegState::Implicit); + } + + void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + llvm_unreachable("Don't know how to assign a value to an address yet"); + } + + MachineInstrBuilder &MIB; +}; +} // End anonymous namespace. + 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"); + + auto Ret = AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); + if (!Val) { + // Nothing more to do + return true; + } + + // Move the insertion point before the return + MIRBuilder.setInstr(*Ret); + + auto &MF = MIRBuilder.getMF(); + const auto &F = *MF.getFunction(); + + auto DL = MF.getDataLayout(); + auto &TLI = *getTLI(); + if (!isSupportedType(DL, TLI, Val->getType())) return false; - AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); + CCAssignFn *AssignFn = + TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg()); + + ArgInfo RetInfo(VReg, Val->getType()); + setArgFlags(RetInfo, AttributeSet::ReturnIndex, DL, F); - return true; + FuncReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), Ret); + return handleAssignments(MIRBuilder, AssignFn, RetInfo, RetHandler); } +namespace { +struct FormalArgHandler : public CallLowering::ValueHandler { + FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI) + : ValueHandler(MIRBuilder, MRI) {} + + unsigned getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + llvm_unreachable("Don't know how to get a stack address yet"); + } + + void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + CCValAssign &VA) override { + assert(VA.isRegLoc() && "Value shouldn't be assigned to reg"); + assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?"); + + assert(VA.getValVT().getSizeInBits() == 32 && "Unsupported value size"); + assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size"); + + MIRBuilder.getMBB().addLiveIn(PhysReg); + MIRBuilder.buildCopy(ValVReg, PhysReg); + } + + void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + llvm_unreachable("Don't know how to assign a value to an address yet"); + } +}; +} // End anonymous namespace + 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; + + CCAssignFn *AssignFn = + TLI.CCAssignFnForCall(F.getCallingConv(), F.isVarArg()); + + SmallVector ArgInfos; + unsigned Idx = 0; + for (auto &Arg : Args) { + ArgInfo AInfo(VRegs[Idx], Arg.getType()); + setArgFlags(AInfo, Idx + 1, DL, F); + ArgInfos.push_back(AInfo); + Idx++; + } + + FormalArgHandler ArgHandler(MIRBuilder, MIRBuilder.getMF().getRegInfo()); + return handleAssignments(MIRBuilder, AssignFn, ArgInfos, ArgHandler); } Index: lib/Target/ARM/ARMISelLowering.h =================================================================== --- lib/Target/ARM/ARMISelLowering.h +++ lib/Target/ARM/ARMISelLowering.h @@ -490,6 +490,9 @@ return HasStandaloneRem; } + CCAssignFn *CCAssignFnForCall(CallingConv::ID CC, bool isVarArg) const; + CCAssignFn *CCAssignFnForReturn(CallingConv::ID CC, bool isVarArg) const; + protected: std::pair findRepresentativeClass(const TargetRegisterInfo *TRI, Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -1621,6 +1621,16 @@ } } +CCAssignFn *ARMTargetLowering::CCAssignFnForCall(CallingConv::ID CC, + bool isVarArg) const { + return CCAssignFnForNode(CC, false, isVarArg); +} + +CCAssignFn *ARMTargetLowering::CCAssignFnForReturn(CallingConv::ID CC, + bool isVarArg) const { + return CCAssignFnForNode(CC, true, isVarArg); +} + /// CCAssignFnForNode - Selects the correct CCAssignFn for the given /// CallingConvention. CCAssignFn *ARMTargetLowering::CCAssignFnForNode(CallingConv::ID CC, Index: lib/Target/ARM/ARMInstructionSelector.h =================================================================== --- lib/Target/ARM/ARMInstructionSelector.h +++ lib/Target/ARM/ARMInstructionSelector.h @@ -32,6 +32,7 @@ private: const ARMBaseInstrInfo &TII; const ARMBaseRegisterInfo &TRI; + const ARMRegisterBankInfo &RBI; }; } // End llvm namespace. 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" @@ -28,8 +29,58 @@ ARMInstructionSelector::ARMInstructionSelector(const ARMSubtarget &STI, const ARMRegisterBankInfo &RBI) : InstructionSelector(), TII(*STI.getInstrInfo()), - TRI(*STI.getRegisterInfo()) {} + 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,91 @@ #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, "GPRB"); + + 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)) { + InstructionMapping Mapping = getInstrMappingImpl(MI); + if (Mapping.isValid()) + return Mapping; + } + + if (Opc == TargetOpcode::G_ADD) { + unsigned NumOperands = MI.getNumOperands(); + ValueMapping *OperandsMapping = &ARM::ValueMappings[0]; + return InstructionMapping{DefaultMappingID, /*Cost=*/1, OperandsMapping, + NumOperands}; + } + + return InstructionMapping{}; +} 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: gprb } + - { id: 1, class: gprb } + - { id: 2, class: gprb } +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_add_s32() { ret void } +... +--- +name: test_add_s32 +# CHECK-LABEL: name: test_add_s32 +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_add_s32() { ret void } +... +--- +name: test_add_s32 +# CHECK-LABEL: name: test_add_s32 +legalized: true +regBankSelected: false +selected: false +# CHECK: registers: +# CHECK: - { id: 0, class: gprb } +# CHECK: - { id: 1, class: gprb } +# CHECK: - { id: 2, class: gprb } + +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 + +...