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 @@ -613,18 +613,33 @@ if (info->EpilogMap.size() != 1) return -1; + const std::vector &Prolog = info->Instructions; 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()) + 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 (info->Instructions[I] != Epilog[Epilog.size() - 1 - I]) - return -1; + // Check that the epilog actually matches the prolog with nops skipped + // in both. + unsigned PNops = 0, ENops = 0; + for (int EI = Epilog.size() - 1, PI = 0; EI >= 0; ) { + assert(PI < Prolog.size() && "Prolog overflow!"); + // Unwinding ops in prolog and epilog match. + if (Prolog[PI] == Epilog[EI]) { + PI++; EI--; + continue; + } + // Skip UOP_Nops in either prolog or epilog. + if (Prolog[PI].Operation == Win64EH::UOP_Nop) { + PNops++; PI++; continue; + } + if (Epilog[EI].Operation == Win64EH::UOP_Nop) { + ENops++; EI--; continue; + } + // If we reach here, we find unmatching ops that are NOT UOP_Nop. + return -1; } // Check that the epilog actually is at the very end of the function, @@ -634,12 +649,13 @@ if (DistanceFromEnd / 4 != Epilog.size()) return -1; - int Offset = Epilog.size() == info->Instructions.size() + int PH = PNops == 4, EH = ENops == 4; + int Offset = (Epilog.size() - (EH * 4)) == (Prolog.size() - (PH * 4)) ? 0 : ARM64CountOfUnwindCodes(ArrayRef( - &info->Instructions[Epilog.size()], - info->Instructions.size() - Epilog.size())); - + &Prolog[0], Prolog.size())) - + ARM64CountOfUnwindCodes(ArrayRef( + &Epilog[0], 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. @@ -652,22 +668,6 @@ static bool tryPackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength, int PackedEpilogOffset) { - if (PackedEpilogOffset == 0) { - // Fully symmetric prolog and epilog, should be ok for packed format. - // For CR=3, the corresponding synthesized epilog actually lacks the - // SetFP opcode, but unwinding should work just fine despite that - // (if at the SetFP opcode, the unwinder considers it as part of the - // function body and just unwinds the full prolog instead). - } else if (PackedEpilogOffset == 1) { - // One single case of differences between prolog and epilog is allowed: - // The epilog can lack a single SetFP that is the last opcode in the - // prolog, for the CR=3 case. - if (info->Instructions.back().Operation != Win64EH::UOP_SetFP) - return false; - } else { - // Too much difference between prolog and epilog. - return false; - } unsigned RegI = 0, RegF = 0; int Predecrement = 0; enum { @@ -843,6 +843,26 @@ if (Nops != 0 && Nops != 4) return false; int H = Nops == 4; + + if (PackedEpilogOffset == 0) { + // Fully symmetric prolog and epilog, should be ok for packed format. + // For CR=3, the corresponding synthesized epilog actually lacks the + // SetFP opcode, but unwinding should work just fine despite that + // (if at the SetFP opcode, the unwinder considers it as part of the + // function body and just unwinds the full prolog instead). + } else if ((PackedEpilogOffset == 1) || + // Homing int param regs + (PackedEpilogOffset == 5 && H == 1)) { + // One single case of differences between prolog and epilog is allowed: + // The epilog can lack a single SetFP that is the last opcode in the + // prolog, for the CR=3 case. + if (info->Instructions.back().Operation != Win64EH::UOP_SetFP) + return false; + } else { + // Too much difference between prolog and epilog. + return false; + } + int IntSZ = 8 * RegI; if (StandaloneLR) IntSZ += 8; diff --git a/llvm/test/MC/AArch64/seh-packed-unwind.s b/llvm/test/MC/AArch64/seh-packed-unwind.s --- a/llvm/test/MC/AArch64/seh-packed-unwind.s +++ b/llvm/test/MC/AArch64/seh-packed-unwind.s @@ -100,6 +100,45 @@ // CHECK-NEXT: ] // CHECK-NEXT: } // CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func5b +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 40 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 1 +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: CR: 0 +// CHECK-NEXT: FrameSize: 112 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #32 +// CHECK-NEXT: stp x6, x7, [sp, #56] +// CHECK-NEXT: stp x4, x5, [sp, #40] +// CHECK-NEXT: stp x2, x3, [sp, #24] +// CHECK-NEXT: stp x0, x1, [sp, #8] +// CHECK-NEXT: str x19, [sp, #-80]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func5c +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 60 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 2 +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 96 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #-16]! +// CHECK-NEXT: stp x6, x7, [sp, #64] +// CHECK-NEXT: stp x4, x5, [sp, #48] +// CHECK-NEXT: stp x2, x3, [sp, #32] +// CHECK-NEXT: stp x0, x1, [sp, #16] +// CHECK-NEXT: stp x19, x20, [sp, #-80]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { // CHECK-NEXT: Function: func6 // CHECK-NEXT: Fragment: No // CHECK-NEXT: FunctionLength: 24 @@ -474,6 +513,71 @@ ret .seh_endproc +// [PRi54879]: homing int param regs, but no nops in epilog +func5b: + .seh_proc func5b + str x19, [sp, #-80]! + .seh_save_reg_x x19, 80 + stp x0, x1, [sp, #8] + .seh_nop + stp x2, x3, [sp, #24] + .seh_nop + stp x4, x5, [sp, #40] + .seh_nop + stp x6, x7, [sp, #56] + .seh_nop + sub sp, sp, #32 + .seh_stackalloc 32 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #32 + .seh_stackalloc 32 + ldr x19, [sp], #80 + .seh_save_reg_x x19, 80 + .seh_endepilogue + ret + .seh_endproc + +// [PRi54879]: homing int param regs, nops in epilog, only difference between +// prolog and epilog is set_fp +func5c: + .seh_proc func5c + stp x19, x20,[sp,#-0x50]! + .seh_save_regp_x x19, 0x50 + stp x0, x1, [sp, #0x10] + .seh_nop + stp x2, x3, [sp, #0x20] + .seh_nop + stp x4, x5, [sp, #0x30] + .seh_nop + stp x6, x7, [sp, #0x40] + .seh_nop + stp fp, lr, [sp, #-0x10]! + .seh_save_fplr_x 0x10 + mov fp, sp + .seh_set_fp + .seh_endprologue + //return success + mov w0, wzr + .seh_startepilogue + ldp fp, lr, [sp], #0x10 + .seh_save_fplr_x 0x10 + nop + .seh_nop + nop + .seh_nop + nop + .seh_nop + nop + .seh_nop + ldp x19, x20, [sp], #0x50 + .seh_save_regp_x x19, 0x50 + .seh_endepilogue + ret + .seh_endfunclet + .seh_endproc + func6: .seh_proc func6 str lr, [sp, #-16]!