From cabefea2ec99f80ecdf9d3d5fe955831532ff4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 13 May 2022 10:42:56 +0300 Subject: [PATCH] [MC] [Win64EH] Try writing an ARM64 "packed epilog" even if the epilog doesn't share opcodes with the prolog The "packed epilog" form only implies that the epilog is located exactly at the end of the function (so the location of the epilog is implicit from the epilog opcodes), but it doesn't have to share opcodes with the prolog - as long as the total number of opcode bytes and the offset to the epilog fit within the bitfields. This avoids writing a 4 byte epilog scope in many cases. (I haven't measured how much this shrinks actual xdata sections in practice though.) Differential Revision: https://reviews.llvm.org/D125536 --- llvm/lib/MC/MCWin64EH.cpp | 64 +++++++++++++++--------- llvm/test/CodeGen/AArch64/wineh1.mir | 28 ++++------- llvm/test/CodeGen/AArch64/wineh2.mir | 32 +++++------- llvm/test/CodeGen/AArch64/wineh5.mir | 22 +++----- llvm/test/MC/AArch64/seh-optimize.s | 18 +++---- llvm/test/MC/AArch64/seh-packed-epilog.s | 52 +++++++++++++++---- 6 files changed, 120 insertions(+), 96 deletions(-) diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp index 0aa066eba594..73d3a53605c8 100644 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -614,17 +614,6 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, const std::vector &Epilog = info->EpilogMap.begin()->second; - // Can pack if the epilog is a subset of the prolog but not vice versa - if (Epilog.size() > info->Instructions.size()) - return -1; - - // Check that the epilog actually is a perfect match for the end (backwrds) - // of the prolog. - for (int I = Epilog.size() - 1; I >= 0; I--) { - if (info->Instructions[I] != Epilog[Epilog.size() - 1 - I]) - return -1; - } - // Check that the epilog actually is at the very end of the function, // otherwise it can't be packed. uint32_t DistanceFromEnd = (uint32_t)GetAbsDifference( @@ -632,6 +621,26 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, if (DistanceFromEnd / 4 != Epilog.size()) return -1; + int RetVal = -1; + // Even if we don't end up sharing opcodes with the prolog, we can still + // write the offset as a packed offset, if the single epilog is located at + // the end of the function and the offset (pointing after the prolog) fits + // as a packed offset. + if (PrologCodeBytes <= 31 && + PrologCodeBytes + ARM64CountOfUnwindCodes(Epilog) <= 124) + RetVal = PrologCodeBytes; + + // Can pack if the epilog is a subset of the prolog but not vice versa + if (Epilog.size() > info->Instructions.size()) + return RetVal; + + // Check that the epilog actually is a perfect match for the end (backwrds) + // of the prolog. + for (int I = Epilog.size() - 1; I >= 0; I--) { + if (info->Instructions[I] != Epilog[Epilog.size() - 1 - I]) + return RetVal; + } + int Offset = Epilog.size() == info->Instructions.size() ? 0 : ARM64CountOfUnwindCodes(ArrayRef( @@ -642,8 +651,10 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, // unclear whether the epilog count in the extension word can be taken // as packed epilog offset. if (Offset > 31 || PrologCodeBytes > 124) - return -1; + return RetVal; + // As we choose to express the epilog as part of the prolog, remove the + // epilog from the map, so we don't try to emit its opcodes. info->EpilogMap.clear(); return Offset; } @@ -952,8 +963,9 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, int PackedEpilogOffset = checkPackedEpilog(streamer, info, PrologCodeBytes); - if (PackedEpilogOffset >= 0 && !info->HandlesExceptions && - FuncLength <= 0x7ff && TryPacked) { + if (PackedEpilogOffset >= 0 && + uint32_t(PackedEpilogOffset) < PrologCodeBytes && + !info->HandlesExceptions && FuncLength <= 0x7ff && TryPacked) { // Matching prolog/epilog and no exception handlers; check if the // prolog matches the patterns that can be described by the packed // format. @@ -1025,17 +1037,19 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, streamer.emitInt32(row2); } - // Epilog Start Index, Epilog Start Offset - for (auto &I : EpilogInfo) { - MCSymbol *EpilogStart = I.first; - uint32_t EpilogIndex = I.second; - uint32_t EpilogOffset = - (uint32_t)GetAbsDifference(streamer, EpilogStart, info->Begin); - if (EpilogOffset) - EpilogOffset /= 4; - uint32_t row3 = EpilogOffset; - row3 |= (EpilogIndex & 0x3FF) << 22; - streamer.emitInt32(row3); + if (PackedEpilogOffset < 0) { + // Epilog Start Index, Epilog Start Offset + for (auto &I : EpilogInfo) { + MCSymbol *EpilogStart = I.first; + uint32_t EpilogIndex = I.second; + uint32_t EpilogOffset = + (uint32_t)GetAbsDifference(streamer, EpilogStart, info->Begin); + if (EpilogOffset) + EpilogOffset /= 4; + uint32_t row3 = EpilogOffset; + row3 |= (EpilogIndex & 0x3FF) << 22; + streamer.emitInt32(row3); + } } // Emit prolog unwind instructions (in reverse order). diff --git a/llvm/test/CodeGen/AArch64/wineh1.mir b/llvm/test/CodeGen/AArch64/wineh1.mir index 5aec175181d3..1ba551a0f04b 100644 --- a/llvm/test/CodeGen/AArch64/wineh1.mir +++ b/llvm/test/CodeGen/AArch64/wineh1.mir @@ -16,8 +16,8 @@ # CHECK-NEXT: FunctionLength: 96 # CHECK-NEXT: Version: 0 # CHECK-NEXT: ExceptionData: No -# CHECK-NEXT: EpiloguePacked: No -# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: EpiloguePacked: Yes +# CHECK-NEXT: EpilogueOffset: 13 # CHECK-NEXT: ByteCodeLength: 28 # CHECK-NEXT: Prologue [ # CHECK-NEXT: 0xc808 ; stp x19, x20, [sp, #64] @@ -28,21 +28,15 @@ # CHECK-NEXT: 0xce09 ; stp x27, x28, [sp, #-80]! # CHECK-NEXT: 0xe4 ; end # CHECK-NEXT: ] -# CHECK-NEXT: EpilogueScopes [ -# CHECK-NEXT: EpilogueScope { -# CHECK-NEXT: StartOffset: 16 -# CHECK-NEXT: EpilogueStartIndex: 13 -# CHECK-NEXT: Opcodes [ -# CHECK-NEXT: 0xc808 ; ldp x19, x20, [sp, #64] -# CHECK-NEXT: 0xd086 ; ldr x21, [sp, #48] -# CHECK-NEXT: 0xe3 ; nop -# CHECK-NEXT: 0xd0c7 ; ldr x22, [sp, #56] -# CHECK-NEXT: 0xc904 ; ldp x23, x24, [sp, #32] -# CHECK-NEXT: 0xc982 ; ldp x25, x26, [sp, #16] -# CHECK-NEXT: 0xce09 ; ldp x27, x28, [sp], #80 -# CHECK-NEXT: 0xe4 ; end -# CHECK-NEXT: ] -# CHECK-NEXT: } +# CHECK-NEXT: Epilogue [ +# CHECK-NEXT: 0xc808 ; ldp x19, x20, [sp, #64] +# CHECK-NEXT: 0xd086 ; ldr x21, [sp, #48] +# CHECK-NEXT: 0xe3 ; nop +# CHECK-NEXT: 0xd0c7 ; ldr x22, [sp, #56] +# CHECK-NEXT: 0xc904 ; ldp x23, x24, [sp, #32] +# CHECK-NEXT: 0xc982 ; ldp x25, x26, [sp, #16] +# CHECK-NEXT: 0xce09 ; ldp x27, x28, [sp], #80 +# CHECK-NEXT: 0xe4 ; end # CHECK-NEXT: ] # CHECK-NEXT: } diff --git a/llvm/test/CodeGen/AArch64/wineh2.mir b/llvm/test/CodeGen/AArch64/wineh2.mir index 81c73460dd9e..9dae28236f45 100644 --- a/llvm/test/CodeGen/AArch64/wineh2.mir +++ b/llvm/test/CodeGen/AArch64/wineh2.mir @@ -6,8 +6,8 @@ # CHECK-NEXT: FunctionLength: 144 # CHECK-NEXT: Version: 0 # CHECK-NEXT: ExceptionData: No -# CHECK-NEXT: EpiloguePacked: No -# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: EpiloguePacked: Yes +# CHECK-NEXT: EpilogueOffset: 19 # CHECK-NEXT: ByteCodeLength: 40 # CHECK-NEXT: Prologue [ # CHECK-NEXT: 0xc80e ; stp x19, x20, [sp, #112] @@ -21,23 +21,17 @@ # CHECK-NEXT: 0xde8f ; str d12, [sp, #-128]! # CHECK-NEXT: 0xe4 ; end # CHECK-NEXT: ] -# CHECK-NEXT: EpilogueScopes [ -# CHECK-NEXT: EpilogueScope { -# CHECK-NEXT: StartOffset: 26 -# CHECK-NEXT: EpilogueStartIndex: 19 -# CHECK-NEXT: Opcodes [ -# CHECK-NEXT: 0xc80e ; ldp x19, x20, [sp, #112] -# CHECK-NEXT: 0xc88c ; ldp x21, x22, [sp, #96] -# CHECK-NEXT: 0xc90a ; ldp x23, x24, [sp, #80] -# CHECK-NEXT: 0xc988 ; ldp x25, x26, [sp, #64] -# CHECK-NEXT: 0xca06 ; ldp x27, x28, [sp, #48] -# CHECK-NEXT: 0xdc04 ; ldr d8, [sp, #32] -# CHECK-NEXT: 0xdc45 ; ldr d9, [sp, #40] -# CHECK-NEXT: 0xd882 ; ldp d10, d11, [sp, #16] -# CHECK-NEXT: 0xde8f ; ldr d12, [sp], #128 -# CHECK-NEXT: 0xe4 ; end -# CHECK-NEXT: ] -# CHECK-NEXT: } +# CHECK-NEXT: Epilogue [ +# CHECK-NEXT: 0xc80e ; ldp x19, x20, [sp, #112] +# CHECK-NEXT: 0xc88c ; ldp x21, x22, [sp, #96] +# CHECK-NEXT: 0xc90a ; ldp x23, x24, [sp, #80] +# CHECK-NEXT: 0xc988 ; ldp x25, x26, [sp, #64] +# CHECK-NEXT: 0xca06 ; ldp x27, x28, [sp, #48] +# CHECK-NEXT: 0xdc04 ; ldr d8, [sp, #32] +# CHECK-NEXT: 0xdc45 ; ldr d9, [sp, #40] +# CHECK-NEXT: 0xd882 ; ldp d10, d11, [sp, #16] +# CHECK-NEXT: 0xde8f ; ldr d12, [sp], #128 +# CHECK-NEXT: 0xe4 ; end # CHECK-NEXT: ] # CHECK-NEXT: } ... diff --git a/llvm/test/CodeGen/AArch64/wineh5.mir b/llvm/test/CodeGen/AArch64/wineh5.mir index 46b127a05a93..06456329778e 100644 --- a/llvm/test/CodeGen/AArch64/wineh5.mir +++ b/llvm/test/CodeGen/AArch64/wineh5.mir @@ -7,8 +7,8 @@ # CHECK-NEXT: FunctionLength: 156 # CHECK-NEXT: Version: 0 # CHECK-NEXT: ExceptionData: No -# CHECK-NEXT: EpiloguePacked: No -# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: EpiloguePacked: Yes +# CHECK-NEXT: EpilogueOffset: 10 # CHECK-NEXT: ByteCodeLength: 20 # CHECK-NEXT: Prologue [ # CHECK-NEXT: 0xe002dac9 ; sub sp, #2993296 @@ -18,18 +18,12 @@ # CHECK-NEXT: 0xd53f ; str x28, [sp, #-256]! # CHECK-NEXT: 0xe4 ; end # CHECK-NEXT: ] -# CHECK-NEXT: EpilogueScopes [ -# CHECK-NEXT: EpilogueScope { -# CHECK-NEXT: StartOffset: 34 -# CHECK-NEXT: EpilogueStartIndex: 10 -# CHECK-NEXT: Opcodes [ -# CHECK-NEXT: 0xe002da00 ; add sp, #2990080 -# CHECK-NEXT: 0xc0c9 ; add sp, #3216 -# CHECK-NEXT: 0x42 ; ldp x29, x30, [sp, #16] -# CHECK-NEXT: 0xd53f ; ldr x28, [sp], #256 -# CHECK-NEXT: 0xe4 ; end -# CHECK-NEXT: ] -# CHECK-NEXT: } +# CHECK-NEXT: Epilogue [ +# CHECK-NEXT: 0xe002da00 ; add sp, #2990080 +# CHECK-NEXT: 0xc0c9 ; add sp, #3216 +# CHECK-NEXT: 0x42 ; ldp x29, x30, [sp, #16] +# CHECK-NEXT: 0xd53f ; ldr x28, [sp], #256 +# CHECK-NEXT: 0xe4 ; end # CHECK-NEXT: ] # CHECK-NEXT: } diff --git a/llvm/test/MC/AArch64/seh-optimize.s b/llvm/test/MC/AArch64/seh-optimize.s index 0bf33af9cc75..56008888fec9 100644 --- a/llvm/test/MC/AArch64/seh-optimize.s +++ b/llvm/test/MC/AArch64/seh-optimize.s @@ -27,17 +27,13 @@ // CHECK-NEXT: 0xe201 ; add fp, sp, #8 // CHECK-NEXT: 0xe4 ; end // CHECK-NEXT: ] -// CHECK-NEXT: EpilogueScopes [ -// CHECK-NEXT: EpilogueScope { -// CHECK: Opcodes [ -// CHECK-NEXT: 0xc904 ; ldp x23, x24, [sp, #32] -// CHECK-NEXT: 0xe6 ; restore next -// CHECK-NEXT: 0xcc83 ; ldp x21, x22, [sp], #32 -// CHECK-NEXT: 0x24 ; ldp x19, x20, [sp], #32 -// CHECK-NEXT: 0xcc1f ; ldp x19, x20, [sp], #256 -// CHECK-NEXT: 0xe4 ; end -// CHECK-NEXT: ] -// CHECK-NEXT: } +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0xc904 ; ldp x23, x24, [sp, #32] +// CHECK-NEXT: 0xe6 ; restore next +// CHECK-NEXT: 0xcc83 ; ldp x21, x22, [sp], #32 +// CHECK-NEXT: 0x24 ; ldp x19, x20, [sp], #32 +// CHECK-NEXT: 0xcc1f ; ldp x19, x20, [sp], #256 +// CHECK-NEXT: 0xe4 ; end // CHECK-NEXT: ] // CHECK-NEXT: } // CHECK-NEXT: } diff --git a/llvm/test/MC/AArch64/seh-packed-epilog.s b/llvm/test/MC/AArch64/seh-packed-epilog.s index f9978ea7a113..85ac8e80dbdd 100644 --- a/llvm/test/MC/AArch64/seh-packed-epilog.s +++ b/llvm/test/MC/AArch64/seh-packed-epilog.s @@ -43,6 +43,13 @@ // CHECK-NEXT: ExceptionData { // CHECK: ExceptionData: // CHECK-NEXT: EpiloguePacked: Yes +// CHECK-NEXT: EpilogueOffset: 0 +// CHECK-NEXT: ByteCodeLength: 4 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: 0x83 ; stp x29, x30, [sp, #-32]! +// CHECK-NEXT: 0x03 ; sub sp, #48 +// CHECK-NEXT: 0xe4 ; end +// CHECK-NEXT: ] // CHECK: RuntimeFunction { // CHECK-NEXT: Function: nonpacked1 // CHECK-NEXT: ExceptionRecord: @@ -50,17 +57,42 @@ // CHECK: ExceptionData: // CHECK-NEXT: EpiloguePacked: No // CHECK: RuntimeFunction { -// CHECK-NEXT: Function: nonpacked2 +// CHECK-NEXT: Function: nonshared2 // CHECK-NEXT: ExceptionRecord: // CHECK-NEXT: ExceptionData { // CHECK: ExceptionData: -// CHECK-NEXT: EpiloguePacked: No +// CHECK-NEXT: EpiloguePacked: Yes +// CHECK-NEXT: EpilogueOffset: 3 +// CHECK-NEXT: ByteCodeLength: 8 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: 0x02 ; sub sp, #32 +// CHECK-NEXT: 0x03 ; sub sp, #48 +// CHECK-NEXT: 0xe4 ; end +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0x01 ; add sp, #16 +// CHECK-NEXT: 0x03 ; add sp, #48 +// CHECK-NEXT: 0xe4 ; end +// CHECK-NEXT: ] // CHECK: RuntimeFunction { -// CHECK-NEXT: Function: nonpacked3 +// CHECK-NEXT: Function: nonshared3 // CHECK-NEXT: ExceptionRecord: // CHECK-NEXT: ExceptionData { // CHECK: ExceptionData: -// CHECK-NEXT: EpiloguePacked: No +// CHECK-NEXT: EpiloguePacked: Yes +// CHECK-NEXT: EpilogueOffset: 3 +// CHECK-NEXT: ByteCodeLength: 8 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: 0x02 ; sub sp, #32 +// CHECK-NEXT: 0x03 ; sub sp, #48 +// CHECK-NEXT: 0xe4 ; end +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0xe1 ; mov sp, fp +// CHECK-NEXT: 0x02 ; add sp, #32 +// CHECK-NEXT: 0x03 ; add sp, #48 +// CHECK-NEXT: 0xe4 ; end +// CHECK-NEXT: ] .text .globl func @@ -146,8 +178,8 @@ nonpacked1: .seh_endproc - .seh_proc nonpacked2 -nonpacked2: + .seh_proc nonshared2 +nonshared2: sub sp, sp, #48 .seh_stackalloc 48 sub sp, sp, #32 @@ -156,7 +188,7 @@ nonpacked2: nop .seh_startepilogue - // Not packed; the epilogue mismatches at the second opcode. + // Not shared; the epilogue mismatches at the second opcode. add sp, sp, #16 .seh_stackalloc 16 add sp, sp, #48 @@ -165,8 +197,8 @@ nonpacked2: ret .seh_endproc - .seh_proc nonpacked3 -nonpacked3: + .seh_proc nonshared3 +nonshared3: sub sp, sp, #48 .seh_stackalloc 48 sub sp, sp, #32 @@ -175,7 +207,7 @@ nonpacked3: nop .seh_startepilogue - // Not packed; the epilogue is longer than the prologue. + // Not shared; the epilogue is longer than the prologue. mov sp, x29 .seh_set_fp add sp, sp, #32