Skip to content

Conversation

@mstorsjo
Copy link
Member

No description provided.

@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2025

@llvm/pr-subscribers-llvm-binary-utilities
@llvm/pr-subscribers-backend-aarch64

@llvm/pr-subscribers-llvm-mc

Author: Martin Storsjö (mstorsjo)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/169697.diff

4 Files Affected:

  • (modified) llvm/lib/MC/MCWin64EH.cpp (+21-1)
  • (modified) llvm/test/MC/AArch64/seh-packed-unwind.s (+83)
  • (modified) llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s (+2-1)
  • (modified) llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp (+8-4)
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
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);

@mstorsjo
Copy link
Member Author

CC @pmsjt @LouisLaf

This goes on top of #169676. This is a requirement for making the case in #169588 packed - I guess I got nerd sniped into implementing this part after all, for completeness. :-) But I won't try to adjust the prologue code generation to hit this.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with one minor comment.

@mstorsjo mstorsjo force-pushed the arm64-packed-unwind-specialcase-match branch from c495bde to 3d3c6da Compare November 26, 2025 19:06
@compnerd
Copy link
Member

But I won't try to adjust the prologue code generation to hit this.

How often does this pattern get emitted?

@efriedma-quic
Copy link
Collaborator

I don't think anyone has ever gathered complete stats for prologue forms. If you want to check for yourself, you can scan llvm-readelf --unwind output for str x30, [...] immediately followed by str x19, [...].

@efriedma-quic
Copy link
Collaborator

(I assumed you're asking for how much benefit we can get. If you're asking how frequently LLVM currently emits the sequence that will actually get compressed, the answer is "never".)

@compnerd
Copy link
Member

Your assumption is spot on - the question was about how profitable it would be. Basically, trying to ascertain if this is something valuable enough that it would make sense to spend some time to optimise the prologue generation or if it rare enough that we should leave it be for the time being and hope that we get to it some day.

@mstorsjo mstorsjo force-pushed the arm64-packed-unwind-specialcase-match branch from bd1b7ca to 78a4a58 Compare December 2, 2025 10:12
@mstorsjo mstorsjo merged commit 4a0b5bc into llvm:main Dec 2, 2025
10 checks passed
@mstorsjo mstorsjo deleted the arm64-packed-unwind-specialcase-match branch December 2, 2025 11:29
kcloudy0717 pushed a commit to kcloudy0717/llvm-project that referenced this pull request Dec 4, 2025
honeygoyal pushed a commit to honeygoyal/llvm-project that referenced this pull request Dec 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants