xref: /freebsd/contrib/llvm-project/llvm/lib/MC/MCWin64EH.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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,
132*bdd1243dSDimitry Andric                                  int64_t Offset) {
133*bdd1243dSDimitry Andric   MCContext &Context = streamer.getContext();
134*bdd1243dSDimitry Andric   const MCConstantExpr *OffExpr = MCConstantExpr::create(Offset, Context);
135*bdd1243dSDimitry Andric   const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Base,
136*bdd1243dSDimitry Andric                                               MCSymbolRefExpr::VK_COFF_IMGREL32,
137*bdd1243dSDimitry Andric                                               Context);
138*bdd1243dSDimitry Andric   streamer.emitValue(MCBinaryExpr::createAdd(BaseRefRel, OffExpr, Context), 4);
139*bdd1243dSDimitry Andric }
140*bdd1243dSDimitry Andric 
141*bdd1243dSDimitry Andric static void EmitSymbolRefWithOfs(MCStreamer &streamer,
142*bdd1243dSDimitry 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 
158*bdd1243dSDimitry 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 
174*bdd1243dSDimitry 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 
276*bdd1243dSDimitry 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()))
289*bdd1243dSDimitry 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) {
295*bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDiff =
296*bdd1243dSDimitry 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 
302*bdd1243dSDimitry Andric static void checkARM64Instructions(MCStreamer &Streamer,
303*bdd1243dSDimitry Andric                                    ArrayRef<WinEH::Instruction> Insns,
304*bdd1243dSDimitry Andric                                    const MCSymbol *Begin, const MCSymbol *End,
305*bdd1243dSDimitry Andric                                    StringRef Name, StringRef Type) {
306*bdd1243dSDimitry Andric   if (!End)
307*bdd1243dSDimitry Andric     return;
308*bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDistance =
309*bdd1243dSDimitry Andric       GetOptionalAbsDifference(Streamer, End, Begin);
310*bdd1243dSDimitry Andric   if (!MaybeDistance)
311*bdd1243dSDimitry Andric     return;
312*bdd1243dSDimitry Andric   uint32_t Distance = (uint32_t)*MaybeDistance;
313*bdd1243dSDimitry Andric 
314*bdd1243dSDimitry Andric   for (const auto &I : Insns) {
315*bdd1243dSDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
316*bdd1243dSDimitry Andric     default:
317*bdd1243dSDimitry Andric       break;
318*bdd1243dSDimitry Andric     case Win64EH::UOP_TrapFrame:
319*bdd1243dSDimitry Andric     case Win64EH::UOP_PushMachFrame:
320*bdd1243dSDimitry Andric     case Win64EH::UOP_Context:
321*bdd1243dSDimitry Andric     case Win64EH::UOP_ClearUnwoundToCall:
322*bdd1243dSDimitry Andric       // Can't reason about these opcodes and how they map to actual
323*bdd1243dSDimitry Andric       // instructions.
324*bdd1243dSDimitry Andric       return;
325*bdd1243dSDimitry Andric     }
326*bdd1243dSDimitry Andric   }
327*bdd1243dSDimitry Andric   // Exclude the end opcode which doesn't map to an instruction.
328*bdd1243dSDimitry Andric   uint32_t InstructionBytes = 4 * (Insns.size() - 1);
329*bdd1243dSDimitry Andric   if (Distance != InstructionBytes) {
330*bdd1243dSDimitry Andric     Streamer.getContext().reportError(
331*bdd1243dSDimitry Andric         SMLoc(), "Incorrect size for " + Name + " " + Type + ": " +
332*bdd1243dSDimitry Andric                      Twine(Distance) +
333*bdd1243dSDimitry Andric                      " bytes of instructions in range, but .seh directives "
334*bdd1243dSDimitry Andric                      "corresponding to " +
335*bdd1243dSDimitry Andric                      Twine(InstructionBytes) + " bytes\n");
336*bdd1243dSDimitry Andric   }
337*bdd1243dSDimitry Andric }
338*bdd1243dSDimitry 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;
417*bdd1243dSDimitry Andric     case Win64EH::UOP_PACSignLR:
418*bdd1243dSDimitry Andric       Count += 1;
419*bdd1243dSDimitry Andric       break;
420*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegI:
421*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIP:
422*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegD:
423*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDP:
424*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQ:
425*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQP:
426*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIX:
427*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIPX:
428*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDX:
429*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDPX:
430*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQX:
431*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQPX:
432*bdd1243dSDimitry Andric       Count += 3;
433*bdd1243dSDimitry 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;
600*bdd1243dSDimitry Andric   case Win64EH::UOP_PACSignLR:
601*bdd1243dSDimitry Andric     b = 0xFC;
602*bdd1243dSDimitry Andric     streamer.emitInt8(b);
603*bdd1243dSDimitry Andric     break;
604*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegI:
605*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegIP:
606*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegD:
607*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegDP:
608*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQ:
609*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQP:
610*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegIX:
611*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegIPX:
612*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegDX:
613*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegDPX:
614*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQX:
615*bdd1243dSDimitry Andric   case Win64EH::UOP_SaveAnyRegQPX: {
616*bdd1243dSDimitry Andric     // This assumes the opcodes are listed in the enum in a particular order.
617*bdd1243dSDimitry Andric     int Op = inst.Operation - Win64EH::UOP_SaveAnyRegI;
618*bdd1243dSDimitry Andric     int Writeback = Op / 6;
619*bdd1243dSDimitry Andric     int Paired = Op % 2;
620*bdd1243dSDimitry Andric     int Mode = (Op / 2) % 3;
621*bdd1243dSDimitry Andric     int Offset = inst.Offset >> 3;
622*bdd1243dSDimitry Andric     if (Writeback || Paired || Mode == 2)
623*bdd1243dSDimitry Andric       Offset >>= 1;
624*bdd1243dSDimitry Andric     if (Writeback)
625*bdd1243dSDimitry Andric       --Offset;
626*bdd1243dSDimitry Andric     b = 0xE7;
627*bdd1243dSDimitry Andric     streamer.emitInt8(b);
628*bdd1243dSDimitry Andric     assert(inst.Register < 32);
629*bdd1243dSDimitry Andric     b = inst.Register | (Writeback << 5) | (Paired << 6);
630*bdd1243dSDimitry Andric     streamer.emitInt8(b);
631*bdd1243dSDimitry Andric     b = Offset | (Mode << 6);
632*bdd1243dSDimitry Andric     streamer.emitInt8(b);
633*bdd1243dSDimitry Andric     break;
634*bdd1243dSDimitry 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;
747*bdd1243dSDimitry Andric 
748*bdd1243dSDimitry 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,
754*bdd1243dSDimitry Andric                                   WinEH::FrameInfo::Segment *Seg,
755e8d8bef9SDimitry Andric                                   int PrologCodeBytes) {
756e8d8bef9SDimitry Andric   // Can only pack if there's one single epilog
757*bdd1243dSDimitry Andric   if (Seg->Epilogs.size() != 1)
758e8d8bef9SDimitry Andric     return -1;
759e8d8bef9SDimitry Andric 
760*bdd1243dSDimitry Andric   MCSymbol *Sym = Seg->Epilogs.begin()->first;
761e8d8bef9SDimitry Andric   const std::vector<WinEH::Instruction> &Epilog =
762*bdd1243dSDimitry 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.
766*bdd1243dSDimitry Andric   uint32_t DistanceFromEnd =
767*bdd1243dSDimitry 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.
792*bdd1243dSDimitry 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,
819*bdd1243dSDimitry 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;
828*bdd1243dSDimitry 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;
844*bdd1243dSDimitry Andric     case Win64EH::UOP_PACSignLR:
845e8d8bef9SDimitry Andric       if (Location != Start2)
846e8d8bef9SDimitry Andric         return false;
847*bdd1243dSDimitry Andric       PAC = true;
848*bdd1243dSDimitry Andric       Location = Start3;
849*bdd1243dSDimitry Andric       break;
850*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveR19R20X:
851*bdd1243dSDimitry Andric       if (Location != Start2 && Location != Start3)
852*bdd1243dSDimitry 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:
858*bdd1243dSDimitry 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:
921*bdd1243dSDimitry 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:
951*bdd1243dSDimitry Andric       if (Location != Start2 && Location != Start3 && Location != IntRegs &&
952*bdd1243dSDimitry Andric           Location != FloatRegs && Location != InputArgs &&
953*bdd1243dSDimitry 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.
968*bdd1243dSDimitry Andric       if (Location != Start2 && Location != Start3 && Location != IntRegs &&
969*bdd1243dSDimitry 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;
987*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegI:
988*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIP:
989*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegD:
990*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDP:
991*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQ:
992*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQP:
993*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIX:
994*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegIPX:
995*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDX:
996*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegDPX:
997*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQX:
998*bdd1243dSDimitry Andric     case Win64EH::UOP_SaveAnyRegQPX:
999*bdd1243dSDimitry Andric       // These are never canonical; they don't show up with the usual Arm64
1000*bdd1243dSDimitry Andric       // calling convention.
1001*bdd1243dSDimitry Andric       return false;
1002*bdd1243dSDimitry Andric     case Win64EH::UOP_AllocLarge:
1003*bdd1243dSDimitry Andric       // Allocations this large can't be represented in packed unwind (and
1004*bdd1243dSDimitry Andric       // usually don't fit the canonical form anyway because we need to use
1005*bdd1243dSDimitry Andric       // __chkstk to allocate the stack space).
1006*bdd1243dSDimitry Andric       return false;
1007*bdd1243dSDimitry Andric     case Win64EH::UOP_AddFP:
1008*bdd1243dSDimitry Andric       // "add x29, sp, #N" doesn't show up in the canonical pattern (except for
1009*bdd1243dSDimitry Andric       // N=0, which is UOP_SetFP).
1010*bdd1243dSDimitry Andric       return false;
1011*bdd1243dSDimitry Andric     case Win64EH::UOP_TrapFrame:
1012*bdd1243dSDimitry Andric     case Win64EH::UOP_Context:
1013*bdd1243dSDimitry Andric     case Win64EH::UOP_ClearUnwoundToCall:
1014*bdd1243dSDimitry Andric     case Win64EH::UOP_PushMachFrame:
1015*bdd1243dSDimitry Andric       // These are special opcodes that aren't normally generated.
1016*bdd1243dSDimitry Andric       return false;
1017*bdd1243dSDimitry Andric     default:
1018*bdd1243dSDimitry 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;
1029*bdd1243dSDimitry Andric   if (PAC && !FPLRPair)
1030*bdd1243dSDimitry 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
1061*bdd1243dSDimitry 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 
1072*bdd1243dSDimitry Andric static void ARM64ProcessEpilogs(WinEH::FrameInfo *info,
1073*bdd1243dSDimitry Andric                                 WinEH::FrameInfo::Segment *Seg,
1074*bdd1243dSDimitry Andric                                 uint32_t &TotalCodeBytes,
1075*bdd1243dSDimitry Andric                                 MapVector<MCSymbol *, uint32_t> &EpilogInfo) {
1076*bdd1243dSDimitry Andric 
1077*bdd1243dSDimitry Andric   std::vector<MCSymbol *> EpilogStarts;
1078*bdd1243dSDimitry Andric   for (auto &I : Seg->Epilogs)
1079*bdd1243dSDimitry Andric     EpilogStarts.push_back(I.first);
1080*bdd1243dSDimitry Andric 
1081*bdd1243dSDimitry Andric   // Epilogs processed so far.
1082*bdd1243dSDimitry Andric   std::vector<MCSymbol *> AddedEpilogs;
1083*bdd1243dSDimitry Andric   for (auto *S : EpilogStarts) {
1084*bdd1243dSDimitry Andric     MCSymbol *EpilogStart = S;
1085*bdd1243dSDimitry Andric     auto &EpilogInstrs = info->EpilogMap[S].Instructions;
1086*bdd1243dSDimitry Andric     uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs);
1087*bdd1243dSDimitry Andric 
1088*bdd1243dSDimitry Andric     MCSymbol* MatchingEpilog =
1089*bdd1243dSDimitry Andric       FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
1090*bdd1243dSDimitry Andric     int PrologOffset;
1091*bdd1243dSDimitry Andric     if (MatchingEpilog) {
1092*bdd1243dSDimitry Andric       assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() &&
1093*bdd1243dSDimitry Andric              "Duplicate epilog not found");
1094*bdd1243dSDimitry Andric       EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog);
1095*bdd1243dSDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
1096*bdd1243dSDimitry Andric       // in ARM64EmitUnwindInfoForSegment().
1097*bdd1243dSDimitry Andric       EpilogInstrs.clear();
1098*bdd1243dSDimitry Andric     } else if ((PrologOffset = getARM64OffsetInProlog(info->Instructions,
1099*bdd1243dSDimitry Andric                                                       EpilogInstrs)) >= 0) {
1100*bdd1243dSDimitry Andric       EpilogInfo[EpilogStart] = PrologOffset;
1101*bdd1243dSDimitry Andric       // If the segment doesn't have a prolog, an end_c will be emitted before
1102*bdd1243dSDimitry Andric       // prolog opcodes. So epilog start index in opcodes array is moved by 1.
1103*bdd1243dSDimitry Andric       if (!Seg->HasProlog)
1104*bdd1243dSDimitry Andric         EpilogInfo[EpilogStart] += 1;
1105*bdd1243dSDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
1106*bdd1243dSDimitry Andric       // in ARM64EmitUnwindInfoForSegment().
1107*bdd1243dSDimitry Andric       EpilogInstrs.clear();
1108*bdd1243dSDimitry Andric     } else {
1109*bdd1243dSDimitry Andric       EpilogInfo[EpilogStart] = TotalCodeBytes;
1110*bdd1243dSDimitry Andric       TotalCodeBytes += CodeBytes;
1111*bdd1243dSDimitry Andric       AddedEpilogs.push_back(EpilogStart);
1112*bdd1243dSDimitry Andric     }
1113*bdd1243dSDimitry Andric   }
1114*bdd1243dSDimitry Andric }
1115*bdd1243dSDimitry Andric 
1116*bdd1243dSDimitry Andric static void ARM64FindSegmentsInFunction(MCStreamer &streamer,
1117*bdd1243dSDimitry Andric                                         WinEH::FrameInfo *info,
1118*bdd1243dSDimitry Andric                                         int64_t RawFuncLength) {
1119*bdd1243dSDimitry Andric   if (info->PrologEnd)
1120*bdd1243dSDimitry Andric     checkARM64Instructions(streamer, info->Instructions, info->Begin,
1121*bdd1243dSDimitry Andric                            info->PrologEnd, info->Function->getName(),
1122*bdd1243dSDimitry Andric                            "prologue");
1123*bdd1243dSDimitry Andric   struct EpilogStartEnd {
1124*bdd1243dSDimitry Andric     MCSymbol *Start;
1125*bdd1243dSDimitry Andric     int64_t Offset;
1126*bdd1243dSDimitry Andric     int64_t End;
1127*bdd1243dSDimitry Andric   };
1128*bdd1243dSDimitry Andric   // Record Start and End of each epilog.
1129*bdd1243dSDimitry Andric   SmallVector<struct EpilogStartEnd, 4> Epilogs;
1130*bdd1243dSDimitry Andric   for (auto &I : info->EpilogMap) {
1131*bdd1243dSDimitry Andric     MCSymbol *Start = I.first;
1132*bdd1243dSDimitry Andric     auto &Instrs = I.second.Instructions;
1133*bdd1243dSDimitry Andric     int64_t Offset = GetAbsDifference(streamer, Start, info->Begin);
1134*bdd1243dSDimitry Andric     checkARM64Instructions(streamer, Instrs, Start, I.second.End,
1135*bdd1243dSDimitry Andric                            info->Function->getName(), "epilogue");
1136*bdd1243dSDimitry Andric     assert((Epilogs.size() == 0 || Offset >= Epilogs.back().End) &&
1137*bdd1243dSDimitry Andric            "Epilogs should be monotonically ordered");
1138*bdd1243dSDimitry Andric     // Exclue the end opcode from Instrs.size() when calculating the end of the
1139*bdd1243dSDimitry Andric     // epilog.
1140*bdd1243dSDimitry Andric     Epilogs.push_back({Start, Offset, Offset + (int64_t)(Instrs.size() - 1) * 4});
1141*bdd1243dSDimitry Andric   }
1142*bdd1243dSDimitry Andric 
1143*bdd1243dSDimitry Andric   unsigned E = 0;
1144*bdd1243dSDimitry Andric   int64_t SegLimit = 0xFFFFC;
1145*bdd1243dSDimitry Andric   int64_t SegOffset = 0;
1146*bdd1243dSDimitry Andric 
1147*bdd1243dSDimitry Andric   if (RawFuncLength > SegLimit) {
1148*bdd1243dSDimitry Andric 
1149*bdd1243dSDimitry Andric     int64_t RemainingLength = RawFuncLength;
1150*bdd1243dSDimitry Andric 
1151*bdd1243dSDimitry Andric     while (RemainingLength > SegLimit) {
1152*bdd1243dSDimitry Andric       // Try divide the function into segments, requirements:
1153*bdd1243dSDimitry Andric       // 1. Segment length <= 0xFFFFC;
1154*bdd1243dSDimitry Andric       // 2. Each Prologue or Epilogue must be fully within a segment.
1155*bdd1243dSDimitry Andric       int64_t SegLength = SegLimit;
1156*bdd1243dSDimitry Andric       int64_t SegEnd = SegOffset + SegLength;
1157*bdd1243dSDimitry Andric       // Keep record on symbols and offsets of epilogs in this segment.
1158*bdd1243dSDimitry Andric       MapVector<MCSymbol *, int64_t> EpilogsInSegment;
1159*bdd1243dSDimitry Andric 
1160*bdd1243dSDimitry Andric       while (E < Epilogs.size() && Epilogs[E].End < SegEnd) {
1161*bdd1243dSDimitry Andric         // Epilogs within current segment.
1162*bdd1243dSDimitry Andric         EpilogsInSegment[Epilogs[E].Start] = Epilogs[E].Offset;
1163*bdd1243dSDimitry Andric         ++E;
1164*bdd1243dSDimitry Andric       }
1165*bdd1243dSDimitry Andric 
1166*bdd1243dSDimitry Andric       // At this point, we have:
1167*bdd1243dSDimitry Andric       // 1. Put all epilogs in segments already. No action needed here; or
1168*bdd1243dSDimitry Andric       // 2. Found an epilog that will cross segments boundry. We need to
1169*bdd1243dSDimitry Andric       //    move back current segment's end boundry, so the epilog is entirely
1170*bdd1243dSDimitry Andric       //    in the next segment; or
1171*bdd1243dSDimitry Andric       // 3. Left at least one epilog that is entirely after this segment.
1172*bdd1243dSDimitry Andric       //    It'll be handled by the next iteration, or the last segment.
1173*bdd1243dSDimitry Andric       if (E < Epilogs.size() && Epilogs[E].Offset <= SegEnd)
1174*bdd1243dSDimitry Andric         // Move back current Segment's end boundry.
1175*bdd1243dSDimitry Andric         SegLength = Epilogs[E].Offset - SegOffset;
1176*bdd1243dSDimitry Andric 
1177*bdd1243dSDimitry Andric       auto Seg = WinEH::FrameInfo::Segment(
1178*bdd1243dSDimitry Andric           SegOffset, SegLength, /* HasProlog */!SegOffset);
1179*bdd1243dSDimitry Andric       Seg.Epilogs = std::move(EpilogsInSegment);
1180*bdd1243dSDimitry Andric       info->Segments.push_back(Seg);
1181*bdd1243dSDimitry Andric 
1182*bdd1243dSDimitry Andric       SegOffset += SegLength;
1183*bdd1243dSDimitry Andric       RemainingLength -= SegLength;
1184*bdd1243dSDimitry Andric     }
1185*bdd1243dSDimitry Andric   }
1186*bdd1243dSDimitry Andric 
1187*bdd1243dSDimitry Andric   // Add the last segment when RawFuncLength > 0xFFFFC,
1188*bdd1243dSDimitry Andric   // or the only segment otherwise.
1189*bdd1243dSDimitry Andric   auto LastSeg =
1190*bdd1243dSDimitry Andric       WinEH::FrameInfo::Segment(SegOffset, RawFuncLength - SegOffset,
1191*bdd1243dSDimitry Andric                                 /* HasProlog */!SegOffset);
1192*bdd1243dSDimitry Andric   for (; E < Epilogs.size(); ++E)
1193*bdd1243dSDimitry Andric     LastSeg.Epilogs[Epilogs[E].Start] = Epilogs[E].Offset;
1194*bdd1243dSDimitry Andric   info->Segments.push_back(LastSeg);
1195*bdd1243dSDimitry Andric }
1196*bdd1243dSDimitry Andric 
1197*bdd1243dSDimitry Andric static void ARM64EmitUnwindInfoForSegment(MCStreamer &streamer,
1198*bdd1243dSDimitry Andric                                           WinEH::FrameInfo *info,
1199*bdd1243dSDimitry Andric                                           WinEH::FrameInfo::Segment &Seg,
1200*bdd1243dSDimitry Andric                                           bool TryPacked = true) {
1201*bdd1243dSDimitry Andric   MCContext &context = streamer.getContext();
1202*bdd1243dSDimitry Andric   MCSymbol *Label = context.createTempSymbol();
1203*bdd1243dSDimitry Andric 
1204*bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
1205*bdd1243dSDimitry Andric   streamer.emitLabel(Label);
1206*bdd1243dSDimitry Andric   Seg.Symbol = Label;
1207*bdd1243dSDimitry Andric   // Use the 1st segemnt's label as function's.
1208*bdd1243dSDimitry Andric   if (Seg.Offset == 0)
1209*bdd1243dSDimitry Andric     info->Symbol = Label;
1210*bdd1243dSDimitry Andric 
1211*bdd1243dSDimitry Andric   bool HasProlog = Seg.HasProlog;
1212*bdd1243dSDimitry Andric   bool HasEpilogs = (Seg.Epilogs.size() != 0);
1213*bdd1243dSDimitry Andric 
1214*bdd1243dSDimitry Andric   uint32_t SegLength = (uint32_t)Seg.Length / 4;
1215*bdd1243dSDimitry Andric   uint32_t PrologCodeBytes = info->PrologCodeBytes;
1216*bdd1243dSDimitry Andric 
1217*bdd1243dSDimitry Andric   int PackedEpilogOffset = HasEpilogs ?
1218*bdd1243dSDimitry Andric       checkARM64PackedEpilog(streamer, info, &Seg, PrologCodeBytes) : -1;
1219*bdd1243dSDimitry Andric 
1220*bdd1243dSDimitry Andric   // TODO:
1221*bdd1243dSDimitry Andric   // 1. Enable packed unwind info (.pdata only) for multi-segment functions.
1222*bdd1243dSDimitry Andric   // 2. Emit packed unwind info (.pdata only) for segments that have neithor
1223*bdd1243dSDimitry Andric   //    prolog nor epilog.
1224*bdd1243dSDimitry Andric   if (info->Segments.size() == 1 && PackedEpilogOffset >= 0 &&
1225*bdd1243dSDimitry Andric       uint32_t(PackedEpilogOffset) < PrologCodeBytes &&
1226*bdd1243dSDimitry Andric       !info->HandlesExceptions && SegLength <= 0x7ff && TryPacked) {
1227*bdd1243dSDimitry Andric     // Matching prolog/epilog and no exception handlers; check if the
1228*bdd1243dSDimitry Andric     // prolog matches the patterns that can be described by the packed
1229*bdd1243dSDimitry Andric     // format.
1230*bdd1243dSDimitry Andric 
1231*bdd1243dSDimitry Andric     // info->Symbol was already set even if we didn't actually write any
1232*bdd1243dSDimitry Andric     // unwind info there. Keep using that as indicator that this unwind
1233*bdd1243dSDimitry Andric     // info has been generated already.
1234*bdd1243dSDimitry Andric     if (tryARM64PackedUnwind(info, SegLength, PackedEpilogOffset))
1235*bdd1243dSDimitry Andric       return;
1236*bdd1243dSDimitry Andric   }
1237*bdd1243dSDimitry Andric 
1238*bdd1243dSDimitry Andric   // If the prolog is not in this segment, we need to emit an end_c, which takes
1239*bdd1243dSDimitry Andric   // 1 byte, before prolog unwind ops.
1240*bdd1243dSDimitry Andric   if (!HasProlog) {
1241*bdd1243dSDimitry Andric     PrologCodeBytes += 1;
1242*bdd1243dSDimitry Andric     if (PackedEpilogOffset >= 0)
1243*bdd1243dSDimitry Andric       PackedEpilogOffset += 1;
1244*bdd1243dSDimitry Andric     // If a segment has neither prolog nor epilog, "With full .xdata record,
1245*bdd1243dSDimitry Andric     // Epilog Count = 1. Epilog Start Index points to end_c."
1246*bdd1243dSDimitry Andric     // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
1247*bdd1243dSDimitry Andric     // TODO: We can remove this if testing shows zero epilog scope is ok with
1248*bdd1243dSDimitry Andric     //       MS unwinder.
1249*bdd1243dSDimitry Andric     if (!HasEpilogs)
1250*bdd1243dSDimitry Andric       // Pack the fake epilog into phantom prolog.
1251*bdd1243dSDimitry Andric       PackedEpilogOffset = 0;
1252*bdd1243dSDimitry Andric   }
1253*bdd1243dSDimitry Andric 
1254*bdd1243dSDimitry Andric   uint32_t TotalCodeBytes = PrologCodeBytes;
1255*bdd1243dSDimitry Andric 
1256*bdd1243dSDimitry Andric   // Process epilogs.
1257*bdd1243dSDimitry Andric   MapVector<MCSymbol *, uint32_t> EpilogInfo;
1258*bdd1243dSDimitry Andric   ARM64ProcessEpilogs(info, &Seg, TotalCodeBytes, EpilogInfo);
1259*bdd1243dSDimitry Andric 
1260*bdd1243dSDimitry Andric   // Code Words, Epilog count, E, X, Vers, Function Length
1261*bdd1243dSDimitry Andric   uint32_t row1 = 0x0;
1262*bdd1243dSDimitry Andric   uint32_t CodeWords = TotalCodeBytes / 4;
1263*bdd1243dSDimitry Andric   uint32_t CodeWordsMod = TotalCodeBytes % 4;
1264*bdd1243dSDimitry Andric   if (CodeWordsMod)
1265*bdd1243dSDimitry Andric     CodeWords++;
1266*bdd1243dSDimitry Andric   uint32_t EpilogCount =
1267*bdd1243dSDimitry Andric       PackedEpilogOffset >= 0 ? PackedEpilogOffset : Seg.Epilogs.size();
1268*bdd1243dSDimitry Andric   bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124;
1269*bdd1243dSDimitry Andric   if (!ExtensionWord) {
1270*bdd1243dSDimitry Andric     row1 |= (EpilogCount & 0x1F) << 22;
1271*bdd1243dSDimitry Andric     row1 |= (CodeWords & 0x1F) << 27;
1272*bdd1243dSDimitry Andric   }
1273*bdd1243dSDimitry Andric   if (info->HandlesExceptions) // X
1274*bdd1243dSDimitry Andric     row1 |= 1 << 20;
1275*bdd1243dSDimitry Andric   if (PackedEpilogOffset >= 0) // E
1276*bdd1243dSDimitry Andric     row1 |= 1 << 21;
1277*bdd1243dSDimitry Andric   row1 |= SegLength & 0x3FFFF;
1278*bdd1243dSDimitry Andric   streamer.emitInt32(row1);
1279*bdd1243dSDimitry Andric 
1280*bdd1243dSDimitry Andric   // Extended Code Words, Extended Epilog Count
1281*bdd1243dSDimitry Andric   if (ExtensionWord) {
1282*bdd1243dSDimitry Andric     // FIXME: We should be able to split unwind info into multiple sections.
1283*bdd1243dSDimitry Andric     if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
1284*bdd1243dSDimitry Andric       report_fatal_error(
1285*bdd1243dSDimitry Andric           "SEH unwind data splitting is only implemented for large functions, "
1286*bdd1243dSDimitry Andric           "cases of too many code words or too many epilogs will be done "
1287*bdd1243dSDimitry Andric           "later");
1288*bdd1243dSDimitry Andric     uint32_t row2 = 0x0;
1289*bdd1243dSDimitry Andric     row2 |= (CodeWords & 0xFF) << 16;
1290*bdd1243dSDimitry Andric     row2 |= (EpilogCount & 0xFFFF);
1291*bdd1243dSDimitry Andric     streamer.emitInt32(row2);
1292*bdd1243dSDimitry Andric   }
1293*bdd1243dSDimitry Andric 
1294*bdd1243dSDimitry Andric   if (PackedEpilogOffset < 0) {
1295*bdd1243dSDimitry Andric     // Epilog Start Index, Epilog Start Offset
1296*bdd1243dSDimitry Andric     for (auto &I : EpilogInfo) {
1297*bdd1243dSDimitry Andric       MCSymbol *EpilogStart = I.first;
1298*bdd1243dSDimitry Andric       uint32_t EpilogIndex = I.second;
1299*bdd1243dSDimitry Andric       // Epilog offset within the Segment.
1300*bdd1243dSDimitry Andric       uint32_t EpilogOffset = (uint32_t)(Seg.Epilogs[EpilogStart] - Seg.Offset);
1301*bdd1243dSDimitry Andric       if (EpilogOffset)
1302*bdd1243dSDimitry Andric         EpilogOffset /= 4;
1303*bdd1243dSDimitry Andric       uint32_t row3 = EpilogOffset;
1304*bdd1243dSDimitry Andric       row3 |= (EpilogIndex & 0x3FF) << 22;
1305*bdd1243dSDimitry Andric       streamer.emitInt32(row3);
1306*bdd1243dSDimitry Andric     }
1307*bdd1243dSDimitry Andric   }
1308*bdd1243dSDimitry Andric 
1309*bdd1243dSDimitry Andric   // Note that even for segments that have no prolog, we still need to emit
1310*bdd1243dSDimitry Andric   // prolog unwinding opcodes so that the unwinder knows how to unwind from
1311*bdd1243dSDimitry Andric   // such a segment.
1312*bdd1243dSDimitry Andric   // The end_c opcode at the start indicates to the unwinder that the actual
1313*bdd1243dSDimitry Andric   // prolog is outside of the current segment, and the unwinder shouldn't try
1314*bdd1243dSDimitry Andric   // to check for unwinding from a partial prolog.
1315*bdd1243dSDimitry Andric   if (!HasProlog)
1316*bdd1243dSDimitry Andric     // Emit an end_c.
1317*bdd1243dSDimitry Andric     streamer.emitInt8((uint8_t)0xE5);
1318*bdd1243dSDimitry Andric 
1319*bdd1243dSDimitry Andric   // Emit prolog unwind instructions (in reverse order).
1320*bdd1243dSDimitry Andric   for (auto Inst : llvm::reverse(info->Instructions))
1321*bdd1243dSDimitry Andric     ARM64EmitUnwindCode(streamer, Inst);
1322*bdd1243dSDimitry Andric 
1323*bdd1243dSDimitry Andric   // Emit epilog unwind instructions
1324*bdd1243dSDimitry Andric   for (auto &I : Seg.Epilogs) {
1325*bdd1243dSDimitry Andric     auto &EpilogInstrs = info->EpilogMap[I.first].Instructions;
1326*bdd1243dSDimitry Andric     for (const WinEH::Instruction &inst : EpilogInstrs)
1327*bdd1243dSDimitry Andric       ARM64EmitUnwindCode(streamer, inst);
1328*bdd1243dSDimitry Andric   }
1329*bdd1243dSDimitry Andric 
1330*bdd1243dSDimitry Andric   int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
1331*bdd1243dSDimitry Andric   assert(BytesMod >= 0);
1332*bdd1243dSDimitry Andric   for (int i = 0; i < BytesMod; i++)
1333*bdd1243dSDimitry Andric     streamer.emitInt8(0xE3);
1334*bdd1243dSDimitry Andric 
1335*bdd1243dSDimitry Andric   if (info->HandlesExceptions)
1336*bdd1243dSDimitry Andric     streamer.emitValue(
1337*bdd1243dSDimitry Andric         MCSymbolRefExpr::create(info->ExceptionHandler,
1338*bdd1243dSDimitry Andric                                 MCSymbolRefExpr::VK_COFF_IMGREL32, context),
1339*bdd1243dSDimitry Andric         4);
1340*bdd1243dSDimitry Andric }
1341*bdd1243dSDimitry 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.
14050b57cec5SDimitry Andric     RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd,
14060b57cec5SDimitry Andric                                      info->Begin);
14070b57cec5SDimitry Andric   }
14080b57cec5SDimitry Andric 
1409*bdd1243dSDimitry Andric   ARM64FindSegmentsInFunction(streamer, info, RawFuncLength);
1410e8d8bef9SDimitry Andric 
1411*bdd1243dSDimitry Andric   info->PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
1412*bdd1243dSDimitry Andric   for (auto &S : info->Segments)
1413*bdd1243dSDimitry Andric     ARM64EmitUnwindInfoForSegment(streamer, info, S, TryPacked);
1414e8d8bef9SDimitry Andric 
1415*bdd1243dSDimitry Andric   // Clear prolog instructions after unwind info is emitted for all segments.
1416*bdd1243dSDimitry Andric   info->Instructions.clear();
14170b57cec5SDimitry Andric }
14180b57cec5SDimitry Andric 
141981ad6265SDimitry Andric static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
142081ad6265SDimitry Andric   uint32_t Count = 0;
142181ad6265SDimitry Andric   for (const auto &I : Insns) {
142281ad6265SDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
142381ad6265SDimitry Andric     default:
142481ad6265SDimitry Andric       llvm_unreachable("Unsupported ARM unwind code");
142581ad6265SDimitry Andric     case Win64EH::UOP_AllocSmall:
142681ad6265SDimitry Andric       Count += 1;
142781ad6265SDimitry Andric       break;
142881ad6265SDimitry Andric     case Win64EH::UOP_AllocLarge:
142981ad6265SDimitry Andric       Count += 3;
143081ad6265SDimitry Andric       break;
143181ad6265SDimitry Andric     case Win64EH::UOP_AllocHuge:
143281ad6265SDimitry Andric       Count += 4;
143381ad6265SDimitry Andric       break;
143481ad6265SDimitry Andric     case Win64EH::UOP_WideAllocMedium:
143581ad6265SDimitry Andric       Count += 2;
143681ad6265SDimitry Andric       break;
143781ad6265SDimitry Andric     case Win64EH::UOP_WideAllocLarge:
143881ad6265SDimitry Andric       Count += 3;
143981ad6265SDimitry Andric       break;
144081ad6265SDimitry Andric     case Win64EH::UOP_WideAllocHuge:
144181ad6265SDimitry Andric       Count += 4;
144281ad6265SDimitry Andric       break;
144381ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegMask:
144481ad6265SDimitry Andric       Count += 2;
144581ad6265SDimitry Andric       break;
144681ad6265SDimitry Andric     case Win64EH::UOP_SaveSP:
144781ad6265SDimitry Andric       Count += 1;
144881ad6265SDimitry Andric       break;
144981ad6265SDimitry Andric     case Win64EH::UOP_SaveRegsR4R7LR:
145081ad6265SDimitry Andric       Count += 1;
145181ad6265SDimitry Andric       break;
145281ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegsR4R11LR:
145381ad6265SDimitry Andric       Count += 1;
145481ad6265SDimitry Andric       break;
145581ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD8D15:
145681ad6265SDimitry Andric       Count += 1;
145781ad6265SDimitry Andric       break;
145881ad6265SDimitry Andric     case Win64EH::UOP_SaveRegMask:
145981ad6265SDimitry Andric       Count += 2;
146081ad6265SDimitry Andric       break;
146181ad6265SDimitry Andric     case Win64EH::UOP_SaveLR:
146281ad6265SDimitry Andric       Count += 2;
146381ad6265SDimitry Andric       break;
146481ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD0D15:
146581ad6265SDimitry Andric       Count += 2;
146681ad6265SDimitry Andric       break;
146781ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD16D31:
146881ad6265SDimitry Andric       Count += 2;
146981ad6265SDimitry Andric       break;
147081ad6265SDimitry Andric     case Win64EH::UOP_Nop:
147181ad6265SDimitry Andric     case Win64EH::UOP_WideNop:
147281ad6265SDimitry Andric     case Win64EH::UOP_End:
147381ad6265SDimitry Andric     case Win64EH::UOP_EndNop:
147481ad6265SDimitry Andric     case Win64EH::UOP_WideEndNop:
147581ad6265SDimitry Andric       Count += 1;
147681ad6265SDimitry Andric       break;
147781ad6265SDimitry Andric     case Win64EH::UOP_Custom: {
147881ad6265SDimitry Andric       int J;
147981ad6265SDimitry Andric       for (J = 3; J > 0; J--)
148081ad6265SDimitry Andric         if (I.Offset & (0xffu << (8 * J)))
148181ad6265SDimitry Andric           break;
148281ad6265SDimitry Andric       Count += J + 1;
148381ad6265SDimitry Andric       break;
148481ad6265SDimitry Andric     }
148581ad6265SDimitry Andric     }
148681ad6265SDimitry Andric   }
148781ad6265SDimitry Andric   return Count;
148881ad6265SDimitry Andric }
148981ad6265SDimitry Andric 
149081ad6265SDimitry Andric static uint32_t ARMCountOfInstructionBytes(ArrayRef<WinEH::Instruction> Insns,
149181ad6265SDimitry Andric                                            bool *HasCustom = nullptr) {
149281ad6265SDimitry Andric   uint32_t Count = 0;
149381ad6265SDimitry Andric   for (const auto &I : Insns) {
149481ad6265SDimitry Andric     switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
149581ad6265SDimitry Andric     default:
149681ad6265SDimitry Andric       llvm_unreachable("Unsupported ARM unwind code");
149781ad6265SDimitry Andric     case Win64EH::UOP_AllocSmall:
149881ad6265SDimitry Andric     case Win64EH::UOP_AllocLarge:
149981ad6265SDimitry Andric     case Win64EH::UOP_AllocHuge:
150081ad6265SDimitry Andric       Count += 2;
150181ad6265SDimitry Andric       break;
150281ad6265SDimitry Andric     case Win64EH::UOP_WideAllocMedium:
150381ad6265SDimitry Andric     case Win64EH::UOP_WideAllocLarge:
150481ad6265SDimitry Andric     case Win64EH::UOP_WideAllocHuge:
150581ad6265SDimitry Andric       Count += 4;
150681ad6265SDimitry Andric       break;
150781ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegMask:
150881ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegsR4R11LR:
150981ad6265SDimitry Andric       Count += 4;
151081ad6265SDimitry Andric       break;
151181ad6265SDimitry Andric     case Win64EH::UOP_SaveSP:
151281ad6265SDimitry Andric       Count += 2;
151381ad6265SDimitry Andric       break;
151481ad6265SDimitry Andric     case Win64EH::UOP_SaveRegMask:
151581ad6265SDimitry Andric     case Win64EH::UOP_SaveRegsR4R7LR:
151681ad6265SDimitry Andric       Count += 2;
151781ad6265SDimitry Andric       break;
151881ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD8D15:
151981ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD0D15:
152081ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD16D31:
152181ad6265SDimitry Andric       Count += 4;
152281ad6265SDimitry Andric       break;
152381ad6265SDimitry Andric     case Win64EH::UOP_SaveLR:
152481ad6265SDimitry Andric       Count += 4;
152581ad6265SDimitry Andric       break;
152681ad6265SDimitry Andric     case Win64EH::UOP_Nop:
152781ad6265SDimitry Andric     case Win64EH::UOP_EndNop:
152881ad6265SDimitry Andric       Count += 2;
152981ad6265SDimitry Andric       break;
153081ad6265SDimitry Andric     case Win64EH::UOP_WideNop:
153181ad6265SDimitry Andric     case Win64EH::UOP_WideEndNop:
153281ad6265SDimitry Andric       Count += 4;
153381ad6265SDimitry Andric       break;
153481ad6265SDimitry Andric     case Win64EH::UOP_End:
153581ad6265SDimitry Andric       // This doesn't map to any instruction
153681ad6265SDimitry Andric       break;
153781ad6265SDimitry Andric     case Win64EH::UOP_Custom:
153881ad6265SDimitry Andric       // We can't reason about what instructions this maps to; return a
153981ad6265SDimitry Andric       // phony number to make sure we don't accidentally do epilog packing.
154081ad6265SDimitry Andric       Count += 1000;
154181ad6265SDimitry Andric       if (HasCustom)
154281ad6265SDimitry Andric         *HasCustom = true;
154381ad6265SDimitry Andric       break;
154481ad6265SDimitry Andric     }
154581ad6265SDimitry Andric   }
154681ad6265SDimitry Andric   return Count;
154781ad6265SDimitry Andric }
154881ad6265SDimitry Andric 
154981ad6265SDimitry Andric static void checkARMInstructions(MCStreamer &Streamer,
155081ad6265SDimitry Andric                                  ArrayRef<WinEH::Instruction> Insns,
155181ad6265SDimitry Andric                                  const MCSymbol *Begin, const MCSymbol *End,
155281ad6265SDimitry Andric                                  StringRef Name, StringRef Type) {
155381ad6265SDimitry Andric   if (!End)
155481ad6265SDimitry Andric     return;
1555*bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDistance =
155681ad6265SDimitry Andric       GetOptionalAbsDifference(Streamer, End, Begin);
155781ad6265SDimitry Andric   if (!MaybeDistance)
155881ad6265SDimitry Andric     return;
155981ad6265SDimitry Andric   uint32_t Distance = (uint32_t)*MaybeDistance;
156081ad6265SDimitry Andric   bool HasCustom = false;
156181ad6265SDimitry Andric   uint32_t InstructionBytes = ARMCountOfInstructionBytes(Insns, &HasCustom);
156281ad6265SDimitry Andric   if (HasCustom)
156381ad6265SDimitry Andric     return;
156481ad6265SDimitry Andric   if (Distance != InstructionBytes) {
156581ad6265SDimitry Andric     Streamer.getContext().reportError(
156681ad6265SDimitry Andric         SMLoc(), "Incorrect size for " + Name + " " + Type + ": " +
156781ad6265SDimitry Andric                      Twine(Distance) +
156881ad6265SDimitry Andric                      " bytes of instructions in range, but .seh directives "
156981ad6265SDimitry Andric                      "corresponding to " +
157081ad6265SDimitry Andric                      Twine(InstructionBytes) + " bytes\n");
157181ad6265SDimitry Andric   }
157281ad6265SDimitry Andric }
157381ad6265SDimitry Andric 
157481ad6265SDimitry Andric static bool isARMTerminator(const WinEH::Instruction &inst) {
157581ad6265SDimitry Andric   switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
157681ad6265SDimitry Andric   case Win64EH::UOP_End:
157781ad6265SDimitry Andric   case Win64EH::UOP_EndNop:
157881ad6265SDimitry Andric   case Win64EH::UOP_WideEndNop:
157981ad6265SDimitry Andric     return true;
158081ad6265SDimitry Andric   default:
158181ad6265SDimitry Andric     return false;
158281ad6265SDimitry Andric   }
158381ad6265SDimitry Andric }
158481ad6265SDimitry Andric 
158581ad6265SDimitry Andric // Unwind opcode encodings and restrictions are documented at
158681ad6265SDimitry Andric // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
158781ad6265SDimitry Andric static void ARMEmitUnwindCode(MCStreamer &streamer,
158881ad6265SDimitry Andric                               const WinEH::Instruction &inst) {
158981ad6265SDimitry Andric   uint32_t w, lr;
159081ad6265SDimitry Andric   int i;
159181ad6265SDimitry Andric   switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
159281ad6265SDimitry Andric   default:
159381ad6265SDimitry Andric     llvm_unreachable("Unsupported ARM unwind code");
159481ad6265SDimitry Andric   case Win64EH::UOP_AllocSmall:
159581ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
159681ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0x7f);
159781ad6265SDimitry Andric     streamer.emitInt8(inst.Offset / 4);
159881ad6265SDimitry Andric     break;
159981ad6265SDimitry Andric   case Win64EH::UOP_WideSaveRegMask:
160081ad6265SDimitry Andric     assert((inst.Register & ~0x5fff) == 0);
160181ad6265SDimitry Andric     lr = (inst.Register >> 14) & 1;
160281ad6265SDimitry Andric     w = 0x8000 | (inst.Register & 0x1fff) | (lr << 13);
160381ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
160481ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
160581ad6265SDimitry Andric     break;
160681ad6265SDimitry Andric   case Win64EH::UOP_SaveSP:
160781ad6265SDimitry Andric     assert(inst.Register <= 0x0f);
160881ad6265SDimitry Andric     streamer.emitInt8(0xc0 | inst.Register);
160981ad6265SDimitry Andric     break;
161081ad6265SDimitry Andric   case Win64EH::UOP_SaveRegsR4R7LR:
161181ad6265SDimitry Andric     assert(inst.Register >= 4 && inst.Register <= 7);
161281ad6265SDimitry Andric     assert(inst.Offset <= 1);
161381ad6265SDimitry Andric     streamer.emitInt8(0xd0 | (inst.Register - 4) | (inst.Offset << 2));
161481ad6265SDimitry Andric     break;
161581ad6265SDimitry Andric   case Win64EH::UOP_WideSaveRegsR4R11LR:
161681ad6265SDimitry Andric     assert(inst.Register >= 8 && inst.Register <= 11);
161781ad6265SDimitry Andric     assert(inst.Offset <= 1);
161881ad6265SDimitry Andric     streamer.emitInt8(0xd8 | (inst.Register - 8) | (inst.Offset << 2));
161981ad6265SDimitry Andric     break;
162081ad6265SDimitry Andric   case Win64EH::UOP_SaveFRegD8D15:
162181ad6265SDimitry Andric     assert(inst.Register >= 8 && inst.Register <= 15);
162281ad6265SDimitry Andric     streamer.emitInt8(0xe0 | (inst.Register - 8));
162381ad6265SDimitry Andric     break;
162481ad6265SDimitry Andric   case Win64EH::UOP_WideAllocMedium:
162581ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
162681ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0x3ff);
162781ad6265SDimitry Andric     w = 0xe800 | (inst.Offset / 4);
162881ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
162981ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
163081ad6265SDimitry Andric     break;
163181ad6265SDimitry Andric   case Win64EH::UOP_SaveRegMask:
163281ad6265SDimitry Andric     assert((inst.Register & ~0x40ff) == 0);
163381ad6265SDimitry Andric     lr = (inst.Register >> 14) & 1;
163481ad6265SDimitry Andric     w = 0xec00 | (inst.Register & 0x0ff) | (lr << 8);
163581ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
163681ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
163781ad6265SDimitry Andric     break;
163881ad6265SDimitry Andric   case Win64EH::UOP_SaveLR:
163981ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
164081ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0x0f);
164181ad6265SDimitry Andric     streamer.emitInt8(0xef);
164281ad6265SDimitry Andric     streamer.emitInt8(inst.Offset / 4);
164381ad6265SDimitry Andric     break;
164481ad6265SDimitry Andric   case Win64EH::UOP_SaveFRegD0D15:
164581ad6265SDimitry Andric     assert(inst.Register <= 15);
164681ad6265SDimitry Andric     assert(inst.Offset <= 15);
164781ad6265SDimitry Andric     assert(inst.Register <= inst.Offset);
164881ad6265SDimitry Andric     streamer.emitInt8(0xf5);
164981ad6265SDimitry Andric     streamer.emitInt8((inst.Register << 4) | inst.Offset);
165081ad6265SDimitry Andric     break;
165181ad6265SDimitry Andric   case Win64EH::UOP_SaveFRegD16D31:
165281ad6265SDimitry Andric     assert(inst.Register >= 16 && inst.Register <= 31);
165381ad6265SDimitry Andric     assert(inst.Offset >= 16 && inst.Offset <= 31);
165481ad6265SDimitry Andric     assert(inst.Register <= inst.Offset);
165581ad6265SDimitry Andric     streamer.emitInt8(0xf6);
165681ad6265SDimitry Andric     streamer.emitInt8(((inst.Register - 16) << 4) | (inst.Offset - 16));
165781ad6265SDimitry Andric     break;
165881ad6265SDimitry Andric   case Win64EH::UOP_AllocLarge:
165981ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
166081ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffff);
166181ad6265SDimitry Andric     w = inst.Offset / 4;
166281ad6265SDimitry Andric     streamer.emitInt8(0xf7);
166381ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
166481ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
166581ad6265SDimitry Andric     break;
166681ad6265SDimitry Andric   case Win64EH::UOP_AllocHuge:
166781ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
166881ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffffff);
166981ad6265SDimitry Andric     w = inst.Offset / 4;
167081ad6265SDimitry Andric     streamer.emitInt8(0xf8);
167181ad6265SDimitry Andric     streamer.emitInt8((w >> 16) & 0xff);
167281ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
167381ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
167481ad6265SDimitry Andric     break;
167581ad6265SDimitry Andric   case Win64EH::UOP_WideAllocLarge:
167681ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
167781ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffff);
167881ad6265SDimitry Andric     w = inst.Offset / 4;
167981ad6265SDimitry Andric     streamer.emitInt8(0xf9);
168081ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
168181ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
168281ad6265SDimitry Andric     break;
168381ad6265SDimitry Andric   case Win64EH::UOP_WideAllocHuge:
168481ad6265SDimitry Andric     assert((inst.Offset & 3) == 0);
168581ad6265SDimitry Andric     assert(inst.Offset / 4 <= 0xffffff);
168681ad6265SDimitry Andric     w = inst.Offset / 4;
168781ad6265SDimitry Andric     streamer.emitInt8(0xfa);
168881ad6265SDimitry Andric     streamer.emitInt8((w >> 16) & 0xff);
168981ad6265SDimitry Andric     streamer.emitInt8((w >> 8) & 0xff);
169081ad6265SDimitry Andric     streamer.emitInt8((w >> 0) & 0xff);
169181ad6265SDimitry Andric     break;
169281ad6265SDimitry Andric   case Win64EH::UOP_Nop:
169381ad6265SDimitry Andric     streamer.emitInt8(0xfb);
169481ad6265SDimitry Andric     break;
169581ad6265SDimitry Andric   case Win64EH::UOP_WideNop:
169681ad6265SDimitry Andric     streamer.emitInt8(0xfc);
169781ad6265SDimitry Andric     break;
169881ad6265SDimitry Andric   case Win64EH::UOP_EndNop:
169981ad6265SDimitry Andric     streamer.emitInt8(0xfd);
170081ad6265SDimitry Andric     break;
170181ad6265SDimitry Andric   case Win64EH::UOP_WideEndNop:
170281ad6265SDimitry Andric     streamer.emitInt8(0xfe);
170381ad6265SDimitry Andric     break;
170481ad6265SDimitry Andric   case Win64EH::UOP_End:
170581ad6265SDimitry Andric     streamer.emitInt8(0xff);
170681ad6265SDimitry Andric     break;
170781ad6265SDimitry Andric   case Win64EH::UOP_Custom:
170881ad6265SDimitry Andric     for (i = 3; i > 0; i--)
170981ad6265SDimitry Andric       if (inst.Offset & (0xffu << (8 * i)))
171081ad6265SDimitry Andric         break;
171181ad6265SDimitry Andric     for (; i >= 0; i--)
171281ad6265SDimitry Andric       streamer.emitInt8((inst.Offset >> (8 * i)) & 0xff);
171381ad6265SDimitry Andric     break;
171481ad6265SDimitry Andric   }
171581ad6265SDimitry Andric }
171681ad6265SDimitry Andric 
171781ad6265SDimitry Andric // Check if an epilog exists as a subset of the end of a prolog (backwards).
171881ad6265SDimitry Andric // An epilog may end with one out of three different end opcodes; if this
171981ad6265SDimitry Andric // is the first epilog that shares opcodes with the prolog, we can tolerate
172081ad6265SDimitry Andric // that this opcode differs (and the caller will update the prolog to use
172181ad6265SDimitry Andric // the same end opcode as the epilog). If another epilog already shares
172281ad6265SDimitry Andric // opcodes with the prolog, the ending opcode must be a strict match.
172381ad6265SDimitry Andric static int getARMOffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
172481ad6265SDimitry Andric                                 const std::vector<WinEH::Instruction> &Epilog,
172581ad6265SDimitry Andric                                 bool CanTweakProlog) {
172681ad6265SDimitry Andric   // Can't find an epilog as a subset if it is longer than the prolog.
172781ad6265SDimitry Andric   if (Epilog.size() > Prolog.size())
172881ad6265SDimitry Andric     return -1;
172981ad6265SDimitry Andric 
173081ad6265SDimitry Andric   // Check that the epilog actually is a perfect match for the end (backwrds)
173181ad6265SDimitry Andric   // of the prolog.
173281ad6265SDimitry Andric   // If we can adjust the prolog afterwards, don't check that the end opcodes
173381ad6265SDimitry Andric   // match.
173481ad6265SDimitry Andric   int EndIdx = CanTweakProlog ? 1 : 0;
173581ad6265SDimitry Andric   for (int I = Epilog.size() - 1; I >= EndIdx; I--) {
173681ad6265SDimitry Andric     // TODO: Could also allow minor mismatches, e.g. "add sp, #16" vs
173781ad6265SDimitry Andric     // "push {r0-r3}".
173881ad6265SDimitry Andric     if (Prolog[I] != Epilog[Epilog.size() - 1 - I])
173981ad6265SDimitry Andric       return -1;
174081ad6265SDimitry Andric   }
174181ad6265SDimitry Andric 
174281ad6265SDimitry Andric   if (CanTweakProlog) {
174381ad6265SDimitry Andric     // Check that both prolog and epilog end with an expected end opcode.
174481ad6265SDimitry Andric     if (Prolog.front().Operation != Win64EH::UOP_End)
174581ad6265SDimitry Andric       return -1;
174681ad6265SDimitry Andric     if (Epilog.back().Operation != Win64EH::UOP_End &&
174781ad6265SDimitry Andric         Epilog.back().Operation != Win64EH::UOP_EndNop &&
174881ad6265SDimitry Andric         Epilog.back().Operation != Win64EH::UOP_WideEndNop)
174981ad6265SDimitry Andric       return -1;
175081ad6265SDimitry Andric   }
175181ad6265SDimitry Andric 
175281ad6265SDimitry Andric   // If the epilog was a subset of the prolog, find its offset.
175381ad6265SDimitry Andric   if (Epilog.size() == Prolog.size())
175481ad6265SDimitry Andric     return 0;
175581ad6265SDimitry Andric   return ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction>(
175681ad6265SDimitry Andric       &Prolog[Epilog.size()], Prolog.size() - Epilog.size()));
175781ad6265SDimitry Andric }
175881ad6265SDimitry Andric 
175981ad6265SDimitry Andric static int checkARMPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
176081ad6265SDimitry Andric                                 int PrologCodeBytes) {
176181ad6265SDimitry Andric   // Can only pack if there's one single epilog
176281ad6265SDimitry Andric   if (info->EpilogMap.size() != 1)
176381ad6265SDimitry Andric     return -1;
176481ad6265SDimitry Andric 
176581ad6265SDimitry Andric   const WinEH::FrameInfo::Epilog &EpilogInfo = info->EpilogMap.begin()->second;
176681ad6265SDimitry Andric   // Can only pack if the epilog is unconditional
176781ad6265SDimitry Andric   if (EpilogInfo.Condition != 0xe) // ARMCC::AL
176881ad6265SDimitry Andric     return -1;
176981ad6265SDimitry Andric 
177081ad6265SDimitry Andric   const std::vector<WinEH::Instruction> &Epilog = EpilogInfo.Instructions;
177181ad6265SDimitry Andric   // Make sure we have at least the trailing end opcode
177281ad6265SDimitry Andric   if (info->Instructions.empty() || Epilog.empty())
177381ad6265SDimitry Andric     return -1;
177481ad6265SDimitry Andric 
177581ad6265SDimitry Andric   // Check that the epilog actually is at the very end of the function,
177681ad6265SDimitry Andric   // otherwise it can't be packed.
1777*bdd1243dSDimitry Andric   std::optional<int64_t> MaybeDistance = GetOptionalAbsDifference(
177881ad6265SDimitry Andric       streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first);
177981ad6265SDimitry Andric   if (!MaybeDistance)
178081ad6265SDimitry Andric     return -1;
178181ad6265SDimitry Andric   uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance;
178281ad6265SDimitry Andric   uint32_t InstructionBytes = ARMCountOfInstructionBytes(Epilog);
178381ad6265SDimitry Andric   if (DistanceFromEnd != InstructionBytes)
178481ad6265SDimitry Andric     return -1;
178581ad6265SDimitry Andric 
178681ad6265SDimitry Andric   int RetVal = -1;
178781ad6265SDimitry Andric   // Even if we don't end up sharing opcodes with the prolog, we can still
178881ad6265SDimitry Andric   // write the offset as a packed offset, if the single epilog is located at
178981ad6265SDimitry Andric   // the end of the function and the offset (pointing after the prolog) fits
179081ad6265SDimitry Andric   // as a packed offset.
179181ad6265SDimitry Andric   if (PrologCodeBytes <= 31 &&
179281ad6265SDimitry Andric       PrologCodeBytes + ARMCountOfUnwindCodes(Epilog) <= 63)
179381ad6265SDimitry Andric     RetVal = PrologCodeBytes;
179481ad6265SDimitry Andric 
179581ad6265SDimitry Andric   int Offset =
179681ad6265SDimitry Andric       getARMOffsetInProlog(info->Instructions, Epilog, /*CanTweakProlog=*/true);
179781ad6265SDimitry Andric   if (Offset < 0)
179881ad6265SDimitry Andric     return RetVal;
179981ad6265SDimitry Andric 
180081ad6265SDimitry Andric   // Check that the offset and prolog size fits in the first word; it's
180181ad6265SDimitry Andric   // unclear whether the epilog count in the extension word can be taken
180281ad6265SDimitry Andric   // as packed epilog offset.
180381ad6265SDimitry Andric   if (Offset > 31 || PrologCodeBytes > 63)
180481ad6265SDimitry Andric     return RetVal;
180581ad6265SDimitry Andric 
180681ad6265SDimitry Andric   // Replace the regular end opcode of the prolog with the one from the
180781ad6265SDimitry Andric   // epilog.
180881ad6265SDimitry Andric   info->Instructions.front() = Epilog.back();
180981ad6265SDimitry Andric 
181081ad6265SDimitry Andric   // As we choose to express the epilog as part of the prolog, remove the
181181ad6265SDimitry Andric   // epilog from the map, so we don't try to emit its opcodes.
181281ad6265SDimitry Andric   info->EpilogMap.clear();
181381ad6265SDimitry Andric   return Offset;
181481ad6265SDimitry Andric }
181581ad6265SDimitry Andric 
181681ad6265SDimitry Andric static bool parseRegMask(unsigned Mask, bool &HasLR, bool &HasR11,
181781ad6265SDimitry Andric                          unsigned &Folded, int &IntRegs) {
181881ad6265SDimitry Andric   if (Mask & (1 << 14)) {
181981ad6265SDimitry Andric     HasLR = true;
182081ad6265SDimitry Andric     Mask &= ~(1 << 14);
182181ad6265SDimitry Andric   }
182281ad6265SDimitry Andric   if (Mask & (1 << 11)) {
182381ad6265SDimitry Andric     HasR11 = true;
182481ad6265SDimitry Andric     Mask &= ~(1 << 11);
182581ad6265SDimitry Andric   }
182681ad6265SDimitry Andric   Folded = 0;
182781ad6265SDimitry Andric   IntRegs = -1;
182881ad6265SDimitry Andric   if (!Mask)
182981ad6265SDimitry Andric     return true;
183081ad6265SDimitry Andric   int First = 0;
183181ad6265SDimitry Andric   // Shift right until we have the bits at the bottom
183281ad6265SDimitry Andric   while ((Mask & 1) == 0) {
183381ad6265SDimitry Andric     First++;
183481ad6265SDimitry Andric     Mask >>= 1;
183581ad6265SDimitry Andric   }
183681ad6265SDimitry Andric   if ((Mask & (Mask + 1)) != 0)
183781ad6265SDimitry Andric     return false; // Not a consecutive series of bits? Can't be packed.
183881ad6265SDimitry Andric   // Count the bits
183981ad6265SDimitry Andric   int N = 0;
184081ad6265SDimitry Andric   while (Mask & (1 << N))
184181ad6265SDimitry Andric     N++;
184281ad6265SDimitry Andric   if (First < 4) {
184381ad6265SDimitry Andric     if (First + N < 4)
184481ad6265SDimitry Andric       return false;
184581ad6265SDimitry Andric     Folded = 4 - First;
184681ad6265SDimitry Andric     N -= Folded;
184781ad6265SDimitry Andric     First = 4;
184881ad6265SDimitry Andric   }
184981ad6265SDimitry Andric   if (First > 4)
185081ad6265SDimitry Andric     return false; // Can't be packed
185181ad6265SDimitry Andric   if (N >= 1)
185281ad6265SDimitry Andric     IntRegs = N - 1;
185381ad6265SDimitry Andric   return true;
185481ad6265SDimitry Andric }
185581ad6265SDimitry Andric 
185681ad6265SDimitry Andric static bool tryARMPackedUnwind(MCStreamer &streamer, WinEH::FrameInfo *info,
185781ad6265SDimitry Andric                                uint32_t FuncLength) {
185881ad6265SDimitry Andric   int Step = 0;
185981ad6265SDimitry Andric   bool Homing = false;
186081ad6265SDimitry Andric   bool HasR11 = false;
186181ad6265SDimitry Andric   bool HasChain = false;
186281ad6265SDimitry Andric   bool HasLR = false;
186381ad6265SDimitry Andric   int IntRegs = -1;   // r4 - r(4+N)
186481ad6265SDimitry Andric   int FloatRegs = -1; // d8 - d(8+N)
186581ad6265SDimitry Andric   unsigned PF = 0;    // Number of extra pushed registers
186681ad6265SDimitry Andric   unsigned StackAdjust = 0;
186781ad6265SDimitry Andric   // Iterate over the prolog and check that all opcodes exactly match
186881ad6265SDimitry Andric   // the canonical order and form.
186981ad6265SDimitry Andric   for (const WinEH::Instruction &Inst : info->Instructions) {
187081ad6265SDimitry Andric     switch (Inst.Operation) {
187181ad6265SDimitry Andric     default:
187281ad6265SDimitry Andric       llvm_unreachable("Unsupported ARM unwind code");
187381ad6265SDimitry Andric     case Win64EH::UOP_Custom:
187481ad6265SDimitry Andric     case Win64EH::UOP_AllocLarge:
187581ad6265SDimitry Andric     case Win64EH::UOP_AllocHuge:
187681ad6265SDimitry Andric     case Win64EH::UOP_WideAllocLarge:
187781ad6265SDimitry Andric     case Win64EH::UOP_WideAllocHuge:
187881ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD0D15:
187981ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD16D31:
188081ad6265SDimitry Andric       // Can't be packed
188181ad6265SDimitry Andric       return false;
188281ad6265SDimitry Andric     case Win64EH::UOP_SaveSP:
188381ad6265SDimitry Andric       // Can't be packed; we can't rely on restoring sp from r11 when
188481ad6265SDimitry Andric       // unwinding a packed prologue.
188581ad6265SDimitry Andric       return false;
188681ad6265SDimitry Andric     case Win64EH::UOP_SaveLR:
188781ad6265SDimitry Andric       // Can't be present in a packed prologue
188881ad6265SDimitry Andric       return false;
188981ad6265SDimitry Andric 
189081ad6265SDimitry Andric     case Win64EH::UOP_End:
189181ad6265SDimitry Andric     case Win64EH::UOP_EndNop:
189281ad6265SDimitry Andric     case Win64EH::UOP_WideEndNop:
189381ad6265SDimitry Andric       if (Step != 0)
189481ad6265SDimitry Andric         return false;
189581ad6265SDimitry Andric       Step = 1;
189681ad6265SDimitry Andric       break;
189781ad6265SDimitry Andric 
189881ad6265SDimitry Andric     case Win64EH::UOP_SaveRegsR4R7LR:
189981ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegsR4R11LR:
190081ad6265SDimitry Andric       // push {r4-r11,lr}
190181ad6265SDimitry Andric       if (Step != 1 && Step != 2)
190281ad6265SDimitry Andric         return false;
190381ad6265SDimitry Andric       assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX
190481ad6265SDimitry Andric       assert(Inst.Offset <= 1);                          // Lr
190581ad6265SDimitry Andric       IntRegs = Inst.Register - 4;
190681ad6265SDimitry Andric       if (Inst.Register == 11) {
190781ad6265SDimitry Andric         HasR11 = true;
190881ad6265SDimitry Andric         IntRegs--;
190981ad6265SDimitry Andric       }
191081ad6265SDimitry Andric       if (Inst.Offset)
191181ad6265SDimitry Andric         HasLR = true;
191281ad6265SDimitry Andric       Step = 3;
191381ad6265SDimitry Andric       break;
191481ad6265SDimitry Andric 
191581ad6265SDimitry Andric     case Win64EH::UOP_SaveRegMask:
191681ad6265SDimitry Andric       if (Step == 1 && Inst.Register == 0x0f) {
191781ad6265SDimitry Andric         // push {r0-r3}
191881ad6265SDimitry Andric         Homing = true;
191981ad6265SDimitry Andric         Step = 2;
192081ad6265SDimitry Andric         break;
192181ad6265SDimitry Andric       }
1922*bdd1243dSDimitry Andric       [[fallthrough]];
192381ad6265SDimitry Andric     case Win64EH::UOP_WideSaveRegMask:
192481ad6265SDimitry Andric       if (Step != 1 && Step != 2)
192581ad6265SDimitry Andric         return false;
192681ad6265SDimitry Andric       // push {r4-r9,r11,lr}
192781ad6265SDimitry Andric       // push {r11,lr}
192881ad6265SDimitry Andric       // push {r1-r5}
192981ad6265SDimitry Andric       if (!parseRegMask(Inst.Register, HasLR, HasR11, PF, IntRegs))
193081ad6265SDimitry Andric         return false;
193181ad6265SDimitry Andric       Step = 3;
193281ad6265SDimitry Andric       break;
193381ad6265SDimitry Andric 
193481ad6265SDimitry Andric     case Win64EH::UOP_Nop:
193581ad6265SDimitry Andric       // mov r11, sp
193681ad6265SDimitry Andric       if (Step != 3 || !HasR11 || IntRegs >= 0 || PF > 0)
193781ad6265SDimitry Andric         return false;
193881ad6265SDimitry Andric       HasChain = true;
193981ad6265SDimitry Andric       Step = 4;
194081ad6265SDimitry Andric       break;
194181ad6265SDimitry Andric     case Win64EH::UOP_WideNop:
194281ad6265SDimitry Andric       // add.w r11, sp, #xx
194381ad6265SDimitry Andric       if (Step != 3 || !HasR11 || (IntRegs < 0 && PF == 0))
194481ad6265SDimitry Andric         return false;
194581ad6265SDimitry Andric       HasChain = true;
194681ad6265SDimitry Andric       Step = 4;
194781ad6265SDimitry Andric       break;
194881ad6265SDimitry Andric 
194981ad6265SDimitry Andric     case Win64EH::UOP_SaveFRegD8D15:
195081ad6265SDimitry Andric       if (Step != 1 && Step != 2 && Step != 3 && Step != 4)
195181ad6265SDimitry Andric         return false;
195281ad6265SDimitry Andric       assert(Inst.Register >= 8 && Inst.Register <= 15);
195381ad6265SDimitry Andric       if (Inst.Register == 15)
195481ad6265SDimitry Andric         return false; // Can't pack this case, R==7 means no IntRegs
195581ad6265SDimitry Andric       if (IntRegs >= 0)
195681ad6265SDimitry Andric         return false;
195781ad6265SDimitry Andric       FloatRegs = Inst.Register - 8;
195881ad6265SDimitry Andric       Step = 5;
195981ad6265SDimitry Andric       break;
196081ad6265SDimitry Andric 
196181ad6265SDimitry Andric     case Win64EH::UOP_AllocSmall:
196281ad6265SDimitry Andric     case Win64EH::UOP_WideAllocMedium:
196381ad6265SDimitry Andric       if (Step != 1 && Step != 2 && Step != 3 && Step != 4 && Step != 5)
196481ad6265SDimitry Andric         return false;
196581ad6265SDimitry Andric       if (PF > 0) // Can't have both folded and explicit stack allocation
196681ad6265SDimitry Andric         return false;
196781ad6265SDimitry Andric       if (Inst.Offset / 4 >= 0x3f4)
196881ad6265SDimitry Andric         return false;
196981ad6265SDimitry Andric       StackAdjust = Inst.Offset / 4;
197081ad6265SDimitry Andric       Step = 6;
197181ad6265SDimitry Andric       break;
197281ad6265SDimitry Andric     }
197381ad6265SDimitry Andric   }
197481ad6265SDimitry Andric   if (HasR11 && !HasChain) {
197581ad6265SDimitry Andric     if (IntRegs + 4 == 10) {
197681ad6265SDimitry Andric       // r11 stored, but not chaining; can be packed if already saving r4-r10
197781ad6265SDimitry Andric       // and we can fit r11 into this range.
197881ad6265SDimitry Andric       IntRegs++;
197981ad6265SDimitry Andric       HasR11 = false;
198081ad6265SDimitry Andric     } else
198181ad6265SDimitry Andric       return false;
198281ad6265SDimitry Andric   }
198381ad6265SDimitry Andric   if (HasChain && !HasLR)
198481ad6265SDimitry Andric     return false;
198581ad6265SDimitry Andric 
198681ad6265SDimitry Andric   // Packed uneind info can't express multiple epilogues.
198781ad6265SDimitry Andric   if (info->EpilogMap.size() > 1)
198881ad6265SDimitry Andric     return false;
198981ad6265SDimitry Andric 
199081ad6265SDimitry Andric   unsigned EF = 0;
199181ad6265SDimitry Andric   int Ret = 0;
199281ad6265SDimitry Andric   if (info->EpilogMap.size() == 0) {
199381ad6265SDimitry Andric     Ret = 3; // No epilogue
199481ad6265SDimitry Andric   } else {
199581ad6265SDimitry Andric     // As the prologue and epilogue aren't exact mirrors of each other,
199681ad6265SDimitry Andric     // we have to check the epilogue too and see if it matches what we've
199781ad6265SDimitry Andric     // concluded from the prologue.
199881ad6265SDimitry Andric     const WinEH::FrameInfo::Epilog &EpilogInfo =
199981ad6265SDimitry Andric         info->EpilogMap.begin()->second;
200081ad6265SDimitry Andric     if (EpilogInfo.Condition != 0xe) // ARMCC::AL
200181ad6265SDimitry Andric       return false;
200281ad6265SDimitry Andric     const std::vector<WinEH::Instruction> &Epilog = EpilogInfo.Instructions;
2003*bdd1243dSDimitry Andric     std::optional<int64_t> MaybeDistance = GetOptionalAbsDifference(
200481ad6265SDimitry Andric         streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first);
200581ad6265SDimitry Andric     if (!MaybeDistance)
200681ad6265SDimitry Andric       return false;
200781ad6265SDimitry Andric     uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance;
200881ad6265SDimitry Andric     uint32_t InstructionBytes = ARMCountOfInstructionBytes(Epilog);
200981ad6265SDimitry Andric     if (DistanceFromEnd != InstructionBytes)
201081ad6265SDimitry Andric       return false;
201181ad6265SDimitry Andric 
201281ad6265SDimitry Andric     bool GotStackAdjust = false;
201381ad6265SDimitry Andric     bool GotFloatRegs = false;
201481ad6265SDimitry Andric     bool GotIntRegs = false;
201581ad6265SDimitry Andric     bool GotHomingRestore = false;
201681ad6265SDimitry Andric     bool GotLRRestore = false;
201781ad6265SDimitry Andric     bool NeedsReturn = false;
201881ad6265SDimitry Andric     bool GotReturn = false;
201981ad6265SDimitry Andric 
202081ad6265SDimitry Andric     Step = 6;
202181ad6265SDimitry Andric     for (const WinEH::Instruction &Inst : Epilog) {
202281ad6265SDimitry Andric       switch (Inst.Operation) {
202381ad6265SDimitry Andric       default:
202481ad6265SDimitry Andric         llvm_unreachable("Unsupported ARM unwind code");
202581ad6265SDimitry Andric       case Win64EH::UOP_Custom:
202681ad6265SDimitry Andric       case Win64EH::UOP_AllocLarge:
202781ad6265SDimitry Andric       case Win64EH::UOP_AllocHuge:
202881ad6265SDimitry Andric       case Win64EH::UOP_WideAllocLarge:
202981ad6265SDimitry Andric       case Win64EH::UOP_WideAllocHuge:
203081ad6265SDimitry Andric       case Win64EH::UOP_SaveFRegD0D15:
203181ad6265SDimitry Andric       case Win64EH::UOP_SaveFRegD16D31:
203281ad6265SDimitry Andric       case Win64EH::UOP_SaveSP:
203381ad6265SDimitry Andric       case Win64EH::UOP_Nop:
203481ad6265SDimitry Andric       case Win64EH::UOP_WideNop:
203581ad6265SDimitry Andric         // Can't be packed in an epilogue
203681ad6265SDimitry Andric         return false;
203781ad6265SDimitry Andric 
203881ad6265SDimitry Andric       case Win64EH::UOP_AllocSmall:
203981ad6265SDimitry Andric       case Win64EH::UOP_WideAllocMedium:
204081ad6265SDimitry Andric         if (Inst.Offset / 4 >= 0x3f4)
204181ad6265SDimitry Andric           return false;
204281ad6265SDimitry Andric         if (Step == 6) {
204381ad6265SDimitry Andric           if (Homing && FloatRegs < 0 && IntRegs < 0 && StackAdjust == 0 &&
204481ad6265SDimitry Andric               PF == 0 && Inst.Offset == 16) {
204581ad6265SDimitry Andric             GotHomingRestore = true;
204681ad6265SDimitry Andric             Step = 10;
204781ad6265SDimitry Andric           } else {
204881ad6265SDimitry Andric             if (StackAdjust > 0) {
204981ad6265SDimitry Andric               // Got stack adjust in prologue too; must match.
205081ad6265SDimitry Andric               if (StackAdjust != Inst.Offset / 4)
205181ad6265SDimitry Andric                 return false;
205281ad6265SDimitry Andric               GotStackAdjust = true;
205381ad6265SDimitry Andric             } else if (PF == Inst.Offset / 4) {
205481ad6265SDimitry Andric               // Folded prologue, non-folded epilogue
205581ad6265SDimitry Andric               StackAdjust = Inst.Offset / 4;
205681ad6265SDimitry Andric               GotStackAdjust = true;
205781ad6265SDimitry Andric             } else {
205881ad6265SDimitry Andric               // StackAdjust == 0 in prologue, mismatch
205981ad6265SDimitry Andric               return false;
206081ad6265SDimitry Andric             }
206181ad6265SDimitry Andric             Step = 7;
206281ad6265SDimitry Andric           }
206381ad6265SDimitry Andric         } else if (Step == 7 || Step == 8 || Step == 9) {
206481ad6265SDimitry Andric           if (!Homing || Inst.Offset != 16)
206581ad6265SDimitry Andric             return false;
206681ad6265SDimitry Andric           GotHomingRestore = true;
206781ad6265SDimitry Andric           Step = 10;
206881ad6265SDimitry Andric         } else
206981ad6265SDimitry Andric           return false;
207081ad6265SDimitry Andric         break;
207181ad6265SDimitry Andric 
207281ad6265SDimitry Andric       case Win64EH::UOP_SaveFRegD8D15:
207381ad6265SDimitry Andric         if (Step != 6 && Step != 7)
207481ad6265SDimitry Andric           return false;
207581ad6265SDimitry Andric         assert(Inst.Register >= 8 && Inst.Register <= 15);
207681ad6265SDimitry Andric         if (FloatRegs != (int)(Inst.Register - 8))
207781ad6265SDimitry Andric           return false;
207881ad6265SDimitry Andric         GotFloatRegs = true;
207981ad6265SDimitry Andric         Step = 8;
208081ad6265SDimitry Andric         break;
208181ad6265SDimitry Andric 
208281ad6265SDimitry Andric       case Win64EH::UOP_SaveRegsR4R7LR:
208381ad6265SDimitry Andric       case Win64EH::UOP_WideSaveRegsR4R11LR: {
208481ad6265SDimitry Andric         // push {r4-r11,lr}
208581ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8)
208681ad6265SDimitry Andric           return false;
208781ad6265SDimitry Andric         assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX
208881ad6265SDimitry Andric         assert(Inst.Offset <= 1);                          // Lr
208981ad6265SDimitry Andric         if (Homing && HasLR) {
209081ad6265SDimitry Andric           // If homing and LR is backed up, we can either restore LR here
209181ad6265SDimitry Andric           // and return with Ret == 1 or 2, or return with SaveLR below
209281ad6265SDimitry Andric           if (Inst.Offset) {
209381ad6265SDimitry Andric             GotLRRestore = true;
209481ad6265SDimitry Andric             NeedsReturn = true;
209581ad6265SDimitry Andric           } else {
209681ad6265SDimitry Andric             // Expecting a separate SaveLR below
209781ad6265SDimitry Andric           }
209881ad6265SDimitry Andric         } else {
209981ad6265SDimitry Andric           if (HasLR != (Inst.Offset == 1))
210081ad6265SDimitry Andric             return false;
210181ad6265SDimitry Andric         }
210281ad6265SDimitry Andric         GotLRRestore = Inst.Offset == 1;
210381ad6265SDimitry Andric         if (IntRegs < 0) // This opcode must include r4
210481ad6265SDimitry Andric           return false;
210581ad6265SDimitry Andric         int Expected = IntRegs;
210681ad6265SDimitry Andric         if (HasChain) {
210781ad6265SDimitry Andric           // Can't express r11 here unless IntRegs describe r4-r10
210881ad6265SDimitry Andric           if (IntRegs != 6)
210981ad6265SDimitry Andric             return false;
211081ad6265SDimitry Andric           Expected++;
211181ad6265SDimitry Andric         }
211281ad6265SDimitry Andric         if (Expected != (int)(Inst.Register - 4))
211381ad6265SDimitry Andric           return false;
211481ad6265SDimitry Andric         GotIntRegs = true;
211581ad6265SDimitry Andric         Step = 9;
211681ad6265SDimitry Andric         break;
211781ad6265SDimitry Andric       }
211881ad6265SDimitry Andric 
211981ad6265SDimitry Andric       case Win64EH::UOP_SaveRegMask:
212081ad6265SDimitry Andric       case Win64EH::UOP_WideSaveRegMask: {
212181ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8)
212281ad6265SDimitry Andric           return false;
212381ad6265SDimitry Andric         // push {r4-r9,r11,lr}
212481ad6265SDimitry Andric         // push {r11,lr}
212581ad6265SDimitry Andric         // push {r1-r5}
212681ad6265SDimitry Andric         bool CurHasLR = false, CurHasR11 = false;
212781ad6265SDimitry Andric         int Regs;
212881ad6265SDimitry Andric         if (!parseRegMask(Inst.Register, CurHasLR, CurHasR11, EF, Regs))
212981ad6265SDimitry Andric           return false;
213081ad6265SDimitry Andric         if (EF > 0) {
213181ad6265SDimitry Andric           if (EF != PF && EF != StackAdjust)
213281ad6265SDimitry Andric             return false;
213381ad6265SDimitry Andric         }
213481ad6265SDimitry Andric         if (Homing && HasLR) {
213581ad6265SDimitry Andric           // If homing and LR is backed up, we can either restore LR here
213681ad6265SDimitry Andric           // and return with Ret == 1 or 2, or return with SaveLR below
213781ad6265SDimitry Andric           if (CurHasLR) {
213881ad6265SDimitry Andric             GotLRRestore = true;
213981ad6265SDimitry Andric             NeedsReturn = true;
214081ad6265SDimitry Andric           } else {
214181ad6265SDimitry Andric             // Expecting a separate SaveLR below
214281ad6265SDimitry Andric           }
214381ad6265SDimitry Andric         } else {
214481ad6265SDimitry Andric           if (CurHasLR != HasLR)
214581ad6265SDimitry Andric             return false;
214681ad6265SDimitry Andric           GotLRRestore = CurHasLR;
214781ad6265SDimitry Andric         }
214881ad6265SDimitry Andric         int Expected = IntRegs;
214981ad6265SDimitry Andric         if (HasChain) {
215081ad6265SDimitry Andric           // If we have chaining, the mask must have included r11.
215181ad6265SDimitry Andric           if (!CurHasR11)
215281ad6265SDimitry Andric             return false;
215381ad6265SDimitry Andric         } else if (Expected == 7) {
215481ad6265SDimitry Andric           // If we don't have chaining, the mask could still include r11,
215581ad6265SDimitry Andric           // expressed as part of IntRegs Instead.
215681ad6265SDimitry Andric           Expected--;
215781ad6265SDimitry Andric           if (!CurHasR11)
215881ad6265SDimitry Andric             return false;
215981ad6265SDimitry Andric         } else {
216081ad6265SDimitry Andric           // Neither HasChain nor r11 included in IntRegs, must not have r11
216181ad6265SDimitry Andric           // here either.
216281ad6265SDimitry Andric           if (CurHasR11)
216381ad6265SDimitry Andric             return false;
216481ad6265SDimitry Andric         }
216581ad6265SDimitry Andric         if (Expected != Regs)
216681ad6265SDimitry Andric           return false;
216781ad6265SDimitry Andric         GotIntRegs = true;
216881ad6265SDimitry Andric         Step = 9;
216981ad6265SDimitry Andric         break;
217081ad6265SDimitry Andric       }
217181ad6265SDimitry Andric 
217281ad6265SDimitry Andric       case Win64EH::UOP_SaveLR:
217381ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8 && Step != 9)
217481ad6265SDimitry Andric           return false;
217581ad6265SDimitry Andric         if (!Homing || Inst.Offset != 20 || GotLRRestore)
217681ad6265SDimitry Andric           return false;
217781ad6265SDimitry Andric         GotLRRestore = true;
217881ad6265SDimitry Andric         GotHomingRestore = true;
217981ad6265SDimitry Andric         Step = 10;
218081ad6265SDimitry Andric         break;
218181ad6265SDimitry Andric 
218281ad6265SDimitry Andric       case Win64EH::UOP_EndNop:
218381ad6265SDimitry Andric       case Win64EH::UOP_WideEndNop:
218481ad6265SDimitry Andric         GotReturn = true;
218581ad6265SDimitry Andric         Ret = (Inst.Operation == Win64EH::UOP_EndNop) ? 1 : 2;
2186*bdd1243dSDimitry Andric         [[fallthrough]];
218781ad6265SDimitry Andric       case Win64EH::UOP_End:
218881ad6265SDimitry Andric         if (Step != 6 && Step != 7 && Step != 8 && Step != 9 && Step != 10)
218981ad6265SDimitry Andric           return false;
219081ad6265SDimitry Andric         Step = 11;
219181ad6265SDimitry Andric         break;
219281ad6265SDimitry Andric       }
219381ad6265SDimitry Andric     }
219481ad6265SDimitry Andric 
219581ad6265SDimitry Andric     if (Step != 11)
219681ad6265SDimitry Andric       return false;
219781ad6265SDimitry Andric     if (StackAdjust > 0 && !GotStackAdjust && EF == 0)
219881ad6265SDimitry Andric       return false;
219981ad6265SDimitry Andric     if (FloatRegs >= 0 && !GotFloatRegs)
220081ad6265SDimitry Andric       return false;
220181ad6265SDimitry Andric     if (IntRegs >= 0 && !GotIntRegs)
220281ad6265SDimitry Andric       return false;
220381ad6265SDimitry Andric     if (Homing && !GotHomingRestore)
220481ad6265SDimitry Andric       return false;
220581ad6265SDimitry Andric     if (HasLR && !GotLRRestore)
220681ad6265SDimitry Andric       return false;
220781ad6265SDimitry Andric     if (NeedsReturn && !GotReturn)
220881ad6265SDimitry Andric       return false;
220981ad6265SDimitry Andric   }
221081ad6265SDimitry Andric 
221181ad6265SDimitry Andric   assert(PF == 0 || EF == 0 ||
221281ad6265SDimitry Andric          StackAdjust == 0); // Can't have adjust in all three
221381ad6265SDimitry Andric   if (PF > 0 || EF > 0) {
221481ad6265SDimitry Andric     StackAdjust = PF > 0 ? (PF - 1) : (EF - 1);
221581ad6265SDimitry Andric     assert(StackAdjust <= 3);
221681ad6265SDimitry Andric     StackAdjust |= 0x3f0;
221781ad6265SDimitry Andric     if (PF > 0)
221881ad6265SDimitry Andric       StackAdjust |= 1 << 2;
221981ad6265SDimitry Andric     if (EF > 0)
222081ad6265SDimitry Andric       StackAdjust |= 1 << 3;
222181ad6265SDimitry Andric   }
222281ad6265SDimitry Andric 
222381ad6265SDimitry Andric   assert(FuncLength <= 0x7FF && "FuncLength should have been checked earlier");
222481ad6265SDimitry Andric   int Flag = info->Fragment ? 0x02 : 0x01;
222581ad6265SDimitry Andric   int H = Homing ? 1 : 0;
222681ad6265SDimitry Andric   int L = HasLR ? 1 : 0;
222781ad6265SDimitry Andric   int C = HasChain ? 1 : 0;
222881ad6265SDimitry Andric   assert(IntRegs < 0 || FloatRegs < 0);
222981ad6265SDimitry Andric   unsigned Reg, R;
223081ad6265SDimitry Andric   if (IntRegs >= 0) {
223181ad6265SDimitry Andric     Reg = IntRegs;
223281ad6265SDimitry Andric     assert(Reg <= 7);
223381ad6265SDimitry Andric     R = 0;
223481ad6265SDimitry Andric   } else if (FloatRegs >= 0) {
223581ad6265SDimitry Andric     Reg = FloatRegs;
223681ad6265SDimitry Andric     assert(Reg < 7);
223781ad6265SDimitry Andric     R = 1;
223881ad6265SDimitry Andric   } else {
223981ad6265SDimitry Andric     // No int or float regs stored (except possibly R11,LR)
224081ad6265SDimitry Andric     Reg = 7;
224181ad6265SDimitry Andric     R = 1;
224281ad6265SDimitry Andric   }
224381ad6265SDimitry Andric   info->PackedInfo |= Flag << 0;
224481ad6265SDimitry Andric   info->PackedInfo |= (FuncLength & 0x7FF) << 2;
224581ad6265SDimitry Andric   info->PackedInfo |= (Ret & 0x3) << 13;
224681ad6265SDimitry Andric   info->PackedInfo |= H << 15;
224781ad6265SDimitry Andric   info->PackedInfo |= Reg << 16;
224881ad6265SDimitry Andric   info->PackedInfo |= R << 19;
224981ad6265SDimitry Andric   info->PackedInfo |= L << 20;
225081ad6265SDimitry Andric   info->PackedInfo |= C << 21;
225181ad6265SDimitry Andric   assert(StackAdjust <= 0x3ff);
225281ad6265SDimitry Andric   info->PackedInfo |= StackAdjust << 22;
225381ad6265SDimitry Andric   return true;
225481ad6265SDimitry Andric }
225581ad6265SDimitry Andric 
225681ad6265SDimitry Andric // Populate the .xdata section.  The format of .xdata on ARM is documented at
225781ad6265SDimitry Andric // https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
225881ad6265SDimitry Andric static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
225981ad6265SDimitry Andric                               bool TryPacked = true) {
226081ad6265SDimitry Andric   // If this UNWIND_INFO already has a symbol, it's already been emitted.
226181ad6265SDimitry Andric   if (info->Symbol)
226281ad6265SDimitry Andric     return;
226381ad6265SDimitry Andric   // If there's no unwind info here (not even a terminating UOP_End), the
226481ad6265SDimitry Andric   // unwind info is considered bogus and skipped. If this was done in
226581ad6265SDimitry Andric   // response to an explicit .seh_handlerdata, the associated trailing
226681ad6265SDimitry Andric   // handler data is left orphaned in the xdata section.
226781ad6265SDimitry Andric   if (info->empty()) {
226881ad6265SDimitry Andric     info->EmitAttempted = true;
226981ad6265SDimitry Andric     return;
227081ad6265SDimitry Andric   }
227181ad6265SDimitry Andric   if (info->EmitAttempted) {
227281ad6265SDimitry Andric     // If we tried to emit unwind info before (due to an explicit
227381ad6265SDimitry Andric     // .seh_handlerdata directive), but skipped it (because there was no
227481ad6265SDimitry Andric     // valid information to emit at the time), and it later got valid unwind
227581ad6265SDimitry Andric     // opcodes, we can't emit it here, because the trailing handler data
227681ad6265SDimitry Andric     // was already emitted elsewhere in the xdata section.
227781ad6265SDimitry Andric     streamer.getContext().reportError(
227881ad6265SDimitry Andric         SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() +
227981ad6265SDimitry Andric                      " skipped due to no unwind info at the time "
228081ad6265SDimitry Andric                      "(.seh_handlerdata too early?), but the function later "
228181ad6265SDimitry Andric                      "did get unwind info that can't be emitted");
228281ad6265SDimitry Andric     return;
228381ad6265SDimitry Andric   }
228481ad6265SDimitry Andric 
228581ad6265SDimitry Andric   MCContext &context = streamer.getContext();
228681ad6265SDimitry Andric   MCSymbol *Label = context.createTempSymbol();
228781ad6265SDimitry Andric 
2288*bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
228981ad6265SDimitry Andric   streamer.emitLabel(Label);
229081ad6265SDimitry Andric   info->Symbol = Label;
229181ad6265SDimitry Andric 
229281ad6265SDimitry Andric   if (!info->PrologEnd)
229381ad6265SDimitry Andric     streamer.getContext().reportError(SMLoc(), "Prologue in " +
229481ad6265SDimitry Andric                                                    info->Function->getName() +
229581ad6265SDimitry Andric                                                    " not correctly terminated");
229681ad6265SDimitry Andric 
229781ad6265SDimitry Andric   if (info->PrologEnd && !info->Fragment)
229881ad6265SDimitry Andric     checkARMInstructions(streamer, info->Instructions, info->Begin,
229981ad6265SDimitry Andric                          info->PrologEnd, info->Function->getName(),
230081ad6265SDimitry Andric                          "prologue");
230181ad6265SDimitry Andric   for (auto &I : info->EpilogMap) {
230281ad6265SDimitry Andric     MCSymbol *EpilogStart = I.first;
230381ad6265SDimitry Andric     auto &Epilog = I.second;
230481ad6265SDimitry Andric     checkARMInstructions(streamer, Epilog.Instructions, EpilogStart, Epilog.End,
230581ad6265SDimitry Andric                          info->Function->getName(), "epilogue");
230681ad6265SDimitry Andric     if (Epilog.Instructions.empty() ||
230781ad6265SDimitry Andric         !isARMTerminator(Epilog.Instructions.back()))
230881ad6265SDimitry Andric       streamer.getContext().reportError(
230981ad6265SDimitry Andric           SMLoc(), "Epilogue in " + info->Function->getName() +
231081ad6265SDimitry Andric                        " not correctly terminated");
231181ad6265SDimitry Andric   }
231281ad6265SDimitry Andric 
2313*bdd1243dSDimitry Andric   std::optional<int64_t> RawFuncLength;
231481ad6265SDimitry Andric   const MCExpr *FuncLengthExpr = nullptr;
231581ad6265SDimitry Andric   if (!info->FuncletOrFuncEnd) {
231681ad6265SDimitry Andric     report_fatal_error("FuncletOrFuncEnd not set");
231781ad6265SDimitry Andric   } else {
231881ad6265SDimitry Andric     // As the size of many thumb2 instructions isn't known until later,
231981ad6265SDimitry Andric     // we can't always rely on being able to calculate the absolute
232081ad6265SDimitry Andric     // length of the function here. If we can't calculate it, defer it
232181ad6265SDimitry Andric     // to a relocation.
232281ad6265SDimitry Andric     //
232381ad6265SDimitry Andric     // In such a case, we won't know if the function is too long so that
232481ad6265SDimitry Andric     // the unwind info would need to be split (but this isn't implemented
232581ad6265SDimitry Andric     // anyway).
232681ad6265SDimitry Andric     RawFuncLength =
232781ad6265SDimitry Andric         GetOptionalAbsDifference(streamer, info->FuncletOrFuncEnd, info->Begin);
232881ad6265SDimitry Andric     if (!RawFuncLength)
232981ad6265SDimitry Andric       FuncLengthExpr =
233081ad6265SDimitry Andric           GetSubDivExpr(streamer, info->FuncletOrFuncEnd, info->Begin, 2);
233181ad6265SDimitry Andric   }
233281ad6265SDimitry Andric   uint32_t FuncLength = 0;
233381ad6265SDimitry Andric   if (RawFuncLength)
233481ad6265SDimitry Andric     FuncLength = (uint32_t)*RawFuncLength / 2;
233581ad6265SDimitry Andric   if (FuncLength > 0x3FFFF)
233681ad6265SDimitry Andric     report_fatal_error("SEH unwind data splitting not yet implemented");
233781ad6265SDimitry Andric   uint32_t PrologCodeBytes = ARMCountOfUnwindCodes(info->Instructions);
233881ad6265SDimitry Andric   uint32_t TotalCodeBytes = PrologCodeBytes;
233981ad6265SDimitry Andric 
234081ad6265SDimitry Andric   if (!info->HandlesExceptions && RawFuncLength && FuncLength <= 0x7ff &&
234181ad6265SDimitry Andric       TryPacked) {
234281ad6265SDimitry Andric     // No exception handlers; check if the prolog and epilog matches the
234381ad6265SDimitry Andric     // patterns that can be described by the packed format. If we don't
234481ad6265SDimitry Andric     // know the exact function length yet, we can't do this.
234581ad6265SDimitry Andric 
234681ad6265SDimitry Andric     // info->Symbol was already set even if we didn't actually write any
234781ad6265SDimitry Andric     // unwind info there. Keep using that as indicator that this unwind
234881ad6265SDimitry Andric     // info has been generated already.
234981ad6265SDimitry Andric 
235081ad6265SDimitry Andric     if (tryARMPackedUnwind(streamer, info, FuncLength))
235181ad6265SDimitry Andric       return;
235281ad6265SDimitry Andric   }
235381ad6265SDimitry Andric 
235481ad6265SDimitry Andric   int PackedEpilogOffset =
235581ad6265SDimitry Andric       checkARMPackedEpilog(streamer, info, PrologCodeBytes);
235681ad6265SDimitry Andric 
235781ad6265SDimitry Andric   // Process epilogs.
235881ad6265SDimitry Andric   MapVector<MCSymbol *, uint32_t> EpilogInfo;
235981ad6265SDimitry Andric   // Epilogs processed so far.
236081ad6265SDimitry Andric   std::vector<MCSymbol *> AddedEpilogs;
236181ad6265SDimitry Andric 
236281ad6265SDimitry Andric   bool CanTweakProlog = true;
236381ad6265SDimitry Andric   for (auto &I : info->EpilogMap) {
236481ad6265SDimitry Andric     MCSymbol *EpilogStart = I.first;
236581ad6265SDimitry Andric     auto &EpilogInstrs = I.second.Instructions;
236681ad6265SDimitry Andric     uint32_t CodeBytes = ARMCountOfUnwindCodes(EpilogInstrs);
236781ad6265SDimitry Andric 
236881ad6265SDimitry Andric     MCSymbol *MatchingEpilog =
236981ad6265SDimitry Andric         FindMatchingEpilog(EpilogInstrs, AddedEpilogs, info);
237081ad6265SDimitry Andric     int PrologOffset;
237181ad6265SDimitry Andric     if (MatchingEpilog) {
237281ad6265SDimitry Andric       assert(EpilogInfo.find(MatchingEpilog) != EpilogInfo.end() &&
237381ad6265SDimitry Andric              "Duplicate epilog not found");
237481ad6265SDimitry Andric       EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog);
237581ad6265SDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
237681ad6265SDimitry Andric       // in the logic below.
237781ad6265SDimitry Andric       EpilogInstrs.clear();
237881ad6265SDimitry Andric     } else if ((PrologOffset = getARMOffsetInProlog(
237981ad6265SDimitry Andric                     info->Instructions, EpilogInstrs, CanTweakProlog)) >= 0) {
238081ad6265SDimitry Andric       if (CanTweakProlog) {
238181ad6265SDimitry Andric         // Replace the regular end opcode of the prolog with the one from the
238281ad6265SDimitry Andric         // epilog.
238381ad6265SDimitry Andric         info->Instructions.front() = EpilogInstrs.back();
238481ad6265SDimitry Andric         // Later epilogs need a strict match for the end opcode.
238581ad6265SDimitry Andric         CanTweakProlog = false;
238681ad6265SDimitry Andric       }
238781ad6265SDimitry Andric       EpilogInfo[EpilogStart] = PrologOffset;
238881ad6265SDimitry Andric       // Clear the unwind codes in the EpilogMap, so that they don't get output
238981ad6265SDimitry Andric       // in the logic below.
239081ad6265SDimitry Andric       EpilogInstrs.clear();
239181ad6265SDimitry Andric     } else {
239281ad6265SDimitry Andric       EpilogInfo[EpilogStart] = TotalCodeBytes;
239381ad6265SDimitry Andric       TotalCodeBytes += CodeBytes;
239481ad6265SDimitry Andric       AddedEpilogs.push_back(EpilogStart);
239581ad6265SDimitry Andric     }
239681ad6265SDimitry Andric   }
239781ad6265SDimitry Andric 
239881ad6265SDimitry Andric   // Code Words, Epilog count, F, E, X, Vers, Function Length
239981ad6265SDimitry Andric   uint32_t row1 = 0x0;
240081ad6265SDimitry Andric   uint32_t CodeWords = TotalCodeBytes / 4;
240181ad6265SDimitry Andric   uint32_t CodeWordsMod = TotalCodeBytes % 4;
240281ad6265SDimitry Andric   if (CodeWordsMod)
240381ad6265SDimitry Andric     CodeWords++;
240481ad6265SDimitry Andric   uint32_t EpilogCount =
240581ad6265SDimitry Andric       PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size();
240681ad6265SDimitry Andric   bool ExtensionWord = EpilogCount > 31 || CodeWords > 15;
240781ad6265SDimitry Andric   if (!ExtensionWord) {
240881ad6265SDimitry Andric     row1 |= (EpilogCount & 0x1F) << 23;
240981ad6265SDimitry Andric     row1 |= (CodeWords & 0x0F) << 28;
241081ad6265SDimitry Andric   }
241181ad6265SDimitry Andric   if (info->HandlesExceptions) // X
241281ad6265SDimitry Andric     row1 |= 1 << 20;
241381ad6265SDimitry Andric   if (PackedEpilogOffset >= 0) // E
241481ad6265SDimitry Andric     row1 |= 1 << 21;
241581ad6265SDimitry Andric   if (info->Fragment) // F
241681ad6265SDimitry Andric     row1 |= 1 << 22;
241781ad6265SDimitry Andric   row1 |= FuncLength & 0x3FFFF;
241881ad6265SDimitry Andric   if (RawFuncLength)
241981ad6265SDimitry Andric     streamer.emitInt32(row1);
242081ad6265SDimitry Andric   else
242181ad6265SDimitry Andric     streamer.emitValue(
242281ad6265SDimitry Andric         MCBinaryExpr::createOr(FuncLengthExpr,
242381ad6265SDimitry Andric                                MCConstantExpr::create(row1, context), context),
242481ad6265SDimitry Andric         4);
242581ad6265SDimitry Andric 
242681ad6265SDimitry Andric   // Extended Code Words, Extended Epilog Count
242781ad6265SDimitry Andric   if (ExtensionWord) {
242881ad6265SDimitry Andric     // FIXME: We should be able to split unwind info into multiple sections.
242981ad6265SDimitry Andric     if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
243081ad6265SDimitry Andric       report_fatal_error("SEH unwind data splitting not yet implemented");
243181ad6265SDimitry Andric     uint32_t row2 = 0x0;
243281ad6265SDimitry Andric     row2 |= (CodeWords & 0xFF) << 16;
243381ad6265SDimitry Andric     row2 |= (EpilogCount & 0xFFFF);
243481ad6265SDimitry Andric     streamer.emitInt32(row2);
243581ad6265SDimitry Andric   }
243681ad6265SDimitry Andric 
243781ad6265SDimitry Andric   if (PackedEpilogOffset < 0) {
243881ad6265SDimitry Andric     // Epilog Start Index, Epilog Start Offset
243981ad6265SDimitry Andric     for (auto &I : EpilogInfo) {
244081ad6265SDimitry Andric       MCSymbol *EpilogStart = I.first;
244181ad6265SDimitry Andric       uint32_t EpilogIndex = I.second;
244281ad6265SDimitry Andric 
2443*bdd1243dSDimitry Andric       std::optional<int64_t> MaybeEpilogOffset =
244481ad6265SDimitry Andric           GetOptionalAbsDifference(streamer, EpilogStart, info->Begin);
244581ad6265SDimitry Andric       const MCExpr *OffsetExpr = nullptr;
244681ad6265SDimitry Andric       uint32_t EpilogOffset = 0;
244781ad6265SDimitry Andric       if (MaybeEpilogOffset)
244881ad6265SDimitry Andric         EpilogOffset = *MaybeEpilogOffset / 2;
244981ad6265SDimitry Andric       else
245081ad6265SDimitry Andric         OffsetExpr = GetSubDivExpr(streamer, EpilogStart, info->Begin, 2);
245181ad6265SDimitry Andric 
245281ad6265SDimitry Andric       assert(info->EpilogMap.find(EpilogStart) != info->EpilogMap.end());
245381ad6265SDimitry Andric       unsigned Condition = info->EpilogMap[EpilogStart].Condition;
245481ad6265SDimitry Andric       assert(Condition <= 0xf);
245581ad6265SDimitry Andric 
245681ad6265SDimitry Andric       uint32_t row3 = EpilogOffset;
245781ad6265SDimitry Andric       row3 |= Condition << 20;
245881ad6265SDimitry Andric       row3 |= (EpilogIndex & 0x3FF) << 24;
245981ad6265SDimitry Andric       if (MaybeEpilogOffset)
246081ad6265SDimitry Andric         streamer.emitInt32(row3);
246181ad6265SDimitry Andric       else
246281ad6265SDimitry Andric         streamer.emitValue(
246381ad6265SDimitry Andric             MCBinaryExpr::createOr(
246481ad6265SDimitry Andric                 OffsetExpr, MCConstantExpr::create(row3, context), context),
246581ad6265SDimitry Andric             4);
246681ad6265SDimitry Andric     }
246781ad6265SDimitry Andric   }
246881ad6265SDimitry Andric 
246981ad6265SDimitry Andric   // Emit prolog unwind instructions (in reverse order).
247081ad6265SDimitry Andric   uint8_t numInst = info->Instructions.size();
247181ad6265SDimitry Andric   for (uint8_t c = 0; c < numInst; ++c) {
247281ad6265SDimitry Andric     WinEH::Instruction inst = info->Instructions.back();
247381ad6265SDimitry Andric     info->Instructions.pop_back();
247481ad6265SDimitry Andric     ARMEmitUnwindCode(streamer, inst);
247581ad6265SDimitry Andric   }
247681ad6265SDimitry Andric 
247781ad6265SDimitry Andric   // Emit epilog unwind instructions
247881ad6265SDimitry Andric   for (auto &I : info->EpilogMap) {
247981ad6265SDimitry Andric     auto &EpilogInstrs = I.second.Instructions;
2480*bdd1243dSDimitry Andric     for (const WinEH::Instruction &inst : EpilogInstrs)
248181ad6265SDimitry Andric       ARMEmitUnwindCode(streamer, inst);
248281ad6265SDimitry Andric   }
248381ad6265SDimitry Andric 
248481ad6265SDimitry Andric   int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
248581ad6265SDimitry Andric   assert(BytesMod >= 0);
248681ad6265SDimitry Andric   for (int i = 0; i < BytesMod; i++)
248781ad6265SDimitry Andric     streamer.emitInt8(0xFB);
248881ad6265SDimitry Andric 
248981ad6265SDimitry Andric   if (info->HandlesExceptions)
249081ad6265SDimitry Andric     streamer.emitValue(
249181ad6265SDimitry Andric         MCSymbolRefExpr::create(info->ExceptionHandler,
249281ad6265SDimitry Andric                                 MCSymbolRefExpr::VK_COFF_IMGREL32, context),
249381ad6265SDimitry Andric         4);
249481ad6265SDimitry Andric }
249581ad6265SDimitry Andric 
2496*bdd1243dSDimitry Andric static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
2497*bdd1243dSDimitry Andric                                      const WinEH::FrameInfo *info) {
2498*bdd1243dSDimitry Andric   MCContext &context = streamer.getContext();
2499*bdd1243dSDimitry Andric 
2500*bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
2501*bdd1243dSDimitry Andric   for (const auto &S : info->Segments) {
2502*bdd1243dSDimitry Andric     EmitSymbolRefWithOfs(streamer, info->Begin, S.Offset);
2503*bdd1243dSDimitry Andric     if (info->PackedInfo)
2504*bdd1243dSDimitry Andric       streamer.emitInt32(info->PackedInfo);
2505*bdd1243dSDimitry Andric     else
2506*bdd1243dSDimitry Andric       streamer.emitValue(
2507*bdd1243dSDimitry Andric           MCSymbolRefExpr::create(S.Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32,
2508*bdd1243dSDimitry Andric                                   context),
2509*bdd1243dSDimitry Andric           4);
2510*bdd1243dSDimitry Andric   }
2511*bdd1243dSDimitry Andric }
2512*bdd1243dSDimitry Andric 
2513*bdd1243dSDimitry Andric 
251481ad6265SDimitry Andric static void ARMEmitRuntimeFunction(MCStreamer &streamer,
25150b57cec5SDimitry Andric                                    const WinEH::FrameInfo *info) {
25160b57cec5SDimitry Andric   MCContext &context = streamer.getContext();
25170b57cec5SDimitry Andric 
2518*bdd1243dSDimitry Andric   streamer.emitValueToAlignment(Align(4));
2519349cc55cSDimitry Andric   EmitSymbolRefWithOfs(streamer, info->Begin, info->Begin);
2520e8d8bef9SDimitry Andric   if (info->PackedInfo)
2521e8d8bef9SDimitry Andric     streamer.emitInt32(info->PackedInfo);
2522e8d8bef9SDimitry Andric   else
2523e8d8bef9SDimitry Andric     streamer.emitValue(
2524e8d8bef9SDimitry Andric         MCSymbolRefExpr::create(info->Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32,
25250b57cec5SDimitry Andric                                 context),
25260b57cec5SDimitry Andric         4);
25270b57cec5SDimitry Andric }
25280b57cec5SDimitry Andric 
25290b57cec5SDimitry Andric void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
25300b57cec5SDimitry Andric   // Emit the unwind info structs first.
25310b57cec5SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
2532e8d8bef9SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
2533e8d8bef9SDimitry Andric     if (Info->empty())
2534e8d8bef9SDimitry Andric       continue;
25350b57cec5SDimitry Andric     MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
253681ad6265SDimitry Andric     Streamer.switchSection(XData);
2537e8d8bef9SDimitry Andric     ARM64EmitUnwindInfo(Streamer, Info);
25380b57cec5SDimitry Andric   }
25390b57cec5SDimitry Andric 
25400b57cec5SDimitry Andric   // Now emit RUNTIME_FUNCTION entries.
25410b57cec5SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
2542e8d8bef9SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
2543e8d8bef9SDimitry Andric     // ARM64EmitUnwindInfo above clears the info struct, so we can't check
2544e8d8bef9SDimitry Andric     // empty here. But if a Symbol is set, we should create the corresponding
2545e8d8bef9SDimitry Andric     // pdata entry.
2546e8d8bef9SDimitry Andric     if (!Info->Symbol)
2547e8d8bef9SDimitry Andric       continue;
25480b57cec5SDimitry Andric     MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
254981ad6265SDimitry Andric     Streamer.switchSection(PData);
2550*bdd1243dSDimitry Andric     ARM64EmitRuntimeFunction(Streamer, Info);
25510b57cec5SDimitry Andric   }
25520b57cec5SDimitry Andric }
25530b57cec5SDimitry Andric 
2554e8d8bef9SDimitry Andric void llvm::Win64EH::ARM64UnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
2555e8d8bef9SDimitry Andric                                                        WinEH::FrameInfo *info,
2556e8d8bef9SDimitry Andric                                                        bool HandlerData) const {
2557e8d8bef9SDimitry Andric   // Called if there's an .seh_handlerdata directive before the end of the
2558e8d8bef9SDimitry Andric   // function. This forces writing the xdata record already here - and
2559e8d8bef9SDimitry Andric   // in this case, the function isn't actually ended already, but the xdata
2560e8d8bef9SDimitry Andric   // record needs to know the function length. In these cases, if the funclet
2561e8d8bef9SDimitry Andric   // end hasn't been marked yet, the xdata function length won't cover the
2562e8d8bef9SDimitry Andric   // whole function, only up to this point.
2563e8d8bef9SDimitry Andric   if (!info->FuncletOrFuncEnd) {
256481ad6265SDimitry Andric     Streamer.switchSection(info->TextSection);
2565e8d8bef9SDimitry Andric     info->FuncletOrFuncEnd = Streamer.emitCFILabel();
2566e8d8bef9SDimitry Andric   }
25670b57cec5SDimitry Andric   // Switch sections (the static function above is meant to be called from
25680b57cec5SDimitry Andric   // here and from Emit().
25690b57cec5SDimitry Andric   MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection);
257081ad6265SDimitry Andric   Streamer.switchSection(XData);
2571e8d8bef9SDimitry Andric   ARM64EmitUnwindInfo(Streamer, info, /* TryPacked = */ !HandlerData);
25720b57cec5SDimitry Andric }
257381ad6265SDimitry Andric 
257481ad6265SDimitry Andric void llvm::Win64EH::ARMUnwindEmitter::Emit(MCStreamer &Streamer) const {
257581ad6265SDimitry Andric   // Emit the unwind info structs first.
257681ad6265SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
257781ad6265SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
257881ad6265SDimitry Andric     if (Info->empty())
257981ad6265SDimitry Andric       continue;
258081ad6265SDimitry Andric     MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection);
258181ad6265SDimitry Andric     Streamer.switchSection(XData);
258281ad6265SDimitry Andric     ARMEmitUnwindInfo(Streamer, Info);
258381ad6265SDimitry Andric   }
258481ad6265SDimitry Andric 
258581ad6265SDimitry Andric   // Now emit RUNTIME_FUNCTION entries.
258681ad6265SDimitry Andric   for (const auto &CFI : Streamer.getWinFrameInfos()) {
258781ad6265SDimitry Andric     WinEH::FrameInfo *Info = CFI.get();
258881ad6265SDimitry Andric     // ARMEmitUnwindInfo above clears the info struct, so we can't check
258981ad6265SDimitry Andric     // empty here. But if a Symbol is set, we should create the corresponding
259081ad6265SDimitry Andric     // pdata entry.
259181ad6265SDimitry Andric     if (!Info->Symbol)
259281ad6265SDimitry Andric       continue;
259381ad6265SDimitry Andric     MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection);
259481ad6265SDimitry Andric     Streamer.switchSection(PData);
259581ad6265SDimitry Andric     ARMEmitRuntimeFunction(Streamer, Info);
259681ad6265SDimitry Andric   }
259781ad6265SDimitry Andric }
259881ad6265SDimitry Andric 
259981ad6265SDimitry Andric void llvm::Win64EH::ARMUnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
260081ad6265SDimitry Andric                                                      WinEH::FrameInfo *info,
260181ad6265SDimitry Andric                                                      bool HandlerData) const {
260281ad6265SDimitry Andric   // Called if there's an .seh_handlerdata directive before the end of the
260381ad6265SDimitry Andric   // function. This forces writing the xdata record already here - and
260481ad6265SDimitry Andric   // in this case, the function isn't actually ended already, but the xdata
260581ad6265SDimitry Andric   // record needs to know the function length. In these cases, if the funclet
260681ad6265SDimitry Andric   // end hasn't been marked yet, the xdata function length won't cover the
260781ad6265SDimitry Andric   // whole function, only up to this point.
260881ad6265SDimitry Andric   if (!info->FuncletOrFuncEnd) {
260981ad6265SDimitry Andric     Streamer.switchSection(info->TextSection);
261081ad6265SDimitry Andric     info->FuncletOrFuncEnd = Streamer.emitCFILabel();
261181ad6265SDimitry Andric   }
261281ad6265SDimitry Andric   // Switch sections (the static function above is meant to be called from
261381ad6265SDimitry Andric   // here and from Emit().
261481ad6265SDimitry Andric   MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection);
261581ad6265SDimitry Andric   Streamer.switchSection(XData);
261681ad6265SDimitry Andric   ARMEmitUnwindInfo(Streamer, info, /* TryPacked = */ !HandlerData);
261781ad6265SDimitry Andric }
2618