[llvm] d4022ff - [MC] [Win64EH] Optimize the ARM unwind info
Martin Storsjö via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 1 01:26:38 PDT 2022
Author: Martin Storsjö
Date: 2022-06-01T11:25:49+03:00
New Revision: d4022ff3310635f682e9937c8ffa4aeb0d2e2854
URL: https://github.com/llvm/llvm-project/commit/d4022ff3310635f682e9937c8ffa4aeb0d2e2854
DIFF: https://github.com/llvm/llvm-project/commit/d4022ff3310635f682e9937c8ffa4aeb0d2e2854.diff
LOG: [MC] [Win64EH] Optimize the ARM unwind info
Use the packed unwind info format if possible; otherwise try to
create a packed epilog.
Differential Revision: https://reviews.llvm.org/D125646
Added:
llvm/test/MC/ARM/seh-epilog-packing.s
llvm/test/MC/ARM/seh-epilog-sharing.s
llvm/test/MC/ARM/seh-packed.s
Modified:
llvm/lib/MC/MCWin64EH.cpp
llvm/test/MC/ARM/seh.s
Removed:
################################################################################
diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp
index df3556e6c22f8..67b3d12729427 100644
--- a/llvm/lib/MC/MCWin64EH.cpp
+++ b/llvm/lib/MC/MCWin64EH.cpp
@@ -565,8 +565,8 @@ FindMatchingEpilog(const std::vector<WinEH::Instruction>& EpilogInstrs,
return nullptr;
}
-static void simplifyOpcodes(std::vector<WinEH::Instruction> &Instructions,
- bool Reverse) {
+static void simplifyARM64Opcodes(std::vector<WinEH::Instruction> &Instructions,
+ bool Reverse) {
unsigned PrevOffset = -1;
unsigned PrevRegister = -1;
@@ -628,8 +628,9 @@ static void simplifyOpcodes(std::vector<WinEH::Instruction> &Instructions,
}
// Check if an epilog exists as a subset of the end of a prolog (backwards).
-static int getOffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
- const std::vector<WinEH::Instruction> &Epilog) {
+static int
+getARM64OffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
+ const std::vector<WinEH::Instruction> &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 @@ static int getOffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
&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 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
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 @@ static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
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 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
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 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
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 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
// 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 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
// 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 @@ static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
return Count;
}
+static uint32_t ARMCountOfInstructionBytes(ArrayRef<WinEH::Instruction> Insns) {
+ uint32_t Count = 0;
+ for (const auto &I : Insns) {
+ switch (static_cast<Win64EH::UnwindOpcodes>(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,545 @@ static void ARMEmitUnwindCode(MCStreamer &streamer,
}
}
+// Check if an epilog exists as a subset of the end of a prolog (backwards).
+// An epilog may end with one out of three
diff erent end opcodes; if this
+// is the first epilog that shares opcodes with the prolog, we can tolerate
+// that this opcode
diff ers (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<WinEH::Instruction> &Prolog,
+ const std::vector<WinEH::Instruction> &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<WinEH::Instruction>(
+ &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<WinEH::Instruction> &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<int64_t> 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;
+
+ // Packed uneind info can't express multiple epilogues.
+ if (info->EpilogMap.size() > 1)
+ return false;
+
+ unsigned EF = 0;
+ int Ret = 0;
+ if (info->EpilogMap.size() == 0) {
+ Ret = 3; // No epilogue
+ } else {
+ // 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.
+ const WinEH::FrameInfo::Epilog &EpilogInfo =
+ info->EpilogMap.begin()->second;
+ if (EpilogInfo.Condition != 0xe) // ARMCC::AL
+ return false;
+ const std::vector<WinEH::Instruction> &Epilog = EpilogInfo.Instructions;
+ Optional<int64_t> 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;
+
+ 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:
+ case Win64EH::UOP_Nop:
+ case Win64EH::UOP_WideNop:
+ // 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 +1987,29 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
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<MCSymbol *, uint32_t> EpilogInfo;
// Epilogs processed so far.
std::vector<MCSymbol *> AddedEpilogs;
+ bool CanTweakProlog = true;
for (auto &I : info->EpilogMap) {
MCSymbol *EpilogStart = I.first;
auto &EpilogInstrs = I.second.Instructions;
@@ -1402,6 +2017,7 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
MCSymbol *MatchingEpilog =
FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
+ int PrologOffset;
if (MatchingEpilog) {
assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() &&
"Duplicate epilog not found");
@@ -1409,6 +2025,19 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
// 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 +2051,8 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
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 +2060,8 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
}
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 +2084,36 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
streamer.emitInt32(row2);
}
- // Epilog Start Index, Epilog Start Offset
- for (auto &I : EpilogInfo) {
- MCSymbol *EpilogStart = I.first;
- uint32_t EpilogIndex = I.second;
-
- Optional<int64_t> 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<int64_t> 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
index 0000000000000..4d4b77b7788e2
--- /dev/null
+++ b/llvm/test/MC/ARM/seh-epilog-packing.s
@@ -0,0 +1,205 @@
+// 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 <target>
+// 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 <target>
+// 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 <reg>
+// 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 <reg>
+// 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_nop_w
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ // 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
index 0000000000000..d96113916d217
--- /dev/null
+++ b/llvm/test/MC/ARM/seh-epilog-sharing.s
@@ -0,0 +1,189 @@
+// 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 <reg>
+// 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 <reg>
+// 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 <reg>
+// 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 <target>
+// 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 <target>
+// 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 <target>
+// 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 <reg>
+// 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
diff erent epilogue; can't be shared with the prologue.
+ .seh_startepilogue
+ add sp, sp, #32
+ .seh_stackalloc 32
+ bx lr
+ .seh_nop
+ .seh_endepilogue
+
+ nop
+
+ // Also a
diff ering epilogue.
+ .seh_startepilogue
+ add sp, sp, #64
+ .seh_stackalloc 64
+ bx lr
+ .seh_nop
+ .seh_endepilogue
+
+ nop
+
+ // Epilogue matches the first one; will reuse that epilogue's opcodes,
+ // even if they
diff er in conditionality.
+ itt ge
+ .seh_startepilogue_cond ge
+ addge sp, sp, #32
+ .seh_stackalloc 32
+ bxge lr
+ .seh_nop
+ .seh_endepilogue
+ .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
diff erent end opcode, but can still be shared with
+ // the prolog.
+ .seh_nop_w
+ .seh_endepilogue
+
+ // 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_nop_w
+ .seh_endepilogue
+
+ // This epilogue
diff ers 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_nop
+ .seh_endepilogue
+ .seh_endproc
diff --git a/llvm/test/MC/ARM/seh-packed.s b/llvm/test/MC/ARM/seh-packed.s
new file mode 100644
index 0000000000000..e56abfbe66c01
--- /dev/null
+++ b/llvm/test/MC/ARM/seh-packed.s
@@ -0,0 +1,1095 @@
+// 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 <reg>
+// 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 <reg>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func7
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 8
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func8
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 10
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func9
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 24
+// CHECK-NEXT: ReturnType: b.w <target>
+// 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 <target>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func10
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 26
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// 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 <target>
+// 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 <target>
+// 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 <target>
+// 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 <target>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func17
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 20
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// 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 <reg>
+// 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 <reg>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func20
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 14
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func21
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 14
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// 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 <reg>
+// 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 <reg>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func27
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 8
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func28
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 8
+// CHECK-NEXT: ReturnType: bx <reg>
+// 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 <reg>
+// 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-NEXT: RuntimeFunction {
+// CHECK-NEXT: Function: func31
+// CHECK-NEXT: Fragment: No
+// CHECK-NEXT: FunctionLength: 4
+// CHECK-NEXT: ReturnType: (no epilogue)
+// 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: }
+
+// 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:
+// CHECK: Function: notpacked9
+// CHECK-NEXT: ExceptionRecord:
+
+ .text
+ .syntax unified
+
+ .seh_proc func6
+func6:
+ .seh_endprologue
+ nop
+ nop
+ nop
+ .seh_startepilogue
+ bx lr
+ .seh_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop_w
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop_w
+ .seh_endepilogue
+ .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_nop_w
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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 func31
+func31:
+ push {r4-r6,lr}
+ .seh_save_regs {r4-r6,lr}
+ .seh_endprologue
+ nop
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .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_nop
+ .seh_endepilogue
+ .seh_endproc
+
+ .seh_proc notpacked8
+notpacked8:
+ push {r4-r7,lr}
+ .seh_save_regs {r4-r7,lr}
+ .seh_endprologue
+ nop
+ .seh_startepilogue
+ pop {r4-r7,pc}
+ .seh_save_regs {r4-r7,pc}
+ .seh_endepilogue
+ // Epilogue isn't at the end of the function; can't be packed.
+ nop
+ .seh_endproc
+
+ .seh_proc notpacked9
+notpacked9:
+ push {r4-r7,lr}
+ .seh_save_regs {r4-r7,lr}
+ .seh_endprologue
+ nop
+ .seh_startepilogue
+ pop {r4-r7,pc}
+ .seh_save_regs {r4-r7,pc}
+ .seh_endepilogue
+ // Multiple epilogues, can't be packed
+ nop
+ .seh_startepilogue
+ pop {r4-r7,pc}
+ .seh_save_regs {r4-r7,pc}
+ .seh_endepilogue
+ .seh_endproc
diff --git a/llvm/test/MC/ARM/seh.s b/llvm/test/MC/ARM/seh.s
index 1b92c0d14e3b3..6cc4cf8ac076a 100644
--- 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 <reg>
-// 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 <reg>
// 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 <target>
-// CHECK-NEXT: ]
-// CHECK-NEXT: }
+// CHECK-NEXT: Epilogue [
+// CHECK-NEXT: 0xd2 ; pop {r4-r6}
+// CHECK-NEXT: 0xfe ; b.w <target>
// 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: ]
More information about the llvm-commits
mailing list