[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
This commit is contained in:
Martin Storsjö 2022-05-13 10:42:56 +03:00
parent 68f37e7991
commit cabefea2ec
6 changed files with 120 additions and 96 deletions

View File

@ -614,17 +614,6 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
const std::vector<WinEH::Instruction> &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<WinEH::Instruction>(
@ -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).

View File

@ -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: }

View File

@ -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: }
...

View File

@ -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: }

View File

@ -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: }

View File

@ -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