[llvm] [MC] [Win64EH] Produce packed unwind for the special case of X19+LR (PR #169697)
Martin Storsjö via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 26 09:47:31 PST 2025
https://github.com/mstorsjo created https://github.com/llvm/llvm-project/pull/169697
None
>From de454d027959695f82995650e7631ecf20479a28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Wed, 26 Nov 2025 18:08:58 +0200
Subject: [PATCH 1/2] [llvm-readobj] [ARMWinEH] Fix the interpretation of
packed unwind CR=01 RegI=1
Even though the table for how to expand packed unwind info at [1]
doesn't explicitly say this, this case is mentioned at [2] under
the case "Only x19 saved":
sub sp,sp,#16 // reg save area allocation*
stp x19,lr,[sp] // save x19, lr
sub sp,sp,#(framesz-16) // allocate the remaining local area
This was discussed and clarified at [3].
[1] https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#packed-unwind-data
[2] https://learn.microsoft.com/en-us/cpp/build/arm64-exception-handling?view=msvc-170#arm64-stack-frame-layout
[3] https://github.com/llvm/llvm-project/issues/169588#issuecomment-3581688753
---
.../tools/llvm-readobj/COFF/arm64-packed-unwind.s | 3 ++-
llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp | 12 ++++++++----
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s b/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s
index d9953ccc3f3d8..72e79b77a01ad 100644
--- a/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s
+++ b/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s
@@ -139,7 +139,8 @@
// CHECK-NEXT: FrameSize: 32
// CHECK-NEXT: Prologue [
// CHECK-NEXT: sub sp, sp, #16
-// CHECK-NEXT: INVALID!
+// CHECK-NEXT: stp x19, lr, [sp]
+// CHECK-NEXT: sub sp, sp, #16
// CHECK-NEXT: end
// CHECK-NEXT: ]
// CHECK-NEXT: }
diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
index c6e409c63ef3a..eedf6732ad3f4 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -1457,10 +1457,14 @@ bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF,
// The last register, an odd register without a pair
if (RF.CR() == 1) {
if (I == 0) { // If this is the only register pair
- // CR=1 combined with RegI=1 doesn't map to a documented case;
- // it doesn't map to any regular unwind info opcode, and the
- // actual unwinder doesn't support it.
- SW.startLine() << "INVALID!\n";
+ // CR=1 combined with RegI=1 maps to a special case; there's
+ // no unwind info opcode that saves a GPR together with LR
+ // with writeback to sp (no save_lrpair_x).
+ // Instead, this case expands to two instructions; a preceding
+ // (in prologue execution order) "sub sp, sp, #16", followed
+ // by a regular "stp x19, lr, [sp]" (save_lrpair).
+ SW.startLine() << format("stp x%d, lr, [sp]\n", 19);
+ SW.startLine() << format("sub sp, sp, #%d\n", SavSZ);
} else
SW.startLine() << format("stp x%d, lr, [sp, #%d]\n", 19 + 2 * I,
16 * I);
>From c495bde181ccf6acedf69dda6378e093e1fb804a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin at martin.st>
Date: Wed, 26 Nov 2025 19:41:44 +0200
Subject: [PATCH 2/2] [MC] [Win64EH] Produce packed unwind for the special case
of X19+LR
---
llvm/lib/MC/MCWin64EH.cpp | 22 ++++++-
llvm/test/MC/AArch64/seh-packed-unwind.s | 83 ++++++++++++++++++++++++
2 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp
index 6d146f6cedd6e..97585fbd5791d 100644
--- a/llvm/lib/MC/MCWin64EH.cpp
+++ b/llvm/lib/MC/MCWin64EH.cpp
@@ -1051,7 +1051,9 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength,
// the order - that would work fine when unwinding from within
// functions, but not be exactly right if unwinding happens within
// prologs/epilogs.
- for (const WinEH::Instruction &Inst : info->Instructions) {
+ for (auto It = info->Instructions.begin(), EndIt = info->Instructions.end();
+ It != EndIt; It++) {
+ const WinEH::Instruction &Inst = *It;
switch (Inst.Operation) {
case Win64EH::UOP_End:
if (Location != Start)
@@ -1169,6 +1171,24 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength,
Location != FloatRegs && Location != InputArgs &&
Location != StackAdjust)
return false;
+ if (Location == Start2) { // Can't have this at Start3, after PACSignLR
+ auto NextIt = It + 1;
+ if (NextIt != EndIt) {
+ const WinEH::Instruction &NextInst = *NextIt;
+ if (NextInst.Operation == Win64EH::UOP_SaveLRPair &&
+ NextInst.Offset == 0 && NextInst.Register == 19) {
+ assert(Predecrement == 0);
+ assert(RegI == 0);
+ assert(!StandaloneLR);
+ Predecrement = Inst.Offset;
+ RegI = 1;
+ StandaloneLR = true;
+ Location = FloatRegs;
+ It++; // Consume both the Alloc and the SaveLRPair
+ continue;
+ }
+ }
+ }
// Can have either a single decrement, or a pair of decrements with
// 4080 and another decrement.
if (StackOffset == 0)
diff --git a/llvm/test/MC/AArch64/seh-packed-unwind.s b/llvm/test/MC/AArch64/seh-packed-unwind.s
index 5b86ab4bc0d49..89269b864e5e9 100644
--- a/llvm/test/MC/AArch64/seh-packed-unwind.s
+++ b/llvm/test/MC/AArch64/seh-packed-unwind.s
@@ -295,6 +295,24 @@
// CHECK-NEXT: end
// CHECK-NEXT: ]
// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func19 (0x2A8)
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 48
+// CHECK-NEXT: RegF: 2
+// CHECK-NEXT: RegI: 1
+// CHECK-NEXT: HomedParameters: No
+// CHECK-NEXT: CR: 1
+// CHECK-NEXT: FrameSize: 112
+// CHECK-NEXT: Prologue [
+// CHECK-NEXT: sub sp, sp, #64
+// CHECK-NEXT: str d10, [sp, #32]
+// CHECK-NEXT: stp d8, d9, [sp, #16]
+// CHECK-NEXT: stp x19, lr, [sp]
+// CHECK-NEXT: sub sp, sp, #48
+// CHECK-NEXT: end
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
// CHECK: RuntimeFunction {
// CHECK-NEXT: Function: nonpacked1
// CHECK-NEXT: ExceptionRecord:
@@ -374,6 +392,11 @@
// CHECK-NEXT: Function: nonpacked16
// CHECK-NEXT: ExceptionRecord:
// CHECK-NEXT: ExceptionData {
+// CHECK: EpiloguePacked: Yes
+// CHECK: RuntimeFunction {
+// CHECK-NEXT: Function: nonpacked17
+// CHECK-NEXT: ExceptionRecord:
+// CHECK-NEXT: ExceptionData {
// CHECK: EpiloguePacked: Yes
@@ -809,6 +832,35 @@ func18:
ret
.seh_endproc
+func19:
+ .seh_proc func19
+ sub sp, sp, #48
+ .seh_stackalloc 48
+ stp x19, lr, [sp]
+ .seh_save_lrpair x19, 0
+ stp d8, d9, [sp, #16]
+ .seh_save_fregp d8, 16
+ str d10, [sp, #32]
+ .seh_save_freg d10, 32
+ sub sp, sp, #64
+ .seh_stackalloc 64
+ .seh_endprologue
+ nop
+ .seh_startepilogue
+ add sp, sp, #64
+ .seh_stackalloc 64
+ ldr d10, [sp, #32]
+ .seh_save_freg d10, 32
+ ldp d8, d9, [sp, #16]
+ .seh_save_fregp d8, 16
+ ldp x19, lr, [sp]
+ .seh_save_lrpair x19, 0
+ add sp, sp, #48
+ .seh_stackalloc 48
+ .seh_endepilogue
+ ret
+ .seh_endproc
+
nonpacked1:
.seh_proc nonpacked1
// Can't be packed; can't save integer registers after float registers.
@@ -1157,3 +1209,34 @@ nonpacked16:
.seh_endepilogue
br x9
.seh_endproc
+
+nonpacked17:
+ .seh_proc nonpacked17
+ // Can't be packed; more predecrement for SavSZ than used for
+ // corresponding RegI/RegF/LR saves
+ sub sp, sp, #64
+ .seh_stackalloc 64
+ stp x19, lr, [sp]
+ .seh_save_lrpair x19, 0
+ stp d8, d9, [sp, #16]
+ .seh_save_fregp d8, 16
+ str d10, [sp, #32]
+ .seh_save_freg d10, 32
+ sub sp, sp, #64
+ .seh_stackalloc 64
+ .seh_endprologue
+ nop
+ .seh_startepilogue
+ add sp, sp, #64
+ .seh_stackalloc 64
+ ldr d10, [sp, #32]
+ .seh_save_freg d10, 32
+ ldp d8, d9, [sp, #16]
+ .seh_save_fregp d8, 16
+ ldp x19, lr, [sp]
+ .seh_save_lrpair x19, 0
+ add sp, sp, #64
+ .seh_stackalloc 64
+ .seh_endepilogue
+ ret
+ .seh_endproc
More information about the llvm-commits
mailing list