From 362c71f4b5be15d63ee44cc0bfdd4d576fb4b5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Tue, 2 Dec 2025 15:47:17 +0200 Subject: [PATCH 1/2] [llvm-readobj] [ARMWinEH] Fix printing of packed unwind with H=1, RegI=RegF=0 In these cases, there are no other GPRs or float registers that would have been backed up before the register homing area. Normally, the register homing part of the prologue consists of 4 nop unwind codes. However, if we haven't allocated stack space for those arguments yet, we can't store them. The previous printout, printing "stp x0, x1, [sp, #-N]!" wouldn't work, as that wouldn't work when mapped to a nop unwind code. Based on "dumpbin -unwindinfo", and from empirical inspection with RtlVirtualUnwind, it turns out that the homing of argument registers is done outside of the prologue. In these cases, "dumpbin -unwindinfo" prints an annotation "(argument registers homed post-prolog)". Adjust the printout accordingly. In these cases, the later stack allocation (either "stp x29, x30, [sp, #-LocSZ]! or "sub sp, sp, #LocSZ") is adjusted to include the space the homed registers (i.e. be the full size from FrameSize). --- .../llvm-readobj/COFF/arm64-packed-unwind.s | 24 +++++++++++++++---- llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp | 17 +++++++------ 2 files changed, 27 insertions(+), 14 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 72e79b77a01ad..ae967f22d0c73 100644 --- a/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s +++ b/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s @@ -105,11 +105,7 @@ // CHECK-NEXT: CR: 0 // CHECK-NEXT: FrameSize: 112 // CHECK-NEXT: Prologue [ -// CHECK-NEXT: sub sp, sp, #48 -// CHECK-NEXT: stp x6, x7, [sp, #48] -// CHECK-NEXT: stp x4, x5, [sp, #32] -// CHECK-NEXT: stp x2, x3, [sp, #16] -// CHECK-NEXT: stp x0, x1, [sp, #-64]! +// CHECK-NEXT: sub sp, sp, #112 // CHECK-NEXT: end // CHECK-NEXT: ] // CHECK-NEXT: } @@ -276,6 +272,21 @@ // CHECK-NEXT: end // CHECK-NEXT: ] // CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func17 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 44 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 0 +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 96 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #-96]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } // CHECK-NEXT: ] .text @@ -296,6 +307,7 @@ func13: func14: func15: func16: +func17: ret .section .pdata,"dr" @@ -331,3 +343,5 @@ func16: .long 0x11820019 // FunctionLength=6 RegF=0 RegI=2 H=0 CR=0 FrameSize=34 .long func16@IMGREL .long 0x03b00039 // FunctionLength=14 RegF=0 RegI=0 H=1 CR=1 FrameSize=7 + .long func17@IMGREL + .long 0x0370002d // FunctionLength=11 RegF=0 RegI=0 H=1 CR=3 FrameSize=6 diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp index 3b2ea5b551117..32e3d059f44d3 100644 --- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -1404,6 +1404,12 @@ bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF, FpSZ += 8; int SavSZ = (IntSZ + FpSZ + 8 * 8 * RF.H() + 0xf) & ~0xf; int LocSZ = (RF.FrameSize() << 4) - SavSZ; + bool Homing = RF.H(); + + if (RF.H() && RF.RegI() == 0 && RF.RegF() == 0 && RF.CR() != 1) { + LocSZ += SavSZ; + Homing = false; + } if (RF.CR() == 2 || RF.CR() == 3) { SW.startLine() << "mov x29, sp\n"; @@ -1419,18 +1425,11 @@ bool Decoder::dumpPackedARM64Entry(const object::COFFObjectFile &COFF, } else if ((RF.CR() != 3 && RF.CR() != 2 && LocSZ > 0) || LocSZ > 512) { SW.startLine() << format("sub sp, sp, #%d\n", LocSZ); } - if (RF.H()) { + if (Homing) { SW.startLine() << format("stp x6, x7, [sp, #%d]\n", SavSZ - 16); SW.startLine() << format("stp x4, x5, [sp, #%d]\n", SavSZ - 32); SW.startLine() << format("stp x2, x3, [sp, #%d]\n", SavSZ - 48); - if (RF.RegI() > 0 || RF.RegF() > 0 || RF.CR() == 1) { - SW.startLine() << format("stp x0, x1, [sp, #%d]\n", SavSZ - 64); - } else { - // This case isn't documented; if neither RegI nor RegF nor CR=1 - // have decremented the stack pointer by SavSZ, we need to do it here - // (as the final stack adjustment of LocSZ excludes SavSZ). - SW.startLine() << format("stp x0, x1, [sp, #-%d]!\n", SavSZ); - } + SW.startLine() << format("stp x0, x1, [sp, #%d]\n", SavSZ - 64); } int FloatRegs = RF.RegF() > 0 ? RF.RegF() + 1 : 0; for (int I = (FloatRegs + 1) / 2 - 1; I >= 0; I--) { From ceb78b9182136ec5b5657ec17f97174ff1bb8bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Tue, 2 Dec 2025 21:50:48 +0200 Subject: [PATCH 2/2] Add a testcase for FrameSize=528 --- .../llvm-readobj/COFF/arm64-packed-unwind.s | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 ae967f22d0c73..489d385468b70 100644 --- a/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s +++ b/llvm/test/tools/llvm-readobj/COFF/arm64-packed-unwind.s @@ -287,6 +287,22 @@ // CHECK-NEXT: end // CHECK-NEXT: ] // CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func18 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 44 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 0 +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 528 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #0] +// CHECK-NEXT: sub sp, sp, #528 +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } // CHECK-NEXT: ] .text @@ -308,6 +324,7 @@ func14: func15: func16: func17: +func18: ret .section .pdata,"dr" @@ -345,3 +362,5 @@ func17: .long 0x03b00039 // FunctionLength=14 RegF=0 RegI=0 H=1 CR=1 FrameSize=7 .long func17@IMGREL .long 0x0370002d // FunctionLength=11 RegF=0 RegI=0 H=1 CR=3 FrameSize=6 + .long func18@IMGREL + .long 0x10f0002d // FunctionLength=11 RegF=0 RegI=0 H=1 CR=3 FrameSize=6