| //===- MipsCallLowering.cpp -------------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| /// \file | |
| /// This file implements the lowering of LLVM calls to machine code calls for | |
| /// GlobalISel. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "MipsCallLowering.h" | |
| #include "MipsCCState.h" | |
| #include "MipsMachineFunction.h" | |
| #include "MipsTargetMachine.h" | |
| #include "llvm/CodeGen/Analysis.h" | |
| #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" | |
| #include "llvm/CodeGen/MachineFrameInfo.h" | |
| using namespace llvm; | |
| MipsCallLowering::MipsCallLowering(const MipsTargetLowering &TLI) | |
| : CallLowering(&TLI) {} | |
| namespace { | |
| class MipsIncomingValueHandler : public CallLowering::IncomingValueHandler { | |
| const MipsSubtarget &STI; | |
| public: | |
| MipsIncomingValueHandler(MachineIRBuilder &MIRBuilder, | |
| MachineRegisterInfo &MRI) | |
| : IncomingValueHandler(MIRBuilder, MRI), | |
| STI(MIRBuilder.getMF().getSubtarget<MipsSubtarget>()) {} | |
| private: | |
| void assignValueToReg(Register ValVReg, Register PhysReg, | |
| const CCValAssign &VA, | |
| ISD::ArgFlagsTy Flags = {}) override; | |
| Register getStackAddress(uint64_t Size, int64_t Offset, | |
| MachinePointerInfo &MPO, | |
| ISD::ArgFlagsTy Flags) override; | |
| void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy, | |
| const MachinePointerInfo &MPO, | |
| const CCValAssign &VA) override; | |
| unsigned assignCustomValue(CallLowering::ArgInfo &Arg, | |
| ArrayRef<CCValAssign> VAs, | |
| std::function<void()> *Thunk = nullptr) override; | |
| virtual void markPhysRegUsed(unsigned PhysReg) { | |
| MIRBuilder.getMRI()->addLiveIn(PhysReg); | |
| MIRBuilder.getMBB().addLiveIn(PhysReg); | |
| } | |
| }; | |
| class CallReturnHandler : public MipsIncomingValueHandler { | |
| public: | |
| CallReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, | |
| MachineInstrBuilder &MIB) | |
| : MipsIncomingValueHandler(MIRBuilder, MRI), MIB(MIB) {} | |
| private: | |
| void markPhysRegUsed(unsigned PhysReg) override { | |
| MIB.addDef(PhysReg, RegState::Implicit); | |
| } | |
| MachineInstrBuilder &MIB; | |
| }; | |
| } // end anonymous namespace | |
| void MipsIncomingValueHandler::assignValueToReg(Register ValVReg, | |
| Register PhysReg, | |
| const CCValAssign &VA, | |
| ISD::ArgFlagsTy Flags) { | |
| markPhysRegUsed(PhysReg); | |
| IncomingValueHandler::assignValueToReg(ValVReg, PhysReg, VA); | |
| } | |
| Register MipsIncomingValueHandler::getStackAddress(uint64_t Size, | |
| int64_t Offset, | |
| MachinePointerInfo &MPO, | |
| ISD::ArgFlagsTy Flags) { | |
| MachineFunction &MF = MIRBuilder.getMF(); | |
| MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| // FIXME: This should only be immutable for non-byval memory arguments. | |
| int FI = MFI.CreateFixedObject(Size, Offset, true); | |
| MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI); | |
| return MIRBuilder.buildFrameIndex(LLT::pointer(0, 32), FI).getReg(0); | |
| } | |
| void MipsIncomingValueHandler::assignValueToAddress( | |
| Register ValVReg, Register Addr, LLT MemTy, const MachinePointerInfo &MPO, | |
| const CCValAssign &VA) { | |
| MachineFunction &MF = MIRBuilder.getMF(); | |
| auto MMO = MF.getMachineMemOperand(MPO, MachineMemOperand::MOLoad, MemTy, | |
| inferAlignFromPtrInfo(MF, MPO)); | |
| MIRBuilder.buildLoad(ValVReg, Addr, *MMO); | |
| } | |
| /// Handle cases when f64 is split into 2 32-bit GPRs. This is a custom | |
| /// assignment because generic code assumes getNumRegistersForCallingConv is | |
| /// accurate. In this case it is not because the type/number are context | |
| /// dependent on other arguments. | |
| unsigned | |
| MipsIncomingValueHandler::assignCustomValue(CallLowering::ArgInfo &Arg, | |
| ArrayRef<CCValAssign> VAs, | |
| std::function<void()> *Thunk) { | |
| const CCValAssign &VALo = VAs[0]; | |
| const CCValAssign &VAHi = VAs[1]; | |
| assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 && | |
| VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 && | |
| "unexpected custom value"); | |
| auto CopyLo = MIRBuilder.buildCopy(LLT::scalar(32), VALo.getLocReg()); | |
| auto CopyHi = MIRBuilder.buildCopy(LLT::scalar(32), VAHi.getLocReg()); | |
| if (!STI.isLittle()) | |
| std::swap(CopyLo, CopyHi); | |
| Arg.OrigRegs.assign(Arg.Regs.begin(), Arg.Regs.end()); | |
| Arg.Regs = { CopyLo.getReg(0), CopyHi.getReg(0) }; | |
| MIRBuilder.buildMergeLikeInstr(Arg.OrigRegs[0], {CopyLo, CopyHi}); | |
| markPhysRegUsed(VALo.getLocReg()); | |
| markPhysRegUsed(VAHi.getLocReg()); | |
| return 2; | |
| } | |
| namespace { | |
| class MipsOutgoingValueHandler : public CallLowering::OutgoingValueHandler { | |
| const MipsSubtarget &STI; | |
| public: | |
| MipsOutgoingValueHandler(MachineIRBuilder &MIRBuilder, | |
| MachineRegisterInfo &MRI, MachineInstrBuilder &MIB) | |
| : OutgoingValueHandler(MIRBuilder, MRI), | |
| STI(MIRBuilder.getMF().getSubtarget<MipsSubtarget>()), MIB(MIB) {} | |
| private: | |
| void assignValueToReg(Register ValVReg, Register PhysReg, | |
| const CCValAssign &VA, | |
| ISD::ArgFlagsTy Flags = {}) override; | |
| Register getStackAddress(uint64_t Size, int64_t Offset, | |
| MachinePointerInfo &MPO, | |
| ISD::ArgFlagsTy Flags) override; | |
| void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy, | |
| const MachinePointerInfo &MPO, | |
| const CCValAssign &VA) override; | |
| unsigned assignCustomValue(CallLowering::ArgInfo &Arg, | |
| ArrayRef<CCValAssign> VAs, | |
| std::function<void()> *Thunk) override; | |
| MachineInstrBuilder &MIB; | |
| }; | |
| } // end anonymous namespace | |
| void MipsOutgoingValueHandler::assignValueToReg(Register ValVReg, | |
| Register PhysReg, | |
| const CCValAssign &VA, | |
| ISD::ArgFlagsTy Flags) { | |
| Register ExtReg = extendRegister(ValVReg, VA); | |
| MIRBuilder.buildCopy(PhysReg, ExtReg); | |
| MIB.addUse(PhysReg, RegState::Implicit); | |
| } | |
| Register MipsOutgoingValueHandler::getStackAddress(uint64_t Size, | |
| int64_t Offset, | |
| MachinePointerInfo &MPO, | |
| ISD::ArgFlagsTy Flags) { | |
| MachineFunction &MF = MIRBuilder.getMF(); | |
| MPO = MachinePointerInfo::getStack(MF, Offset); | |
| LLT p0 = LLT::pointer(0, 32); | |
| LLT s32 = LLT::scalar(32); | |
| auto SPReg = MIRBuilder.buildCopy(p0, Register(Mips::SP)); | |
| auto OffsetReg = MIRBuilder.buildConstant(s32, Offset); | |
| auto AddrReg = MIRBuilder.buildPtrAdd(p0, SPReg, OffsetReg); | |
| return AddrReg.getReg(0); | |
| } | |
| void MipsOutgoingValueHandler::assignValueToAddress( | |
| Register ValVReg, Register Addr, LLT MemTy, const MachinePointerInfo &MPO, | |
| const CCValAssign &VA) { | |
| MachineFunction &MF = MIRBuilder.getMF(); | |
| uint64_t LocMemOffset = VA.getLocMemOffset(); | |
| auto MMO = MF.getMachineMemOperand( | |
| MPO, MachineMemOperand::MOStore, MemTy, | |
| commonAlignment(STI.getStackAlignment(), LocMemOffset)); | |
| Register ExtReg = extendRegister(ValVReg, VA); | |
| MIRBuilder.buildStore(ExtReg, Addr, *MMO); | |
| } | |
| unsigned | |
| MipsOutgoingValueHandler::assignCustomValue(CallLowering::ArgInfo &Arg, | |
| ArrayRef<CCValAssign> VAs, | |
| std::function<void()> *Thunk) { | |
| const CCValAssign &VALo = VAs[0]; | |
| const CCValAssign &VAHi = VAs[1]; | |
| assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 && | |
| VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 && | |
| "unexpected custom value"); | |
| auto Unmerge = | |
| MIRBuilder.buildUnmerge({LLT::scalar(32), LLT::scalar(32)}, Arg.Regs[0]); | |
| Register Lo = Unmerge.getReg(0); | |
| Register Hi = Unmerge.getReg(1); | |
| Arg.OrigRegs.assign(Arg.Regs.begin(), Arg.Regs.end()); | |
| Arg.Regs = { Lo, Hi }; | |
| if (!STI.isLittle()) | |
| std::swap(Lo, Hi); | |
| MIB.addUse(VALo.getLocReg(), RegState::Implicit); | |
| MIB.addUse(VAHi.getLocReg(), RegState::Implicit); | |
| // If we can return a thunk, just include the register copies. The unmerge can | |
| // be emitted earlier. | |
| if (Thunk) { | |
| *Thunk = [=]() { | |
| MIRBuilder.buildCopy(VALo.getLocReg(), Lo); | |
| MIRBuilder.buildCopy(VAHi.getLocReg(), Hi); | |
| }; | |
| return 2; | |
| } | |
| MIRBuilder.buildCopy(VALo.getLocReg(), Lo); | |
| MIRBuilder.buildCopy(VAHi.getLocReg(), Hi); | |
| return 2; | |
| } | |
| static bool isSupportedArgumentType(Type *T) { | |
| if (T->isIntegerTy()) | |
| return true; | |
| if (T->isPointerTy()) | |
| return true; | |
| if (T->isFloatingPointTy()) | |
| return true; | |
| return false; | |
| } | |
| static bool isSupportedReturnType(Type *T) { | |
| if (T->isIntegerTy()) | |
| return true; | |
| if (T->isPointerTy()) | |
| return true; | |
| if (T->isFloatingPointTy()) | |
| return true; | |
| if (T->isAggregateType()) | |
| return true; | |
| return false; | |
| } | |
| bool MipsCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, | |
| const Value *Val, ArrayRef<Register> VRegs, | |
| FunctionLoweringInfo &FLI) const { | |
| MachineInstrBuilder Ret = MIRBuilder.buildInstrNoInsert(Mips::RetRA); | |
| if (Val != nullptr && !isSupportedReturnType(Val->getType())) | |
| return false; | |
| if (!VRegs.empty()) { | |
| MachineFunction &MF = MIRBuilder.getMF(); | |
| const Function &F = MF.getFunction(); | |
| const DataLayout &DL = MF.getDataLayout(); | |
| const MipsTargetLowering &TLI = *getTLI<MipsTargetLowering>(); | |
| SmallVector<ArgInfo, 8> RetInfos; | |
| ArgInfo ArgRetInfo(VRegs, *Val, 0); | |
| setArgFlags(ArgRetInfo, AttributeList::ReturnIndex, DL, F); | |
| splitToValueTypes(ArgRetInfo, RetInfos, DL, F.getCallingConv()); | |
| SmallVector<CCValAssign, 16> ArgLocs; | |
| MipsCCState CCInfo(F.getCallingConv(), F.isVarArg(), MF, ArgLocs, | |
| F.getContext()); | |
| MipsOutgoingValueHandler RetHandler(MIRBuilder, MF.getRegInfo(), Ret); | |
| OutgoingValueAssigner Assigner(TLI.CCAssignFnForReturn()); | |
| if (!determineAssignments(Assigner, RetInfos, CCInfo)) | |
| return false; | |
| if (!handleAssignments(RetHandler, RetInfos, CCInfo, ArgLocs, MIRBuilder)) | |
| return false; | |
| } | |
| MIRBuilder.insertInstr(Ret); | |
| return true; | |
| } | |
| bool MipsCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, | |
| const Function &F, | |
| ArrayRef<ArrayRef<Register>> VRegs, | |
| FunctionLoweringInfo &FLI) const { | |
| // Quick exit if there aren't any args. | |
| if (F.arg_empty()) | |
| return true; | |
| for (auto &Arg : F.args()) { | |
| if (!isSupportedArgumentType(Arg.getType())) | |
| return false; | |
| } | |
| MachineFunction &MF = MIRBuilder.getMF(); | |
| const DataLayout &DL = MF.getDataLayout(); | |
| const MipsTargetLowering &TLI = *getTLI<MipsTargetLowering>(); | |
| SmallVector<ArgInfo, 8> ArgInfos; | |
| unsigned i = 0; | |
| for (auto &Arg : F.args()) { | |
| ArgInfo AInfo(VRegs[i], Arg, i); | |
| setArgFlags(AInfo, i + AttributeList::FirstArgIndex, DL, F); | |
| splitToValueTypes(AInfo, ArgInfos, DL, F.getCallingConv()); | |
| ++i; | |
| } | |
| SmallVector<CCValAssign, 16> ArgLocs; | |
| MipsCCState CCInfo(F.getCallingConv(), F.isVarArg(), MF, ArgLocs, | |
| F.getContext()); | |
| const MipsABIInfo &ABI = MF.getSubtarget<MipsSubtarget>().getABI(); | |
| CCInfo.AllocateStack(ABI.GetCalleeAllocdArgSizeInBytes(F.getCallingConv()), | |
| Align(1)); | |
| IncomingValueAssigner Assigner(TLI.CCAssignFnForCall()); | |
| if (!determineAssignments(Assigner, ArgInfos, CCInfo)) | |
| return false; | |
| MipsIncomingValueHandler Handler(MIRBuilder, MF.getRegInfo()); | |
| if (!handleAssignments(Handler, ArgInfos, CCInfo, ArgLocs, MIRBuilder)) | |
| return false; | |
| if (F.isVarArg()) { | |
| ArrayRef<MCPhysReg> ArgRegs = | |
| ABI.getVarArgRegs(MF.getSubtarget<MipsSubtarget>().isGP64bit()); | |
| unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs); | |
| int VaArgOffset; | |
| unsigned RegSize = 4; | |
| if (ArgRegs.size() == Idx) | |
| VaArgOffset = alignTo(CCInfo.getStackSize(), RegSize); | |
| else { | |
| VaArgOffset = | |
| (int)ABI.GetCalleeAllocdArgSizeInBytes(CCInfo.getCallingConv()) - | |
| (int)(RegSize * (ArgRegs.size() - Idx)); | |
| } | |
| MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| int FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true); | |
| MF.getInfo<MipsFunctionInfo>()->setVarArgsFrameIndex(FI); | |
| for (unsigned I = Idx; I < ArgRegs.size(); ++I, VaArgOffset += RegSize) { | |
| MIRBuilder.getMBB().addLiveIn(ArgRegs[I]); | |
| LLT RegTy = LLT::scalar(RegSize * 8); | |
| MachineInstrBuilder Copy = | |
| MIRBuilder.buildCopy(RegTy, Register(ArgRegs[I])); | |
| FI = MFI.CreateFixedObject(RegSize, VaArgOffset, true); | |
| MachinePointerInfo MPO = MachinePointerInfo::getFixedStack(MF, FI); | |
| const LLT PtrTy = LLT::pointer(MPO.getAddrSpace(), 32); | |
| auto FrameIndex = MIRBuilder.buildFrameIndex(PtrTy, FI); | |
| MachineMemOperand *MMO = MF.getMachineMemOperand( | |
| MPO, MachineMemOperand::MOStore, RegTy, Align(RegSize)); | |
| MIRBuilder.buildStore(Copy, FrameIndex, *MMO); | |
| } | |
| } | |
| return true; | |
| } | |
| bool MipsCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, | |
| CallLoweringInfo &Info) const { | |
| if (Info.CallConv != CallingConv::C) | |
| return false; | |
| for (auto &Arg : Info.OrigArgs) { | |
| if (!isSupportedArgumentType(Arg.Ty)) | |
| return false; | |
| if (Arg.Flags[0].isByVal()) | |
| return false; | |
| if (Arg.Flags[0].isSRet() && !Arg.Ty->isPointerTy()) | |
| return false; | |
| } | |
| if (!Info.OrigRet.Ty->isVoidTy() && !isSupportedReturnType(Info.OrigRet.Ty)) | |
| return false; | |
| MachineFunction &MF = MIRBuilder.getMF(); | |
| const Function &F = MF.getFunction(); | |
| const DataLayout &DL = MF.getDataLayout(); | |
| const MipsTargetLowering &TLI = *getTLI<MipsTargetLowering>(); | |
| const MipsTargetMachine &TM = | |
| static_cast<const MipsTargetMachine &>(MF.getTarget()); | |
| const MipsABIInfo &ABI = MF.getSubtarget<MipsSubtarget>().getABI(); | |
| MachineInstrBuilder CallSeqStart = | |
| MIRBuilder.buildInstr(Mips::ADJCALLSTACKDOWN); | |
| const bool IsCalleeGlobalPIC = | |
| Info.Callee.isGlobal() && TM.isPositionIndependent(); | |
| MachineInstrBuilder MIB = MIRBuilder.buildInstrNoInsert( | |
| Info.Callee.isReg() || IsCalleeGlobalPIC ? Mips::JALRPseudo : Mips::JAL); | |
| MIB.setOperandDead(0); // implicit-def $ra | |
| MIB.addDef(Mips::SP, RegState::Implicit); | |
| if (IsCalleeGlobalPIC) { | |
| Register CalleeReg = | |
| MF.getRegInfo().createGenericVirtualRegister(LLT::pointer(0, 32)); | |
| MachineInstr *CalleeGlobalValue = | |
| MIRBuilder.buildGlobalValue(CalleeReg, Info.Callee.getGlobal()); | |
| if (!Info.Callee.getGlobal()->hasLocalLinkage()) | |
| CalleeGlobalValue->getOperand(1).setTargetFlags(MipsII::MO_GOT_CALL); | |
| MIB.addUse(CalleeReg); | |
| } else | |
| MIB.add(Info.Callee); | |
| const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); | |
| MIB.addRegMask(TRI->getCallPreservedMask(MF, Info.CallConv)); | |
| TargetLowering::ArgListTy FuncOrigArgs; | |
| FuncOrigArgs.reserve(Info.OrigArgs.size()); | |
| SmallVector<ArgInfo, 8> ArgInfos; | |
| for (auto &Arg : Info.OrigArgs) | |
| splitToValueTypes(Arg, ArgInfos, DL, Info.CallConv); | |
| SmallVector<CCValAssign, 8> ArgLocs; | |
| bool IsCalleeVarArg = false; | |
| if (Info.Callee.isGlobal()) { | |
| const Function *CF = static_cast<const Function *>(Info.Callee.getGlobal()); | |
| IsCalleeVarArg = CF->isVarArg(); | |
| } | |
| // FIXME: Should use MipsCCState::getSpecialCallingConvForCallee, but it | |
| // depends on looking directly at the call target. | |
| MipsCCState CCInfo(Info.CallConv, IsCalleeVarArg, MF, ArgLocs, | |
| F.getContext()); | |
| CCInfo.AllocateStack(ABI.GetCalleeAllocdArgSizeInBytes(Info.CallConv), | |
| Align(1)); | |
| OutgoingValueAssigner Assigner(TLI.CCAssignFnForCall()); | |
| if (!determineAssignments(Assigner, ArgInfos, CCInfo)) | |
| return false; | |
| MipsOutgoingValueHandler ArgHandler(MIRBuilder, MF.getRegInfo(), MIB); | |
| if (!handleAssignments(ArgHandler, ArgInfos, CCInfo, ArgLocs, MIRBuilder)) | |
| return false; | |
| unsigned StackSize = CCInfo.getStackSize(); | |
| unsigned StackAlignment = F.getParent()->getOverrideStackAlignment(); | |
| if (!StackAlignment) { | |
| const TargetFrameLowering *TFL = MF.getSubtarget().getFrameLowering(); | |
| StackAlignment = TFL->getStackAlignment(); | |
| } | |
| StackSize = alignTo(StackSize, StackAlignment); | |
| CallSeqStart.addImm(StackSize).addImm(0); | |
| if (IsCalleeGlobalPIC) { | |
| MIRBuilder.buildCopy( | |
| Register(Mips::GP), | |
| MF.getInfo<MipsFunctionInfo>()->getGlobalBaseRegForGlobalISel(MF)); | |
| MIB.addDef(Mips::GP, RegState::Implicit); | |
| } | |
| MIRBuilder.insertInstr(MIB); | |
| if (MIB->getOpcode() == Mips::JALRPseudo) { | |
| const MipsSubtarget &STI = MIRBuilder.getMF().getSubtarget<MipsSubtarget>(); | |
| MIB.constrainAllUses(MIRBuilder.getTII(), *STI.getRegisterInfo(), | |
| *STI.getRegBankInfo()); | |
| } | |
| if (!Info.OrigRet.Ty->isVoidTy()) { | |
| ArgInfos.clear(); | |
| CallLowering::splitToValueTypes(Info.OrigRet, ArgInfos, DL, | |
| F.getCallingConv()); | |
| SmallVector<CCValAssign, 8> ArgLocs; | |
| IncomingValueAssigner Assigner(TLI.CCAssignFnForReturn()); | |
| CallReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), MIB); | |
| MipsCCState CCInfo(F.getCallingConv(), F.isVarArg(), MF, ArgLocs, | |
| F.getContext()); | |
| if (!determineAssignments(Assigner, ArgInfos, CCInfo)) | |
| return false; | |
| if (!handleAssignments(RetHandler, ArgInfos, CCInfo, ArgLocs, MIRBuilder)) | |
| return false; | |
| } | |
| MIRBuilder.buildInstr(Mips::ADJCALLSTACKUP).addImm(StackSize).addImm(0); | |
| return true; | |
| } |