631 lines
24 KiB
C++
631 lines
24 KiB
C++
//===-- RISCVCallLowering.cpp - Call lowering -------------------*- 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 "RISCVCallLowering.h"
|
|
#include "RISCVISelLowering.h"
|
|
#include "RISCVMachineFunctionInfo.h"
|
|
#include "RISCVSubtarget.h"
|
|
#include "llvm/CodeGen/Analysis.h"
|
|
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
struct RISCVOutgoingValueAssigner : public CallLowering::OutgoingValueAssigner {
|
|
private:
|
|
// The function used internally to assign args - we ignore the AssignFn stored
|
|
// by OutgoingValueAssigner since RISC-V implements its CC using a custom
|
|
// function with a different signature.
|
|
RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn;
|
|
|
|
// Whether this is assigning args for a return.
|
|
bool IsRet;
|
|
|
|
public:
|
|
RISCVOutgoingValueAssigner(
|
|
RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn_, bool IsRet)
|
|
: CallLowering::OutgoingValueAssigner(nullptr),
|
|
RISCVAssignFn(RISCVAssignFn_), IsRet(IsRet) {}
|
|
|
|
bool assignArg(unsigned ValNo, EVT OrigVT, MVT ValVT, MVT LocVT,
|
|
CCValAssign::LocInfo LocInfo,
|
|
const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags,
|
|
CCState &State) override {
|
|
MachineFunction &MF = State.getMachineFunction();
|
|
const DataLayout &DL = MF.getDataLayout();
|
|
const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>();
|
|
|
|
if (RISCVAssignFn(DL, Subtarget.getTargetABI(), ValNo, ValVT, LocVT,
|
|
LocInfo, Flags, State, Info.IsFixed, IsRet, Info.Ty,
|
|
*Subtarget.getTargetLowering(),
|
|
/*FirstMaskArgument=*/std::nullopt))
|
|
return true;
|
|
|
|
StackSize = State.getStackSize();
|
|
return false;
|
|
}
|
|
};
|
|
|
|
struct RISCVOutgoingValueHandler : public CallLowering::OutgoingValueHandler {
|
|
RISCVOutgoingValueHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI,
|
|
MachineInstrBuilder MIB)
|
|
: OutgoingValueHandler(B, MRI), MIB(MIB),
|
|
Subtarget(MIRBuilder.getMF().getSubtarget<RISCVSubtarget>()) {}
|
|
Register getStackAddress(uint64_t MemSize, int64_t Offset,
|
|
MachinePointerInfo &MPO,
|
|
ISD::ArgFlagsTy Flags) override {
|
|
MachineFunction &MF = MIRBuilder.getMF();
|
|
LLT p0 = LLT::pointer(0, Subtarget.getXLen());
|
|
LLT sXLen = LLT::scalar(Subtarget.getXLen());
|
|
|
|
if (!SPReg)
|
|
SPReg = MIRBuilder.buildCopy(p0, Register(RISCV::X2)).getReg(0);
|
|
|
|
auto OffsetReg = MIRBuilder.buildConstant(sXLen, Offset);
|
|
|
|
auto AddrReg = MIRBuilder.buildPtrAdd(p0, SPReg, OffsetReg);
|
|
|
|
MPO = MachinePointerInfo::getStack(MF, Offset);
|
|
return AddrReg.getReg(0);
|
|
}
|
|
|
|
void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
|
|
const MachinePointerInfo &MPO,
|
|
const CCValAssign &VA) override {
|
|
MachineFunction &MF = MIRBuilder.getMF();
|
|
uint64_t LocMemOffset = VA.getLocMemOffset();
|
|
|
|
// TODO: Move StackAlignment to subtarget and share with FrameLowering.
|
|
auto MMO =
|
|
MF.getMachineMemOperand(MPO, MachineMemOperand::MOStore, MemTy,
|
|
commonAlignment(Align(16), LocMemOffset));
|
|
|
|
Register ExtReg = extendRegister(ValVReg, VA);
|
|
MIRBuilder.buildStore(ExtReg, Addr, *MMO);
|
|
}
|
|
|
|
void assignValueToReg(Register ValVReg, Register PhysReg,
|
|
const CCValAssign &VA) override {
|
|
// If we're passing an f32 value into an i64, anyextend before copying.
|
|
if (VA.getLocVT() == MVT::i64 && VA.getValVT() == MVT::f32)
|
|
ValVReg = MIRBuilder.buildAnyExt(LLT::scalar(64), ValVReg).getReg(0);
|
|
|
|
Register ExtReg = extendRegister(ValVReg, VA);
|
|
MIRBuilder.buildCopy(PhysReg, ExtReg);
|
|
MIB.addUse(PhysReg, RegState::Implicit);
|
|
}
|
|
|
|
unsigned assignCustomValue(CallLowering::ArgInfo &Arg,
|
|
ArrayRef<CCValAssign> VAs,
|
|
std::function<void()> *Thunk) override {
|
|
assert(VAs.size() >= 2 && "Expected at least 2 VAs.");
|
|
const CCValAssign &VALo = VAs[0];
|
|
const CCValAssign &VAHi = VAs[1];
|
|
|
|
assert(VAHi.needsCustom() && "Value doesn't need custom handling");
|
|
assert(VALo.getValNo() == VAHi.getValNo() &&
|
|
"Values belong to different arguments");
|
|
|
|
assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
|
|
VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
|
|
"unexpected custom value");
|
|
|
|
Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
|
|
MRI.createGenericVirtualRegister(LLT::scalar(32))};
|
|
MIRBuilder.buildUnmerge(NewRegs, Arg.Regs[0]);
|
|
|
|
if (VAHi.isMemLoc()) {
|
|
LLT MemTy(VAHi.getLocVT());
|
|
|
|
MachinePointerInfo MPO;
|
|
Register StackAddr = getStackAddress(
|
|
MemTy.getSizeInBytes(), VAHi.getLocMemOffset(), MPO, Arg.Flags[0]);
|
|
|
|
assignValueToAddress(NewRegs[1], StackAddr, MemTy, MPO,
|
|
const_cast<CCValAssign &>(VAHi));
|
|
}
|
|
|
|
auto assignFunc = [=]() {
|
|
assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo);
|
|
if (VAHi.isRegLoc())
|
|
assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi);
|
|
};
|
|
|
|
if (Thunk) {
|
|
*Thunk = assignFunc;
|
|
return 2;
|
|
}
|
|
|
|
assignFunc();
|
|
return 2;
|
|
}
|
|
|
|
private:
|
|
MachineInstrBuilder MIB;
|
|
|
|
// Cache the SP register vreg if we need it more than once in this call site.
|
|
Register SPReg;
|
|
|
|
const RISCVSubtarget &Subtarget;
|
|
};
|
|
|
|
struct RISCVIncomingValueAssigner : public CallLowering::IncomingValueAssigner {
|
|
private:
|
|
// The function used internally to assign args - we ignore the AssignFn stored
|
|
// by IncomingValueAssigner since RISC-V implements its CC using a custom
|
|
// function with a different signature.
|
|
RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn;
|
|
|
|
// Whether this is assigning args from a return.
|
|
bool IsRet;
|
|
|
|
public:
|
|
RISCVIncomingValueAssigner(
|
|
RISCVTargetLowering::RISCVCCAssignFn *RISCVAssignFn_, bool IsRet)
|
|
: CallLowering::IncomingValueAssigner(nullptr),
|
|
RISCVAssignFn(RISCVAssignFn_), IsRet(IsRet) {}
|
|
|
|
bool assignArg(unsigned ValNo, EVT OrigVT, MVT ValVT, MVT LocVT,
|
|
CCValAssign::LocInfo LocInfo,
|
|
const CallLowering::ArgInfo &Info, ISD::ArgFlagsTy Flags,
|
|
CCState &State) override {
|
|
MachineFunction &MF = State.getMachineFunction();
|
|
const DataLayout &DL = MF.getDataLayout();
|
|
const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>();
|
|
|
|
if (LocVT.isScalableVector())
|
|
MF.getInfo<RISCVMachineFunctionInfo>()->setIsVectorCall();
|
|
|
|
if (RISCVAssignFn(DL, Subtarget.getTargetABI(), ValNo, ValVT, LocVT,
|
|
LocInfo, Flags, State, /*IsFixed=*/true, IsRet, Info.Ty,
|
|
*Subtarget.getTargetLowering(),
|
|
/*FirstMaskArgument=*/std::nullopt))
|
|
return true;
|
|
|
|
StackSize = State.getStackSize();
|
|
return false;
|
|
}
|
|
};
|
|
|
|
struct RISCVIncomingValueHandler : public CallLowering::IncomingValueHandler {
|
|
RISCVIncomingValueHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI)
|
|
: IncomingValueHandler(B, MRI),
|
|
Subtarget(MIRBuilder.getMF().getSubtarget<RISCVSubtarget>()) {}
|
|
|
|
Register getStackAddress(uint64_t MemSize, int64_t Offset,
|
|
MachinePointerInfo &MPO,
|
|
ISD::ArgFlagsTy Flags) override {
|
|
MachineFrameInfo &MFI = MIRBuilder.getMF().getFrameInfo();
|
|
|
|
int FI = MFI.CreateFixedObject(MemSize, Offset, /*Immutable=*/true);
|
|
MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI);
|
|
return MIRBuilder.buildFrameIndex(LLT::pointer(0, Subtarget.getXLen()), FI)
|
|
.getReg(0);
|
|
}
|
|
|
|
void assignValueToAddress(Register ValVReg, Register Addr, LLT MemTy,
|
|
const MachinePointerInfo &MPO,
|
|
const CCValAssign &VA) override {
|
|
MachineFunction &MF = MIRBuilder.getMF();
|
|
auto MMO = MF.getMachineMemOperand(MPO, MachineMemOperand::MOLoad, MemTy,
|
|
inferAlignFromPtrInfo(MF, MPO));
|
|
MIRBuilder.buildLoad(ValVReg, Addr, *MMO);
|
|
}
|
|
|
|
void assignValueToReg(Register ValVReg, Register PhysReg,
|
|
const CCValAssign &VA) override {
|
|
markPhysRegUsed(PhysReg);
|
|
IncomingValueHandler::assignValueToReg(ValVReg, PhysReg, VA);
|
|
}
|
|
|
|
unsigned assignCustomValue(CallLowering::ArgInfo &Arg,
|
|
ArrayRef<CCValAssign> VAs,
|
|
std::function<void()> *Thunk) override {
|
|
assert(VAs.size() >= 2 && "Expected at least 2 VAs.");
|
|
const CCValAssign &VALo = VAs[0];
|
|
const CCValAssign &VAHi = VAs[1];
|
|
|
|
assert(VAHi.needsCustom() && "Value doesn't need custom handling");
|
|
assert(VALo.getValNo() == VAHi.getValNo() &&
|
|
"Values belong to different arguments");
|
|
|
|
assert(VALo.getLocVT() == MVT::i32 && VAHi.getLocVT() == MVT::i32 &&
|
|
VALo.getValVT() == MVT::f64 && VAHi.getValVT() == MVT::f64 &&
|
|
"unexpected custom value");
|
|
|
|
Register NewRegs[] = {MRI.createGenericVirtualRegister(LLT::scalar(32)),
|
|
MRI.createGenericVirtualRegister(LLT::scalar(32))};
|
|
|
|
if (VAHi.isMemLoc()) {
|
|
LLT MemTy(VAHi.getLocVT());
|
|
|
|
MachinePointerInfo MPO;
|
|
Register StackAddr = getStackAddress(
|
|
MemTy.getSizeInBytes(), VAHi.getLocMemOffset(), MPO, Arg.Flags[0]);
|
|
|
|
assignValueToAddress(NewRegs[1], StackAddr, MemTy, MPO,
|
|
const_cast<CCValAssign &>(VAHi));
|
|
}
|
|
|
|
assignValueToReg(NewRegs[0], VALo.getLocReg(), VALo);
|
|
if (VAHi.isRegLoc())
|
|
assignValueToReg(NewRegs[1], VAHi.getLocReg(), VAHi);
|
|
|
|
MIRBuilder.buildMergeLikeInstr(Arg.Regs[0], NewRegs);
|
|
|
|
return 2;
|
|
}
|
|
|
|
/// 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(MCRegister PhysReg) = 0;
|
|
|
|
private:
|
|
const RISCVSubtarget &Subtarget;
|
|
};
|
|
|
|
struct RISCVFormalArgHandler : public RISCVIncomingValueHandler {
|
|
RISCVFormalArgHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI)
|
|
: RISCVIncomingValueHandler(B, MRI) {}
|
|
|
|
void markPhysRegUsed(MCRegister PhysReg) override {
|
|
MIRBuilder.getMRI()->addLiveIn(PhysReg);
|
|
MIRBuilder.getMBB().addLiveIn(PhysReg);
|
|
}
|
|
};
|
|
|
|
struct RISCVCallReturnHandler : public RISCVIncomingValueHandler {
|
|
RISCVCallReturnHandler(MachineIRBuilder &B, MachineRegisterInfo &MRI,
|
|
MachineInstrBuilder &MIB)
|
|
: RISCVIncomingValueHandler(B, MRI), MIB(MIB) {}
|
|
|
|
void markPhysRegUsed(MCRegister PhysReg) override {
|
|
MIB.addDef(PhysReg, RegState::Implicit);
|
|
}
|
|
|
|
MachineInstrBuilder MIB;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
RISCVCallLowering::RISCVCallLowering(const RISCVTargetLowering &TLI)
|
|
: CallLowering(&TLI) {}
|
|
|
|
/// Return true if scalable vector with ScalarTy is legal for lowering.
|
|
static bool isLegalElementTypeForRVV(Type *EltTy,
|
|
const RISCVSubtarget &Subtarget) {
|
|
if (EltTy->isPointerTy())
|
|
return Subtarget.is64Bit() ? Subtarget.hasVInstructionsI64() : true;
|
|
if (EltTy->isIntegerTy(1) || EltTy->isIntegerTy(8) ||
|
|
EltTy->isIntegerTy(16) || EltTy->isIntegerTy(32))
|
|
return true;
|
|
if (EltTy->isIntegerTy(64))
|
|
return Subtarget.hasVInstructionsI64();
|
|
if (EltTy->isHalfTy())
|
|
return Subtarget.hasVInstructionsF16();
|
|
if (EltTy->isBFloatTy())
|
|
return Subtarget.hasVInstructionsBF16();
|
|
if (EltTy->isFloatTy())
|
|
return Subtarget.hasVInstructionsF32();
|
|
if (EltTy->isDoubleTy())
|
|
return Subtarget.hasVInstructionsF64();
|
|
return false;
|
|
}
|
|
|
|
// TODO: Support all argument types.
|
|
// TODO: Remove IsLowerArgs argument by adding support for vectors in lowerCall.
|
|
static bool isSupportedArgumentType(Type *T, const RISCVSubtarget &Subtarget,
|
|
bool IsLowerArgs = false) {
|
|
// TODO: Integers larger than 2*XLen are passed indirectly which is not
|
|
// supported yet.
|
|
if (T->isIntegerTy())
|
|
return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2;
|
|
if (T->isFloatTy() || T->isDoubleTy())
|
|
return true;
|
|
if (T->isPointerTy())
|
|
return true;
|
|
// TODO: Support fixed vector types.
|
|
if (IsLowerArgs && T->isVectorTy() && Subtarget.hasVInstructions() &&
|
|
T->isScalableTy() &&
|
|
isLegalElementTypeForRVV(T->getScalarType(), Subtarget))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// TODO: Only integer, pointer and aggregate types are supported now.
|
|
// TODO: Remove IsLowerRetVal argument by adding support for vectors in
|
|
// lowerCall.
|
|
static bool isSupportedReturnType(Type *T, const RISCVSubtarget &Subtarget,
|
|
bool IsLowerRetVal = false) {
|
|
// TODO: Integers larger than 2*XLen are passed indirectly which is not
|
|
// supported yet.
|
|
if (T->isIntegerTy())
|
|
return T->getIntegerBitWidth() <= Subtarget.getXLen() * 2;
|
|
if (T->isFloatTy() || T->isDoubleTy())
|
|
return true;
|
|
if (T->isPointerTy())
|
|
return true;
|
|
|
|
if (T->isArrayTy())
|
|
return isSupportedReturnType(T->getArrayElementType(), Subtarget);
|
|
|
|
if (T->isStructTy()) {
|
|
auto StructT = cast<StructType>(T);
|
|
for (unsigned i = 0, e = StructT->getNumElements(); i != e; ++i)
|
|
if (!isSupportedReturnType(StructT->getElementType(i), Subtarget))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
if (IsLowerRetVal && T->isVectorTy() && Subtarget.hasVInstructions() &&
|
|
T->isScalableTy() &&
|
|
isLegalElementTypeForRVV(T->getScalarType(), Subtarget))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool RISCVCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder,
|
|
const Value *Val,
|
|
ArrayRef<Register> VRegs,
|
|
MachineInstrBuilder &Ret) const {
|
|
if (!Val)
|
|
return true;
|
|
|
|
const RISCVSubtarget &Subtarget =
|
|
MIRBuilder.getMF().getSubtarget<RISCVSubtarget>();
|
|
if (!isSupportedReturnType(Val->getType(), Subtarget, /*IsLowerRetVal=*/true))
|
|
return false;
|
|
|
|
MachineFunction &MF = MIRBuilder.getMF();
|
|
const DataLayout &DL = MF.getDataLayout();
|
|
const Function &F = MF.getFunction();
|
|
CallingConv::ID CC = F.getCallingConv();
|
|
|
|
ArgInfo OrigRetInfo(VRegs, Val->getType(), 0);
|
|
setArgFlags(OrigRetInfo, AttributeList::ReturnIndex, DL, F);
|
|
|
|
SmallVector<ArgInfo, 4> SplitRetInfos;
|
|
splitToValueTypes(OrigRetInfo, SplitRetInfos, DL, CC);
|
|
|
|
RISCVOutgoingValueAssigner Assigner(
|
|
CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV,
|
|
/*IsRet=*/true);
|
|
RISCVOutgoingValueHandler Handler(MIRBuilder, MF.getRegInfo(), Ret);
|
|
return determineAndHandleAssignments(Handler, Assigner, SplitRetInfos,
|
|
MIRBuilder, CC, F.isVarArg());
|
|
}
|
|
|
|
bool RISCVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
|
|
const Value *Val, ArrayRef<Register> VRegs,
|
|
FunctionLoweringInfo &FLI) const {
|
|
assert(!Val == VRegs.empty() && "Return value without a vreg");
|
|
MachineInstrBuilder Ret = MIRBuilder.buildInstrNoInsert(RISCV::PseudoRET);
|
|
|
|
if (!lowerReturnVal(MIRBuilder, Val, VRegs, Ret))
|
|
return false;
|
|
|
|
MIRBuilder.insertInstr(Ret);
|
|
return true;
|
|
}
|
|
|
|
/// If there are varargs that were passed in a0-a7, the data in those registers
|
|
/// must be copied to the varargs save area on the stack.
|
|
void RISCVCallLowering::saveVarArgRegisters(
|
|
MachineIRBuilder &MIRBuilder, CallLowering::IncomingValueHandler &Handler,
|
|
IncomingValueAssigner &Assigner, CCState &CCInfo) const {
|
|
MachineFunction &MF = MIRBuilder.getMF();
|
|
const RISCVSubtarget &Subtarget = MF.getSubtarget<RISCVSubtarget>();
|
|
unsigned XLenInBytes = Subtarget.getXLen() / 8;
|
|
ArrayRef<MCPhysReg> ArgRegs = RISCV::getArgGPRs(Subtarget.getTargetABI());
|
|
MachineRegisterInfo &MRI = MF.getRegInfo();
|
|
unsigned Idx = CCInfo.getFirstUnallocated(ArgRegs);
|
|
MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
RISCVMachineFunctionInfo *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
|
|
|
|
// Size of the vararg save area. For now, the varargs save area is either
|
|
// zero or large enough to hold a0-a7.
|
|
int VarArgsSaveSize = XLenInBytes * (ArgRegs.size() - Idx);
|
|
int FI;
|
|
|
|
// If all registers are allocated, then all varargs must be passed on the
|
|
// stack and we don't need to save any argregs.
|
|
if (VarArgsSaveSize == 0) {
|
|
int VaArgOffset = Assigner.StackSize;
|
|
FI = MFI.CreateFixedObject(XLenInBytes, VaArgOffset, true);
|
|
} else {
|
|
int VaArgOffset = -VarArgsSaveSize;
|
|
FI = MFI.CreateFixedObject(VarArgsSaveSize, VaArgOffset, true);
|
|
|
|
// If saving an odd number of registers then create an extra stack slot to
|
|
// ensure that the frame pointer is 2*XLEN-aligned, which in turn ensures
|
|
// offsets to even-numbered registered remain 2*XLEN-aligned.
|
|
if (Idx % 2) {
|
|
MFI.CreateFixedObject(XLenInBytes,
|
|
VaArgOffset - static_cast<int>(XLenInBytes), true);
|
|
VarArgsSaveSize += XLenInBytes;
|
|
}
|
|
|
|
const LLT p0 = LLT::pointer(MF.getDataLayout().getAllocaAddrSpace(),
|
|
Subtarget.getXLen());
|
|
const LLT sXLen = LLT::scalar(Subtarget.getXLen());
|
|
|
|
auto FIN = MIRBuilder.buildFrameIndex(p0, FI);
|
|
auto Offset = MIRBuilder.buildConstant(
|
|
MRI.createGenericVirtualRegister(sXLen), XLenInBytes);
|
|
|
|
// Copy the integer registers that may have been used for passing varargs
|
|
// to the vararg save area.
|
|
const MVT XLenVT = Subtarget.getXLenVT();
|
|
for (unsigned I = Idx; I < ArgRegs.size(); ++I) {
|
|
const Register VReg = MRI.createGenericVirtualRegister(sXLen);
|
|
Handler.assignValueToReg(
|
|
VReg, ArgRegs[I],
|
|
CCValAssign::getReg(I + MF.getFunction().getNumOperands(), XLenVT,
|
|
ArgRegs[I], XLenVT, CCValAssign::Full));
|
|
auto MPO =
|
|
MachinePointerInfo::getFixedStack(MF, FI, (I - Idx) * XLenInBytes);
|
|
MIRBuilder.buildStore(VReg, FIN, MPO, inferAlignFromPtrInfo(MF, MPO));
|
|
FIN = MIRBuilder.buildPtrAdd(MRI.createGenericVirtualRegister(p0),
|
|
FIN.getReg(0), Offset);
|
|
}
|
|
}
|
|
|
|
// Record the frame index of the first variable argument which is a value
|
|
// necessary to G_VASTART.
|
|
RVFI->setVarArgsFrameIndex(FI);
|
|
RVFI->setVarArgsSaveSize(VarArgsSaveSize);
|
|
}
|
|
|
|
bool RISCVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
|
|
const Function &F,
|
|
ArrayRef<ArrayRef<Register>> VRegs,
|
|
FunctionLoweringInfo &FLI) const {
|
|
// Early exit if there are no arguments. varargs are not part of F.args() but
|
|
// must be lowered.
|
|
if (F.arg_empty() && !F.isVarArg())
|
|
return true;
|
|
|
|
const RISCVSubtarget &Subtarget =
|
|
MIRBuilder.getMF().getSubtarget<RISCVSubtarget>();
|
|
for (auto &Arg : F.args()) {
|
|
if (!isSupportedArgumentType(Arg.getType(), Subtarget,
|
|
/*IsLowerArgs=*/true))
|
|
return false;
|
|
}
|
|
|
|
MachineFunction &MF = MIRBuilder.getMF();
|
|
const DataLayout &DL = MF.getDataLayout();
|
|
CallingConv::ID CC = F.getCallingConv();
|
|
|
|
SmallVector<ArgInfo, 32> SplitArgInfos;
|
|
unsigned Index = 0;
|
|
for (auto &Arg : F.args()) {
|
|
// Construct the ArgInfo object from destination register and argument type.
|
|
ArgInfo AInfo(VRegs[Index], Arg.getType(), Index);
|
|
setArgFlags(AInfo, Index + AttributeList::FirstArgIndex, DL, F);
|
|
|
|
// Handle any required merging from split value types from physical
|
|
// registers into the desired VReg. ArgInfo objects are constructed
|
|
// correspondingly and appended to SplitArgInfos.
|
|
splitToValueTypes(AInfo, SplitArgInfos, DL, CC);
|
|
|
|
++Index;
|
|
}
|
|
|
|
RISCVIncomingValueAssigner Assigner(
|
|
CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV,
|
|
/*IsRet=*/false);
|
|
RISCVFormalArgHandler Handler(MIRBuilder, MF.getRegInfo());
|
|
|
|
SmallVector<CCValAssign, 16> ArgLocs;
|
|
CCState CCInfo(CC, F.isVarArg(), MIRBuilder.getMF(), ArgLocs, F.getContext());
|
|
if (!determineAssignments(Assigner, SplitArgInfos, CCInfo) ||
|
|
!handleAssignments(Handler, SplitArgInfos, CCInfo, ArgLocs, MIRBuilder))
|
|
return false;
|
|
|
|
if (F.isVarArg())
|
|
saveVarArgRegisters(MIRBuilder, Handler, Assigner, CCInfo);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RISCVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
|
|
CallLoweringInfo &Info) const {
|
|
MachineFunction &MF = MIRBuilder.getMF();
|
|
const DataLayout &DL = MF.getDataLayout();
|
|
const Function &F = MF.getFunction();
|
|
CallingConv::ID CC = F.getCallingConv();
|
|
|
|
const RISCVSubtarget &Subtarget =
|
|
MIRBuilder.getMF().getSubtarget<RISCVSubtarget>();
|
|
for (auto &AInfo : Info.OrigArgs) {
|
|
if (!isSupportedArgumentType(AInfo.Ty, Subtarget))
|
|
return false;
|
|
}
|
|
|
|
if (!Info.OrigRet.Ty->isVoidTy() &&
|
|
!isSupportedReturnType(Info.OrigRet.Ty, Subtarget))
|
|
return false;
|
|
|
|
MachineInstrBuilder CallSeqStart =
|
|
MIRBuilder.buildInstr(RISCV::ADJCALLSTACKDOWN);
|
|
|
|
SmallVector<ArgInfo, 32> SplitArgInfos;
|
|
SmallVector<ISD::OutputArg, 8> Outs;
|
|
for (auto &AInfo : Info.OrigArgs) {
|
|
// Handle any required unmerging of split value types from a given VReg into
|
|
// physical registers. ArgInfo objects are constructed correspondingly and
|
|
// appended to SplitArgInfos.
|
|
splitToValueTypes(AInfo, SplitArgInfos, DL, CC);
|
|
}
|
|
|
|
// TODO: Support tail calls.
|
|
Info.IsTailCall = false;
|
|
|
|
// Select the recommended relocation type R_RISCV_CALL_PLT.
|
|
if (!Info.Callee.isReg())
|
|
Info.Callee.setTargetFlags(RISCVII::MO_CALL);
|
|
|
|
MachineInstrBuilder Call =
|
|
MIRBuilder
|
|
.buildInstrNoInsert(Info.Callee.isReg() ? RISCV::PseudoCALLIndirect
|
|
: RISCV::PseudoCALL)
|
|
.add(Info.Callee);
|
|
const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo();
|
|
Call.addRegMask(TRI->getCallPreservedMask(MF, Info.CallConv));
|
|
|
|
RISCVOutgoingValueAssigner ArgAssigner(
|
|
CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV,
|
|
/*IsRet=*/false);
|
|
RISCVOutgoingValueHandler ArgHandler(MIRBuilder, MF.getRegInfo(), Call);
|
|
if (!determineAndHandleAssignments(ArgHandler, ArgAssigner, SplitArgInfos,
|
|
MIRBuilder, CC, Info.IsVarArg))
|
|
return false;
|
|
|
|
MIRBuilder.insertInstr(Call);
|
|
|
|
CallSeqStart.addImm(ArgAssigner.StackSize).addImm(0);
|
|
MIRBuilder.buildInstr(RISCV::ADJCALLSTACKUP)
|
|
.addImm(ArgAssigner.StackSize)
|
|
.addImm(0);
|
|
|
|
// 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 (Call->getOperand(0).isReg())
|
|
constrainOperandRegClass(MF, *TRI, MF.getRegInfo(),
|
|
*Subtarget.getInstrInfo(),
|
|
*Subtarget.getRegBankInfo(), *Call,
|
|
Call->getDesc(), Call->getOperand(0), 0);
|
|
|
|
if (Info.OrigRet.Ty->isVoidTy())
|
|
return true;
|
|
|
|
SmallVector<ArgInfo, 4> SplitRetInfos;
|
|
splitToValueTypes(Info.OrigRet, SplitRetInfos, DL, CC);
|
|
|
|
RISCVIncomingValueAssigner RetAssigner(
|
|
CC == CallingConv::Fast ? RISCV::CC_RISCV_FastCC : RISCV::CC_RISCV,
|
|
/*IsRet=*/true);
|
|
RISCVCallReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), Call);
|
|
if (!determineAndHandleAssignments(RetHandler, RetAssigner, SplitRetInfos,
|
|
MIRBuilder, CC, Info.IsVarArg))
|
|
return false;
|
|
|
|
return true;
|
|
}
|