diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp index f5f8bbb6a3859..6835ba73ffab8 100644 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -1051,7 +1051,9 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength, // the order - that would work fine when unwinding from within // functions, but not be exactly right if unwinding happens within // prologs/epilogs. - for (const WinEH::Instruction &Inst : info->Instructions) { + for (auto It = info->Instructions.begin(), EndIt = info->Instructions.end(); + It != EndIt; It++) { + const WinEH::Instruction &Inst = *It; switch (Inst.Operation) { case Win64EH::UOP_End: if (Location != Start) @@ -1169,6 +1171,28 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength, Location != FloatRegs && Location != InputArgs && Location != StackAdjust) return false; + // Becuase there's no save_lrpair_x opcode, the case of CR=01, + // RegI=1 is handled as a special case with a pair of instructions; an + // alloc followed by a regular save_lrpair. So when encountering an + // alloc here, check if this is the start of such an instruction pair. + if (Location == Start2) { // Can't have this at Start3, after PACSignLR + auto NextIt = It + 1; + if (NextIt != EndIt) { + const WinEH::Instruction &NextInst = *NextIt; + if (NextInst.Operation == Win64EH::UOP_SaveLRPair && + NextInst.Offset == 0 && NextInst.Register == 19) { + assert(Predecrement == 0); + assert(RegI == 0); + assert(!StandaloneLR); + Predecrement = Inst.Offset; + RegI = 1; + StandaloneLR = true; + Location = FloatRegs; + It++; // Consume both the Alloc and the SaveLRPair + continue; + } + } + } // Can have either a single decrement, or a pair of decrements with // 4080 and another decrement. if (StackOffset == 0) @@ -1269,6 +1293,15 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength, // according to the documentation. if (H) return false; + // Older versions of Windows (at least in 10.0.22000.2176) incorrectly + // unwind packed unwind info with CR=01, RegI=1, RegF>0, see + // https://github.com/llvm/llvm-project/issues/169588#issuecomment-3584907886. + // This issue only exists in older versions; current versions + // (10.0.26100.6899) do handle it correctly. As long as we can't be sure + // that we won't run on older versions, avoid producing the packed form + // here. + if (StandaloneLR && RegI == 1 && RegF > 0) + 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 index 5b86ab4bc0d49..4c0f30b2536cd 100644 --- a/llvm/test/MC/AArch64/seh-packed-unwind.s +++ b/llvm/test/MC/AArch64/seh-packed-unwind.s @@ -295,6 +295,26 @@ // CHECK-NEXT: end // CHECK-NEXT: ] // CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func19 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 32 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 1 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 1 +// CHECK-NEXT: FrameSize: 80 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #64 +// CHECK-NEXT: stp x19, lr, [sp] +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: notpacked_func20 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { // CHECK: RuntimeFunction { // CHECK-NEXT: Function: nonpacked1 // CHECK-NEXT: ExceptionRecord: @@ -374,6 +394,11 @@ // CHECK-NEXT: Function: nonpacked16 // CHECK-NEXT: ExceptionRecord: // CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked17 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { // CHECK: EpiloguePacked: Yes @@ -809,6 +834,59 @@ func18: ret .seh_endproc +func19: + .seh_proc func19 + sub sp, sp, #16 + .seh_stackalloc 16 + stp x19, lr, [sp] + .seh_save_lrpair x19, 0 + sub sp, sp, #64 + .seh_stackalloc 64 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #64 + .seh_stackalloc 64 + ldp x19, lr, [sp] + .seh_save_lrpair x19, 0 + add sp, sp, #16 + .seh_stackalloc 16 + .seh_endepilogue + ret + .seh_endproc + +notpacked_func20: + // This function is expressible with packed unwind info, but older + // versions of Windows unwind cases with CR=01, RegI=1, RegF>0 + // incorrectly; therefore, we choose not to pack this case. + .seh_proc notpacked_func20 + sub sp, sp, #48 + .seh_stackalloc 48 + stp x19, lr, [sp] + .seh_save_lrpair x19, 0 + stp d8, d9, [sp, #16] + .seh_save_fregp d8, 16 + str d10, [sp, #32] + .seh_save_freg d10, 32 + sub sp, sp, #64 + .seh_stackalloc 64 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #64 + .seh_stackalloc 64 + ldr d10, [sp, #32] + .seh_save_freg d10, 32 + ldp d8, d9, [sp, #16] + .seh_save_fregp d8, 16 + ldp x19, lr, [sp] + .seh_save_lrpair x19, 0 + add sp, sp, #48 + .seh_stackalloc 48 + .seh_endepilogue + ret + .seh_endproc + nonpacked1: .seh_proc nonpacked1 // Can't be packed; can't save integer registers after float registers. @@ -1157,3 +1235,34 @@ nonpacked16: .seh_endepilogue br x9 .seh_endproc + +nonpacked17: + .seh_proc nonpacked17 + // Can't be packed; more predecrement for SavSZ than used for + // corresponding RegI/RegF/LR saves + sub sp, sp, #64 + .seh_stackalloc 64 + stp x19, lr, [sp] + .seh_save_lrpair x19, 0 + stp d8, d9, [sp, #16] + .seh_save_fregp d8, 16 + str d10, [sp, #32] + .seh_save_freg d10, 32 + sub sp, sp, #64 + .seh_stackalloc 64 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #64 + .seh_stackalloc 64 + ldr d10, [sp, #32] + .seh_save_freg d10, 32 + ldp d8, d9, [sp, #16] + .seh_save_fregp d8, 16 + ldp x19, lr, [sp] + .seh_save_lrpair x19, 0 + add sp, sp, #64 + .seh_stackalloc 64 + .seh_endepilogue + ret + .seh_endproc