[llvm] [llvm-readobj] [ARMWinEH] Fix the interpretation of packed unwind CR=01 RegI=1 (PR #169676)

Martin Storsjö via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 26 09:44:23 PST 2025


https://github.com/mstorsjo updated https://github.com/llvm/llvm-project/pull/169676

>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] [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);



More information about the llvm-commits mailing list