//===-- llvm/lib/Target/AArch64/AArch64CallLowering.cpp - Call lowering ---===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// This file implements the lowering of LLVM calls to machine code calls for /// GlobalISel. /// //===----------------------------------------------------------------------===// #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 #error "This shouldn't be built without GISel" #endif 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 &SplitArgs, const DataLayout &DL, MachineRegisterInfo &MRI, SplitArgTy PerformArgSplit) const { const AArch64TargetLowering &TLI = *getTLI(); LLVMContext &Ctx = OrigArg.Ty->getContext(); SmallVector SplitVTs; SmallVector 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 BitOffsets; for (auto Offset : Offsets) BitOffsets.push_back(Offset * 8); SmallVector 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 { 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) { const AArch64TargetLowering &TLI = *getTLI(); 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 SplitArgs; splitToValueTypes(OrigArg, SplitArgs, DL, MRI, [&](ArrayRef Regs, ArrayRef 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 VRegs) const { auto &Args = F.getArgumentList(); MachineFunction &MF = MIRBuilder.getMF(); MachineBasicBlock &MBB = MIRBuilder.getMBB(); MachineRegisterInfo &MRI = MF.getRegInfo(); auto &DL = F.getParent()->getDataLayout(); SmallVector 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 Regs, ArrayRef Offsets) { MIRBuilder.buildSequence(VRegs[i], Regs, Offsets); }); ++i; } if (!MBB.empty()) MIRBuilder.setInstr(*MBB.begin()); const AArch64TargetLowering &TLI = *getTLI(); 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::lowerCall(MachineIRBuilder &MIRBuilder, const MachineOperand &Callee, const ArgInfo &OrigRet, ArrayRef OrigArgs) const { MachineFunction &MF = MIRBuilder.getMF(); const Function &F = *MF.getFunction(); MachineRegisterInfo &MRI = MF.getRegInfo(); auto &DL = F.getParent()->getDataLayout(); SmallVector SplitArgs; for (auto &OrigArg : OrigArgs) { splitToValueTypes(OrigArg, SplitArgs, DL, MRI, [&](ArrayRef Regs, ArrayRef Offsets) { MIRBuilder.buildExtract(Regs, Offsets, OrigArg.Reg); }); } // Find out which ABI gets to decide where things go. const AArch64TargetLowering &TLI = *getTLI(); CCAssignFn *CallAssignFn = TLI.CCAssignFnForCall(F.getCallingConv(), /*IsVarArg=*/false); // 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); // 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 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 RegOffsets; SmallVector SplitRegs; splitToValueTypes(OrigRet, SplitArgs, DL, MRI, [&](ArrayRef Regs, ArrayRef 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; }