[llvm] 0b61db4 - [AArch64][Windows] Add llvm-readobj support for save_any_reg unwind opcode.

Eli Friedman via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 4 18:55:58 PDT 2022


Author: Eli Friedman
Date: 2022-10-04T18:55:01-07:00
New Revision: 0b61db423bfec26f110c27b7282c3f51aa0f3006

URL: https://github.com/llvm/llvm-project/commit/0b61db423bfec26f110c27b7282c3f51aa0f3006
DIFF: https://github.com/llvm/llvm-project/commit/0b61db423bfec26f110c27b7282c3f51aa0f3006.diff

LOG: [AArch64][Windows] Add llvm-readobj support for save_any_reg unwind opcode.

This is primarily used for Arm64EC, but it could be used for other
non-standard calling conventions. The testcase is based on an Arm64EC
thunk generated by MSVC.

The name save_any_reg comes from Microsoft documentation, but the full
encoding isn't specified there; this is reverse-engineered from the
behavior of the unwinder. (Thanks to Martin Storsjö for his example of
how to write simple unwinder testcases by directly calling
RtlVirtualUnwind.)

Differential Revision: https://reviews.llvm.org/D135196

Added: 
    llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s

Modified: 
    llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
    llvm/tools/llvm-readobj/ARMWinEHPrinter.h

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s b/llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s
new file mode 100644
index 0000000000000..bcf35f05809ec
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s
@@ -0,0 +1,98 @@
+// REQUIRES: aarch64-registered-target
+// RUN: llvm-mc -filetype=obj -triple aarch64-windows %s -o %t.o
+// RUN: llvm-readobj --unwind %t.o | FileCheck --strict-whitespace %s
+
+//CHECK:           Prologue [
+//CHECK-NEXT:        0xe1                ; mov fp, sp
+//CHECK-NEXT:        0x83                ; stp x29, x30, [sp, #-32]!
+//CHECK-NEXT:        0xe6                ; save next
+//CHECK-NEXT:        0xe6                ; save next
+//CHECK-NEXT:        0xe6                ; save next
+//CHECK-NEXT:        0xe6                ; save next
+//CHECK-NEXT:        0xe76689            ; stp q6, q7, [sp, #-160]!
+//CHECK-NEXT:        0xe4                ; end
+//CHECK-NEXT:      ]
+//CHECK-NEXT:      EpilogueScopes [
+//CHECK-NEXT:        EpilogueScope {
+//CHECK-NEXT:          StartOffset: 12
+//CHECK-NEXT:          EpilogueStartIndex: 10
+//CHECK-NEXT:          Opcodes [
+//CHECK-NEXT:            0x83                ; ldp x29, x30, [sp], #32
+//CHECK-NEXT:            0xe74e88            ; ldp q14, q15, [sp, #128]
+//CHECK-NEXT:            0xe74c86            ; ldp q12, q13, [sp, #96]
+//CHECK-NEXT:            0xe74a84            ; ldp q10, q11, [sp, #64]
+//CHECK-NEXT:            0xe74882            ; ldp q8, q9, [sp, #32]
+//CHECK-NEXT:            0xe76689            ; ldp q6, q7, [sp], #160
+//CHECK-NEXT:            0xe3                ; nop
+//CHECK-NEXT:            0xe3                ; nop
+//CHECK-NEXT:            0xe4                ; end
+//CHECK-NEXT:          ]
+//CHECK-NEXT:        }
+//CHECK-NEXT:      ]
+
+//CHECK:           Prologue [
+//CHECK-NEXT:        0xe70001            ; str x0, [sp, #8]
+//CHECK-NEXT:        0xe70041            ; str d0, [sp, #8]
+//CHECK-NEXT:        0xe70081            ; str q0, [sp, #16]
+//CHECK-NEXT:        0xe72001            ; str x0, [sp, #-32]!
+//CHECK-NEXT:        0xe77d01            ; stp x29, x30, [sp, #-32]!
+//CHECK-NEXT:        0xe4                ; end
+//CHECK-NEXT:      ]
+//CHECK-NEXT:      EpilogueScopes [
+//CHECK-NEXT:      ]
+
+.section .pdata,"dr"
+        .long func at IMGREL
+        .long "$unwind$func"@IMGREL
+        .long func2 at IMGREL
+        .long "$unwind$func2"@IMGREL
+
+        .text
+        .globl  func
+func:
+        stp q6, q7, [sp, #-160]!
+        stp q8, q9, [sp, #32]
+        stp q10, q11, [sp, #64]
+        stp q12, q13, [sp, #96]
+        stp q14, q15, [sp, #128]
+        stp x29, x30, [sp, #-32]!
+        mov x29, sp
+        str x0, [sp, #16]
+        str x9, [sp, #24]
+        ldr x0, [sp, #16]
+        ldr x8, [sp, #24]
+        blr x8
+        ldp x29, x30, [sp], #32
+        ldp q14, q15, [sp, #128]
+        ldp q12, q13, [sp, #96]
+        ldp q10, q11, [sp, #64]
+        ldp q8, q9, [sp, #32]
+        ldp q6, q7, [sp], #160
+        nop
+        ldr x16, [x16]
+        br  x16
+
+func2:
+        ret
+
+.section .xdata,"dr"
+"$unwind$func":
+.byte 0x15, 0x00, 0x40, 0x40
+.byte 0x0c, 0x00, 0x80, 0x02
+.byte 0xe1, 0x83, 0xe6, 0xe6
+.byte 0xe6, 0xe6, 0xe7, 0x66
+.byte 0x89, 0xe4, 0x83, 0xe7
+.byte 0x4e, 0x88, 0xe7, 0x4c
+.byte 0x86, 0xe7, 0x4a, 0x84
+.byte 0xe7, 0x48, 0x82, 0xe7
+.byte 0x66, 0x89, 0xe3, 0xe3
+.byte 0xe4, 0xe3, 0xe3, 0xe3
+"$unwind$func2":
+.byte 0x15, 0x00, 0x00, 0x40
+.byte 0xe7, 0x00, 0x01
+.byte 0xe7, 0x00, 0x41
+.byte 0xe7, 0x00, 0x81
+.byte 0xe7, 0x20, 0x01
+.byte 0xe7, 0x7d, 0x01
+.byte 0xe4
+.fill 20, 1, 0xe3

diff  --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
index fab99c8b7e7d8..188199c6c129d 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -143,35 +143,35 @@ const Decoder::RingEntry Decoder::Ring[] = {
   { 0xff, 0xff, 1, &Decoder::opcode_11111111 },  // UOP_END
 };
 
-
 // Unwind opcodes for ARM64.
 // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
 const Decoder::RingEntry Decoder::Ring64[] = {
-  { 0xe0, 0x00, 1, &Decoder::opcode_alloc_s },
-  { 0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x },
-  { 0xc0, 0x40, 1, &Decoder::opcode_save_fplr },
-  { 0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x },
-  { 0xf8, 0xc0, 2, &Decoder::opcode_alloc_m },
-  { 0xfc, 0xc8, 2, &Decoder::opcode_save_regp },
-  { 0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x },
-  { 0xfc, 0xd0, 2, &Decoder::opcode_save_reg },
-  { 0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x },
-  { 0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair },
-  { 0xfe, 0xd8, 2, &Decoder::opcode_save_fregp },
-  { 0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x },
-  { 0xfe, 0xdc, 2, &Decoder::opcode_save_freg },
-  { 0xff, 0xde, 2, &Decoder::opcode_save_freg_x },
-  { 0xff, 0xe0, 4, &Decoder::opcode_alloc_l },
-  { 0xff, 0xe1, 1, &Decoder::opcode_setfp },
-  { 0xff, 0xe2, 2, &Decoder::opcode_addfp },
-  { 0xff, 0xe3, 1, &Decoder::opcode_nop },
-  { 0xff, 0xe4, 1, &Decoder::opcode_end },
-  { 0xff, 0xe5, 1, &Decoder::opcode_end_c },
-  { 0xff, 0xe6, 1, &Decoder::opcode_save_next },
-  { 0xff, 0xe8, 1, &Decoder::opcode_trap_frame },
-  { 0xff, 0xe9, 1, &Decoder::opcode_machine_frame },
-  { 0xff, 0xea, 1, &Decoder::opcode_context },
-  { 0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call },
+    {0xe0, 0x00, 1, &Decoder::opcode_alloc_s},
+    {0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x},
+    {0xc0, 0x40, 1, &Decoder::opcode_save_fplr},
+    {0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x},
+    {0xf8, 0xc0, 2, &Decoder::opcode_alloc_m},
+    {0xfc, 0xc8, 2, &Decoder::opcode_save_regp},
+    {0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x},
+    {0xfc, 0xd0, 2, &Decoder::opcode_save_reg},
+    {0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x},
+    {0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair},
+    {0xfe, 0xd8, 2, &Decoder::opcode_save_fregp},
+    {0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x},
+    {0xfe, 0xdc, 2, &Decoder::opcode_save_freg},
+    {0xff, 0xde, 2, &Decoder::opcode_save_freg_x},
+    {0xff, 0xe0, 4, &Decoder::opcode_alloc_l},
+    {0xff, 0xe1, 1, &Decoder::opcode_setfp},
+    {0xff, 0xe2, 2, &Decoder::opcode_addfp},
+    {0xff, 0xe3, 1, &Decoder::opcode_nop},
+    {0xff, 0xe4, 1, &Decoder::opcode_end},
+    {0xff, 0xe5, 1, &Decoder::opcode_end_c},
+    {0xff, 0xe6, 1, &Decoder::opcode_save_next},
+    {0xff, 0xe7, 3, &Decoder::opcode_save_any_reg},
+    {0xff, 0xe8, 1, &Decoder::opcode_trap_frame},
+    {0xff, 0xe9, 1, &Decoder::opcode_machine_frame},
+    {0xff, 0xea, 1, &Decoder::opcode_context},
+    {0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call},
 };
 
 static void printRange(raw_ostream &OS, ListSeparator &LS, unsigned First,
@@ -869,6 +869,83 @@ bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset,
   return false;
 }
 
+bool Decoder::opcode_save_any_reg(const uint8_t *OC, unsigned &Offset,
+                                  unsigned Length, bool Prologue) {
+  // Whether the instruction has writeback
+  bool Writeback = (OC[Offset + 1] & 0x20) == 0x20;
+  // Whether the instruction is paired.  (Paired instructions are required
+  // to save/restore adjacent registers.)
+  bool Paired = (OC[Offset + 1] & 0x40) == 0x40;
+  // The kind of register saved:
+  // - 0 is an x register
+  // - 1 is the low half of a q register
+  // - 2 is a whole q register
+  int RegKind = (OC[Offset + 2] & 0xC0) >> 6;
+  // Encoded register name (0 -> x0/q0, 1 -> x1/q1, etc.)
+  int Reg = OC[Offset + 1] & 0x1F;
+  // Encoded stack offset of load/store instruction; decoding varies by mode.
+  int StackOffset = OC[Offset + 2] & 0x3F;
+  if (Writeback)
+    StackOffset++;
+  if (!Writeback && !Paired && RegKind != 2)
+    StackOffset *= 8;
+  else
+    StackOffset *= 16;
+
+  SW.startLine() << format("0x%02x%02x%02x            ; ", OC[Offset],
+                           OC[Offset + 1], OC[Offset + 2]);
+
+  // Verify the encoding is in a form we understand.  The high bit of the first
+  // byte, and mode 3 for the register kind are apparently reserved.  The
+  // encoded register must refer to a valid register.
+  int MaxReg = 0x1F;
+  if (Paired)
+    --MaxReg;
+  if (RegKind == 0)
+    --MaxReg;
+  if ((OC[Offset + 1] & 0x80) == 0x80 || RegKind == 3 || Reg > MaxReg) {
+    SW.getOStream() << "invalid save_any_reg encoding\n";
+    Offset += 3;
+    return false;
+  }
+
+  if (Paired) {
+    if (Prologue)
+      SW.getOStream() << "stp ";
+    else
+      SW.getOStream() << "ldp ";
+  } else {
+    if (Prologue)
+      SW.getOStream() << "str ";
+    else
+      SW.getOStream() << "ldr ";
+  }
+
+  char RegChar = 'x';
+  if (RegKind == 1) {
+    RegChar = 'd';
+  } else if (RegKind == 2) {
+    RegChar = 'q';
+  }
+
+  if (Paired)
+    SW.getOStream() << format("%c%d, %c%d, ", RegChar, Reg, RegChar, Reg + 1);
+  else
+    SW.getOStream() << format("%c%d, ", RegChar, Reg);
+
+  if (Writeback) {
+    if (Prologue)
+      SW.getOStream() << format("[sp, #-%d]!\n", StackOffset);
+    else
+      SW.getOStream() << format("[sp], #%d\n", StackOffset);
+  } else {
+    SW.getOStream() << format("[sp, #%d]\n", StackOffset);
+  }
+
+  Offset += 3;
+  return false;
+}
+
 bool Decoder::opcode_trap_frame(const uint8_t *OC, unsigned &Offset,
                                 unsigned Length, bool Prologue) {
   SW.startLine() << format("0x%02x                ; trap frame\n", OC[Offset]);

diff  --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
index ceaa866ff2150..eb2393de8f67d 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
@@ -121,6 +121,8 @@ class Decoder {
                     bool Prologue);
   bool opcode_save_next(const uint8_t *Opcodes, unsigned &Offset,
                         unsigned Length, bool Prologue);
+  bool opcode_save_any_reg(const uint8_t *Opcodes, unsigned &Offset,
+                           unsigned Length, bool Prologue);
   bool opcode_trap_frame(const uint8_t *Opcodes, unsigned &Offset,
                          unsigned Length, bool Prologue);
   bool opcode_machine_frame(const uint8_t *Opcodes, unsigned &Offset,


        


More information about the llvm-commits mailing list