Skip to content

[GISel][RISCV]Implement indirect parameter passing #95429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 101 additions & 29 deletions llvm/lib/CodeGen/GlobalISel/CallLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void CallLowering::anchor() {}
static void
addFlagsUsingAttrFn(ISD::ArgFlagsTy &Flags,
const std::function<bool(Attribute::AttrKind)> &AttrFn) {
// TODO: There are missing flags. Add them here.
if (AttrFn(Attribute::SExt))
Flags.setSExt();
if (AttrFn(Attribute::ZExt))
Expand Down Expand Up @@ -743,6 +744,8 @@ bool CallLowering::handleAssignments(ValueHandler &Handler,
continue;
}

auto AllocaAddressSpace = MF.getDataLayout().getAllocaAddrSpace();

const MVT ValVT = VA.getValVT();
const MVT LocVT = VA.getLocVT();

Expand All @@ -751,6 +754,8 @@ bool CallLowering::handleAssignments(ValueHandler &Handler,
const LLT NewLLT = Handler.isIncomingArgumentHandler() ? LocTy : ValTy;
const EVT OrigVT = EVT::getEVT(Args[i].Ty);
const LLT OrigTy = getLLTForType(*Args[i].Ty, DL);
const LLT PointerTy = LLT::pointer(
AllocaAddressSpace, DL.getPointerSizeInBits(AllocaAddressSpace));

// Expected to be multiple regs for a single incoming arg.
// There should be Regs.size() ArgLocs per argument.
Expand All @@ -765,31 +770,76 @@ bool CallLowering::handleAssignments(ValueHandler &Handler,
// intermediate values.
Args[i].Regs.resize(NumParts);

// For each split register, create and assign a vreg that will store
// the incoming component of the larger value. These will later be
// merged to form the final vreg.
for (unsigned Part = 0; Part < NumParts; ++Part)
Args[i].Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
// When we have indirect parameter passing we are receiving a pointer,
// that points to the actual value, so we need one "temporary" pointer.
if (VA.getLocInfo() == CCValAssign::Indirect) {
if (Handler.isIncomingArgumentHandler())
Args[i].Regs[0] = MRI.createGenericVirtualRegister(PointerTy);
} else {
// For each split register, create and assign a vreg that will store
// the incoming component of the larger value. These will later be
// merged to form the final vreg.
for (unsigned Part = 0; Part < NumParts; ++Part)
Args[i].Regs[Part] = MRI.createGenericVirtualRegister(NewLLT);
}
}

assert((j + (NumParts - 1)) < ArgLocs.size() &&
"Too many regs for number of args");

// Coerce into outgoing value types before register assignment.
if (!Handler.isIncomingArgumentHandler() && OrigTy != ValTy) {
if (!Handler.isIncomingArgumentHandler() && OrigTy != ValTy &&
VA.getLocInfo() != CCValAssign::Indirect) {
assert(Args[i].OrigRegs.size() == 1);
buildCopyToRegs(MIRBuilder, Args[i].Regs, Args[i].OrigRegs[0], OrigTy,
ValTy, extendOpFromFlags(Args[i].Flags[0]));
}

bool IndirectParameterPassingHandled = false;
bool BigEndianPartOrdering = TLI->hasBigEndianPartOrdering(OrigVT, DL);
for (unsigned Part = 0; Part < NumParts; ++Part) {
assert((VA.getLocInfo() != CCValAssign::Indirect || Part == 0) &&
"Only the first parameter should be processed when "
"handling indirect passing!");
Register ArgReg = Args[i].Regs[Part];
// There should be Regs.size() ArgLocs per argument.
unsigned Idx = BigEndianPartOrdering ? NumParts - 1 - Part : Part;
CCValAssign &VA = ArgLocs[j + Idx];
const ISD::ArgFlagsTy Flags = Args[i].Flags[Part];

// We found an indirect parameter passing, and we have an
// OutgoingValueHandler as our handler (so we are at the call site or the
// return value). In this case, start the construction of the following
// GMIR, that is responsible for the preparation of indirect parameter
// passing:
//
// %1(indirectly passed type) = The value to pass
// %3(pointer) = G_FRAME_INDEX %stack.0
// G_STORE %1, %3 :: (store (s128), align 8)
//
// After this GMIR, the remaining part of the loop body will decide how
// to get the value to the caller and we break out of the loop.
if (VA.getLocInfo() == CCValAssign::Indirect &&
!Handler.isIncomingArgumentHandler()) {
Align AlignmentForStored = DL.getPrefTypeAlign(Args[i].Ty);
MachineFrameInfo &MFI = MF.getFrameInfo();
// Get some space on the stack for the value, so later we can pass it
// as a reference.
int FrameIdx = MFI.CreateStackObject(OrigTy.getScalarSizeInBits(),
AlignmentForStored, false);
Register PointerToStackReg =
MIRBuilder.buildFrameIndex(PointerTy, FrameIdx).getReg(0);
MachinePointerInfo StackPointerMPO =
MachinePointerInfo::getFixedStack(MF, FrameIdx);
// Store the value in the previously created stack space.
MIRBuilder.buildStore(Args[i].OrigRegs[Part], PointerToStackReg,
StackPointerMPO,
inferAlignFromPtrInfo(MF, StackPointerMPO));

ArgReg = PointerToStackReg;
IndirectParameterPassingHandled = true;
}

if (VA.isMemLoc() && !Flags.isByVal()) {
// Individual pieces may have been spilled to the stack and others
// passed in registers.
Expand All @@ -799,14 +849,21 @@ bool CallLowering::handleAssignments(ValueHandler &Handler,
LLT MemTy = Handler.getStackValueStoreType(DL, VA, Flags);

MachinePointerInfo MPO;
Register StackAddr = Handler.getStackAddress(
MemTy.getSizeInBytes(), VA.getLocMemOffset(), MPO, Flags);

Handler.assignValueToAddress(Args[i], Part, StackAddr, MemTy, MPO, VA);
continue;
}

if (VA.isMemLoc() && Flags.isByVal()) {
Register StackAddr =
Handler.getStackAddress(VA.getLocInfo() == CCValAssign::Indirect
? PointerTy.getSizeInBytes()
: MemTy.getSizeInBytes(),
VA.getLocMemOffset(), MPO, Flags);

// Finish the handling of indirect passing from the passers
// (OutgoingParameterHandler) side.
// This branch is needed, so the pointer to the value is loaded onto the
// stack.
if (VA.getLocInfo() == CCValAssign::Indirect)
Handler.assignValueToAddress(ArgReg, StackAddr, PointerTy, MPO, VA);
else
Handler.assignValueToAddress(Args[i], Part, StackAddr, MemTy, MPO, VA);
} else if (VA.isMemLoc() && Flags.isByVal()) {
assert(Args[i].Regs.size() == 1 &&
"didn't expect split byval pointer");

Expand Down Expand Up @@ -845,30 +902,45 @@ bool CallLowering::handleAssignments(ValueHandler &Handler,
DstMPO, DstAlign, SrcMPO, SrcAlign,
MemSize, VA);
}
continue;
}

assert(!VA.needsCustom() && "custom loc should have been handled already");

if (i == 0 && !ThisReturnRegs.empty() &&
Handler.isIncomingArgumentHandler() &&
isTypeIsValidForThisReturn(ValVT)) {
} else if (i == 0 && !ThisReturnRegs.empty() &&
Handler.isIncomingArgumentHandler() &&
isTypeIsValidForThisReturn(ValVT)) {
Handler.assignValueToReg(ArgReg, ThisReturnRegs[Part], VA);
continue;
}

if (Handler.isIncomingArgumentHandler())
} else if (Handler.isIncomingArgumentHandler()) {
Handler.assignValueToReg(ArgReg, VA.getLocReg(), VA);
else {
} else {
DelayedOutgoingRegAssignments.emplace_back([=, &Handler]() {
Handler.assignValueToReg(ArgReg, VA.getLocReg(), VA);
});
}

// Finish the handling of indirect parameter passing when receiving
// the value (we are in the called function or the caller when receiving
// the return value).
if (VA.getLocInfo() == CCValAssign::Indirect &&
Handler.isIncomingArgumentHandler()) {
Align Alignment = DL.getABITypeAlign(Args[i].Ty);
MachinePointerInfo MPO = MachinePointerInfo::getUnknownStack(MF);

// Since we are doing indirect parameter passing, we know that the value
// in the temporary register is not the value passed to the function,
// but rather a pointer to that value. Let's load that value into the
// virtual register where the parameter should go.
MIRBuilder.buildLoad(Args[i].OrigRegs[0], Args[i].Regs[0], MPO,
Alignment);

IndirectParameterPassingHandled = true;
}

if (IndirectParameterPassingHandled)
break;
}

// Now that all pieces have been assigned, re-pack the register typed values
// into the original value typed registers.
if (Handler.isIncomingArgumentHandler() && OrigVT != LocVT) {
// into the original value typed registers. This is only necessary, when
// the value was passed in multiple registers, not indirectly.
if (Handler.isIncomingArgumentHandler() && OrigVT != LocVT &&
!IndirectParameterPassingHandled) {
// Merge the split registers into the expected larger result vregs of
// the original call.
buildCopyFromRegs(MIRBuilder, Args[i].OrigRegs, Args[i].Regs, OrigTy,
Expand Down
4 changes: 1 addition & 3 deletions llvm/lib/Target/RISCV/GISel/RISCVCallLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,8 @@ static bool isLegalElementTypeForRVV(Type *EltTy,
// 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;
return true;
if (T->isHalfTy() || T->isFloatTy() || T->isDoubleTy())
return true;
if (T->isPointerTy())
Expand Down
Loading
Loading