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