[llvm] [X86] Reuse reserved call frames in Win64 exception funclets (PR #226340)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 19:51:46 PDT 2026
https://github.com/kingakasa created https://github.com/llvm/llvm-project/pull/226340
Win64 exception funclets already reserve outgoing argument space in their prologues, including when the parent function has a variable-sized allocation. However, call-frame pseudo elimination uses the parent function's reserved-frame decision and can emit additional per-call stack adjustments inside the funclet. These adjustments disagree with its unwind information.
Use EH-scope membership to recognize funclet blocks and reuse their reserved call frames. Add a regression with a dynamic alloca and a catch funclet making a call with stack arguments.
Validation:
- The new regression fails on the baseline at `-O0` because of the extra stack adjustment; the patched compiler passes at `-O0` and `-O2`.
- Full X86 CodeGen suite: 5,668 passed, 15 expected failures, 3 unsupported; no unexpected failures.
- CodeGen unit tests: 274 passed, 319 internally skipped.
Related: #191616 addresses the wider Win64 dynamic-allocation/call-frame problem. This change is limited to eliminating call-frame pseudos within exception funclets; it does not change parent-frame dynamic-allocation lowering.
Tested independently on Windows x64 using a fresh Release LLVM/Clang build with assertions enabled and a matching Release ASan runtime, based on `6dc4a4ba2850369afd537001377fb9d404653d26`.
Changed-line clang-format and git diff --check pass.
Assisted-by: OpenAI Codex
>From fa4533198eea106c180722bf31b0d32bb1ca4bee Mon Sep 17 00:00:00 2001
From: Kinga Kasa <kasakinga14 at gmail.com>
Date: Thu, 24 Sep 2026 22:50:02 -0400
Subject: [PATCH] [X86] Reuse reserved call frames in Win64 exception funclets
Win64 exception funclets already reserve outgoing argument space in their prologues, including when the parent function has a variable-sized allocation. However, call-frame pseudo elimination uses the parent function's reserved-frame decision and can emit additional per-call stack adjustments inside the funclet. These adjustments disagree with its unwind information.
Use EH-scope membership to recognize funclet blocks and reuse their reserved call frames. Add a regression with a dynamic alloca and a catch funclet making a call with stack arguments.
Validation:
- The new regression fails on the baseline at `-O0` because of the extra stack adjustment; the patched compiler passes at `-O0` and `-O2`.
- Full X86 CodeGen suite: 5,668 passed, 15 expected failures, 3 unsupported; no unexpected failures.
- CodeGen unit tests: 274 passed, 319 internally skipped.
Related: #191616 addresses the wider Win64 dynamic-allocation/call-frame problem. This change is limited to eliminating call-frame pseudos within exception funclets; it does not change parent-frame dynamic-allocation lowering.
Tested independently on Windows x64 using a fresh Release LLVM/Clang build with assertions enabled and a matching Release ASan runtime, based on `6dc4a4ba2850369afd537001377fb9d404653d26`.
Changed-line clang-format and git diff --check pass.
Assisted-by: OpenAI Codex
---
llvm/lib/Target/X86/X86FrameLowering.cpp | 11 ++++++++
.../X86/win64-funclet-dynamic-call-frame.ll | 26 +++++++++++++++++++
2 files changed, 37 insertions(+)
create mode 100644 llvm/test/CodeGen/X86/win64-funclet-dynamic-call-frame.ll
diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp
index a25aba6d0afe0..9798a2b69f2bd 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -19,6 +19,7 @@
#include "X86Subtarget.h"
#include "X86TargetMachine.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
@@ -4035,6 +4036,16 @@ MachineBasicBlock::iterator X86FrameLowering::eliminateCallFramePseudoInstr(
MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const {
bool reserveCallFrame = hasReservedCallFrame(MF);
+ // Win64 funclet prologues reserve their outgoing argument space even when
+ // the parent frame has a variable-sized allocation. Their frame pointer
+ // addresses the parent frame, so additional stack adjustments cannot be
+ // described by the funclet's unwind information.
+ if (!reserveCallFrame && STI.isTargetWin64() && MF.hasEHFunclets()) {
+ auto Membership = getEHScopeMembership(MF);
+ auto Scope = Membership.find(&MBB);
+ if (Scope != Membership.end() && Scope->second != MF.front().getNumber())
+ reserveCallFrame = true;
+ }
unsigned Opcode = I->getOpcode();
bool isDestroy = Opcode == TII.getCallFrameDestroyOpcode();
DebugLoc DL = I->getDebugLoc(); // copy DebugLoc as I will be erased.
diff --git a/llvm/test/CodeGen/X86/win64-funclet-dynamic-call-frame.ll b/llvm/test/CodeGen/X86/win64-funclet-dynamic-call-frame.ll
new file mode 100644
index 0000000000000..6dd92a00297a2
--- /dev/null
+++ b/llvm/test/CodeGen/X86/win64-funclet-dynamic-call-frame.ll
@@ -0,0 +1,26 @@
+; RUN: llc -mtriple=x86_64-pc-windows-msvc -O0 < %s | FileCheck %s
+; RUN: llc -mtriple=x86_64-pc-windows-msvc -O2 < %s | FileCheck %s
+; Funclets already reserve outgoing argument space in their prologues, even
+; when the parent has a variable-sized stack allocation.
+declare i32 @__CxxFrameHandler3(...)
+declare void @use(ptr)
+declare void @may_throw(i64, i64, i64, i64, i64, i64)
+define void @dynamic_catch(i64 %n) personality ptr @__CxxFrameHandler3 {
+entry:
+ %a = alloca i8, i64 %n, align 32
+ invoke void @use(ptr %a) to label %done unwind label %dispatch
+dispatch:
+ %switch = catchswitch within none [label %catch] unwind to caller
+catch:
+ %pad = catchpad within %switch [ptr null, i32 64, ptr null]
+ call void @may_throw(i64 1, i64 2, i64 3, i64 4, i64 5, i64 6) [ "funclet"(token %pad) ]
+ catchret from %pad to label %done
+done:
+ ret void
+}
+; CHECK-LABEL: "?catch$
+; CHECK: .seh_endprologue
+; CHECK-NOT: subq {{.*}}%rsp
+; CHECK: callq may_throw
+; CHECK-NOT: addq {{.*}}%rsp
+; CHECK: .seh_startepilogue
More information about the llvm-commits
mailing list