1//===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/MC/MCWin64EH.h"
10#include "llvm/ADT/Twine.h"
11#include "llvm/MC/MCAssembler.h"
12#include "llvm/MC/MCContext.h"
13#include "llvm/MC/MCExpr.h"
14#include "llvm/MC/MCObjectStreamer.h"
15#include "llvm/MC/MCStreamer.h"
16#include "llvm/MC/MCSymbol.h"
17#include "llvm/MC/MCValue.h"
18#include "llvm/Support/Win64EH.h"
19
20namespace llvm {
21class MCSection;
22
23/// MCExpr that represents the epilog unwind code in an unwind table.
24class MCUnwindV2EpilogTargetExpr final : public MCTargetExpr {
25 const MCSymbol *FunctionEnd;
26 const MCSymbol *UnwindV2Start;
27 const MCSymbol *EpilogEnd;
28 uint8_t EpilogSize;
29 SMLoc Loc;
30
31 MCUnwindV2EpilogTargetExpr(const WinEH::FrameInfo &FrameInfo,
32 const WinEH::FrameInfo::Epilog &Epilog,
33 uint8_t EpilogSize_)
34 : FunctionEnd(FrameInfo.FuncletOrFuncEnd),
35 UnwindV2Start(Epilog.UnwindV2Start), EpilogEnd(Epilog.End),
36 EpilogSize(EpilogSize_), Loc(Epilog.Loc) {}
37
38public:
39 static MCUnwindV2EpilogTargetExpr *
40 create(const WinEH::FrameInfo &FrameInfo,
41 const WinEH::FrameInfo::Epilog &Epilog, uint8_t EpilogSize_,
42 MCContext &Ctx) {
43 return new (Ctx) MCUnwindV2EpilogTargetExpr(FrameInfo, Epilog, EpilogSize_);
44 }
45
46 void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override {
47 OS << ":epilog:";
48 UnwindV2Start->print(OS, MAI);
49 }
50
51 bool evaluateAsRelocatableImpl(MCValue &Res,
52 const MCAssembler *Asm) const override;
53
54 void visitUsedExpr(MCStreamer &Streamer) const override {
55 // Contains no sub-expressions.
56 }
57
58 MCFragment *findAssociatedFragment() const override {
59 return UnwindV2Start->getFragment();
60 }
61};
62}
63
64using namespace llvm;
65
66// NOTE: All relocations generated here are 4-byte image-relative.
67
68static uint8_t CountOfUnwindCodes(std::vector<WinEH::Instruction> &Insns) {
69 uint8_t Count = 0;
70 for (const auto &I : Insns) {
71 switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
72 default:
73 llvm_unreachable("Unsupported unwind code");
74 case Win64EH::UOP_PushNonVol:
75 case Win64EH::UOP_AllocSmall:
76 case Win64EH::UOP_SetFPReg:
77 case Win64EH::UOP_PushMachFrame:
78 Count += 1;
79 break;
80 case Win64EH::UOP_SaveNonVol:
81 case Win64EH::UOP_SaveXMM128:
82 Count += 2;
83 break;
84 case Win64EH::UOP_SaveNonVolBig:
85 case Win64EH::UOP_SaveXMM128Big:
86 Count += 3;
87 break;
88 case Win64EH::UOP_AllocLarge:
89 Count += (I.Offset > 512 * 1024 - 8) ? 3 : 2;
90 break;
91 }
92 }
93 return Count;
94}
95
96static void EmitAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
97 const MCSymbol *RHS) {
98 MCContext &Context = Streamer.getContext();
99 const MCExpr *Diff =
100 MCBinaryExpr::createSub(LHS: MCSymbolRefExpr::create(Symbol: LHS, Ctx&: Context),
101 RHS: MCSymbolRefExpr::create(Symbol: RHS, Ctx&: Context), Ctx&: Context);
102 Streamer.emitValue(Value: Diff, Size: 1);
103}
104
105static void EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin,
106 WinEH::Instruction &inst) {
107 uint8_t b2;
108 uint16_t w;
109 b2 = (inst.Operation & 0x0F);
110 switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
111 default:
112 llvm_unreachable("Unsupported unwind code");
113 case Win64EH::UOP_PushNonVol:
114 EmitAbsDifference(Streamer&: streamer, LHS: inst.Label, RHS: begin);
115 b2 |= (inst.Register & 0x0F) << 4;
116 streamer.emitInt8(Value: b2);
117 break;
118 case Win64EH::UOP_AllocLarge:
119 EmitAbsDifference(Streamer&: streamer, LHS: inst.Label, RHS: begin);
120 if (inst.Offset > 512 * 1024 - 8) {
121 b2 |= 0x10;
122 streamer.emitInt8(Value: b2);
123 w = inst.Offset & 0xFFF8;
124 streamer.emitInt16(Value: w);
125 w = inst.Offset >> 16;
126 } else {
127 streamer.emitInt8(Value: b2);
128 w = inst.Offset >> 3;
129 }
130 streamer.emitInt16(Value: w);
131 break;
132 case Win64EH::UOP_AllocSmall:
133 b2 |= (((inst.Offset - 8) >> 3) & 0x0F) << 4;
134 EmitAbsDifference(Streamer&: streamer, LHS: inst.Label, RHS: begin);
135 streamer.emitInt8(Value: b2);
136 break;
137 case Win64EH::UOP_SetFPReg:
138 EmitAbsDifference(Streamer&: streamer, LHS: inst.Label, RHS: begin);
139 streamer.emitInt8(Value: b2);
140 break;
141 case Win64EH::UOP_SaveNonVol:
142 case Win64EH::UOP_SaveXMM128:
143 b2 |= (inst.Register & 0x0F) << 4;
144 EmitAbsDifference(Streamer&: streamer, LHS: inst.Label, RHS: begin);
145 streamer.emitInt8(Value: b2);
146 w = inst.Offset >> 3;
147 if (inst.Operation == Win64EH::UOP_SaveXMM128)
148 w >>= 1;
149 streamer.emitInt16(Value: w);
150 break;
151 case Win64EH::UOP_SaveNonVolBig:
152 case Win64EH::UOP_SaveXMM128Big:
153 b2 |= (inst.Register & 0x0F) << 4;
154 EmitAbsDifference(Streamer&: streamer, LHS: inst.Label, RHS: begin);
155 streamer.emitInt8(Value: b2);
156 if (inst.Operation == Win64EH::UOP_SaveXMM128Big)
157 w = inst.Offset & 0xFFF0;
158 else
159 w = inst.Offset & 0xFFF8;
160 streamer.emitInt16(Value: w);
161 w = inst.Offset >> 16;
162 streamer.emitInt16(Value: w);
163 break;
164 case Win64EH::UOP_PushMachFrame:
165 if (inst.Offset == 1)
166 b2 |= 0x10;
167 EmitAbsDifference(Streamer&: streamer, LHS: inst.Label, RHS: begin);
168 streamer.emitInt8(Value: b2);
169 break;
170 }
171}
172
173static void EmitSymbolRefWithOfs(MCStreamer &streamer,
174 const MCSymbol *Base,
175 int64_t Offset) {
176 MCContext &Context = streamer.getContext();
177 const MCConstantExpr *OffExpr = MCConstantExpr::create(Value: Offset, Ctx&: Context);
178 const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Symbol: Base,
179 specifier: MCSymbolRefExpr::VK_COFF_IMGREL32,
180 Ctx&: Context);
181 streamer.emitValue(Value: MCBinaryExpr::createAdd(LHS: BaseRefRel, RHS: OffExpr, Ctx&: Context), Size: 4);
182}
183
184static void EmitSymbolRefWithOfs(MCStreamer &streamer,
185 const MCSymbol *Base,
186 const MCSymbol *Other) {
187 MCContext &Context = streamer.getContext();
188 const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::create(Symbol: Base, Ctx&: Context);
189 const MCSymbolRefExpr *OtherRef = MCSymbolRefExpr::create(Symbol: Other, Ctx&: Context);
190 const MCExpr *Ofs = MCBinaryExpr::createSub(LHS: OtherRef, RHS: BaseRef, Ctx&: Context);
191 const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Symbol: Base,
192 specifier: MCSymbolRefExpr::VK_COFF_IMGREL32,
193 Ctx&: Context);
194 streamer.emitValue(Value: MCBinaryExpr::createAdd(LHS: BaseRefRel, RHS: Ofs, Ctx&: Context), Size: 4);
195}
196
197static void EmitRuntimeFunction(MCStreamer &streamer,
198 const WinEH::FrameInfo *info) {
199 MCContext &context = streamer.getContext();
200
201 streamer.emitValueToAlignment(Alignment: Align(4));
202 EmitSymbolRefWithOfs(streamer, Base: info->Begin, Other: info->Begin);
203 EmitSymbolRefWithOfs(streamer, Base: info->Begin, Other: info->End);
204 streamer.emitValue(Value: MCSymbolRefExpr::create(Symbol: info->Symbol,
205 specifier: MCSymbolRefExpr::VK_COFF_IMGREL32,
206 Ctx&: context), Size: 4);
207}
208
209static std::optional<int64_t>
210GetOptionalAbsDifference(const MCAssembler &Assembler, const MCSymbol *LHS,
211 const MCSymbol *RHS) {
212 MCContext &Context = Assembler.getContext();
213 const MCExpr *Diff =
214 MCBinaryExpr::createSub(LHS: MCSymbolRefExpr::create(Symbol: LHS, Ctx&: Context),
215 RHS: MCSymbolRefExpr::create(Symbol: RHS, Ctx&: Context), Ctx&: Context);
216 // It should normally be possible to calculate the length of a function
217 // at this point, but it might not be possible in the presence of certain
218 // unusual constructs, like an inline asm with an alignment directive.
219 int64_t value;
220 if (!Diff->evaluateAsAbsolute(Res&: value, Asm: Assembler))
221 return std::nullopt;
222 return value;
223}
224
225static void EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
226 // If this UNWIND_INFO already has a symbol, it's already been emitted.
227 if (info->Symbol)
228 return;
229
230 MCContext &context = streamer.getContext();
231 MCObjectStreamer *OS = (MCObjectStreamer *)(&streamer);
232 MCSymbol *Label = context.createTempSymbol();
233
234 streamer.emitValueToAlignment(Alignment: Align(4));
235 streamer.emitLabel(Symbol: Label);
236 info->Symbol = Label;
237
238 uint8_t numCodes = CountOfUnwindCodes(Insns&: info->Instructions);
239 bool LastEpilogIsAtEnd = false;
240 bool AddPaddingEpilogCode = false;
241 uint8_t EpilogSize = 0;
242 bool EnableUnwindV2 = (info->Version >= 2) && !info->EpilogMap.empty();
243 if (EnableUnwindV2) {
244 auto &LastEpilog = info->EpilogMap.back().second;
245
246 // Calculate the size of the epilogs. Note that we +1 to the size so that
247 // the terminator instruction is also included in the epilog (the Windows
248 // unwinder does a simple range check versus the current instruction pointer
249 // so, although there are terminators that are large than 1 byte, the
250 // starting address of the terminator instruction will always be considered
251 // inside the epilog).
252 auto MaybeSize = GetOptionalAbsDifference(
253 Assembler: OS->getAssembler(), LHS: LastEpilog.End, RHS: LastEpilog.UnwindV2Start);
254 if (!MaybeSize) {
255 context.reportError(L: LastEpilog.Loc,
256 Msg: "Failed to evaluate epilog size for Unwind v2");
257 return;
258 }
259 assert(*MaybeSize >= 0);
260 if (*MaybeSize >= (int64_t)UINT8_MAX) {
261 context.reportError(L: LastEpilog.Loc,
262 Msg: "Epilog size is too large for Unwind v2");
263 return;
264 }
265 EpilogSize = *MaybeSize + 1;
266
267 // If the last epilog is at the end of the function, we can use a special
268 // encoding for it. Because of our +1 trick for the size, this will only
269 // work where that final terminator instruction is 1 byte long.
270 auto LastEpilogToFuncEnd = GetOptionalAbsDifference(
271 Assembler: OS->getAssembler(), LHS: info->FuncletOrFuncEnd, RHS: LastEpilog.UnwindV2Start);
272 LastEpilogIsAtEnd = (LastEpilogToFuncEnd == EpilogSize);
273
274 // If we have an odd number of epilog codes, we need to add a padding code.
275 size_t numEpilogCodes =
276 info->EpilogMap.size() + (LastEpilogIsAtEnd ? 0 : 1);
277 if ((numEpilogCodes % 2) != 0) {
278 AddPaddingEpilogCode = true;
279 numEpilogCodes++;
280 }
281
282 // Too many epilogs to handle.
283 if ((size_t)numCodes + numEpilogCodes > UINT8_MAX) {
284 context.reportError(L: info->FunctionLoc,
285 Msg: "Too many unwind codes with Unwind v2 enabled");
286 return;
287 }
288
289 numCodes += numEpilogCodes;
290 }
291
292 // Upper 3 bits are the version number.
293 uint8_t flags = info->Version;
294 if (info->ChainedParent)
295 flags |= Win64EH::UNW_ChainInfo << 3;
296 else {
297 if (info->HandlesUnwind)
298 flags |= Win64EH::UNW_TerminateHandler << 3;
299 if (info->HandlesExceptions)
300 flags |= Win64EH::UNW_ExceptionHandler << 3;
301 }
302 streamer.emitInt8(Value: flags);
303
304 if (info->PrologEnd)
305 EmitAbsDifference(Streamer&: streamer, LHS: info->PrologEnd, RHS: info->Begin);
306 else
307 streamer.emitInt8(Value: 0);
308
309 streamer.emitInt8(Value: numCodes);
310
311 uint8_t frame = 0;
312 if (info->LastFrameInst >= 0) {
313 WinEH::Instruction &frameInst = info->Instructions[info->LastFrameInst];
314 assert(frameInst.Operation == Win64EH::UOP_SetFPReg);
315 frame = (frameInst.Register & 0x0F) | (frameInst.Offset & 0xF0);
316 }
317 streamer.emitInt8(Value: frame);
318
319 // Emit the epilog instructions.
320 if (EnableUnwindV2) {
321 MCDataFragment *DF = OS->getOrCreateDataFragment();
322
323 bool IsLast = true;
324 for (const auto &Epilog : llvm::reverse(C&: info->EpilogMap)) {
325 if (IsLast) {
326 IsLast = false;
327 uint8_t Flags = LastEpilogIsAtEnd ? 0x01 : 0;
328 streamer.emitInt8(Value: EpilogSize);
329 streamer.emitInt8(Value: (Flags << 4) | Win64EH::UOP_Epilog);
330
331 if (LastEpilogIsAtEnd)
332 continue;
333 }
334
335 // Each epilog is emitted as a fixup, since we can't measure the distance
336 // between the start of the epilog and the end of the function until
337 // layout has been completed.
338 auto *MCE = MCUnwindV2EpilogTargetExpr::create(FrameInfo: *info, Epilog: Epilog.second,
339 EpilogSize_: EpilogSize, Ctx&: context);
340 MCFixup Fixup = MCFixup::create(Offset: DF->getContents().size(), Value: MCE, Kind: FK_Data_2);
341 DF->addFixup(Fixup);
342 DF->appendContents(Num: 2, Elt: 0);
343 }
344 }
345 if (AddPaddingEpilogCode)
346 streamer.emitInt16(Value: Win64EH::UOP_Epilog << 8);
347
348 // Emit unwind instructions (in reverse order).
349 uint8_t numInst = info->Instructions.size();
350 for (uint8_t c = 0; c < numInst; ++c) {
351 WinEH::Instruction inst = info->Instructions.back();
352 info->Instructions.pop_back();
353 EmitUnwindCode(streamer, begin: info->Begin, inst);
354 }
355
356 // For alignment purposes, the instruction array will always have an even
357 // number of entries, with the final entry potentially unused (in which case
358 // the array will be one longer than indicated by the count of unwind codes
359 // field).
360 if (numCodes & 1) {
361 streamer.emitInt16(Value: 0);
362 }
363
364 if (flags & (Win64EH::UNW_ChainInfo << 3))
365 EmitRuntimeFunction(streamer, info: info->ChainedParent);
366 else if (flags &
367 ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3))
368 streamer.emitValue(Value: MCSymbolRefExpr::create(Symbol: info->ExceptionHandler,
369 specifier: MCSymbolRefExpr::VK_COFF_IMGREL32,
370 Ctx&: context), Size: 4);
371 else if (numCodes == 0) {
372 // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not
373 // a chained unwind info, if there is no handler, and if there are fewer
374 // than 2 slots used in the unwind code array, we have to pad to 8 bytes.
375 streamer.emitInt32(Value: 0);
376 }
377}
378
379bool MCUnwindV2EpilogTargetExpr::evaluateAsRelocatableImpl(
380 MCValue &Res, const MCAssembler *Asm) const {
381 // Calculate the offset to this epilog, and validate it's within the allowed
382 // range.
383 auto Offset = GetOptionalAbsDifference(Assembler: *Asm, LHS: FunctionEnd, RHS: UnwindV2Start);
384 if (!Offset) {
385 Asm->getContext().reportError(
386 L: Loc, Msg: "Failed to evaluate epilog offset for Unwind v2");
387 return false;
388 }
389 assert(*Offset > 0);
390 constexpr uint16_t MaxEpilogOffset = 0x0fff;
391 if (*Offset > MaxEpilogOffset) {
392 Asm->getContext().reportError(L: Loc,
393 Msg: "Epilog offset is too large for Unwind v2");
394 return false;
395 }
396
397 // Sanity check that all epilogs are the same size.
398 auto Size = GetOptionalAbsDifference(Assembler: *Asm, LHS: EpilogEnd, RHS: UnwindV2Start);
399 if (Size != (EpilogSize - 1)) {
400 Asm->getContext().reportError(
401 L: Loc,
402 Msg: "Size of this epilog does not match size of last epilog in function");
403 return false;
404 }
405
406 auto HighBits = *Offset >> 8;
407 Res = MCValue::get(Val: (HighBits << 12) | (Win64EH::UOP_Epilog << 8) |
408 (*Offset & 0xFF));
409 return true;
410}
411
412void llvm::Win64EH::UnwindEmitter::Emit(MCStreamer &Streamer) const {
413 // Emit the unwind info structs first.
414 for (const auto &CFI : Streamer.getWinFrameInfos()) {
415 MCSection *XData = Streamer.getAssociatedXDataSection(TextSec: CFI->TextSection);
416 Streamer.switchSection(Section: XData);
417 ::EmitUnwindInfo(streamer&: Streamer, info: CFI.get());
418 }
419
420 // Now emit RUNTIME_FUNCTION entries.
421 for (const auto &CFI : Streamer.getWinFrameInfos()) {
422 MCSection *PData = Streamer.getAssociatedPDataSection(TextSec: CFI->TextSection);
423 Streamer.switchSection(Section: PData);
424 EmitRuntimeFunction(streamer&: Streamer, info: CFI.get());
425 }
426}
427
428void llvm::Win64EH::UnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
429 WinEH::FrameInfo *info,
430 bool HandlerData) const {
431 // Switch sections (the static function above is meant to be called from
432 // here and from Emit().
433 MCSection *XData = Streamer.getAssociatedXDataSection(TextSec: info->TextSection);
434 Streamer.switchSection(Section: XData);
435
436 ::EmitUnwindInfo(streamer&: Streamer, info);
437}
438
439static const MCExpr *GetSubDivExpr(MCStreamer &Streamer, const MCSymbol *LHS,
440 const MCSymbol *RHS, int Div) {
441 MCContext &Context = Streamer.getContext();
442 const MCExpr *Expr =
443 MCBinaryExpr::createSub(LHS: MCSymbolRefExpr::create(Symbol: LHS, Ctx&: Context),
444 RHS: MCSymbolRefExpr::create(Symbol: RHS, Ctx&: Context), Ctx&: Context);
445 if (Div != 1)
446 Expr = MCBinaryExpr::createDiv(LHS: Expr, RHS: MCConstantExpr::create(Value: Div, Ctx&: Context),
447 Ctx&: Context);
448 return Expr;
449}
450
451static std::optional<int64_t> GetOptionalAbsDifference(MCStreamer &Streamer,
452 const MCSymbol *LHS,
453 const MCSymbol *RHS) {
454 MCObjectStreamer *OS = (MCObjectStreamer *)(&Streamer);
455 return GetOptionalAbsDifference(Assembler: OS->getAssembler(), LHS, RHS);
456}
457
458static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
459 const MCSymbol *RHS) {
460 std::optional<int64_t> MaybeDiff =
461 GetOptionalAbsDifference(Streamer, LHS, RHS);
462 if (!MaybeDiff)
463 report_fatal_error(reason: "Failed to evaluate function length in SEH unwind info");
464 return *MaybeDiff;
465}
466
467static void checkARM64Instructions(MCStreamer &Streamer,
468 ArrayRef<WinEH::Instruction> Insns,
469 const MCSymbol *Begin, const MCSymbol *End,
470 StringRef Name, StringRef Type) {
471 if (!End)
472 return;
473 std::optional<int64_t> MaybeDistance =
474 GetOptionalAbsDifference(Streamer, LHS: End, RHS: Begin);
475 if (!MaybeDistance)
476 return;
477 uint32_t Distance = (uint32_t)*MaybeDistance;
478
479 for (const auto &I : Insns) {
480 switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
481 default:
482 break;
483 case Win64EH::UOP_TrapFrame:
484 case Win64EH::UOP_PushMachFrame:
485 case Win64EH::UOP_Context:
486 case Win64EH::UOP_ECContext:
487 case Win64EH::UOP_ClearUnwoundToCall:
488 // Can't reason about these opcodes and how they map to actual
489 // instructions.
490 return;
491 }
492 }
493 // Exclude the end opcode which doesn't map to an instruction.
494 uint32_t InstructionBytes = 4 * (Insns.size() - 1);
495 if (Distance != InstructionBytes) {
496 Streamer.getContext().reportError(
497 L: SMLoc(), Msg: "Incorrect size for " + Name + " " + Type + ": " +
498 Twine(Distance) +
499 " bytes of instructions in range, but .seh directives "
500 "corresponding to " +
501 Twine(InstructionBytes) + " bytes\n");
502 }
503}
504
505static uint32_t ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
506 uint32_t Count = 0;
507 for (const auto &I : Insns) {
508 switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
509 default:
510 llvm_unreachable("Unsupported ARM64 unwind code");
511 case Win64EH::UOP_AllocSmall:
512 Count += 1;
513 break;
514 case Win64EH::UOP_AllocMedium:
515 Count += 2;
516 break;
517 case Win64EH::UOP_AllocLarge:
518 Count += 4;
519 break;
520 case Win64EH::UOP_SaveR19R20X:
521 Count += 1;
522 break;
523 case Win64EH::UOP_SaveFPLRX:
524 Count += 1;
525 break;
526 case Win64EH::UOP_SaveFPLR:
527 Count += 1;
528 break;
529 case Win64EH::UOP_SaveReg:
530 Count += 2;
531 break;
532 case Win64EH::UOP_SaveRegP:
533 Count += 2;
534 break;
535 case Win64EH::UOP_SaveRegPX:
536 Count += 2;
537 break;
538 case Win64EH::UOP_SaveRegX:
539 Count += 2;
540 break;
541 case Win64EH::UOP_SaveLRPair:
542 Count += 2;
543 break;
544 case Win64EH::UOP_SaveFReg:
545 Count += 2;
546 break;
547 case Win64EH::UOP_SaveFRegP:
548 Count += 2;
549 break;
550 case Win64EH::UOP_SaveFRegX:
551 Count += 2;
552 break;
553 case Win64EH::UOP_SaveFRegPX:
554 Count += 2;
555 break;
556 case Win64EH::UOP_SetFP:
557 Count += 1;
558 break;
559 case Win64EH::UOP_AddFP:
560 Count += 2;
561 break;
562 case Win64EH::UOP_Nop:
563 Count += 1;
564 break;
565 case Win64EH::UOP_End:
566 Count += 1;
567 break;
568 case Win64EH::UOP_SaveNext:
569 Count += 1;
570 break;
571 case Win64EH::UOP_TrapFrame:
572 Count += 1;
573 break;
574 case Win64EH::UOP_PushMachFrame:
575 Count += 1;
576 break;
577 case Win64EH::UOP_Context:
578 Count += 1;
579 break;
580 case Win64EH::UOP_ECContext:
581 Count += 1;
582 break;
583 case Win64EH::UOP_ClearUnwoundToCall:
584 Count += 1;
585 break;
586 case Win64EH::UOP_PACSignLR:
587 Count += 1;
588 break;
589 case Win64EH::UOP_AllocZ:
590 Count += 2;
591 break;
592 case Win64EH::UOP_SaveAnyRegI:
593 case Win64EH::UOP_SaveAnyRegIP:
594 case Win64EH::UOP_SaveAnyRegD:
595 case Win64EH::UOP_SaveAnyRegDP:
596 case Win64EH::UOP_SaveAnyRegQ:
597 case Win64EH::UOP_SaveAnyRegQP:
598 case Win64EH::UOP_SaveAnyRegIX:
599 case Win64EH::UOP_SaveAnyRegIPX:
600 case Win64EH::UOP_SaveAnyRegDX:
601 case Win64EH::UOP_SaveAnyRegDPX:
602 case Win64EH::UOP_SaveAnyRegQX:
603 case Win64EH::UOP_SaveAnyRegQPX:
604 case Win64EH::UOP_SaveZReg:
605 case Win64EH::UOP_SavePReg:
606 Count += 3;
607 break;
608 }
609 }
610 return Count;
611}
612
613// Unwind opcode encodings and restrictions are documented at
614// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
615static void ARM64EmitUnwindCode(MCStreamer &streamer,
616 const WinEH::Instruction &inst) {
617 uint8_t b, reg;
618 switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
619 default:
620 llvm_unreachable("Unsupported ARM64 unwind code");
621 case Win64EH::UOP_AllocSmall:
622 b = (inst.Offset >> 4) & 0x1F;
623 streamer.emitInt8(Value: b);
624 break;
625 case Win64EH::UOP_AllocMedium: {
626 uint16_t hw = (inst.Offset >> 4) & 0x7FF;
627 b = 0xC0;
628 b |= (hw >> 8);
629 streamer.emitInt8(Value: b);
630 b = hw & 0xFF;
631 streamer.emitInt8(Value: b);
632 break;
633 }
634 case Win64EH::UOP_AllocLarge: {
635 uint32_t w;
636 b = 0xE0;
637 streamer.emitInt8(Value: b);
638 w = inst.Offset >> 4;
639 b = (w & 0x00FF0000) >> 16;
640 streamer.emitInt8(Value: b);
641 b = (w & 0x0000FF00) >> 8;
642 streamer.emitInt8(Value: b);
643 b = w & 0x000000FF;
644 streamer.emitInt8(Value: b);
645 break;
646 }
647 case Win64EH::UOP_SetFP:
648 b = 0xE1;
649 streamer.emitInt8(Value: b);
650 break;
651 case Win64EH::UOP_AddFP:
652 b = 0xE2;
653 streamer.emitInt8(Value: b);
654 b = (inst.Offset >> 3);
655 streamer.emitInt8(Value: b);
656 break;
657 case Win64EH::UOP_Nop:
658 b = 0xE3;
659 streamer.emitInt8(Value: b);
660 break;
661 case Win64EH::UOP_SaveR19R20X:
662 b = 0x20;
663 b |= (inst.Offset >> 3) & 0x1F;
664 streamer.emitInt8(Value: b);
665 break;
666 case Win64EH::UOP_SaveFPLRX:
667 b = 0x80;
668 b |= ((inst.Offset - 1) >> 3) & 0x3F;
669 streamer.emitInt8(Value: b);
670 break;
671 case Win64EH::UOP_SaveFPLR:
672 b = 0x40;
673 b |= (inst.Offset >> 3) & 0x3F;
674 streamer.emitInt8(Value: b);
675 break;
676 case Win64EH::UOP_SaveReg:
677 assert(inst.Register >= 19 && "Saved reg must be >= 19");
678 reg = inst.Register - 19;
679 b = 0xD0 | ((reg & 0xC) >> 2);
680 streamer.emitInt8(Value: b);
681 b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
682 streamer.emitInt8(Value: b);
683 break;
684 case Win64EH::UOP_SaveRegX:
685 assert(inst.Register >= 19 && "Saved reg must be >= 19");
686 reg = inst.Register - 19;
687 b = 0xD4 | ((reg & 0x8) >> 3);
688 streamer.emitInt8(Value: b);
689 b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1);
690 streamer.emitInt8(Value: b);
691 break;
692 case Win64EH::UOP_SaveRegP:
693 assert(inst.Register >= 19 && "Saved registers must be >= 19");
694 reg = inst.Register - 19;
695 b = 0xC8 | ((reg & 0xC) >> 2);
696 streamer.emitInt8(Value: b);
697 b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
698 streamer.emitInt8(Value: b);
699 break;
700 case Win64EH::UOP_SaveRegPX:
701 assert(inst.Register >= 19 && "Saved registers must be >= 19");
702 reg = inst.Register - 19;
703 b = 0xCC | ((reg & 0xC) >> 2);
704 streamer.emitInt8(Value: b);
705 b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1);
706 streamer.emitInt8(Value: b);
707 break;
708 case Win64EH::UOP_SaveLRPair:
709 assert(inst.Register >= 19 && "Saved reg must be >= 19");
710 reg = inst.Register - 19;
711 assert((reg % 2) == 0 && "Saved reg must be 19+2*X");
712 reg /= 2;
713 b = 0xD6 | ((reg & 0x7) >> 2);
714 streamer.emitInt8(Value: b);
715 b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
716 streamer.emitInt8(Value: b);
717 break;
718 case Win64EH::UOP_SaveFReg:
719 assert(inst.Register >= 8 && "Saved dreg must be >= 8");
720 reg = inst.Register - 8;
721 b = 0xDC | ((reg & 0x4) >> 2);
722 streamer.emitInt8(Value: b);
723 b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
724 streamer.emitInt8(Value: b);
725 break;
726 case Win64EH::UOP_SaveFRegX:
727 assert(inst.Register >= 8 && "Saved dreg must be >= 8");
728 reg = inst.Register - 8;
729 b = 0xDE;
730 streamer.emitInt8(Value: b);
731 b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1);
732 streamer.emitInt8(Value: b);
733 break;
734 case Win64EH::UOP_SaveFRegP:
735 assert(inst.Register >= 8 && "Saved dregs must be >= 8");
736 reg = inst.Register - 8;
737 b = 0xD8 | ((reg & 0x4) >> 2);
738 streamer.emitInt8(Value: b);
739 b = ((reg & 0x3) << 6) | (inst.Offset >> 3);
740 streamer.emitInt8(Value: b);
741 break;
742 case Win64EH::UOP_SaveFRegPX:
743 assert(inst.Register >= 8 && "Saved dregs must be >= 8");
744 reg = inst.Register - 8;
745 b = 0xDA | ((reg & 0x4) >> 2);
746 streamer.emitInt8(Value: b);
747 b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1);
748 streamer.emitInt8(Value: b);
749 break;
750 case Win64EH::UOP_End:
751 b = 0xE4;
752 streamer.emitInt8(Value: b);
753 break;
754 case Win64EH::UOP_SaveNext:
755 b = 0xE6;
756 streamer.emitInt8(Value: b);
757 break;
758 case Win64EH::UOP_TrapFrame:
759 b = 0xE8;
760 streamer.emitInt8(Value: b);
761 break;
762 case Win64EH::UOP_PushMachFrame:
763 b = 0xE9;
764 streamer.emitInt8(Value: b);
765 break;
766 case Win64EH::UOP_Context:
767 b = 0xEA;
768 streamer.emitInt8(Value: b);
769 break;
770 case Win64EH::UOP_ECContext:
771 b = 0xEB;
772 streamer.emitInt8(Value: b);
773 break;
774 case Win64EH::UOP_ClearUnwoundToCall:
775 b = 0xEC;
776 streamer.emitInt8(Value: b);
777 break;
778 case Win64EH::UOP_PACSignLR:
779 b = 0xFC;
780 streamer.emitInt8(Value: b);
781 break;
782 case Win64EH::UOP_SaveAnyRegI:
783 case Win64EH::UOP_SaveAnyRegIP:
784 case Win64EH::UOP_SaveAnyRegD:
785 case Win64EH::UOP_SaveAnyRegDP:
786 case Win64EH::UOP_SaveAnyRegQ:
787 case Win64EH::UOP_SaveAnyRegQP:
788 case Win64EH::UOP_SaveAnyRegIX:
789 case Win64EH::UOP_SaveAnyRegIPX:
790 case Win64EH::UOP_SaveAnyRegDX:
791 case Win64EH::UOP_SaveAnyRegDPX:
792 case Win64EH::UOP_SaveAnyRegQX:
793 case Win64EH::UOP_SaveAnyRegQPX: {
794 // This assumes the opcodes are listed in the enum in a particular order.
795 int Op = inst.Operation - Win64EH::UOP_SaveAnyRegI;
796 int Writeback = Op / 6;
797 int Paired = Op % 2;
798 int Mode = (Op / 2) % 3;
799 int Offset = inst.Offset >> 3;
800 if (Writeback || Paired || Mode == 2)
801 Offset >>= 1;
802 if (Writeback)
803 --Offset;
804 b = 0xE7;
805 streamer.emitInt8(Value: b);
806 assert(inst.Register < 32);
807 b = inst.Register | (Writeback << 5) | (Paired << 6);
808 streamer.emitInt8(Value: b);
809 b = Offset | (Mode << 6);
810 streamer.emitInt8(Value: b);
811 break;
812 }
813 case Win64EH::UOP_AllocZ: {
814 b = 0xDF;
815 streamer.emitInt8(Value: b);
816 b = inst.Offset;
817 streamer.emitInt8(Value: b);
818 break;
819 }
820 case Win64EH::UOP_SaveZReg: {
821 assert(inst.Register >= 8 && inst.Register <= 23);
822 assert(inst.Offset < 256);
823 b = 0xE7;
824 streamer.emitInt8(Value: b);
825 reg = inst.Register - 8;
826 b = ((inst.Offset & 0xC0) >> 1) | reg;
827 streamer.emitInt8(Value: b);
828 b = 0xC0 | (inst.Offset & 0x3F);
829 streamer.emitInt8(Value: b);
830 break;
831 }
832 case Win64EH::UOP_SavePReg: {
833 assert(inst.Register >= 4 && inst.Register <= 15);
834 assert(inst.Offset < 256);
835 b = 0xE7;
836 streamer.emitInt8(Value: b);
837 reg = inst.Register;
838 b = ((inst.Offset & 0xC0) >> 1) | 0x10 | reg;
839 streamer.emitInt8(Value: b);
840 b = 0xC0 | (inst.Offset & 0x3F);
841 streamer.emitInt8(Value: b);
842 break;
843 }
844 }
845}
846
847// Returns the epilog symbol of an epilog with the exact same unwind code
848// sequence, if it exists. Otherwise, returns nullptr.
849// EpilogInstrs - Unwind codes for the current epilog.
850// Epilogs - Epilogs that potentialy match the current epilog.
851static MCSymbol*
852FindMatchingEpilog(const std::vector<WinEH::Instruction>& EpilogInstrs,
853 const std::vector<MCSymbol *>& Epilogs,
854 const WinEH::FrameInfo *info) {
855 for (auto *EpilogStart : Epilogs) {
856 auto InstrsIter = info->EpilogMap.find(Key: EpilogStart);
857 assert(InstrsIter != info->EpilogMap.end() &&
858 "Epilog not found in EpilogMap");
859 const auto &Instrs = InstrsIter->second.Instructions;
860
861 if (Instrs.size() != EpilogInstrs.size())
862 continue;
863
864 bool Match = true;
865 for (unsigned i = 0; i < Instrs.size(); ++i)
866 if (Instrs[i] != EpilogInstrs[i]) {
867 Match = false;
868 break;
869 }
870
871 if (Match)
872 return EpilogStart;
873 }
874 return nullptr;
875}
876
877static void simplifyARM64Opcodes(std::vector<WinEH::Instruction> &Instructions,
878 bool Reverse) {
879 unsigned PrevOffset = -1;
880 unsigned PrevRegister = -1;
881
882 auto VisitInstruction = [&](WinEH::Instruction &Inst) {
883 // Convert 2-byte opcodes into equivalent 1-byte ones.
884 if (Inst.Operation == Win64EH::UOP_SaveRegP && Inst.Register == 29) {
885 Inst.Operation = Win64EH::UOP_SaveFPLR;
886 Inst.Register = -1;
887 } else if (Inst.Operation == Win64EH::UOP_SaveRegPX &&
888 Inst.Register == 29) {
889 Inst.Operation = Win64EH::UOP_SaveFPLRX;
890 Inst.Register = -1;
891 } else if (Inst.Operation == Win64EH::UOP_SaveRegPX &&
892 Inst.Register == 19 && Inst.Offset <= 248) {
893 Inst.Operation = Win64EH::UOP_SaveR19R20X;
894 Inst.Register = -1;
895 } else if (Inst.Operation == Win64EH::UOP_AddFP && Inst.Offset == 0) {
896 Inst.Operation = Win64EH::UOP_SetFP;
897 } else if (Inst.Operation == Win64EH::UOP_SaveRegP &&
898 Inst.Register == PrevRegister + 2 &&
899 Inst.Offset == PrevOffset + 16) {
900 Inst.Operation = Win64EH::UOP_SaveNext;
901 Inst.Register = -1;
902 Inst.Offset = 0;
903 // Intentionally not creating UOP_SaveNext for float register pairs,
904 // as current versions of Windows (up to at least 20.04) is buggy
905 // regarding SaveNext for float pairs.
906 }
907 // Update info about the previous instruction, for detecting if
908 // the next one can be made a UOP_SaveNext
909 if (Inst.Operation == Win64EH::UOP_SaveR19R20X) {
910 PrevOffset = 0;
911 PrevRegister = 19;
912 } else if (Inst.Operation == Win64EH::UOP_SaveRegPX) {
913 PrevOffset = 0;
914 PrevRegister = Inst.Register;
915 } else if (Inst.Operation == Win64EH::UOP_SaveRegP) {
916 PrevOffset = Inst.Offset;
917 PrevRegister = Inst.Register;
918 } else if (Inst.Operation == Win64EH::UOP_SaveNext) {
919 PrevRegister += 2;
920 PrevOffset += 16;
921 } else {
922 PrevRegister = -1;
923 PrevOffset = -1;
924 }
925 };
926
927 // Iterate over instructions in a forward order (for prologues),
928 // backwards for epilogues (i.e. always reverse compared to how the
929 // opcodes are stored).
930 if (Reverse) {
931 for (auto It = Instructions.rbegin(); It != Instructions.rend(); It++)
932 VisitInstruction(*It);
933 } else {
934 for (WinEH::Instruction &Inst : Instructions)
935 VisitInstruction(Inst);
936 }
937}
938
939// Check if an epilog exists as a subset of the end of a prolog (backwards).
940static int
941getARM64OffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
942 const std::vector<WinEH::Instruction> &Epilog) {
943 // Can't find an epilog as a subset if it is longer than the prolog.
944 if (Epilog.size() > Prolog.size())
945 return -1;
946
947 // Check that the epilog actually is a perfect match for the end (backwrds)
948 // of the prolog.
949 for (int I = Epilog.size() - 1; I >= 0; I--) {
950 if (Prolog[I] != Epilog[Epilog.size() - 1 - I])
951 return -1;
952 }
953
954 if (Epilog.size() == Prolog.size())
955 return 0;
956
957 // If the epilog was a subset of the prolog, find its offset.
958 return ARM64CountOfUnwindCodes(Insns: ArrayRef<WinEH::Instruction>(
959 &Prolog[Epilog.size()], Prolog.size() - Epilog.size()));
960}
961
962static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
963 WinEH::FrameInfo::Segment *Seg,
964 int PrologCodeBytes) {
965 // Can only pack if there's one single epilog
966 if (Seg->Epilogs.size() != 1)
967 return -1;
968
969 MCSymbol *Sym = Seg->Epilogs.begin()->first;
970 const std::vector<WinEH::Instruction> &Epilog =
971 info->EpilogMap[Sym].Instructions;
972
973 // Check that the epilog actually is at the very end of the function,
974 // otherwise it can't be packed.
975 uint32_t DistanceFromEnd =
976 (uint32_t)(Seg->Offset + Seg->Length - Seg->Epilogs.begin()->second);
977 if (DistanceFromEnd / 4 != Epilog.size())
978 return -1;
979
980 int RetVal = -1;
981 // Even if we don't end up sharing opcodes with the prolog, we can still
982 // write the offset as a packed offset, if the single epilog is located at
983 // the end of the function and the offset (pointing after the prolog) fits
984 // as a packed offset.
985 if (PrologCodeBytes <= 31 &&
986 PrologCodeBytes + ARM64CountOfUnwindCodes(Insns: Epilog) <= 124)
987 RetVal = PrologCodeBytes;
988
989 int Offset = getARM64OffsetInProlog(Prolog: info->Instructions, Epilog);
990 if (Offset < 0)
991 return RetVal;
992
993 // Check that the offset and prolog size fits in the first word; it's
994 // unclear whether the epilog count in the extension word can be taken
995 // as packed epilog offset.
996 if (Offset > 31 || PrologCodeBytes > 124)
997 return RetVal;
998
999 // As we choose to express the epilog as part of the prolog, remove the
1000 // epilog from the map, so we don't try to emit its opcodes.
1001 info->EpilogMap.erase(Key: Sym);
1002 return Offset;
1003}
1004
1005static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength,
1006 int PackedEpilogOffset) {
1007 if (PackedEpilogOffset == 0) {
1008 // Fully symmetric prolog and epilog, should be ok for packed format.
1009 // For CR=3, the corresponding synthesized epilog actually lacks the
1010 // SetFP opcode, but unwinding should work just fine despite that
1011 // (if at the SetFP opcode, the unwinder considers it as part of the
1012 // function body and just unwinds the full prolog instead).
1013 } else if (PackedEpilogOffset == 1) {
1014 // One single case of differences between prolog and epilog is allowed:
1015 // The epilog can lack a single SetFP that is the last opcode in the
1016 // prolog, for the CR=3 case.
1017 if (info->Instructions.back().Operation != Win64EH::UOP_SetFP)
1018 return false;
1019 } else {
1020 // Too much difference between prolog and epilog.
1021 return false;
1022 }
1023 unsigned RegI = 0, RegF = 0;
1024 int Predecrement = 0;
1025 enum {
1026 Start,
1027 Start2,
1028 Start3,
1029 IntRegs,
1030 FloatRegs,
1031 InputArgs,
1032 StackAdjust,
1033 FrameRecord,
1034 End
1035 } Location = Start;
1036 bool StandaloneLR = false, FPLRPair = false;
1037 bool PAC = false;
1038 int StackOffset = 0;
1039 int Nops = 0;
1040 // Iterate over the prolog and check that all opcodes exactly match
1041 // the canonical order and form. A more lax check could verify that
1042 // all saved registers are in the expected locations, but not enforce
1043 // the order - that would work fine when unwinding from within
1044 // functions, but not be exactly right if unwinding happens within
1045 // prologs/epilogs.
1046 for (const WinEH::Instruction &Inst : info->Instructions) {
1047 switch (Inst.Operation) {
1048 case Win64EH::UOP_End:
1049 if (Location != Start)
1050 return false;
1051 Location = Start2;
1052 break;
1053 case Win64EH::UOP_PACSignLR:
1054 if (Location != Start2)
1055 return false;
1056 PAC = true;
1057 Location = Start3;
1058 break;
1059 case Win64EH::UOP_SaveR19R20X:
1060 if (Location != Start2 && Location != Start3)
1061 return false;
1062 Predecrement = Inst.Offset;
1063 RegI = 2;
1064 Location = IntRegs;
1065 break;
1066 case Win64EH::UOP_SaveRegX:
1067 if (Location != Start2 && Location != Start3)
1068 return false;
1069 Predecrement = Inst.Offset;
1070 if (Inst.Register == 19)
1071 RegI += 1;
1072 else if (Inst.Register == 30)
1073 StandaloneLR = true;
1074 else
1075 return false;
1076 // Odd register; can't be any further int registers.
1077 Location = FloatRegs;
1078 break;
1079 case Win64EH::UOP_SaveRegPX:
1080 // Can't have this in a canonical prologue. Either this has been
1081 // canonicalized into SaveR19R20X or SaveFPLRX, or it's an unsupported
1082 // register pair.
1083 // It can't be canonicalized into SaveR19R20X if the offset is
1084 // larger than 248 bytes, but even with the maximum case with
1085 // RegI=10/RegF=8/CR=1/H=1, we end up with SavSZ = 216, which should
1086 // fit into SaveR19R20X.
1087 // The unwinding opcodes can't describe the otherwise seemingly valid
1088 // case for RegI=1 CR=1, that would start with a
1089 // "stp x19, lr, [sp, #-...]!" as that fits neither SaveRegPX nor
1090 // SaveLRPair.
1091 return false;
1092 case Win64EH::UOP_SaveRegP:
1093 if (Location != IntRegs || Inst.Offset != 8 * RegI ||
1094 Inst.Register != 19 + RegI)
1095 return false;
1096 RegI += 2;
1097 break;
1098 case Win64EH::UOP_SaveReg:
1099 if (Location != IntRegs || Inst.Offset != 8 * RegI)
1100 return false;
1101 if (Inst.Register == 19 + RegI)
1102 RegI += 1;
1103 else if (Inst.Register == 30)
1104 StandaloneLR = true;
1105 else
1106 return false;
1107 // Odd register; can't be any further int registers.
1108 Location = FloatRegs;
1109 break;
1110 case Win64EH::UOP_SaveLRPair:
1111 if (Location != IntRegs || Inst.Offset != 8 * RegI ||
1112 Inst.Register != 19 + RegI)
1113 return false;
1114 RegI += 1;
1115 StandaloneLR = true;
1116 Location = FloatRegs;
1117 break;
1118 case Win64EH::UOP_SaveFRegX:
1119 // Packed unwind can't handle prologs that only save one single
1120 // float register.
1121 return false;
1122 case Win64EH::UOP_SaveFReg:
1123 if (Location != FloatRegs || RegF == 0 || Inst.Register != 8 + RegF ||
1124 Inst.Offset != 8 * (RegI + (StandaloneLR ? 1 : 0) + RegF))
1125 return false;
1126 RegF += 1;
1127 Location = InputArgs;
1128 break;
1129 case Win64EH::UOP_SaveFRegPX:
1130 if ((Location != Start2 && Location != Start3) || Inst.Register != 8)
1131 return false;
1132 Predecrement = Inst.Offset;
1133 RegF = 2;
1134 Location = FloatRegs;
1135 break;
1136 case Win64EH::UOP_SaveFRegP:
1137 if ((Location != IntRegs && Location != FloatRegs) ||
1138 Inst.Register != 8 + RegF ||
1139 Inst.Offset != 8 * (RegI + (StandaloneLR ? 1 : 0) + RegF))
1140 return false;
1141 RegF += 2;
1142 Location = FloatRegs;
1143 break;
1144 case Win64EH::UOP_SaveNext:
1145 if (Location == IntRegs)
1146 RegI += 2;
1147 else if (Location == FloatRegs)
1148 RegF += 2;
1149 else
1150 return false;
1151 break;
1152 case Win64EH::UOP_Nop:
1153 if (Location != IntRegs && Location != FloatRegs && Location != InputArgs)
1154 return false;
1155 Location = InputArgs;
1156 Nops++;
1157 break;
1158 case Win64EH::UOP_AllocSmall:
1159 case Win64EH::UOP_AllocMedium:
1160 if (Location != Start2 && Location != Start3 && Location != IntRegs &&
1161 Location != FloatRegs && Location != InputArgs &&
1162 Location != StackAdjust)
1163 return false;
1164 // Can have either a single decrement, or a pair of decrements with
1165 // 4080 and another decrement.
1166 if (StackOffset == 0)
1167 StackOffset = Inst.Offset;
1168 else if (StackOffset != 4080)
1169 return false;
1170 else
1171 StackOffset += Inst.Offset;
1172 Location = StackAdjust;
1173 break;
1174 case Win64EH::UOP_SaveFPLRX:
1175 // Not allowing FPLRX after StackAdjust; if a StackAdjust is used, it
1176 // should be followed by a FPLR instead.
1177 if (Location != Start2 && Location != Start3 && Location != IntRegs &&
1178 Location != FloatRegs && Location != InputArgs)
1179 return false;
1180 StackOffset = Inst.Offset;
1181 Location = FrameRecord;
1182 FPLRPair = true;
1183 break;
1184 case Win64EH::UOP_SaveFPLR:
1185 // This can only follow after a StackAdjust
1186 if (Location != StackAdjust || Inst.Offset != 0)
1187 return false;
1188 Location = FrameRecord;
1189 FPLRPair = true;
1190 break;
1191 case Win64EH::UOP_SetFP:
1192 if (Location != FrameRecord)
1193 return false;
1194 Location = End;
1195 break;
1196 case Win64EH::UOP_SaveAnyRegI:
1197 case Win64EH::UOP_SaveAnyRegIP:
1198 case Win64EH::UOP_SaveAnyRegD:
1199 case Win64EH::UOP_SaveAnyRegDP:
1200 case Win64EH::UOP_SaveAnyRegQ:
1201 case Win64EH::UOP_SaveAnyRegQP:
1202 case Win64EH::UOP_SaveAnyRegIX:
1203 case Win64EH::UOP_SaveAnyRegIPX:
1204 case Win64EH::UOP_SaveAnyRegDX:
1205 case Win64EH::UOP_SaveAnyRegDPX:
1206 case Win64EH::UOP_SaveAnyRegQX:
1207 case Win64EH::UOP_SaveAnyRegQPX:
1208 // These are never canonical; they don't show up with the usual Arm64
1209 // calling convention.
1210 return false;
1211 case Win64EH::UOP_AllocLarge:
1212 // Allocations this large can't be represented in packed unwind (and
1213 // usually don't fit the canonical form anyway because we need to use
1214 // __chkstk to allocate the stack space).
1215 return false;
1216 case Win64EH::UOP_AddFP:
1217 // "add x29, sp, #N" doesn't show up in the canonical pattern (except for
1218 // N=0, which is UOP_SetFP).
1219 return false;
1220 case Win64EH::UOP_AllocZ:
1221 case Win64EH::UOP_SaveZReg:
1222 case Win64EH::UOP_SavePReg:
1223 // Canonical prologues don't support spilling SVE registers.
1224 return false;
1225 case Win64EH::UOP_TrapFrame:
1226 case Win64EH::UOP_Context:
1227 case Win64EH::UOP_ECContext:
1228 case Win64EH::UOP_ClearUnwoundToCall:
1229 case Win64EH::UOP_PushMachFrame:
1230 // These are special opcodes that aren't normally generated.
1231 return false;
1232 default:
1233 report_fatal_error(reason: "Unknown Arm64 unwind opcode");
1234 }
1235 }
1236 if (RegI > 10 || RegF > 8)
1237 return false;
1238 if (StandaloneLR && FPLRPair)
1239 return false;
1240 if (FPLRPair && Location != End)
1241 return false;
1242 if (Nops != 0 && Nops != 4)
1243 return false;
1244 if (PAC && !FPLRPair)
1245 return false;
1246 int H = Nops == 4;
1247 // There's an inconsistency regarding packed unwind info with homed
1248 // parameters; according to the documentation, the epilog shouldn't have
1249 // the same corresponding nops (and thus, to set the H bit, we should
1250 // require an epilog which isn't exactly symmetrical - we shouldn't accept
1251 // an exact mirrored epilog for those cases), but in practice,
1252 // RtlVirtualUnwind behaves as if it does expect the epilogue to contain
1253 // the same nops. See https://github.com/llvm/llvm-project/issues/54879.
1254 // To play it safe, don't produce packed unwind info with homed parameters.
1255 if (H)
1256 return false;
1257 int IntSZ = 8 * RegI;
1258 if (StandaloneLR)
1259 IntSZ += 8;
1260 int FpSZ = 8 * RegF; // RegF not yet decremented
1261 int SavSZ = (IntSZ + FpSZ + 8 * 8 * H + 0xF) & ~0xF;
1262 if (Predecrement != SavSZ)
1263 return false;
1264 if (FPLRPair && StackOffset < 16)
1265 return false;
1266 if (StackOffset % 16)
1267 return false;
1268 uint32_t FrameSize = (StackOffset + SavSZ) / 16;
1269 if (FrameSize > 0x1FF)
1270 return false;
1271 assert(RegF != 1 && "One single float reg not allowed");
1272 if (RegF > 0)
1273 RegF--; // Convert from actual number of registers, to value stored
1274 assert(FuncLength <= 0x7FF && "FuncLength should have been checked earlier");
1275 int Flag = 0x01; // Function segments not supported yet
1276 int CR = PAC ? 2 : FPLRPair ? 3 : StandaloneLR ? 1 : 0;
1277 info->PackedInfo |= Flag << 0;
1278 info->PackedInfo |= (FuncLength & 0x7FF) << 2;
1279 info->PackedInfo |= (RegF & 0x7) << 13;
1280 info->PackedInfo |= (RegI & 0xF) << 16;
1281 info->PackedInfo |= (H & 0x1) << 20;
1282 info->PackedInfo |= (CR & 0x3) << 21;
1283 info->PackedInfo |= (FrameSize & 0x1FF) << 23;
1284 return true;
1285}
1286
1287static void ARM64ProcessEpilogs(WinEH::FrameInfo *info,
1288 WinEH::FrameInfo::Segment *Seg,
1289 uint32_t &TotalCodeBytes,
1290 MapVector<MCSymbol *, uint32_t> &EpilogInfo) {
1291
1292 std::vector<MCSymbol *> EpilogStarts;
1293 for (auto &I : Seg->Epilogs)
1294 EpilogStarts.push_back(x: I.first);
1295
1296 // Epilogs processed so far.
1297 std::vector<MCSymbol *> AddedEpilogs;
1298 for (auto *S : EpilogStarts) {
1299 MCSymbol *EpilogStart = S;
1300 auto &EpilogInstrs = info->EpilogMap[S].Instructions;
1301 uint32_t CodeBytes = ARM64CountOfUnwindCodes(Insns: EpilogInstrs);
1302
1303 MCSymbol* MatchingEpilog =
1304 FindMatchingEpilog(EpilogInstrs, Epilogs: AddedEpilogs, info);
1305 int PrologOffset;
1306 if (MatchingEpilog) {
1307 assert(EpilogInfo.contains(MatchingEpilog) &&
1308 "Duplicate epilog not found");
1309 EpilogInfo[EpilogStart] = EpilogInfo.lookup(Key: MatchingEpilog);
1310 // Clear the unwind codes in the EpilogMap, so that they don't get output
1311 // in ARM64EmitUnwindInfoForSegment().
1312 EpilogInstrs.clear();
1313 } else if ((PrologOffset = getARM64OffsetInProlog(Prolog: info->Instructions,
1314 Epilog: EpilogInstrs)) >= 0) {
1315 EpilogInfo[EpilogStart] = PrologOffset;
1316 // If the segment doesn't have a prolog, an end_c will be emitted before
1317 // prolog opcodes. So epilog start index in opcodes array is moved by 1.
1318 if (!Seg->HasProlog)
1319 EpilogInfo[EpilogStart] += 1;
1320 // Clear the unwind codes in the EpilogMap, so that they don't get output
1321 // in ARM64EmitUnwindInfoForSegment().
1322 EpilogInstrs.clear();
1323 } else {
1324 EpilogInfo[EpilogStart] = TotalCodeBytes;
1325 TotalCodeBytes += CodeBytes;
1326 AddedEpilogs.push_back(x: EpilogStart);
1327 }
1328 }
1329}
1330
1331static void ARM64FindSegmentsInFunction(MCStreamer &streamer,
1332 WinEH::FrameInfo *info,
1333 int64_t RawFuncLength) {
1334 if (info->PrologEnd)
1335 checkARM64Instructions(Streamer&: streamer, Insns: info->Instructions, Begin: info->Begin,
1336 End: info->PrologEnd, Name: info->Function->getName(),
1337 Type: "prologue");
1338 struct EpilogStartEnd {
1339 MCSymbol *Start;
1340 int64_t Offset;
1341 int64_t End;
1342 };
1343 // Record Start and End of each epilog.
1344 SmallVector<struct EpilogStartEnd, 4> Epilogs;
1345 for (auto &I : info->EpilogMap) {
1346 MCSymbol *Start = I.first;
1347 auto &Instrs = I.second.Instructions;
1348 int64_t Offset = GetAbsDifference(Streamer&: streamer, LHS: Start, RHS: info->Begin);
1349 checkARM64Instructions(Streamer&: streamer, Insns: Instrs, Begin: Start, End: I.second.End,
1350 Name: info->Function->getName(), Type: "epilogue");
1351 assert((Epilogs.size() == 0 || Offset >= Epilogs.back().End) &&
1352 "Epilogs should be monotonically ordered");
1353 // Exclue the end opcode from Instrs.size() when calculating the end of the
1354 // epilog.
1355 Epilogs.push_back(Elt: {.Start: Start, .Offset: Offset, .End: Offset + (int64_t)(Instrs.size() - 1) * 4});
1356 }
1357
1358 unsigned E = 0;
1359 int64_t SegLimit = 0xFFFFC;
1360 int64_t SegOffset = 0;
1361
1362 if (RawFuncLength > SegLimit) {
1363
1364 int64_t RemainingLength = RawFuncLength;
1365
1366 while (RemainingLength > SegLimit) {
1367 // Try divide the function into segments, requirements:
1368 // 1. Segment length <= 0xFFFFC;
1369 // 2. Each Prologue or Epilogue must be fully within a segment.
1370 int64_t SegLength = SegLimit;
1371 int64_t SegEnd = SegOffset + SegLength;
1372 // Keep record on symbols and offsets of epilogs in this segment.
1373 MapVector<MCSymbol *, int64_t> EpilogsInSegment;
1374
1375 while (E < Epilogs.size() && Epilogs[E].End < SegEnd) {
1376 // Epilogs within current segment.
1377 EpilogsInSegment[Epilogs[E].Start] = Epilogs[E].Offset;
1378 ++E;
1379 }
1380
1381 // At this point, we have:
1382 // 1. Put all epilogs in segments already. No action needed here; or
1383 // 2. Found an epilog that will cross segments boundry. We need to
1384 // move back current segment's end boundry, so the epilog is entirely
1385 // in the next segment; or
1386 // 3. Left at least one epilog that is entirely after this segment.
1387 // It'll be handled by the next iteration, or the last segment.
1388 if (E < Epilogs.size() && Epilogs[E].Offset <= SegEnd)
1389 // Move back current Segment's end boundry.
1390 SegLength = Epilogs[E].Offset - SegOffset;
1391
1392 auto Seg = WinEH::FrameInfo::Segment(
1393 SegOffset, SegLength, /* HasProlog */!SegOffset);
1394 Seg.Epilogs = std::move(EpilogsInSegment);
1395 info->Segments.push_back(x: Seg);
1396
1397 SegOffset += SegLength;
1398 RemainingLength -= SegLength;
1399 }
1400 }
1401
1402 // Add the last segment when RawFuncLength > 0xFFFFC,
1403 // or the only segment otherwise.
1404 auto LastSeg =
1405 WinEH::FrameInfo::Segment(SegOffset, RawFuncLength - SegOffset,
1406 /* HasProlog */!SegOffset);
1407 for (; E < Epilogs.size(); ++E)
1408 LastSeg.Epilogs[Epilogs[E].Start] = Epilogs[E].Offset;
1409 info->Segments.push_back(x: LastSeg);
1410}
1411
1412static void ARM64EmitUnwindInfoForSegment(MCStreamer &streamer,
1413 WinEH::FrameInfo *info,
1414 WinEH::FrameInfo::Segment &Seg,
1415 bool TryPacked = true) {
1416 MCContext &context = streamer.getContext();
1417 MCSymbol *Label = context.createTempSymbol();
1418
1419 streamer.emitValueToAlignment(Alignment: Align(4));
1420 streamer.emitLabel(Symbol: Label);
1421 Seg.Symbol = Label;
1422 // Use the 1st segemnt's label as function's.
1423 if (Seg.Offset == 0)
1424 info->Symbol = Label;
1425
1426 bool HasProlog = Seg.HasProlog;
1427 bool HasEpilogs = (Seg.Epilogs.size() != 0);
1428
1429 uint32_t SegLength = (uint32_t)Seg.Length / 4;
1430 uint32_t PrologCodeBytes = info->PrologCodeBytes;
1431
1432 int PackedEpilogOffset = HasEpilogs ?
1433 checkARM64PackedEpilog(streamer, info, Seg: &Seg, PrologCodeBytes) : -1;
1434
1435 // TODO:
1436 // 1. Enable packed unwind info (.pdata only) for multi-segment functions.
1437 // 2. Emit packed unwind info (.pdata only) for segments that have neithor
1438 // prolog nor epilog.
1439 if (info->Segments.size() == 1 && PackedEpilogOffset >= 0 &&
1440 uint32_t(PackedEpilogOffset) < PrologCodeBytes &&
1441 !info->HandlesExceptions && SegLength <= 0x7ff && TryPacked) {
1442 // Matching prolog/epilog and no exception handlers; check if the
1443 // prolog matches the patterns that can be described by the packed
1444 // format.
1445
1446 // info->Symbol was already set even if we didn't actually write any
1447 // unwind info there. Keep using that as indicator that this unwind
1448 // info has been generated already.
1449 if (tryARM64PackedUnwind(info, FuncLength: SegLength, PackedEpilogOffset))
1450 return;
1451 }
1452
1453 // If the prolog is not in this segment, we need to emit an end_c, which takes
1454 // 1 byte, before prolog unwind ops.
1455 if (!HasProlog) {
1456 PrologCodeBytes += 1;
1457 if (PackedEpilogOffset >= 0)
1458 PackedEpilogOffset += 1;
1459 // If a segment has neither prolog nor epilog, "With full .xdata record,
1460 // Epilog Count = 1. Epilog Start Index points to end_c."
1461 // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
1462 // TODO: We can remove this if testing shows zero epilog scope is ok with
1463 // MS unwinder.
1464 if (!HasEpilogs)
1465 // Pack the fake epilog into phantom prolog.
1466 PackedEpilogOffset = 0;
1467 }
1468
1469 uint32_t TotalCodeBytes = PrologCodeBytes;
1470
1471 // Process epilogs.
1472 MapVector<MCSymbol *, uint32_t> EpilogInfo;
1473 ARM64ProcessEpilogs(info, Seg: &Seg, TotalCodeBytes, EpilogInfo);
1474
1475 // Code Words, Epilog count, E, X, Vers, Function Length
1476 uint32_t row1 = 0x0;
1477 uint32_t CodeWords = TotalCodeBytes / 4;
1478 uint32_t CodeWordsMod = TotalCodeBytes % 4;
1479 if (CodeWordsMod)
1480 CodeWords++;
1481 uint32_t EpilogCount =
1482 PackedEpilogOffset >= 0 ? PackedEpilogOffset : Seg.Epilogs.size();
1483 bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124;
1484 if (!ExtensionWord) {
1485 row1 |= (EpilogCount & 0x1F) << 22;
1486 row1 |= (CodeWords & 0x1F) << 27;
1487 }
1488 if (info->HandlesExceptions) // X
1489 row1 |= 1 << 20;
1490 if (PackedEpilogOffset >= 0) // E
1491 row1 |= 1 << 21;
1492 row1 |= SegLength & 0x3FFFF;
1493 streamer.emitInt32(Value: row1);
1494
1495 // Extended Code Words, Extended Epilog Count
1496 if (ExtensionWord) {
1497 // FIXME: We should be able to split unwind info into multiple sections.
1498 if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
1499 report_fatal_error(
1500 reason: "SEH unwind data splitting is only implemented for large functions, "
1501 "cases of too many code words or too many epilogs will be done "
1502 "later");
1503 uint32_t row2 = 0x0;
1504 row2 |= (CodeWords & 0xFF) << 16;
1505 row2 |= (EpilogCount & 0xFFFF);
1506 streamer.emitInt32(Value: row2);
1507 }
1508
1509 if (PackedEpilogOffset < 0) {
1510 // Epilog Start Index, Epilog Start Offset
1511 for (auto &I : EpilogInfo) {
1512 MCSymbol *EpilogStart = I.first;
1513 uint32_t EpilogIndex = I.second;
1514 // Epilog offset within the Segment.
1515 uint32_t EpilogOffset = (uint32_t)(Seg.Epilogs[EpilogStart] - Seg.Offset);
1516 if (EpilogOffset)
1517 EpilogOffset /= 4;
1518 uint32_t row3 = EpilogOffset;
1519 row3 |= (EpilogIndex & 0x3FF) << 22;
1520 streamer.emitInt32(Value: row3);
1521 }
1522 }
1523
1524 // Note that even for segments that have no prolog, we still need to emit
1525 // prolog unwinding opcodes so that the unwinder knows how to unwind from
1526 // such a segment.
1527 // The end_c opcode at the start indicates to the unwinder that the actual
1528 // prolog is outside of the current segment, and the unwinder shouldn't try
1529 // to check for unwinding from a partial prolog.
1530 if (!HasProlog)
1531 // Emit an end_c.
1532 streamer.emitInt8(Value: (uint8_t)0xE5);
1533
1534 // Emit prolog unwind instructions (in reverse order).
1535 for (auto Inst : llvm::reverse(C&: info->Instructions))
1536 ARM64EmitUnwindCode(streamer, inst: Inst);
1537
1538 // Emit epilog unwind instructions
1539 for (auto &I : Seg.Epilogs) {
1540 auto &EpilogInstrs = info->EpilogMap[I.first].Instructions;
1541 for (const WinEH::Instruction &inst : EpilogInstrs)
1542 ARM64EmitUnwindCode(streamer, inst);
1543 }
1544
1545 int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
1546 assert(BytesMod >= 0);
1547 for (int i = 0; i < BytesMod; i++)
1548 streamer.emitInt8(Value: 0xE3);
1549
1550 if (info->HandlesExceptions)
1551 streamer.emitValue(
1552 Value: MCSymbolRefExpr::create(Symbol: info->ExceptionHandler,
1553 specifier: MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx&: context),
1554 Size: 4);
1555}
1556
1557// Populate the .xdata section. The format of .xdata on ARM64 is documented at
1558// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
1559static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
1560 bool TryPacked = true) {
1561 // If this UNWIND_INFO already has a symbol, it's already been emitted.
1562 if (info->Symbol)
1563 return;
1564 // If there's no unwind info here (not even a terminating UOP_End), the
1565 // unwind info is considered bogus and skipped. If this was done in
1566 // response to an explicit .seh_handlerdata, the associated trailing
1567 // handler data is left orphaned in the xdata section.
1568 if (info->empty()) {
1569 info->EmitAttempted = true;
1570 return;
1571 }
1572 if (info->EmitAttempted) {
1573 // If we tried to emit unwind info before (due to an explicit
1574 // .seh_handlerdata directive), but skipped it (because there was no
1575 // valid information to emit at the time), and it later got valid unwind
1576 // opcodes, we can't emit it here, because the trailing handler data
1577 // was already emitted elsewhere in the xdata section.
1578 streamer.getContext().reportError(
1579 L: SMLoc(), Msg: "Earlier .seh_handlerdata for " + info->Function->getName() +
1580 " skipped due to no unwind info at the time "
1581 "(.seh_handlerdata too early?), but the function later "
1582 "did get unwind info that can't be emitted");
1583 return;
1584 }
1585
1586 simplifyARM64Opcodes(Instructions&: info->Instructions, Reverse: false);
1587 for (auto &I : info->EpilogMap)
1588 simplifyARM64Opcodes(Instructions&: I.second.Instructions, Reverse: true);
1589
1590 int64_t RawFuncLength;
1591 if (!info->FuncletOrFuncEnd) {
1592 report_fatal_error(reason: "FuncletOrFuncEnd not set");
1593 } else {
1594 // FIXME: GetAbsDifference tries to compute the length of the function
1595 // immediately, before the whole file is emitted, but in general
1596 // that's impossible: the size in bytes of certain assembler directives
1597 // like .align and .fill is not known until the whole file is parsed and
1598 // relaxations are applied. Currently, GetAbsDifference fails with a fatal
1599 // error in that case. (We mostly don't hit this because inline assembly
1600 // specifying those directives is rare, and we don't normally try to
1601 // align loops on AArch64.)
1602 //
1603 // There are two potential approaches to delaying the computation. One,
1604 // we could emit something like ".word (endfunc-beginfunc)/4+0x10800000",
1605 // as long as we have some conservative estimate we could use to prove
1606 // that we don't need to split the unwind data. Emitting the constant
1607 // is straightforward, but there's no existing code for estimating the
1608 // size of the function.
1609 //
1610 // The other approach would be to use a dedicated, relaxable fragment,
1611 // which could grow to accommodate splitting the unwind data if
1612 // necessary. This is more straightforward, since it automatically works
1613 // without any new infrastructure, and it's consistent with how we handle
1614 // relaxation in other contexts. But it would require some refactoring
1615 // to move parts of the pdata/xdata emission into the implementation of
1616 // a fragment. We could probably continue to encode the unwind codes
1617 // here, but we'd have to emit the pdata, the xdata header, and the
1618 // epilogue scopes later, since they depend on whether the we need to
1619 // split the unwind data.
1620 //
1621 // If this is fixed, remove code in AArch64ISelLowering.cpp that
1622 // disables loop alignment on Windows.
1623 RawFuncLength = GetAbsDifference(Streamer&: streamer, LHS: info->FuncletOrFuncEnd,
1624 RHS: info->Begin);
1625 }
1626
1627 ARM64FindSegmentsInFunction(streamer, info, RawFuncLength);
1628
1629 info->PrologCodeBytes = ARM64CountOfUnwindCodes(Insns: info->Instructions);
1630 for (auto &S : info->Segments)
1631 ARM64EmitUnwindInfoForSegment(streamer, info, Seg&: S, TryPacked);
1632
1633 // Clear prolog instructions after unwind info is emitted for all segments.
1634 info->Instructions.clear();
1635}
1636
1637static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
1638 uint32_t Count = 0;
1639 for (const auto &I : Insns) {
1640 switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
1641 default:
1642 llvm_unreachable("Unsupported ARM unwind code");
1643 case Win64EH::UOP_AllocSmall:
1644 Count += 1;
1645 break;
1646 case Win64EH::UOP_AllocLarge:
1647 Count += 3;
1648 break;
1649 case Win64EH::UOP_AllocHuge:
1650 Count += 4;
1651 break;
1652 case Win64EH::UOP_WideAllocMedium:
1653 Count += 2;
1654 break;
1655 case Win64EH::UOP_WideAllocLarge:
1656 Count += 3;
1657 break;
1658 case Win64EH::UOP_WideAllocHuge:
1659 Count += 4;
1660 break;
1661 case Win64EH::UOP_WideSaveRegMask:
1662 Count += 2;
1663 break;
1664 case Win64EH::UOP_SaveSP:
1665 Count += 1;
1666 break;
1667 case Win64EH::UOP_SaveRegsR4R7LR:
1668 Count += 1;
1669 break;
1670 case Win64EH::UOP_WideSaveRegsR4R11LR:
1671 Count += 1;
1672 break;
1673 case Win64EH::UOP_SaveFRegD8D15:
1674 Count += 1;
1675 break;
1676 case Win64EH::UOP_SaveRegMask:
1677 Count += 2;
1678 break;
1679 case Win64EH::UOP_SaveLR:
1680 Count += 2;
1681 break;
1682 case Win64EH::UOP_SaveFRegD0D15:
1683 Count += 2;
1684 break;
1685 case Win64EH::UOP_SaveFRegD16D31:
1686 Count += 2;
1687 break;
1688 case Win64EH::UOP_Nop:
1689 case Win64EH::UOP_WideNop:
1690 case Win64EH::UOP_End:
1691 case Win64EH::UOP_EndNop:
1692 case Win64EH::UOP_WideEndNop:
1693 Count += 1;
1694 break;
1695 case Win64EH::UOP_Custom: {
1696 int J;
1697 for (J = 3; J > 0; J--)
1698 if (I.Offset & (0xffu << (8 * J)))
1699 break;
1700 Count += J + 1;
1701 break;
1702 }
1703 }
1704 }
1705 return Count;
1706}
1707
1708static uint32_t ARMCountOfInstructionBytes(ArrayRef<WinEH::Instruction> Insns,
1709 bool *HasCustom = nullptr) {
1710 uint32_t Count = 0;
1711 for (const auto &I : Insns) {
1712 switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
1713 default:
1714 llvm_unreachable("Unsupported ARM unwind code");
1715 case Win64EH::UOP_AllocSmall:
1716 case Win64EH::UOP_AllocLarge:
1717 case Win64EH::UOP_AllocHuge:
1718 Count += 2;
1719 break;
1720 case Win64EH::UOP_WideAllocMedium:
1721 case Win64EH::UOP_WideAllocLarge:
1722 case Win64EH::UOP_WideAllocHuge:
1723 Count += 4;
1724 break;
1725 case Win64EH::UOP_WideSaveRegMask:
1726 case Win64EH::UOP_WideSaveRegsR4R11LR:
1727 Count += 4;
1728 break;
1729 case Win64EH::UOP_SaveSP:
1730 Count += 2;
1731 break;
1732 case Win64EH::UOP_SaveRegMask:
1733 case Win64EH::UOP_SaveRegsR4R7LR:
1734 Count += 2;
1735 break;
1736 case Win64EH::UOP_SaveFRegD8D15:
1737 case Win64EH::UOP_SaveFRegD0D15:
1738 case Win64EH::UOP_SaveFRegD16D31:
1739 Count += 4;
1740 break;
1741 case Win64EH::UOP_SaveLR:
1742 Count += 4;
1743 break;
1744 case Win64EH::UOP_Nop:
1745 case Win64EH::UOP_EndNop:
1746 Count += 2;
1747 break;
1748 case Win64EH::UOP_WideNop:
1749 case Win64EH::UOP_WideEndNop:
1750 Count += 4;
1751 break;
1752 case Win64EH::UOP_End:
1753 // This doesn't map to any instruction
1754 break;
1755 case Win64EH::UOP_Custom:
1756 // We can't reason about what instructions this maps to; return a
1757 // phony number to make sure we don't accidentally do epilog packing.
1758 Count += 1000;
1759 if (HasCustom)
1760 *HasCustom = true;
1761 break;
1762 }
1763 }
1764 return Count;
1765}
1766
1767static void checkARMInstructions(MCStreamer &Streamer,
1768 ArrayRef<WinEH::Instruction> Insns,
1769 const MCSymbol *Begin, const MCSymbol *End,
1770 StringRef Name, StringRef Type) {
1771 if (!End)
1772 return;
1773 std::optional<int64_t> MaybeDistance =
1774 GetOptionalAbsDifference(Streamer, LHS: End, RHS: Begin);
1775 if (!MaybeDistance)
1776 return;
1777 uint32_t Distance = (uint32_t)*MaybeDistance;
1778 bool HasCustom = false;
1779 uint32_t InstructionBytes = ARMCountOfInstructionBytes(Insns, HasCustom: &HasCustom);
1780 if (HasCustom)
1781 return;
1782 if (Distance != InstructionBytes) {
1783 Streamer.getContext().reportError(
1784 L: SMLoc(), Msg: "Incorrect size for " + Name + " " + Type + ": " +
1785 Twine(Distance) +
1786 " bytes of instructions in range, but .seh directives "
1787 "corresponding to " +
1788 Twine(InstructionBytes) + " bytes\n");
1789 }
1790}
1791
1792static bool isARMTerminator(const WinEH::Instruction &inst) {
1793 switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
1794 case Win64EH::UOP_End:
1795 case Win64EH::UOP_EndNop:
1796 case Win64EH::UOP_WideEndNop:
1797 return true;
1798 default:
1799 return false;
1800 }
1801}
1802
1803// Unwind opcode encodings and restrictions are documented at
1804// https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
1805static void ARMEmitUnwindCode(MCStreamer &streamer,
1806 const WinEH::Instruction &inst) {
1807 uint32_t w, lr;
1808 int i;
1809 switch (static_cast<Win64EH::UnwindOpcodes>(inst.Operation)) {
1810 default:
1811 llvm_unreachable("Unsupported ARM unwind code");
1812 case Win64EH::UOP_AllocSmall:
1813 assert((inst.Offset & 3) == 0);
1814 assert(inst.Offset / 4 <= 0x7f);
1815 streamer.emitInt8(Value: inst.Offset / 4);
1816 break;
1817 case Win64EH::UOP_WideSaveRegMask:
1818 assert((inst.Register & ~0x5fff) == 0);
1819 lr = (inst.Register >> 14) & 1;
1820 w = 0x8000 | (inst.Register & 0x1fff) | (lr << 13);
1821 streamer.emitInt8(Value: (w >> 8) & 0xff);
1822 streamer.emitInt8(Value: (w >> 0) & 0xff);
1823 break;
1824 case Win64EH::UOP_SaveSP:
1825 assert(inst.Register <= 0x0f);
1826 streamer.emitInt8(Value: 0xc0 | inst.Register);
1827 break;
1828 case Win64EH::UOP_SaveRegsR4R7LR:
1829 assert(inst.Register >= 4 && inst.Register <= 7);
1830 assert(inst.Offset <= 1);
1831 streamer.emitInt8(Value: 0xd0 | (inst.Register - 4) | (inst.Offset << 2));
1832 break;
1833 case Win64EH::UOP_WideSaveRegsR4R11LR:
1834 assert(inst.Register >= 8 && inst.Register <= 11);
1835 assert(inst.Offset <= 1);
1836 streamer.emitInt8(Value: 0xd8 | (inst.Register - 8) | (inst.Offset << 2));
1837 break;
1838 case Win64EH::UOP_SaveFRegD8D15:
1839 assert(inst.Register >= 8 && inst.Register <= 15);
1840 streamer.emitInt8(Value: 0xe0 | (inst.Register - 8));
1841 break;
1842 case Win64EH::UOP_WideAllocMedium:
1843 assert((inst.Offset & 3) == 0);
1844 assert(inst.Offset / 4 <= 0x3ff);
1845 w = 0xe800 | (inst.Offset / 4);
1846 streamer.emitInt8(Value: (w >> 8) & 0xff);
1847 streamer.emitInt8(Value: (w >> 0) & 0xff);
1848 break;
1849 case Win64EH::UOP_SaveRegMask:
1850 assert((inst.Register & ~0x40ff) == 0);
1851 lr = (inst.Register >> 14) & 1;
1852 w = 0xec00 | (inst.Register & 0x0ff) | (lr << 8);
1853 streamer.emitInt8(Value: (w >> 8) & 0xff);
1854 streamer.emitInt8(Value: (w >> 0) & 0xff);
1855 break;
1856 case Win64EH::UOP_SaveLR:
1857 assert((inst.Offset & 3) == 0);
1858 assert(inst.Offset / 4 <= 0x0f);
1859 streamer.emitInt8(Value: 0xef);
1860 streamer.emitInt8(Value: inst.Offset / 4);
1861 break;
1862 case Win64EH::UOP_SaveFRegD0D15:
1863 assert(inst.Register <= 15);
1864 assert(inst.Offset <= 15);
1865 assert(inst.Register <= inst.Offset);
1866 streamer.emitInt8(Value: 0xf5);
1867 streamer.emitInt8(Value: (inst.Register << 4) | inst.Offset);
1868 break;
1869 case Win64EH::UOP_SaveFRegD16D31:
1870 assert(inst.Register >= 16 && inst.Register <= 31);
1871 assert(inst.Offset >= 16 && inst.Offset <= 31);
1872 assert(inst.Register <= inst.Offset);
1873 streamer.emitInt8(Value: 0xf6);
1874 streamer.emitInt8(Value: ((inst.Register - 16) << 4) | (inst.Offset - 16));
1875 break;
1876 case Win64EH::UOP_AllocLarge:
1877 assert((inst.Offset & 3) == 0);
1878 assert(inst.Offset / 4 <= 0xffff);
1879 w = inst.Offset / 4;
1880 streamer.emitInt8(Value: 0xf7);
1881 streamer.emitInt8(Value: (w >> 8) & 0xff);
1882 streamer.emitInt8(Value: (w >> 0) & 0xff);
1883 break;
1884 case Win64EH::UOP_AllocHuge:
1885 assert((inst.Offset & 3) == 0);
1886 assert(inst.Offset / 4 <= 0xffffff);
1887 w = inst.Offset / 4;
1888 streamer.emitInt8(Value: 0xf8);
1889 streamer.emitInt8(Value: (w >> 16) & 0xff);
1890 streamer.emitInt8(Value: (w >> 8) & 0xff);
1891 streamer.emitInt8(Value: (w >> 0) & 0xff);
1892 break;
1893 case Win64EH::UOP_WideAllocLarge:
1894 assert((inst.Offset & 3) == 0);
1895 assert(inst.Offset / 4 <= 0xffff);
1896 w = inst.Offset / 4;
1897 streamer.emitInt8(Value: 0xf9);
1898 streamer.emitInt8(Value: (w >> 8) & 0xff);
1899 streamer.emitInt8(Value: (w >> 0) & 0xff);
1900 break;
1901 case Win64EH::UOP_WideAllocHuge:
1902 assert((inst.Offset & 3) == 0);
1903 assert(inst.Offset / 4 <= 0xffffff);
1904 w = inst.Offset / 4;
1905 streamer.emitInt8(Value: 0xfa);
1906 streamer.emitInt8(Value: (w >> 16) & 0xff);
1907 streamer.emitInt8(Value: (w >> 8) & 0xff);
1908 streamer.emitInt8(Value: (w >> 0) & 0xff);
1909 break;
1910 case Win64EH::UOP_Nop:
1911 streamer.emitInt8(Value: 0xfb);
1912 break;
1913 case Win64EH::UOP_WideNop:
1914 streamer.emitInt8(Value: 0xfc);
1915 break;
1916 case Win64EH::UOP_EndNop:
1917 streamer.emitInt8(Value: 0xfd);
1918 break;
1919 case Win64EH::UOP_WideEndNop:
1920 streamer.emitInt8(Value: 0xfe);
1921 break;
1922 case Win64EH::UOP_End:
1923 streamer.emitInt8(Value: 0xff);
1924 break;
1925 case Win64EH::UOP_Custom:
1926 for (i = 3; i > 0; i--)
1927 if (inst.Offset & (0xffu << (8 * i)))
1928 break;
1929 for (; i >= 0; i--)
1930 streamer.emitInt8(Value: (inst.Offset >> (8 * i)) & 0xff);
1931 break;
1932 }
1933}
1934
1935// Check if an epilog exists as a subset of the end of a prolog (backwards).
1936// An epilog may end with one out of three different end opcodes; if this
1937// is the first epilog that shares opcodes with the prolog, we can tolerate
1938// that this opcode differs (and the caller will update the prolog to use
1939// the same end opcode as the epilog). If another epilog already shares
1940// opcodes with the prolog, the ending opcode must be a strict match.
1941static int getARMOffsetInProlog(const std::vector<WinEH::Instruction> &Prolog,
1942 const std::vector<WinEH::Instruction> &Epilog,
1943 bool CanTweakProlog) {
1944 // Can't find an epilog as a subset if it is longer than the prolog.
1945 if (Epilog.size() > Prolog.size())
1946 return -1;
1947
1948 // Check that the epilog actually is a perfect match for the end (backwrds)
1949 // of the prolog.
1950 // If we can adjust the prolog afterwards, don't check that the end opcodes
1951 // match.
1952 int EndIdx = CanTweakProlog ? 1 : 0;
1953 for (int I = Epilog.size() - 1; I >= EndIdx; I--) {
1954 // TODO: Could also allow minor mismatches, e.g. "add sp, #16" vs
1955 // "push {r0-r3}".
1956 if (Prolog[I] != Epilog[Epilog.size() - 1 - I])
1957 return -1;
1958 }
1959
1960 if (CanTweakProlog) {
1961 // Check that both prolog and epilog end with an expected end opcode.
1962 if (Prolog.front().Operation != Win64EH::UOP_End)
1963 return -1;
1964 if (Epilog.back().Operation != Win64EH::UOP_End &&
1965 Epilog.back().Operation != Win64EH::UOP_EndNop &&
1966 Epilog.back().Operation != Win64EH::UOP_WideEndNop)
1967 return -1;
1968 }
1969
1970 // If the epilog was a subset of the prolog, find its offset.
1971 if (Epilog.size() == Prolog.size())
1972 return 0;
1973 return ARMCountOfUnwindCodes(Insns: ArrayRef<WinEH::Instruction>(
1974 &Prolog[Epilog.size()], Prolog.size() - Epilog.size()));
1975}
1976
1977static int checkARMPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
1978 int PrologCodeBytes) {
1979 // Can only pack if there's one single epilog
1980 if (info->EpilogMap.size() != 1)
1981 return -1;
1982
1983 const WinEH::FrameInfo::Epilog &EpilogInfo = info->EpilogMap.begin()->second;
1984 // Can only pack if the epilog is unconditional
1985 if (EpilogInfo.Condition != 0xe) // ARMCC::AL
1986 return -1;
1987
1988 const std::vector<WinEH::Instruction> &Epilog = EpilogInfo.Instructions;
1989 // Make sure we have at least the trailing end opcode
1990 if (info->Instructions.empty() || Epilog.empty())
1991 return -1;
1992
1993 // Check that the epilog actually is at the very end of the function,
1994 // otherwise it can't be packed.
1995 std::optional<int64_t> MaybeDistance = GetOptionalAbsDifference(
1996 Streamer&: streamer, LHS: info->FuncletOrFuncEnd, RHS: info->EpilogMap.begin()->first);
1997 if (!MaybeDistance)
1998 return -1;
1999 uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance;
2000 uint32_t InstructionBytes = ARMCountOfInstructionBytes(Insns: Epilog);
2001 if (DistanceFromEnd != InstructionBytes)
2002 return -1;
2003
2004 int RetVal = -1;
2005 // Even if we don't end up sharing opcodes with the prolog, we can still
2006 // write the offset as a packed offset, if the single epilog is located at
2007 // the end of the function and the offset (pointing after the prolog) fits
2008 // as a packed offset.
2009 if (PrologCodeBytes <= 31 &&
2010 PrologCodeBytes + ARMCountOfUnwindCodes(Insns: Epilog) <= 63)
2011 RetVal = PrologCodeBytes;
2012
2013 int Offset =
2014 getARMOffsetInProlog(Prolog: info->Instructions, Epilog, /*CanTweakProlog=*/true);
2015 if (Offset < 0)
2016 return RetVal;
2017
2018 // Check that the offset and prolog size fits in the first word; it's
2019 // unclear whether the epilog count in the extension word can be taken
2020 // as packed epilog offset.
2021 if (Offset > 31 || PrologCodeBytes > 63)
2022 return RetVal;
2023
2024 // Replace the regular end opcode of the prolog with the one from the
2025 // epilog.
2026 info->Instructions.front() = Epilog.back();
2027
2028 // As we choose to express the epilog as part of the prolog, remove the
2029 // epilog from the map, so we don't try to emit its opcodes.
2030 info->EpilogMap.clear();
2031 return Offset;
2032}
2033
2034static bool parseRegMask(unsigned Mask, bool &HasLR, bool &HasR11,
2035 unsigned &Folded, int &IntRegs) {
2036 if (Mask & (1 << 14)) {
2037 HasLR = true;
2038 Mask &= ~(1 << 14);
2039 }
2040 if (Mask & (1 << 11)) {
2041 HasR11 = true;
2042 Mask &= ~(1 << 11);
2043 }
2044 Folded = 0;
2045 IntRegs = -1;
2046 if (!Mask)
2047 return true;
2048 int First = 0;
2049 // Shift right until we have the bits at the bottom
2050 while ((Mask & 1) == 0) {
2051 First++;
2052 Mask >>= 1;
2053 }
2054 if ((Mask & (Mask + 1)) != 0)
2055 return false; // Not a consecutive series of bits? Can't be packed.
2056 // Count the bits
2057 int N = 0;
2058 while (Mask & (1 << N))
2059 N++;
2060 if (First < 4) {
2061 if (First + N < 4)
2062 return false;
2063 Folded = 4 - First;
2064 N -= Folded;
2065 First = 4;
2066 }
2067 if (First > 4)
2068 return false; // Can't be packed
2069 if (N >= 1)
2070 IntRegs = N - 1;
2071 return true;
2072}
2073
2074static bool tryARMPackedUnwind(MCStreamer &streamer, WinEH::FrameInfo *info,
2075 uint32_t FuncLength) {
2076 int Step = 0;
2077 bool Homing = false;
2078 bool HasR11 = false;
2079 bool HasChain = false;
2080 bool HasLR = false;
2081 int IntRegs = -1; // r4 - r(4+N)
2082 int FloatRegs = -1; // d8 - d(8+N)
2083 unsigned PF = 0; // Number of extra pushed registers
2084 unsigned StackAdjust = 0;
2085 // Iterate over the prolog and check that all opcodes exactly match
2086 // the canonical order and form.
2087 for (const WinEH::Instruction &Inst : info->Instructions) {
2088 switch (Inst.Operation) {
2089 default:
2090 llvm_unreachable("Unsupported ARM unwind code");
2091 case Win64EH::UOP_Custom:
2092 case Win64EH::UOP_AllocLarge:
2093 case Win64EH::UOP_AllocHuge:
2094 case Win64EH::UOP_WideAllocLarge:
2095 case Win64EH::UOP_WideAllocHuge:
2096 case Win64EH::UOP_SaveFRegD0D15:
2097 case Win64EH::UOP_SaveFRegD16D31:
2098 // Can't be packed
2099 return false;
2100 case Win64EH::UOP_SaveSP:
2101 // Can't be packed; we can't rely on restoring sp from r11 when
2102 // unwinding a packed prologue.
2103 return false;
2104 case Win64EH::UOP_SaveLR:
2105 // Can't be present in a packed prologue
2106 return false;
2107
2108 case Win64EH::UOP_End:
2109 case Win64EH::UOP_EndNop:
2110 case Win64EH::UOP_WideEndNop:
2111 if (Step != 0)
2112 return false;
2113 Step = 1;
2114 break;
2115
2116 case Win64EH::UOP_SaveRegsR4R7LR:
2117 case Win64EH::UOP_WideSaveRegsR4R11LR:
2118 // push {r4-r11,lr}
2119 if (Step != 1 && Step != 2)
2120 return false;
2121 assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX
2122 assert(Inst.Offset <= 1); // Lr
2123 IntRegs = Inst.Register - 4;
2124 if (Inst.Register == 11) {
2125 HasR11 = true;
2126 IntRegs--;
2127 }
2128 if (Inst.Offset)
2129 HasLR = true;
2130 Step = 3;
2131 break;
2132
2133 case Win64EH::UOP_SaveRegMask:
2134 if (Step == 1 && Inst.Register == 0x0f) {
2135 // push {r0-r3}
2136 Homing = true;
2137 Step = 2;
2138 break;
2139 }
2140 [[fallthrough]];
2141 case Win64EH::UOP_WideSaveRegMask:
2142 if (Step != 1 && Step != 2)
2143 return false;
2144 // push {r4-r9,r11,lr}
2145 // push {r11,lr}
2146 // push {r1-r5}
2147 if (!parseRegMask(Mask: Inst.Register, HasLR, HasR11, Folded&: PF, IntRegs))
2148 return false;
2149 Step = 3;
2150 break;
2151
2152 case Win64EH::UOP_Nop:
2153 // mov r11, sp
2154 if (Step != 3 || !HasR11 || IntRegs >= 0 || PF > 0)
2155 return false;
2156 HasChain = true;
2157 Step = 4;
2158 break;
2159 case Win64EH::UOP_WideNop:
2160 // add.w r11, sp, #xx
2161 if (Step != 3 || !HasR11 || (IntRegs < 0 && PF == 0))
2162 return false;
2163 HasChain = true;
2164 Step = 4;
2165 break;
2166
2167 case Win64EH::UOP_SaveFRegD8D15:
2168 if (Step != 1 && Step != 2 && Step != 3 && Step != 4)
2169 return false;
2170 assert(Inst.Register >= 8 && Inst.Register <= 15);
2171 if (Inst.Register == 15)
2172 return false; // Can't pack this case, R==7 means no IntRegs
2173 if (IntRegs >= 0)
2174 return false;
2175 FloatRegs = Inst.Register - 8;
2176 Step = 5;
2177 break;
2178
2179 case Win64EH::UOP_AllocSmall:
2180 case Win64EH::UOP_WideAllocMedium:
2181 if (Step != 1 && Step != 2 && Step != 3 && Step != 4 && Step != 5)
2182 return false;
2183 if (PF > 0) // Can't have both folded and explicit stack allocation
2184 return false;
2185 if (Inst.Offset / 4 >= 0x3f4)
2186 return false;
2187 StackAdjust = Inst.Offset / 4;
2188 Step = 6;
2189 break;
2190 }
2191 }
2192 if (HasR11 && !HasChain) {
2193 if (IntRegs + 4 == 10) {
2194 // r11 stored, but not chaining; can be packed if already saving r4-r10
2195 // and we can fit r11 into this range.
2196 IntRegs++;
2197 HasR11 = false;
2198 } else
2199 return false;
2200 }
2201 if (HasChain && !HasLR)
2202 return false;
2203
2204 // Packed uneind info can't express multiple epilogues.
2205 if (info->EpilogMap.size() > 1)
2206 return false;
2207
2208 unsigned EF = 0;
2209 int Ret = 0;
2210 if (info->EpilogMap.size() == 0) {
2211 Ret = 3; // No epilogue
2212 } else {
2213 // As the prologue and epilogue aren't exact mirrors of each other,
2214 // we have to check the epilogue too and see if it matches what we've
2215 // concluded from the prologue.
2216 const WinEH::FrameInfo::Epilog &EpilogInfo =
2217 info->EpilogMap.begin()->second;
2218 if (EpilogInfo.Condition != 0xe) // ARMCC::AL
2219 return false;
2220 const std::vector<WinEH::Instruction> &Epilog = EpilogInfo.Instructions;
2221 std::optional<int64_t> MaybeDistance = GetOptionalAbsDifference(
2222 Streamer&: streamer, LHS: info->FuncletOrFuncEnd, RHS: info->EpilogMap.begin()->first);
2223 if (!MaybeDistance)
2224 return false;
2225 uint32_t DistanceFromEnd = (uint32_t)*MaybeDistance;
2226 uint32_t InstructionBytes = ARMCountOfInstructionBytes(Insns: Epilog);
2227 if (DistanceFromEnd != InstructionBytes)
2228 return false;
2229
2230 bool GotStackAdjust = false;
2231 bool GotFloatRegs = false;
2232 bool GotIntRegs = false;
2233 bool GotHomingRestore = false;
2234 bool GotLRRestore = false;
2235 bool NeedsReturn = false;
2236 bool GotReturn = false;
2237
2238 Step = 6;
2239 for (const WinEH::Instruction &Inst : Epilog) {
2240 switch (Inst.Operation) {
2241 default:
2242 llvm_unreachable("Unsupported ARM unwind code");
2243 case Win64EH::UOP_Custom:
2244 case Win64EH::UOP_AllocLarge:
2245 case Win64EH::UOP_AllocHuge:
2246 case Win64EH::UOP_WideAllocLarge:
2247 case Win64EH::UOP_WideAllocHuge:
2248 case Win64EH::UOP_SaveFRegD0D15:
2249 case Win64EH::UOP_SaveFRegD16D31:
2250 case Win64EH::UOP_SaveSP:
2251 case Win64EH::UOP_Nop:
2252 case Win64EH::UOP_WideNop:
2253 // Can't be packed in an epilogue
2254 return false;
2255
2256 case Win64EH::UOP_AllocSmall:
2257 case Win64EH::UOP_WideAllocMedium:
2258 if (Inst.Offset / 4 >= 0x3f4)
2259 return false;
2260 if (Step == 6) {
2261 if (Homing && FloatRegs < 0 && IntRegs < 0 && StackAdjust == 0 &&
2262 PF == 0 && Inst.Offset == 16) {
2263 GotHomingRestore = true;
2264 Step = 10;
2265 } else {
2266 if (StackAdjust > 0) {
2267 // Got stack adjust in prologue too; must match.
2268 if (StackAdjust != Inst.Offset / 4)
2269 return false;
2270 GotStackAdjust = true;
2271 } else if (PF == Inst.Offset / 4) {
2272 // Folded prologue, non-folded epilogue
2273 StackAdjust = Inst.Offset / 4;
2274 GotStackAdjust = true;
2275 } else {
2276 // StackAdjust == 0 in prologue, mismatch
2277 return false;
2278 }
2279 Step = 7;
2280 }
2281 } else if (Step == 7 || Step == 8 || Step == 9) {
2282 if (!Homing || Inst.Offset != 16)
2283 return false;
2284 GotHomingRestore = true;
2285 Step = 10;
2286 } else
2287 return false;
2288 break;
2289
2290 case Win64EH::UOP_SaveFRegD8D15:
2291 if (Step != 6 && Step != 7)
2292 return false;
2293 assert(Inst.Register >= 8 && Inst.Register <= 15);
2294 if (FloatRegs != (int)(Inst.Register - 8))
2295 return false;
2296 GotFloatRegs = true;
2297 Step = 8;
2298 break;
2299
2300 case Win64EH::UOP_SaveRegsR4R7LR:
2301 case Win64EH::UOP_WideSaveRegsR4R11LR: {
2302 // push {r4-r11,lr}
2303 if (Step != 6 && Step != 7 && Step != 8)
2304 return false;
2305 assert(Inst.Register >= 4 && Inst.Register <= 11); // r4-rX
2306 assert(Inst.Offset <= 1); // Lr
2307 if (Homing && HasLR) {
2308 // If homing and LR is backed up, we can either restore LR here
2309 // and return with Ret == 1 or 2, or return with SaveLR below
2310 if (Inst.Offset) {
2311 GotLRRestore = true;
2312 NeedsReturn = true;
2313 } else {
2314 // Expecting a separate SaveLR below
2315 }
2316 } else {
2317 if (HasLR != (Inst.Offset == 1))
2318 return false;
2319 }
2320 GotLRRestore = Inst.Offset == 1;
2321 if (IntRegs < 0) // This opcode must include r4
2322 return false;
2323 int Expected = IntRegs;
2324 if (HasChain) {
2325 // Can't express r11 here unless IntRegs describe r4-r10
2326 if (IntRegs != 6)
2327 return false;
2328 Expected++;
2329 }
2330 if (Expected != (int)(Inst.Register - 4))
2331 return false;
2332 GotIntRegs = true;
2333 Step = 9;
2334 break;
2335 }
2336
2337 case Win64EH::UOP_SaveRegMask:
2338 case Win64EH::UOP_WideSaveRegMask: {
2339 if (Step != 6 && Step != 7 && Step != 8)
2340 return false;
2341 // push {r4-r9,r11,lr}
2342 // push {r11,lr}
2343 // push {r1-r5}
2344 bool CurHasLR = false, CurHasR11 = false;
2345 int Regs;
2346 if (!parseRegMask(Mask: Inst.Register, HasLR&: CurHasLR, HasR11&: CurHasR11, Folded&: EF, IntRegs&: Regs))
2347 return false;
2348 if (EF > 0) {
2349 if (EF != PF && EF != StackAdjust)
2350 return false;
2351 }
2352 if (Homing && HasLR) {
2353 // If homing and LR is backed up, we can either restore LR here
2354 // and return with Ret == 1 or 2, or return with SaveLR below
2355 if (CurHasLR) {
2356 GotLRRestore = true;
2357 NeedsReturn = true;
2358 } else {
2359 // Expecting a separate SaveLR below
2360 }
2361 } else {
2362 if (CurHasLR != HasLR)
2363 return false;
2364 GotLRRestore = CurHasLR;
2365 }
2366 int Expected = IntRegs;
2367 if (HasChain) {
2368 // If we have chaining, the mask must have included r11.
2369 if (!CurHasR11)
2370 return false;
2371 } else if (Expected == 7) {
2372 // If we don't have chaining, the mask could still include r11,
2373 // expressed as part of IntRegs Instead.
2374 Expected--;
2375 if (!CurHasR11)
2376 return false;
2377 } else {
2378 // Neither HasChain nor r11 included in IntRegs, must not have r11
2379 // here either.
2380 if (CurHasR11)
2381 return false;
2382 }
2383 if (Expected != Regs)
2384 return false;
2385 GotIntRegs = true;
2386 Step = 9;
2387 break;
2388 }
2389
2390 case Win64EH::UOP_SaveLR:
2391 if (Step != 6 && Step != 7 && Step != 8 && Step != 9)
2392 return false;
2393 if (!Homing || Inst.Offset != 20 || GotLRRestore)
2394 return false;
2395 GotLRRestore = true;
2396 GotHomingRestore = true;
2397 Step = 10;
2398 break;
2399
2400 case Win64EH::UOP_EndNop:
2401 case Win64EH::UOP_WideEndNop:
2402 GotReturn = true;
2403 Ret = (Inst.Operation == Win64EH::UOP_EndNop) ? 1 : 2;
2404 [[fallthrough]];
2405 case Win64EH::UOP_End:
2406 if (Step != 6 && Step != 7 && Step != 8 && Step != 9 && Step != 10)
2407 return false;
2408 Step = 11;
2409 break;
2410 }
2411 }
2412
2413 if (Step != 11)
2414 return false;
2415 if (StackAdjust > 0 && !GotStackAdjust && EF == 0)
2416 return false;
2417 if (FloatRegs >= 0 && !GotFloatRegs)
2418 return false;
2419 if (IntRegs >= 0 && !GotIntRegs)
2420 return false;
2421 if (Homing && !GotHomingRestore)
2422 return false;
2423 if (HasLR && !GotLRRestore)
2424 return false;
2425 if (NeedsReturn && !GotReturn)
2426 return false;
2427 }
2428
2429 assert(PF == 0 || EF == 0 ||
2430 StackAdjust == 0); // Can't have adjust in all three
2431 if (PF > 0 || EF > 0) {
2432 StackAdjust = PF > 0 ? (PF - 1) : (EF - 1);
2433 assert(StackAdjust <= 3);
2434 StackAdjust |= 0x3f0;
2435 if (PF > 0)
2436 StackAdjust |= 1 << 2;
2437 if (EF > 0)
2438 StackAdjust |= 1 << 3;
2439 }
2440
2441 assert(FuncLength <= 0x7FF && "FuncLength should have been checked earlier");
2442 int Flag = info->Fragment ? 0x02 : 0x01;
2443 int H = Homing ? 1 : 0;
2444 int L = HasLR ? 1 : 0;
2445 int C = HasChain ? 1 : 0;
2446 assert(IntRegs < 0 || FloatRegs < 0);
2447 unsigned Reg, R;
2448 if (IntRegs >= 0) {
2449 Reg = IntRegs;
2450 assert(Reg <= 7);
2451 R = 0;
2452 } else if (FloatRegs >= 0) {
2453 Reg = FloatRegs;
2454 assert(Reg < 7);
2455 R = 1;
2456 } else {
2457 // No int or float regs stored (except possibly R11,LR)
2458 Reg = 7;
2459 R = 1;
2460 }
2461 info->PackedInfo |= Flag << 0;
2462 info->PackedInfo |= (FuncLength & 0x7FF) << 2;
2463 info->PackedInfo |= (Ret & 0x3) << 13;
2464 info->PackedInfo |= H << 15;
2465 info->PackedInfo |= Reg << 16;
2466 info->PackedInfo |= R << 19;
2467 info->PackedInfo |= L << 20;
2468 info->PackedInfo |= C << 21;
2469 assert(StackAdjust <= 0x3ff);
2470 info->PackedInfo |= StackAdjust << 22;
2471 return true;
2472}
2473
2474// Populate the .xdata section. The format of .xdata on ARM is documented at
2475// https://docs.microsoft.com/en-us/cpp/build/arm-exception-handling
2476static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info,
2477 bool TryPacked = true) {
2478 // If this UNWIND_INFO already has a symbol, it's already been emitted.
2479 if (info->Symbol)
2480 return;
2481 // If there's no unwind info here (not even a terminating UOP_End), the
2482 // unwind info is considered bogus and skipped. If this was done in
2483 // response to an explicit .seh_handlerdata, the associated trailing
2484 // handler data is left orphaned in the xdata section.
2485 if (info->empty()) {
2486 info->EmitAttempted = true;
2487 return;
2488 }
2489 if (info->EmitAttempted) {
2490 // If we tried to emit unwind info before (due to an explicit
2491 // .seh_handlerdata directive), but skipped it (because there was no
2492 // valid information to emit at the time), and it later got valid unwind
2493 // opcodes, we can't emit it here, because the trailing handler data
2494 // was already emitted elsewhere in the xdata section.
2495 streamer.getContext().reportError(
2496 L: SMLoc(), Msg: "Earlier .seh_handlerdata for " + info->Function->getName() +
2497 " skipped due to no unwind info at the time "
2498 "(.seh_handlerdata too early?), but the function later "
2499 "did get unwind info that can't be emitted");
2500 return;
2501 }
2502
2503 MCContext &context = streamer.getContext();
2504 MCSymbol *Label = context.createTempSymbol();
2505
2506 streamer.emitValueToAlignment(Alignment: Align(4));
2507 streamer.emitLabel(Symbol: Label);
2508 info->Symbol = Label;
2509
2510 if (!info->PrologEnd)
2511 streamer.getContext().reportError(L: SMLoc(), Msg: "Prologue in " +
2512 info->Function->getName() +
2513 " not correctly terminated");
2514
2515 if (info->PrologEnd && !info->Fragment)
2516 checkARMInstructions(Streamer&: streamer, Insns: info->Instructions, Begin: info->Begin,
2517 End: info->PrologEnd, Name: info->Function->getName(),
2518 Type: "prologue");
2519 for (auto &I : info->EpilogMap) {
2520 MCSymbol *EpilogStart = I.first;
2521 auto &Epilog = I.second;
2522 checkARMInstructions(Streamer&: streamer, Insns: Epilog.Instructions, Begin: EpilogStart, End: Epilog.End,
2523 Name: info->Function->getName(), Type: "epilogue");
2524 if (Epilog.Instructions.empty() ||
2525 !isARMTerminator(inst: Epilog.Instructions.back()))
2526 streamer.getContext().reportError(
2527 L: SMLoc(), Msg: "Epilogue in " + info->Function->getName() +
2528 " not correctly terminated");
2529 }
2530
2531 std::optional<int64_t> RawFuncLength;
2532 const MCExpr *FuncLengthExpr = nullptr;
2533 if (!info->FuncletOrFuncEnd) {
2534 report_fatal_error(reason: "FuncletOrFuncEnd not set");
2535 } else {
2536 // As the size of many thumb2 instructions isn't known until later,
2537 // we can't always rely on being able to calculate the absolute
2538 // length of the function here. If we can't calculate it, defer it
2539 // to a relocation.
2540 //
2541 // In such a case, we won't know if the function is too long so that
2542 // the unwind info would need to be split (but this isn't implemented
2543 // anyway).
2544 RawFuncLength =
2545 GetOptionalAbsDifference(Streamer&: streamer, LHS: info->FuncletOrFuncEnd, RHS: info->Begin);
2546 if (!RawFuncLength)
2547 FuncLengthExpr =
2548 GetSubDivExpr(Streamer&: streamer, LHS: info->FuncletOrFuncEnd, RHS: info->Begin, Div: 2);
2549 }
2550 uint32_t FuncLength = 0;
2551 if (RawFuncLength)
2552 FuncLength = (uint32_t)*RawFuncLength / 2;
2553 if (FuncLength > 0x3FFFF)
2554 report_fatal_error(reason: "SEH unwind data splitting not yet implemented");
2555 uint32_t PrologCodeBytes = ARMCountOfUnwindCodes(Insns: info->Instructions);
2556 uint32_t TotalCodeBytes = PrologCodeBytes;
2557
2558 if (!info->HandlesExceptions && RawFuncLength && FuncLength <= 0x7ff &&
2559 TryPacked) {
2560 // No exception handlers; check if the prolog and epilog matches the
2561 // patterns that can be described by the packed format. If we don't
2562 // know the exact function length yet, we can't do this.
2563
2564 // info->Symbol was already set even if we didn't actually write any
2565 // unwind info there. Keep using that as indicator that this unwind
2566 // info has been generated already.
2567
2568 if (tryARMPackedUnwind(streamer, info, FuncLength))
2569 return;
2570 }
2571
2572 int PackedEpilogOffset =
2573 checkARMPackedEpilog(streamer, info, PrologCodeBytes);
2574
2575 // Process epilogs.
2576 MapVector<MCSymbol *, uint32_t> EpilogInfo;
2577 // Epilogs processed so far.
2578 std::vector<MCSymbol *> AddedEpilogs;
2579
2580 bool CanTweakProlog = true;
2581 for (auto &I : info->EpilogMap) {
2582 MCSymbol *EpilogStart = I.first;
2583 auto &EpilogInstrs = I.second.Instructions;
2584 uint32_t CodeBytes = ARMCountOfUnwindCodes(Insns: EpilogInstrs);
2585
2586 MCSymbol *MatchingEpilog =
2587 FindMatchingEpilog(EpilogInstrs, Epilogs: AddedEpilogs, info);
2588 int PrologOffset;
2589 if (MatchingEpilog) {
2590 assert(EpilogInfo.contains(MatchingEpilog) &&
2591 "Duplicate epilog not found");
2592 EpilogInfo[EpilogStart] = EpilogInfo.lookup(Key: MatchingEpilog);
2593 // Clear the unwind codes in the EpilogMap, so that they don't get output
2594 // in the logic below.
2595 EpilogInstrs.clear();
2596 } else if ((PrologOffset = getARMOffsetInProlog(
2597 Prolog: info->Instructions, Epilog: EpilogInstrs, CanTweakProlog)) >= 0) {
2598 if (CanTweakProlog) {
2599 // Replace the regular end opcode of the prolog with the one from the
2600 // epilog.
2601 info->Instructions.front() = EpilogInstrs.back();
2602 // Later epilogs need a strict match for the end opcode.
2603 CanTweakProlog = false;
2604 }
2605 EpilogInfo[EpilogStart] = PrologOffset;
2606 // Clear the unwind codes in the EpilogMap, so that they don't get output
2607 // in the logic below.
2608 EpilogInstrs.clear();
2609 } else {
2610 EpilogInfo[EpilogStart] = TotalCodeBytes;
2611 TotalCodeBytes += CodeBytes;
2612 AddedEpilogs.push_back(x: EpilogStart);
2613 }
2614 }
2615
2616 // Code Words, Epilog count, F, E, X, Vers, Function Length
2617 uint32_t row1 = 0x0;
2618 uint32_t CodeWords = TotalCodeBytes / 4;
2619 uint32_t CodeWordsMod = TotalCodeBytes % 4;
2620 if (CodeWordsMod)
2621 CodeWords++;
2622 uint32_t EpilogCount =
2623 PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size();
2624 bool ExtensionWord = EpilogCount > 31 || CodeWords > 15;
2625 if (!ExtensionWord) {
2626 row1 |= (EpilogCount & 0x1F) << 23;
2627 row1 |= (CodeWords & 0x0F) << 28;
2628 }
2629 if (info->HandlesExceptions) // X
2630 row1 |= 1 << 20;
2631 if (PackedEpilogOffset >= 0) // E
2632 row1 |= 1 << 21;
2633 if (info->Fragment) // F
2634 row1 |= 1 << 22;
2635 row1 |= FuncLength & 0x3FFFF;
2636 if (RawFuncLength)
2637 streamer.emitInt32(Value: row1);
2638 else
2639 streamer.emitValue(
2640 Value: MCBinaryExpr::createOr(LHS: FuncLengthExpr,
2641 RHS: MCConstantExpr::create(Value: row1, Ctx&: context), Ctx&: context),
2642 Size: 4);
2643
2644 // Extended Code Words, Extended Epilog Count
2645 if (ExtensionWord) {
2646 // FIXME: We should be able to split unwind info into multiple sections.
2647 if (CodeWords > 0xFF || EpilogCount > 0xFFFF)
2648 report_fatal_error(reason: "SEH unwind data splitting not yet implemented");
2649 uint32_t row2 = 0x0;
2650 row2 |= (CodeWords & 0xFF) << 16;
2651 row2 |= (EpilogCount & 0xFFFF);
2652 streamer.emitInt32(Value: row2);
2653 }
2654
2655 if (PackedEpilogOffset < 0) {
2656 // Epilog Start Index, Epilog Start Offset
2657 for (auto &I : EpilogInfo) {
2658 MCSymbol *EpilogStart = I.first;
2659 uint32_t EpilogIndex = I.second;
2660
2661 std::optional<int64_t> MaybeEpilogOffset =
2662 GetOptionalAbsDifference(Streamer&: streamer, LHS: EpilogStart, RHS: info->Begin);
2663 const MCExpr *OffsetExpr = nullptr;
2664 uint32_t EpilogOffset = 0;
2665 if (MaybeEpilogOffset)
2666 EpilogOffset = *MaybeEpilogOffset / 2;
2667 else
2668 OffsetExpr = GetSubDivExpr(Streamer&: streamer, LHS: EpilogStart, RHS: info->Begin, Div: 2);
2669
2670 assert(info->EpilogMap.contains(EpilogStart));
2671 unsigned Condition = info->EpilogMap[EpilogStart].Condition;
2672 assert(Condition <= 0xf);
2673
2674 uint32_t row3 = EpilogOffset;
2675 row3 |= Condition << 20;
2676 row3 |= (EpilogIndex & 0x3FF) << 24;
2677 if (MaybeEpilogOffset)
2678 streamer.emitInt32(Value: row3);
2679 else
2680 streamer.emitValue(
2681 Value: MCBinaryExpr::createOr(
2682 LHS: OffsetExpr, RHS: MCConstantExpr::create(Value: row3, Ctx&: context), Ctx&: context),
2683 Size: 4);
2684 }
2685 }
2686
2687 // Emit prolog unwind instructions (in reverse order).
2688 uint8_t numInst = info->Instructions.size();
2689 for (uint8_t c = 0; c < numInst; ++c) {
2690 WinEH::Instruction inst = info->Instructions.back();
2691 info->Instructions.pop_back();
2692 ARMEmitUnwindCode(streamer, inst);
2693 }
2694
2695 // Emit epilog unwind instructions
2696 for (auto &I : info->EpilogMap) {
2697 auto &EpilogInstrs = I.second.Instructions;
2698 for (const WinEH::Instruction &inst : EpilogInstrs)
2699 ARMEmitUnwindCode(streamer, inst);
2700 }
2701
2702 int32_t BytesMod = CodeWords * 4 - TotalCodeBytes;
2703 assert(BytesMod >= 0);
2704 for (int i = 0; i < BytesMod; i++)
2705 streamer.emitInt8(Value: 0xFB);
2706
2707 if (info->HandlesExceptions)
2708 streamer.emitValue(
2709 Value: MCSymbolRefExpr::create(Symbol: info->ExceptionHandler,
2710 specifier: MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx&: context),
2711 Size: 4);
2712}
2713
2714static void ARM64EmitRuntimeFunction(MCStreamer &streamer,
2715 const WinEH::FrameInfo *info) {
2716 MCContext &context = streamer.getContext();
2717
2718 streamer.emitValueToAlignment(Alignment: Align(4));
2719 for (const auto &S : info->Segments) {
2720 EmitSymbolRefWithOfs(streamer, Base: info->Begin, Offset: S.Offset);
2721 if (info->PackedInfo)
2722 streamer.emitInt32(Value: info->PackedInfo);
2723 else
2724 streamer.emitValue(
2725 Value: MCSymbolRefExpr::create(Symbol: S.Symbol, specifier: MCSymbolRefExpr::VK_COFF_IMGREL32,
2726 Ctx&: context),
2727 Size: 4);
2728 }
2729}
2730
2731
2732static void ARMEmitRuntimeFunction(MCStreamer &streamer,
2733 const WinEH::FrameInfo *info) {
2734 MCContext &context = streamer.getContext();
2735
2736 streamer.emitValueToAlignment(Alignment: Align(4));
2737 EmitSymbolRefWithOfs(streamer, Base: info->Begin, Other: info->Begin);
2738 if (info->PackedInfo)
2739 streamer.emitInt32(Value: info->PackedInfo);
2740 else
2741 streamer.emitValue(
2742 Value: MCSymbolRefExpr::create(Symbol: info->Symbol, specifier: MCSymbolRefExpr::VK_COFF_IMGREL32,
2743 Ctx&: context),
2744 Size: 4);
2745}
2746
2747void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const {
2748 // Emit the unwind info structs first.
2749 for (const auto &CFI : Streamer.getWinFrameInfos()) {
2750 WinEH::FrameInfo *Info = CFI.get();
2751 if (Info->empty())
2752 continue;
2753 MCSection *XData = Streamer.getAssociatedXDataSection(TextSec: CFI->TextSection);
2754 Streamer.switchSection(Section: XData);
2755 ARM64EmitUnwindInfo(streamer&: Streamer, info: Info);
2756 }
2757
2758 // Now emit RUNTIME_FUNCTION entries.
2759 for (const auto &CFI : Streamer.getWinFrameInfos()) {
2760 WinEH::FrameInfo *Info = CFI.get();
2761 // ARM64EmitUnwindInfo above clears the info struct, so we can't check
2762 // empty here. But if a Symbol is set, we should create the corresponding
2763 // pdata entry.
2764 if (!Info->Symbol)
2765 continue;
2766 MCSection *PData = Streamer.getAssociatedPDataSection(TextSec: CFI->TextSection);
2767 Streamer.switchSection(Section: PData);
2768 ARM64EmitRuntimeFunction(streamer&: Streamer, info: Info);
2769 }
2770}
2771
2772void llvm::Win64EH::ARM64UnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
2773 WinEH::FrameInfo *info,
2774 bool HandlerData) const {
2775 // Called if there's an .seh_handlerdata directive before the end of the
2776 // function. This forces writing the xdata record already here - and
2777 // in this case, the function isn't actually ended already, but the xdata
2778 // record needs to know the function length. In these cases, if the funclet
2779 // end hasn't been marked yet, the xdata function length won't cover the
2780 // whole function, only up to this point.
2781 if (!info->FuncletOrFuncEnd) {
2782 Streamer.switchSection(Section: info->TextSection);
2783 info->FuncletOrFuncEnd = Streamer.emitCFILabel();
2784 }
2785 // Switch sections (the static function above is meant to be called from
2786 // here and from Emit().
2787 MCSection *XData = Streamer.getAssociatedXDataSection(TextSec: info->TextSection);
2788 Streamer.switchSection(Section: XData);
2789 ARM64EmitUnwindInfo(streamer&: Streamer, info, /* TryPacked = */ !HandlerData);
2790}
2791
2792void llvm::Win64EH::ARMUnwindEmitter::Emit(MCStreamer &Streamer) const {
2793 // Emit the unwind info structs first.
2794 for (const auto &CFI : Streamer.getWinFrameInfos()) {
2795 WinEH::FrameInfo *Info = CFI.get();
2796 if (Info->empty())
2797 continue;
2798 MCSection *XData = Streamer.getAssociatedXDataSection(TextSec: CFI->TextSection);
2799 Streamer.switchSection(Section: XData);
2800 ARMEmitUnwindInfo(streamer&: Streamer, info: Info);
2801 }
2802
2803 // Now emit RUNTIME_FUNCTION entries.
2804 for (const auto &CFI : Streamer.getWinFrameInfos()) {
2805 WinEH::FrameInfo *Info = CFI.get();
2806 // ARMEmitUnwindInfo above clears the info struct, so we can't check
2807 // empty here. But if a Symbol is set, we should create the corresponding
2808 // pdata entry.
2809 if (!Info->Symbol)
2810 continue;
2811 MCSection *PData = Streamer.getAssociatedPDataSection(TextSec: CFI->TextSection);
2812 Streamer.switchSection(Section: PData);
2813 ARMEmitRuntimeFunction(streamer&: Streamer, info: Info);
2814 }
2815}
2816
2817void llvm::Win64EH::ARMUnwindEmitter::EmitUnwindInfo(MCStreamer &Streamer,
2818 WinEH::FrameInfo *info,
2819 bool HandlerData) const {
2820 // Called if there's an .seh_handlerdata directive before the end of the
2821 // function. This forces writing the xdata record already here - and
2822 // in this case, the function isn't actually ended already, but the xdata
2823 // record needs to know the function length. In these cases, if the funclet
2824 // end hasn't been marked yet, the xdata function length won't cover the
2825 // whole function, only up to this point.
2826 if (!info->FuncletOrFuncEnd) {
2827 Streamer.switchSection(Section: info->TextSection);
2828 info->FuncletOrFuncEnd = Streamer.emitCFILabel();
2829 }
2830 // Switch sections (the static function above is meant to be called from
2831 // here and from Emit().
2832 MCSection *XData = Streamer.getAssociatedXDataSection(TextSec: info->TextSection);
2833 Streamer.switchSection(Section: XData);
2834 ARMEmitUnwindInfo(streamer&: Streamer, info, /* TryPacked = */ !HandlerData);
2835}
2836