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