diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -605,6 +605,27 @@ } } +// Check if an epilog exists as a subset of the end of a prolog (backwards). +static int getOffsetInProlog(const std::vector &Prolog, + const std::vector &Epilog) { + // Can't find an epilog as a subset if it is longer than the prolog. + if (Epilog.size() > Prolog.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 (Prolog[I] != Epilog[Epilog.size() - 1 - I]) + return -1; + } + + // If the epilog was a subset of the prolog, find its offset. + if (Epilog.size() == Prolog.size()) + return 0; + return ARM64CountOfUnwindCodes(ArrayRef( + &Prolog[Epilog.size()], Prolog.size() - Epilog.size())); +} + static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, int PrologCodeBytes) { // Can only pack if there's one single epilog @@ -630,23 +651,10 @@ 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()) + int Offset = getOffsetInProlog(info->Instructions, Epilog); + if (Offset < 0) 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( - &info->Instructions[Epilog.size()], - info->Instructions.size() - Epilog.size())); - // Check that the offset and prolog size fits in the first word; it's // unclear whether the epilog count in the extension word can be taken // as packed epilog offset. @@ -990,6 +998,7 @@ MCSymbol* MatchingEpilog = FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info); + int PrologOffset; if (MatchingEpilog) { assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() && "Duplicate epilog not found"); @@ -997,6 +1006,12 @@ // Clear the unwind codes in the EpilogMap, so that they don't get output // in the logic below. EpilogInstrs.clear(); + } else if ((PrologOffset = + getOffsetInProlog(info->Instructions, EpilogInstrs)) >= 0) { + EpilogInfo[EpilogStart] = PrologOffset; + // Clear the unwind codes in the EpilogMap, so that they don't get output + // in the logic below. + EpilogInstrs.clear(); } else { EpilogInfo[EpilogStart] = TotalCodeBytes; TotalCodeBytes += CodeBytes; @@ -1027,8 +1042,6 @@ // Extended Code Words, Extended Epilog Count if (ExtensionWord) { // FIXME: We should be able to split unwind info into multiple sections. - // FIXME: We should share epilog codes across epilogs, where possible, - // which would make this issue show up less frequently. if (CodeWords > 0xFF || EpilogCount > 0xFFFF) report_fatal_error("SEH unwind data splitting not yet implemented"); uint32_t row2 = 0x0; diff --git a/llvm/test/CodeGen/AArch64/wineh4.mir b/llvm/test/CodeGen/AArch64/wineh4.mir --- a/llvm/test/CodeGen/AArch64/wineh4.mir +++ b/llvm/test/CodeGen/AArch64/wineh4.mir @@ -9,7 +9,7 @@ # CHECK-NEXT: ExceptionData: No # CHECK-NEXT: EpiloguePacked: No # CHECK-NEXT: EpilogueScopes: 2 -# CHECK-NEXT: ByteCodeLength: 32 +# CHECK-NEXT: ByteCodeLength: 16 # CHECK-NEXT: Prologue [ # CHECK-NEXT: 0xc80c ; stp x19, x20, [sp, #96] # CHECK-NEXT: 0xc88a ; stp x21, x22, [sp, #80] @@ -23,7 +23,7 @@ # CHECK-NEXT: EpilogueScopes [ # CHECK-NEXT: EpilogueScope { # CHECK-NEXT: StartOffset: 16 -# CHECK-NEXT: EpilogueStartIndex: 15 +# CHECK-NEXT: EpilogueStartIndex: 0 # CHECK-NEXT: Opcodes [ # CHECK-NEXT: 0xc80c ; ldp x19, x20, [sp, #96] # CHECK-NEXT: 0xc88a ; ldp x21, x22, [sp, #80] @@ -37,7 +37,7 @@ # CHECK-NEXT: } # CHECK-NEXT: EpilogueScope { # CHECK-NEXT: StartOffset: 33 -# CHECK-NEXT: EpilogueStartIndex: 15 +# CHECK-NEXT: EpilogueStartIndex: 0 # CHECK-NEXT: Opcodes [ # CHECK-NEXT: 0xc80c ; ldp x19, x20, [sp, #96] # CHECK-NEXT: 0xc88a ; ldp x21, x22, [sp, #80] diff --git a/llvm/test/CodeGen/AArch64/wineh8.mir b/llvm/test/CodeGen/AArch64/wineh8.mir --- a/llvm/test/CodeGen/AArch64/wineh8.mir +++ b/llvm/test/CodeGen/AArch64/wineh8.mir @@ -9,7 +9,7 @@ # CHECK-NEXT: ExceptionData: No # CHECK-NEXT: EpiloguePacked: No # CHECK-NEXT: EpilogueScopes: 2 -# CHECK-NEXT: ByteCodeLength: 44 +# CHECK-NEXT: ByteCodeLength: 28 # CHECK-NEXT: Prologue [ # CHECK-NEXT: 0xc80c ; stp x19, x20, [sp, #96] # CHECK-NEXT: 0xc88a ; stp x21, x22, [sp, #80] @@ -36,7 +36,7 @@ # CHECK-NEXT: } # CHECK-NEXT: EpilogueScope { # CHECK-NEXT: StartOffset: 32 -# CHECK-NEXT: EpilogueStartIndex: 28 +# CHECK-NEXT: EpilogueStartIndex: 0 # CHECK-NEXT: Opcodes [ # CHECK-NEXT: 0xc80c ; ldp x19, x20, [sp, #96] # CHECK-NEXT: 0xc88a ; ldp x21, x22, [sp, #80]