Index: lib/Target/ARM/ARMCallLowering.cpp =================================================================== --- lib/Target/ARM/ARMCallLowering.cpp +++ lib/Target/ARM/ARMCallLowering.cpp @@ -19,6 +19,7 @@ #include "ARMISelLowering.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" using namespace llvm; @@ -35,29 +36,99 @@ return VT.isSimple() && 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?"); + + 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 { assert(!Val == !VReg && "Return value without a vreg"); + auto Ret = AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); if (!Val) { - AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); + // Nothing more to do return true; } - auto DL = MIRBuilder.getMF().getDataLayout(); + // 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; - auto PhysReg = ARM::R0; // TODO: use CCState instead of hardcoding - MIRBuilder.buildCopy(PhysReg, VReg); + CCAssignFn *AssignFn = + TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg()); - auto Ret = AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET)); - Ret.addUse(PhysReg, RegState::Implicit); + 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 { + auto &MFI = MIRBuilder.getMF().getFrameInfo(); + int FI = MFI.CreateFixedObject(Size, Offset, true); + MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI); + unsigned AddrReg = MRI.createGenericVirtualRegister(LLT::pointer(0, 32)); + MIRBuilder.buildFrameIndex(AddrReg, FI); + return AddrReg; + } + + void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOLoad, Size, /* Alignment */ 0); + MIRBuilder.buildLoad(ValVReg, Addr, *MMO); + } + + 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?"); + + MIRBuilder.getMBB().addLiveIn(PhysReg); + MIRBuilder.buildCopy(ValVReg, PhysReg); + } +}; +} // End anonymous namespace + bool ARMCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, ArrayRef VRegs) const { @@ -65,10 +136,6 @@ 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; @@ -80,18 +147,18 @@ if (!isSupportedType(DL, TLI, Arg.getType())) return false; - // TODO: use CCState instead of hardcoding - MCPhysReg PhysRegs[] = {ARM::R0, ARM::R1, ARM::R2, ARM::R3}; + CCAssignFn *AssignFn = + TLI.CCAssignFnForCall(F.getCallingConv(), F.isVarArg()); + SmallVector ArgInfos; 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); + ArgInfo AInfo(VRegs[Idx], Arg.getType()); + setArgFlags(AInfo, Idx + 1, DL, F); + ArgInfos.push_back(AInfo); Idx++; } - return true; + 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.cpp =================================================================== --- lib/Target/ARM/ARMInstructionSelector.cpp +++ lib/Target/ARM/ARMInstructionSelector.cpp @@ -79,9 +79,23 @@ return true; } - if (I.getOpcode() == TargetOpcode::G_ADD) { + MachineInstrBuilder MIB{MF, I}; + + using namespace TargetOpcode; + switch (I.getOpcode()) { + case G_ADD: I.setDesc(TII.get(ARM::ADDrr)); - AddDefaultCC(AddDefaultPred(MachineInstrBuilder(MF, I))); + AddDefaultCC(AddDefaultPred(MIB)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + case G_FRAME_INDEX: + // Add 0 to the given frame index and hope it will eventually be folded into + // the user(s). + I.setDesc(TII.get(ARM::ADDri)); + AddDefaultCC(AddDefaultPred(MIB.addImm(0))); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + case G_LOAD: + I.setDesc(TII.get(ARM::LDRi12)); + AddDefaultPred(MIB.addImm(0)); return constrainSelectedInstRegOperands(I, TII, TRI, RBI); } Index: lib/Target/ARM/ARMLegalizerInfo.cpp =================================================================== --- lib/Target/ARM/ARMLegalizerInfo.cpp +++ lib/Target/ARM/ARMLegalizerInfo.cpp @@ -25,8 +25,14 @@ ARMLegalizerInfo::ARMLegalizerInfo() { using namespace TargetOpcode; + const LLT p0 = LLT::pointer(0, 32); const LLT s32 = LLT::scalar(32); + setAction({G_FRAME_INDEX, p0}, Legal); + + setAction({G_LOAD, s32}, Legal); + setAction({G_LOAD, 1, p0}, Legal); + setAction({G_ADD, s32}, Legal); computeTables(); Index: lib/Target/ARM/ARMRegisterBankInfo.cpp =================================================================== --- lib/Target/ARM/ARMRegisterBankInfo.cpp +++ lib/Target/ARM/ARMRegisterBankInfo.cpp @@ -99,18 +99,31 @@ // 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); + 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}; + using namespace TargetOpcode; + + unsigned Cost = 1; + unsigned NumOperands = MI.getNumOperands(); + const ValueMapping *OperandsMapping = &ARM::ValueMappings[0]; + + switch (Opc) { + case G_ADD: + case G_LOAD: + // FIXME: We're abusing the fact that everything lives in a GPR for now; in + // the real world we would use different mappings. + OperandsMapping = &ARM::ValueMappings[0]; + break; + case G_FRAME_INDEX: + OperandsMapping = getOperandsMapping({&ARM::ValueMappings[0], nullptr}); + break; + default: + return InstructionMapping{}; } - llvm_unreachable("Unsupported opcode"); + return InstructionMapping{DefaultMappingID, Cost, OperandsMapping, + NumOperands}; } Index: test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir +++ test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir @@ -1,6 +1,7 @@ # RUN: llc -O0 -mtriple arm-- -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s --- | define void @test_adds32() { ret void } + define void @test_load_from_stack() { ret void } ... --- name: test_adds32 @@ -32,3 +33,33 @@ BX_RET 14, _, implicit %r0 ; CHECK: BX_RET 14, _, implicit %r0 ... +--- +name: test_load_from_stack +# CHECK-LABEL: name: test_load_from_stack +legalized: true +regBankSelected: true +selected: false +# CHECK: selected: true +registers: + - { id: 0, class: gprrb } + - { id: 1, class: gprrb } + - { id: 2, class: gprrb } + - { id: 3, class: gprrb } +fixedStack: + - { id: 0, offset: 0, size: 4, alignment: 4, isImmutable: true, isAliased: false } + - { id: 1, offset: 4, size: 4, alignment: 4, isImmutable: true, isAliased: false } + - { id: 2, offset: 8, size: 4, alignment: 4, isImmutable: true, isAliased: false } +# CHECK: id: [[FRAME_INDEX:[0-9]+]], offset: 8 +body: | + bb.0: + liveins: %r0, %r1, %r2, %r3 + + %0(p0) = G_FRAME_INDEX %fixed-stack.2 + ; CHECK: [[FIVREG:%[0-9]+]] = ADDri %fixed-stack.[[FRAME_INDEX]], 0, 14, _, _ + + %1(s32) = G_LOAD %0(p0) + ; CHECK: {{%[0-9]+}} = LDRi12 [[FIVREG]], 0, 14, _ + + BX_RET 14, _ + ; CHECK: BX_RET 14, _ +... Index: test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll +++ test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll @@ -19,3 +19,20 @@ %sum = add i32 %x, %y ret i32 %sum } + +define i32 @test_many_args(i32 %p0, i32 %p1, i32 %p2, i32 %p3, i32 %p4, i32 %p5) { +; CHECK-LABEL: name: test_many_args +; CHECK: fixedStack: +; CHECK-DAG: id: [[P4:[0-9]]]{{.*}}offset: 0{{.*}}size: 4 +; CHECK-DAG: id: [[P5:[0-9]]]{{.*}}offset: 4{{.*}}size: 4 +; CHECK: liveins: %r0, %r1, %r2, %r3 +; CHECK: [[VREGP2:%[0-9]+]]{{.*}} = COPY %r2 +; CHECK: [[FIP5:%[0-9]+]]{{.*}} = G_FRAME_INDEX %fixed-stack.[[P5]] +; CHECK: [[VREGP5:%[0-9]+]]{{.*}} = G_LOAD [[FIP5]] +; CHECK: [[SUM:%[0-9]+]]{{.*}} = G_ADD [[VREGP2]], [[VREGP5]] +; CHECK: %r0 = COPY [[SUM]] +; CHECK: BX_RET 14, _, implicit %r0 +entry: + %sum = add i32 %p2, %p5 + ret i32 %sum +} Index: test/CodeGen/ARM/GlobalISel/arm-isel.ll =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-isel.ll +++ test/CodeGen/ARM/GlobalISel/arm-isel.ll @@ -15,3 +15,14 @@ %sum = add i32 %x, %y ret i32 %sum } + +define i32 @test_many_args(i32 %p0, i32 %p1, i32 %p2, i32 %p3, i32 %p4, i32 %p5) { +; CHECK-LABEL: test_many_args: +; CHECK: add [[P5ADDR:r[0-9]+]], sp, #4 +; CHECK: ldr [[P5:r[0-9]+]], {{.*}}[[P5ADDR]] +; CHECK: add r0, r2, [[P5]] +; CHECK: bx lr +entry: + %sum = add i32 %p2, %p5 + ret i32 %sum +} Index: test/CodeGen/ARM/GlobalISel/arm-legalizer.mir =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-legalizer.mir +++ test/CodeGen/ARM/GlobalISel/arm-legalizer.mir @@ -1,6 +1,7 @@ # RUN: llc -mtriple arm-- -global-isel -run-pass=legalizer %s -o - | FileCheck %s --- | define void @test_adds32() { ret void } + define void @test_load_from_stack() { ret void } ... --- name: test_adds32 @@ -27,3 +28,33 @@ BX_RET 14, _, implicit %r0 ... +--- +name: test_load_from_stack +# CHECK-LABEL: name: test_load_from_stack +legalized: false +# CHECK: legalized: true +regBankSelected: false +selected: false +tracksRegLiveness: true +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } + - { id: 3, class: _ } +fixedStack: + - { id: 0, offset: 0, size: 4, alignment: 4, isImmutable: true, isAliased: false } + - { id: 1, offset: 4, size: 4, alignment: 4, isImmutable: true, isAliased: false } + - { id: 2, offset: 8, size: 4, alignment: 4, isImmutable: true, isAliased: false } + # CHECK: id: [[FRAME_INDEX:[0-9]+]], offset: 8 +body: | + bb.0: + liveins: %r0, %r1, %r2, %r3 + + ; This is legal, so we should find it unchanged in the output + ; CHECK: [[FIVREG:%[0-9]+]](p0) = G_FRAME_INDEX %fixed-stack.[[FRAME_INDEX]] + ; CHECK: {{%[0-9]+}}(s32) = G_LOAD [[FIVREG]](p0) + %0(p0) = G_FRAME_INDEX %fixed-stack.2 + %1(s32) = G_LOAD %0(p0) + BX_RET 14, _ + +...