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 @@ -951,9 +952,9 @@ return; } - simplifyOpcodes(info->Instructions, false); + simplifyARM64Opcodes(info->Instructions, false); for (auto &I : info->EpilogMap) - simplifyOpcodes(I.second.Instructions, true); + simplifyARM64Opcodes(I.second.Instructions, true); MCContext &context = streamer.getContext(); MCSymbol *Label = context.createTempSymbol(); @@ -1001,7 +1002,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 && @@ -1014,7 +1016,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; } @@ -1038,8 +1040,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. @@ -1195,6 +1197,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_SaveSP: + 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, @@ -1327,6 +1385,536 @@ } } +// Check if an epilog exists as a subset of the end of a prolog (backwards). +// An epilog may end with one out of three different end opcodes; if this +// is the first epilog that shares opcodes with the prolog, we can tolerate +// that this opcode differs (and the caller will update the prolog to use +// the same end opcode as the epilog). If another epilog already shares +// opcodes with the prolog, the ending opcode must be a strict match. +static int getARMOffsetInProlog(const std::vector &Prolog, + const std::vector &Epilog, + bool CanTweakProlog) { + // Can't find an epilog as a subset if it is longer than the prolog. + if (Epilog.size() > Prolog.size()) + return -1; + + // Check that the epilog actually is a perfect match for the end (backwrds) + // of the prolog. + // If we can adjust the prolog afterwards, don't check that the end opcodes + // match. + int EndIdx = CanTweakProlog ? 1 : 0; + for (int I = Epilog.size() - 1; I >= EndIdx; I--) { + // TODO: Could also allow minor mismatches, e.g. "add sp, #16" vs + // "push {r0-r3}". + if (Prolog[I] != Epilog[Epilog.size() - 1 - I]) + return -1; + } + + if (CanTweakProlog) { + // Check that both prolog and epilog end with an expected end opcode. + if (Prolog.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; + } + + // If the epilog was a subset of the prolog, find its offset. + if (Epilog.size() == Prolog.size()) + return 0; + return ARMCountOfUnwindCodes(ArrayRef( + &Prolog[Epilog.size()], Prolog.size() - Epilog.size())); +} + +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 WinEH::FrameInfo::Epilog &EpilogInfo = info->EpilogMap.begin()->second; + // Can only pack if the epilog is unconditional + if (EpilogInfo.Condition != 0xe) // ARMCC::AL + return -1; + + const std::vector &Epilog = EpilogInfo.Instructions; + // Make sure we have at least the trailing end opcode + if (info->Instructions.empty() || Epilog.empty()) + 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 RetVal = -1; + // Even if we don't end up sharing opcodes with the prolog, we can still + // write the offset as a packed offset, if the single epilog is located at + // the end of the function and the offset (pointing after the prolog) fits + // as a packed offset. + if (PrologCodeBytes <= 31 && + PrologCodeBytes + ARMCountOfUnwindCodes(Epilog) <= 63) + RetVal = PrologCodeBytes; + + int Offset = + getARMOffsetInProlog(info->Instructions, Epilog, /*CanTweakProlog=*/true); + if (Offset < 0) + return RetVal; + + // 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 RetVal; + + // Replace the regular end opcode of the prolog with the one from the + // epilog. + info->Instructions.front() = Epilog.back(); + + // As we choose to express the epilog as part of the prolog, remove the + // epilog from the map, so we don't try to emit its opcodes. + 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_SaveSP: + // 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. + if (info->EpilogMap.size() != 1) + return false; + const WinEH::FrameInfo::Epilog &EpilogInfo = info->EpilogMap.begin()->second; + if (EpilogInfo.Condition != 0xe) // ARMCC::AL + return false; + const std::vector &Epilog = EpilogInfo.Instructions; + 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_SaveSP: + // 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 = info->Fragment ? 0x02 : 0x01; + 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, @@ -1390,11 +1978,29 @@ 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. std::vector AddedEpilogs; + bool CanTweakProlog = true; for (auto &I : info->EpilogMap) { MCSymbol *EpilogStart = I.first; auto &EpilogInstrs = I.second.Instructions; @@ -1402,6 +2008,7 @@ MCSymbol *MatchingEpilog = FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info); + int PrologOffset; if (MatchingEpilog) { assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() && "Duplicate epilog not found"); @@ -1409,6 +2016,19 @@ // Clear the unwind codes in the EpilogMap, so that they don't get output // in the logic below. EpilogInstrs.clear(); + } else if ((PrologOffset = getARMOffsetInProlog( + info->Instructions, EpilogInstrs, CanTweakProlog)) >= 0) { + if (CanTweakProlog) { + // Replace the regular end opcode of the prolog with the one from the + // epilog. + info->Instructions.front() = EpilogInstrs.back(); + // Later epilogs need a strict match for the end opcode. + CanTweakProlog = false; + } + EpilogInfo[EpilogStart] = PrologOffset; + // Clear the unwind codes in the EpilogMap, so that they don't get output + // in the logic below. + EpilogInstrs.clear(); } else { EpilogInfo[EpilogStart] = TotalCodeBytes; TotalCodeBytes += CodeBytes; @@ -1422,7 +2042,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; @@ -1430,6 +2051,8 @@ } if (info->HandlesExceptions) // X row1 |= 1 << 20; + if (PackedEpilogOffset >= 0) // E + row1 |= 1 << 21; if (info->Fragment) // F row1 |= 1 << 22; row1 |= FuncLength & 0x3FFFF; @@ -1452,34 +2075,36 @@ streamer.emitInt32(row2); } - // Epilog Start Index, Epilog Start Offset - for (auto &I : EpilogInfo) { - MCSymbol *EpilogStart = I.first; - uint32_t EpilogIndex = I.second; - - Optional MaybeEpilogOffset = - GetOptionalAbsDifference(streamer, EpilogStart, info->Begin); - const MCExpr *OffsetExpr = nullptr; - uint32_t EpilogOffset = 0; - if (MaybeEpilogOffset) - EpilogOffset = *MaybeEpilogOffset / 2; - else - OffsetExpr = GetSubDivExpr(streamer, EpilogStart, info->Begin, 2); + if (PackedEpilogOffset < 0) { + // Epilog Start Index, Epilog Start Offset + for (auto &I : EpilogInfo) { + MCSymbol *EpilogStart = I.first; + uint32_t EpilogIndex = I.second; - assert(info->EpilogMap.find(EpilogStart) != info->EpilogMap.end()); - unsigned Condition = info->EpilogMap[EpilogStart].Condition; - assert(Condition <= 0xf); + Optional MaybeEpilogOffset = + GetOptionalAbsDifference(streamer, EpilogStart, info->Begin); + const MCExpr *OffsetExpr = nullptr; + uint32_t EpilogOffset = 0; + if (MaybeEpilogOffset) + EpilogOffset = *MaybeEpilogOffset / 2; + else + OffsetExpr = GetSubDivExpr(streamer, EpilogStart, info->Begin, 2); - uint32_t row3 = EpilogOffset; - row3 |= Condition << 20; - row3 |= (EpilogIndex & 0x3FF) << 24; - if (MaybeEpilogOffset) - streamer.emitInt32(row3); - else - streamer.emitValue( - MCBinaryExpr::createOr( - OffsetExpr, MCConstantExpr::create(row3, context), context), - 4); + assert(info->EpilogMap.find(EpilogStart) != info->EpilogMap.end()); + unsigned Condition = info->EpilogMap[EpilogStart].Condition; + assert(Condition <= 0xf); + + uint32_t row3 = EpilogOffset; + row3 |= Condition << 20; + row3 |= (EpilogIndex & 0x3FF) << 24; + if (MaybeEpilogOffset) + streamer.emitInt32(row3); + else + streamer.emitValue( + MCBinaryExpr::createOr( + OffsetExpr, MCConstantExpr::create(row3, context), context), + 4); + } } // Emit prolog unwind instructions (in reverse order). 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,201 @@ +// 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: Yes +// CHECK-NEXT: Fragment: +// CHECK-NEXT: EpilogueOffset: 2 +// CHECK-NEXT: ByteCodeLength: 4 +// CHECK-NEXT: Prologue [ +// CHECK-NEXT: 0xdf ; push.w {r4-r11, lr} +// CHECK-NEXT: ] +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0xdb ; pop.w {r4-r11} +// CHECK-NEXT: 0xfd ; bx +// CHECK-NEXT: ] +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: notpacked2 +// CHECK-NEXT: ExceptionRecord: +// CHECK-NEXT: ExceptionData { +// CHECK-NEXT: FunctionLength: +// CHECK-NEXT: Version: +// CHECK-NEXT: ExceptionData: +// CHECK-NEXT: EpiloguePacked: No +// CHECK: RuntimeFunction { +// CHECK-NEXT: Function: notpacked3 +// 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 + // Packed, but 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 notpacked2 +notpacked2: + 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 packed, as the epilog isn't at the end of the function + nop + .seh_endproc + + .seh_proc notpacked3 +notpacked3: + push {r4-r11,lr} + .seh_save_regs_w {r4-r11,lr} + .seh_endprologue + nop + it ge + // Not packed, as the epilog is conditional + .seh_startepilogue_cond ge + popge {r4-r11,pc} + .seh_save_regs_w {r4-r11,pc} + .seh_endepilogue + .seh_endproc diff --git a/llvm/test/MC/ARM/seh-epilog-sharing.s b/llvm/test/MC/ARM/seh-epilog-sharing.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/seh-epilog-sharing.s @@ -0,0 +1,183 @@ +// This test checks various cases around sharing opcodes between epilogue and prologue with more than one epilogue. + +// 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: No +// CHECK-NEXT: Fragment: +// CHECK-NEXT: EpilogueScopes: 3 +// CHECK-NEXT: ByteCodeLength: 12 +// 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: ] +// CHECK-NEXT: EpilogueScopes [ +// CHECK-NEXT: EpilogueScope { +// CHECK-NEXT: StartOffset: 6 +// CHECK-NEXT: Condition: 14 +// CHECK-NEXT: EpilogueStartIndex: 6 +// CHECK-NEXT: Opcodes [ +// CHECK-NEXT: 0x08 ; add sp, #(8 * 4) +// CHECK-NEXT: 0xfd ; bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: EpilogueScope { +// CHECK-NEXT: StartOffset: 9 +// CHECK-NEXT: Condition: 14 +// CHECK-NEXT: EpilogueStartIndex: 8 +// CHECK-NEXT: Opcodes [ +// CHECK-NEXT: 0x10 ; add sp, #(16 * 4) +// CHECK-NEXT: 0xfd ; bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: EpilogueScope { +// CHECK-NEXT: StartOffset: 13 +// CHECK-NEXT: Condition: 10 +// CHECK-NEXT: EpilogueStartIndex: 6 +// CHECK-NEXT: Opcodes [ +// CHECK-NEXT: 0x08 ; add sp, #(8 * 4) +// CHECK-NEXT: 0xfd ; bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// 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: No +// CHECK-NEXT: Fragment: +// CHECK-NEXT: EpilogueScopes: 3 +// CHECK-NEXT: ByteCodeLength: 12 +// 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: EpilogueScopes [ +// CHECK-NEXT: EpilogueScope { +// CHECK-NEXT: StartOffset: 6 +// CHECK-NEXT: Condition: 14 +// CHECK-NEXT: EpilogueStartIndex: 2 +// CHECK-NEXT: Opcodes [ +// 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: EpilogueScope { +// CHECK-NEXT: StartOffset: 11 +// CHECK-NEXT: Condition: 14 +// CHECK-NEXT: EpilogueStartIndex: 3 +// CHECK-NEXT: Opcodes [ +// CHECK-NEXT: 0xa0 0xf0 ; pop.w {r4-r7, pc} +// CHECK-NEXT: 0xfe ; b.w +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: EpilogueScope { +// CHECK-NEXT: StartOffset: 15 +// CHECK-NEXT: Condition: 14 +// CHECK-NEXT: EpilogueStartIndex: 6 +// CHECK-NEXT: Opcodes [ +// CHECK-NEXT: 0xa0 0xf0 ; pop.w {r4-r7, pc} +// CHECK-NEXT: 0xfd ; bx +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: } + + .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 + + // Entirely different epilogue; can't be shared with the prologue. + .seh_startepilogue + add sp, sp, #32 + .seh_stackalloc 32 + bx lr + .seh_endepilogue_nop + + nop + + // Also a differing epilogue. + .seh_startepilogue + add sp, sp, #64 + .seh_stackalloc 64 + bx lr + .seh_endepilogue_nop + + nop + + // Epilogue matches the first one; will reuse that epilogue's opcodes, + // even if they differ in conditionality. + itt ge + .seh_startepilogue_cond ge + addge sp, sp, #32 + .seh_stackalloc 32 + bxge lr + .seh_endepilogue_nop + .seh_endproc + + .seh_proc func2 +func2: + 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 + // Ending with a different end opcode, but can still be shared with + // the prolog. + .seh_endepilogue_nop_w + + // Another epilogue, matching the end of the previous epilogue. + .seh_startepilogue + pop.w {r4-r7,lr} + .seh_save_regs_w {r4-r7,lr} + b.w tailcall + .seh_endepilogue_nop_w + + // This epilogue differs in the end opcode, and can't be shared with + // the prologue. + .seh_startepilogue + pop.w {r4-r7,lr} + .seh_save_regs_w {r4-r7,lr} + bx lr + .seh_endepilogue_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,1032 @@ +// 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: sub sp, sp, #32 +// CHECK-NEXT: vpush {d8} +// CHECK-NEXT: push {lr} +// 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: sub sp, sp, #16 +// CHECK-NEXT: vpush {d8-d9} +// CHECK-NEXT: mov r11, sp +// CHECK-NEXT: push {r11, lr} +// 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: sub sp, sp, #16 +// CHECK-NEXT: vpush {d8-d9} +// CHECK-NEXT: mov r11, sp +// CHECK-NEXT: push {r11, lr} +// 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: sub sp, sp, #16 +// CHECK-NEXT: vpush {d8-d14} +// 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: sub sp, sp, #20 +// CHECK-NEXT: add.w r11, sp, #28 +// CHECK-NEXT: push {r4-r11, lr} +// 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: sub sp, sp, #20 +// CHECK-NEXT: push {r4-r11, lr} +// 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: sub sp, sp, #512 +// CHECK-NEXT: push {r4, lr} +// CHECK-NEXT: push {r0-r3} +// 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: mov r11, sp +// CHECK-NEXT: push {r11, lr} +// CHECK-NEXT: push {r0-r3} +// 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: sub sp, sp, #512 +// CHECK-NEXT: push {r4} +// CHECK-NEXT: push {r0-r3} +// 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-r4} +// CHECK-NEXT: push {r0-r3} +// 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-r4} +// CHECK-NEXT: push {r0-r3} +// 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: sub sp, sp, #16 +// CHECK-NEXT: push {r4} +// CHECK-NEXT: push {r0-r3} +// 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: func22 +// CHECK-NEXT: Fragment: Yes +// CHECK-NEXT: FunctionLength: 14 +// 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: sub sp, sp, #512 +// CHECK-NEXT: push {r4, lr} +// CHECK-NEXT: push {r0-r3} +// 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: 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: add.w r11, sp, #24 +// CHECK-NEXT: push {r2-r7, r11, lr} +// 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: sub sp, sp, #8 +// CHECK-NEXT: add.w r11, sp, #16 +// CHECK-NEXT: push {r4-r7, r11, lr} +// 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 {lr} +// CHECK-NEXT: push {r0-r3} +// 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 func22 +func22: + .seh_save_regs {r0-r3} + .seh_save_regs {r4,lr} + .seh_stackalloc_w 512 + .seh_endprologue_fragment + nop + 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 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 diff --git a/llvm/test/MC/ARM/seh.s b/llvm/test/MC/ARM/seh.s --- a/llvm/test/MC/ARM/seh.s +++ b/llvm/test/MC/ARM/seh.s @@ -22,7 +22,7 @@ // CHECK-NEXT: } // CHECK: Section { // CHECK: Name: .xdata -// CHECK: RawDataSize: 120 +// CHECK: RawDataSize: 100 // CHECK: RelocationCount: 1 // CHECK: Characteristics [ // CHECK-NEXT: ALIGN_4BYTES @@ -46,7 +46,7 @@ // CHECK-NEXT: 0x5C IMAGE_REL_ARM_BRANCH24T tailcall // CHECK-NEXT: } // CHECK-NEXT: Section (4) .xdata { -// CHECK-NEXT: 0x38 IMAGE_REL_ARM_ADDR32NB __C_specific_handler +// CHECK-NEXT: 0x34 IMAGE_REL_ARM_ADDR32NB __C_specific_handler // CHECK-NEXT: } // CHECK-NEXT: Section (5) .pdata { // CHECK-NEXT: 0x0 IMAGE_REL_ARM_ADDR32NB .text @@ -68,7 +68,9 @@ // CHECK-NEXT: ExceptionRecord: .xdata // CHECK-NEXT: ExceptionData { // CHECK-NEXT: FunctionLength: 86 +// CHECK: EpiloguePacked: Yes // CHECK: Fragment: No +// CHECK: EpilogueOffset: 31 // CHECK: Prologue [ // CHECK-NEXT: 0xed 0xf8 ; push {r3-r7, lr} // CHECK-NEXT: 0xf6 0x27 ; vpush {d18-d23} @@ -89,22 +91,15 @@ // CHECK-NEXT: 0xe8 0x80 ; sub.w sp, #(128 * 4) // CHECK-NEXT: 0x06 ; sub sp, #(6 * 4) // CHECK-NEXT: ] -// CHECK-NEXT: EpilogueScopes [ -// CHECK-NEXT: EpilogueScope { -// CHECK-NEXT: StartOffset: 31 -// CHECK-NEXT: Condition: 14 -// CHECK-NEXT: EpilogueStartIndex: 31 -// CHECK-NEXT: Opcodes [ -// CHECK-NEXT: 0xfc ; nop.w -// CHECK-NEXT: 0xf7 0x00 0x80 ; add sp, sp, #(128 * 4) -// CHECK-NEXT: 0xfc ; nop.w -// CHECK-NEXT: 0xfc ; nop.w -// CHECK-NEXT: 0xf8 0x01 0x00 0x00 ; add sp, sp, #(65536 * 4) -// CHECK-NEXT: 0x06 ; add sp, #(6 * 4) -// CHECK-NEXT: 0xef 0x04 ; ldr.w lr, [sp], #16 -// CHECK-NEXT: 0xfd ; bx -// CHECK-NEXT: ] -// CHECK-NEXT: } +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0xfc ; nop.w +// CHECK-NEXT: 0xf7 0x00 0x80 ; add sp, sp, #(128 * 4) +// CHECK-NEXT: 0xfc ; nop.w +// CHECK-NEXT: 0xfc ; nop.w +// CHECK-NEXT: 0xf8 0x01 0x00 0x00 ; add sp, sp, #(65536 * 4) +// CHECK-NEXT: 0x06 ; add sp, #(6 * 4) +// CHECK-NEXT: 0xef 0x04 ; ldr.w lr, [sp], #16 +// CHECK-NEXT: 0xfd ; bx // CHECK-NEXT: ] // CHECK-NEXT: ExceptionHandler [ // CHECK-NEXT: Routine: __C_specific_handler @@ -117,31 +112,21 @@ // CHECK: Prologue [ // CHECK-NEXT: 0xd3 ; push {r4-r7} // CHECK-NEXT: ] -// CHECK-NEXT: EpilogueScopes [ -// CHECK-NEXT: EpilogueScope { -// CHECK: Opcodes [ -// CHECK-NEXT: 0xd2 ; pop {r4-r6} -// CHECK-NEXT: 0xfe ; b.w -// CHECK-NEXT: ] -// CHECK-NEXT: } +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0xd2 ; pop {r4-r6} +// CHECK-NEXT: 0xfe ; b.w // CHECK-NEXT: ] // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: RuntimeFunction { // CHECK-NEXT: Function: func3 // CHECK: FunctionLength: 8 +// CHECK: EpilogueOffset: 2 // CHECK: Prologue [ // CHECK-NEXT: 0xd5 ; push {r4-r5, lr} // CHECK-NEXT: ] -// CHECK-NEXT: EpilogueScopes [ -// CHECK-NEXT: EpilogueScope { -// CHECK-NEXT: StartOffset: 3 -// CHECK-NEXT: Condition: 14 -// CHECK-NEXT: EpilogueStartIndex: 2 -// CHECK-NEXT: Opcodes [ -// CHECK-NEXT: 0xd6 ; pop {r4-r6, pc} -// CHECK-NEXT: ] -// CHECK-NEXT: } +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0xd6 ; pop {r4-r6, pc} // CHECK-NEXT: ] // CHECK-NEXT: } // CHECK-NEXT: } @@ -154,16 +139,9 @@ // CHECK-NEXT: 0x10 ; sub sp, #(16 * 4) // CHECK-NEXT: 0xd5 ; push {r4-r5, lr} // CHECK-NEXT: ] -// CHECK-NEXT: EpilogueScopes [ -// CHECK-NEXT: EpilogueScope { -// CHECK-NEXT: StartOffset: 1 -// CHECK-NEXT: Condition: 14 -// CHECK-NEXT: EpilogueStartIndex: 4 -// CHECK-NEXT: Opcodes [ -// CHECK-NEXT: 0x10 ; add sp, #(16 * 4) -// CHECK-NEXT: 0xd5 ; pop {r4-r5, pc} -// CHECK-NEXT: ] -// CHECK-NEXT: } +// CHECK-NEXT: Epilogue [ +// CHECK-NEXT: 0x10 ; add sp, #(16 * 4) +// CHECK-NEXT: 0xd5 ; pop {r4-r5, pc} // CHECK-NEXT: ] // CHECK-NEXT: } // CHECK-NEXT: } @@ -177,7 +155,7 @@ // CHECK-NEXT: EpilogueScope { // CHECK-NEXT: StartOffset: 3 // CHECK-NEXT: Condition: 10 -// CHECK-NEXT: EpilogueStartIndex: 2 +// CHECK-NEXT: EpilogueStartIndex: 0 // CHECK-NEXT: Opcodes [ // CHECK-NEXT: 0xd5 ; pop {r4-r5, pc} // CHECK-NEXT: ]