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