[llvm] [AArch64][Win] Account for the fixed object area when classifying CSR… (PR #222111)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 00:21:01 PDT 2026


https://github.com/kausgarg-qcom updated https://github.com/llvm/llvm-project/pull/222111

>From 8fd2c3f41a212a55b14bec1e0dcba00fbebc9a82 Mon Sep 17 00:00:00 2001
From: Kaustubh Garg <kausgarg at qti.qualcomm.com>
Date: Sat, 22 Aug 2026 23:06:11 +0530
Subject: [PATCH 1/3] [AArch64][Win] Account for fixed objects when resolving
 frame offsets

Include the fixed object area in the callee-saved threshold used by
resolveFrameOffsetReference(). Without this, objects in the
callee-saved area can be incorrectly addressed through the base
pointer in stack-realigned frames.
---
 .../Target/AArch64/AArch64FrameLowering.cpp   | 13 +++-
 .../CodeGen/AArch64/wineh-csr-area-spill.ll   | 61 +++++++++++++++++++
 2 files changed, 73 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AArch64/wineh-csr-area-spill.ll

diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index b41d575ec8b78..9a77cd060e495 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -1390,8 +1390,19 @@ StackOffset AArch64FrameLowering::resolveFrameOffsetReference(
 
   int64_t FPOffset = getFPOffset(MF, ObjectOffset).getFixed();
   int64_t Offset = getStackOffset(MF, ObjectOffset).getFixed();
+
+  // The fixed object area sits above the callee-saved area and can grow
+  // large enough to displace it; account for that displacement here so CSR
+  // objects aren't misclassified as locals and addressed via the base
+  // pointer.
+  const int64_t FixedObjectSize = getFixedObjectSize(
+      MF, AFI,
+      Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv(),
+                                   MF.getFunction().isVarArg()),
+      false);
   bool isCSR =
-      !isFixed && ObjectOffset >= -((int)AFI->getCalleeSavedStackSize(MFI));
+      !isFixed && ObjectOffset >= -((int64_t)AFI->getCalleeSavedStackSize(MFI) +
+                                    FixedObjectSize);
   bool isSVE = MFI.isScalableStackID(StackID);
 
   StackOffset ZPRStackSize = getZPRStackSize(MF);
diff --git a/llvm/test/CodeGen/AArch64/wineh-csr-area-spill.ll b/llvm/test/CodeGen/AArch64/wineh-csr-area-spill.ll
new file mode 100644
index 0000000000000..ff3399b5932c5
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/wineh-csr-area-spill.ll
@@ -0,0 +1,61 @@
+; RUN: llc %s --mtriple=aarch64-pc-windows-msvc -o - | FileCheck %s
+
+; Regression test: a stack object inside the callee-saved register area must
+; not be addressed via the base pointer, since its distance from the base
+; pointer varies with the stack realignment padding.
+
+; CHECK-LABEL: "?repro@@YAHH at Z":
+; CHECK:       str x19, [sp, #-64]!
+; CHECK:       add x29, sp, #8
+; CHECK:       sub x[[TMP:[0-9]+]], sp, #64
+; CHECK:       and sp, x[[TMP]], #0xffffffffffffffc0
+; CHECK:       mov x19, sp
+
+; This spill must be x29-relative, not reached through the base pointer, whose
+; distance to it varies with the realignment padding.
+; CHECK:       str wzr, [x29, #20]
+; CHECK-NOT:   [x19, #92]
+
+
+define i32 @"?repro@@YAHH at Z"(i32 %common.ret.op1) personality ptr @__CxxFrameHandler3 {
+entry:
+  %b.sroa.0 = alloca [16 x i32], align 64
+  %e = alloca ptr, align 8
+  %e156 = alloca ptr, align 8
+  %e162 = alloca ptr, align 8
+  call void @llvm.memset.p0.i64(ptr %b.sroa.0, i8 0, i64 0, i1 false)
+  invoke void @"?thrower@@YAXH at Z"(i32 0)
+          to label %common.ret unwind label %catch.dispatch
+
+catch.dispatch:                                   ; preds = %entry
+  %0 = catchswitch within none [label %catch173, label %catch161, label %catch155, label %catch] unwind to caller
+
+catch173:                                         ; preds = %catch.dispatch
+  %1 = catchpad within %0 [ptr null, i32 0, ptr null]
+  catchret from %1 to label %common.ret
+
+catch161:                                         ; preds = %catch.dispatch
+  %2 = catchpad within %0 [ptr null, i32 0, ptr %e162]
+  catchret from %2 to label %common.ret
+
+common.ret:                                       ; preds = %catch, %catch155, %catch161, %catch173, %entry
+  %common.ret.op11 = phi i32 [ 0, %catch155 ], [ 1, %catch ], [ 0, %catch161 ], [ 0, %catch173 ], [ 0, %entry ]
+  ret i32 %common.ret.op11
+
+catch155:                                         ; preds = %catch.dispatch
+  %3 = catchpad within %0 [ptr null, i32 0, ptr %e156]
+  br label %common.ret
+
+catch:                                            ; preds = %catch.dispatch
+  %4 = catchpad within %0 [ptr null, i32 0, ptr %e]
+  catchret from %4 to label %common.ret
+}
+
+; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
+declare void @llvm.memset.p0.i64(ptr writeonly captures(none), i8, i64, i1 immarg) #0
+
+declare i32 @__CxxFrameHandler3(...)
+
+declare void @"?thrower@@YAXH at Z"(i32)
+
+attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }

>From 8692e5650a7681d88bc16dd8e18110564fcb43af Mon Sep 17 00:00:00 2001
From: Kaustubh Garg <kausgarg at qti.qualcomm.com>
Date: Tue, 15 Sep 2026 10:16:33 +0530
Subject: [PATCH 2/3] [AArch64][NFC] Simplify getFixedObjectSize call in
 resolveFrameOffsetReference

Factor out the isCallingConvWin64() check into a named IsWin64
variable and annotate the IsFunclet argument.
---
 llvm/lib/Target/AArch64/AArch64FrameLowering.cpp | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index 9a77cd060e495..99ea16db5d390 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -1395,11 +1395,10 @@ StackOffset AArch64FrameLowering::resolveFrameOffsetReference(
   // large enough to displace it; account for that displacement here so CSR
   // objects aren't misclassified as locals and addressed via the base
   // pointer.
+  bool IsWin64 = Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv(),
+                                              MF.getFunction().isVarArg());
   const int64_t FixedObjectSize = getFixedObjectSize(
-      MF, AFI,
-      Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv(),
-                                   MF.getFunction().isVarArg()),
-      false);
+      MF, AFI, IsWin64, /*IsFunclet*/ false);
   bool isCSR =
       !isFixed && ObjectOffset >= -((int64_t)AFI->getCalleeSavedStackSize(MFI) +
                                     FixedObjectSize);

>From e5b8c88f78797e623a1ac1f76942d084235df015 Mon Sep 17 00:00:00 2001
From: Kaustubh Garg <kausgarg at qti.qualcomm.com>
Date: Mon, 21 Sep 2026 12:50:01 +0530
Subject: [PATCH 3/3] [AArch64][Win] Fix clang-format wrapping in
 resolveFrameOffsetReference

Reformat FixedObjectSize initializer per clang-format after dropping the merge commit.
---
 llvm/lib/Target/AArch64/AArch64FrameLowering.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
index 99ea16db5d390..64cb8a69eca22 100644
--- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
@@ -1397,8 +1397,8 @@ StackOffset AArch64FrameLowering::resolveFrameOffsetReference(
   // pointer.
   bool IsWin64 = Subtarget.isCallingConvWin64(MF.getFunction().getCallingConv(),
                                               MF.getFunction().isVarArg());
-  const int64_t FixedObjectSize = getFixedObjectSize(
-      MF, AFI, IsWin64, /*IsFunclet*/ false);
+  const int64_t FixedObjectSize =
+      getFixedObjectSize(MF, AFI, IsWin64, /*IsFunclet*/ false);
   bool isCSR =
       !isFixed && ObjectOffset >= -((int64_t)AFI->getCalleeSavedStackSize(MFI) +
                                     FixedObjectSize);



More information about the llvm-commits mailing list