[llvm] [RISCV] Fix musttail with indirect arguments by forwarding incoming pointers (PR #185094)
Craig Topper via llvm-commits
llvm-commits at lists.llvm.org
Wed May 6 09:59:48 PDT 2026
================
@@ -24665,51 +24695,148 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
// Promote the value if needed.
// For now, only handle fully promoted and indirect arguments.
if (VA.getLocInfo() == CCValAssign::Indirect) {
- // Store the argument in a stack slot and pass its address.
- Align StackAlign =
- std::max(getPrefTypeAlign(Outs[OutIdx].ArgVT, DAG),
- getPrefTypeAlign(ArgValue.getValueType(), DAG));
- TypeSize StoredSize = ArgValue.getValueType().getStoreSize();
- // If the original argument was split (e.g. i128), we need
- // to store the required parts of it here (and pass just one address).
- // Vectors may be partly split to registers and partly to the stack, in
- // which case the base address is partly offset and subsequent stores are
- // relative to that.
- unsigned ArgIndex = Outs[OutIdx].OrigArgIndex;
- unsigned ArgPartOffset = Outs[OutIdx].PartOffset;
- assert(VA.getValVT().isVector() || ArgPartOffset == 0);
- // Calculate the total size to store. We don't have access to what we're
- // actually storing other than performing the loop and collecting the
- // info.
- SmallVector<std::pair<SDValue, SDValue>> Parts;
- while (i + 1 != e && Outs[OutIdx + 1].OrigArgIndex == ArgIndex) {
- SDValue PartValue = OutVals[OutIdx + 1];
- unsigned PartOffset = Outs[OutIdx + 1].PartOffset - ArgPartOffset;
- SDValue Offset = DAG.getIntPtrConstant(PartOffset, DL);
- EVT PartVT = PartValue.getValueType();
- if (PartVT.isScalableVector())
- Offset = DAG.getNode(ISD::VSCALE, DL, XLenVT, Offset);
- StoredSize += PartVT.getStoreSize();
- StackAlign = std::max(StackAlign, getPrefTypeAlign(PartVT, DAG));
- Parts.push_back(std::make_pair(PartValue, Offset));
- ++i;
- ++OutIdx;
- }
- SDValue SpillSlot = DAG.CreateStackTemporary(StoredSize, StackAlign);
- int FI = cast<FrameIndexSDNode>(SpillSlot)->getIndex();
- MemOpChains.push_back(
- DAG.getStore(Chain, DL, ArgValue, SpillSlot,
- MachinePointerInfo::getFixedStack(MF, FI)));
- for (const auto &Part : Parts) {
- SDValue PartValue = Part.first;
- SDValue PartOffset = Part.second;
- SDValue Address =
- DAG.getNode(ISD::ADD, DL, PtrVT, SpillSlot, PartOffset);
+ // For musttail calls, reuse incoming indirect pointers instead of
+ // creating new stack temporaries. The incoming pointers point to the
+ // caller's caller's frame, which remains valid after a tail call.
+ if (IsTailCall && CLI.CB && CLI.CB->isMustTailCall()) {
+ RISCVMachineFunctionInfo *RVFI = MF.getInfo<RISCVMachineFunctionInfo>();
+ unsigned CallArgIdx = Outs[OutIdx].OrigArgIndex;
+
+ // Resolve which formal parameter is being passed at this call
+ // position.
+ //
+ // FIXME: Ins[].OrigArgIndex is Argument::getArgNo() (unfiltered),
+ // but Outs[].OrigArgIndex is an index into a filtered arg list
+ // (empty types removed, via CallLoweringInfo in the target-
+ // independent layer). IncomingIndirectArgs is keyed by the
+ // caller's unfiltered Argument::getArgNo(), so we have to walk
+ // the caller's formals (same filter) to translate the index.
+ // This target-independent asymmetry should be normalized so
+ // backends do not need to re-derive the mapping.
+ //
+ // Steps:
+ // 1. Find the call operand at filtered position CallArgIdx.
+ // 2. If it is an Argument, use getArgNo() directly (same filter
+ // for caller formals and call operands).
+ // 3. Otherwise (computed value), walk the caller's formals and
+ // skip empty types to map the filtered index to getArgNo().
+ const Argument *FormalArg = nullptr;
+ unsigned FilteredIdx = 0;
+ for (const auto &CallArg : CLI.CB->args()) {
+ if (CallArg->getType()->isEmptyTy())
+ continue;
+ if (FilteredIdx == CallArgIdx) {
+ FormalArg = dyn_cast<Argument>(CallArg);
+ break;
+ }
+ ++FilteredIdx;
+ }
+
+ // For forwarded args, getArgNo() gives the unfiltered index directly.
+ // For computed args, walk the caller's formals to resolve it.
+ unsigned FormalArgIdx = CallArgIdx;
+ if (FormalArg) {
+ FormalArgIdx = FormalArg->getArgNo();
+ } else {
+ FilteredIdx = 0;
+ for (const auto &Arg : MF.getFunction().args()) {
+ if (Arg.getType()->isEmptyTy())
+ continue;
+ if (FilteredIdx == CallArgIdx) {
+ FormalArgIdx = Arg.getArgNo();
+ break;
+ }
+ ++FilteredIdx;
+ }
+ }
+
+ Register VReg = RVFI->getIncomingIndirectArg(FormalArgIdx);
+ SDValue CopyOp = DAG.getCopyFromReg(Chain, DL, VReg, PtrVT);
+ // Thread the CopyFromReg output chain through MemOpChains so the
+ // TokenFactor below sequences the copy with any stores we emit
+ // for this argument.
+ MemOpChains.push_back(CopyOp.getValue(1));
+ SDValue IncomingPtr = CopyOp;
+
+ if (!FormalArg) {
+ // Computed value: store into the incoming indirect pointer for the
+ // same-position formal parameter (musttail guarantees matching
+ // prototypes, so types match). The pointer survives the tail call
+ // since it points to the caller's caller's frame. Use the
+ // CopyFromReg output chain so the store is sequenced after the
+ // copy that produced IncomingPtr.
+ MemOpChains.push_back(
+ DAG.getStore(CopyOp.getValue(1), DL, ArgValue, IncomingPtr,
+ MachinePointerInfo::getUnknownStack(MF)));
+ // Store any split parts at their respective offsets.
+ unsigned ArgPartOffset = Outs[OutIdx].PartOffset;
+ while (i + 1 != e && Outs[OutIdx + 1].OrigArgIndex == CallArgIdx) {
+ SDValue PartValue = OutVals[OutIdx + 1];
----------------
topperc wrote:
I see you've updated the code, but I don't see a test.
https://github.com/llvm/llvm-project/pull/185094
More information about the llvm-commits
mailing list