diff options
Diffstat (limited to 'lib/Target/AArch64/AArch64CallLowering.cpp')
| -rw-r--r-- | lib/Target/AArch64/AArch64CallLowering.cpp | 325 |
1 files changed, 269 insertions, 56 deletions
diff --git a/lib/Target/AArch64/AArch64CallLowering.cpp b/lib/Target/AArch64/AArch64CallLowering.cpp index e3522e63c21c..a4950af32097 100644 --- a/lib/Target/AArch64/AArch64CallLowering.cpp +++ b/lib/Target/AArch64/AArch64CallLowering.cpp @@ -16,9 +16,14 @@ #include "AArch64CallLowering.h" #include "AArch64ISelLowering.h" +#include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" +#include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineInstrBuilder.h" - +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Target/TargetRegisterInfo.h" +#include "llvm/Target/TargetSubtargetInfo.h" using namespace llvm; #ifndef LLVM_BUILD_GLOBAL_ISEL @@ -29,76 +34,284 @@ AArch64CallLowering::AArch64CallLowering(const AArch64TargetLowering &TLI) : CallLowering(&TLI) { } +struct IncomingArgHandler : public CallLowering::ValueHandler { + IncomingArgHandler(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, 64)); + MIRBuilder.buildFrameIndex(AddrReg, FI); + return AddrReg; + } + + void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + CCValAssign &VA) override { + markPhysRegUsed(PhysReg); + MIRBuilder.buildCopy(ValVReg, PhysReg); + // FIXME: assert extension + } + + void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOLoad | MachineMemOperand::MOInvariant, Size, + 0); + MIRBuilder.buildLoad(ValVReg, Addr, *MMO); + } + + /// How the physical register gets marked varies between formal + /// parameters (it's a basic-block live-in), and a call instruction + /// (it's an implicit-def of the BL). + virtual void markPhysRegUsed(unsigned PhysReg) = 0; +}; + +struct FormalArgHandler : public IncomingArgHandler { + FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI) + : IncomingArgHandler(MIRBuilder, MRI) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIRBuilder.getMBB().addLiveIn(PhysReg); + } +}; + +struct CallReturnHandler : public IncomingArgHandler { + CallReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder MIB) + : IncomingArgHandler(MIRBuilder, MRI), MIB(MIB) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIB.addDef(PhysReg, RegState::Implicit); + } + + MachineInstrBuilder MIB; +}; + +struct OutgoingArgHandler : public CallLowering::ValueHandler { + OutgoingArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder MIB) + : ValueHandler(MIRBuilder, MRI), MIB(MIB) {} + + unsigned getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + LLT p0 = LLT::pointer(0, 64); + LLT s64 = LLT::scalar(64); + unsigned SPReg = MRI.createGenericVirtualRegister(p0); + MIRBuilder.buildCopy(SPReg, AArch64::SP); + + unsigned OffsetReg = MRI.createGenericVirtualRegister(s64); + MIRBuilder.buildConstant(OffsetReg, Offset); + + unsigned AddrReg = MRI.createGenericVirtualRegister(p0); + MIRBuilder.buildGEP(AddrReg, SPReg, OffsetReg); + + MPO = MachinePointerInfo::getStack(MIRBuilder.getMF(), Offset); + return AddrReg; + } + + void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + CCValAssign &VA) override { + MIB.addUse(PhysReg, RegState::Implicit); + unsigned ExtReg = extendRegister(ValVReg, VA); + MIRBuilder.buildCopy(PhysReg, ExtReg); + } + + void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOStore, Size, 0); + MIRBuilder.buildStore(ValVReg, Addr, *MMO); + } + + MachineInstrBuilder MIB; +}; + +void AArch64CallLowering::splitToValueTypes(const ArgInfo &OrigArg, + SmallVectorImpl<ArgInfo> &SplitArgs, + const DataLayout &DL, + MachineRegisterInfo &MRI, + SplitArgTy PerformArgSplit) const { + const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>(); + LLVMContext &Ctx = OrigArg.Ty->getContext(); + + SmallVector<EVT, 4> SplitVTs; + SmallVector<uint64_t, 4> Offsets; + ComputeValueVTs(TLI, DL, OrigArg.Ty, SplitVTs, &Offsets, 0); + + if (SplitVTs.size() == 1) { + // No splitting to do, but we want to replace the original type (e.g. [1 x + // double] -> double). + SplitArgs.emplace_back(OrigArg.Reg, SplitVTs[0].getTypeForEVT(Ctx), + OrigArg.Flags); + return; + } + + unsigned FirstRegIdx = SplitArgs.size(); + for (auto SplitVT : SplitVTs) { + // FIXME: set split flags if they're actually used (e.g. i128 on AAPCS). + Type *SplitTy = SplitVT.getTypeForEVT(Ctx); + SplitArgs.push_back( + ArgInfo{MRI.createGenericVirtualRegister(LLT{*SplitTy, DL}), SplitTy, + OrigArg.Flags}); + } + + SmallVector<uint64_t, 4> BitOffsets; + for (auto Offset : Offsets) + BitOffsets.push_back(Offset * 8); + + SmallVector<unsigned, 8> SplitRegs; + for (auto I = &SplitArgs[FirstRegIdx]; I != SplitArgs.end(); ++I) + SplitRegs.push_back(I->Reg); + + PerformArgSplit(SplitRegs, BitOffsets); +} + bool AArch64CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, - const Value *Val, unsigned VReg) const { - MachineInstr *Return = MIRBuilder.buildInstr(AArch64::RET_ReallyLR); - assert(Return && "Unable to build a return instruction?!"); + const Value *Val, unsigned VReg) const { + MachineFunction &MF = MIRBuilder.getMF(); + const Function &F = *MF.getFunction(); + auto MIB = MIRBuilder.buildInstrNoInsert(AArch64::RET_ReallyLR); assert(((Val && VReg) || (!Val && !VReg)) && "Return value without a vreg"); + bool Success = true; if (VReg) { - assert(Val->getType()->isIntegerTy() && "Type not supported yet"); - unsigned Size = Val->getType()->getPrimitiveSizeInBits(); - assert((Size == 64 || Size == 32) && "Size not supported yet"); - unsigned ResReg = (Size == 32) ? AArch64::W0 : AArch64::X0; - // Set the insertion point to be right before Return. - MIRBuilder.setInstr(*Return, /* Before */ true); - MachineInstr *Copy = - MIRBuilder.buildInstr(TargetOpcode::COPY, ResReg, VReg); - (void)Copy; - assert(Copy->getNextNode() == Return && - "The insertion did not happen where we expected"); - MachineInstrBuilder(MIRBuilder.getMF(), Return) - .addReg(ResReg, RegState::Implicit); + const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>(); + CCAssignFn *AssignFn = TLI.CCAssignFnForReturn(F.getCallingConv()); + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &DL = F.getParent()->getDataLayout(); + + ArgInfo OrigArg{VReg, Val->getType()}; + setArgFlags(OrigArg, AttributeSet::ReturnIndex, DL, F); + + SmallVector<ArgInfo, 8> SplitArgs; + splitToValueTypes(OrigArg, SplitArgs, DL, MRI, + [&](ArrayRef<unsigned> Regs, ArrayRef<uint64_t> Offsets) { + MIRBuilder.buildExtract(Regs, Offsets, VReg); + }); + + OutgoingArgHandler Handler(MIRBuilder, MRI, MIB); + Success = handleAssignments(MIRBuilder, AssignFn, SplitArgs, Handler); + } + + MIRBuilder.insertInstr(MIB); + return Success; +} + +bool AArch64CallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, + const Function &F, + ArrayRef<unsigned> VRegs) const { + auto &Args = F.getArgumentList(); + MachineFunction &MF = MIRBuilder.getMF(); + MachineBasicBlock &MBB = MIRBuilder.getMBB(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &DL = F.getParent()->getDataLayout(); + + SmallVector<ArgInfo, 8> SplitArgs; + unsigned i = 0; + for (auto &Arg : Args) { + ArgInfo OrigArg{VRegs[i], Arg.getType()}; + setArgFlags(OrigArg, i + 1, DL, F); + splitToValueTypes(OrigArg, SplitArgs, DL, MRI, + [&](ArrayRef<unsigned> Regs, ArrayRef<uint64_t> Offsets) { + MIRBuilder.buildSequence(VRegs[i], Regs, Offsets); + }); + ++i; } + + if (!MBB.empty()) + MIRBuilder.setInstr(*MBB.begin()); + + const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>(); + CCAssignFn *AssignFn = + TLI.CCAssignFnForCall(F.getCallingConv(), /*IsVarArg=*/false); + + FormalArgHandler Handler(MIRBuilder, MRI); + if (!handleAssignments(MIRBuilder, AssignFn, SplitArgs, Handler)) + return false; + + // Move back to the end of the basic block. + MIRBuilder.setMBB(MBB); + return true; } -bool AArch64CallLowering::lowerFormalArguments( - MachineIRBuilder &MIRBuilder, const Function::ArgumentListType &Args, - const SmallVectorImpl<unsigned> &VRegs) const { +bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, + const MachineOperand &Callee, + const ArgInfo &OrigRet, + ArrayRef<ArgInfo> OrigArgs) const { MachineFunction &MF = MIRBuilder.getMF(); const Function &F = *MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &DL = F.getParent()->getDataLayout(); - SmallVector<CCValAssign, 16> ArgLocs; - CCState CCInfo(F.getCallingConv(), F.isVarArg(), MF, ArgLocs, F.getContext()); + SmallVector<ArgInfo, 8> SplitArgs; + for (auto &OrigArg : OrigArgs) { + splitToValueTypes(OrigArg, SplitArgs, DL, MRI, + [&](ArrayRef<unsigned> Regs, ArrayRef<uint64_t> Offsets) { + MIRBuilder.buildExtract(Regs, Offsets, OrigArg.Reg); + }); + } - unsigned NumArgs = Args.size(); - Function::const_arg_iterator CurOrigArg = Args.begin(); + // Find out which ABI gets to decide where things go. const AArch64TargetLowering &TLI = *getTLI<AArch64TargetLowering>(); - for (unsigned i = 0; i != NumArgs; ++i, ++CurOrigArg) { - MVT ValVT = MVT::getVT(CurOrigArg->getType()); - CCAssignFn *AssignFn = - TLI.CCAssignFnForCall(F.getCallingConv(), /*IsVarArg=*/false); - bool Res = - AssignFn(i, ValVT, ValVT, CCValAssign::Full, ISD::ArgFlagsTy(), CCInfo); - assert(!Res && "Call operand has unhandled type"); - (void)Res; - } - assert(ArgLocs.size() == Args.size() && - "We have a different number of location and args?!"); - for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { - CCValAssign &VA = ArgLocs[i]; + CCAssignFn *CallAssignFn = + TLI.CCAssignFnForCall(F.getCallingConv(), /*IsVarArg=*/false); - assert(VA.isRegLoc() && "Not yet implemented"); - // Transform the arguments in physical registers into virtual ones. - MIRBuilder.getMBB().addLiveIn(VA.getLocReg()); - MIRBuilder.buildInstr(TargetOpcode::COPY, VRegs[i], VA.getLocReg()); + // Create a temporarily-floating call instruction so we can add the implicit + // uses of arg registers. + auto MIB = MIRBuilder.buildInstrNoInsert(Callee.isReg() ? AArch64::BLR + : AArch64::BL); + MIB.addOperand(Callee); - switch (VA.getLocInfo()) { - default: - llvm_unreachable("Unknown loc info!"); - case CCValAssign::Full: - break; - case CCValAssign::BCvt: - // We don't care about bitcast. - break; - case CCValAssign::AExt: - case CCValAssign::SExt: - case CCValAssign::ZExt: - // Zero/Sign extend the register. - assert(0 && "Not yet implemented"); - break; - } + // Tell the call which registers are clobbered. + auto TRI = MF.getSubtarget().getRegisterInfo(); + MIB.addRegMask(TRI->getCallPreservedMask(MF, F.getCallingConv())); + + // Do the actual argument marshalling. + SmallVector<unsigned, 8> PhysRegs; + OutgoingArgHandler Handler(MIRBuilder, MRI, MIB); + if (!handleAssignments(MIRBuilder, CallAssignFn, SplitArgs, Handler)) + return false; + + // Now we can add the actual call instruction to the correct basic block. + MIRBuilder.insertInstr(MIB); + + // If Callee is a reg, since it is used by a target specific + // instruction, it must have a register class matching the + // constraint of that instruction. + if (Callee.isReg()) + MIB->getOperand(0).setReg(constrainOperandRegClass( + MF, *TRI, MRI, *MF.getSubtarget().getInstrInfo(), + *MF.getSubtarget().getRegBankInfo(), *MIB, MIB->getDesc(), + Callee.getReg(), 0)); + + // Finally we can copy the returned value back into its virtual-register. In + // symmetry with the arugments, the physical register must be an + // implicit-define of the call instruction. + CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(F.getCallingConv()); + if (OrigRet.Reg) { + SplitArgs.clear(); + + SmallVector<uint64_t, 8> RegOffsets; + SmallVector<unsigned, 8> SplitRegs; + splitToValueTypes(OrigRet, SplitArgs, DL, MRI, + [&](ArrayRef<unsigned> Regs, ArrayRef<uint64_t> Offsets) { + std::copy(Offsets.begin(), Offsets.end(), + std::back_inserter(RegOffsets)); + std::copy(Regs.begin(), Regs.end(), + std::back_inserter(SplitRegs)); + }); + + CallReturnHandler Handler(MIRBuilder, MRI, MIB); + if (!handleAssignments(MIRBuilder, RetAssignFn, SplitArgs, Handler)) + return false; + + if (!RegOffsets.empty()) + MIRBuilder.buildSequence(OrigRet.Reg, SplitRegs, RegOffsets); } + return true; } |
