[llvm] [X86] Reuse unchanged incoming stack args for tail calls (PR #208248)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 8 09:00:40 PDT 2026


https://github.com/AxXxB created https://github.com/llvm/llvm-project/pull/208248

When a musttail call forwards an incoming stack argument unchanged to the
same outgoing stack offset, X86 lowering still creates a fixed object and
stores the value back to the stack. In the common same-signature tail-call
case this materializes unnecessary stack traffic before the tail jump.

Reuse the incoming stack slot when MatchingStackOffset proves that the
outgoing argument already resides at the required offset. Keep byval
arguments on the existing copy path and limit the first reuse path to
non-negative fixed stack offsets.

The test covers unchanged stack arguments, a changed stack argument, and a
phi case where only the modified stack argument is stored.

Tested with `ninja -C build -j8 llc` and
`build/bin/llvm-lit -q llvm/test/CodeGen/X86`.

Assisted-by: OpenAI Codex


>From 74816ccb5be9ed91cda7667cc2ccee4d986f7cce Mon Sep 17 00:00:00 2001
From: NoName <bokamaza at gmail.com>
Date: Wed, 8 Jul 2026 18:58:47 +0300
Subject: [PATCH] [X86] Reuse unchanged incoming stack args for tail calls

When a musttail call forwards an incoming stack argument unchanged to the same outgoing stack offset, X86 lowering still creates a fixed object and stores the value back to the stack. In the common same-signature tail-call case this materializes unnecessary stack traffic before the tail jump.

Reuse the incoming stack slot when MatchingStackOffset proves that the outgoing argument already resides at the required offset. Keep byval arguments on the existing copy path and limit the first reuse path to non-negative fixed stack offsets.

Assisted-by: OpenAI Codex
---
 llvm/lib/Target/X86/X86ISelLoweringCall.cpp   | 32 +++++++++++-
 .../preserve-nonecc-tailcall-stack-args.ll    | 51 +++++++++++++++++++
 2 files changed, 81 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/CodeGen/X86/preserve-nonecc-tailcall-stack-args.ll

diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index 819e0a023c1c5..335b76eef955b 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -2084,6 +2084,28 @@ X86TargetLowering::ByValCopyKind X86TargetLowering::ByValNeedsCopyForTailCall(
     return CopyViaTemp;
 }
 
+static bool MatchingStackOffset(SDValue Arg, unsigned Offset,
+                                ISD::ArgFlagsTy Flags, MachineFrameInfo &MFI,
+                                const MachineRegisterInfo *MRI,
+                                const X86InstrInfo *TII,
+                                const CCValAssign &VA);
+
+static bool canReuseIncomingTailCallStackArg(
+    SDValue Arg, int32_t Offset, ISD::ArgFlagsTy Flags, MachineFunction &MF,
+    const X86Subtarget &Subtarget, const CCValAssign &VA) {
+  // ByVal tailcall arguments have extra temporary-copy handling above this
+  // point. Keep the first reuse pass limited to direct scalar/vector stack
+  // arguments, matching the common musttail same-signature case.
+  if (Flags.isByVal())
+    return false;
+
+  if (Offset < 0)
+    return false;
+
+  return MatchingStackOffset(Arg, unsigned(Offset), Flags, MF.getFrameInfo(),
+                             &MF.getRegInfo(), Subtarget.getInstrInfo(), VA);
+}
+
 SDValue
 X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
                              SmallVectorImpl<SDValue> &InVals) const {
@@ -2532,8 +2554,14 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
       if (Flags.isInAlloca() || Flags.isPreallocated())
         continue;
       // Create frame index.
-      int32_t Offset = VA.getLocMemOffset()+FPDiff;
-      uint32_t OpSize = (VA.getLocVT().getSizeInBits()+7)/8;
+      int32_t Offset = VA.getLocMemOffset() + FPDiff;
+      uint32_t OpSize = (VA.getLocVT().getSizeInBits() + 7) / 8;
+
+      if (Is64Bit && isTailCall &&
+          canReuseIncomingTailCallStackArg(Arg, Offset, Flags, MF, Subtarget,
+                                           VA))
+        continue;
+
       FI = MF.getFrameInfo().CreateFixedObject(OpSize, Offset, true);
       FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout()));
 
diff --git a/llvm/test/CodeGen/X86/preserve-nonecc-tailcall-stack-args.ll b/llvm/test/CodeGen/X86/preserve-nonecc-tailcall-stack-args.ll
new file mode 100644
index 0000000000000..939efecf417c7
--- /dev/null
+++ b/llvm/test/CodeGen/X86/preserve-nonecc-tailcall-stack-args.ll
@@ -0,0 +1,51 @@
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -relocation-model=static | FileCheck %s
+
+declare preserve_nonecc void @next(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64)
+declare preserve_nonecc void @next_with_cond(i1, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64)
+
+define preserve_nonecc void @static_tail_unchanged(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12, i64 %a13, i64 %a14, i64 %a15, i64 %a16, i64 %a17, i64 %a18, i64 %a19, i64 %a20, i64 %a21, i64 %a22, i64 %a23, i64 %a24, i64 %a25, i64 %a26, i64 %a27, i64 %a28, i64 %a29, i64 %a30, i64 %a31, i64 %a32, i64 %a33, i64 %a34, i64 %a35) {
+; CHECK-LABEL: static_tail_unchanged:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    jmp next at PLT # TAILCALL
+entry:
+  musttail call preserve_nonecc void @next(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12, i64 %a13, i64 %a14, i64 %a15, i64 %a16, i64 %a17, i64 %a18, i64 %a19, i64 %a20, i64 %a21, i64 %a22, i64 %a23, i64 %a24, i64 %a25, i64 %a26, i64 %a27, i64 %a28, i64 %a29, i64 %a30, i64 %a31, i64 %a32, i64 %a33, i64 %a34, i64 %a35)
+  ret void
+}
+
+define preserve_nonecc void @static_tail_changed(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12, i64 %a13, i64 %a14, i64 %a15, i64 %a16, i64 %a17, i64 %a18, i64 %a19, i64 %a20, i64 %a21, i64 %a22, i64 %a23, i64 %a24, i64 %a25, i64 %a26, i64 %a27, i64 %a28, i64 %a29, i64 %a30, i64 %a31, i64 %a32, i64 %a33, i64 %a34, i64 %a35) {
+; CHECK-LABEL: static_tail_changed:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movq 168(%rsp), %r10
+; CHECK-NEXT:    incq %r10
+; CHECK-NEXT:    movq %r10, 168(%rsp)
+; CHECK-NEXT:    jmp next at PLT # TAILCALL
+entry:
+  %a32x = add i64 %a32, 1
+  musttail call preserve_nonecc void @next(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12, i64 %a13, i64 %a14, i64 %a15, i64 %a16, i64 %a17, i64 %a18, i64 %a19, i64 %a20, i64 %a21, i64 %a22, i64 %a23, i64 %a24, i64 %a25, i64 %a26, i64 %a27, i64 %a28, i64 %a29, i64 %a30, i64 %a31, i64 %a32x, i64 %a33, i64 %a34, i64 %a35)
+  ret void
+}
+
+define preserve_nonecc void @phi_tail(i1 %cond, i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12, i64 %a13, i64 %a14, i64 %a15, i64 %a16, i64 %a17, i64 %a18, i64 %a19, i64 %a20, i64 %a21, i64 %a22, i64 %a23, i64 %a24, i64 %a25, i64 %a26, i64 %a27, i64 %a28, i64 %a29, i64 %a30, i64 %a31, i64 %a32, i64 %a33, i64 %a34, i64 %a35) {
+; CHECK-LABEL: phi_tail:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movq 176(%rsp), %r10
+; CHECK-NEXT:    testb $1, %dil
+; CHECK-NEXT:    je .LBB2_2
+; CHECK:       # %bb.1:
+; CHECK-NEXT:    incq %r10
+; CHECK:       .LBB2_2:
+; CHECK-NEXT:    movq %r10, 176(%rsp)
+; CHECK-NEXT:    movzbl %dil, %edi
+; CHECK-NEXT:    jmp next_with_cond at PLT # TAILCALL
+entry:
+  br i1 %cond, label %a, label %b
+a:
+  %a32x = add i64 %a32, 1
+  br label %join
+b:
+  br label %join
+join:
+  %a32m = phi i64 [ %a32x, %a ], [ %a32, %b ]
+  musttail call preserve_nonecc void @next_with_cond(i1 %cond, i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, i64 %a9, i64 %a10, i64 %a11, i64 %a12, i64 %a13, i64 %a14, i64 %a15, i64 %a16, i64 %a17, i64 %a18, i64 %a19, i64 %a20, i64 %a21, i64 %a22, i64 %a23, i64 %a24, i64 %a25, i64 %a26, i64 %a27, i64 %a28, i64 %a29, i64 %a30, i64 %a31, i64 %a32m, i64 %a33, i64 %a34, i64 %a35)
+  ret void
+}



More information about the llvm-commits mailing list