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
CountOfUnwindCodes(std::vector<WinEH::Instruction> & Insns)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
EmitAbsDifference(MCStreamer & Streamer,const MCSymbol * LHS,const MCSymbol * RHS)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
EmitUnwindCode(MCStreamer & streamer,const MCSymbol * begin,WinEH::Instruction & inst)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
EmitSymbolRefWithOfs(MCStreamer & streamer,const MCSymbol * Base,int64_t Offset)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
EmitSymbolRefWithOfs(MCStreamer & streamer,const MCSymbol * Base,const MCSymbol * Other)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
EmitRuntimeFunction(MCStreamer & streamer,const WinEH::FrameInfo * info)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
EmitUnwindInfo(MCStreamer & streamer,WinEH::FrameInfo * info)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
Emit(MCStreamer & Streamer) const2370b57cec5SDimitry 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
EmitUnwindInfo(MCStreamer & Streamer,WinEH::FrameInfo * info,bool HandlerData) const253e8d8bef9SDimitry 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
GetSubDivExpr(MCStreamer & Streamer,const MCSymbol * LHS,const MCSymbol * RHS,int Div)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
GetOptionalAbsDifference(MCStreamer & Streamer,const MCSymbol * LHS,const MCSymbol * RHS)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
GetAbsDifference(MCStreamer & Streamer,const MCSymbol * LHS,const MCSymbol * RHS)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
checkARM64Instructions(MCStreamer & Streamer,ArrayRef<WinEH::Instruction> Insns,const MCSymbol * Begin,const MCSymbol * End,StringRef Name,StringRef Type)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
ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns)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
ARM64EmitUnwindCode(MCStreamer & streamer,const WinEH::Instruction & inst)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*
FindMatchingEpilog(const std::vector<WinEH::Instruction> & EpilogInstrs,const std::vector<MCSymbol * > & Epilogs,const WinEH::FrameInfo * info)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
simplifyARM64Opcodes(std::vector<WinEH::Instruction> & Instructions,bool Reverse)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
getARM64OffsetInProlog(const std::vector<WinEH::Instruction> & Prolog,const std::vector<WinEH::Instruction> & Epilog)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
checkARM64PackedEpilog(MCStreamer & streamer,WinEH::FrameInfo * info,WinEH::FrameInfo::Segment * Seg,int PrologCodeBytes)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
tryARM64PackedUnwind(WinEH::FrameInfo * info,uint32_t FuncLength,int PackedEpilogOffset)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
ARM64ProcessEpilogs(WinEH::FrameInfo * info,WinEH::FrameInfo::Segment * Seg,uint32_t & TotalCodeBytes,MapVector<MCSymbol *,uint32_t> & EpilogInfo)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
ARM64FindSegmentsInFunction(MCStreamer & streamer,WinEH::FrameInfo * info,int64_t RawFuncLength)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
ARM64EmitUnwindInfoForSegment(MCStreamer & streamer,WinEH::FrameInfo * info,WinEH::FrameInfo::Segment & Seg,bool TryPacked=true)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
ARM64EmitUnwindInfo(MCStreamer & streamer,WinEH::FrameInfo * info,bool TryPacked=true)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
ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns)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
ARMCountOfInstructionBytes(ArrayRef<WinEH::Instruction> Insns,bool * HasCustom=nullptr)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
checkARMInstructions(MCStreamer & Streamer,ArrayRef<WinEH::Instruction> Insns,const MCSymbol * Begin,const MCSymbol * End,StringRef Name,StringRef Type)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
isARMTerminator(const WinEH::Instruction & inst)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
ARMEmitUnwindCode(MCStreamer & streamer,const WinEH::Instruction & inst)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.
getARMOffsetInProlog(const std::vector<WinEH::Instruction> & Prolog,const std::vector<WinEH::Instruction> & Epilog,bool CanTweakProlog)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
checkARMPackedEpilog(MCStreamer & streamer,WinEH::FrameInfo * info,int PrologCodeBytes)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
parseRegMask(unsigned Mask,bool & HasLR,bool & HasR11,unsigned & Folded,int & IntRegs)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
tryARMPackedUnwind(MCStreamer & streamer,WinEH::FrameInfo * info,uint32_t FuncLength)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
ARMEmitUnwindInfo(MCStreamer & streamer,WinEH::FrameInfo * info,bool TryPacked=true)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
ARM64EmitRuntimeFunction(MCStreamer & streamer,const WinEH::FrameInfo * info)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
ARMEmitRuntimeFunction(MCStreamer & streamer,const WinEH::FrameInfo * info)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
Emit(MCStreamer & Streamer) const25410b57cec5SDimitry 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
EmitUnwindInfo(MCStreamer & Streamer,WinEH::FrameInfo * info,bool HandlerData) const2566e8d8bef9SDimitry 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
Emit(MCStreamer & Streamer) const258681ad6265SDimitry 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
EmitUnwindInfo(MCStreamer & Streamer,WinEH::FrameInfo * info,bool HandlerData) const261181ad6265SDimitry 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