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 @@ -565,8 +565,8 @@ return nullptr; } -static void simplifyOpcodes(std::vector &Instructions, - bool Reverse) { +static void simplifyARM64Opcodes(std::vector &Instructions, + bool Reverse) { unsigned PrevOffset = -1; unsigned PrevRegister = -1; @@ -628,8 +628,9 @@ } // 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) { +static int +getARM64OffsetInProlog(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; @@ -648,8 +649,8 @@ &Prolog[Epilog.size()], Prolog.size() - Epilog.size())); } -static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, - int PrologCodeBytes) { +static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, + int PrologCodeBytes) { // Can only pack if there's one single epilog if (info->EpilogMap.size() != 1) return -1; @@ -673,7 +674,7 @@ PrologCodeBytes + ARM64CountOfUnwindCodes(Epilog) <= 124) RetVal = PrologCodeBytes; - int Offset = getOffsetInProlog(info->Instructions, Epilog); + int Offset = getARM64OffsetInProlog(info->Instructions, Epilog); if (Offset < 0) return RetVal; @@ -689,8 +690,8 @@ return Offset; } -static bool tryPackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength, - int PackedEpilogOffset) { +static bool tryARM64PackedUnwind(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 @@ -941,9 +942,9 @@ return; } - simplifyOpcodes(info->Instructions, false); + simplifyARM64Opcodes(info->Instructions, false); for (auto &I : info->EpilogMap) - simplifyOpcodes(I.second, true); + simplifyARM64Opcodes(I.second, true); MCContext &context = streamer.getContext(); MCSymbol *Label = context.createTempSymbol(); @@ -991,7 +992,8 @@ uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions); uint32_t TotalCodeBytes = PrologCodeBytes; - int PackedEpilogOffset = checkPackedEpilog(streamer, info, PrologCodeBytes); + int PackedEpilogOffset = + checkARM64PackedEpilog(streamer, info, PrologCodeBytes); if (PackedEpilogOffset >= 0 && uint32_t(PackedEpilogOffset) < PrologCodeBytes && @@ -1004,7 +1006,7 @@ // unwind info there. Keep using that as indicator that this unwind // info has been generated already. - if (tryPackedUnwind(info, FuncLength, PackedEpilogOffset)) + if (tryARM64PackedUnwind(info, FuncLength, PackedEpilogOffset)) return; } @@ -1028,8 +1030,8 @@ // 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) { + } else if ((PrologOffset = getARM64OffsetInProlog(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. @@ -1187,6 +1189,62 @@ return Count; } +static uint32_t ARMCountOfInstructionBytes(ArrayRef Insns) { + uint32_t Count = 0; + for (const auto &I : Insns) { + switch (static_cast(I.Operation)) { + default: + llvm_unreachable("Unsupported ARM unwind code"); + case Win64EH::UOP_AllocSmall: + case Win64EH::UOP_AllocLarge: + case Win64EH::UOP_AllocHuge: + Count += 2; + break; + case Win64EH::UOP_WideAllocMedium: + case Win64EH::UOP_WideAllocLarge: + case Win64EH::UOP_WideAllocHuge: + Count += 4; + break; + case Win64EH::UOP_WideSaveRegMask: + case Win64EH::UOP_WideSaveRegsR4R11LR: + Count += 4; + break; + case Win64EH::UOP_SetFP: + Count += 2; + break; + case Win64EH::UOP_SaveRegMask: + case Win64EH::UOP_SaveRegsR4R7LR: + Count += 2; + break; + case Win64EH::UOP_SaveFRegD8D15: + case Win64EH::UOP_SaveFRegD0D15: + case Win64EH::UOP_SaveFRegD16D31: + Count += 4; + break; + case Win64EH::UOP_SaveLR: + Count += 4; + break; + case Win64EH::UOP_Nop: + case Win64EH::UOP_EndNop: + Count += 2; + break; + case Win64EH::UOP_WideNop: + case Win64EH::UOP_WideEndNop: + Count += 4; + break; + case Win64EH::UOP_End: + // This doesn't map to any instruction + break; + case Win64EH::UOP_Custom: + // We can't reason about what instructions this maps to; return a + // phony number to make sure we don't accidentally do epilog packing. + Count += 1000; + break; + } + } + return Count; +} + // Unwind opcode encodings and restrictions are documented at // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling static void ARMEmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin, @@ -1319,6 +1377,499 @@ } } +static int checkARMPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, + int PrologCodeBytes) { + // Can only pack if there's one single epilog + if (info->EpilogMap.size() != 1) + return -1; + + const std::vector &Epilog = + info->EpilogMap.begin()->second; + // Make sure we have at least the trailing end opcode + if (info->Instructions.empty() || Epilog.empty()) + return -1; + + // Can pack if the epilog is a subset of the prolog but not vice versa + if (Epilog.size() > info->Instructions.size()) + return -1; + + // Check that the epilog actually is a perfect match for the end (backwrds) + // of the prolog. Skip the last opcode, which is checked separately below. + for (int I = Epilog.size() - 1; I >= 1; I--) { + // TODO: Could also allow minor mismatches, e.g. "add sp, #16" vs + // "push {r0-r3}". + if (info->Instructions[I] != Epilog[Epilog.size() - 1 - I]) + return -1; + } + + // Check that both prolog and epilog end with an expected end opcode. + if (info->Instructions.front().Operation != Win64EH::UOP_End) + return -1; + if (Epilog.back().Operation != Win64EH::UOP_End && + Epilog.back().Operation != Win64EH::UOP_EndNop && + Epilog.back().Operation != Win64EH::UOP_WideEndNop) + return -1; + + // Check that the epilog actually is at the very end of the function, + // otherwise it can't be packed. + Optional MaybeDistance = GetOptionalAbsDifference( + streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first); + if (!MaybeDistance) + return -1; + uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance; + uint32_t InstructionBytes = ARMCountOfInstructionBytes(Epilog); + if (DistanceFromEnd != InstructionBytes) + return -1; + + int Offset = Epilog.size() == info->Instructions.size() + ? 0 + : ARMCountOfUnwindCodes(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. + if (Offset > 31 || PrologCodeBytes > 63) + return -1; + + // Replace the regular end opcode of the prolog with the one from the + // epilog. + info->Instructions.front() = Epilog.back(); + + info->EpilogMap.clear(); + return Offset; +} + +static bool parseRegMask(unsigned Mask, bool &HasLR, bool &HasR11, + unsigned &Folded, int &IntRegs) { + if (Mask & (1 << 14)) { + HasLR = true; + Mask &= ~(1 << 14); + } + if (Mask & (1 << 11)) { + HasR11 = true; + Mask &= ~(1 << 11); + } + Folded = 0; + IntRegs = -1; + if (!Mask) + return true; + int First = 0; + // Shift right until we have the bits at the bottom + while ((Mask & 1) == 0) { + First++; + Mask >>= 1; + } + if ((Mask & (Mask + 1)) != 0) + return false; // Not a consecutive series of bits? Can't be packed. + // Count the bits + int N = 0; + while (Mask & (1 << N)) + N++; + if (First < 4) { + if (First + N < 4) + return false; + Folded = 4 - First; + N -= Folded; + First = 4; + } + if (First > 4) + return false; // Can't be packed + if (N >= 1) + IntRegs = N - 1; + return true; +} + +static bool tryARMPackedUnwind(MCStreamer &streamer, WinEH::FrameInfo *info, + uint32_t FuncLength) { + int Step = 0; + bool Homing = false; + bool HasR11 = false; + bool HasChain = false; + bool HasLR = false; + int IntRegs = -1; // r4 - r(4+N) + int FloatRegs = -1; // d8 - d(8+N) + unsigned PF = 0; // Number of extra pushed registers + unsigned StackAdjust = 0; + // Iterate over the prolog and check that all opcodes exactly match + // the canonical order and form. + for (const WinEH::Instruction &Inst : info->Instructions) { + switch (Inst.Operation) { + default: + llvm_unreachable("Unsupported ARM unwind code"); + case Win64EH::UOP_Custom: + case Win64EH::UOP_AllocLarge: + case Win64EH::UOP_AllocHuge: + case Win64EH::UOP_WideAllocLarge: + case Win64EH::UOP_WideAllocHuge: + case Win64EH::UOP_SaveFRegD0D15: + case Win64EH::UOP_SaveFRegD16D31: + // Can't be packed + return false; + case Win64EH::UOP_SetFP: + // Can't be packed; we can't rely on restoring sp from r11 when + // unwinding a packed prologue. + return false; + case Win64EH::UOP_SaveLR: + // Can't be present in a packed prologue + return false; + + case Win64EH::UOP_End: + case Win64EH::UOP_EndNop: + case Win64EH::UOP_WideEndNop: + if (Step != 0) + return false; + Step = 1; + break; + + case Win64EH::UOP_SaveRegsR4R7LR: + case Win64EH::UOP_WideSaveRegsR4R11LR: + // push {r4-r11,lr} + if (Step != 1 && Step != 2) + return false; + assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX + assert(Inst.Offset <= 1); // Lr + IntRegs = Inst.Register - 4; + if (Inst.Register == 11) { + HasR11 = true; + IntRegs--; + } + if (Inst.Offset) + HasLR = true; + Step = 3; + break; + + case Win64EH::UOP_SaveRegMask: + if (Step == 1 && Inst.Register == 0x0f) { + // push {r0-r3} + Homing = true; + Step = 2; + break; + } + LLVM_FALLTHROUGH; + case Win64EH::UOP_WideSaveRegMask: + if (Step != 1 && Step != 2) + return false; + // push {r4-r9,r11,lr} + // push {r11,lr} + // push {r1-r5} + if (!parseRegMask(Inst.Register, HasLR, HasR11, PF, IntRegs)) + return false; + Step = 3; + break; + + case Win64EH::UOP_Nop: + // mov r11, sp + if (Step != 3 || !HasR11 || IntRegs >= 0 || PF > 0) + return false; + HasChain = true; + Step = 4; + break; + case Win64EH::UOP_WideNop: + // add.w r11, sp, #xx + if (Step != 3 || !HasR11 || (IntRegs < 0 && PF == 0)) + return false; + HasChain = true; + Step = 4; + break; + + case Win64EH::UOP_SaveFRegD8D15: + if (Step != 1 && Step != 2 && Step != 3 && Step != 4) + return false; + assert(Inst.Register >= 8 && Inst.Register <= 15); + if (Inst.Register == 15) + return false; // Can't pack this case, R==7 means no IntRegs + if (IntRegs >= 0) + return false; + FloatRegs = Inst.Register - 8; + Step = 5; + break; + + case Win64EH::UOP_AllocSmall: + case Win64EH::UOP_WideAllocMedium: + if (Step != 1 && Step != 2 && Step != 3 && Step != 4 && Step != 5) + return false; + if (PF > 0) // Can't have both folded and explicit stack allocation + return false; + if (Inst.Offset / 4 >= 0x3f4) + return false; + StackAdjust = Inst.Offset / 4; + Step = 6; + break; + } + } + if (HasR11 && !HasChain) { + if (IntRegs + 4 == 10) { + // r11 stored, but not chaining; can be packed if already saving r4-r10 + // and we can fit r11 into this range. + IntRegs++; + HasR11 = false; + } else + return false; + } + if (HasChain && !HasLR) + return false; + + // As the prologue and epilogue aren't exact mirrors of each other, + // we have to check the epilogue too and see if it matches what we've + // concluded from the prologue. + std::vector Epilog; + if (info->EpilogMap.size() != 1) + return false; + Epilog = info->EpilogMap.begin()->second; + Optional MaybeDistance = GetOptionalAbsDifference( + streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first); + if (!MaybeDistance) + return false; + uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance; + uint32_t InstructionBytes = ARMCountOfInstructionBytes(Epilog); + if (DistanceFromEnd != InstructionBytes) + return false; + + bool GotStackAdjust = false; + bool GotFloatRegs = false; + bool GotIntRegs = false; + bool GotHomingRestore = false; + bool GotLRRestore = false; + bool NeedsReturn = false; + bool GotReturn = false; + unsigned EF = 0; + int Ret = 0; + + Step = 6; + for (const WinEH::Instruction &Inst : Epilog) { + switch (Inst.Operation) { + default: + llvm_unreachable("Unsupported ARM unwind code"); + case Win64EH::UOP_Custom: + case Win64EH::UOP_AllocLarge: + case Win64EH::UOP_AllocHuge: + case Win64EH::UOP_WideAllocLarge: + case Win64EH::UOP_WideAllocHuge: + case Win64EH::UOP_SaveFRegD0D15: + case Win64EH::UOP_SaveFRegD16D31: + case Win64EH::UOP_SetFP: + // Can't be packed in an epilogue + return false; + + case Win64EH::UOP_AllocSmall: + case Win64EH::UOP_WideAllocMedium: + if (Inst.Offset / 4 >= 0x3f4) + return false; + if (Step == 6) { + if (Homing && FloatRegs < 0 && IntRegs < 0 && StackAdjust == 0 && + PF == 0 && Inst.Offset == 16) { + GotHomingRestore = true; + Step = 10; + } else { + if (StackAdjust > 0) { + // Got stack adjust in prologue too; must match. + if (StackAdjust != Inst.Offset / 4) + return false; + GotStackAdjust = true; + } else if (PF == Inst.Offset / 4) { + // Folded prologue, non-folded epilogue + StackAdjust = Inst.Offset / 4; + GotStackAdjust = true; + } else { + // StackAdjust == 0 in prologue, mismatch + return false; + } + Step = 7; + } + } else if (Step == 7 || Step == 8 || Step == 9) { + if (!Homing || Inst.Offset != 16) + return false; + GotHomingRestore = true; + Step = 10; + } else + return false; + break; + + case Win64EH::UOP_SaveFRegD8D15: + if (Step != 6 && Step != 7) + return false; + assert(Inst.Register >= 8 && Inst.Register <= 15); + if (FloatRegs != (int)(Inst.Register - 8)) + return false; + GotFloatRegs = true; + Step = 8; + break; + + case Win64EH::UOP_SaveRegsR4R7LR: + case Win64EH::UOP_WideSaveRegsR4R11LR: { + // push {r4-r11,lr} + if (Step != 6 && Step != 7 && Step != 8) + return false; + assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX + assert(Inst.Offset <= 1); // Lr + if (Homing && HasLR) { + // If homing and LR is backed up, we can either restore LR here + // and return with Ret == 1 or 2, or return with SaveLR below + if (Inst.Offset) { + GotLRRestore = true; + NeedsReturn = true; + } else { + // Expecting a separate SaveLR below + } + } else { + if (HasLR != (Inst.Offset == 1)) + return false; + } + GotLRRestore = Inst.Offset == 1; + if (IntRegs < 0) // This opcode must include r4 + return false; + int Expected = IntRegs; + if (HasChain) { + // Can't express r11 here unless IntRegs describe r4-r10 + if (IntRegs != 6) + return false; + Expected++; + } + if (Expected != (int)(Inst.Register - 4)) + return false; + GotIntRegs = true; + Step = 9; + break; + } + + case Win64EH::UOP_SaveRegMask: + case Win64EH::UOP_WideSaveRegMask: { + if (Step != 6 && Step != 7 && Step != 8) + return false; + // push {r4-r9,r11,lr} + // push {r11,lr} + // push {r1-r5} + bool CurHasLR = false, CurHasR11 = false; + int Regs; + if (!parseRegMask(Inst.Register, CurHasLR, CurHasR11, EF, Regs)) + return false; + if (EF > 0) { + if (EF != PF && EF != StackAdjust) + return false; + } + if (Homing && HasLR) { + // If homing and LR is backed up, we can either restore LR here + // and return with Ret == 1 or 2, or return with SaveLR below + if (CurHasLR) { + GotLRRestore = true; + NeedsReturn = true; + } else { + // Expecting a separate SaveLR below + } + } else { + if (CurHasLR != HasLR) + return false; + GotLRRestore = CurHasLR; + } + int Expected = IntRegs; + if (HasChain) { + // If we have chaining, the mask must have included r11. + if (!CurHasR11) + return false; + } else if (Expected == 7) { + // If we don't have chaining, the mask could still include r11, + // expressed as part of IntRegs Instead. + Expected--; + if (!CurHasR11) + return false; + } else { + // Neither HasChain nor r11 included in IntRegs, must not have r11 + // here either. + if (CurHasR11) + return false; + } + if (Expected != Regs) + return false; + GotIntRegs = true; + Step = 9; + break; + } + + case Win64EH::UOP_SaveLR: + if (Step != 6 && Step != 7 && Step != 8 && Step != 9) + return false; + if (!Homing || Inst.Offset != 20 || GotLRRestore) + return false; + GotLRRestore = true; + GotHomingRestore = true; + Step = 10; + break; + + case Win64EH::UOP_EndNop: + case Win64EH::UOP_WideEndNop: + GotReturn = true; + Ret = (Inst.Operation == Win64EH::UOP_EndNop) ? 1 : 2; + LLVM_FALLTHROUGH; + case Win64EH::UOP_End: + if (Step != 6 && Step != 7 && Step != 8 && Step != 9 && Step != 10) + return false; + Step = 11; + break; + } + } + + if (Step != 11) + return false; + if (StackAdjust > 0 && !GotStackAdjust && EF == 0) + return false; + if (FloatRegs >= 0 && !GotFloatRegs) + return false; + if (IntRegs >= 0 && !GotIntRegs) + return false; + if (Homing && !GotHomingRestore) + return false; + if (HasLR && !GotLRRestore) + return false; + if (NeedsReturn && !GotReturn) + return false; + + assert(PF == 0 || EF == 0 || + StackAdjust == 0); // Can't have adjust in all three + if (PF > 0 || EF > 0) { + StackAdjust = PF > 0 ? (PF - 1) : (EF - 1); + assert(StackAdjust <= 3); + StackAdjust |= 0x3f0; + if (PF > 0) + StackAdjust |= 1 << 2; + if (EF > 0) + StackAdjust |= 1 << 3; + } + + assert(FuncLength <= 0x7FF && "FuncLength should have been checked earlier"); + int Flag = 0x01; // Function segments not supported yet + int H = Homing ? 1 : 0; + int L = HasLR ? 1 : 0; + int C = HasChain ? 1 : 0; + assert(IntRegs < 0 || FloatRegs < 0); + unsigned Reg, R; + if (IntRegs >= 0) { + Reg = IntRegs; + assert(Reg <= 7); + R = 0; + } else if (FloatRegs >= 0) { + Reg = FloatRegs; + assert(Reg < 7); + R = 1; + } else { + // No int or float regs stored (except possibly R11,LR) + Reg = 7; + R = 1; + } + info->PackedInfo |= Flag << 0; + info->PackedInfo |= (FuncLength & 0x7FF) << 2; + info->PackedInfo |= (Ret & 0x3) << 13; + info->PackedInfo |= H << 15; + info->PackedInfo |= Reg << 16; + info->PackedInfo |= R << 19; + info->PackedInfo |= L << 20; + info->PackedInfo |= C << 21; + assert(StackAdjust <= 0x3ff); + info->PackedInfo |= StackAdjust << 22; + return true; +} + // Populate the .xdata section. The format of .xdata on ARM is documented at // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, @@ -1383,6 +1934,23 @@ uint32_t PrologCodeBytes = ARMCountOfUnwindCodes(info->Instructions); uint32_t TotalCodeBytes = PrologCodeBytes; + if (!info->HandlesExceptions && RawFuncLength && FuncLength <= 0x7ff && + TryPacked) { + // No exception handlers; check if the prolog and epilog matches the + // patterns that can be described by the packed format. If we don't + // know the exact function length yet, we can't do this. + + // 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 (tryARMPackedUnwind(streamer, info, FuncLength)) + return; + } + + int PackedEpilogOffset = + checkARMPackedEpilog(streamer, info, PrologCodeBytes); + // Process epilogs. MapVector EpilogInfo; // Epilogs processed so far. @@ -1415,7 +1983,8 @@ uint32_t CodeWordsMod = TotalCodeBytes % 4; if (CodeWordsMod) CodeWords++; - uint32_t EpilogCount = info->EpilogMap.size(); + uint32_t EpilogCount = + PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size(); bool ExtensionWord = EpilogCount > 31 || CodeWords > 15; if (!ExtensionWord) { row1 |= (EpilogCount & 0x1F) << 23; @@ -1423,6 +1992,8 @@ } if (info->HandlesExceptions) // X row1 |= 1 << 20; + if (PackedEpilogOffset >= 0) // E + row1 |= 1 << 21; row1 |= FuncLength & 0x3FFFF; if (RawFuncLength) streamer.emitInt32(row1); diff --git a/llvm/test/MC/ARM/seh-epilog-packing.s b/llvm/test/MC/ARM/seh-epilog-packing.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/seh-epilog-packing.s @@ -0,0 +1,169 @@ +// This test checks various cases around sharing opcodes between epilogue and prologue + +// RUN: llvm-mc -triple thumbv7-pc-win32 -filetype=obj %s | llvm-readobj -u - | FileCheck %s + +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: func1 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK-NEXT: FunctionLength: +// CHECK-NEXT: Version: +// CHECK-NEXT: ExceptionData: +// CHECK-NEXT: EpiloguePacked: Yes +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: EpilogueOffset: 2 +// CHECK-NEXT: ByteCodeLength: +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: 0xf5 0x15 ; vpush {d1-d5} +// CHECK-NEXT: 0x05 ; sub sp, #(5 * 4) +// CHECK-NEXT: 0xa0 0xf0 ; push.w {r4-r7, lr} +// CHECK-NEXT: 0xfe ; b.w +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0x05 ; add sp, #(5 * 4) +// CHECK-NEXT: 0xa0 0xf0 ; pop.w {r4-r7, pc} +// CHECK-NEXT: 0xfe ; b.w +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func2 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK-NEXT: FunctionLength: +// CHECK-NEXT: Version: +// CHECK-NEXT: ExceptionData: +// CHECK-NEXT: EpiloguePacked: Yes +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: EpilogueOffset: 0 +// CHECK-NEXT: ByteCodeLength: 4 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: 0xd2 ; push {r4-r6} +// CHECK-NEXT: 0x04 ; sub sp, #(4 * 4) +// CHECK-NEXT: 0xfd ; bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func3 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK-NEXT: FunctionLength: +// CHECK-NEXT: Version: +// CHECK-NEXT: ExceptionData: +// CHECK-NEXT: EpiloguePacked: Yes +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: EpilogueOffset: 0 +// CHECK-NEXT: ByteCodeLength: 4 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: 0xe1 ; vpush {d8-d9} +// CHECK-NEXT: 0xdf ; push.w {r4-r11, lr} +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: notshared1 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK-NEXT: FunctionLength: +// CHECK-NEXT: Version: +// CHECK-NEXT: ExceptionData: +// CHECK-NEXT: EpiloguePacked: No +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: notshared2 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK-NEXT: FunctionLength: +// CHECK-NEXT: Version: +// CHECK-NEXT: ExceptionData: +// CHECK-NEXT: EpiloguePacked: No + + .text + .syntax unified + .seh_proc func1 +func1: + push.w {r4-r7,lr} + .seh_save_regs_w {r4-r7,lr} + sub sp, sp, #20 + .seh_stackalloc 20 + vpush {d1-d5} + .seh_save_fregs {d1-d5} + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #20 + .seh_stackalloc 20 + // As we're popping into lr instead of directly into pc, this pop + // becomes a wide instruction. To match prologue vs epilogue, the + // push in the prologue has been made wide too. + pop.w {r4-r7,lr} + .seh_save_regs_w {r4-r7,lr} + b.w tailcall + .seh_endepilogue_nop_w + .seh_endproc + + .seh_proc func2 +func2: + sub sp, sp, #16 + .seh_stackalloc 16 + push {r4-r6} + .seh_save_regs {r4-r6} + .seh_endprologue + nop + .seh_startepilogue + pop {r4-r6} + .seh_save_regs {r4-r6} + add sp, sp, #16 + .seh_stackalloc 16 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func3 +func3: + push {r4-r11,lr} + .seh_save_regs_w {r4-r11,lr} + vpush {d8-d9} + .seh_save_fregs {d8-d9} + .seh_endprologue + nop + .seh_startepilogue + vpop {d8-d9} + .seh_save_fregs {d8-d9} + pop {r4-r11,pc} + .seh_save_regs_w {r4-r11,pc} + .seh_endepilogue + .seh_endproc + + .seh_proc notshared1 +notshared1: + push {r4-r11,lr} + .seh_save_regs_w {r4-r11,lr} + .seh_endprologue + nop + .seh_startepilogue + // not shared as this opcode doesn't match the prolog + pop {r4-r11} + .seh_save_regs_w {r4-r11} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc notshared2 +notshared2: + push {r4-r11} + .seh_save_regs_w {r4-r11} + vpush {d8-d9} + .seh_save_fregs {d8-d9} + .seh_endprologue + nop + .seh_startepilogue + vpop {d8-d9} + .seh_save_fregs {d8-d9} + pop {r4-r11} + .seh_save_regs_w {r4-r11} + bx lr + .seh_endepilogue_nop + // Not shared, as the epilog isn't at the end of the function + nop + .seh_endproc diff --git a/llvm/test/MC/ARM/seh-packed.s b/llvm/test/MC/ARM/seh-packed.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/seh-packed.s @@ -0,0 +1,992 @@ +// This test checks various cases around generating packed unwind info. + +// RUN: llvm-mc -triple thumbv7-pc-win32 -filetype=obj %s | llvm-readobj -u - | FileCheck %s + +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: func6 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 8 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 0 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func7 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 8 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 0 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r4} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r4} +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func8 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 10 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 0 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r4, lr} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r4, lr} +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func9 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 24 +// CHECK-NEXT: ReturnType: b.w +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 32 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {lr} +// CHECK-NEXT: vpush {d8} +// CHECK-NEXT: sub sp, sp, #32 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #32 +// CHECK-NEXT: vpop {d8} +// CHECK-NEXT: pop {lr} +// CHECK-NEXT: b.w +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func10 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 26 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 1 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: Yes +// CHECK-NEXT: StackAdjustment: 16 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r11, lr} +// CHECK-NEXT: mov r11, sp +// CHECK-NEXT: vpush {d8-d9} +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: vpop {d8-d9} +// CHECK-NEXT: pop {r11, lr} +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func11 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 24 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 1 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: Yes +// CHECK-NEXT: StackAdjustment: 16 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r11, lr} +// CHECK-NEXT: mov r11, sp +// CHECK-NEXT: vpush {d8-d9} +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: vpop {d8-d9} +// CHECK-NEXT: pop {r11, pc} +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func12 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 18 +// CHECK-NEXT: ReturnType: b.w +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 6 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 16 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: vpush {d8-d14} +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: vpop {d8-d14} +// CHECK-NEXT: b.w +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func13 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 18 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 6 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: Yes +// CHECK-NEXT: StackAdjustment: 20 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r4-r11, lr} +// CHECK-NEXT: add.w r11, sp, #28 +// CHECK-NEXT: sub sp, sp, #20 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #20 +// CHECK-NEXT: pop {r4-r11, pc} +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func14 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 14 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 20 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r4-r11, lr} +// CHECK-NEXT: sub sp, sp, #20 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #20 +// CHECK-NEXT: pop {r4-r11, pc} +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func15 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 20 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 512 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: push {r4, lr} +// CHECK-NEXT: sub sp, sp, #512 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #512 +// CHECK-NEXT: pop {r4} +// CHECK-NEXT: ldr pc, [sp], #20 +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func16 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 20 +// CHECK-NEXT: ReturnType: b.w +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: Yes +// CHECK-NEXT: StackAdjustment: 0 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: push {r11, lr} +// CHECK-NEXT: mov r11, sp +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r11, lr} +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: b.w +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func17 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 20 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 512 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: push {r4} +// CHECK-NEXT: sub sp, sp, #512 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #512 +// CHECK-NEXT: pop {r4} +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func18 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 6 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 4 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r3, lr} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r3, pc} +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func19 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 12 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 16 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: push {r0-r4} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r0-r4} +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func20 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 14 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 16 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: push {r0-r4} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: pop {r4} +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func21 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 14 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 0 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 16 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: push {r4} +// CHECK-NEXT: sub sp, sp, #16 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r0-r4} +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func24 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 16 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 3 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: Yes +// CHECK-NEXT: StackAdjustment: 8 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r2-r7, r11, lr} +// CHECK-NEXT: add.w r11, sp, #24 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #8 +// CHECK-NEXT: pop {r4-r7, r11, pc} +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func25 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 16 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 3 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: Yes +// CHECK-NEXT: StackAdjustment: 8 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r4-r7, r11, lr} +// CHECK-NEXT: add.w r11, sp, #16 +// CHECK-NEXT: sub sp, sp, #8 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r2-r7, r11, pc} +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func26 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 8 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 12 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r1-r3} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #12 +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func27 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 8 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 12 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: sub sp, sp, #12 +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r1-r3} +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func28 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 8 +// CHECK-NEXT: ReturnType: bx +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: No +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 0 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: add sp, sp, #16 +// CHECK-NEXT: bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func29 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 10 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: Yes +// CHECK-NEXT: Reg: 7 +// CHECK-NEXT: R: 1 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 0 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r0-r3} +// CHECK-NEXT: push {lr} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: ldr pc, [sp], #20 +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: RuntimeFunction { +// CHECK-NEXT: Function: func30 +// CHECK-NEXT: Fragment: No +// CHECK-NEXT: FunctionLength: 6 +// CHECK-NEXT: ReturnType: pop {pc} +// CHECK-NEXT: HomedParameters: No +// CHECK-NEXT: Reg: 2 +// CHECK-NEXT: R: 0 +// CHECK-NEXT: LinkRegister: Yes +// CHECK-NEXT: Chaining: No +// CHECK-NEXT: StackAdjustment: 0 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: push {r4-r6, lr} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: pop {r4-r6, pc} +// CHECK-NEXT: ] +// CHECK-NEXT: } + +// CHECK: Function: notpacked1 +// CHECK-NEXT: ExceptionRecord: +// CHECK: Function: notpacked2 +// CHECK-NEXT: ExceptionRecord: +// CHECK: Function: notpacked3 +// CHECK-NEXT: ExceptionRecord: +// CHECK: Function: notpacked4 +// CHECK-NEXT: ExceptionRecord: +// CHECK: Function: notpacked5 +// CHECK-NEXT: ExceptionRecord: +// CHECK: Function: notpacked6 +// CHECK-NEXT: ExceptionRecord: +// CHECK: Function: notpacked7 +// CHECK-NEXT: ExceptionRecord: +// CHECK: Function: notpacked8 +// CHECK-NEXT: ExceptionRecord: + + .text + .syntax unified + + .seh_proc func6 +func6: + .seh_endprologue + nop + nop + nop + .seh_startepilogue + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func7 +func7: + push {r4} + .seh_save_regs {r4} + .seh_endprologue + nop + .seh_startepilogue + pop {r4} + .seh_save_regs {r4} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func8 +func8: + push {r4,lr} + .seh_save_regs {r4,lr} + .seh_endprologue + nop + .seh_startepilogue + pop.w {r4,lr} + .seh_save_regs_w {r4,lr} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func9 +func9: + push {lr} + .seh_save_regs {lr} + vpush {d8} + .seh_save_fregs {d8} + sub sp, sp, #32 + .seh_stackalloc 32 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #32 + .seh_stackalloc 32 + vpop {d8} + .seh_save_fregs {d8} + pop.w {lr} + .seh_save_regs_w {lr} + b.w tailcall + .seh_endepilogue_nop_w + .seh_endproc + + .seh_proc func10 +func10: + push.w {r11,lr} + .seh_save_regs_w {r11,lr} + mov r11, sp + .seh_nop + vpush {d8-d9} + .seh_save_fregs {d8-d9} + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + vpop {d8-d9} + .seh_save_fregs {d8-d9} + pop.w {r11,lr} + .seh_save_regs_w {r11,lr} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func11 +func11: + push.w {r11,lr} + .seh_save_regs_w {r11,lr} + mov r11, sp + .seh_nop + vpush {d8-d9} + .seh_save_fregs {d8-d9} + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + vpop {d8-d9} + .seh_save_fregs {d8-d9} + pop.w {r11,pc} + .seh_save_regs_w {r11,pc} + .seh_endepilogue + .seh_endproc + + .seh_proc func12 +func12: + vpush {d8-d14} + .seh_save_fregs {d8-d14} + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + vpop {d8-d14} + .seh_save_fregs {d8-d14} + b.w tailcall + .seh_endepilogue_nop_w + .seh_endproc + + .seh_proc func13 +func13: + push.w {r4-r11,lr} + .seh_save_regs_w {r4-r11,lr} + add.w r11, sp, #0x1c + .seh_nop_w + sub sp, sp, #20 + .seh_stackalloc 20 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #20 + .seh_stackalloc 20 + pop.w {r4-r11,pc} + .seh_save_regs_w {r4-r11,lr} + .seh_endepilogue + .seh_endproc + + .seh_proc func14 +func14: + push.w {r4-r11,lr} + .seh_save_regs_w {r4-r11,lr} + sub sp, sp, #20 + .seh_stackalloc 20 + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #20 + .seh_stackalloc 20 + pop.w {r4-r11,pc} + .seh_save_regs_w {r4-r11,lr} + .seh_endepilogue + .seh_endproc + + .seh_proc func15 +func15: + push {r0-r3} + .seh_save_regs {r0-r3} + push {r4,lr} + .seh_save_regs {r4,lr} + sub.w sp, sp, #512 + .seh_stackalloc_w 512 + .seh_endprologue + nop + .seh_startepilogue + add.w sp, sp, #512 + .seh_stackalloc_w 512 + pop {r4} + .seh_save_regs {r4} + ldr pc, [sp], #20 + .seh_save_lr 20 + .seh_endepilogue + .seh_endproc + + .seh_proc func16 +func16: + push {r0-r3} + .seh_save_regs {r0-r3} + push.w {r11,lr} + .seh_save_regs_w {r11,lr} + mov r11, sp + .seh_nop + .seh_endprologue + nop + .seh_startepilogue + pop.w {r11, lr} + .seh_save_regs_w {r11,lr} + add sp, sp, #16 + .seh_stackalloc 16 + b.w tailcall + .seh_endepilogue_nop_w + .seh_endproc + + .seh_proc func17 +func17: + push {r0-r3} + .seh_save_regs {r0-r3} + push {r4} + .seh_save_regs {r4} + sub.w sp, sp, #512 + .seh_stackalloc_w 512 + .seh_endprologue + nop + .seh_startepilogue + add.w sp, sp, #512 + .seh_stackalloc_w 512 + pop {r4} + .seh_save_regs {r4} + add sp, sp, #16 + .seh_stackalloc 16 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func18 +func18: + push {r3,lr} + .seh_save_regs {r3,lr} + .seh_endprologue + nop + .seh_startepilogue + pop {r3,pc} + .seh_save_regs {r3,pc} + .seh_endepilogue + .seh_endproc + + .seh_proc func19 +func19: + push {r0-r3} + .seh_save_regs {r0-r3} + push {r0-r4} + .seh_save_regs {r0-r4} + .seh_endprologue + nop + .seh_startepilogue + pop {r0-r4} + .seh_save_regs {r0-r4} + add sp, sp, #16 + .seh_stackalloc 16 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func20 +func20: + push {r0-r3} + .seh_save_regs {r0-r3} + push {r0-r4} + .seh_save_regs {r0-r4} + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + pop {r4} + .seh_save_regs {r4} + add sp, sp, #16 + .seh_stackalloc 16 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func21 +func21: + push {r0-r3} + .seh_save_regs {r0-r3} + push {r4} + .seh_save_regs {r4} + sub sp, sp, #16 + .seh_stackalloc 16 + .seh_endprologue + nop + .seh_startepilogue + pop {r0-r4} + .seh_save_regs {r0-r4} + add sp, sp, #16 + .seh_stackalloc 16 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func24 +func24: + push.w {r2-r7,r11,lr} + .seh_save_regs_w {r2-r7,r11,lr} + add.w r11, sp, #24 + .seh_nop_w + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #8 + .seh_stackalloc 8 + pop.w {r4-r7,r11,pc} + .seh_save_regs_w {r4-r7,r11,pc} + .seh_endepilogue + .seh_endproc + + .seh_proc func25 +func25: + push.w {r4-r7,r11,lr} + .seh_save_regs_w {r4-r7,r11,lr} + add.w r11, sp, #16 + .seh_nop_w + sub sp, sp, #8 + .seh_stackalloc 8 + .seh_endprologue + nop + .seh_startepilogue + pop.w {r2-r7,r11,pc} + .seh_save_regs_w {r2-r7,r11,pc} + .seh_endepilogue + .seh_endproc + + .seh_proc func26 +func26: + push {r1-r3} + .seh_save_regs {r1-r3} + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #12 + .seh_stackalloc 12 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func27 +func27: + sub sp, sp, #12 + .seh_stackalloc 12 + .seh_endprologue + nop + .seh_startepilogue + pop {r1-r3} + .seh_save_regs {r1-r3} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func28 +func28: + push {r0-r3} + .seh_save_regs {r0-r3} + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func29 +func29: + push {r0-r3} + .seh_save_regs {r0-r3} + push {lr} + .seh_save_regs {lr} + .seh_endprologue + nop + .seh_startepilogue + ldr pc, [sp], #20 + .seh_save_lr 20 + .seh_endepilogue + .seh_endproc + + .seh_proc func30 +func30: + push {r4-r6,lr} + .seh_save_regs {r4-r6,lr} + .seh_endprologue + nop + .seh_startepilogue + pop {r4-r6,pc} + .seh_save_regs {r4-r6,pc} + .seh_endepilogue + .seh_endproc + + .seh_proc notpacked1 +notpacked1: + push {r1-r3} + .seh_save_regs {r1-r3} + .seh_endprologue + nop + .seh_startepilogue + // Mismatch with the folded prologue + add sp, sp, #8 + .seh_stackalloc 8 + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc notpacked2 +notpacked2: + sub sp, sp, #8 + .seh_stackalloc 8 + .seh_endprologue + nop + .seh_startepilogue + // Folded epilogue is a mismatch to the regular stack adjust in the prologue + pop {r1-r3} + .seh_save_regs {r1-r3} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc notpacked3 +notpacked3: + // Can't represent d8-d15 in the packed form + vpush {d8-d15} + .seh_save_fregs {d8-d15} + .seh_endprologue + nop + .seh_startepilogue + vpop {d8-d15} + .seh_save_fregs {d8-d15} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc notpacked4 +notpacked4: + push {r2-r7} + .seh_save_regs {r2-r7} + sub sp, sp, #16 + .seh_stackalloc 16 + // Can't have both a folded stack adjustment and a separate one + .seh_endprologue + nop + .seh_startepilogue + add sp, sp, #16 + .seh_stackalloc 16 + pop {r2-r7} + .seh_save_regs {r2-r7} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc notpacked5 +notpacked5: + // Can't represent r11 in packed form when it's not contiguous + // with the rest and when it's not chained (missing "add.w r11, sp, #.." + // and .seh_nop_w). + push.w {r4-r7,r11,lr} + .seh_save_regs_w {r4-r7,r11,lr} + sub sp, sp, #8 + .seh_stackalloc 8 + .seh_endprologue + nop + .seh_startepilogue + pop.w {r2-r7,r11,pc} + .seh_save_regs_w {r2-r7,r11,pc} + .seh_endepilogue + .seh_endproc + + .seh_proc notpacked6 +notpacked6: + // Can't pack non-contiguous registers + push {r4,r7} + .seh_save_regs {r4,r7} + .seh_endprologue + nop + .seh_startepilogue + pop {r4,r7} + .seh_save_regs {r4,r7} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc notpacked7 +notpacked7: + // Can't pack float registers ouside of d8-d14 + vpush {d0-d3} + .seh_save_fregs {d0-d3} + .seh_endprologue + nop + .seh_startepilogue + vpop {d0-d3} + .seh_save_fregs {d0-d3} + bx lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc notpacked8 +notpacked8: + push {r4-r7,lr} + .seh_save_regs {r4-r7,lr} + .seh_endprologue + nop + .seh_startepilogue + pop {r4-r7,lr} + .seh_save_regs {r4-r7,pc} + .seh_endepilogue_nop + // Epilogue isn't at the end of the function; can't be packed. + nop + .seh_endproc