xref: /freebsd/contrib/llvm-project/llvm/lib/MC/MCWin64EH.cpp (revision bdb86d1a853a919764f65fdedcea76d76e4d619b)
10b57cec5SDimitry Andric //===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "llvm/MC/MCWin64EH.h"
100b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
110b57cec5SDimitry Andric #include "llvm/MC/MCContext.h"
120b57cec5SDimitry Andric #include "llvm/MC/MCExpr.h"
130b57cec5SDimitry Andric #include "llvm/MC/MCObjectStreamer.h"
140b57cec5SDimitry Andric #include "llvm/MC/MCStreamer.h"
150b57cec5SDimitry Andric #include "llvm/MC/MCSymbol.h"
160b57cec5SDimitry Andric #include "llvm/Support/Win64EH.h"
1781ad6265SDimitry Andric namespace llvm {
1881ad6265SDimitry Andric class MCSection;
1981ad6265SDimitry Andric }
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric using namespace llvm;
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric // NOTE: All relocations generated here are 4-byte image-relative.
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric static uint8_t CountOfUnwindCodes(std::vector<WinEH::Instruction> &Insns) {
260b57cec5SDimitry Andric   uint8_t Count = 0;
270b57cec5SDimitry Andric   for (const auto &I : Insns) {
280b57cec5SDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
290b57cec5SDimitry Andric     default:
300b57cec5SDimitry Andric       llvm_unreachable("Unsupported unwind code");
310b57cec5SDimitry Andric     case Win64EH::UOP_PushNonVol:
320b57cec5SDimitry Andric     case Win64EH::UOP_AllocSmall:
330b57cec5SDimitry Andric     case Win64EH::UOP_SetFPReg:
340b57cec5SDimitry Andric     case Win64EH::UOP_PushMachFrame:
350b57cec5SDimitry Andric       Count += 1;
360b57cec5SDimitry Andric       break;
370b57cec5SDimitry Andric     case Win64EH::UOP_SaveNonVol:
380b57cec5SDimitry Andric     case Win64EH::UOP_SaveXMM128:
390b57cec5SDimitry Andric       Count += 2;
400b57cec5SDimitry Andric       break;
410b57cec5SDimitry Andric     case Win64EH::UOP_SaveNonVolBig:
420b57cec5SDimitry Andric     case Win64EH::UOP_SaveXMM128Big:
430b57cec5SDimitry Andric       Count += 3;
440b57cec5SDimitry Andric       break;
450b57cec5SDimitry Andric     case Win64EH::UOP_AllocLarge:
460b57cec5SDimitry Andric       Count += (I.Offset > 512 * 1024 - 8) ? 3 : 2;
470b57cec5SDimitry Andric       break;
480b57cec5SDimitry Andric     }
490b57cec5SDimitry Andric   }
500b57cec5SDimitry Andric   return Count;
510b57cec5SDimitry Andric }
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric static void EmitAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
540b57cec5SDimitry Andric                               const MCSymbol *RHS) {
550b57cec5SDimitry Andric   MCContext &Context = Streamer.getContext();
560b57cec5SDimitry Andric   const MCExpr *Diff =
570b57cec5SDimitry Andric       MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context),
580b57cec5SDimitry Andric                               MCSymbolRefExpr::create(RHS, Context), Context);
595ffd83dbSDimitry Andric   Streamer.emitValue(Diff, 1);
600b57cec5SDimitry Andric }
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric static void EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
630b57cec5SDimitry Andric                            WinEH::Instruction &inst) {
640b57cec5SDimitry Andric   uint8_t b2;
650b57cec5SDimitry Andric   uint16_t w;
660b57cec5SDimitry Andric   b2 = (inst.Operation & 0x0F);
670b57cec5SDimitry Andric   switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
680b57cec5SDimitry Andric   default:
690b57cec5SDimitry Andric     llvm_unreachable("Unsupported unwind code");
700b57cec5SDimitry Andric   case Win64EH::UOP_PushNonVol:
710b57cec5SDimitry Andric     EmitAbsDifference(streamer, inst.Label, begin);
720b57cec5SDimitry Andric     b2 |= (inst.Register & 0x0F) << 4;
735ffd83dbSDimitry Andric     streamer.emitInt8(b2);
740b57cec5SDimitry Andric     break;
750b57cec5SDimitry Andric   case Win64EH::UOP_AllocLarge:
760b57cec5SDimitry Andric     EmitAbsDifference(streamer, inst.Label, begin);
770b57cec5SDimitry Andric     if (inst.Offset > 512 * 1024 - 8) {
780b57cec5SDimitry Andric       b2 |= 0x10;
795ffd83dbSDimitry Andric       streamer.emitInt8(b2);
800b57cec5SDimitry Andric       w = inst.Offset & 0xFFF8;
815ffd83dbSDimitry Andric       streamer.emitInt16(w);
820b57cec5SDimitry Andric       w = inst.Offset >> 16;
830b57cec5SDimitry Andric     } else {
845ffd83dbSDimitry Andric       streamer.emitInt8(b2);
850b57cec5SDimitry Andric       w = inst.Offset >> 3;
860b57cec5SDimitry Andric     }
875ffd83dbSDimitry Andric     streamer.emitInt16(w);
880b57cec5SDimitry Andric     break;
890b57cec5SDimitry Andric   case Win64EH::UOP_AllocSmall:
900b57cec5SDimitry Andric     b2 |= (((inst.Offset - 8) >> 3) & 0x0F) << 4;
910b57cec5SDimitry Andric     EmitAbsDifference(streamer, inst.Label, begin);
925ffd83dbSDimitry Andric     streamer.emitInt8(b2);
930b57cec5SDimitry Andric     break;
940b57cec5SDimitry Andric   case Win64EH::UOP_SetFPReg:
950b57cec5SDimitry Andric     EmitAbsDifference(streamer, inst.Label, begin);
965ffd83dbSDimitry Andric     streamer.emitInt8(b2);
970b57cec5SDimitry Andric     break;
980b57cec5SDimitry Andric   case Win64EH::UOP_SaveNonVol:
990b57cec5SDimitry Andric   case Win64EH::UOP_SaveXMM128:
1000b57cec5SDimitry Andric     b2 |= (inst.Register & 0x0F) << 4;
1010b57cec5SDimitry Andric     EmitAbsDifference(streamer, inst.Label, begin);
1025ffd83dbSDimitry Andric     streamer.emitInt8(b2);
1030b57cec5SDimitry Andric     w = inst.Offset >> 3;
1040b57cec5SDimitry Andric     if (inst.Operation == Win64EH::UOP_SaveXMM128)
1050b57cec5SDimitry Andric       w >>= 1;
1065ffd83dbSDimitry Andric     streamer.emitInt16(w);
1070b57cec5SDimitry Andric     break;
1080b57cec5SDimitry Andric   case Win64EH::UOP_SaveNonVolBig:
1090b57cec5SDimitry Andric   case Win64EH::UOP_SaveXMM128Big:
1100b57cec5SDimitry Andric     b2 |= (inst.Register & 0x0F) << 4;
1110b57cec5SDimitry Andric     EmitAbsDifference(streamer, inst.Label, begin);
1125ffd83dbSDimitry Andric     streamer.emitInt8(b2);
1130b57cec5SDimitry Andric     if (inst.Operation == Win64EH::UOP_SaveXMM128Big)
1140b57cec5SDimitry Andric       w = inst.Offset & 0xFFF0;
1150b57cec5SDimitry Andric     else
1160b57cec5SDimitry Andric       w = inst.Offset & 0xFFF8;
1175ffd83dbSDimitry Andric     streamer.emitInt16(w);
1180b57cec5SDimitry Andric     w = inst.Offset >> 16;
1195ffd83dbSDimitry Andric     streamer.emitInt16(w);
1200b57cec5SDimitry Andric     break;
1210b57cec5SDimitry Andric   case Win64EH::UOP_PushMachFrame:
1220b57cec5SDimitry Andric     if (inst.Offset == 1)
1230b57cec5SDimitry Andric       b2 |= 0x10;
1240b57cec5SDimitry Andric     EmitAbsDifference(streamer, inst.Label, begin);
1255ffd83dbSDimitry Andric     streamer.emitInt8(b2);
1260b57cec5SDimitry Andric     break;
1270b57cec5SDimitry Andric   }
1280b57cec5SDimitry Andric }
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric static void EmitSymbolRefWithOfs(MCStreamer &streamer,
1310b57cec5SDimitry Andric                                  const MCSymbol *Base,
132bdd1243dSDimitry Andric                                  int64_t Offset) {
133bdd1243dSDimitry Andric   MCContext &Context = streamer.getContext();
134bdd1243dSDimitry Andric   const MCConstantExpr *OffExpr = MCConstantExpr::create(Offset, Context);
135bdd1243dSDimitry Andric   const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Base,
136bdd1243dSDimitry Andric                                               MCSymbolRefExpr::VK_COFF_IMGREL32,
137bdd1243dSDimitry Andric                                               Context);
138bdd1243dSDimitry Andric   streamer.emitValue(MCBinaryExpr::createAdd(BaseRefRel, OffExpr, Context), 4);
139bdd1243dSDimitry Andric }
140bdd1243dSDimitry Andric 
141bdd1243dSDimitry Andric static void EmitSymbolRefWithOfs(MCStreamer &streamer,
142bdd1243dSDimitry Andric                                  const MCSymbol *Base,
1430b57cec5SDimitry Andric                                  const MCSymbol *Other) {
1440b57cec5SDimitry Andric   MCContext &Context = streamer.getContext();
1450b57cec5SDimitry Andric   const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::create(Base, Context);
1460b57cec5SDimitry Andric   const MCSymbolRefExpr *OtherRef = MCSymbolRefExpr::create(Other, Context);
1470b57cec5SDimitry Andric   const MCExpr *Ofs = MCBinaryExpr::createSub(OtherRef, BaseRef, Context);
1480b57cec5SDimitry Andric   const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Base,
1490b57cec5SDimitry Andric                                               MCSymbolRefExpr::VK_COFF_IMGREL32,
1500b57cec5SDimitry Andric                                               Context);
1515ffd83dbSDimitry Andric   streamer.emitValue(MCBinaryExpr::createAdd(BaseRefRel, Ofs, Context), 4);
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric static void EmitRuntimeFunction(MCStreamer &streamer,
1550b57cec5SDimitry Andric                                 const WinEH::FrameInfo *info) {
1560b57cec5SDimitry Andric   MCContext &context = streamer.getContext();
1570b57cec5SDimitry Andric 
158bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
159349cc55cSDimitry Andric   EmitSymbolRefWithOfs(streamer, info->Begin, info->Begin);
160349cc55cSDimitry Andric   EmitSymbolRefWithOfs(streamer, info->Begin, info->End);
1615ffd83dbSDimitry Andric   streamer.emitValue(MCSymbolRefExpr::create(info->Symbol,
1620b57cec5SDimitry Andric                                              MCSymbolRefExpr::VK_COFF_IMGREL32,
1630b57cec5SDimitry Andric                                              context), 4);
1640b57cec5SDimitry Andric }
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
1670b57cec5SDimitry Andric   // If this UNWIND_INFO already has a symbol, it's already been emitted.
1680b57cec5SDimitry Andric   if (info->Symbol)
1690b57cec5SDimitry Andric     return;
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric   MCContext &context = streamer.getContext();
1720b57cec5SDimitry Andric   MCSymbol *Label = context.createTempSymbol();
1730b57cec5SDimitry Andric 
174bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
1755ffd83dbSDimitry Andric   streamer.emitLabel(Label);
1760b57cec5SDimitry Andric   info->Symbol = Label;
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric   // Upper 3 bits are the version number (currently 1).
1790b57cec5SDimitry Andric   uint8_t flags = 0x01;
1800b57cec5SDimitry Andric   if (info->ChainedParent)
1810b57cec5SDimitry Andric     flags |= Win64EH::UNW_ChainInfo << 3;
1820b57cec5SDimitry Andric   else {
1830b57cec5SDimitry Andric     if (info->HandlesUnwind)
1840b57cec5SDimitry Andric       flags |= Win64EH::UNW_TerminateHandler << 3;
1850b57cec5SDimitry Andric     if (info->HandlesExceptions)
1860b57cec5SDimitry Andric       flags |= Win64EH::UNW_ExceptionHandler << 3;
1870b57cec5SDimitry Andric   }
1885ffd83dbSDimitry Andric   streamer.emitInt8(flags);
1890b57cec5SDimitry Andric 
1900b57cec5SDimitry Andric   if (info->PrologEnd)
1910b57cec5SDimitry Andric     EmitAbsDifference(streamer, info->PrologEnd, info->Begin);
1920b57cec5SDimitry Andric   else
1935ffd83dbSDimitry Andric     streamer.emitInt8(0);
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric   uint8_t numCodes = CountOfUnwindCodes(info->Instructions);
1965ffd83dbSDimitry Andric   streamer.emitInt8(numCodes);
1970b57cec5SDimitry Andric 
1980b57cec5SDimitry Andric   uint8_t frame = 0;
1990b57cec5SDimitry Andric   if (info->LastFrameInst >= 0) {
2000b57cec5SDimitry Andric     WinEH::Instruction &frameInst = info->Instructions[info->LastFrameInst];
2010b57cec5SDimitry Andric     assert(frameInst.Operation == Win64EH::UOP_SetFPReg);
2020b57cec5SDimitry Andric     frame = (frameInst.Register & 0x0F) | (frameInst.Offset & 0xF0);
2030b57cec5SDimitry Andric   }
2045ffd83dbSDimitry Andric   streamer.emitInt8(frame);
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric   // Emit unwind instructions (in reverse order).
2070b57cec5SDimitry Andric   uint8_t numInst = info->Instructions.size();
2080b57cec5SDimitry Andric   for (uint8_t c = 0; c < numInst; ++c) {
2090b57cec5SDimitry Andric     WinEH::Instruction inst = info->Instructions.back();
2100b57cec5SDimitry Andric     info->Instructions.pop_back();
2110b57cec5SDimitry Andric     EmitUnwindCode(streamer, info->Begin, inst);
2120b57cec5SDimitry Andric   }
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric   // For alignment purposes, the instruction array will always have an even
2150b57cec5SDimitry Andric   // number of entries, with the final entry potentially unused (in which case
2160b57cec5SDimitry Andric   // the array will be one longer than indicated by the count of unwind codes
2170b57cec5SDimitry Andric   // field).
2180b57cec5SDimitry Andric   if (numCodes & 1) {
2195ffd83dbSDimitry Andric     streamer.emitInt16(0);
2200b57cec5SDimitry Andric   }
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric   if (flags & (Win64EH::UNW_ChainInfo << 3))
2230b57cec5SDimitry Andric     EmitRuntimeFunction(streamer, info->ChainedParent);
2240b57cec5SDimitry Andric   else if (flags &
2250b57cec5SDimitry Andric            ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3))
2265ffd83dbSDimitry Andric     streamer.emitValue(MCSymbolRefExpr::create(info->ExceptionHandler,
2270b57cec5SDimitry Andric                                               MCSymbolRefExpr::VK_COFF_IMGREL32,
2280b57cec5SDimitry Andric                                               context), 4);
2290b57cec5SDimitry Andric   else if (numCodes == 0) {
2300b57cec5SDimitry Andric     // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not
2310b57cec5SDimitry Andric     // a chained unwind info, if there is no handler, and if there are fewer
2320b57cec5SDimitry Andric     // than 2 slots used in the unwind code array, we have to pad to 8 bytes.
2335ffd83dbSDimitry Andric     streamer.emitInt32(0);
2340b57cec5SDimitry Andric   }
2350b57cec5SDimitry Andric }
2360b57cec5SDimitry Andric 
2370b57cec5SDimitry Andric void llvm::Win64EH::UnwindEmitter::Emit(MCStreamer &Streamer) const {
2380b57cec5SDimitry Andric   // Emit the unwind info structs first.
2390b57cec5SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
2400b57cec5SDimitry Andric     MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
24181ad6265SDimitry Andric     Streamer.switchSection(XData);
2420b57cec5SDimitry Andric     ::EmitUnwindInfo(Streamer, CFI.get());
2430b57cec5SDimitry Andric   }
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric   // Now emit RUNTIME_FUNCTION entries.
2460b57cec5SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
2470b57cec5SDimitry Andric     MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
24881ad6265SDimitry Andric     Streamer.switchSection(PData);
2490b57cec5SDimitry Andric     EmitRuntimeFunction(Streamer, CFI.get());
2500b57cec5SDimitry Andric   }
2510b57cec5SDimitry Andric }
2520b57cec5SDimitry Andric 
253e8d8bef9SDimitry Andric void llvm::Win64EH::UnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
254e8d8bef9SDimitry Andric                                                   WinEH::FrameInfo *info,
255e8d8bef9SDimitry Andric                                                   bool HandlerData) const {
2560b57cec5SDimitry Andric   // Switch sections (the static function above is meant to be called from
2570b57cec5SDimitry Andric   // here and from Emit().
2580b57cec5SDimitry Andric   MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection);
25981ad6265SDimitry Andric   Streamer.switchSection(XData);
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric   ::EmitUnwindInfo(Streamer, info);
2620b57cec5SDimitry Andric }
2630b57cec5SDimitry Andric 
26481ad6265SDimitry Andric static const MCExpr *GetSubDivExpr(MCStreamer &Streamer, const MCSymbol *LHS,
26581ad6265SDimitry Andric                                    const MCSymbol *RHS, int Div) {
26681ad6265SDimitry Andric   MCContext &Context = Streamer.getContext();
26781ad6265SDimitry Andric   const MCExpr *Expr =
26881ad6265SDimitry Andric       MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context),
26981ad6265SDimitry Andric                               MCSymbolRefExpr::create(RHS, Context), Context);
27081ad6265SDimitry Andric   if (Div != 1)
27181ad6265SDimitry Andric     Expr = MCBinaryExpr::createDiv(Expr, MCConstantExpr::create(Div, Context),
27281ad6265SDimitry Andric                                    Context);
27381ad6265SDimitry Andric   return Expr;
27481ad6265SDimitry Andric }
27581ad6265SDimitry Andric 
276bdd1243dSDimitry Andric static std::optional<int64_t> GetOptionalAbsDifference(MCStreamer &Streamer,
27781ad6265SDimitry Andric                                                        const MCSymbol *LHS,
2780b57cec5SDimitry Andric                                                        const MCSymbol *RHS) {
2790b57cec5SDimitry Andric   MCContext &Context = Streamer.getContext();
2800b57cec5SDimitry Andric   const MCExpr *Diff =
2810b57cec5SDimitry Andric       MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context),
2820b57cec5SDimitry Andric                               MCSymbolRefExpr::create(RHS, Context), Context);
2830b57cec5SDimitry Andric   MCObjectStreamer *OS = (MCObjectStreamer *)(&Streamer);
2840b57cec5SDimitry Andric   // It should normally be possible to calculate the length of a function
2850b57cec5SDimitry Andric   // at this point, but it might not be possible in the presence of certain
2860b57cec5SDimitry Andric   // unusual constructs, like an inline asm with an alignment directive.
2870b57cec5SDimitry Andric   int64_t value;
2880b57cec5SDimitry Andric   if (!Diff->evaluateAsAbsolute(value, OS->getAssembler()))
289bdd1243dSDimitry Andric     return std::nullopt;
2900b57cec5SDimitry Andric   return value;
2910b57cec5SDimitry Andric }
2920b57cec5SDimitry Andric 
29381ad6265SDimitry Andric static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
29481ad6265SDimitry Andric                                 const MCSymbol *RHS) {
295bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDiff =
296bdd1243dSDimitry Andric       GetOptionalAbsDifference(Streamer, LHS, RHS);
29781ad6265SDimitry Andric   if (!MaybeDiff)
29881ad6265SDimitry Andric     report_fatal_error("Failed to evaluate function length in SEH unwind info");
29981ad6265SDimitry Andric   return *MaybeDiff;
30081ad6265SDimitry Andric }
30181ad6265SDimitry Andric 
302bdd1243dSDimitry Andric static void checkARM64Instructions(MCStreamer &Streamer,
303bdd1243dSDimitry Andric                                    ArrayRef<WinEH::Instruction> Insns,
304bdd1243dSDimitry Andric                                    const MCSymbol *Begin, const MCSymbol *End,
305bdd1243dSDimitry Andric                                    StringRef Name, StringRef Type) {
306bdd1243dSDimitry Andric   if (!End)
307bdd1243dSDimitry Andric     return;
308bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDistance =
309bdd1243dSDimitry Andric       GetOptionalAbsDifference(Streamer, End, Begin);
310bdd1243dSDimitry Andric   if (!MaybeDistance)
311bdd1243dSDimitry Andric     return;
312bdd1243dSDimitry Andric   uint32_t Distance = (uint32_t)*MaybeDistance;
313bdd1243dSDimitry Andric 
314bdd1243dSDimitry Andric   for (const auto &I : Insns) {
315bdd1243dSDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
316bdd1243dSDimitry Andric     default:
317bdd1243dSDimitry Andric       break;
318bdd1243dSDimitry Andric     case Win64EH::UOP_TrapFrame:
319bdd1243dSDimitry Andric     case Win64EH::UOP_PushMachFrame:
320bdd1243dSDimitry Andric     case Win64EH::UOP_Context:
321bdd1243dSDimitry Andric     case Win64EH::UOP_ClearUnwoundToCall:
322bdd1243dSDimitry Andric       // Can't reason about these opcodes and how they map to actual
323bdd1243dSDimitry Andric       // instructions.
324bdd1243dSDimitry Andric       return;
325bdd1243dSDimitry Andric     }
326bdd1243dSDimitry Andric   }
327bdd1243dSDimitry Andric   // Exclude the end opcode which doesn't map to an instruction.
328bdd1243dSDimitry Andric   uint32_t InstructionBytes = 4 * (Insns.size() - 1);
329bdd1243dSDimitry Andric   if (Distance != InstructionBytes) {
330bdd1243dSDimitry Andric     Streamer.getContext().reportError(
331bdd1243dSDimitry Andric         SMLoc(), "Incorrect size for " + Name + " " + Type + ": " +
332bdd1243dSDimitry Andric                      Twine(Distance) +
333bdd1243dSDimitry Andric                      " bytes of instructions in range, but .seh directives "
334bdd1243dSDimitry Andric                      "corresponding to " +
335bdd1243dSDimitry Andric                      Twine(InstructionBytes) + " bytes\n");
336bdd1243dSDimitry Andric   }
337bdd1243dSDimitry Andric }
338bdd1243dSDimitry Andric 
339e8d8bef9SDimitry Andric static uint32_t ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
3400b57cec5SDimitry Andric   uint32_t Count = 0;
3410b57cec5SDimitry Andric   for (const auto &I : Insns) {
3420b57cec5SDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
3430b57cec5SDimitry Andric     default:
3440b57cec5SDimitry Andric       llvm_unreachable("Unsupported ARM64 unwind code");
3450b57cec5SDimitry Andric     case Win64EH::UOP_AllocSmall:
3460b57cec5SDimitry Andric       Count += 1;
3470b57cec5SDimitry Andric       break;
3480b57cec5SDimitry Andric     case Win64EH::UOP_AllocMedium:
3490b57cec5SDimitry Andric       Count += 2;
3500b57cec5SDimitry Andric       break;
3510b57cec5SDimitry Andric     case Win64EH::UOP_AllocLarge:
3520b57cec5SDimitry Andric       Count += 4;
3530b57cec5SDimitry Andric       break;
354e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveR19R20X:
355e8d8bef9SDimitry Andric       Count += 1;
356e8d8bef9SDimitry Andric       break;
3570b57cec5SDimitry Andric     case Win64EH::UOP_SaveFPLRX:
3580b57cec5SDimitry Andric       Count += 1;
3590b57cec5SDimitry Andric       break;
3600b57cec5SDimitry Andric     case Win64EH::UOP_SaveFPLR:
3610b57cec5SDimitry Andric       Count += 1;
3620b57cec5SDimitry Andric       break;
3630b57cec5SDimitry Andric     case Win64EH::UOP_SaveReg:
3640b57cec5SDimitry Andric       Count += 2;
3650b57cec5SDimitry Andric       break;
3660b57cec5SDimitry Andric     case Win64EH::UOP_SaveRegP:
3670b57cec5SDimitry Andric       Count += 2;
3680b57cec5SDimitry Andric       break;
3690b57cec5SDimitry Andric     case Win64EH::UOP_SaveRegPX:
3700b57cec5SDimitry Andric       Count += 2;
3710b57cec5SDimitry Andric       break;
3720b57cec5SDimitry Andric     case Win64EH::UOP_SaveRegX:
3730b57cec5SDimitry Andric       Count += 2;
3740b57cec5SDimitry Andric       break;
375e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveLRPair:
376e8d8bef9SDimitry Andric       Count += 2;
377e8d8bef9SDimitry Andric       break;
3780b57cec5SDimitry Andric     case Win64EH::UOP_SaveFReg:
3790b57cec5SDimitry Andric       Count += 2;
3800b57cec5SDimitry Andric       break;
3810b57cec5SDimitry Andric     case Win64EH::UOP_SaveFRegP:
3820b57cec5SDimitry Andric       Count += 2;
3830b57cec5SDimitry Andric       break;
3840b57cec5SDimitry Andric     case Win64EH::UOP_SaveFRegX:
3850b57cec5SDimitry Andric       Count += 2;
3860b57cec5SDimitry Andric       break;
3870b57cec5SDimitry Andric     case Win64EH::UOP_SaveFRegPX:
3880b57cec5SDimitry Andric       Count += 2;
3890b57cec5SDimitry Andric       break;
3900b57cec5SDimitry Andric     case Win64EH::UOP_SetFP:
3910b57cec5SDimitry Andric       Count += 1;
3920b57cec5SDimitry Andric       break;
3930b57cec5SDimitry Andric     case Win64EH::UOP_AddFP:
3940b57cec5SDimitry Andric       Count += 2;
3950b57cec5SDimitry Andric       break;
3960b57cec5SDimitry Andric     case Win64EH::UOP_Nop:
3970b57cec5SDimitry Andric       Count += 1;
3980b57cec5SDimitry Andric       break;
3990b57cec5SDimitry Andric     case Win64EH::UOP_End:
4000b57cec5SDimitry Andric       Count += 1;
4010b57cec5SDimitry Andric       break;
402e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveNext:
403e8d8bef9SDimitry Andric       Count += 1;
404e8d8bef9SDimitry Andric       break;
405e8d8bef9SDimitry Andric     case Win64EH::UOP_TrapFrame:
406e8d8bef9SDimitry Andric       Count += 1;
407e8d8bef9SDimitry Andric       break;
408e8d8bef9SDimitry Andric     case Win64EH::UOP_PushMachFrame:
409e8d8bef9SDimitry Andric       Count += 1;
410e8d8bef9SDimitry Andric       break;
411e8d8bef9SDimitry Andric     case Win64EH::UOP_Context:
412e8d8bef9SDimitry Andric       Count += 1;
413e8d8bef9SDimitry Andric       break;
414e8d8bef9SDimitry Andric     case Win64EH::UOP_ClearUnwoundToCall:
415e8d8bef9SDimitry Andric       Count += 1;
416e8d8bef9SDimitry Andric       break;
417bdd1243dSDimitry Andric     case Win64EH::UOP_PACSignLR:
418bdd1243dSDimitry Andric       Count += 1;
419bdd1243dSDimitry Andric       break;
420bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegI:
421bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIP:
422bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegD:
423bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDP:
424bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQ:
425bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQP:
426bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIX:
427bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIPX:
428bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDX:
429bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDPX:
430bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQX:
431bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQPX:
432bdd1243dSDimitry Andric       Count += 3;
433bdd1243dSDimitry Andric       break;
4340b57cec5SDimitry Andric     }
4350b57cec5SDimitry Andric   }
4360b57cec5SDimitry Andric   return Count;
4370b57cec5SDimitry Andric }
4380b57cec5SDimitry Andric 
4390b57cec5SDimitry Andric // Unwind opcode encodings and restrictions are documented at
4400b57cec5SDimitry Andric // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
44181ad6265SDimitry Andric static void ARM64EmitUnwindCode(MCStreamer &streamer,
4420eae32dcSDimitry Andric                                 const WinEH::Instruction &inst) {
4430b57cec5SDimitry Andric   uint8_t b, reg;
4440b57cec5SDimitry Andric   switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
4450b57cec5SDimitry Andric   default:
4460b57cec5SDimitry Andric     llvm_unreachable("Unsupported ARM64 unwind code");
4470b57cec5SDimitry Andric   case Win64EH::UOP_AllocSmall:
4480b57cec5SDimitry Andric     b = (inst.Offset >> 4) & 0x1F;
4495ffd83dbSDimitry Andric     streamer.emitInt8(b);
4500b57cec5SDimitry Andric     break;
4510b57cec5SDimitry Andric   case Win64EH::UOP_AllocMedium: {
4520b57cec5SDimitry Andric     uint16_t hw = (inst.Offset >> 4) & 0x7FF;
4530b57cec5SDimitry Andric     b = 0xC0;
4540b57cec5SDimitry Andric     b |= (hw >> 8);
4555ffd83dbSDimitry Andric     streamer.emitInt8(b);
4560b57cec5SDimitry Andric     b = hw & 0xFF;
4575ffd83dbSDimitry Andric     streamer.emitInt8(b);
4580b57cec5SDimitry Andric     break;
4590b57cec5SDimitry Andric   }
4600b57cec5SDimitry Andric   case Win64EH::UOP_AllocLarge: {
4610b57cec5SDimitry Andric     uint32_t w;
4620b57cec5SDimitry Andric     b = 0xE0;
4635ffd83dbSDimitry Andric     streamer.emitInt8(b);
4640b57cec5SDimitry Andric     w = inst.Offset >> 4;
4650b57cec5SDimitry Andric     b = (w & 0x00FF0000) >> 16;
4665ffd83dbSDimitry Andric     streamer.emitInt8(b);
4670b57cec5SDimitry Andric     b = (w & 0x0000FF00) >> 8;
4685ffd83dbSDimitry Andric     streamer.emitInt8(b);
4690b57cec5SDimitry Andric     b = w & 0x000000FF;
4705ffd83dbSDimitry Andric     streamer.emitInt8(b);
4710b57cec5SDimitry Andric     break;
4720b57cec5SDimitry Andric   }
4730b57cec5SDimitry Andric   case Win64EH::UOP_SetFP:
4740b57cec5SDimitry Andric     b = 0xE1;
4755ffd83dbSDimitry Andric     streamer.emitInt8(b);
4760b57cec5SDimitry Andric     break;
4770b57cec5SDimitry Andric   case Win64EH::UOP_AddFP:
4780b57cec5SDimitry Andric     b = 0xE2;
4795ffd83dbSDimitry Andric     streamer.emitInt8(b);
4800b57cec5SDimitry Andric     b = (inst.Offset >> 3);
4815ffd83dbSDimitry Andric     streamer.emitInt8(b);
4820b57cec5SDimitry Andric     break;
4830b57cec5SDimitry Andric   case Win64EH::UOP_Nop:
4840b57cec5SDimitry Andric     b = 0xE3;
4855ffd83dbSDimitry Andric     streamer.emitInt8(b);
4860b57cec5SDimitry Andric     break;
487e8d8bef9SDimitry Andric   case Win64EH::UOP_SaveR19R20X:
488e8d8bef9SDimitry Andric     b = 0x20;
489e8d8bef9SDimitry Andric     b |= (inst.Offset >> 3) & 0x1F;
490e8d8bef9SDimitry Andric     streamer.emitInt8(b);
491e8d8bef9SDimitry Andric     break;
4920b57cec5SDimitry Andric   case Win64EH::UOP_SaveFPLRX:
4930b57cec5SDimitry Andric     b = 0x80;
4940b57cec5SDimitry Andric     b |= ((inst.Offset - 1) >> 3) & 0x3F;
4955ffd83dbSDimitry Andric     streamer.emitInt8(b);
4960b57cec5SDimitry Andric     break;
4970b57cec5SDimitry Andric   case Win64EH::UOP_SaveFPLR:
4980b57cec5SDimitry Andric     b = 0x40;
4990b57cec5SDimitry Andric     b |= (inst.Offset >> 3) & 0x3F;
5005ffd83dbSDimitry Andric     streamer.emitInt8(b);
5010b57cec5SDimitry Andric     break;
5020b57cec5SDimitry Andric   case Win64EH::UOP_SaveReg:
5030b57cec5SDimitry Andric     assert(inst.Register >= 19 && "Saved reg must be >= 19");
5040b57cec5SDimitry Andric     reg = inst.Register - 19;
5050b57cec5SDimitry Andric     b = 0xD0 | ((reg & 0xC) >> 2);
5065ffd83dbSDimitry Andric     streamer.emitInt8(b);
5070b57cec5SDimitry Andric     b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
5085ffd83dbSDimitry Andric     streamer.emitInt8(b);
5090b57cec5SDimitry Andric     break;
5100b57cec5SDimitry Andric   case Win64EH::UOP_SaveRegX:
5110b57cec5SDimitry Andric     assert(inst.Register >= 19 && "Saved reg must be >= 19");
5120b57cec5SDimitry Andric     reg = inst.Register - 19;
5130b57cec5SDimitry Andric     b = 0xD4 | ((reg & 0x8) >> 3);
5145ffd83dbSDimitry Andric     streamer.emitInt8(b);
5150b57cec5SDimitry Andric     b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1);
5165ffd83dbSDimitry Andric     streamer.emitInt8(b);
5170b57cec5SDimitry Andric     break;
5180b57cec5SDimitry Andric   case Win64EH::UOP_SaveRegP:
5190b57cec5SDimitry Andric     assert(inst.Register >= 19 && "Saved registers must be >= 19");
5200b57cec5SDimitry Andric     reg = inst.Register - 19;
5210b57cec5SDimitry Andric     b = 0xC8 | ((reg & 0xC) >> 2);
5225ffd83dbSDimitry Andric     streamer.emitInt8(b);
5230b57cec5SDimitry Andric     b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
5245ffd83dbSDimitry Andric     streamer.emitInt8(b);
5250b57cec5SDimitry Andric     break;
5260b57cec5SDimitry Andric   case Win64EH::UOP_SaveRegPX:
5270b57cec5SDimitry Andric     assert(inst.Register >= 19 && "Saved registers must be >= 19");
5280b57cec5SDimitry Andric     reg = inst.Register - 19;
5290b57cec5SDimitry Andric     b = 0xCC | ((reg & 0xC) >> 2);
5305ffd83dbSDimitry Andric     streamer.emitInt8(b);
5310b57cec5SDimitry Andric     b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1);
5325ffd83dbSDimitry Andric     streamer.emitInt8(b);
5330b57cec5SDimitry Andric     break;
534e8d8bef9SDimitry Andric   case Win64EH::UOP_SaveLRPair:
535e8d8bef9SDimitry Andric     assert(inst.Register >= 19 && "Saved reg must be >= 19");
536e8d8bef9SDimitry Andric     reg = inst.Register - 19;
537e8d8bef9SDimitry Andric     assert((reg % 2) == 0 && "Saved reg must be 19+2*X");
538e8d8bef9SDimitry Andric     reg /= 2;
539e8d8bef9SDimitry Andric     b = 0xD6 | ((reg & 0x7) >> 2);
540e8d8bef9SDimitry Andric     streamer.emitInt8(b);
541e8d8bef9SDimitry Andric     b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
542e8d8bef9SDimitry Andric     streamer.emitInt8(b);
543e8d8bef9SDimitry Andric     break;
5440b57cec5SDimitry Andric   case Win64EH::UOP_SaveFReg:
5450b57cec5SDimitry Andric     assert(inst.Register >= 8 && "Saved dreg must be >= 8");
5460b57cec5SDimitry Andric     reg = inst.Register - 8;
5470b57cec5SDimitry Andric     b = 0xDC | ((reg & 0x4) >> 2);
5485ffd83dbSDimitry Andric     streamer.emitInt8(b);
5490b57cec5SDimitry Andric     b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
5505ffd83dbSDimitry Andric     streamer.emitInt8(b);
5510b57cec5SDimitry Andric     break;
5520b57cec5SDimitry Andric   case Win64EH::UOP_SaveFRegX:
5530b57cec5SDimitry Andric     assert(inst.Register >= 8 && "Saved dreg must be >= 8");
5540b57cec5SDimitry Andric     reg = inst.Register - 8;
5550b57cec5SDimitry Andric     b = 0xDE;
5565ffd83dbSDimitry Andric     streamer.emitInt8(b);
5570b57cec5SDimitry Andric     b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1);
5585ffd83dbSDimitry Andric     streamer.emitInt8(b);
5590b57cec5SDimitry Andric     break;
5600b57cec5SDimitry Andric   case Win64EH::UOP_SaveFRegP:
5610b57cec5SDimitry Andric     assert(inst.Register >= 8 && "Saved dregs must be >= 8");
5620b57cec5SDimitry Andric     reg = inst.Register - 8;
5630b57cec5SDimitry Andric     b = 0xD8 | ((reg & 0x4) >> 2);
5645ffd83dbSDimitry Andric     streamer.emitInt8(b);
5650b57cec5SDimitry Andric     b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
5665ffd83dbSDimitry Andric     streamer.emitInt8(b);
5670b57cec5SDimitry Andric     break;
5680b57cec5SDimitry Andric   case Win64EH::UOP_SaveFRegPX:
5690b57cec5SDimitry Andric     assert(inst.Register >= 8 && "Saved dregs must be >= 8");
5700b57cec5SDimitry Andric     reg = inst.Register - 8;
5710b57cec5SDimitry Andric     b = 0xDA | ((reg & 0x4) >> 2);
5725ffd83dbSDimitry Andric     streamer.emitInt8(b);
5730b57cec5SDimitry Andric     b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1);
5745ffd83dbSDimitry Andric     streamer.emitInt8(b);
5750b57cec5SDimitry Andric     break;
5760b57cec5SDimitry Andric   case Win64EH::UOP_End:
5770b57cec5SDimitry Andric     b = 0xE4;
5785ffd83dbSDimitry Andric     streamer.emitInt8(b);
5790b57cec5SDimitry Andric     break;
580e8d8bef9SDimitry Andric   case Win64EH::UOP_SaveNext:
581e8d8bef9SDimitry Andric     b = 0xE6;
582e8d8bef9SDimitry Andric     streamer.emitInt8(b);
583e8d8bef9SDimitry Andric     break;
584e8d8bef9SDimitry Andric   case Win64EH::UOP_TrapFrame:
585e8d8bef9SDimitry Andric     b = 0xE8;
586e8d8bef9SDimitry Andric     streamer.emitInt8(b);
587e8d8bef9SDimitry Andric     break;
588e8d8bef9SDimitry Andric   case Win64EH::UOP_PushMachFrame:
589e8d8bef9SDimitry Andric     b = 0xE9;
590e8d8bef9SDimitry Andric     streamer.emitInt8(b);
591e8d8bef9SDimitry Andric     break;
592e8d8bef9SDimitry Andric   case Win64EH::UOP_Context:
593e8d8bef9SDimitry Andric     b = 0xEA;
594e8d8bef9SDimitry Andric     streamer.emitInt8(b);
595e8d8bef9SDimitry Andric     break;
596e8d8bef9SDimitry Andric   case Win64EH::UOP_ClearUnwoundToCall:
597e8d8bef9SDimitry Andric     b = 0xEC;
598e8d8bef9SDimitry Andric     streamer.emitInt8(b);
599e8d8bef9SDimitry Andric     break;
600bdd1243dSDimitry Andric   case Win64EH::UOP_PACSignLR:
601bdd1243dSDimitry Andric     b = 0xFC;
602bdd1243dSDimitry Andric     streamer.emitInt8(b);
603bdd1243dSDimitry Andric     break;
604bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegI:
605bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegIP:
606bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegD:
607bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegDP:
608bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQ:
609bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQP:
610bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegIX:
611bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegIPX:
612bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegDX:
613bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegDPX:
614bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQX:
615bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQPX: {
616bdd1243dSDimitry Andric     // This assumes the opcodes are listed in the enum in a particular order.
617bdd1243dSDimitry Andric     int Op = inst.Operation - Win64EH::UOP_SaveAnyRegI;
618bdd1243dSDimitry Andric     int Writeback = Op / 6;
619bdd1243dSDimitry Andric     int Paired = Op % 2;
620bdd1243dSDimitry Andric     int Mode = (Op / 2) % 3;
621bdd1243dSDimitry Andric     int Offset = inst.Offset >> 3;
622bdd1243dSDimitry Andric     if (Writeback || Paired || Mode == 2)
623bdd1243dSDimitry Andric       Offset >>= 1;
624bdd1243dSDimitry Andric     if (Writeback)
625bdd1243dSDimitry Andric       --Offset;
626bdd1243dSDimitry Andric     b = 0xE7;
627bdd1243dSDimitry Andric     streamer.emitInt8(b);
628bdd1243dSDimitry Andric     assert(inst.Register < 32);
629bdd1243dSDimitry Andric     b = inst.Register | (Writeback << 5) | (Paired << 6);
630bdd1243dSDimitry Andric     streamer.emitInt8(b);
631bdd1243dSDimitry Andric     b = Offset | (Mode << 6);
632bdd1243dSDimitry Andric     streamer.emitInt8(b);
633bdd1243dSDimitry Andric     break;
634bdd1243dSDimitry Andric   }
6350b57cec5SDimitry Andric   }
6360b57cec5SDimitry Andric }
6370b57cec5SDimitry Andric 
6380b57cec5SDimitry Andric // Returns the epilog symbol of an epilog with the exact same unwind code
63981ad6265SDimitry Andric // sequence, if it exists.  Otherwise, returns nullptr.
6400b57cec5SDimitry Andric // EpilogInstrs - Unwind codes for the current epilog.
6410b57cec5SDimitry Andric // Epilogs - Epilogs that potentialy match the current epilog.
6420b57cec5SDimitry Andric static MCSymbol*
6430b57cec5SDimitry Andric FindMatchingEpilog(const std::vector<WinEH::Instruction>& EpilogInstrs,
6440b57cec5SDimitry Andric                    const std::vector<MCSymbol *>& Epilogs,
6450b57cec5SDimitry Andric                    const WinEH::FrameInfo *info) {
6460b57cec5SDimitry Andric   for (auto *EpilogStart : Epilogs) {
6470b57cec5SDimitry Andric     auto InstrsIter = info->EpilogMap.find(EpilogStart);
6480b57cec5SDimitry Andric     assert(InstrsIter != info->EpilogMap.end() &&
6490b57cec5SDimitry Andric            "Epilog not found in EpilogMap");
65081ad6265SDimitry Andric     const auto &Instrs = InstrsIter->second.Instructions;
6510b57cec5SDimitry Andric 
6520b57cec5SDimitry Andric     if (Instrs.size() != EpilogInstrs.size())
6530b57cec5SDimitry Andric       continue;
6540b57cec5SDimitry Andric 
6550b57cec5SDimitry Andric     bool Match = true;
6560b57cec5SDimitry Andric     for (unsigned i = 0; i < Instrs.size(); ++i)
65781ad6265SDimitry Andric       if (Instrs[i] != EpilogInstrs[i]) {
6580b57cec5SDimitry Andric         Match = false;
6590b57cec5SDimitry Andric         break;
6600b57cec5SDimitry Andric       }
6610b57cec5SDimitry Andric 
6620b57cec5SDimitry Andric     if (Match)
6630b57cec5SDimitry Andric       return EpilogStart;
6640b57cec5SDimitry Andric   }
6650b57cec5SDimitry Andric   return nullptr;
6660b57cec5SDimitry Andric }
6670b57cec5SDimitry Andric 
66881ad6265SDimitry Andric static void simplifyARM64Opcodes(std::vector<WinEH::Instruction> &Instructions,
669e8d8bef9SDimitry Andric                                  bool Reverse) {
670e8d8bef9SDimitry Andric   unsigned PrevOffset = -1;
671e8d8bef9SDimitry Andric   unsigned PrevRegister = -1;
672e8d8bef9SDimitry Andric 
673e8d8bef9SDimitry Andric   auto VisitInstruction = [&](WinEH::Instruction &Inst) {
674e8d8bef9SDimitry Andric     // Convert 2-byte opcodes into equivalent 1-byte ones.
675e8d8bef9SDimitry Andric     if (Inst.Operation == Win64EH::UOP_SaveRegP && Inst.Register == 29) {
676e8d8bef9SDimitry Andric       Inst.Operation = Win64EH::UOP_SaveFPLR;
677e8d8bef9SDimitry Andric       Inst.Register = -1;
678e8d8bef9SDimitry Andric     } else if (Inst.Operation == Win64EH::UOP_SaveRegPX &&
679e8d8bef9SDimitry Andric                Inst.Register == 29) {
680e8d8bef9SDimitry Andric       Inst.Operation = Win64EH::UOP_SaveFPLRX;
681e8d8bef9SDimitry Andric       Inst.Register = -1;
682e8d8bef9SDimitry Andric     } else if (Inst.Operation == Win64EH::UOP_SaveRegPX &&
683e8d8bef9SDimitry Andric                Inst.Register == 19 && Inst.Offset <= 248) {
684e8d8bef9SDimitry Andric       Inst.Operation = Win64EH::UOP_SaveR19R20X;
685e8d8bef9SDimitry Andric       Inst.Register = -1;
686e8d8bef9SDimitry Andric     } else if (Inst.Operation == Win64EH::UOP_AddFP && Inst.Offset == 0) {
687e8d8bef9SDimitry Andric       Inst.Operation = Win64EH::UOP_SetFP;
688e8d8bef9SDimitry Andric     } else if (Inst.Operation == Win64EH::UOP_SaveRegP &&
689e8d8bef9SDimitry Andric                Inst.Register == PrevRegister + 2 &&
690e8d8bef9SDimitry Andric                Inst.Offset == PrevOffset + 16) {
691e8d8bef9SDimitry Andric       Inst.Operation = Win64EH::UOP_SaveNext;
692e8d8bef9SDimitry Andric       Inst.Register = -1;
693e8d8bef9SDimitry Andric       Inst.Offset = 0;
694e8d8bef9SDimitry Andric       // Intentionally not creating UOP_SaveNext for float register pairs,
695e8d8bef9SDimitry Andric       // as current versions of Windows (up to at least 20.04) is buggy
696e8d8bef9SDimitry Andric       // regarding SaveNext for float pairs.
697e8d8bef9SDimitry Andric     }
698e8d8bef9SDimitry Andric     // Update info about the previous instruction, for detecting if
699e8d8bef9SDimitry Andric     // the next one can be made a UOP_SaveNext
700e8d8bef9SDimitry Andric     if (Inst.Operation == Win64EH::UOP_SaveR19R20X) {
701e8d8bef9SDimitry Andric       PrevOffset = 0;
702e8d8bef9SDimitry Andric       PrevRegister = 19;
703e8d8bef9SDimitry Andric     } else if (Inst.Operation == Win64EH::UOP_SaveRegPX) {
704e8d8bef9SDimitry Andric       PrevOffset = 0;
705e8d8bef9SDimitry Andric       PrevRegister = Inst.Register;
706e8d8bef9SDimitry Andric     } else if (Inst.Operation == Win64EH::UOP_SaveRegP) {
707e8d8bef9SDimitry Andric       PrevOffset = Inst.Offset;
708e8d8bef9SDimitry Andric       PrevRegister = Inst.Register;
709e8d8bef9SDimitry Andric     } else if (Inst.Operation == Win64EH::UOP_SaveNext) {
710e8d8bef9SDimitry Andric       PrevRegister += 2;
711e8d8bef9SDimitry Andric       PrevOffset += 16;
712e8d8bef9SDimitry Andric     } else {
713e8d8bef9SDimitry Andric       PrevRegister = -1;
714e8d8bef9SDimitry Andric       PrevOffset = -1;
715e8d8bef9SDimitry Andric     }
716e8d8bef9SDimitry Andric   };
717e8d8bef9SDimitry Andric 
718e8d8bef9SDimitry Andric   // Iterate over instructions in a forward order (for prologues),
719e8d8bef9SDimitry Andric   // backwards for epilogues (i.e. always reverse compared to how the
720e8d8bef9SDimitry Andric   // opcodes are stored).
721e8d8bef9SDimitry Andric   if (Reverse) {
722e8d8bef9SDimitry Andric     for (auto It = Instructions.rbegin(); It != Instructions.rend(); It++)
723e8d8bef9SDimitry Andric       VisitInstruction(*It);
724e8d8bef9SDimitry Andric   } else {
725e8d8bef9SDimitry Andric     for (WinEH::Instruction &Inst : Instructions)
726e8d8bef9SDimitry Andric       VisitInstruction(Inst);
727e8d8bef9SDimitry Andric   }
728e8d8bef9SDimitry Andric }
729e8d8bef9SDimitry Andric 
73081ad6265SDimitry Andric // Check if an epilog exists as a subset of the end of a prolog (backwards).
73181ad6265SDimitry Andric static int
73281ad6265SDimitry Andric getARM64OffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
73381ad6265SDimitry Andric                        const std::vector<WinEH::Instruction> &Epilog) {
73481ad6265SDimitry Andric   // Can't find an epilog as a subset if it is longer than the prolog.
73581ad6265SDimitry Andric   if (Epilog.size() > Prolog.size())
73681ad6265SDimitry Andric     return -1;
73781ad6265SDimitry Andric 
73881ad6265SDimitry Andric   // Check that the epilog actually is a perfect match for the end (backwrds)
73981ad6265SDimitry Andric   // of the prolog.
74081ad6265SDimitry Andric   for (int I = Epilog.size() - 1; I >= 0; I--) {
74181ad6265SDimitry Andric     if (Prolog[I] != Epilog[Epilog.size() - 1 - I])
74281ad6265SDimitry Andric       return -1;
74381ad6265SDimitry Andric   }
74481ad6265SDimitry Andric 
74581ad6265SDimitry Andric   if (Epilog.size() == Prolog.size())
74681ad6265SDimitry Andric     return 0;
747bdd1243dSDimitry Andric 
748bdd1243dSDimitry Andric   // If the epilog was a subset of the prolog, find its offset.
74981ad6265SDimitry Andric   return ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction>(
75081ad6265SDimitry Andric       &Prolog[Epilog.size()], Prolog.size() - Epilog.size()));
75181ad6265SDimitry Andric }
75281ad6265SDimitry Andric 
75381ad6265SDimitry Andric static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
754bdd1243dSDimitry Andric                                   WinEH::FrameInfo::Segment *Seg,
755e8d8bef9SDimitry Andric                                   int PrologCodeBytes) {
756e8d8bef9SDimitry Andric   // Can only pack if there's one single epilog
757bdd1243dSDimitry Andric   if (Seg->Epilogs.size() != 1)
758e8d8bef9SDimitry Andric     return -1;
759e8d8bef9SDimitry Andric 
760bdd1243dSDimitry Andric   MCSymbol *Sym = Seg->Epilogs.begin()->first;
761e8d8bef9SDimitry Andric   const std::vector<WinEH::Instruction> &Epilog =
762bdd1243dSDimitry Andric       info->EpilogMap[Sym].Instructions;
763e8d8bef9SDimitry Andric 
764e8d8bef9SDimitry Andric   // Check that the epilog actually is at the very end of the function,
765e8d8bef9SDimitry Andric   // otherwise it can't be packed.
766bdd1243dSDimitry Andric   uint32_t DistanceFromEnd =
767bdd1243dSDimitry Andric       (uint32_t)(Seg->Offset + Seg->Length - Seg->Epilogs.begin()->second);
768e8d8bef9SDimitry Andric   if (DistanceFromEnd / 4 != Epilog.size())
769e8d8bef9SDimitry Andric     return -1;
770e8d8bef9SDimitry Andric 
77181ad6265SDimitry Andric   int RetVal = -1;
77281ad6265SDimitry Andric   // Even if we don't end up sharing opcodes with the prolog, we can still
77381ad6265SDimitry Andric   // write the offset as a packed offset, if the single epilog is located at
77481ad6265SDimitry Andric   // the end of the function and the offset (pointing after the prolog) fits
77581ad6265SDimitry Andric   // as a packed offset.
77681ad6265SDimitry Andric   if (PrologCodeBytes <= 31 &&
77781ad6265SDimitry Andric       PrologCodeBytes + ARM64CountOfUnwindCodes(Epilog) <= 124)
77881ad6265SDimitry Andric     RetVal = PrologCodeBytes;
77981ad6265SDimitry Andric 
78081ad6265SDimitry Andric   int Offset = getARM64OffsetInProlog(info->Instructions, Epilog);
78181ad6265SDimitry Andric   if (Offset < 0)
78281ad6265SDimitry Andric     return RetVal;
783e8d8bef9SDimitry Andric 
784e8d8bef9SDimitry Andric   // Check that the offset and prolog size fits in the first word; it's
785e8d8bef9SDimitry Andric   // unclear whether the epilog count in the extension word can be taken
786e8d8bef9SDimitry Andric   // as packed epilog offset.
787e8d8bef9SDimitry Andric   if (Offset > 31 || PrologCodeBytes > 124)
78881ad6265SDimitry Andric     return RetVal;
789e8d8bef9SDimitry Andric 
79081ad6265SDimitry Andric   // As we choose to express the epilog as part of the prolog, remove the
79181ad6265SDimitry Andric   // epilog from the map, so we don't try to emit its opcodes.
792bdd1243dSDimitry Andric   info->EpilogMap.erase(Sym);
793e8d8bef9SDimitry Andric   return Offset;
794e8d8bef9SDimitry Andric }
795e8d8bef9SDimitry Andric 
79681ad6265SDimitry Andric static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength,
797e8d8bef9SDimitry Andric                                  int PackedEpilogOffset) {
798e8d8bef9SDimitry Andric   if (PackedEpilogOffset == 0) {
799e8d8bef9SDimitry Andric     // Fully symmetric prolog and epilog, should be ok for packed format.
800e8d8bef9SDimitry Andric     // For CR=3, the corresponding synthesized epilog actually lacks the
801e8d8bef9SDimitry Andric     // SetFP opcode, but unwinding should work just fine despite that
802e8d8bef9SDimitry Andric     // (if at the SetFP opcode, the unwinder considers it as part of the
803e8d8bef9SDimitry Andric     // function body and just unwinds the full prolog instead).
804e8d8bef9SDimitry Andric   } else if (PackedEpilogOffset == 1) {
805e8d8bef9SDimitry Andric     // One single case of differences between prolog and epilog is allowed:
806e8d8bef9SDimitry Andric     // The epilog can lack a single SetFP that is the last opcode in the
807e8d8bef9SDimitry Andric     // prolog, for the CR=3 case.
808e8d8bef9SDimitry Andric     if (info->Instructions.back().Operation != Win64EH::UOP_SetFP)
809e8d8bef9SDimitry Andric       return false;
810e8d8bef9SDimitry Andric   } else {
811e8d8bef9SDimitry Andric     // Too much difference between prolog and epilog.
812e8d8bef9SDimitry Andric     return false;
813e8d8bef9SDimitry Andric   }
814e8d8bef9SDimitry Andric   unsigned RegI = 0, RegF = 0;
815e8d8bef9SDimitry Andric   int Predecrement = 0;
816e8d8bef9SDimitry Andric   enum {
817e8d8bef9SDimitry Andric     Start,
818e8d8bef9SDimitry Andric     Start2,
819bdd1243dSDimitry Andric     Start3,
820e8d8bef9SDimitry Andric     IntRegs,
821e8d8bef9SDimitry Andric     FloatRegs,
822e8d8bef9SDimitry Andric     InputArgs,
823e8d8bef9SDimitry Andric     StackAdjust,
824e8d8bef9SDimitry Andric     FrameRecord,
825e8d8bef9SDimitry Andric     End
826e8d8bef9SDimitry Andric   } Location = Start;
827e8d8bef9SDimitry Andric   bool StandaloneLR = false, FPLRPair = false;
828bdd1243dSDimitry Andric   bool PAC = false;
829e8d8bef9SDimitry Andric   int StackOffset = 0;
830e8d8bef9SDimitry Andric   int Nops = 0;
831e8d8bef9SDimitry Andric   // Iterate over the prolog and check that all opcodes exactly match
832e8d8bef9SDimitry Andric   // the canonical order and form. A more lax check could verify that
833e8d8bef9SDimitry Andric   // all saved registers are in the expected locations, but not enforce
834e8d8bef9SDimitry Andric   // the order - that would work fine when unwinding from within
835e8d8bef9SDimitry Andric   // functions, but not be exactly right if unwinding happens within
836e8d8bef9SDimitry Andric   // prologs/epilogs.
837e8d8bef9SDimitry Andric   for (const WinEH::Instruction &Inst : info->Instructions) {
838e8d8bef9SDimitry Andric     switch (Inst.Operation) {
839e8d8bef9SDimitry Andric     case Win64EH::UOP_End:
840e8d8bef9SDimitry Andric       if (Location != Start)
841e8d8bef9SDimitry Andric         return false;
842e8d8bef9SDimitry Andric       Location = Start2;
843e8d8bef9SDimitry Andric       break;
844bdd1243dSDimitry Andric     case Win64EH::UOP_PACSignLR:
845e8d8bef9SDimitry Andric       if (Location != Start2)
846e8d8bef9SDimitry Andric         return false;
847bdd1243dSDimitry Andric       PAC = true;
848bdd1243dSDimitry Andric       Location = Start3;
849bdd1243dSDimitry Andric       break;
850bdd1243dSDimitry Andric     case Win64EH::UOP_SaveR19R20X:
851bdd1243dSDimitry Andric       if (Location != Start2 && Location != Start3)
852bdd1243dSDimitry Andric         return false;
853e8d8bef9SDimitry Andric       Predecrement = Inst.Offset;
854e8d8bef9SDimitry Andric       RegI = 2;
855e8d8bef9SDimitry Andric       Location = IntRegs;
856e8d8bef9SDimitry Andric       break;
857e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveRegX:
858bdd1243dSDimitry Andric       if (Location != Start2 && Location != Start3)
859e8d8bef9SDimitry Andric         return false;
860e8d8bef9SDimitry Andric       Predecrement = Inst.Offset;
861e8d8bef9SDimitry Andric       if (Inst.Register == 19)
862e8d8bef9SDimitry Andric         RegI += 1;
863e8d8bef9SDimitry Andric       else if (Inst.Register == 30)
864e8d8bef9SDimitry Andric         StandaloneLR = true;
865e8d8bef9SDimitry Andric       else
866e8d8bef9SDimitry Andric         return false;
867e8d8bef9SDimitry Andric       // Odd register; can't be any further int registers.
868e8d8bef9SDimitry Andric       Location = FloatRegs;
869e8d8bef9SDimitry Andric       break;
870e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveRegPX:
871e8d8bef9SDimitry Andric       // Can't have this in a canonical prologue. Either this has been
872e8d8bef9SDimitry Andric       // canonicalized into SaveR19R20X or SaveFPLRX, or it's an unsupported
873e8d8bef9SDimitry Andric       // register pair.
874e8d8bef9SDimitry Andric       // It can't be canonicalized into SaveR19R20X if the offset is
875e8d8bef9SDimitry Andric       // larger than 248 bytes, but even with the maximum case with
876e8d8bef9SDimitry Andric       // RegI=10/RegF=8/CR=1/H=1, we end up with SavSZ = 216, which should
877e8d8bef9SDimitry Andric       // fit into SaveR19R20X.
878e8d8bef9SDimitry Andric       // The unwinding opcodes can't describe the otherwise seemingly valid
879e8d8bef9SDimitry Andric       // case for RegI=1 CR=1, that would start with a
880e8d8bef9SDimitry Andric       // "stp x19, lr, [sp, #-...]!" as that fits neither SaveRegPX nor
881e8d8bef9SDimitry Andric       // SaveLRPair.
882e8d8bef9SDimitry Andric       return false;
883e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveRegP:
884e8d8bef9SDimitry Andric       if (Location != IntRegs || Inst.Offset != 8 * RegI ||
885e8d8bef9SDimitry Andric           Inst.Register != 19 + RegI)
886e8d8bef9SDimitry Andric         return false;
887e8d8bef9SDimitry Andric       RegI += 2;
888e8d8bef9SDimitry Andric       break;
889e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveReg:
890e8d8bef9SDimitry Andric       if (Location != IntRegs || Inst.Offset != 8 * RegI)
891e8d8bef9SDimitry Andric         return false;
892e8d8bef9SDimitry Andric       if (Inst.Register == 19 + RegI)
893e8d8bef9SDimitry Andric         RegI += 1;
894e8d8bef9SDimitry Andric       else if (Inst.Register == 30)
895e8d8bef9SDimitry Andric         StandaloneLR = true;
896e8d8bef9SDimitry Andric       else
897e8d8bef9SDimitry Andric         return false;
898e8d8bef9SDimitry Andric       // Odd register; can't be any further int registers.
899e8d8bef9SDimitry Andric       Location = FloatRegs;
900e8d8bef9SDimitry Andric       break;
901e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveLRPair:
902e8d8bef9SDimitry Andric       if (Location != IntRegs || Inst.Offset != 8 * RegI ||
903e8d8bef9SDimitry Andric           Inst.Register != 19 + RegI)
904e8d8bef9SDimitry Andric         return false;
905e8d8bef9SDimitry Andric       RegI += 1;
906e8d8bef9SDimitry Andric       StandaloneLR = true;
907e8d8bef9SDimitry Andric       Location = FloatRegs;
908e8d8bef9SDimitry Andric       break;
909e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveFRegX:
910e8d8bef9SDimitry Andric       // Packed unwind can't handle prologs that only save one single
911e8d8bef9SDimitry Andric       // float register.
912e8d8bef9SDimitry Andric       return false;
913e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveFReg:
914e8d8bef9SDimitry Andric       if (Location != FloatRegs || RegF == 0 || Inst.Register != 8 + RegF ||
915e8d8bef9SDimitry Andric           Inst.Offset != 8 * (RegI + (StandaloneLR ? 1 : 0) + RegF))
916e8d8bef9SDimitry Andric         return false;
917e8d8bef9SDimitry Andric       RegF += 1;
918e8d8bef9SDimitry Andric       Location = InputArgs;
919e8d8bef9SDimitry Andric       break;
920e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveFRegPX:
921bdd1243dSDimitry Andric       if ((Location != Start2 && Location != Start3) || Inst.Register != 8)
922e8d8bef9SDimitry Andric         return false;
923e8d8bef9SDimitry Andric       Predecrement = Inst.Offset;
924e8d8bef9SDimitry Andric       RegF = 2;
925e8d8bef9SDimitry Andric       Location = FloatRegs;
926e8d8bef9SDimitry Andric       break;
927e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveFRegP:
928e8d8bef9SDimitry Andric       if ((Location != IntRegs && Location != FloatRegs) ||
929e8d8bef9SDimitry Andric           Inst.Register != 8 + RegF ||
930e8d8bef9SDimitry Andric           Inst.Offset != 8 * (RegI + (StandaloneLR ? 1 : 0) + RegF))
931e8d8bef9SDimitry Andric         return false;
932e8d8bef9SDimitry Andric       RegF += 2;
933e8d8bef9SDimitry Andric       Location = FloatRegs;
934e8d8bef9SDimitry Andric       break;
935e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveNext:
936e8d8bef9SDimitry Andric       if (Location == IntRegs)
937e8d8bef9SDimitry Andric         RegI += 2;
938e8d8bef9SDimitry Andric       else if (Location == FloatRegs)
939e8d8bef9SDimitry Andric         RegF += 2;
940e8d8bef9SDimitry Andric       else
941e8d8bef9SDimitry Andric         return false;
942e8d8bef9SDimitry Andric       break;
943e8d8bef9SDimitry Andric     case Win64EH::UOP_Nop:
944e8d8bef9SDimitry Andric       if (Location != IntRegs && Location != FloatRegs && Location != InputArgs)
945e8d8bef9SDimitry Andric         return false;
946e8d8bef9SDimitry Andric       Location = InputArgs;
947e8d8bef9SDimitry Andric       Nops++;
948e8d8bef9SDimitry Andric       break;
949e8d8bef9SDimitry Andric     case Win64EH::UOP_AllocSmall:
950e8d8bef9SDimitry Andric     case Win64EH::UOP_AllocMedium:
951bdd1243dSDimitry Andric       if (Location != Start2 && Location != Start3 && Location != IntRegs &&
952bdd1243dSDimitry Andric           Location != FloatRegs && Location != InputArgs &&
953bdd1243dSDimitry Andric           Location != StackAdjust)
954e8d8bef9SDimitry Andric         return false;
955e8d8bef9SDimitry Andric       // Can have either a single decrement, or a pair of decrements with
956e8d8bef9SDimitry Andric       // 4080 and another decrement.
957e8d8bef9SDimitry Andric       if (StackOffset == 0)
958e8d8bef9SDimitry Andric         StackOffset = Inst.Offset;
959e8d8bef9SDimitry Andric       else if (StackOffset != 4080)
960e8d8bef9SDimitry Andric         return false;
961e8d8bef9SDimitry Andric       else
962e8d8bef9SDimitry Andric         StackOffset += Inst.Offset;
963e8d8bef9SDimitry Andric       Location = StackAdjust;
964e8d8bef9SDimitry Andric       break;
965e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveFPLRX:
966e8d8bef9SDimitry Andric       // Not allowing FPLRX after StackAdjust; if a StackAdjust is used, it
967e8d8bef9SDimitry Andric       // should be followed by a FPLR instead.
968bdd1243dSDimitry Andric       if (Location != Start2 && Location != Start3 && Location != IntRegs &&
969bdd1243dSDimitry Andric           Location != FloatRegs && Location != InputArgs)
970e8d8bef9SDimitry Andric         return false;
971e8d8bef9SDimitry Andric       StackOffset = Inst.Offset;
972e8d8bef9SDimitry Andric       Location = FrameRecord;
973e8d8bef9SDimitry Andric       FPLRPair = true;
974e8d8bef9SDimitry Andric       break;
975e8d8bef9SDimitry Andric     case Win64EH::UOP_SaveFPLR:
976e8d8bef9SDimitry Andric       // This can only follow after a StackAdjust
977e8d8bef9SDimitry Andric       if (Location != StackAdjust || Inst.Offset != 0)
978e8d8bef9SDimitry Andric         return false;
979e8d8bef9SDimitry Andric       Location = FrameRecord;
980e8d8bef9SDimitry Andric       FPLRPair = true;
981e8d8bef9SDimitry Andric       break;
982e8d8bef9SDimitry Andric     case Win64EH::UOP_SetFP:
983e8d8bef9SDimitry Andric       if (Location != FrameRecord)
984e8d8bef9SDimitry Andric         return false;
985e8d8bef9SDimitry Andric       Location = End;
986e8d8bef9SDimitry Andric       break;
987bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegI:
988bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIP:
989bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegD:
990bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDP:
991bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQ:
992bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQP:
993bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIX:
994bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIPX:
995bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDX:
996bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDPX:
997bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQX:
998bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQPX:
999bdd1243dSDimitry Andric       // These are never canonical; they don't show up with the usual Arm64
1000bdd1243dSDimitry Andric       // calling convention.
1001bdd1243dSDimitry Andric       return false;
1002bdd1243dSDimitry Andric     case Win64EH::UOP_AllocLarge:
1003bdd1243dSDimitry Andric       // Allocations this large can't be represented in packed unwind (and
1004bdd1243dSDimitry Andric       // usually don't fit the canonical form anyway because we need to use
1005bdd1243dSDimitry Andric       // __chkstk to allocate the stack space).
1006bdd1243dSDimitry Andric       return false;
1007bdd1243dSDimitry Andric     case Win64EH::UOP_AddFP:
1008bdd1243dSDimitry Andric       // "add x29, sp, #N" doesn't show up in the canonical pattern (except for
1009bdd1243dSDimitry Andric       // N=0, which is UOP_SetFP).
1010bdd1243dSDimitry Andric       return false;
1011bdd1243dSDimitry Andric     case Win64EH::UOP_TrapFrame:
1012bdd1243dSDimitry Andric     case Win64EH::UOP_Context:
1013bdd1243dSDimitry Andric     case Win64EH::UOP_ClearUnwoundToCall:
1014bdd1243dSDimitry Andric     case Win64EH::UOP_PushMachFrame:
1015bdd1243dSDimitry Andric       // These are special opcodes that aren't normally generated.
1016bdd1243dSDimitry Andric       return false;
1017bdd1243dSDimitry Andric     default:
1018bdd1243dSDimitry Andric       report_fatal_error("Unknown Arm64 unwind opcode");
1019e8d8bef9SDimitry Andric     }
1020e8d8bef9SDimitry Andric   }
1021e8d8bef9SDimitry Andric   if (RegI > 10 || RegF > 8)
1022e8d8bef9SDimitry Andric     return false;
1023e8d8bef9SDimitry Andric   if (StandaloneLR && FPLRPair)
1024e8d8bef9SDimitry Andric     return false;
1025e8d8bef9SDimitry Andric   if (FPLRPair && Location != End)
1026e8d8bef9SDimitry Andric     return false;
1027e8d8bef9SDimitry Andric   if (Nops != 0 && Nops != 4)
1028e8d8bef9SDimitry Andric     return false;
1029bdd1243dSDimitry Andric   if (PAC && !FPLRPair)
1030bdd1243dSDimitry Andric     return false;
1031e8d8bef9SDimitry Andric   int H = Nops == 4;
103281ad6265SDimitry Andric   // There's an inconsistency regarding packed unwind info with homed
103381ad6265SDimitry Andric   // parameters; according to the documentation, the epilog shouldn't have
103481ad6265SDimitry Andric   // the same corresponding nops (and thus, to set the H bit, we should
103581ad6265SDimitry Andric   // require an epilog which isn't exactly symmetrical - we shouldn't accept
103681ad6265SDimitry Andric   // an exact mirrored epilog for those cases), but in practice,
103781ad6265SDimitry Andric   // RtlVirtualUnwind behaves as if it does expect the epilogue to contain
103881ad6265SDimitry Andric   // the same nops. See https://github.com/llvm/llvm-project/issues/54879.
103981ad6265SDimitry Andric   // To play it safe, don't produce packed unwind info with homed parameters.
104081ad6265SDimitry Andric   if (H)
104181ad6265SDimitry Andric     return false;
1042e8d8bef9SDimitry Andric   int IntSZ = 8 * RegI;
1043e8d8bef9SDimitry Andric   if (StandaloneLR)
1044e8d8bef9SDimitry Andric     IntSZ += 8;
1045e8d8bef9SDimitry Andric   int FpSZ = 8 * RegF; // RegF not yet decremented
1046e8d8bef9SDimitry Andric   int SavSZ = (IntSZ + FpSZ + 8 * 8 * H + 0xF) & ~0xF;
1047e8d8bef9SDimitry Andric   if (Predecrement != SavSZ)
1048e8d8bef9SDimitry Andric     return false;
1049e8d8bef9SDimitry Andric   if (FPLRPair && StackOffset < 16)
1050e8d8bef9SDimitry Andric     return false;
1051e8d8bef9SDimitry Andric   if (StackOffset % 16)
1052e8d8bef9SDimitry Andric     return false;
1053e8d8bef9SDimitry Andric   uint32_t FrameSize = (StackOffset + SavSZ) / 16;
1054e8d8bef9SDimitry Andric   if (FrameSize > 0x1FF)
1055e8d8bef9SDimitry Andric     return false;
1056e8d8bef9SDimitry Andric   assert(RegF != 1 && "One single float reg not allowed");
1057e8d8bef9SDimitry Andric   if (RegF > 0)
1058e8d8bef9SDimitry Andric     RegF--; // Convert from actual number of registers, to value stored
1059e8d8bef9SDimitry Andric   assert(FuncLength <= 0x7FF && "FuncLength should have been checked earlier");
1060e8d8bef9SDimitry Andric   int Flag = 0x01; // Function segments not supported yet
1061bdd1243dSDimitry Andric   int CR = PAC ? 2 : FPLRPair ? 3 : StandaloneLR ? 1 : 0;
1062e8d8bef9SDimitry Andric   info->PackedInfo |= Flag << 0;
1063e8d8bef9SDimitry Andric   info->PackedInfo |= (FuncLength & 0x7FF) << 2;
1064e8d8bef9SDimitry Andric   info->PackedInfo |= (RegF & 0x7) << 13;
1065e8d8bef9SDimitry Andric   info->PackedInfo |= (RegI & 0xF) << 16;
1066e8d8bef9SDimitry Andric   info->PackedInfo |= (H & 0x1) << 20;
1067e8d8bef9SDimitry Andric   info->PackedInfo |= (CR & 0x3) << 21;
1068e8d8bef9SDimitry Andric   info->PackedInfo |= (FrameSize & 0x1FF) << 23;
1069e8d8bef9SDimitry Andric   return true;
1070e8d8bef9SDimitry Andric }
1071e8d8bef9SDimitry Andric 
1072bdd1243dSDimitry Andric static void ARM64ProcessEpilogs(WinEH::FrameInfo *info,
1073bdd1243dSDimitry Andric                                 WinEH::FrameInfo::Segment *Seg,
1074bdd1243dSDimitry Andric                                 uint32_t &TotalCodeBytes,
1075bdd1243dSDimitry Andric                                 MapVector<MCSymbol *, uint32_t> &EpilogInfo) {
1076bdd1243dSDimitry Andric 
1077bdd1243dSDimitry Andric   std::vector<MCSymbol *> EpilogStarts;
1078bdd1243dSDimitry Andric   for (auto &I : Seg->Epilogs)
1079bdd1243dSDimitry Andric     EpilogStarts.push_back(I.first);
1080bdd1243dSDimitry Andric 
1081bdd1243dSDimitry Andric   // Epilogs processed so far.
1082bdd1243dSDimitry Andric   std::vector<MCSymbol *> AddedEpilogs;
1083bdd1243dSDimitry Andric   for (auto *S : EpilogStarts) {
1084bdd1243dSDimitry Andric     MCSymbol *EpilogStart = S;
1085bdd1243dSDimitry Andric     auto &EpilogInstrs = info->EpilogMap[S].Instructions;
1086bdd1243dSDimitry Andric     uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs);
1087bdd1243dSDimitry Andric 
1088bdd1243dSDimitry Andric     MCSymbol* MatchingEpilog =
1089bdd1243dSDimitry Andric       FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
1090bdd1243dSDimitry Andric     int PrologOffset;
1091bdd1243dSDimitry Andric     if (MatchingEpilog) {
109206c3fb27SDimitry Andric       assert(EpilogInfo.contains(MatchingEpilog) &&
1093bdd1243dSDimitry Andric              "Duplicate epilog not found");
1094bdd1243dSDimitry Andric       EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog);
1095bdd1243dSDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
1096bdd1243dSDimitry Andric       // in ARM64EmitUnwindInfoForSegment().
1097bdd1243dSDimitry Andric       EpilogInstrs.clear();
1098bdd1243dSDimitry Andric     } else if ((PrologOffset = getARM64OffsetInProlog(info->Instructions,
1099bdd1243dSDimitry Andric                                                       EpilogInstrs)) >= 0) {
1100bdd1243dSDimitry Andric       EpilogInfo[EpilogStart] = PrologOffset;
1101bdd1243dSDimitry Andric       // If the segment doesn't have a prolog, an end_c will be emitted before
1102bdd1243dSDimitry Andric       // prolog opcodes. So epilog start index in opcodes array is moved by 1.
1103bdd1243dSDimitry Andric       if (!Seg->HasProlog)
1104bdd1243dSDimitry Andric         EpilogInfo[EpilogStart] += 1;
1105bdd1243dSDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
1106bdd1243dSDimitry Andric       // in ARM64EmitUnwindInfoForSegment().
1107bdd1243dSDimitry Andric       EpilogInstrs.clear();
1108bdd1243dSDimitry Andric     } else {
1109bdd1243dSDimitry Andric       EpilogInfo[EpilogStart] = TotalCodeBytes;
1110bdd1243dSDimitry Andric       TotalCodeBytes += CodeBytes;
1111bdd1243dSDimitry Andric       AddedEpilogs.push_back(EpilogStart);
1112bdd1243dSDimitry Andric     }
1113bdd1243dSDimitry Andric   }
1114bdd1243dSDimitry Andric }
1115bdd1243dSDimitry Andric 
1116bdd1243dSDimitry Andric static void ARM64FindSegmentsInFunction(MCStreamer &streamer,
1117bdd1243dSDimitry Andric                                         WinEH::FrameInfo *info,
1118bdd1243dSDimitry Andric                                         int64_t RawFuncLength) {
1119bdd1243dSDimitry Andric   if (info->PrologEnd)
1120bdd1243dSDimitry Andric     checkARM64Instructions(streamer, info->Instructions, info->Begin,
1121bdd1243dSDimitry Andric                            info->PrologEnd, info->Function->getName(),
1122bdd1243dSDimitry Andric                            "prologue");
1123bdd1243dSDimitry Andric   struct EpilogStartEnd {
1124bdd1243dSDimitry Andric     MCSymbol *Start;
1125bdd1243dSDimitry Andric     int64_t Offset;
1126bdd1243dSDimitry Andric     int64_t End;
1127bdd1243dSDimitry Andric   };
1128bdd1243dSDimitry Andric   // Record Start and End of each epilog.
1129bdd1243dSDimitry Andric   SmallVector<struct EpilogStartEnd, 4> Epilogs;
1130bdd1243dSDimitry Andric   for (auto &I : info->EpilogMap) {
1131bdd1243dSDimitry Andric     MCSymbol *Start = I.first;
1132bdd1243dSDimitry Andric     auto &Instrs = I.second.Instructions;
1133bdd1243dSDimitry Andric     int64_t Offset = GetAbsDifference(streamer, Start, info->Begin);
1134bdd1243dSDimitry Andric     checkARM64Instructions(streamer, Instrs, Start, I.second.End,
1135bdd1243dSDimitry Andric                            info->Function->getName(), "epilogue");
1136bdd1243dSDimitry Andric     assert((Epilogs.size() == 0 || Offset >= Epilogs.back().End) &&
1137bdd1243dSDimitry Andric            "Epilogs should be monotonically ordered");
1138bdd1243dSDimitry Andric     // Exclue the end opcode from Instrs.size() when calculating the end of the
1139bdd1243dSDimitry Andric     // epilog.
1140bdd1243dSDimitry Andric     Epilogs.push_back({Start, Offset, Offset + (int64_t)(Instrs.size() - 1) * 4});
1141bdd1243dSDimitry Andric   }
1142bdd1243dSDimitry Andric 
1143bdd1243dSDimitry Andric   unsigned E = 0;
1144bdd1243dSDimitry Andric   int64_t SegLimit = 0xFFFFC;
1145bdd1243dSDimitry Andric   int64_t SegOffset = 0;
1146bdd1243dSDimitry Andric 
1147bdd1243dSDimitry Andric   if (RawFuncLength > SegLimit) {
1148bdd1243dSDimitry Andric 
1149bdd1243dSDimitry Andric     int64_t RemainingLength = RawFuncLength;
1150bdd1243dSDimitry Andric 
1151bdd1243dSDimitry Andric     while (RemainingLength > SegLimit) {
1152bdd1243dSDimitry Andric       // Try divide the function into segments, requirements:
1153bdd1243dSDimitry Andric       // 1. Segment length <= 0xFFFFC;
1154bdd1243dSDimitry Andric       // 2. Each Prologue or Epilogue must be fully within a segment.
1155bdd1243dSDimitry Andric       int64_t SegLength = SegLimit;
1156bdd1243dSDimitry Andric       int64_t SegEnd = SegOffset + SegLength;
1157bdd1243dSDimitry Andric       // Keep record on symbols and offsets of epilogs in this segment.
1158bdd1243dSDimitry Andric       MapVector<MCSymbol *, int64_t> EpilogsInSegment;
1159bdd1243dSDimitry Andric 
1160bdd1243dSDimitry Andric       while (E < Epilogs.size() && Epilogs[E].End < SegEnd) {
1161bdd1243dSDimitry Andric         // Epilogs within current segment.
1162bdd1243dSDimitry Andric         EpilogsInSegment[Epilogs[E].Start] = Epilogs[E].Offset;
1163bdd1243dSDimitry Andric         ++E;
1164bdd1243dSDimitry Andric       }
1165bdd1243dSDimitry Andric 
1166bdd1243dSDimitry Andric       // At this point, we have:
1167bdd1243dSDimitry Andric       // 1. Put all epilogs in segments already. No action needed here; or
1168bdd1243dSDimitry Andric       // 2. Found an epilog that will cross segments boundry. We need to
1169bdd1243dSDimitry Andric       //    move back current segment's end boundry, so the epilog is entirely
1170bdd1243dSDimitry Andric       //    in the next segment; or
1171bdd1243dSDimitry Andric       // 3. Left at least one epilog that is entirely after this segment.
1172bdd1243dSDimitry Andric       //    It'll be handled by the next iteration, or the last segment.
1173bdd1243dSDimitry Andric       if (E < Epilogs.size() && Epilogs[E].Offset <= SegEnd)
1174bdd1243dSDimitry Andric         // Move back current Segment's end boundry.
1175bdd1243dSDimitry Andric         SegLength = Epilogs[E].Offset - SegOffset;
1176bdd1243dSDimitry Andric 
1177bdd1243dSDimitry Andric       auto Seg = WinEH::FrameInfo::Segment(
1178bdd1243dSDimitry Andric           SegOffset, SegLength, /* HasProlog */!SegOffset);
1179bdd1243dSDimitry Andric       Seg.Epilogs = std::move(EpilogsInSegment);
1180bdd1243dSDimitry Andric       info->Segments.push_back(Seg);
1181bdd1243dSDimitry Andric 
1182bdd1243dSDimitry Andric       SegOffset += SegLength;
1183bdd1243dSDimitry Andric       RemainingLength -= SegLength;
1184bdd1243dSDimitry Andric     }
1185bdd1243dSDimitry Andric   }
1186bdd1243dSDimitry Andric 
1187bdd1243dSDimitry Andric   // Add the last segment when RawFuncLength > 0xFFFFC,
1188bdd1243dSDimitry Andric   // or the only segment otherwise.
1189bdd1243dSDimitry Andric   auto LastSeg =
1190bdd1243dSDimitry Andric       WinEH::FrameInfo::Segment(SegOffset, RawFuncLength - SegOffset,
1191bdd1243dSDimitry Andric                                 /* HasProlog */!SegOffset);
1192bdd1243dSDimitry Andric   for (; E < Epilogs.size(); ++E)
1193bdd1243dSDimitry Andric     LastSeg.Epilogs[Epilogs[E].Start] = Epilogs[E].Offset;
1194bdd1243dSDimitry Andric   info->Segments.push_back(LastSeg);
1195bdd1243dSDimitry Andric }
1196bdd1243dSDimitry Andric 
1197bdd1243dSDimitry Andric static void ARM64EmitUnwindInfoForSegment(MCStreamer &streamer,
1198bdd1243dSDimitry Andric                                           WinEH::FrameInfo *info,
1199bdd1243dSDimitry Andric                                           WinEH::FrameInfo::Segment &Seg,
1200bdd1243dSDimitry Andric                                           bool TryPacked = true) {
1201bdd1243dSDimitry Andric   MCContext &context = streamer.getContext();
1202bdd1243dSDimitry Andric   MCSymbol *Label = context.createTempSymbol();
1203bdd1243dSDimitry Andric 
1204bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
1205bdd1243dSDimitry Andric   streamer.emitLabel(Label);
1206bdd1243dSDimitry Andric   Seg.Symbol = Label;
1207bdd1243dSDimitry Andric   // Use the 1st segemnt's label as function's.
1208bdd1243dSDimitry Andric   if (Seg.Offset == 0)
1209bdd1243dSDimitry Andric     info->Symbol = Label;
1210bdd1243dSDimitry Andric 
1211bdd1243dSDimitry Andric   bool HasProlog = Seg.HasProlog;
1212bdd1243dSDimitry Andric   bool HasEpilogs = (Seg.Epilogs.size() != 0);
1213bdd1243dSDimitry Andric 
1214bdd1243dSDimitry Andric   uint32_t SegLength = (uint32_t)Seg.Length / 4;
1215bdd1243dSDimitry Andric   uint32_t PrologCodeBytes = info->PrologCodeBytes;
1216bdd1243dSDimitry Andric 
1217bdd1243dSDimitry Andric   int PackedEpilogOffset = HasEpilogs ?
1218bdd1243dSDimitry Andric       checkARM64PackedEpilog(streamer, info, &Seg, PrologCodeBytes) : -1;
1219bdd1243dSDimitry Andric 
1220bdd1243dSDimitry Andric   // TODO:
1221bdd1243dSDimitry Andric   // 1. Enable packed unwind info (.pdata only) for multi-segment functions.
1222bdd1243dSDimitry Andric   // 2. Emit packed unwind info (.pdata only) for segments that have neithor
1223bdd1243dSDimitry Andric   //    prolog nor epilog.
1224bdd1243dSDimitry Andric   if (info->Segments.size() == 1 && PackedEpilogOffset >= 0 &&
1225bdd1243dSDimitry Andric       uint32_t(PackedEpilogOffset) < PrologCodeBytes &&
1226bdd1243dSDimitry Andric       !info->HandlesExceptions && SegLength <= 0x7ff && TryPacked) {
1227bdd1243dSDimitry Andric     // Matching prolog/epilog and no exception handlers; check if the
1228bdd1243dSDimitry Andric     // prolog matches the patterns that can be described by the packed
1229bdd1243dSDimitry Andric     // format.
1230bdd1243dSDimitry Andric 
1231bdd1243dSDimitry Andric     // info->Symbol was already set even if we didn't actually write any
1232bdd1243dSDimitry Andric     // unwind info there. Keep using that as indicator that this unwind
1233bdd1243dSDimitry Andric     // info has been generated already.
1234bdd1243dSDimitry Andric     if (tryARM64PackedUnwind(info, SegLength, PackedEpilogOffset))
1235bdd1243dSDimitry Andric       return;
1236bdd1243dSDimitry Andric   }
1237bdd1243dSDimitry Andric 
1238bdd1243dSDimitry Andric   // If the prolog is not in this segment, we need to emit an end_c, which takes
1239bdd1243dSDimitry Andric   // 1 byte, before prolog unwind ops.
1240bdd1243dSDimitry Andric   if (!HasProlog) {
1241bdd1243dSDimitry Andric     PrologCodeBytes += 1;
1242bdd1243dSDimitry Andric     if (PackedEpilogOffset >= 0)
1243bdd1243dSDimitry Andric       PackedEpilogOffset += 1;
1244bdd1243dSDimitry Andric     // If a segment has neither prolog nor epilog, "With full .xdata record,
1245bdd1243dSDimitry Andric     // Epilog Count = 1. Epilog Start Index points to end_c."
1246bdd1243dSDimitry Andric     // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
1247bdd1243dSDimitry Andric     // TODO: We can remove this if testing shows zero epilog scope is ok with
1248bdd1243dSDimitry Andric     //       MS unwinder.
1249bdd1243dSDimitry Andric     if (!HasEpilogs)
1250bdd1243dSDimitry Andric       // Pack the fake epilog into phantom prolog.
1251bdd1243dSDimitry Andric       PackedEpilogOffset = 0;
1252bdd1243dSDimitry Andric   }
1253bdd1243dSDimitry Andric 
1254bdd1243dSDimitry Andric   uint32_t TotalCodeBytes = PrologCodeBytes;
1255bdd1243dSDimitry Andric 
1256bdd1243dSDimitry Andric   // Process epilogs.
1257bdd1243dSDimitry Andric   MapVector<MCSymbol *, uint32_t> EpilogInfo;
1258bdd1243dSDimitry Andric   ARM64ProcessEpilogs(info, &Seg, TotalCodeBytes, EpilogInfo);
1259bdd1243dSDimitry Andric 
1260bdd1243dSDimitry Andric   // Code Words, Epilog count, E, X, Vers, Function Length
1261bdd1243dSDimitry Andric   uint32_t row1 = 0x0;
1262bdd1243dSDimitry Andric   uint32_t CodeWords = TotalCodeBytes / 4;
1263bdd1243dSDimitry Andric   uint32_t CodeWordsMod = TotalCodeBytes % 4;
1264bdd1243dSDimitry Andric   if (CodeWordsMod)
1265bdd1243dSDimitry Andric     CodeWords++;
1266bdd1243dSDimitry Andric   uint32_t EpilogCount =
1267bdd1243dSDimitry Andric       PackedEpilogOffset >= 0 ? PackedEpilogOffset : Seg.Epilogs.size();
1268bdd1243dSDimitry Andric   bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124;
1269bdd1243dSDimitry Andric   if (!ExtensionWord) {
1270bdd1243dSDimitry Andric     row1 |= (EpilogCount & 0x1F) << 22;
1271bdd1243dSDimitry Andric     row1 |= (CodeWords & 0x1F) << 27;
1272bdd1243dSDimitry Andric   }
1273bdd1243dSDimitry Andric   if (info->HandlesExceptions) // X
1274bdd1243dSDimitry Andric     row1 |= 1 << 20;
1275bdd1243dSDimitry Andric   if (PackedEpilogOffset >= 0) // E
1276bdd1243dSDimitry Andric     row1 |= 1 << 21;
1277bdd1243dSDimitry Andric   row1 |= SegLength & 0x3FFFF;
1278bdd1243dSDimitry Andric   streamer.emitInt32(row1);
1279bdd1243dSDimitry Andric 
1280bdd1243dSDimitry Andric   // Extended Code Words, Extended Epilog Count
1281bdd1243dSDimitry Andric   if (ExtensionWord) {
1282bdd1243dSDimitry Andric     // FIXME: We should be able to split unwind info into multiple sections.
1283bdd1243dSDimitry Andric     if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
1284bdd1243dSDimitry Andric       report_fatal_error(
1285bdd1243dSDimitry Andric           "SEH unwind data splitting is only implemented for large functions, "
1286bdd1243dSDimitry Andric           "cases of too many code words or too many epilogs will be done "
1287bdd1243dSDimitry Andric           "later");
1288bdd1243dSDimitry Andric     uint32_t row2 = 0x0;
1289bdd1243dSDimitry Andric     row2 |= (CodeWords & 0xFF) << 16;
1290bdd1243dSDimitry Andric     row2 |= (EpilogCount & 0xFFFF);
1291bdd1243dSDimitry Andric     streamer.emitInt32(row2);
1292bdd1243dSDimitry Andric   }
1293bdd1243dSDimitry Andric 
1294bdd1243dSDimitry Andric   if (PackedEpilogOffset < 0) {
1295bdd1243dSDimitry Andric     // Epilog Start Index, Epilog Start Offset
1296bdd1243dSDimitry Andric     for (auto &I : EpilogInfo) {
1297bdd1243dSDimitry Andric       MCSymbol *EpilogStart = I.first;
1298bdd1243dSDimitry Andric       uint32_t EpilogIndex = I.second;
1299bdd1243dSDimitry Andric       // Epilog offset within the Segment.
1300bdd1243dSDimitry Andric       uint32_t EpilogOffset = (uint32_t)(Seg.Epilogs[EpilogStart] - Seg.Offset);
1301bdd1243dSDimitry Andric       if (EpilogOffset)
1302bdd1243dSDimitry Andric         EpilogOffset /= 4;
1303bdd1243dSDimitry Andric       uint32_t row3 = EpilogOffset;
1304bdd1243dSDimitry Andric       row3 |= (EpilogIndex & 0x3FF) << 22;
1305bdd1243dSDimitry Andric       streamer.emitInt32(row3);
1306bdd1243dSDimitry Andric     }
1307bdd1243dSDimitry Andric   }
1308bdd1243dSDimitry Andric 
1309bdd1243dSDimitry Andric   // Note that even for segments that have no prolog, we still need to emit
1310bdd1243dSDimitry Andric   // prolog unwinding opcodes so that the unwinder knows how to unwind from
1311bdd1243dSDimitry Andric   // such a segment.
1312bdd1243dSDimitry Andric   // The end_c opcode at the start indicates to the unwinder that the actual
1313bdd1243dSDimitry Andric   // prolog is outside of the current segment, and the unwinder shouldn't try
1314bdd1243dSDimitry Andric   // to check for unwinding from a partial prolog.
1315bdd1243dSDimitry Andric   if (!HasProlog)
1316bdd1243dSDimitry Andric     // Emit an end_c.
1317bdd1243dSDimitry Andric     streamer.emitInt8((uint8_t)0xE5);
1318bdd1243dSDimitry Andric 
1319bdd1243dSDimitry Andric   // Emit prolog unwind instructions (in reverse order).
1320bdd1243dSDimitry Andric   for (auto Inst : llvm::reverse(info->Instructions))
1321bdd1243dSDimitry Andric     ARM64EmitUnwindCode(streamer, Inst);
1322bdd1243dSDimitry Andric 
1323bdd1243dSDimitry Andric   // Emit epilog unwind instructions
1324bdd1243dSDimitry Andric   for (auto &I : Seg.Epilogs) {
1325bdd1243dSDimitry Andric     auto &EpilogInstrs = info->EpilogMap[I.first].Instructions;
1326bdd1243dSDimitry Andric     for (const WinEH::Instruction &inst : EpilogInstrs)
1327bdd1243dSDimitry Andric       ARM64EmitUnwindCode(streamer, inst);
1328bdd1243dSDimitry Andric   }
1329bdd1243dSDimitry Andric 
1330bdd1243dSDimitry Andric   int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
1331bdd1243dSDimitry Andric   assert(BytesMod >= 0);
1332bdd1243dSDimitry Andric   for (int i = 0; i < BytesMod; i++)
1333bdd1243dSDimitry Andric     streamer.emitInt8(0xE3);
1334bdd1243dSDimitry Andric 
1335bdd1243dSDimitry Andric   if (info->HandlesExceptions)
1336bdd1243dSDimitry Andric     streamer.emitValue(
1337bdd1243dSDimitry Andric         MCSymbolRefExpr::create(info->ExceptionHandler,
1338bdd1243dSDimitry Andric                                 MCSymbolRefExpr::VK_COFF_IMGREL32, context),
1339bdd1243dSDimitry Andric         4);
1340bdd1243dSDimitry Andric }
1341bdd1243dSDimitry Andric 
13420b57cec5SDimitry Andric // Populate the .xdata section.  The format of .xdata on ARM64 is documented at
13430b57cec5SDimitry Andric // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
1344e8d8bef9SDimitry Andric static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
1345e8d8bef9SDimitry Andric                                 bool TryPacked = true) {
13460b57cec5SDimitry Andric   // If this UNWIND_INFO already has a symbol, it's already been emitted.
13470b57cec5SDimitry Andric   if (info->Symbol)
13480b57cec5SDimitry Andric     return;
1349e8d8bef9SDimitry Andric   // If there's no unwind info here (not even a terminating UOP_End), the
1350e8d8bef9SDimitry Andric   // unwind info is considered bogus and skipped. If this was done in
1351e8d8bef9SDimitry Andric   // response to an explicit .seh_handlerdata, the associated trailing
1352e8d8bef9SDimitry Andric   // handler data is left orphaned in the xdata section.
1353e8d8bef9SDimitry Andric   if (info->empty()) {
1354e8d8bef9SDimitry Andric     info->EmitAttempted = true;
1355e8d8bef9SDimitry Andric     return;
1356e8d8bef9SDimitry Andric   }
1357e8d8bef9SDimitry Andric   if (info->EmitAttempted) {
1358e8d8bef9SDimitry Andric     // If we tried to emit unwind info before (due to an explicit
1359e8d8bef9SDimitry Andric     // .seh_handlerdata directive), but skipped it (because there was no
1360e8d8bef9SDimitry Andric     // valid information to emit at the time), and it later got valid unwind
1361e8d8bef9SDimitry Andric     // opcodes, we can't emit it here, because the trailing handler data
1362e8d8bef9SDimitry Andric     // was already emitted elsewhere in the xdata section.
1363e8d8bef9SDimitry Andric     streamer.getContext().reportError(
1364e8d8bef9SDimitry Andric         SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() +
1365e8d8bef9SDimitry Andric                      " skipped due to no unwind info at the time "
1366e8d8bef9SDimitry Andric                      "(.seh_handlerdata too early?), but the function later "
1367e8d8bef9SDimitry Andric                      "did get unwind info that can't be emitted");
1368e8d8bef9SDimitry Andric     return;
1369e8d8bef9SDimitry Andric   }
1370e8d8bef9SDimitry Andric 
137181ad6265SDimitry Andric   simplifyARM64Opcodes(info->Instructions, false);
1372e8d8bef9SDimitry Andric   for (auto &I : info->EpilogMap)
137381ad6265SDimitry Andric     simplifyARM64Opcodes(I.second.Instructions, true);
13740b57cec5SDimitry Andric 
13750b57cec5SDimitry Andric   int64_t RawFuncLength;
13760b57cec5SDimitry Andric   if (!info->FuncletOrFuncEnd) {
1377e8d8bef9SDimitry Andric     report_fatal_error("FuncletOrFuncEnd not set");
13780b57cec5SDimitry Andric   } else {
13790b57cec5SDimitry Andric     // FIXME: GetAbsDifference tries to compute the length of the function
13800b57cec5SDimitry Andric     // immediately, before the whole file is emitted, but in general
13810b57cec5SDimitry Andric     // that's impossible: the size in bytes of certain assembler directives
13820b57cec5SDimitry Andric     // like .align and .fill is not known until the whole file is parsed and
13830b57cec5SDimitry Andric     // relaxations are applied. Currently, GetAbsDifference fails with a fatal
13840b57cec5SDimitry Andric     // error in that case. (We mostly don't hit this because inline assembly
13850b57cec5SDimitry Andric     // specifying those directives is rare, and we don't normally try to
13860b57cec5SDimitry Andric     // align loops on AArch64.)
13870b57cec5SDimitry Andric     //
13880b57cec5SDimitry Andric     // There are two potential approaches to delaying the computation. One,
13890b57cec5SDimitry Andric     // we could emit something like ".word (endfunc-beginfunc)/4+0x10800000",
13900b57cec5SDimitry Andric     // as long as we have some conservative estimate we could use to prove
13910b57cec5SDimitry Andric     // that we don't need to split the unwind data. Emitting the constant
13920b57cec5SDimitry Andric     // is straightforward, but there's no existing code for estimating the
13930b57cec5SDimitry Andric     // size of the function.
13940b57cec5SDimitry Andric     //
13950b57cec5SDimitry Andric     // The other approach would be to use a dedicated, relaxable fragment,
13960b57cec5SDimitry Andric     // which could grow to accommodate splitting the unwind data if
13970b57cec5SDimitry Andric     // necessary. This is more straightforward, since it automatically works
13980b57cec5SDimitry Andric     // without any new infrastructure, and it's consistent with how we handle
13990b57cec5SDimitry Andric     // relaxation in other contexts.  But it would require some refactoring
14000b57cec5SDimitry Andric     // to move parts of the pdata/xdata emission into the implementation of
14010b57cec5SDimitry Andric     // a fragment. We could probably continue to encode the unwind codes
14020b57cec5SDimitry Andric     // here, but we'd have to emit the pdata, the xdata header, and the
14030b57cec5SDimitry Andric     // epilogue scopes later, since they depend on whether the we need to
14040b57cec5SDimitry Andric     // split the unwind data.
1405*bdb86d1aSDimitry Andric     //
1406*bdb86d1aSDimitry Andric     // If this is fixed, remove code in AArch64ISelLowering.cpp that
1407*bdb86d1aSDimitry Andric     // disables loop alignment on Windows.
14080b57cec5SDimitry Andric     RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd,
14090b57cec5SDimitry Andric                                      info->Begin);
14100b57cec5SDimitry Andric   }
14110b57cec5SDimitry Andric 
1412bdd1243dSDimitry Andric   ARM64FindSegmentsInFunction(streamer, info, RawFuncLength);
1413e8d8bef9SDimitry Andric 
1414bdd1243dSDimitry Andric   info->PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
1415bdd1243dSDimitry Andric   for (auto &S : info->Segments)
1416bdd1243dSDimitry Andric     ARM64EmitUnwindInfoForSegment(streamer, info, S, TryPacked);
1417e8d8bef9SDimitry Andric 
1418bdd1243dSDimitry Andric   // Clear prolog instructions after unwind info is emitted for all segments.
1419bdd1243dSDimitry Andric   info->Instructions.clear();
14200b57cec5SDimitry Andric }
14210b57cec5SDimitry Andric 
142281ad6265SDimitry Andric static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
142381ad6265SDimitry Andric   uint32_t Count = 0;
142481ad6265SDimitry Andric   for (const auto &I : Insns) {
142581ad6265SDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
142681ad6265SDimitry Andric     default:
142781ad6265SDimitry Andric       llvm_unreachable("Unsupported ARM unwind code");
142881ad6265SDimitry Andric     case Win64EH::UOP_AllocSmall:
142981ad6265SDimitry Andric       Count += 1;
143081ad6265SDimitry Andric       break;
143181ad6265SDimitry Andric     case Win64EH::UOP_AllocLarge:
143281ad6265SDimitry Andric       Count += 3;
143381ad6265SDimitry Andric       break;
143481ad6265SDimitry Andric     case Win64EH::UOP_AllocHuge:
143581ad6265SDimitry Andric       Count += 4;
143681ad6265SDimitry Andric       break;
143781ad6265SDimitry Andric     case Win64EH::UOP_WideAllocMedium:
143881ad6265SDimitry Andric       Count += 2;
143981ad6265SDimitry Andric       break;
144081ad6265SDimitry Andric     case Win64EH::UOP_WideAllocLarge:
144181ad6265SDimitry Andric       Count += 3;
144281ad6265SDimitry Andric       break;
144381ad6265SDimitry Andric     case Win64EH::UOP_WideAllocHuge:
144481ad6265SDimitry Andric       Count += 4;
144581ad6265SDimitry Andric       break;
144681ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegMask:
144781ad6265SDimitry Andric       Count += 2;
144881ad6265SDimitry Andric       break;
144981ad6265SDimitry Andric     case Win64EH::UOP_SaveSP:
145081ad6265SDimitry Andric       Count += 1;
145181ad6265SDimitry Andric       break;
145281ad6265SDimitry Andric     case Win64EH::UOP_SaveRegsR4R7LR:
145381ad6265SDimitry Andric       Count += 1;
145481ad6265SDimitry Andric       break;
145581ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegsR4R11LR:
145681ad6265SDimitry Andric       Count += 1;
145781ad6265SDimitry Andric       break;
145881ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD8D15:
145981ad6265SDimitry Andric       Count += 1;
146081ad6265SDimitry Andric       break;
146181ad6265SDimitry Andric     case Win64EH::UOP_SaveRegMask:
146281ad6265SDimitry Andric       Count += 2;
146381ad6265SDimitry Andric       break;
146481ad6265SDimitry Andric     case Win64EH::UOP_SaveLR:
146581ad6265SDimitry Andric       Count += 2;
146681ad6265SDimitry Andric       break;
146781ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD0D15:
146881ad6265SDimitry Andric       Count += 2;
146981ad6265SDimitry Andric       break;
147081ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD16D31:
147181ad6265SDimitry Andric       Count += 2;
147281ad6265SDimitry Andric       break;
147381ad6265SDimitry Andric     case Win64EH::UOP_Nop:
147481ad6265SDimitry Andric     case Win64EH::UOP_WideNop:
147581ad6265SDimitry Andric     case Win64EH::UOP_End:
147681ad6265SDimitry Andric     case Win64EH::UOP_EndNop:
147781ad6265SDimitry Andric     case Win64EH::UOP_WideEndNop:
147881ad6265SDimitry Andric       Count += 1;
147981ad6265SDimitry Andric       break;
148081ad6265SDimitry Andric     case Win64EH::UOP_Custom: {
148181ad6265SDimitry Andric       int J;
148281ad6265SDimitry Andric       for (J = 3; J > 0; J--)
148381ad6265SDimitry Andric         if (I.Offset & (0xffu << (8 * J)))
148481ad6265SDimitry Andric           break;
148581ad6265SDimitry Andric       Count += J + 1;
148681ad6265SDimitry Andric       break;
148781ad6265SDimitry Andric     }
148881ad6265SDimitry Andric     }
148981ad6265SDimitry Andric   }
149081ad6265SDimitry Andric   return Count;
149181ad6265SDimitry Andric }
149281ad6265SDimitry Andric 
149381ad6265SDimitry Andric static uint32_t ARMCountOfInstructionBytes(ArrayRef<WinEH::Instruction> Insns,
149481ad6265SDimitry Andric                                            bool *HasCustom = nullptr) {
149581ad6265SDimitry Andric   uint32_t Count = 0;
149681ad6265SDimitry Andric   for (const auto &I : Insns) {
149781ad6265SDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
149881ad6265SDimitry Andric     default:
149981ad6265SDimitry Andric       llvm_unreachable("Unsupported ARM unwind code");
150081ad6265SDimitry Andric     case Win64EH::UOP_AllocSmall:
150181ad6265SDimitry Andric     case Win64EH::UOP_AllocLarge:
150281ad6265SDimitry Andric     case Win64EH::UOP_AllocHuge:
150381ad6265SDimitry Andric       Count += 2;
150481ad6265SDimitry Andric       break;
150581ad6265SDimitry Andric     case Win64EH::UOP_WideAllocMedium:
150681ad6265SDimitry Andric     case Win64EH::UOP_WideAllocLarge:
150781ad6265SDimitry Andric     case Win64EH::UOP_WideAllocHuge:
150881ad6265SDimitry Andric       Count += 4;
150981ad6265SDimitry Andric       break;
151081ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegMask:
151181ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegsR4R11LR:
151281ad6265SDimitry Andric       Count += 4;
151381ad6265SDimitry Andric       break;
151481ad6265SDimitry Andric     case Win64EH::UOP_SaveSP:
151581ad6265SDimitry Andric       Count += 2;
151681ad6265SDimitry Andric       break;
151781ad6265SDimitry Andric     case Win64EH::UOP_SaveRegMask:
151881ad6265SDimitry Andric     case Win64EH::UOP_SaveRegsR4R7LR:
151981ad6265SDimitry Andric       Count += 2;
152081ad6265SDimitry Andric       break;
152181ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD8D15:
152281ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD0D15:
152381ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD16D31:
152481ad6265SDimitry Andric       Count += 4;
152581ad6265SDimitry Andric       break;
152681ad6265SDimitry Andric     case Win64EH::UOP_SaveLR:
152781ad6265SDimitry Andric       Count += 4;
152881ad6265SDimitry Andric       break;
152981ad6265SDimitry Andric     case Win64EH::UOP_Nop:
153081ad6265SDimitry Andric     case Win64EH::UOP_EndNop:
153181ad6265SDimitry Andric       Count += 2;
153281ad6265SDimitry Andric       break;
153381ad6265SDimitry Andric     case Win64EH::UOP_WideNop:
153481ad6265SDimitry Andric     case Win64EH::UOP_WideEndNop:
153581ad6265SDimitry Andric       Count += 4;
153681ad6265SDimitry Andric       break;
153781ad6265SDimitry Andric     case Win64EH::UOP_End:
153881ad6265SDimitry Andric       // This doesn't map to any instruction
153981ad6265SDimitry Andric       break;
154081ad6265SDimitry Andric     case Win64EH::UOP_Custom:
154181ad6265SDimitry Andric       // We can't reason about what instructions this maps to; return a
154281ad6265SDimitry Andric       // phony number to make sure we don't accidentally do epilog packing.
154381ad6265SDimitry Andric       Count += 1000;
154481ad6265SDimitry Andric       if (HasCustom)
154581ad6265SDimitry Andric         *HasCustom = true;
154681ad6265SDimitry Andric       break;
154781ad6265SDimitry Andric     }
154881ad6265SDimitry Andric   }
154981ad6265SDimitry Andric   return Count;
155081ad6265SDimitry Andric }
155181ad6265SDimitry Andric 
155281ad6265SDimitry Andric static void checkARMInstructions(MCStreamer &Streamer,
155381ad6265SDimitry Andric                                  ArrayRef<WinEH::Instruction> Insns,
155481ad6265SDimitry Andric                                  const MCSymbol *Begin, const MCSymbol *End,
155581ad6265SDimitry Andric                                  StringRef Name, StringRef Type) {
155681ad6265SDimitry Andric   if (!End)
155781ad6265SDimitry Andric     return;
1558bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDistance =
155981ad6265SDimitry Andric       GetOptionalAbsDifference(Streamer, End, Begin);
156081ad6265SDimitry Andric   if (!MaybeDistance)
156181ad6265SDimitry Andric     return;
156281ad6265SDimitry Andric   uint32_t Distance = (uint32_t)*MaybeDistance;
156381ad6265SDimitry Andric   bool HasCustom = false;
156481ad6265SDimitry Andric   uint32_t InstructionBytes = ARMCountOfInstructionBytes(Insns, &HasCustom);
156581ad6265SDimitry Andric   if (HasCustom)
156681ad6265SDimitry Andric     return;
156781ad6265SDimitry Andric   if (Distance != InstructionBytes) {
156881ad6265SDimitry Andric     Streamer.getContext().reportError(
156981ad6265SDimitry Andric         SMLoc(), "Incorrect size for " + Name + " " + Type + ": " +
157081ad6265SDimitry Andric                      Twine(Distance) +
157181ad6265SDimitry Andric                      " bytes of instructions in range, but .seh directives "
157281ad6265SDimitry Andric                      "corresponding to " +
157381ad6265SDimitry Andric                      Twine(InstructionBytes) + " bytes\n");
157481ad6265SDimitry Andric   }
157581ad6265SDimitry Andric }
157681ad6265SDimitry Andric 
157781ad6265SDimitry Andric static bool isARMTerminator(const WinEH::Instruction &inst) {
157881ad6265SDimitry Andric   switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
157981ad6265SDimitry Andric   case Win64EH::UOP_End:
158081ad6265SDimitry Andric   case Win64EH::UOP_EndNop:
158181ad6265SDimitry Andric   case Win64EH::UOP_WideEndNop:
158281ad6265SDimitry Andric     return true;
158381ad6265SDimitry Andric   default:
158481ad6265SDimitry Andric     return false;
158581ad6265SDimitry Andric   }
158681ad6265SDimitry Andric }
158781ad6265SDimitry Andric 
158881ad6265SDimitry Andric // Unwind opcode encodings and restrictions are documented at
158981ad6265SDimitry Andric // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
159081ad6265SDimitry Andric static void ARMEmitUnwindCode(MCStreamer &streamer,
159181ad6265SDimitry Andric                               const WinEH::Instruction &inst) {
159281ad6265SDimitry Andric   uint32_t w, lr;
159381ad6265SDimitry Andric   int i;
159481ad6265SDimitry Andric   switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
159581ad6265SDimitry Andric   default:
159681ad6265SDimitry Andric     llvm_unreachable("Unsupported ARM unwind code");
159781ad6265SDimitry Andric   case Win64EH::UOP_AllocSmall:
159881ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
159981ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0x7f);
160081ad6265SDimitry Andric     streamer.emitInt8(inst.Offset / 4);
160181ad6265SDimitry Andric     break;
160281ad6265SDimitry Andric   case Win64EH::UOP_WideSaveRegMask:
160381ad6265SDimitry Andric     assert((inst.Register & ~0x5fff) == 0);
160481ad6265SDimitry Andric     lr = (inst.Register >> 14) & 1;
160581ad6265SDimitry Andric     w = 0x8000 | (inst.Register & 0x1fff) | (lr << 13);
160681ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
160781ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
160881ad6265SDimitry Andric     break;
160981ad6265SDimitry Andric   case Win64EH::UOP_SaveSP:
161081ad6265SDimitry Andric     assert(inst.Register <= 0x0f);
161181ad6265SDimitry Andric     streamer.emitInt8(0xc0 | inst.Register);
161281ad6265SDimitry Andric     break;
161381ad6265SDimitry Andric   case Win64EH::UOP_SaveRegsR4R7LR:
161481ad6265SDimitry Andric     assert(inst.Register >= 4 && inst.Register <= 7);
161581ad6265SDimitry Andric     assert(inst.Offset <= 1);
161681ad6265SDimitry Andric     streamer.emitInt8(0xd0 | (inst.Register - 4) | (inst.Offset << 2));
161781ad6265SDimitry Andric     break;
161881ad6265SDimitry Andric   case Win64EH::UOP_WideSaveRegsR4R11LR:
161981ad6265SDimitry Andric     assert(inst.Register >= 8 && inst.Register <= 11);
162081ad6265SDimitry Andric     assert(inst.Offset <= 1);
162181ad6265SDimitry Andric     streamer.emitInt8(0xd8 | (inst.Register - 8) | (inst.Offset << 2));
162281ad6265SDimitry Andric     break;
162381ad6265SDimitry Andric   case Win64EH::UOP_SaveFRegD8D15:
162481ad6265SDimitry Andric     assert(inst.Register >= 8 && inst.Register <= 15);
162581ad6265SDimitry Andric     streamer.emitInt8(0xe0 | (inst.Register - 8));
162681ad6265SDimitry Andric     break;
162781ad6265SDimitry Andric   case Win64EH::UOP_WideAllocMedium:
162881ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
162981ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0x3ff);
163081ad6265SDimitry Andric     w = 0xe800 | (inst.Offset / 4);
163181ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
163281ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
163381ad6265SDimitry Andric     break;
163481ad6265SDimitry Andric   case Win64EH::UOP_SaveRegMask:
163581ad6265SDimitry Andric     assert((inst.Register & ~0x40ff) == 0);
163681ad6265SDimitry Andric     lr = (inst.Register >> 14) & 1;
163781ad6265SDimitry Andric     w = 0xec00 | (inst.Register & 0x0ff) | (lr << 8);
163881ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
163981ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
164081ad6265SDimitry Andric     break;
164181ad6265SDimitry Andric   case Win64EH::UOP_SaveLR:
164281ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
164381ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0x0f);
164481ad6265SDimitry Andric     streamer.emitInt8(0xef);
164581ad6265SDimitry Andric     streamer.emitInt8(inst.Offset / 4);
164681ad6265SDimitry Andric     break;
164781ad6265SDimitry Andric   case Win64EH::UOP_SaveFRegD0D15:
164881ad6265SDimitry Andric     assert(inst.Register <= 15);
164981ad6265SDimitry Andric     assert(inst.Offset <= 15);
165081ad6265SDimitry Andric     assert(inst.Register <= inst.Offset);
165181ad6265SDimitry Andric     streamer.emitInt8(0xf5);
165281ad6265SDimitry Andric     streamer.emitInt8((inst.Register << 4) | inst.Offset);
165381ad6265SDimitry Andric     break;
165481ad6265SDimitry Andric   case Win64EH::UOP_SaveFRegD16D31:
165581ad6265SDimitry Andric     assert(inst.Register >= 16 && inst.Register <= 31);
165681ad6265SDimitry Andric     assert(inst.Offset >= 16 && inst.Offset <= 31);
165781ad6265SDimitry Andric     assert(inst.Register <= inst.Offset);
165881ad6265SDimitry Andric     streamer.emitInt8(0xf6);
165981ad6265SDimitry Andric     streamer.emitInt8(((inst.Register - 16) << 4) | (inst.Offset - 16));
166081ad6265SDimitry Andric     break;
166181ad6265SDimitry Andric   case Win64EH::UOP_AllocLarge:
166281ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
166381ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffff);
166481ad6265SDimitry Andric     w = inst.Offset / 4;
166581ad6265SDimitry Andric     streamer.emitInt8(0xf7);
166681ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
166781ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
166881ad6265SDimitry Andric     break;
166981ad6265SDimitry Andric   case Win64EH::UOP_AllocHuge:
167081ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
167181ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffffff);
167281ad6265SDimitry Andric     w = inst.Offset / 4;
167381ad6265SDimitry Andric     streamer.emitInt8(0xf8);
167481ad6265SDimitry Andric     streamer.emitInt8((w >> 16) & 0xff);
167581ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
167681ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
167781ad6265SDimitry Andric     break;
167881ad6265SDimitry Andric   case Win64EH::UOP_WideAllocLarge:
167981ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
168081ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffff);
168181ad6265SDimitry Andric     w = inst.Offset / 4;
168281ad6265SDimitry Andric     streamer.emitInt8(0xf9);
168381ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
168481ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
168581ad6265SDimitry Andric     break;
168681ad6265SDimitry Andric   case Win64EH::UOP_WideAllocHuge:
168781ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
168881ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffffff);
168981ad6265SDimitry Andric     w = inst.Offset / 4;
169081ad6265SDimitry Andric     streamer.emitInt8(0xfa);
169181ad6265SDimitry Andric     streamer.emitInt8((w >> 16) & 0xff);
169281ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
169381ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
169481ad6265SDimitry Andric     break;
169581ad6265SDimitry Andric   case Win64EH::UOP_Nop:
169681ad6265SDimitry Andric     streamer.emitInt8(0xfb);
169781ad6265SDimitry Andric     break;
169881ad6265SDimitry Andric   case Win64EH::UOP_WideNop:
169981ad6265SDimitry Andric     streamer.emitInt8(0xfc);
170081ad6265SDimitry Andric     break;
170181ad6265SDimitry Andric   case Win64EH::UOP_EndNop:
170281ad6265SDimitry Andric     streamer.emitInt8(0xfd);
170381ad6265SDimitry Andric     break;
170481ad6265SDimitry Andric   case Win64EH::UOP_WideEndNop:
170581ad6265SDimitry Andric     streamer.emitInt8(0xfe);
170681ad6265SDimitry Andric     break;
170781ad6265SDimitry Andric   case Win64EH::UOP_End:
170881ad6265SDimitry Andric     streamer.emitInt8(0xff);
170981ad6265SDimitry Andric     break;
171081ad6265SDimitry Andric   case Win64EH::UOP_Custom:
171181ad6265SDimitry Andric     for (i = 3; i > 0; i--)
171281ad6265SDimitry Andric       if (inst.Offset & (0xffu << (8 * i)))
171381ad6265SDimitry Andric         break;
171481ad6265SDimitry Andric     for (; i >= 0; i--)
171581ad6265SDimitry Andric       streamer.emitInt8((inst.Offset >> (8 * i)) & 0xff);
171681ad6265SDimitry Andric     break;
171781ad6265SDimitry Andric   }
171881ad6265SDimitry Andric }
171981ad6265SDimitry Andric 
172081ad6265SDimitry Andric // Check if an epilog exists as a subset of the end of a prolog (backwards).
172181ad6265SDimitry Andric // An epilog may end with one out of three different end opcodes; if this
172281ad6265SDimitry Andric // is the first epilog that shares opcodes with the prolog, we can tolerate
172381ad6265SDimitry Andric // that this opcode differs (and the caller will update the prolog to use
172481ad6265SDimitry Andric // the same end opcode as the epilog). If another epilog already shares
172581ad6265SDimitry Andric // opcodes with the prolog, the ending opcode must be a strict match.
172681ad6265SDimitry Andric static int getARMOffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
172781ad6265SDimitry Andric                                 const std::vector<WinEH::Instruction> &Epilog,
172881ad6265SDimitry Andric                                 bool CanTweakProlog) {
172981ad6265SDimitry Andric   // Can't find an epilog as a subset if it is longer than the prolog.
173081ad6265SDimitry Andric   if (Epilog.size() > Prolog.size())
173181ad6265SDimitry Andric     return -1;
173281ad6265SDimitry Andric 
173381ad6265SDimitry Andric   // Check that the epilog actually is a perfect match for the end (backwrds)
173481ad6265SDimitry Andric   // of the prolog.
173581ad6265SDimitry Andric   // If we can adjust the prolog afterwards, don't check that the end opcodes
173681ad6265SDimitry Andric   // match.
173781ad6265SDimitry Andric   int EndIdx = CanTweakProlog ? 1 : 0;
173881ad6265SDimitry Andric   for (int I = Epilog.size() - 1; I >= EndIdx; I--) {
173981ad6265SDimitry Andric     // TODO: Could also allow minor mismatches, e.g. "add sp, #16" vs
174081ad6265SDimitry Andric     // "push {r0-r3}".
174181ad6265SDimitry Andric     if (Prolog[I] != Epilog[Epilog.size() - 1 - I])
174281ad6265SDimitry Andric       return -1;
174381ad6265SDimitry Andric   }
174481ad6265SDimitry Andric 
174581ad6265SDimitry Andric   if (CanTweakProlog) {
174681ad6265SDimitry Andric     // Check that both prolog and epilog end with an expected end opcode.
174781ad6265SDimitry Andric     if (Prolog.front().Operation != Win64EH::UOP_End)
174881ad6265SDimitry Andric       return -1;
174981ad6265SDimitry Andric     if (Epilog.back().Operation != Win64EH::UOP_End &&
175081ad6265SDimitry Andric         Epilog.back().Operation != Win64EH::UOP_EndNop &&
175181ad6265SDimitry Andric         Epilog.back().Operation != Win64EH::UOP_WideEndNop)
175281ad6265SDimitry Andric       return -1;
175381ad6265SDimitry Andric   }
175481ad6265SDimitry Andric 
175581ad6265SDimitry Andric   // If the epilog was a subset of the prolog, find its offset.
175681ad6265SDimitry Andric   if (Epilog.size() == Prolog.size())
175781ad6265SDimitry Andric     return 0;
175881ad6265SDimitry Andric   return ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction>(
175981ad6265SDimitry Andric       &Prolog[Epilog.size()], Prolog.size() - Epilog.size()));
176081ad6265SDimitry Andric }
176181ad6265SDimitry Andric 
176281ad6265SDimitry Andric static int checkARMPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
176381ad6265SDimitry Andric                                 int PrologCodeBytes) {
176481ad6265SDimitry Andric   // Can only pack if there's one single epilog
176581ad6265SDimitry Andric   if (info->EpilogMap.size() != 1)
176681ad6265SDimitry Andric     return -1;
176781ad6265SDimitry Andric 
176881ad6265SDimitry Andric   const WinEH::FrameInfo::Epilog &EpilogInfo = info->EpilogMap.begin()->second;
176981ad6265SDimitry Andric   // Can only pack if the epilog is unconditional
177081ad6265SDimitry Andric   if (EpilogInfo.Condition != 0xe) // ARMCC::AL
177181ad6265SDimitry Andric     return -1;
177281ad6265SDimitry Andric 
177381ad6265SDimitry Andric   const std::vector<WinEH::Instruction> &Epilog = EpilogInfo.Instructions;
177481ad6265SDimitry Andric   // Make sure we have at least the trailing end opcode
177581ad6265SDimitry Andric   if (info->Instructions.empty() || Epilog.empty())
177681ad6265SDimitry Andric     return -1;
177781ad6265SDimitry Andric 
177881ad6265SDimitry Andric   // Check that the epilog actually is at the very end of the function,
177981ad6265SDimitry Andric   // otherwise it can't be packed.
1780bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDistance = GetOptionalAbsDifference(
178181ad6265SDimitry Andric       streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first);
178281ad6265SDimitry Andric   if (!MaybeDistance)
178381ad6265SDimitry Andric     return -1;
178481ad6265SDimitry Andric   uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance;
178581ad6265SDimitry Andric   uint32_t InstructionBytes = ARMCountOfInstructionBytes(Epilog);
178681ad6265SDimitry Andric   if (DistanceFromEnd != InstructionBytes)
178781ad6265SDimitry Andric     return -1;
178881ad6265SDimitry Andric 
178981ad6265SDimitry Andric   int RetVal = -1;
179081ad6265SDimitry Andric   // Even if we don't end up sharing opcodes with the prolog, we can still
179181ad6265SDimitry Andric   // write the offset as a packed offset, if the single epilog is located at
179281ad6265SDimitry Andric   // the end of the function and the offset (pointing after the prolog) fits
179381ad6265SDimitry Andric   // as a packed offset.
179481ad6265SDimitry Andric   if (PrologCodeBytes <= 31 &&
179581ad6265SDimitry Andric       PrologCodeBytes + ARMCountOfUnwindCodes(Epilog) <= 63)
179681ad6265SDimitry Andric     RetVal = PrologCodeBytes;
179781ad6265SDimitry Andric 
179881ad6265SDimitry Andric   int Offset =
179981ad6265SDimitry Andric       getARMOffsetInProlog(info->Instructions, Epilog, /*CanTweakProlog=*/true);
180081ad6265SDimitry Andric   if (Offset < 0)
180181ad6265SDimitry Andric     return RetVal;
180281ad6265SDimitry Andric 
180381ad6265SDimitry Andric   // Check that the offset and prolog size fits in the first word; it's
180481ad6265SDimitry Andric   // unclear whether the epilog count in the extension word can be taken
180581ad6265SDimitry Andric   // as packed epilog offset.
180681ad6265SDimitry Andric   if (Offset > 31 || PrologCodeBytes > 63)
180781ad6265SDimitry Andric     return RetVal;
180881ad6265SDimitry Andric 
180981ad6265SDimitry Andric   // Replace the regular end opcode of the prolog with the one from the
181081ad6265SDimitry Andric   // epilog.
181181ad6265SDimitry Andric   info->Instructions.front() = Epilog.back();
181281ad6265SDimitry Andric 
181381ad6265SDimitry Andric   // As we choose to express the epilog as part of the prolog, remove the
181481ad6265SDimitry Andric   // epilog from the map, so we don't try to emit its opcodes.
181581ad6265SDimitry Andric   info->EpilogMap.clear();
181681ad6265SDimitry Andric   return Offset;
181781ad6265SDimitry Andric }
181881ad6265SDimitry Andric 
181981ad6265SDimitry Andric static bool parseRegMask(unsigned Mask, bool &HasLR, bool &HasR11,
182081ad6265SDimitry Andric                          unsigned &Folded, int &IntRegs) {
182181ad6265SDimitry Andric   if (Mask & (1 << 14)) {
182281ad6265SDimitry Andric     HasLR = true;
182381ad6265SDimitry Andric     Mask &= ~(1 << 14);
182481ad6265SDimitry Andric   }
182581ad6265SDimitry Andric   if (Mask & (1 << 11)) {
182681ad6265SDimitry Andric     HasR11 = true;
182781ad6265SDimitry Andric     Mask &= ~(1 << 11);
182881ad6265SDimitry Andric   }
182981ad6265SDimitry Andric   Folded = 0;
183081ad6265SDimitry Andric   IntRegs = -1;
183181ad6265SDimitry Andric   if (!Mask)
183281ad6265SDimitry Andric     return true;
183381ad6265SDimitry Andric   int First = 0;
183481ad6265SDimitry Andric   // Shift right until we have the bits at the bottom
183581ad6265SDimitry Andric   while ((Mask & 1) == 0) {
183681ad6265SDimitry Andric     First++;
183781ad6265SDimitry Andric     Mask >>= 1;
183881ad6265SDimitry Andric   }
183981ad6265SDimitry Andric   if ((Mask & (Mask + 1)) != 0)
184081ad6265SDimitry Andric     return false; // Not a consecutive series of bits? Can't be packed.
184181ad6265SDimitry Andric   // Count the bits
184281ad6265SDimitry Andric   int N = 0;
184381ad6265SDimitry Andric   while (Mask & (1 << N))
184481ad6265SDimitry Andric     N++;
184581ad6265SDimitry Andric   if (First < 4) {
184681ad6265SDimitry Andric     if (First + N < 4)
184781ad6265SDimitry Andric       return false;
184881ad6265SDimitry Andric     Folded = 4 - First;
184981ad6265SDimitry Andric     N -= Folded;
185081ad6265SDimitry Andric     First = 4;
185181ad6265SDimitry Andric   }
185281ad6265SDimitry Andric   if (First > 4)
185381ad6265SDimitry Andric     return false; // Can't be packed
185481ad6265SDimitry Andric   if (N >= 1)
185581ad6265SDimitry Andric     IntRegs = N - 1;
185681ad6265SDimitry Andric   return true;
185781ad6265SDimitry Andric }
185881ad6265SDimitry Andric 
185981ad6265SDimitry Andric static bool tryARMPackedUnwind(MCStreamer &streamer, WinEH::FrameInfo *info,
186081ad6265SDimitry Andric                                uint32_t FuncLength) {
186181ad6265SDimitry Andric   int Step = 0;
186281ad6265SDimitry Andric   bool Homing = false;
186381ad6265SDimitry Andric   bool HasR11 = false;
186481ad6265SDimitry Andric   bool HasChain = false;
186581ad6265SDimitry Andric   bool HasLR = false;
186681ad6265SDimitry Andric   int IntRegs = -1;   // r4 - r(4+N)
186781ad6265SDimitry Andric   int FloatRegs = -1; // d8 - d(8+N)
186881ad6265SDimitry Andric   unsigned PF = 0;    // Number of extra pushed registers
186981ad6265SDimitry Andric   unsigned StackAdjust = 0;
187081ad6265SDimitry Andric   // Iterate over the prolog and check that all opcodes exactly match
187181ad6265SDimitry Andric   // the canonical order and form.
187281ad6265SDimitry Andric   for (const WinEH::Instruction &Inst : info->Instructions) {
187381ad6265SDimitry Andric     switch (Inst.Operation) {
187481ad6265SDimitry Andric     default:
187581ad6265SDimitry Andric       llvm_unreachable("Unsupported ARM unwind code");
187681ad6265SDimitry Andric     case Win64EH::UOP_Custom:
187781ad6265SDimitry Andric     case Win64EH::UOP_AllocLarge:
187881ad6265SDimitry Andric     case Win64EH::UOP_AllocHuge:
187981ad6265SDimitry Andric     case Win64EH::UOP_WideAllocLarge:
188081ad6265SDimitry Andric     case Win64EH::UOP_WideAllocHuge:
188181ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD0D15:
188281ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD16D31:
188381ad6265SDimitry Andric       // Can't be packed
188481ad6265SDimitry Andric       return false;
188581ad6265SDimitry Andric     case Win64EH::UOP_SaveSP:
188681ad6265SDimitry Andric       // Can't be packed; we can't rely on restoring sp from r11 when
188781ad6265SDimitry Andric       // unwinding a packed prologue.
188881ad6265SDimitry Andric       return false;
188981ad6265SDimitry Andric     case Win64EH::UOP_SaveLR:
189081ad6265SDimitry Andric       // Can't be present in a packed prologue
189181ad6265SDimitry Andric       return false;
189281ad6265SDimitry Andric 
189381ad6265SDimitry Andric     case Win64EH::UOP_End:
189481ad6265SDimitry Andric     case Win64EH::UOP_EndNop:
189581ad6265SDimitry Andric     case Win64EH::UOP_WideEndNop:
189681ad6265SDimitry Andric       if (Step != 0)
189781ad6265SDimitry Andric         return false;
189881ad6265SDimitry Andric       Step = 1;
189981ad6265SDimitry Andric       break;
190081ad6265SDimitry Andric 
190181ad6265SDimitry Andric     case Win64EH::UOP_SaveRegsR4R7LR:
190281ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegsR4R11LR:
190381ad6265SDimitry Andric       // push {r4-r11,lr}
190481ad6265SDimitry Andric       if (Step != 1 && Step != 2)
190581ad6265SDimitry Andric         return false;
190681ad6265SDimitry Andric       assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX
190781ad6265SDimitry Andric       assert(Inst.Offset <= 1);                          // Lr
190881ad6265SDimitry Andric       IntRegs = Inst.Register - 4;
190981ad6265SDimitry Andric       if (Inst.Register == 11) {
191081ad6265SDimitry Andric         HasR11 = true;
191181ad6265SDimitry Andric         IntRegs--;
191281ad6265SDimitry Andric       }
191381ad6265SDimitry Andric       if (Inst.Offset)
191481ad6265SDimitry Andric         HasLR = true;
191581ad6265SDimitry Andric       Step = 3;
191681ad6265SDimitry Andric       break;
191781ad6265SDimitry Andric 
191881ad6265SDimitry Andric     case Win64EH::UOP_SaveRegMask:
191981ad6265SDimitry Andric       if (Step == 1 && Inst.Register == 0x0f) {
192081ad6265SDimitry Andric         // push {r0-r3}
192181ad6265SDimitry Andric         Homing = true;
192281ad6265SDimitry Andric         Step = 2;
192381ad6265SDimitry Andric         break;
192481ad6265SDimitry Andric       }
1925bdd1243dSDimitry Andric       [[fallthrough]];
192681ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegMask:
192781ad6265SDimitry Andric       if (Step != 1 && Step != 2)
192881ad6265SDimitry Andric         return false;
192981ad6265SDimitry Andric       // push {r4-r9,r11,lr}
193081ad6265SDimitry Andric       // push {r11,lr}
193181ad6265SDimitry Andric       // push {r1-r5}
193281ad6265SDimitry Andric       if (!parseRegMask(Inst.Register, HasLR, HasR11, PF, IntRegs))
193381ad6265SDimitry Andric         return false;
193481ad6265SDimitry Andric       Step = 3;
193581ad6265SDimitry Andric       break;
193681ad6265SDimitry Andric 
193781ad6265SDimitry Andric     case Win64EH::UOP_Nop:
193881ad6265SDimitry Andric       // mov r11, sp
193981ad6265SDimitry Andric       if (Step != 3 || !HasR11 || IntRegs >= 0 || PF > 0)
194081ad6265SDimitry Andric         return false;
194181ad6265SDimitry Andric       HasChain = true;
194281ad6265SDimitry Andric       Step = 4;
194381ad6265SDimitry Andric       break;
194481ad6265SDimitry Andric     case Win64EH::UOP_WideNop:
194581ad6265SDimitry Andric       // add.w r11, sp, #xx
194681ad6265SDimitry Andric       if (Step != 3 || !HasR11 || (IntRegs < 0 && PF == 0))
194781ad6265SDimitry Andric         return false;
194881ad6265SDimitry Andric       HasChain = true;
194981ad6265SDimitry Andric       Step = 4;
195081ad6265SDimitry Andric       break;
195181ad6265SDimitry Andric 
195281ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD8D15:
195381ad6265SDimitry Andric       if (Step != 1 && Step != 2 && Step != 3 && Step != 4)
195481ad6265SDimitry Andric         return false;
195581ad6265SDimitry Andric       assert(Inst.Register >= 8 && Inst.Register <= 15);
195681ad6265SDimitry Andric       if (Inst.Register == 15)
195781ad6265SDimitry Andric         return false; // Can't pack this case, R==7 means no IntRegs
195881ad6265SDimitry Andric       if (IntRegs >= 0)
195981ad6265SDimitry Andric         return false;
196081ad6265SDimitry Andric       FloatRegs = Inst.Register - 8;
196181ad6265SDimitry Andric       Step = 5;
196281ad6265SDimitry Andric       break;
196381ad6265SDimitry Andric 
196481ad6265SDimitry Andric     case Win64EH::UOP_AllocSmall:
196581ad6265SDimitry Andric     case Win64EH::UOP_WideAllocMedium:
196681ad6265SDimitry Andric       if (Step != 1 && Step != 2 && Step != 3 && Step != 4 && Step != 5)
196781ad6265SDimitry Andric         return false;
196881ad6265SDimitry Andric       if (PF > 0) // Can't have both folded and explicit stack allocation
196981ad6265SDimitry Andric         return false;
197081ad6265SDimitry Andric       if (Inst.Offset / 4 >= 0x3f4)
197181ad6265SDimitry Andric         return false;
197281ad6265SDimitry Andric       StackAdjust = Inst.Offset / 4;
197381ad6265SDimitry Andric       Step = 6;
197481ad6265SDimitry Andric       break;
197581ad6265SDimitry Andric     }
197681ad6265SDimitry Andric   }
197781ad6265SDimitry Andric   if (HasR11 && !HasChain) {
197881ad6265SDimitry Andric     if (IntRegs + 4 == 10) {
197981ad6265SDimitry Andric       // r11 stored, but not chaining; can be packed if already saving r4-r10
198081ad6265SDimitry Andric       // and we can fit r11 into this range.
198181ad6265SDimitry Andric       IntRegs++;
198281ad6265SDimitry Andric       HasR11 = false;
198381ad6265SDimitry Andric     } else
198481ad6265SDimitry Andric       return false;
198581ad6265SDimitry Andric   }
198681ad6265SDimitry Andric   if (HasChain && !HasLR)
198781ad6265SDimitry Andric     return false;
198881ad6265SDimitry Andric 
198981ad6265SDimitry Andric   // Packed uneind info can't express multiple epilogues.
199081ad6265SDimitry Andric   if (info->EpilogMap.size() > 1)
199181ad6265SDimitry Andric     return false;
199281ad6265SDimitry Andric 
199381ad6265SDimitry Andric   unsigned EF = 0;
199481ad6265SDimitry Andric   int Ret = 0;
199581ad6265SDimitry Andric   if (info->EpilogMap.size() == 0) {
199681ad6265SDimitry Andric     Ret = 3; // No epilogue
199781ad6265SDimitry Andric   } else {
199881ad6265SDimitry Andric     // As the prologue and epilogue aren't exact mirrors of each other,
199981ad6265SDimitry Andric     // we have to check the epilogue too and see if it matches what we've
200081ad6265SDimitry Andric     // concluded from the prologue.
200181ad6265SDimitry Andric     const WinEH::FrameInfo::Epilog &EpilogInfo =
200281ad6265SDimitry Andric         info->EpilogMap.begin()->second;
200381ad6265SDimitry Andric     if (EpilogInfo.Condition != 0xe) // ARMCC::AL
200481ad6265SDimitry Andric       return false;
200581ad6265SDimitry Andric     const std::vector<WinEH::Instruction> &Epilog = EpilogInfo.Instructions;
2006bdd1243dSDimitry Andric     std::optional<int64_t> MaybeDistance = GetOptionalAbsDifference(
200781ad6265SDimitry Andric         streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first);
200881ad6265SDimitry Andric     if (!MaybeDistance)
200981ad6265SDimitry Andric       return false;
201081ad6265SDimitry Andric     uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance;
201181ad6265SDimitry Andric     uint32_t InstructionBytes = ARMCountOfInstructionBytes(Epilog);
201281ad6265SDimitry Andric     if (DistanceFromEnd != InstructionBytes)
201381ad6265SDimitry Andric       return false;
201481ad6265SDimitry Andric 
201581ad6265SDimitry Andric     bool GotStackAdjust = false;
201681ad6265SDimitry Andric     bool GotFloatRegs = false;
201781ad6265SDimitry Andric     bool GotIntRegs = false;
201881ad6265SDimitry Andric     bool GotHomingRestore = false;
201981ad6265SDimitry Andric     bool GotLRRestore = false;
202081ad6265SDimitry Andric     bool NeedsReturn = false;
202181ad6265SDimitry Andric     bool GotReturn = false;
202281ad6265SDimitry Andric 
202381ad6265SDimitry Andric     Step = 6;
202481ad6265SDimitry Andric     for (const WinEH::Instruction &Inst : Epilog) {
202581ad6265SDimitry Andric       switch (Inst.Operation) {
202681ad6265SDimitry Andric       default:
202781ad6265SDimitry Andric         llvm_unreachable("Unsupported ARM unwind code");
202881ad6265SDimitry Andric       case Win64EH::UOP_Custom:
202981ad6265SDimitry Andric       case Win64EH::UOP_AllocLarge:
203081ad6265SDimitry Andric       case Win64EH::UOP_AllocHuge:
203181ad6265SDimitry Andric       case Win64EH::UOP_WideAllocLarge:
203281ad6265SDimitry Andric       case Win64EH::UOP_WideAllocHuge:
203381ad6265SDimitry Andric       case Win64EH::UOP_SaveFRegD0D15:
203481ad6265SDimitry Andric       case Win64EH::UOP_SaveFRegD16D31:
203581ad6265SDimitry Andric       case Win64EH::UOP_SaveSP:
203681ad6265SDimitry Andric       case Win64EH::UOP_Nop:
203781ad6265SDimitry Andric       case Win64EH::UOP_WideNop:
203881ad6265SDimitry Andric         // Can't be packed in an epilogue
203981ad6265SDimitry Andric         return false;
204081ad6265SDimitry Andric 
204181ad6265SDimitry Andric       case Win64EH::UOP_AllocSmall:
204281ad6265SDimitry Andric       case Win64EH::UOP_WideAllocMedium:
204381ad6265SDimitry Andric         if (Inst.Offset / 4 >= 0x3f4)
204481ad6265SDimitry Andric           return false;
204581ad6265SDimitry Andric         if (Step == 6) {
204681ad6265SDimitry Andric           if (Homing && FloatRegs < 0 && IntRegs < 0 && StackAdjust == 0 &&
204781ad6265SDimitry Andric               PF == 0 && Inst.Offset == 16) {
204881ad6265SDimitry Andric             GotHomingRestore = true;
204981ad6265SDimitry Andric             Step = 10;
205081ad6265SDimitry Andric           } else {
205181ad6265SDimitry Andric             if (StackAdjust > 0) {
205281ad6265SDimitry Andric               // Got stack adjust in prologue too; must match.
205381ad6265SDimitry Andric               if (StackAdjust != Inst.Offset / 4)
205481ad6265SDimitry Andric                 return false;
205581ad6265SDimitry Andric               GotStackAdjust = true;
205681ad6265SDimitry Andric             } else if (PF == Inst.Offset / 4) {
205781ad6265SDimitry Andric               // Folded prologue, non-folded epilogue
205881ad6265SDimitry Andric               StackAdjust = Inst.Offset / 4;
205981ad6265SDimitry Andric               GotStackAdjust = true;
206081ad6265SDimitry Andric             } else {
206181ad6265SDimitry Andric               // StackAdjust == 0 in prologue, mismatch
206281ad6265SDimitry Andric               return false;
206381ad6265SDimitry Andric             }
206481ad6265SDimitry Andric             Step = 7;
206581ad6265SDimitry Andric           }
206681ad6265SDimitry Andric         } else if (Step == 7 || Step == 8 || Step == 9) {
206781ad6265SDimitry Andric           if (!Homing || Inst.Offset != 16)
206881ad6265SDimitry Andric             return false;
206981ad6265SDimitry Andric           GotHomingRestore = true;
207081ad6265SDimitry Andric           Step = 10;
207181ad6265SDimitry Andric         } else
207281ad6265SDimitry Andric           return false;
207381ad6265SDimitry Andric         break;
207481ad6265SDimitry Andric 
207581ad6265SDimitry Andric       case Win64EH::UOP_SaveFRegD8D15:
207681ad6265SDimitry Andric         if (Step != 6 && Step != 7)
207781ad6265SDimitry Andric           return false;
207881ad6265SDimitry Andric         assert(Inst.Register >= 8 && Inst.Register <= 15);
207981ad6265SDimitry Andric         if (FloatRegs != (int)(Inst.Register - 8))
208081ad6265SDimitry Andric           return false;
208181ad6265SDimitry Andric         GotFloatRegs = true;
208281ad6265SDimitry Andric         Step = 8;
208381ad6265SDimitry Andric         break;
208481ad6265SDimitry Andric 
208581ad6265SDimitry Andric       case Win64EH::UOP_SaveRegsR4R7LR:
208681ad6265SDimitry Andric       case Win64EH::UOP_WideSaveRegsR4R11LR: {
208781ad6265SDimitry Andric         // push {r4-r11,lr}
208881ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8)
208981ad6265SDimitry Andric           return false;
209081ad6265SDimitry Andric         assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX
209181ad6265SDimitry Andric         assert(Inst.Offset <= 1);                          // Lr
209281ad6265SDimitry Andric         if (Homing && HasLR) {
209381ad6265SDimitry Andric           // If homing and LR is backed up, we can either restore LR here
209481ad6265SDimitry Andric           // and return with Ret == 1 or 2, or return with SaveLR below
209581ad6265SDimitry Andric           if (Inst.Offset) {
209681ad6265SDimitry Andric             GotLRRestore = true;
209781ad6265SDimitry Andric             NeedsReturn = true;
209881ad6265SDimitry Andric           } else {
209981ad6265SDimitry Andric             // Expecting a separate SaveLR below
210081ad6265SDimitry Andric           }
210181ad6265SDimitry Andric         } else {
210281ad6265SDimitry Andric           if (HasLR != (Inst.Offset == 1))
210381ad6265SDimitry Andric             return false;
210481ad6265SDimitry Andric         }
210581ad6265SDimitry Andric         GotLRRestore = Inst.Offset == 1;
210681ad6265SDimitry Andric         if (IntRegs < 0) // This opcode must include r4
210781ad6265SDimitry Andric           return false;
210881ad6265SDimitry Andric         int Expected = IntRegs;
210981ad6265SDimitry Andric         if (HasChain) {
211081ad6265SDimitry Andric           // Can't express r11 here unless IntRegs describe r4-r10
211181ad6265SDimitry Andric           if (IntRegs != 6)
211281ad6265SDimitry Andric             return false;
211381ad6265SDimitry Andric           Expected++;
211481ad6265SDimitry Andric         }
211581ad6265SDimitry Andric         if (Expected != (int)(Inst.Register - 4))
211681ad6265SDimitry Andric           return false;
211781ad6265SDimitry Andric         GotIntRegs = true;
211881ad6265SDimitry Andric         Step = 9;
211981ad6265SDimitry Andric         break;
212081ad6265SDimitry Andric       }
212181ad6265SDimitry Andric 
212281ad6265SDimitry Andric       case Win64EH::UOP_SaveRegMask:
212381ad6265SDimitry Andric       case Win64EH::UOP_WideSaveRegMask: {
212481ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8)
212581ad6265SDimitry Andric           return false;
212681ad6265SDimitry Andric         // push {r4-r9,r11,lr}
212781ad6265SDimitry Andric         // push {r11,lr}
212881ad6265SDimitry Andric         // push {r1-r5}
212981ad6265SDimitry Andric         bool CurHasLR = false, CurHasR11 = false;
213081ad6265SDimitry Andric         int Regs;
213181ad6265SDimitry Andric         if (!parseRegMask(Inst.Register, CurHasLR, CurHasR11, EF, Regs))
213281ad6265SDimitry Andric           return false;
213381ad6265SDimitry Andric         if (EF > 0) {
213481ad6265SDimitry Andric           if (EF != PF && EF != StackAdjust)
213581ad6265SDimitry Andric             return false;
213681ad6265SDimitry Andric         }
213781ad6265SDimitry Andric         if (Homing && HasLR) {
213881ad6265SDimitry Andric           // If homing and LR is backed up, we can either restore LR here
213981ad6265SDimitry Andric           // and return with Ret == 1 or 2, or return with SaveLR below
214081ad6265SDimitry Andric           if (CurHasLR) {
214181ad6265SDimitry Andric             GotLRRestore = true;
214281ad6265SDimitry Andric             NeedsReturn = true;
214381ad6265SDimitry Andric           } else {
214481ad6265SDimitry Andric             // Expecting a separate SaveLR below
214581ad6265SDimitry Andric           }
214681ad6265SDimitry Andric         } else {
214781ad6265SDimitry Andric           if (CurHasLR != HasLR)
214881ad6265SDimitry Andric             return false;
214981ad6265SDimitry Andric           GotLRRestore = CurHasLR;
215081ad6265SDimitry Andric         }
215181ad6265SDimitry Andric         int Expected = IntRegs;
215281ad6265SDimitry Andric         if (HasChain) {
215381ad6265SDimitry Andric           // If we have chaining, the mask must have included r11.
215481ad6265SDimitry Andric           if (!CurHasR11)
215581ad6265SDimitry Andric             return false;
215681ad6265SDimitry Andric         } else if (Expected == 7) {
215781ad6265SDimitry Andric           // If we don't have chaining, the mask could still include r11,
215881ad6265SDimitry Andric           // expressed as part of IntRegs Instead.
215981ad6265SDimitry Andric           Expected--;
216081ad6265SDimitry Andric           if (!CurHasR11)
216181ad6265SDimitry Andric             return false;
216281ad6265SDimitry Andric         } else {
216381ad6265SDimitry Andric           // Neither HasChain nor r11 included in IntRegs, must not have r11
216481ad6265SDimitry Andric           // here either.
216581ad6265SDimitry Andric           if (CurHasR11)
216681ad6265SDimitry Andric             return false;
216781ad6265SDimitry Andric         }
216881ad6265SDimitry Andric         if (Expected != Regs)
216981ad6265SDimitry Andric           return false;
217081ad6265SDimitry Andric         GotIntRegs = true;
217181ad6265SDimitry Andric         Step = 9;
217281ad6265SDimitry Andric         break;
217381ad6265SDimitry Andric       }
217481ad6265SDimitry Andric 
217581ad6265SDimitry Andric       case Win64EH::UOP_SaveLR:
217681ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8 && Step != 9)
217781ad6265SDimitry Andric           return false;
217881ad6265SDimitry Andric         if (!Homing || Inst.Offset != 20 || GotLRRestore)
217981ad6265SDimitry Andric           return false;
218081ad6265SDimitry Andric         GotLRRestore = true;
218181ad6265SDimitry Andric         GotHomingRestore = true;
218281ad6265SDimitry Andric         Step = 10;
218381ad6265SDimitry Andric         break;
218481ad6265SDimitry Andric 
218581ad6265SDimitry Andric       case Win64EH::UOP_EndNop:
218681ad6265SDimitry Andric       case Win64EH::UOP_WideEndNop:
218781ad6265SDimitry Andric         GotReturn = true;
218881ad6265SDimitry Andric         Ret = (Inst.Operation == Win64EH::UOP_EndNop) ? 1 : 2;
2189bdd1243dSDimitry Andric         [[fallthrough]];
219081ad6265SDimitry Andric       case Win64EH::UOP_End:
219181ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8 && Step != 9 && Step != 10)
219281ad6265SDimitry Andric           return false;
219381ad6265SDimitry Andric         Step = 11;
219481ad6265SDimitry Andric         break;
219581ad6265SDimitry Andric       }
219681ad6265SDimitry Andric     }
219781ad6265SDimitry Andric 
219881ad6265SDimitry Andric     if (Step != 11)
219981ad6265SDimitry Andric       return false;
220081ad6265SDimitry Andric     if (StackAdjust > 0 && !GotStackAdjust && EF == 0)
220181ad6265SDimitry Andric       return false;
220281ad6265SDimitry Andric     if (FloatRegs >= 0 && !GotFloatRegs)
220381ad6265SDimitry Andric       return false;
220481ad6265SDimitry Andric     if (IntRegs >= 0 && !GotIntRegs)
220581ad6265SDimitry Andric       return false;
220681ad6265SDimitry Andric     if (Homing && !GotHomingRestore)
220781ad6265SDimitry Andric       return false;
220881ad6265SDimitry Andric     if (HasLR && !GotLRRestore)
220981ad6265SDimitry Andric       return false;
221081ad6265SDimitry Andric     if (NeedsReturn && !GotReturn)
221181ad6265SDimitry Andric       return false;
221281ad6265SDimitry Andric   }
221381ad6265SDimitry Andric 
221481ad6265SDimitry Andric   assert(PF == 0 || EF == 0 ||
221581ad6265SDimitry Andric          StackAdjust == 0); // Can't have adjust in all three
221681ad6265SDimitry Andric   if (PF > 0 || EF > 0) {
221781ad6265SDimitry Andric     StackAdjust = PF > 0 ? (PF - 1) : (EF - 1);
221881ad6265SDimitry Andric     assert(StackAdjust <= 3);
221981ad6265SDimitry Andric     StackAdjust |= 0x3f0;
222081ad6265SDimitry Andric     if (PF > 0)
222181ad6265SDimitry Andric       StackAdjust |= 1 << 2;
222281ad6265SDimitry Andric     if (EF > 0)
222381ad6265SDimitry Andric       StackAdjust |= 1 << 3;
222481ad6265SDimitry Andric   }
222581ad6265SDimitry Andric 
222681ad6265SDimitry Andric   assert(FuncLength <= 0x7FF && "FuncLength should have been checked earlier");
222781ad6265SDimitry Andric   int Flag = info->Fragment ? 0x02 : 0x01;
222881ad6265SDimitry Andric   int H = Homing ? 1 : 0;
222981ad6265SDimitry Andric   int L = HasLR ? 1 : 0;
223081ad6265SDimitry Andric   int C = HasChain ? 1 : 0;
223181ad6265SDimitry Andric   assert(IntRegs < 0 || FloatRegs < 0);
223281ad6265SDimitry Andric   unsigned Reg, R;
223381ad6265SDimitry Andric   if (IntRegs >= 0) {
223481ad6265SDimitry Andric     Reg = IntRegs;
223581ad6265SDimitry Andric     assert(Reg <= 7);
223681ad6265SDimitry Andric     R = 0;
223781ad6265SDimitry Andric   } else if (FloatRegs >= 0) {
223881ad6265SDimitry Andric     Reg = FloatRegs;
223981ad6265SDimitry Andric     assert(Reg < 7);
224081ad6265SDimitry Andric     R = 1;
224181ad6265SDimitry Andric   } else {
224281ad6265SDimitry Andric     // No int or float regs stored (except possibly R11,LR)
224381ad6265SDimitry Andric     Reg = 7;
224481ad6265SDimitry Andric     R = 1;
224581ad6265SDimitry Andric   }
224681ad6265SDimitry Andric   info->PackedInfo |= Flag << 0;
224781ad6265SDimitry Andric   info->PackedInfo |= (FuncLength & 0x7FF) << 2;
224881ad6265SDimitry Andric   info->PackedInfo |= (Ret & 0x3) << 13;
224981ad6265SDimitry Andric   info->PackedInfo |= H << 15;
225081ad6265SDimitry Andric   info->PackedInfo |= Reg << 16;
225181ad6265SDimitry Andric   info->PackedInfo |= R << 19;
225281ad6265SDimitry Andric   info->PackedInfo |= L << 20;
225381ad6265SDimitry Andric   info->PackedInfo |= C << 21;
225481ad6265SDimitry Andric   assert(StackAdjust <= 0x3ff);
225581ad6265SDimitry Andric   info->PackedInfo |= StackAdjust << 22;
225681ad6265SDimitry Andric   return true;
225781ad6265SDimitry Andric }
225881ad6265SDimitry Andric 
225981ad6265SDimitry Andric // Populate the .xdata section.  The format of .xdata on ARM is documented at
226081ad6265SDimitry Andric // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
226181ad6265SDimitry Andric static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
226281ad6265SDimitry Andric                               bool TryPacked = true) {
226381ad6265SDimitry Andric   // If this UNWIND_INFO already has a symbol, it's already been emitted.
226481ad6265SDimitry Andric   if (info->Symbol)
226581ad6265SDimitry Andric     return;
226681ad6265SDimitry Andric   // If there's no unwind info here (not even a terminating UOP_End), the
226781ad6265SDimitry Andric   // unwind info is considered bogus and skipped. If this was done in
226881ad6265SDimitry Andric   // response to an explicit .seh_handlerdata, the associated trailing
226981ad6265SDimitry Andric   // handler data is left orphaned in the xdata section.
227081ad6265SDimitry Andric   if (info->empty()) {
227181ad6265SDimitry Andric     info->EmitAttempted = true;
227281ad6265SDimitry Andric     return;
227381ad6265SDimitry Andric   }
227481ad6265SDimitry Andric   if (info->EmitAttempted) {
227581ad6265SDimitry Andric     // If we tried to emit unwind info before (due to an explicit
227681ad6265SDimitry Andric     // .seh_handlerdata directive), but skipped it (because there was no
227781ad6265SDimitry Andric     // valid information to emit at the time), and it later got valid unwind
227881ad6265SDimitry Andric     // opcodes, we can't emit it here, because the trailing handler data
227981ad6265SDimitry Andric     // was already emitted elsewhere in the xdata section.
228081ad6265SDimitry Andric     streamer.getContext().reportError(
228181ad6265SDimitry Andric         SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() +
228281ad6265SDimitry Andric                      " skipped due to no unwind info at the time "
228381ad6265SDimitry Andric                      "(.seh_handlerdata too early?), but the function later "
228481ad6265SDimitry Andric                      "did get unwind info that can't be emitted");
228581ad6265SDimitry Andric     return;
228681ad6265SDimitry Andric   }
228781ad6265SDimitry Andric 
228881ad6265SDimitry Andric   MCContext &context = streamer.getContext();
228981ad6265SDimitry Andric   MCSymbol *Label = context.createTempSymbol();
229081ad6265SDimitry Andric 
2291bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
229281ad6265SDimitry Andric   streamer.emitLabel(Label);
229381ad6265SDimitry Andric   info->Symbol = Label;
229481ad6265SDimitry Andric 
229581ad6265SDimitry Andric   if (!info->PrologEnd)
229681ad6265SDimitry Andric     streamer.getContext().reportError(SMLoc(), "Prologue in " +
229781ad6265SDimitry Andric                                                    info->Function->getName() +
229881ad6265SDimitry Andric                                                    " not correctly terminated");
229981ad6265SDimitry Andric 
230081ad6265SDimitry Andric   if (info->PrologEnd && !info->Fragment)
230181ad6265SDimitry Andric     checkARMInstructions(streamer, info->Instructions, info->Begin,
230281ad6265SDimitry Andric                          info->PrologEnd, info->Function->getName(),
230381ad6265SDimitry Andric                          "prologue");
230481ad6265SDimitry Andric   for (auto &I : info->EpilogMap) {
230581ad6265SDimitry Andric     MCSymbol *EpilogStart = I.first;
230681ad6265SDimitry Andric     auto &Epilog = I.second;
230781ad6265SDimitry Andric     checkARMInstructions(streamer, Epilog.Instructions, EpilogStart, Epilog.End,
230881ad6265SDimitry Andric                          info->Function->getName(), "epilogue");
230981ad6265SDimitry Andric     if (Epilog.Instructions.empty() ||
231081ad6265SDimitry Andric         !isARMTerminator(Epilog.Instructions.back()))
231181ad6265SDimitry Andric       streamer.getContext().reportError(
231281ad6265SDimitry Andric           SMLoc(), "Epilogue in " + info->Function->getName() +
231381ad6265SDimitry Andric                        " not correctly terminated");
231481ad6265SDimitry Andric   }
231581ad6265SDimitry Andric 
2316bdd1243dSDimitry Andric   std::optional<int64_t> RawFuncLength;
231781ad6265SDimitry Andric   const MCExpr *FuncLengthExpr = nullptr;
231881ad6265SDimitry Andric   if (!info->FuncletOrFuncEnd) {
231981ad6265SDimitry Andric     report_fatal_error("FuncletOrFuncEnd not set");
232081ad6265SDimitry Andric   } else {
232181ad6265SDimitry Andric     // As the size of many thumb2 instructions isn't known until later,
232281ad6265SDimitry Andric     // we can't always rely on being able to calculate the absolute
232381ad6265SDimitry Andric     // length of the function here. If we can't calculate it, defer it
232481ad6265SDimitry Andric     // to a relocation.
232581ad6265SDimitry Andric     //
232681ad6265SDimitry Andric     // In such a case, we won't know if the function is too long so that
232781ad6265SDimitry Andric     // the unwind info would need to be split (but this isn't implemented
232881ad6265SDimitry Andric     // anyway).
232981ad6265SDimitry Andric     RawFuncLength =
233081ad6265SDimitry Andric         GetOptionalAbsDifference(streamer, info->FuncletOrFuncEnd, info->Begin);
233181ad6265SDimitry Andric     if (!RawFuncLength)
233281ad6265SDimitry Andric       FuncLengthExpr =
233381ad6265SDimitry Andric           GetSubDivExpr(streamer, info->FuncletOrFuncEnd, info->Begin, 2);
233481ad6265SDimitry Andric   }
233581ad6265SDimitry Andric   uint32_t FuncLength = 0;
233681ad6265SDimitry Andric   if (RawFuncLength)
233781ad6265SDimitry Andric     FuncLength = (uint32_t)*RawFuncLength / 2;
233881ad6265SDimitry Andric   if (FuncLength > 0x3FFFF)
233981ad6265SDimitry Andric     report_fatal_error("SEH unwind data splitting not yet implemented");
234081ad6265SDimitry Andric   uint32_t PrologCodeBytes = ARMCountOfUnwindCodes(info->Instructions);
234181ad6265SDimitry Andric   uint32_t TotalCodeBytes = PrologCodeBytes;
234281ad6265SDimitry Andric 
234381ad6265SDimitry Andric   if (!info->HandlesExceptions && RawFuncLength && FuncLength <= 0x7ff &&
234481ad6265SDimitry Andric       TryPacked) {
234581ad6265SDimitry Andric     // No exception handlers; check if the prolog and epilog matches the
234681ad6265SDimitry Andric     // patterns that can be described by the packed format. If we don't
234781ad6265SDimitry Andric     // know the exact function length yet, we can't do this.
234881ad6265SDimitry Andric 
234981ad6265SDimitry Andric     // info->Symbol was already set even if we didn't actually write any
235081ad6265SDimitry Andric     // unwind info there. Keep using that as indicator that this unwind
235181ad6265SDimitry Andric     // info has been generated already.
235281ad6265SDimitry Andric 
235381ad6265SDimitry Andric     if (tryARMPackedUnwind(streamer, info, FuncLength))
235481ad6265SDimitry Andric       return;
235581ad6265SDimitry Andric   }
235681ad6265SDimitry Andric 
235781ad6265SDimitry Andric   int PackedEpilogOffset =
235881ad6265SDimitry Andric       checkARMPackedEpilog(streamer, info, PrologCodeBytes);
235981ad6265SDimitry Andric 
236081ad6265SDimitry Andric   // Process epilogs.
236181ad6265SDimitry Andric   MapVector<MCSymbol *, uint32_t> EpilogInfo;
236281ad6265SDimitry Andric   // Epilogs processed so far.
236381ad6265SDimitry Andric   std::vector<MCSymbol *> AddedEpilogs;
236481ad6265SDimitry Andric 
236581ad6265SDimitry Andric   bool CanTweakProlog = true;
236681ad6265SDimitry Andric   for (auto &I : info->EpilogMap) {
236781ad6265SDimitry Andric     MCSymbol *EpilogStart = I.first;
236881ad6265SDimitry Andric     auto &EpilogInstrs = I.second.Instructions;
236981ad6265SDimitry Andric     uint32_t CodeBytes = ARMCountOfUnwindCodes(EpilogInstrs);
237081ad6265SDimitry Andric 
237181ad6265SDimitry Andric     MCSymbol *MatchingEpilog =
237281ad6265SDimitry Andric         FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
237381ad6265SDimitry Andric     int PrologOffset;
237481ad6265SDimitry Andric     if (MatchingEpilog) {
237506c3fb27SDimitry Andric       assert(EpilogInfo.contains(MatchingEpilog) &&
237681ad6265SDimitry Andric              "Duplicate epilog not found");
237781ad6265SDimitry Andric       EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog);
237881ad6265SDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
237981ad6265SDimitry Andric       // in the logic below.
238081ad6265SDimitry Andric       EpilogInstrs.clear();
238181ad6265SDimitry Andric     } else if ((PrologOffset = getARMOffsetInProlog(
238281ad6265SDimitry Andric                     info->Instructions, EpilogInstrs, CanTweakProlog)) >= 0) {
238381ad6265SDimitry Andric       if (CanTweakProlog) {
238481ad6265SDimitry Andric         // Replace the regular end opcode of the prolog with the one from the
238581ad6265SDimitry Andric         // epilog.
238681ad6265SDimitry Andric         info->Instructions.front() = EpilogInstrs.back();
238781ad6265SDimitry Andric         // Later epilogs need a strict match for the end opcode.
238881ad6265SDimitry Andric         CanTweakProlog = false;
238981ad6265SDimitry Andric       }
239081ad6265SDimitry Andric       EpilogInfo[EpilogStart] = PrologOffset;
239181ad6265SDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
239281ad6265SDimitry Andric       // in the logic below.
239381ad6265SDimitry Andric       EpilogInstrs.clear();
239481ad6265SDimitry Andric     } else {
239581ad6265SDimitry Andric       EpilogInfo[EpilogStart] = TotalCodeBytes;
239681ad6265SDimitry Andric       TotalCodeBytes += CodeBytes;
239781ad6265SDimitry Andric       AddedEpilogs.push_back(EpilogStart);
239881ad6265SDimitry Andric     }
239981ad6265SDimitry Andric   }
240081ad6265SDimitry Andric 
240181ad6265SDimitry Andric   // Code Words, Epilog count, F, E, X, Vers, Function Length
240281ad6265SDimitry Andric   uint32_t row1 = 0x0;
240381ad6265SDimitry Andric   uint32_t CodeWords = TotalCodeBytes / 4;
240481ad6265SDimitry Andric   uint32_t CodeWordsMod = TotalCodeBytes % 4;
240581ad6265SDimitry Andric   if (CodeWordsMod)
240681ad6265SDimitry Andric     CodeWords++;
240781ad6265SDimitry Andric   uint32_t EpilogCount =
240881ad6265SDimitry Andric       PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size();
240981ad6265SDimitry Andric   bool ExtensionWord = EpilogCount > 31 || CodeWords > 15;
241081ad6265SDimitry Andric   if (!ExtensionWord) {
241181ad6265SDimitry Andric     row1 |= (EpilogCount & 0x1F) << 23;
241281ad6265SDimitry Andric     row1 |= (CodeWords & 0x0F) << 28;
241381ad6265SDimitry Andric   }
241481ad6265SDimitry Andric   if (info->HandlesExceptions) // X
241581ad6265SDimitry Andric     row1 |= 1 << 20;
241681ad6265SDimitry Andric   if (PackedEpilogOffset >= 0) // E
241781ad6265SDimitry Andric     row1 |= 1 << 21;
241881ad6265SDimitry Andric   if (info->Fragment) // F
241981ad6265SDimitry Andric     row1 |= 1 << 22;
242081ad6265SDimitry Andric   row1 |= FuncLength & 0x3FFFF;
242181ad6265SDimitry Andric   if (RawFuncLength)
242281ad6265SDimitry Andric     streamer.emitInt32(row1);
242381ad6265SDimitry Andric   else
242481ad6265SDimitry Andric     streamer.emitValue(
242581ad6265SDimitry Andric         MCBinaryExpr::createOr(FuncLengthExpr,
242681ad6265SDimitry Andric                                MCConstantExpr::create(row1, context), context),
242781ad6265SDimitry Andric         4);
242881ad6265SDimitry Andric 
242981ad6265SDimitry Andric   // Extended Code Words, Extended Epilog Count
243081ad6265SDimitry Andric   if (ExtensionWord) {
243181ad6265SDimitry Andric     // FIXME: We should be able to split unwind info into multiple sections.
243281ad6265SDimitry Andric     if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
243381ad6265SDimitry Andric       report_fatal_error("SEH unwind data splitting not yet implemented");
243481ad6265SDimitry Andric     uint32_t row2 = 0x0;
243581ad6265SDimitry Andric     row2 |= (CodeWords & 0xFF) << 16;
243681ad6265SDimitry Andric     row2 |= (EpilogCount & 0xFFFF);
243781ad6265SDimitry Andric     streamer.emitInt32(row2);
243881ad6265SDimitry Andric   }
243981ad6265SDimitry Andric 
244081ad6265SDimitry Andric   if (PackedEpilogOffset < 0) {
244181ad6265SDimitry Andric     // Epilog Start Index, Epilog Start Offset
244281ad6265SDimitry Andric     for (auto &I : EpilogInfo) {
244381ad6265SDimitry Andric       MCSymbol *EpilogStart = I.first;
244481ad6265SDimitry Andric       uint32_t EpilogIndex = I.second;
244581ad6265SDimitry Andric 
2446bdd1243dSDimitry Andric       std::optional<int64_t> MaybeEpilogOffset =
244781ad6265SDimitry Andric           GetOptionalAbsDifference(streamer, EpilogStart, info->Begin);
244881ad6265SDimitry Andric       const MCExpr *OffsetExpr = nullptr;
244981ad6265SDimitry Andric       uint32_t EpilogOffset = 0;
245081ad6265SDimitry Andric       if (MaybeEpilogOffset)
245181ad6265SDimitry Andric         EpilogOffset = *MaybeEpilogOffset / 2;
245281ad6265SDimitry Andric       else
245381ad6265SDimitry Andric         OffsetExpr = GetSubDivExpr(streamer, EpilogStart, info->Begin, 2);
245481ad6265SDimitry Andric 
245506c3fb27SDimitry Andric       assert(info->EpilogMap.contains(EpilogStart));
245681ad6265SDimitry Andric       unsigned Condition = info->EpilogMap[EpilogStart].Condition;
245781ad6265SDimitry Andric       assert(Condition <= 0xf);
245881ad6265SDimitry Andric 
245981ad6265SDimitry Andric       uint32_t row3 = EpilogOffset;
246081ad6265SDimitry Andric       row3 |= Condition << 20;
246181ad6265SDimitry Andric       row3 |= (EpilogIndex & 0x3FF) << 24;
246281ad6265SDimitry Andric       if (MaybeEpilogOffset)
246381ad6265SDimitry Andric         streamer.emitInt32(row3);
246481ad6265SDimitry Andric       else
246581ad6265SDimitry Andric         streamer.emitValue(
246681ad6265SDimitry Andric             MCBinaryExpr::createOr(
246781ad6265SDimitry Andric                 OffsetExpr, MCConstantExpr::create(row3, context), context),
246881ad6265SDimitry Andric             4);
246981ad6265SDimitry Andric     }
247081ad6265SDimitry Andric   }
247181ad6265SDimitry Andric 
247281ad6265SDimitry Andric   // Emit prolog unwind instructions (in reverse order).
247381ad6265SDimitry Andric   uint8_t numInst = info->Instructions.size();
247481ad6265SDimitry Andric   for (uint8_t c = 0; c < numInst; ++c) {
247581ad6265SDimitry Andric     WinEH::Instruction inst = info->Instructions.back();
247681ad6265SDimitry Andric     info->Instructions.pop_back();
247781ad6265SDimitry Andric     ARMEmitUnwindCode(streamer, inst);
247881ad6265SDimitry Andric   }
247981ad6265SDimitry Andric 
248081ad6265SDimitry Andric   // Emit epilog unwind instructions
248181ad6265SDimitry Andric   for (auto &I : info->EpilogMap) {
248281ad6265SDimitry Andric     auto &EpilogInstrs = I.second.Instructions;
2483bdd1243dSDimitry Andric     for (const WinEH::Instruction &inst : EpilogInstrs)
248481ad6265SDimitry Andric       ARMEmitUnwindCode(streamer, inst);
248581ad6265SDimitry Andric   }
248681ad6265SDimitry Andric 
248781ad6265SDimitry Andric   int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
248881ad6265SDimitry Andric   assert(BytesMod >= 0);
248981ad6265SDimitry Andric   for (int i = 0; i < BytesMod; i++)
249081ad6265SDimitry Andric     streamer.emitInt8(0xFB);
249181ad6265SDimitry Andric 
249281ad6265SDimitry Andric   if (info->HandlesExceptions)
249381ad6265SDimitry Andric     streamer.emitValue(
249481ad6265SDimitry Andric         MCSymbolRefExpr::create(info->ExceptionHandler,
249581ad6265SDimitry Andric                                 MCSymbolRefExpr::VK_COFF_IMGREL32, context),
249681ad6265SDimitry Andric         4);
249781ad6265SDimitry Andric }
249881ad6265SDimitry Andric 
2499bdd1243dSDimitry Andric static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
2500bdd1243dSDimitry Andric                                      const WinEH::FrameInfo *info) {
2501bdd1243dSDimitry Andric   MCContext &context = streamer.getContext();
2502bdd1243dSDimitry Andric 
2503bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
2504bdd1243dSDimitry Andric   for (const auto &S : info->Segments) {
2505bdd1243dSDimitry Andric     EmitSymbolRefWithOfs(streamer, info->Begin, S.Offset);
2506bdd1243dSDimitry Andric     if (info->PackedInfo)
2507bdd1243dSDimitry Andric       streamer.emitInt32(info->PackedInfo);
2508bdd1243dSDimitry Andric     else
2509bdd1243dSDimitry Andric       streamer.emitValue(
2510bdd1243dSDimitry Andric           MCSymbolRefExpr::create(S.Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32,
2511bdd1243dSDimitry Andric                                   context),
2512bdd1243dSDimitry Andric           4);
2513bdd1243dSDimitry Andric   }
2514bdd1243dSDimitry Andric }
2515bdd1243dSDimitry Andric 
2516bdd1243dSDimitry Andric 
251781ad6265SDimitry Andric static void ARMEmitRuntimeFunction(MCStreamer &streamer,
25180b57cec5SDimitry Andric                                    const WinEH::FrameInfo *info) {
25190b57cec5SDimitry Andric   MCContext &context = streamer.getContext();
25200b57cec5SDimitry Andric 
2521bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
2522349cc55cSDimitry Andric   EmitSymbolRefWithOfs(streamer, info->Begin, info->Begin);
2523e8d8bef9SDimitry Andric   if (info->PackedInfo)
2524e8d8bef9SDimitry Andric     streamer.emitInt32(info->PackedInfo);
2525e8d8bef9SDimitry Andric   else
2526e8d8bef9SDimitry Andric     streamer.emitValue(
2527e8d8bef9SDimitry Andric         MCSymbolRefExpr::create(info->Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32,
25280b57cec5SDimitry Andric                                 context),
25290b57cec5SDimitry Andric         4);
25300b57cec5SDimitry Andric }
25310b57cec5SDimitry Andric 
25320b57cec5SDimitry Andric void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
25330b57cec5SDimitry Andric   // Emit the unwind info structs first.
25340b57cec5SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
2535e8d8bef9SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
2536e8d8bef9SDimitry Andric     if (Info->empty())
2537e8d8bef9SDimitry Andric       continue;
25380b57cec5SDimitry Andric     MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
253981ad6265SDimitry Andric     Streamer.switchSection(XData);
2540e8d8bef9SDimitry Andric     ARM64EmitUnwindInfo(Streamer, Info);
25410b57cec5SDimitry Andric   }
25420b57cec5SDimitry Andric 
25430b57cec5SDimitry Andric   // Now emit RUNTIME_FUNCTION entries.
25440b57cec5SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
2545e8d8bef9SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
2546e8d8bef9SDimitry Andric     // ARM64EmitUnwindInfo above clears the info struct, so we can't check
2547e8d8bef9SDimitry Andric     // empty here. But if a Symbol is set, we should create the corresponding
2548e8d8bef9SDimitry Andric     // pdata entry.
2549e8d8bef9SDimitry Andric     if (!Info->Symbol)
2550e8d8bef9SDimitry Andric       continue;
25510b57cec5SDimitry Andric     MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
255281ad6265SDimitry Andric     Streamer.switchSection(PData);
2553bdd1243dSDimitry Andric     ARM64EmitRuntimeFunction(Streamer, Info);
25540b57cec5SDimitry Andric   }
25550b57cec5SDimitry Andric }
25560b57cec5SDimitry Andric 
2557e8d8bef9SDimitry Andric void llvm::Win64EH::ARM64UnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
2558e8d8bef9SDimitry Andric                                                        WinEH::FrameInfo *info,
2559e8d8bef9SDimitry Andric                                                        bool HandlerData) const {
2560e8d8bef9SDimitry Andric   // Called if there's an .seh_handlerdata directive before the end of the
2561e8d8bef9SDimitry Andric   // function. This forces writing the xdata record already here - and
2562e8d8bef9SDimitry Andric   // in this case, the function isn't actually ended already, but the xdata
2563e8d8bef9SDimitry Andric   // record needs to know the function length. In these cases, if the funclet
2564e8d8bef9SDimitry Andric   // end hasn't been marked yet, the xdata function length won't cover the
2565e8d8bef9SDimitry Andric   // whole function, only up to this point.
2566e8d8bef9SDimitry Andric   if (!info->FuncletOrFuncEnd) {
256781ad6265SDimitry Andric     Streamer.switchSection(info->TextSection);
2568e8d8bef9SDimitry Andric     info->FuncletOrFuncEnd = Streamer.emitCFILabel();
2569e8d8bef9SDimitry Andric   }
25700b57cec5SDimitry Andric   // Switch sections (the static function above is meant to be called from
25710b57cec5SDimitry Andric   // here and from Emit().
25720b57cec5SDimitry Andric   MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection);
257381ad6265SDimitry Andric   Streamer.switchSection(XData);
2574e8d8bef9SDimitry Andric   ARM64EmitUnwindInfo(Streamer, info, /* TryPacked = */ !HandlerData);
25750b57cec5SDimitry Andric }
257681ad6265SDimitry Andric 
257781ad6265SDimitry Andric void llvm::Win64EH::ARMUnwindEmitter::Emit(MCStreamer &Streamer) const {
257881ad6265SDimitry Andric   // Emit the unwind info structs first.
257981ad6265SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
258081ad6265SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
258181ad6265SDimitry Andric     if (Info->empty())
258281ad6265SDimitry Andric       continue;
258381ad6265SDimitry Andric     MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
258481ad6265SDimitry Andric     Streamer.switchSection(XData);
258581ad6265SDimitry Andric     ARMEmitUnwindInfo(Streamer, Info);
258681ad6265SDimitry Andric   }
258781ad6265SDimitry Andric 
258881ad6265SDimitry Andric   // Now emit RUNTIME_FUNCTION entries.
258981ad6265SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
259081ad6265SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
259181ad6265SDimitry Andric     // ARMEmitUnwindInfo above clears the info struct, so we can't check
259281ad6265SDimitry Andric     // empty here. But if a Symbol is set, we should create the corresponding
259381ad6265SDimitry Andric     // pdata entry.
259481ad6265SDimitry Andric     if (!Info->Symbol)
259581ad6265SDimitry Andric       continue;
259681ad6265SDimitry Andric     MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
259781ad6265SDimitry Andric     Streamer.switchSection(PData);
259881ad6265SDimitry Andric     ARMEmitRuntimeFunction(Streamer, Info);
259981ad6265SDimitry Andric   }
260081ad6265SDimitry Andric }
260181ad6265SDimitry Andric 
260281ad6265SDimitry Andric void llvm::Win64EH::ARMUnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
260381ad6265SDimitry Andric                                                      WinEH::FrameInfo *info,
260481ad6265SDimitry Andric                                                      bool HandlerData) const {
260581ad6265SDimitry Andric   // Called if there's an .seh_handlerdata directive before the end of the
260681ad6265SDimitry Andric   // function. This forces writing the xdata record already here - and
260781ad6265SDimitry Andric   // in this case, the function isn't actually ended already, but the xdata
260881ad6265SDimitry Andric   // record needs to know the function length. In these cases, if the funclet
260981ad6265SDimitry Andric   // end hasn't been marked yet, the xdata function length won't cover the
261081ad6265SDimitry Andric   // whole function, only up to this point.
261181ad6265SDimitry Andric   if (!info->FuncletOrFuncEnd) {
261281ad6265SDimitry Andric     Streamer.switchSection(info->TextSection);
261381ad6265SDimitry Andric     info->FuncletOrFuncEnd = Streamer.emitCFILabel();
261481ad6265SDimitry Andric   }
261581ad6265SDimitry Andric   // Switch sections (the static function above is meant to be called from
261681ad6265SDimitry Andric   // here and from Emit().
261781ad6265SDimitry Andric   MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection);
261881ad6265SDimitry Andric   Streamer.switchSection(XData);
261981ad6265SDimitry Andric   ARMEmitUnwindInfo(Streamer, info, /* TryPacked = */ !HandlerData);
262081ad6265SDimitry Andric }
2621