From f69e090d7dca6bf2786145a9e97b0a7ddb3b514a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Wed, 9 Sep 2020 10:34:15 +0300 Subject: [PATCH] [MC] [Win64EH] Try to generate packed unwind info where possible In practice, this only gives modest savings (for a 6.5 MB DLL with 230 KB xdata, the xdata sections shrinks by around 2.5 KB); to gain more, the frame lowering would need to be tweaked to more often generate frame layouts that match the canonical layouts that can be written in packed form. Differential Revision: https://reviews.llvm.org/D87371 --- llvm/include/llvm/MC/MCWinEH.h | 1 + llvm/lib/MC/MCWin64EH.cpp | 253 +++++- llvm/test/MC/AArch64/seh-packed-unwind.s | 947 +++++++++++++++++++++++ 3 files changed, 1195 insertions(+), 6 deletions(-) create mode 100644 llvm/test/MC/AArch64/seh-packed-unwind.s diff --git a/llvm/include/llvm/MC/MCWinEH.h b/llvm/include/llvm/MC/MCWinEH.h index f05f5f1641cd..a46f56d708ce 100644 --- a/llvm/include/llvm/MC/MCWinEH.h +++ b/llvm/include/llvm/MC/MCWinEH.h @@ -45,6 +45,7 @@ struct FrameInfo { const MCSymbol *PrologEnd = nullptr; const MCSymbol *Symbol = nullptr; MCSection *TextSection = nullptr; + uint32_t PackedInfo = 0; bool HandlesUnwind = false; bool HandlesExceptions = false; diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp index 8e8dba760853..ffc2ed9a3fb6 100644 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -648,9 +648,233 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, return Offset; } +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 { + Start, + Start2, + IntRegs, + FloatRegs, + InputArgs, + StackAdjust, + FrameRecord, + End + } Location = Start; + bool StandaloneLR = false, FPLRPair = false; + int StackOffset = 0; + int Nops = 0; + // Iterate over the prolog and check that all opcodes exactly match + // the canonical order and form. A more lax check could verify that + // all saved registers are in the expected locations, but not enforce + // 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) { + switch (Inst.Operation) { + case Win64EH::UOP_End: + if (Location != Start) + return false; + Location = Start2; + break; + case Win64EH::UOP_SaveR19R20X: + if (Location != Start2) + return false; + Predecrement = Inst.Offset; + RegI = 2; + Location = IntRegs; + break; + case Win64EH::UOP_SaveRegX: + if (Location != Start2) + return false; + Predecrement = Inst.Offset; + if (Inst.Register == 19) + RegI += 1; + else if (Inst.Register == 30) + StandaloneLR = true; + else + return false; + // Odd register; can't be any further int registers. + Location = FloatRegs; + break; + case Win64EH::UOP_SaveRegPX: + // Can't have this in a canonical prologue. Either this has been + // canonicalized into SaveR19R20X or SaveFPLRX, or it's an unsupported + // register pair. + // It can't be canonicalized into SaveR19R20X if the offset is + // larger than 248 bytes, but even with the maximum case with + // RegI=10/RegF=8/CR=1/H=1, we end up with SavSZ = 216, which should + // fit into SaveR19R20X. + // The unwinding opcodes can't describe the otherwise seemingly valid + // case for RegI=1 CR=1, that would start with a + // "stp x19, lr, [sp, #-...]!" as that fits neither SaveRegPX nor + // SaveLRPair. + return false; + case Win64EH::UOP_SaveRegP: + if (Location != IntRegs || Inst.Offset != 8 * RegI || + Inst.Register != 19 + RegI) + return false; + RegI += 2; + break; + case Win64EH::UOP_SaveReg: + if (Location != IntRegs || Inst.Offset != 8 * RegI) + return false; + if (Inst.Register == 19 + RegI) + RegI += 1; + else if (Inst.Register == 30) + StandaloneLR = true; + else + return false; + // Odd register; can't be any further int registers. + Location = FloatRegs; + break; + case Win64EH::UOP_SaveLRPair: + if (Location != IntRegs || Inst.Offset != 8 * RegI || + Inst.Register != 19 + RegI) + return false; + RegI += 1; + StandaloneLR = true; + Location = FloatRegs; + break; + case Win64EH::UOP_SaveFRegX: + // Packed unwind can't handle prologs that only save one single + // float register. + return false; + case Win64EH::UOP_SaveFReg: + if (Location != FloatRegs || RegF == 0 || Inst.Register != 8 + RegF || + Inst.Offset != 8 * (RegI + (StandaloneLR ? 1 : 0) + RegF)) + return false; + RegF += 1; + Location = InputArgs; + break; + case Win64EH::UOP_SaveFRegPX: + if (Location != Start2 || Inst.Register != 8) + return false; + Predecrement = Inst.Offset; + RegF = 2; + Location = FloatRegs; + break; + case Win64EH::UOP_SaveFRegP: + if ((Location != IntRegs && Location != FloatRegs) || + Inst.Register != 8 + RegF || + Inst.Offset != 8 * (RegI + (StandaloneLR ? 1 : 0) + RegF)) + return false; + RegF += 2; + Location = FloatRegs; + break; + case Win64EH::UOP_SaveNext: + if (Location == IntRegs) + RegI += 2; + else if (Location == FloatRegs) + RegF += 2; + else + return false; + break; + case Win64EH::UOP_Nop: + if (Location != IntRegs && Location != FloatRegs && Location != InputArgs) + return false; + Location = InputArgs; + Nops++; + break; + case Win64EH::UOP_AllocSmall: + case Win64EH::UOP_AllocMedium: + if (Location != Start2 && Location != IntRegs && Location != FloatRegs && + Location != InputArgs && Location != StackAdjust) + return false; + // Can have either a single decrement, or a pair of decrements with + // 4080 and another decrement. + if (StackOffset == 0) + StackOffset = Inst.Offset; + else if (StackOffset != 4080) + return false; + else + StackOffset += Inst.Offset; + Location = StackAdjust; + break; + case Win64EH::UOP_SaveFPLRX: + // Not allowing FPLRX after StackAdjust; if a StackAdjust is used, it + // should be followed by a FPLR instead. + if (Location != Start2 && Location != IntRegs && Location != FloatRegs && + Location != InputArgs) + return false; + StackOffset = Inst.Offset; + Location = FrameRecord; + FPLRPair = true; + break; + case Win64EH::UOP_SaveFPLR: + // This can only follow after a StackAdjust + if (Location != StackAdjust || Inst.Offset != 0) + return false; + Location = FrameRecord; + FPLRPair = true; + break; + case Win64EH::UOP_SetFP: + if (Location != FrameRecord) + return false; + Location = End; + break; + } + } + if (RegI > 10 || RegF > 8) + return false; + if (StandaloneLR && FPLRPair) + return false; + if (FPLRPair && Location != End) + return false; + if (Nops != 0 && Nops != 4) + return false; + int H = Nops == 4; + int IntSZ = 8 * RegI; + if (StandaloneLR) + IntSZ += 8; + int FpSZ = 8 * RegF; // RegF not yet decremented + int SavSZ = (IntSZ + FpSZ + 8 * 8 * H + 0xF) & ~0xF; + if (Predecrement != SavSZ) + return false; + if (FPLRPair && StackOffset < 16) + return false; + if (StackOffset % 16) + return false; + uint32_t FrameSize = (StackOffset + SavSZ) / 16; + if (FrameSize > 0x1FF) + return false; + assert(RegF != 1 && "One single float reg not allowed"); + if (RegF > 0) + RegF--; // Convert from actual number of registers, to value stored + assert(FuncLength <= 0x7FF && "FuncLength should have been checked earlier"); + int Flag = 0x01; // Function segments not supported yet + int CR = FPLRPair ? 3 : StandaloneLR ? 1 : 0; + info->PackedInfo |= Flag << 0; + info->PackedInfo |= (FuncLength & 0x7FF) << 2; + info->PackedInfo |= (RegF & 0x7) << 13; + info->PackedInfo |= (RegI & 0xF) << 16; + info->PackedInfo |= (H & 0x1) << 20; + info->PackedInfo |= (CR & 0x3) << 21; + info->PackedInfo |= (FrameSize & 0x1FF) << 23; + return true; +} + // Populate the .xdata section. The format of .xdata on ARM64 is documented at // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling -static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { +static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, + bool TryPacked = true) { // If this UNWIND_INFO already has a symbol, it's already been emitted. if (info->Symbol) return; @@ -728,6 +952,20 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { int PackedEpilogOffset = checkPackedEpilog(streamer, info, PrologCodeBytes); + if (PackedEpilogOffset >= 0 && !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. + + // info->Symbol was already set even if we didn't actually write any + // unwind info there. Keep using that as indicator that this unwind + // info has been generated already. + + if (tryPackedUnwind(info, FuncLength, PackedEpilogOffset)) + return; + } + // Process epilogs. MapVector EpilogInfo; // Epilogs processed so far. @@ -835,10 +1073,13 @@ static void ARM64EmitRuntimeFunction(MCStreamer &streamer, streamer.emitValueToAlignment(4); EmitSymbolRefWithOfs(streamer, info->Function, info->Begin); - streamer.emitValue(MCSymbolRefExpr::create(info->Symbol, - MCSymbolRefExpr::VK_COFF_IMGREL32, - context), - 4); + if (info->PackedInfo) + streamer.emitInt32(info->PackedInfo); + else + streamer.emitValue( + MCSymbolRefExpr::create(info->Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32, + context), + 4); } void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const { @@ -882,5 +1123,5 @@ void llvm::Win64EH::ARM64UnwindEmitter::EmitUnwindInfo( // here and from Emit(). MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection); Streamer.SwitchSection(XData); - ARM64EmitUnwindInfo(Streamer, info); + ARM64EmitUnwindInfo(Streamer, info, false); } diff --git a/llvm/test/MC/AArch64/seh-packed-unwind.s b/llvm/test/MC/AArch64/seh-packed-unwind.s new file mode 100644 index 000000000000..a053d7a29b5c --- /dev/null +++ b/llvm/test/MC/AArch64/seh-packed-unwind.s @@ -0,0 +1,947 @@ +// Check that we generate the packed unwind info format whe possible. + +// For tests that don't generate packed unwind info, we still check that +// the epilog was packed (so that the testcase otherwise had all other +// preconditions for possibly making packed unwind info). + +// REQUIRES: aarch64-registered-target +// RUN: llvm-mc -filetype=obj -triple aarch64-windows %s -o %t.o +// RUN: llvm-readobj --unwind %t.o | FileCheck %s + +// CHECK: UnwindInformation [ +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func1 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 88 +// CHECK-NEXT: RegF: 7 +// CHECK-NEXT: RegI: 10 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 0 +// CHECK-NEXT: FrameSize: 160 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: stp d14, d15, [sp, #128] +// CHECK-NEXT: stp d12, d13, [sp, #112] +// CHECK-NEXT: stp d10, d11, [sp, #96] +// CHECK-NEXT: stp d8, d9, [sp, #80] +// CHECK-NEXT: stp x27, x28, [sp, #64] +// CHECK-NEXT: stp x25, x26, [sp, #48] +// CHECK-NEXT: stp x23, x24, [sp, #32] +// CHECK-NEXT: stp x21, x22, [sp, #16] +// CHECK-NEXT: stp x19, x20, [sp, #-144]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func2 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 48 +// CHECK-NEXT: RegF: 2 +// CHECK-NEXT: RegI: 3 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 0 +// CHECK-NEXT: FrameSize: 48 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: str d10, [sp, #40] +// CHECK-NEXT: stp d8, d9, [sp, #24] +// CHECK-NEXT: str x21, [sp, #16] +// CHECK-NEXT: stp x19, x20, [sp, #-48]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func3 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 32 +// CHECK-NEXT: RegF: 3 +// CHECK-NEXT: RegI: 1 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 0 +// CHECK-NEXT: FrameSize: 48 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: stp d10, d11, [sp, #24] +// CHECK-NEXT: stp d8, d9, [sp, #8] +// CHECK-NEXT: str x19, [sp, #-48]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func4 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 24 +// CHECK-NEXT: RegF: 1 +// CHECK-NEXT: RegI: 0 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 0 +// CHECK-NEXT: FrameSize: 48 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #32 +// CHECK-NEXT: stp d8, d9, [sp, #-16]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func5 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 56 +// 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: func6 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 24 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 0 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 1 +// CHECK-NEXT: FrameSize: 32 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: str lr, [sp, #-16]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func7 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 24 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 2 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 1 +// CHECK-NEXT: FrameSize: 32 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: str lr, [sp, #16] +// CHECK-NEXT: stp x19, x20, [sp, #-32]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func8 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 32 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 3 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 1 +// CHECK-NEXT: FrameSize: 48 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: stp x21, lr, [sp, #16] +// CHECK-NEXT: stp x19, x20, [sp, #-32]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func9 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 32 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 2 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 48 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #-32]! +// CHECK-NEXT: stp x19, x20, [sp, #-16]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func10 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 24 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 0 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 32 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #-32]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func11 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 40 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 2 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 544 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #0] +// CHECK-NEXT: sub sp, sp, #528 +// CHECK-NEXT: stp x19, x20, [sp, #-16]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func12 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 48 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 2 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 4112 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #0] +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: sub sp, sp, #4080 +// CHECK-NEXT: stp x19, x20, [sp, #-16]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func13 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 32 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 2 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 0 +// CHECK-NEXT: FrameSize: 4112 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: sub sp, sp, #4080 +// CHECK-NEXT: stp x19, x20, [sp, #-16]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func14 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 32 +// CHECK-NEXT: RegF: 2 +// CHECK-NEXT: RegI: 0 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 1 +// CHECK-NEXT: FrameSize: 32 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: str d10, [sp, #24] +// CHECK-NEXT: stp d8, d9, [sp, #8] +// CHECK-NEXT: str lr, [sp, #-32]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func15 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 20 +// CHECK-NEXT: RegF: 0 +// CHECK-NEXT: RegI: 0 +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: CR: 3 +// CHECK-NEXT: FrameSize: 32 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: mov x29, sp +// CHECK-NEXT: stp x29, lr, [sp, #-32]! +// CHECK-NEXT: end +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked1 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked2 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked3 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked4 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked5 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked6 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked7 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked8 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked9 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked10 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked11 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked12 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: nonpacked13 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK: EpiloguePacked: Yes + + .text +func1: + .seh_proc func1 + stp x19, x20, [sp, #-144]! + .seh_save_r19r20_x 144 + stp x21, x22, [sp, #16] + .seh_save_regp x21, 16 + stp x23, x24, [sp, #32] + .seh_save_next + stp x25, x26, [sp, #48] + .seh_save_next + stp x27, x28, [sp, #64] + .seh_save_next + stp d8, d9, [sp, #80] + .seh_save_fregp d8, 80 + stp d10, d11, [sp, #96] + .seh_save_fregp d10, 96 + stp d12, d13, [sp, #112] + .seh_save_fregp d12, 112 + stp d14, d15, [sp, #128] + .seh_save_fregp d14, 128 + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + ldp d14, d15, [sp, #128] + .seh_save_fregp d14, 128 + ldp d12, d13, [sp, #112] + .seh_save_fregp d12, 112 + ldp d10, d11, [sp, #96] + .seh_save_fregp d10, 96 + ldp d8, d9, [sp, #80] + .seh_save_fregp d8, 80 + ldp x27, x28, [sp, #64] + .seh_save_next + ldp x25, x26, [sp, #48] + .seh_save_next + ldp x23, x24, [sp, #32] + .seh_save_next + ldp x21, x22, [sp, #16] + .seh_save_next + ldp x19, x20, [sp], #144 + .seh_save_regp_x x19, 144 + .seh_endepilogue + ret + .seh_endproc + +func2: + .seh_proc func2 + stp x19, x20, [sp, #-48]! + .seh_save_r19r20_x 48 + str x21, [sp, #16] + .seh_save_reg x21, 16 + stp d8, d9, [sp, #24] + .seh_save_fregp d8, 24 + str d10, [sp, #40] + .seh_save_freg d10, 40 + sub sp, sp, #0 + .seh_stackalloc 0 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #0 + .seh_stackalloc 0 + ldr d10, [sp, #40] + .seh_save_freg d10, 40 + ldp d8, d9, [sp, #24] + .seh_save_fregp d8, 24 + ldr x21, [sp, #16] + .seh_save_reg x21, 16 + ldp x19, x20, [sp], #48 + .seh_save_r19r20_x 48 + .seh_endepilogue + ret + .seh_endproc + +func3: + .seh_proc func3 + str x19, [sp, #-48]! + .seh_save_reg_x x19, 48 + stp d8, d9, [sp, #8] + .seh_save_fregp d8, 8 + stp d10, d11, [sp, #24] + .seh_save_fregp d10, 24 + .seh_endprologue + nop + .seh_startepilogue + ldp d10, d11, [sp, #24] + .seh_save_fregp d10, 24 + ldp d8, d9, [sp, #8] + .seh_save_fregp d8, 8 + ldr x19, [sp], #48 + .seh_save_reg_x x19, 48 + .seh_endepilogue + ret + .seh_endproc + +func4: + .seh_proc func4 + stp d8, d9, [sp, #-16]! + .seh_save_fregp_x d8, 16 + sub sp, sp, #32 + .seh_stackalloc 32 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #32 + .seh_stackalloc 32 + ldp d8, d9, [sp], #16 + .seh_save_fregp_x d8, 16 + .seh_endepilogue + ret + .seh_endproc + +func5: + .seh_proc func5 + 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 + nop + .seh_nop + nop + .seh_nop + nop + .seh_nop + nop + .seh_nop + ldr x19, [sp], #80 + .seh_save_reg_x x19, 80 + .seh_endepilogue + ret + .seh_endproc + +func6: + .seh_proc func6 + str lr, [sp, #-16]! + .seh_save_reg_x lr, 16 + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + ldr lr, [sp], #16 + .seh_save_reg_x lr, 16 + .seh_endepilogue + ret + .seh_endproc + +func7: + .seh_proc func7 + stp x19, x20, [sp, #-32]! + .seh_save_r19r20_x 32 + str lr, [sp, #16] + .seh_save_reg lr, 16 + .seh_endprologue + nop + .seh_startepilogue + ldr lr, [sp, #16] + .seh_save_reg lr, 16 + ldp x19, x20, [sp], #32 + .seh_save_r19r20_x 32 + .seh_endepilogue + ret + .seh_endproc + +func8: + .seh_proc func8 + stp x19, x20, [sp, #-32]! + .seh_save_r19r20_x 32 + stp x21, lr, [sp, #16] + .seh_save_lrpair x21, 16 + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + ldp x21, lr, [sp, #16] + .seh_save_lrpair x21, 16 + ldp x19, x20, [sp], #32 + .seh_save_r19r20_x 32 + .seh_endepilogue + ret + .seh_endproc + +func9: + .seh_proc func9 + stp x19, x20, [sp, #-16]! + .seh_save_r19r20_x 16 + stp x29, lr, [sp, #-32]! + .seh_save_fplr_x 32 + mov x29, sp + .seh_set_fp + .seh_endprologue + nop + .seh_startepilogue + mov sp, x29 + .seh_set_fp + ldp x29, lr, [sp], #32 + .seh_save_fplr_x 32 + ldp x19, x20, [sp], #16 + .seh_save_r19r20_x 16 + .seh_endepilogue + ret + .seh_endproc + +func10: + .seh_proc func10 + stp x29, lr, [sp, #-32]! + .seh_save_fplr_x 32 + mov x29, sp + .seh_set_fp + .seh_endprologue + nop + .seh_startepilogue + mov sp, x29 + .seh_set_fp + ldp x29, lr, [sp], #32 + .seh_save_fplr_x 32 + .seh_endepilogue + ret + .seh_endproc + +func11: + .seh_proc func11 + stp x19, x20, [sp, #-16]! + .seh_save_r19r20_x 16 + sub sp, sp, #528 + .seh_stackalloc 528 + stp x29, lr, [sp, #0] + .seh_save_fplr 0 + mov x29, sp + .seh_set_fp + .seh_endprologue + nop + .seh_startepilogue + mov sp, x29 + .seh_set_fp + ldp x29, lr, [sp, #0] + .seh_save_fplr 0 + add sp, sp, #528 + .seh_stackalloc 528 + ldp x19, x20, [sp], #16 + .seh_save_r19r20_x 16 + .seh_endepilogue + ret + .seh_endproc + +func12: + .seh_proc func12 + stp x19, x20, [sp, #-16]! + .seh_save_r19r20_x 16 + sub sp, sp, #4080 + .seh_stackalloc 4080 + sub sp, sp, #16 + .seh_stackalloc 16 + stp x29, lr, [sp, #0] + .seh_save_fplr 0 + mov x29, sp + .seh_set_fp + .seh_endprologue + nop + .seh_startepilogue + mov sp, x29 + .seh_set_fp + ldp x29, lr, [sp, #0] + .seh_save_fplr 0 + add sp, sp, #16 + .seh_stackalloc 16 + add sp, sp, #4080 + .seh_stackalloc 4080 + ldp x19, x20, [sp], #16 + .seh_save_r19r20_x 16 + .seh_endepilogue + ret + .seh_endproc + +func13: + .seh_proc func13 + stp x19, x20, [sp, #-16]! + .seh_save_r19r20_x 16 + sub sp, sp, #4080 + .seh_stackalloc 4080 + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + add sp, sp, #4080 + .seh_stackalloc 4080 + ldp x19, x20, [sp], #16 + .seh_save_r19r20_x 16 + .seh_endepilogue + ret + .seh_endproc + +func14: + .seh_proc func14 + str lr, [sp, #-32]! + .seh_save_reg_x lr, 32 + stp d8, d9, [sp, #8] + .seh_save_fregp d8, 8 + str d10, [sp, #24] + .seh_save_freg d10, 24 + .seh_endprologue + nop + .seh_startepilogue + ldr d10, [sp, #24] + .seh_save_freg d10, 24 + ldp d8, d9, [sp, #8] + .seh_save_fregp d8, 8 + ldr lr, [sp], #32 + .seh_save_reg_x lr, 32 + .seh_endepilogue + ret + .seh_endproc + +func15: + .seh_proc func15 + stp x29, lr, [sp, #-32]! + .seh_save_fplr_x 32 + mov x29, sp + .seh_set_fp + .seh_endprologue + nop + .seh_startepilogue + // Epilogue missing the .seh_set_fp, but still generating packed info. + ldp x29, lr, [sp], #32 + .seh_save_fplr_x 32 + .seh_endepilogue + ret + .seh_endproc + +nonpacked1: + .seh_proc nonpacked1 + // Can't be packed; can't save integer registers after float registers. + stp d8, d9, [sp, #-32]! + .seh_save_fregp_x d8, 32 + stp x19, x20, [sp, #16]! + .seh_save_regp x19, 16 + .seh_endprologue + nop + .seh_startepilogue + ldp x19, x20, [sp, #16] + .seh_save_regp x19, 16 + ldp d8, d9, [sp], #32 + .seh_save_fregp_x d8, 32 + .seh_endepilogue + ret + .seh_endproc + +nonpacked2: + .seh_proc nonpacked2 + // Can't be packed; x21/x22 aren't saved in the expected spot + stp x19, x20, [sp, #-48]! + .seh_save_r19r20_x 48 + stp x21, x22, [sp, #32] + .seh_save_regp x21, 32 + .seh_endprologue + nop + .seh_startepilogue + ldp x21, x22, [sp, #32] + .seh_save_regp x21, 32 + ldp x19, x20, [sp], #48 + .seh_save_r19r20_x 48 + .seh_endepilogue + ret + .seh_endproc + +nonpacked3: + .seh_proc nonpacked3 + // Can't be packed; x29/x30 can't be treated as the other saved registers + stp x19, x20, [sp, #-96]! + .seh_save_r19r20_x 96 + stp x21, x22, [sp, #16] + .seh_save_next + stp x23, x24, [sp, #32] + .seh_save_next + stp x25, x26, [sp, #48] + .seh_save_next + stp x27, x28, [sp, #64] + .seh_save_next + stp x29, x30, [sp, #80] + .seh_save_next + .seh_endprologue + nop + .seh_startepilogue + ldp x29, x30, [sp, #80] + .seh_save_next + ldp x27, x28, [sp, #64] + .seh_save_next + ldp x25, x26, [sp, #48] + .seh_save_next + ldp x23, x24, [sp, #32] + .seh_save_next + ldp x21, x22, [sp, #16] + .seh_save_next + ldp x19, x20, [sp], #96 + .seh_save_r19r20_x 96 + .seh_endepilogue + ret + .seh_endproc + +nonpacked4: + .seh_proc nonpacked4 + // Can't be packed; more predecrement for x19/x20 than used for + // corresponding RegI/RegF/LR saves + stp x19, x20, [sp, #-32]! + .seh_save_r19r20_x 32 + .seh_endprologue + nop + .seh_startepilogue + ldp x19, x20, [sp], #32 + .seh_save_r19r20_x 32 + .seh_endepilogue + ret + .seh_endproc + +nonpacked5: + .seh_proc nonpacked5 + // Can't be packed; can't save LR twice + stp x19, x20, [sp, #-32]! + .seh_save_r19r20_x 32 + str lr, [sp, #16] + .seh_save_reg lr, 16 + str lr, [sp, #24] + .seh_save_reg lr, 24 + .seh_endprologue + nop + .seh_startepilogue + ldr lr, [sp, #24] + .seh_save_reg lr, 24 + ldr lr, [sp, #16] + .seh_save_reg lr, 16 + ldp x19, x20, [sp], #32 + .seh_save_r19r20_x 32 + .seh_endepilogue + ret + .seh_endproc + +nonpacked6: + .seh_proc nonpacked6 + // Can't be packed; can't save LR both standalone (CR 1) and as FPLR (CR 3) + stp x19, x20, [sp, #-32]! + .seh_save_r19r20_x 32 + str lr, [sp, #16] + .seh_save_reg lr, 16 + stp x29, lr, [sp, #-16]! + .seh_save_fplr_x 16 + mov x29, sp + .seh_set_fp + .seh_endprologue + nop + .seh_startepilogue + mov sp, x29 + .seh_set_fp + ldp x29, lr, [sp], #32 + .seh_save_fplr_x 16 + ldr lr, [sp, #16] + .seh_save_reg lr, 16 + ldp x19, x20, [sp], #32 + .seh_save_r19r20_x 32 + .seh_endepilogue + ret + .seh_endproc + +nonpacked7: + .seh_proc nonpacked7 + // Can't be packed; too many saved FP regs + stp d8, d9, [sp, #-80]! + .seh_save_fregp_x d8, 80 + stp d10, d11, [sp, #16] + .seh_save_fregp d10, 16 + stp d12, d13, [sp, #32] + .seh_save_fregp d12, 32 + stp d14, d15, [sp, #48] + .seh_save_fregp d14, 48 + stp d16, d17, [sp, #64] + .seh_save_next + .seh_endprologue + nop + .seh_startepilogue + ldp d16, d17, [sp, #64] + .seh_save_next + ldp d14, d15, [sp, #48] + .seh_save_fregp d14, 48 + ldp d12, d13, [sp, #32] + .seh_save_fregp d12, 32 + ldp d10, d11, [sp, #16] + .seh_save_fregp d10, 16 + ldp d8, d9, [sp], #80 + .seh_save_fregp_x d8, 80 + .seh_endepilogue + ret + .seh_endproc + +nonpacked8: + .seh_proc nonpacked8 + // Can't be packed; Can't handle only a single FP reg + str d8, [sp, #-16]! + .seh_save_freg_x d8, 16 + .seh_endprologue + nop + .seh_startepilogue + ldr d8, [sp], #16 + .seh_save_freg_x d8, 16 + .seh_endepilogue + ret + .seh_endproc + +nonpacked9: + .seh_proc nonpacked9 + // Can't be packed; can't have a separate stack adjustment with save_fplr_x + sub sp, sp, #32 + .seh_stackalloc 32 + stp x29, lr, [sp, #-16]! + .seh_save_fplr_x 16 + mov x29, sp + .seh_set_fp + .seh_endprologue + nop + .seh_startepilogue + mov sp, x29 + .seh_set_fp + ldp x29, lr, [sp], #32 + .seh_save_fplr_x 16 + add sp, sp, #32 + .seh_stackalloc 32 + .seh_endepilogue + ret + .seh_endproc + +nonpacked10: + .seh_proc nonpacked10 + // Can't be packed; wrong predecrement + stp x19, x20, [sp, #-16]! + .seh_save_r19r20_x 16 + stp x21, x22, [sp, #16] + .seh_save_next + .seh_endprologue + nop + .seh_startepilogue + ldp x21, x22, [sp, #16] + .seh_save_next + ldp x19, x20, [sp], #16 + .seh_save_r19r20_x 16 + .seh_endepilogue + ret + .seh_endproc + +nonpacked11: + .seh_proc nonpacked11 + // Can't be packed; too big stack allocation + sub sp, sp, #4080 + .seh_stackalloc 4080 + sub sp, sp, #8192 + .seh_stackalloc 8192 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #8192 + .seh_stackalloc 8192 + add sp, sp, #4080 + .seh_stackalloc 4080 + .seh_endepilogue + ret + .seh_endproc + +nonpacked12: + .seh_proc nonpacked12 + // Can't be packed; missing .seh_set_fp + stp x29, lr, [sp, #-32]! + .seh_save_fplr_x 32 + .seh_endprologue + nop + .seh_startepilogue + ldp x29, lr, [sp], #32 + .seh_save_fplr_x 32 + .seh_endepilogue + ret + .seh_endproc + +nonpacked13: + .seh_proc nonpacked13 + // Can't be packed; not doing a packed info if .seh_handlerdata is used + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + .seh_endepilogue + ret + .seh_endfunclet + .seh_handlerdata + .long 0 + .text + .seh_endproc