diff options
author | Martin Storsjö <martin@martin.st> | 2022-05-13 10:42:56 +0300 |
---|---|---|
committer | Martin Storsjö <martin@martin.st> | 2022-05-17 00:41:39 +0300 |
commit | cabefea2ec99f80ecdf9d3d5fe955831532ff4b0 (patch) | |
tree | e70e33ea274eb0cc3e8495e20be9b22328057cd7 /llvm/lib/MC/MCWin64EH.cpp | |
parent | 68f37e7991bf41a6f3fb3dd12d0e7a42822de347 (diff) | |
download | llvm-cabefea2ec99f80ecdf9d3d5fe955831532ff4b0.zip llvm-cabefea2ec99f80ecdf9d3d5fe955831532ff4b0.tar.gz llvm-cabefea2ec99f80ecdf9d3d5fe955831532ff4b0.tar.bz2 |
[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
Diffstat (limited to 'llvm/lib/MC/MCWin64EH.cpp')
-rw-r--r-- | llvm/lib/MC/MCWin64EH.cpp | 60 |
1 files changed, 37 insertions, 23 deletions
diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp index 0aa066e..73d3a53 100644 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -614,24 +614,33 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, const std::vector<WinEH::Instruction> &Epilog = info->EpilogMap.begin()->second; + // 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( + streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first); + 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 -1; + 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 -1; + return RetVal; } - // 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( - streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first); - if (DistanceFromEnd / 4 != Epilog.size()) - return -1; - 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). |