1 | //===-- CodeGen/AsmPrinter/WinException.cpp - Dwarf Exception Impl ------===// |
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 | // This file contains support for writing Win64 exception info into asm files. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "WinException.h" |
14 | #include "llvm/ADT/Twine.h" |
15 | #include "llvm/BinaryFormat/COFF.h" |
16 | #include "llvm/BinaryFormat/Dwarf.h" |
17 | #include "llvm/CodeGen/AsmPrinter.h" |
18 | #include "llvm/CodeGen/MachineFrameInfo.h" |
19 | #include "llvm/CodeGen/MachineFunction.h" |
20 | #include "llvm/CodeGen/MachineModuleInfo.h" |
21 | #include "llvm/CodeGen/TargetFrameLowering.h" |
22 | #include "llvm/CodeGen/TargetLowering.h" |
23 | #include "llvm/CodeGen/TargetSubtargetInfo.h" |
24 | #include "llvm/CodeGen/WinEHFuncInfo.h" |
25 | #include "llvm/IR/DataLayout.h" |
26 | #include "llvm/IR/Module.h" |
27 | #include "llvm/MC/MCAsmInfo.h" |
28 | #include "llvm/MC/MCContext.h" |
29 | #include "llvm/MC/MCExpr.h" |
30 | #include "llvm/MC/MCStreamer.h" |
31 | #include "llvm/Target/TargetLoweringObjectFile.h" |
32 | #include "llvm/Target/TargetMachine.h" |
33 | using namespace llvm; |
34 | |
35 | WinException::WinException(AsmPrinter *A) : EHStreamer(A) { |
36 | // MSVC's EH tables are always composed of 32-bit words. All known 64-bit |
37 | // platforms use an imagerel32 relocation to refer to symbols. |
38 | useImageRel32 = (A->getDataLayout().getPointerSizeInBits() == 64); |
39 | isAArch64 = Asm->TM.getTargetTriple().isAArch64(); |
40 | isThumb = Asm->TM.getTargetTriple().isThumb(); |
41 | } |
42 | |
43 | WinException::~WinException() = default; |
44 | |
45 | /// endModule - Emit all exception information that should come after the |
46 | /// content. |
47 | void WinException::endModule() { |
48 | auto &OS = *Asm->OutStreamer; |
49 | const Module *M = MMI->getModule(); |
50 | for (const Function &F : *M) |
51 | if (F.hasFnAttribute(Kind: "safeseh" )) |
52 | OS.emitCOFFSafeSEH(Symbol: Asm->getSymbol(GV: &F)); |
53 | |
54 | if (M->getModuleFlag(Key: "ehcontguard" ) && !EHContTargets.empty()) { |
55 | // Emit the symbol index of each ehcont target. |
56 | OS.switchSection(Section: Asm->OutContext.getObjectFileInfo()->getGEHContSection()); |
57 | for (const MCSymbol *S : EHContTargets) { |
58 | OS.emitCOFFSymbolIndex(Symbol: S); |
59 | } |
60 | } |
61 | } |
62 | |
63 | void WinException::beginFunction(const MachineFunction *MF) { |
64 | shouldEmitMoves = shouldEmitPersonality = shouldEmitLSDA = false; |
65 | |
66 | // If any landing pads survive, we need an EH table. |
67 | bool hasLandingPads = !MF->getLandingPads().empty(); |
68 | bool hasEHFunclets = MF->hasEHFunclets(); |
69 | |
70 | const Function &F = MF->getFunction(); |
71 | |
72 | shouldEmitMoves = Asm->needsSEHMoves() && MF->hasWinCFI(); |
73 | |
74 | const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering(); |
75 | unsigned PerEncoding = TLOF.getPersonalityEncoding(); |
76 | |
77 | EHPersonality Per = EHPersonality::Unknown; |
78 | const Function *PerFn = nullptr; |
79 | if (F.hasPersonalityFn()) { |
80 | PerFn = dyn_cast<Function>(Val: F.getPersonalityFn()->stripPointerCasts()); |
81 | Per = classifyEHPersonality(Pers: PerFn); |
82 | } |
83 | |
84 | bool forceEmitPersonality = F.hasPersonalityFn() && |
85 | !isNoOpWithoutInvoke(Pers: Per) && |
86 | F.needsUnwindTableEntry(); |
87 | |
88 | shouldEmitPersonality = |
89 | forceEmitPersonality || ((hasLandingPads || hasEHFunclets) && |
90 | PerEncoding != dwarf::DW_EH_PE_omit && PerFn); |
91 | |
92 | unsigned LSDAEncoding = TLOF.getLSDAEncoding(); |
93 | shouldEmitLSDA = shouldEmitPersonality && |
94 | LSDAEncoding != dwarf::DW_EH_PE_omit; |
95 | |
96 | // If we're not using CFI, we don't want the CFI or the personality, but we |
97 | // might want EH tables if we had EH pads. |
98 | if (!Asm->MAI->usesWindowsCFI()) { |
99 | if (Per == EHPersonality::MSVC_X86SEH && !hasEHFunclets) { |
100 | // If this is 32-bit SEH and we don't have any funclets (really invokes), |
101 | // make sure we emit the parent offset label. Some unreferenced filter |
102 | // functions may still refer to it. |
103 | const WinEHFuncInfo &FuncInfo = *MF->getWinEHFuncInfo(); |
104 | StringRef FLinkageName = |
105 | GlobalValue::dropLLVMManglingEscape(Name: MF->getFunction().getName()); |
106 | emitEHRegistrationOffsetLabel(FuncInfo, FLinkageName); |
107 | } |
108 | shouldEmitLSDA = hasEHFunclets; |
109 | shouldEmitPersonality = false; |
110 | return; |
111 | } |
112 | |
113 | beginFunclet(MBB: MF->front(), Sym: Asm->CurrentFnSym); |
114 | } |
115 | |
116 | void WinException::markFunctionEnd() { |
117 | if (isAArch64 && CurrentFuncletEntry && |
118 | (shouldEmitMoves || shouldEmitPersonality)) |
119 | Asm->OutStreamer->emitWinCFIFuncletOrFuncEnd(); |
120 | } |
121 | |
122 | /// endFunction - Gather and emit post-function exception information. |
123 | /// |
124 | void WinException::endFunction(const MachineFunction *MF) { |
125 | if (!shouldEmitPersonality && !shouldEmitMoves && !shouldEmitLSDA) |
126 | return; |
127 | |
128 | const Function &F = MF->getFunction(); |
129 | EHPersonality Per = EHPersonality::Unknown; |
130 | if (F.hasPersonalityFn()) |
131 | Per = classifyEHPersonality(Pers: F.getPersonalityFn()->stripPointerCasts()); |
132 | |
133 | endFuncletImpl(); |
134 | |
135 | // endFunclet will emit the necessary .xdata tables for table-based SEH. |
136 | if (Per == EHPersonality::MSVC_TableSEH && MF->hasEHFunclets()) |
137 | return; |
138 | |
139 | if (shouldEmitPersonality || shouldEmitLSDA) { |
140 | Asm->OutStreamer->pushSection(); |
141 | |
142 | // Just switch sections to the right xdata section. |
143 | MCSection *XData = Asm->OutStreamer->getAssociatedXDataSection( |
144 | TextSec: Asm->OutStreamer->getCurrentSectionOnly()); |
145 | Asm->OutStreamer->switchSection(Section: XData); |
146 | |
147 | // Emit the tables appropriate to the personality function in use. If we |
148 | // don't recognize the personality, assume it uses an Itanium-style LSDA. |
149 | if (Per == EHPersonality::MSVC_TableSEH) |
150 | emitCSpecificHandlerTable(MF); |
151 | else if (Per == EHPersonality::MSVC_X86SEH) |
152 | emitExceptHandlerTable(MF); |
153 | else if (Per == EHPersonality::MSVC_CXX) |
154 | emitCXXFrameHandler3Table(MF); |
155 | else if (Per == EHPersonality::CoreCLR) |
156 | emitCLRExceptionTable(MF); |
157 | else |
158 | emitExceptionTable(); |
159 | |
160 | Asm->OutStreamer->popSection(); |
161 | } |
162 | |
163 | if (!MF->getCatchretTargets().empty()) { |
164 | // Copy the function's catchret targets to a module-level list. |
165 | EHContTargets.insert(position: EHContTargets.end(), first: MF->getCatchretTargets().begin(), |
166 | last: MF->getCatchretTargets().end()); |
167 | } |
168 | } |
169 | |
170 | /// Retrieve the MCSymbol for a GlobalValue or MachineBasicBlock. |
171 | static MCSymbol *getMCSymbolForMBB(AsmPrinter *Asm, |
172 | const MachineBasicBlock *MBB) { |
173 | if (!MBB) |
174 | return nullptr; |
175 | |
176 | assert(MBB->isEHFuncletEntry()); |
177 | |
178 | // Give catches and cleanups a name based off of their parent function and |
179 | // their funclet entry block's number. |
180 | const MachineFunction *MF = MBB->getParent(); |
181 | const Function &F = MF->getFunction(); |
182 | StringRef FuncLinkageName = GlobalValue::dropLLVMManglingEscape(Name: F.getName()); |
183 | MCContext &Ctx = MF->getContext(); |
184 | StringRef HandlerPrefix = MBB->isCleanupFuncletEntry() ? "dtor" : "catch" ; |
185 | return Ctx.getOrCreateSymbol(Name: "?" + HandlerPrefix + "$" + |
186 | Twine(MBB->getNumber()) + "@?0?" + |
187 | FuncLinkageName + "@4HA" ); |
188 | } |
189 | |
190 | void WinException::beginFunclet(const MachineBasicBlock &MBB, |
191 | MCSymbol *Sym) { |
192 | CurrentFuncletEntry = &MBB; |
193 | |
194 | const Function &F = Asm->MF->getFunction(); |
195 | // If a symbol was not provided for the funclet, invent one. |
196 | if (!Sym) { |
197 | Sym = getMCSymbolForMBB(Asm, MBB: &MBB); |
198 | |
199 | // Describe our funclet symbol as a function with internal linkage. |
200 | Asm->OutStreamer->beginCOFFSymbolDef(Symbol: Sym); |
201 | Asm->OutStreamer->emitCOFFSymbolStorageClass(StorageClass: COFF::IMAGE_SYM_CLASS_STATIC); |
202 | Asm->OutStreamer->emitCOFFSymbolType(Type: COFF::IMAGE_SYM_DTYPE_FUNCTION |
203 | << COFF::SCT_COMPLEX_TYPE_SHIFT); |
204 | Asm->OutStreamer->endCOFFSymbolDef(); |
205 | |
206 | // We want our funclet's entry point to be aligned such that no nops will be |
207 | // present after the label. |
208 | Asm->emitAlignment(Alignment: std::max(a: Asm->MF->getAlignment(), b: MBB.getAlignment()), |
209 | GV: &F); |
210 | |
211 | // Now that we've emitted the alignment directive, point at our funclet. |
212 | Asm->OutStreamer->emitLabel(Symbol: Sym); |
213 | } |
214 | |
215 | // Mark 'Sym' as starting our funclet. |
216 | if (shouldEmitMoves || shouldEmitPersonality) { |
217 | CurrentFuncletTextSection = Asm->OutStreamer->getCurrentSectionOnly(); |
218 | Asm->OutStreamer->emitWinCFIStartProc(Symbol: Sym); |
219 | } |
220 | |
221 | if (shouldEmitPersonality) { |
222 | const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering(); |
223 | const Function *PerFn = nullptr; |
224 | |
225 | // Determine which personality routine we are using for this funclet. |
226 | if (F.hasPersonalityFn()) |
227 | PerFn = dyn_cast<Function>(Val: F.getPersonalityFn()->stripPointerCasts()); |
228 | const MCSymbol *PersHandlerSym = |
229 | TLOF.getCFIPersonalitySymbol(GV: PerFn, TM: Asm->TM, MMI); |
230 | |
231 | // Do not emit a .seh_handler directives for cleanup funclets. |
232 | // FIXME: This means cleanup funclets cannot handle exceptions. Given that |
233 | // Clang doesn't produce EH constructs inside cleanup funclets and LLVM's |
234 | // inliner doesn't allow inlining them, this isn't a major problem in |
235 | // practice. |
236 | if (!CurrentFuncletEntry->isCleanupFuncletEntry()) |
237 | Asm->OutStreamer->emitWinEHHandler(Sym: PersHandlerSym, Unwind: true, Except: true); |
238 | } |
239 | } |
240 | |
241 | void WinException::endFunclet() { |
242 | if (isAArch64 && CurrentFuncletEntry && |
243 | (shouldEmitMoves || shouldEmitPersonality)) { |
244 | Asm->OutStreamer->switchSection(Section: CurrentFuncletTextSection); |
245 | Asm->OutStreamer->emitWinCFIFuncletOrFuncEnd(); |
246 | } |
247 | endFuncletImpl(); |
248 | } |
249 | |
250 | void WinException::endFuncletImpl() { |
251 | // No funclet to process? Great, we have nothing to do. |
252 | if (!CurrentFuncletEntry) |
253 | return; |
254 | |
255 | const MachineFunction *MF = Asm->MF; |
256 | if (shouldEmitMoves || shouldEmitPersonality) { |
257 | const Function &F = MF->getFunction(); |
258 | EHPersonality Per = EHPersonality::Unknown; |
259 | if (F.hasPersonalityFn()) |
260 | Per = classifyEHPersonality(Pers: F.getPersonalityFn()->stripPointerCasts()); |
261 | |
262 | if (Per == EHPersonality::MSVC_CXX && shouldEmitPersonality && |
263 | !CurrentFuncletEntry->isCleanupFuncletEntry()) { |
264 | // Emit an UNWIND_INFO struct describing the prologue. |
265 | Asm->OutStreamer->emitWinEHHandlerData(); |
266 | |
267 | // If this is a C++ catch funclet (or the parent function), |
268 | // emit a reference to the LSDA for the parent function. |
269 | StringRef FuncLinkageName = GlobalValue::dropLLVMManglingEscape(Name: F.getName()); |
270 | MCSymbol *FuncInfoXData = Asm->OutContext.getOrCreateSymbol( |
271 | Name: Twine("$cppxdata$" , FuncLinkageName)); |
272 | Asm->OutStreamer->emitValue(Value: create32bitRef(Value: FuncInfoXData), Size: 4); |
273 | } else if (Per == EHPersonality::MSVC_TableSEH && MF->hasEHFunclets() && |
274 | !CurrentFuncletEntry->isEHFuncletEntry()) { |
275 | // Emit an UNWIND_INFO struct describing the prologue. |
276 | Asm->OutStreamer->emitWinEHHandlerData(); |
277 | |
278 | // If this is the parent function in Win64 SEH, emit the LSDA immediately |
279 | // following .seh_handlerdata. |
280 | emitCSpecificHandlerTable(MF); |
281 | } else if (shouldEmitPersonality || shouldEmitLSDA) { |
282 | // Emit an UNWIND_INFO struct describing the prologue. |
283 | Asm->OutStreamer->emitWinEHHandlerData(); |
284 | // In these cases, no further info is written to the .xdata section |
285 | // right here, but is written by e.g. emitExceptionTable in endFunction() |
286 | // above. |
287 | } else { |
288 | // No need to emit the EH handler data right here if nothing needs |
289 | // writing to the .xdata section; it will be emitted for all |
290 | // functions that need it in the end anyway. |
291 | } |
292 | |
293 | // Switch back to the funclet start .text section now that we are done |
294 | // writing to .xdata, and emit an .seh_endproc directive to mark the end of |
295 | // the function. |
296 | Asm->OutStreamer->switchSection(Section: CurrentFuncletTextSection); |
297 | Asm->OutStreamer->emitWinCFIEndProc(); |
298 | } |
299 | |
300 | // Let's make sure we don't try to end the same funclet twice. |
301 | CurrentFuncletEntry = nullptr; |
302 | } |
303 | |
304 | const MCExpr *WinException::create32bitRef(const MCSymbol *Value) { |
305 | if (!Value) |
306 | return MCConstantExpr::create(Value: 0, Ctx&: Asm->OutContext); |
307 | return MCSymbolRefExpr::create(Symbol: Value, Kind: useImageRel32 |
308 | ? MCSymbolRefExpr::VK_COFF_IMGREL32 |
309 | : MCSymbolRefExpr::VK_None, |
310 | Ctx&: Asm->OutContext); |
311 | } |
312 | |
313 | const MCExpr *WinException::create32bitRef(const GlobalValue *GV) { |
314 | if (!GV) |
315 | return MCConstantExpr::create(Value: 0, Ctx&: Asm->OutContext); |
316 | return create32bitRef(Value: Asm->getSymbol(GV)); |
317 | } |
318 | |
319 | const MCExpr *WinException::getLabel(const MCSymbol *Label) { |
320 | return MCSymbolRefExpr::create(Symbol: Label, Kind: MCSymbolRefExpr::VK_COFF_IMGREL32, |
321 | Ctx&: Asm->OutContext); |
322 | } |
323 | |
324 | const MCExpr *WinException::getLabelPlusOne(const MCSymbol *Label) { |
325 | return MCBinaryExpr::createAdd(LHS: getLabel(Label), |
326 | RHS: MCConstantExpr::create(Value: 1, Ctx&: Asm->OutContext), |
327 | Ctx&: Asm->OutContext); |
328 | } |
329 | |
330 | const MCExpr *WinException::getOffset(const MCSymbol *OffsetOf, |
331 | const MCSymbol *OffsetFrom) { |
332 | return MCBinaryExpr::createSub( |
333 | LHS: MCSymbolRefExpr::create(Symbol: OffsetOf, Ctx&: Asm->OutContext), |
334 | RHS: MCSymbolRefExpr::create(Symbol: OffsetFrom, Ctx&: Asm->OutContext), Ctx&: Asm->OutContext); |
335 | } |
336 | |
337 | const MCExpr *WinException::getOffsetPlusOne(const MCSymbol *OffsetOf, |
338 | const MCSymbol *OffsetFrom) { |
339 | return MCBinaryExpr::createAdd(LHS: getOffset(OffsetOf, OffsetFrom), |
340 | RHS: MCConstantExpr::create(Value: 1, Ctx&: Asm->OutContext), |
341 | Ctx&: Asm->OutContext); |
342 | } |
343 | |
344 | int WinException::getFrameIndexOffset(int FrameIndex, |
345 | const WinEHFuncInfo &FuncInfo) { |
346 | const TargetFrameLowering &TFI = *Asm->MF->getSubtarget().getFrameLowering(); |
347 | Register UnusedReg; |
348 | if (Asm->MAI->usesWindowsCFI()) { |
349 | StackOffset Offset = |
350 | TFI.getFrameIndexReferencePreferSP(MF: *Asm->MF, FI: FrameIndex, FrameReg&: UnusedReg, |
351 | /*IgnoreSPUpdates*/ true); |
352 | assert(UnusedReg == |
353 | Asm->MF->getSubtarget() |
354 | .getTargetLowering() |
355 | ->getStackPointerRegisterToSaveRestore()); |
356 | return Offset.getFixed(); |
357 | } |
358 | |
359 | // For 32-bit, offsets should be relative to the end of the EH registration |
360 | // node. For 64-bit, it's relative to SP at the end of the prologue. |
361 | assert(FuncInfo.EHRegNodeEndOffset != INT_MAX); |
362 | StackOffset Offset = TFI.getFrameIndexReference(MF: *Asm->MF, FI: FrameIndex, FrameReg&: UnusedReg); |
363 | Offset += StackOffset::getFixed(Fixed: FuncInfo.EHRegNodeEndOffset); |
364 | assert(!Offset.getScalable() && |
365 | "Frame offsets with a scalable component are not supported" ); |
366 | return Offset.getFixed(); |
367 | } |
368 | |
369 | namespace { |
370 | |
371 | /// Top-level state used to represent unwind to caller |
372 | const int NullState = -1; |
373 | |
374 | struct InvokeStateChange { |
375 | /// EH Label immediately after the last invoke in the previous state, or |
376 | /// nullptr if the previous state was the null state. |
377 | const MCSymbol *PreviousEndLabel; |
378 | |
379 | /// EH label immediately before the first invoke in the new state, or nullptr |
380 | /// if the new state is the null state. |
381 | const MCSymbol *NewStartLabel; |
382 | |
383 | /// State of the invoke following NewStartLabel, or NullState to indicate |
384 | /// the presence of calls which may unwind to caller. |
385 | int NewState; |
386 | }; |
387 | |
388 | /// Iterator that reports all the invoke state changes in a range of machine |
389 | /// basic blocks. Changes to the null state are reported whenever a call that |
390 | /// may unwind to caller is encountered. The MBB range is expected to be an |
391 | /// entire function or funclet, and the start and end of the range are treated |
392 | /// as being in the NullState even if there's not an unwind-to-caller call |
393 | /// before the first invoke or after the last one (i.e., the first state change |
394 | /// reported is the first change to something other than NullState, and a |
395 | /// change back to NullState is always reported at the end of iteration). |
396 | class InvokeStateChangeIterator { |
397 | InvokeStateChangeIterator(const WinEHFuncInfo &EHInfo, |
398 | MachineFunction::const_iterator MFI, |
399 | MachineFunction::const_iterator MFE, |
400 | MachineBasicBlock::const_iterator MBBI, |
401 | int BaseState) |
402 | : EHInfo(EHInfo), MFI(MFI), MFE(MFE), MBBI(MBBI), BaseState(BaseState) { |
403 | LastStateChange.PreviousEndLabel = nullptr; |
404 | LastStateChange.NewStartLabel = nullptr; |
405 | LastStateChange.NewState = BaseState; |
406 | scan(); |
407 | } |
408 | |
409 | public: |
410 | static iterator_range<InvokeStateChangeIterator> |
411 | range(const WinEHFuncInfo &EHInfo, MachineFunction::const_iterator Begin, |
412 | MachineFunction::const_iterator End, int BaseState = NullState) { |
413 | // Reject empty ranges to simplify bookkeeping by ensuring that we can get |
414 | // the end of the last block. |
415 | assert(Begin != End); |
416 | auto BlockBegin = Begin->begin(); |
417 | auto BlockEnd = std::prev(x: End)->end(); |
418 | return make_range( |
419 | x: InvokeStateChangeIterator(EHInfo, Begin, End, BlockBegin, BaseState), |
420 | y: InvokeStateChangeIterator(EHInfo, End, End, BlockEnd, BaseState)); |
421 | } |
422 | |
423 | // Iterator methods. |
424 | bool operator==(const InvokeStateChangeIterator &O) const { |
425 | assert(BaseState == O.BaseState); |
426 | // Must be visiting same block. |
427 | if (MFI != O.MFI) |
428 | return false; |
429 | // Must be visiting same isntr. |
430 | if (MBBI != O.MBBI) |
431 | return false; |
432 | // At end of block/instr iteration, we can still have two distinct states: |
433 | // one to report the final EndLabel, and another indicating the end of the |
434 | // state change iteration. Check for CurrentEndLabel equality to |
435 | // distinguish these. |
436 | return CurrentEndLabel == O.CurrentEndLabel; |
437 | } |
438 | |
439 | bool operator!=(const InvokeStateChangeIterator &O) const { |
440 | return !operator==(O); |
441 | } |
442 | InvokeStateChange &operator*() { return LastStateChange; } |
443 | InvokeStateChange *operator->() { return &LastStateChange; } |
444 | InvokeStateChangeIterator &operator++() { return scan(); } |
445 | |
446 | private: |
447 | InvokeStateChangeIterator &scan(); |
448 | |
449 | const WinEHFuncInfo &EHInfo; |
450 | const MCSymbol *CurrentEndLabel = nullptr; |
451 | MachineFunction::const_iterator MFI; |
452 | MachineFunction::const_iterator MFE; |
453 | MachineBasicBlock::const_iterator MBBI; |
454 | InvokeStateChange LastStateChange; |
455 | bool VisitingInvoke = false; |
456 | int BaseState; |
457 | }; |
458 | |
459 | } // end anonymous namespace |
460 | |
461 | InvokeStateChangeIterator &InvokeStateChangeIterator::scan() { |
462 | bool IsNewBlock = false; |
463 | for (; MFI != MFE; ++MFI, IsNewBlock = true) { |
464 | if (IsNewBlock) |
465 | MBBI = MFI->begin(); |
466 | for (auto MBBE = MFI->end(); MBBI != MBBE; ++MBBI) { |
467 | const MachineInstr &MI = *MBBI; |
468 | if (!VisitingInvoke && LastStateChange.NewState != BaseState && |
469 | MI.isCall() && !EHStreamer::callToNoUnwindFunction(MI: &MI)) { |
470 | // Indicate a change of state to the null state. We don't have |
471 | // start/end EH labels handy but the caller won't expect them for |
472 | // null state regions. |
473 | LastStateChange.PreviousEndLabel = CurrentEndLabel; |
474 | LastStateChange.NewStartLabel = nullptr; |
475 | LastStateChange.NewState = BaseState; |
476 | CurrentEndLabel = nullptr; |
477 | // Don't re-visit this instr on the next scan |
478 | ++MBBI; |
479 | return *this; |
480 | } |
481 | |
482 | // All other state changes are at EH labels before/after invokes. |
483 | if (!MI.isEHLabel()) |
484 | continue; |
485 | MCSymbol *Label = MI.getOperand(i: 0).getMCSymbol(); |
486 | if (Label == CurrentEndLabel) { |
487 | VisitingInvoke = false; |
488 | continue; |
489 | } |
490 | auto InvokeMapIter = EHInfo.LabelToStateMap.find(Val: Label); |
491 | // Ignore EH labels that aren't the ones inserted before an invoke |
492 | if (InvokeMapIter == EHInfo.LabelToStateMap.end()) |
493 | continue; |
494 | auto &StateAndEnd = InvokeMapIter->second; |
495 | int NewState = StateAndEnd.first; |
496 | // Keep track of the fact that we're between EH start/end labels so |
497 | // we know not to treat the inoke we'll see as unwinding to caller. |
498 | VisitingInvoke = true; |
499 | if (NewState == LastStateChange.NewState) { |
500 | // The state isn't actually changing here. Record the new end and |
501 | // keep going. |
502 | CurrentEndLabel = StateAndEnd.second; |
503 | continue; |
504 | } |
505 | // Found a state change to report |
506 | LastStateChange.PreviousEndLabel = CurrentEndLabel; |
507 | LastStateChange.NewStartLabel = Label; |
508 | LastStateChange.NewState = NewState; |
509 | // Start keeping track of the new current end |
510 | CurrentEndLabel = StateAndEnd.second; |
511 | // Don't re-visit this instr on the next scan |
512 | ++MBBI; |
513 | return *this; |
514 | } |
515 | } |
516 | // Iteration hit the end of the block range. |
517 | if (LastStateChange.NewState != BaseState) { |
518 | // Report the end of the last new state |
519 | LastStateChange.PreviousEndLabel = CurrentEndLabel; |
520 | LastStateChange.NewStartLabel = nullptr; |
521 | LastStateChange.NewState = BaseState; |
522 | // Leave CurrentEndLabel non-null to distinguish this state from end. |
523 | assert(CurrentEndLabel != nullptr); |
524 | return *this; |
525 | } |
526 | // We've reported all state changes and hit the end state. |
527 | CurrentEndLabel = nullptr; |
528 | return *this; |
529 | } |
530 | |
531 | /// Emit the language-specific data that __C_specific_handler expects. This |
532 | /// handler lives in the x64 Microsoft C runtime and allows catching or cleaning |
533 | /// up after faults with __try, __except, and __finally. The typeinfo values |
534 | /// are not really RTTI data, but pointers to filter functions that return an |
535 | /// integer (1, 0, or -1) indicating how to handle the exception. For __finally |
536 | /// blocks and other cleanups, the landing pad label is zero, and the filter |
537 | /// function is actually a cleanup handler with the same prototype. A catch-all |
538 | /// entry is modeled with a null filter function field and a non-zero landing |
539 | /// pad label. |
540 | /// |
541 | /// Possible filter function return values: |
542 | /// EXCEPTION_EXECUTE_HANDLER (1): |
543 | /// Jump to the landing pad label after cleanups. |
544 | /// EXCEPTION_CONTINUE_SEARCH (0): |
545 | /// Continue searching this table or continue unwinding. |
546 | /// EXCEPTION_CONTINUE_EXECUTION (-1): |
547 | /// Resume execution at the trapping PC. |
548 | /// |
549 | /// Inferred table structure: |
550 | /// struct Table { |
551 | /// int NumEntries; |
552 | /// struct Entry { |
553 | /// imagerel32 LabelStart; // Inclusive |
554 | /// imagerel32 LabelEnd; // Exclusive |
555 | /// imagerel32 FilterOrFinally; // One means catch-all. |
556 | /// imagerel32 LabelLPad; // Zero means __finally. |
557 | /// } Entries[NumEntries]; |
558 | /// }; |
559 | void WinException::emitCSpecificHandlerTable(const MachineFunction *MF) { |
560 | auto &OS = *Asm->OutStreamer; |
561 | MCContext &Ctx = Asm->OutContext; |
562 | const WinEHFuncInfo &FuncInfo = *MF->getWinEHFuncInfo(); |
563 | |
564 | bool VerboseAsm = OS.isVerboseAsm(); |
565 | auto = [&](const Twine &) { |
566 | if (VerboseAsm) |
567 | OS.AddComment(T: Comment); |
568 | }; |
569 | |
570 | if (!isAArch64) { |
571 | // Emit a label assignment with the SEH frame offset so we can use it for |
572 | // llvm.eh.recoverfp. |
573 | StringRef FLinkageName = |
574 | GlobalValue::dropLLVMManglingEscape(Name: MF->getFunction().getName()); |
575 | MCSymbol *ParentFrameOffset = |
576 | Ctx.getOrCreateParentFrameOffsetSymbol(FuncName: FLinkageName); |
577 | const MCExpr *MCOffset = |
578 | MCConstantExpr::create(Value: FuncInfo.SEHSetFrameOffset, Ctx); |
579 | Asm->OutStreamer->emitAssignment(Symbol: ParentFrameOffset, Value: MCOffset); |
580 | } |
581 | |
582 | // Use the assembler to compute the number of table entries through label |
583 | // difference and division. |
584 | MCSymbol *TableBegin = |
585 | Ctx.createTempSymbol(Name: "lsda_begin" , /*AlwaysAddSuffix=*/true); |
586 | MCSymbol *TableEnd = |
587 | Ctx.createTempSymbol(Name: "lsda_end" , /*AlwaysAddSuffix=*/true); |
588 | const MCExpr *LabelDiff = getOffset(OffsetOf: TableEnd, OffsetFrom: TableBegin); |
589 | const MCExpr *EntrySize = MCConstantExpr::create(Value: 16, Ctx); |
590 | const MCExpr *EntryCount = MCBinaryExpr::createDiv(LHS: LabelDiff, RHS: EntrySize, Ctx); |
591 | AddComment("Number of call sites" ); |
592 | OS.emitValue(Value: EntryCount, Size: 4); |
593 | |
594 | OS.emitLabel(Symbol: TableBegin); |
595 | |
596 | // Iterate over all the invoke try ranges. Unlike MSVC, LLVM currently only |
597 | // models exceptions from invokes. LLVM also allows arbitrary reordering of |
598 | // the code, so our tables end up looking a bit different. Rather than |
599 | // trying to match MSVC's tables exactly, we emit a denormalized table. For |
600 | // each range of invokes in the same state, we emit table entries for all |
601 | // the actions that would be taken in that state. This means our tables are |
602 | // slightly bigger, which is OK. |
603 | const MCSymbol *LastStartLabel = nullptr; |
604 | int LastEHState = -1; |
605 | // Break out before we enter into a finally funclet. |
606 | // FIXME: We need to emit separate EH tables for cleanups. |
607 | MachineFunction::const_iterator End = MF->end(); |
608 | MachineFunction::const_iterator Stop = std::next(x: MF->begin()); |
609 | while (Stop != End && !Stop->isEHFuncletEntry()) |
610 | ++Stop; |
611 | for (const auto &StateChange : |
612 | InvokeStateChangeIterator::range(EHInfo: FuncInfo, Begin: MF->begin(), End: Stop)) { |
613 | // Emit all the actions for the state we just transitioned out of |
614 | // if it was not the null state |
615 | if (LastEHState != -1) |
616 | emitSEHActionsForRange(FuncInfo, BeginLabel: LastStartLabel, |
617 | EndLabel: StateChange.PreviousEndLabel, State: LastEHState); |
618 | LastStartLabel = StateChange.NewStartLabel; |
619 | LastEHState = StateChange.NewState; |
620 | } |
621 | |
622 | OS.emitLabel(Symbol: TableEnd); |
623 | } |
624 | |
625 | void WinException::emitSEHActionsForRange(const WinEHFuncInfo &FuncInfo, |
626 | const MCSymbol *BeginLabel, |
627 | const MCSymbol *EndLabel, int State) { |
628 | auto &OS = *Asm->OutStreamer; |
629 | MCContext &Ctx = Asm->OutContext; |
630 | bool VerboseAsm = OS.isVerboseAsm(); |
631 | auto = [&](const Twine &) { |
632 | if (VerboseAsm) |
633 | OS.AddComment(T: Comment); |
634 | }; |
635 | |
636 | assert(BeginLabel && EndLabel); |
637 | while (State != -1) { |
638 | const SEHUnwindMapEntry &UME = FuncInfo.SEHUnwindMap[State]; |
639 | const MCExpr *FilterOrFinally; |
640 | const MCExpr *ExceptOrNull; |
641 | auto *Handler = cast<MachineBasicBlock *>(Val: UME.Handler); |
642 | if (UME.IsFinally) { |
643 | FilterOrFinally = create32bitRef(Value: getMCSymbolForMBB(Asm, MBB: Handler)); |
644 | ExceptOrNull = MCConstantExpr::create(Value: 0, Ctx); |
645 | } else { |
646 | // For an except, the filter can be 1 (catch-all) or a function |
647 | // label. |
648 | FilterOrFinally = UME.Filter ? create32bitRef(GV: UME.Filter) |
649 | : MCConstantExpr::create(Value: 1, Ctx); |
650 | ExceptOrNull = create32bitRef(Value: Handler->getSymbol()); |
651 | } |
652 | |
653 | AddComment("LabelStart" ); |
654 | OS.emitValue(Value: getLabel(Label: BeginLabel), Size: 4); |
655 | AddComment("LabelEnd" ); |
656 | OS.emitValue(Value: getLabelPlusOne(Label: EndLabel), Size: 4); |
657 | AddComment(UME.IsFinally ? "FinallyFunclet" : UME.Filter ? "FilterFunction" |
658 | : "CatchAll" ); |
659 | OS.emitValue(Value: FilterOrFinally, Size: 4); |
660 | AddComment(UME.IsFinally ? "Null" : "ExceptionHandler" ); |
661 | OS.emitValue(Value: ExceptOrNull, Size: 4); |
662 | |
663 | assert(UME.ToState < State && "states should decrease" ); |
664 | State = UME.ToState; |
665 | } |
666 | } |
667 | |
668 | void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) { |
669 | const Function &F = MF->getFunction(); |
670 | auto &OS = *Asm->OutStreamer; |
671 | const WinEHFuncInfo &FuncInfo = *MF->getWinEHFuncInfo(); |
672 | |
673 | StringRef FuncLinkageName = GlobalValue::dropLLVMManglingEscape(Name: F.getName()); |
674 | |
675 | SmallVector<std::pair<const MCExpr *, int>, 4> IPToStateTable; |
676 | MCSymbol *FuncInfoXData = nullptr; |
677 | if (shouldEmitPersonality) { |
678 | // If we're 64-bit, emit a pointer to the C++ EH data, and build a map from |
679 | // IPs to state numbers. |
680 | FuncInfoXData = |
681 | Asm->OutContext.getOrCreateSymbol(Name: Twine("$cppxdata$" , FuncLinkageName)); |
682 | computeIP2StateTable(MF, FuncInfo, IPToStateTable); |
683 | } else { |
684 | FuncInfoXData = Asm->OutContext.getOrCreateLSDASymbol(FuncName: FuncLinkageName); |
685 | } |
686 | |
687 | int UnwindHelpOffset = 0; |
688 | // TODO: The check for UnwindHelpFrameIdx against max() below (and the |
689 | // second check further below) can be removed if MS C++ unwinding is |
690 | // implemented for ARM, when test/CodeGen/ARM/Windows/wineh-basic.ll |
691 | // passes without the check. |
692 | if (Asm->MAI->usesWindowsCFI() && |
693 | FuncInfo.UnwindHelpFrameIdx != std::numeric_limits<int>::max()) |
694 | UnwindHelpOffset = |
695 | getFrameIndexOffset(FrameIndex: FuncInfo.UnwindHelpFrameIdx, FuncInfo); |
696 | |
697 | MCSymbol *UnwindMapXData = nullptr; |
698 | MCSymbol *TryBlockMapXData = nullptr; |
699 | MCSymbol *IPToStateXData = nullptr; |
700 | if (!FuncInfo.CxxUnwindMap.empty()) |
701 | UnwindMapXData = Asm->OutContext.getOrCreateSymbol( |
702 | Name: Twine("$stateUnwindMap$" , FuncLinkageName)); |
703 | if (!FuncInfo.TryBlockMap.empty()) |
704 | TryBlockMapXData = |
705 | Asm->OutContext.getOrCreateSymbol(Name: Twine("$tryMap$" , FuncLinkageName)); |
706 | if (!IPToStateTable.empty()) |
707 | IPToStateXData = |
708 | Asm->OutContext.getOrCreateSymbol(Name: Twine("$ip2state$" , FuncLinkageName)); |
709 | |
710 | bool VerboseAsm = OS.isVerboseAsm(); |
711 | auto = [&](const Twine &) { |
712 | if (VerboseAsm) |
713 | OS.AddComment(T: Comment); |
714 | }; |
715 | |
716 | // FuncInfo { |
717 | // uint32_t MagicNumber |
718 | // int32_t MaxState; |
719 | // UnwindMapEntry *UnwindMap; |
720 | // uint32_t NumTryBlocks; |
721 | // TryBlockMapEntry *TryBlockMap; |
722 | // uint32_t IPMapEntries; // always 0 for x86 |
723 | // IPToStateMapEntry *IPToStateMap; // always 0 for x86 |
724 | // uint32_t UnwindHelp; // non-x86 only |
725 | // ESTypeList *ESTypeList; |
726 | // int32_t EHFlags; |
727 | // } |
728 | // EHFlags & 1 -> Synchronous exceptions only, no async exceptions. |
729 | // EHFlags & 2 -> ??? |
730 | // EHFlags & 4 -> The function is noexcept(true), unwinding can't continue. |
731 | OS.emitValueToAlignment(Alignment: Align(4)); |
732 | OS.emitLabel(Symbol: FuncInfoXData); |
733 | |
734 | AddComment("MagicNumber" ); |
735 | OS.emitInt32(Value: 0x19930522); |
736 | |
737 | AddComment("MaxState" ); |
738 | OS.emitInt32(Value: FuncInfo.CxxUnwindMap.size()); |
739 | |
740 | AddComment("UnwindMap" ); |
741 | OS.emitValue(Value: create32bitRef(Value: UnwindMapXData), Size: 4); |
742 | |
743 | AddComment("NumTryBlocks" ); |
744 | OS.emitInt32(Value: FuncInfo.TryBlockMap.size()); |
745 | |
746 | AddComment("TryBlockMap" ); |
747 | OS.emitValue(Value: create32bitRef(Value: TryBlockMapXData), Size: 4); |
748 | |
749 | AddComment("IPMapEntries" ); |
750 | OS.emitInt32(Value: IPToStateTable.size()); |
751 | |
752 | AddComment("IPToStateXData" ); |
753 | OS.emitValue(Value: create32bitRef(Value: IPToStateXData), Size: 4); |
754 | |
755 | if (Asm->MAI->usesWindowsCFI() && |
756 | FuncInfo.UnwindHelpFrameIdx != std::numeric_limits<int>::max()) { |
757 | AddComment("UnwindHelp" ); |
758 | OS.emitInt32(Value: UnwindHelpOffset); |
759 | } |
760 | |
761 | AddComment("ESTypeList" ); |
762 | OS.emitInt32(Value: 0); |
763 | |
764 | AddComment("EHFlags" ); |
765 | if (MMI->getModule()->getModuleFlag(Key: "eh-asynch" )) { |
766 | OS.emitInt32(Value: 0); |
767 | } else { |
768 | OS.emitInt32(Value: 1); |
769 | } |
770 | |
771 | // UnwindMapEntry { |
772 | // int32_t ToState; |
773 | // void (*Action)(); |
774 | // }; |
775 | if (UnwindMapXData) { |
776 | OS.emitLabel(Symbol: UnwindMapXData); |
777 | for (const CxxUnwindMapEntry &UME : FuncInfo.CxxUnwindMap) { |
778 | MCSymbol *CleanupSym = getMCSymbolForMBB( |
779 | Asm, MBB: dyn_cast_if_present<MachineBasicBlock *>(Val: UME.Cleanup)); |
780 | AddComment("ToState" ); |
781 | OS.emitInt32(Value: UME.ToState); |
782 | |
783 | AddComment("Action" ); |
784 | OS.emitValue(Value: create32bitRef(Value: CleanupSym), Size: 4); |
785 | } |
786 | } |
787 | |
788 | // TryBlockMap { |
789 | // int32_t TryLow; |
790 | // int32_t TryHigh; |
791 | // int32_t CatchHigh; |
792 | // int32_t NumCatches; |
793 | // HandlerType *HandlerArray; |
794 | // }; |
795 | if (TryBlockMapXData) { |
796 | OS.emitLabel(Symbol: TryBlockMapXData); |
797 | SmallVector<MCSymbol *, 1> HandlerMaps; |
798 | for (size_t I = 0, E = FuncInfo.TryBlockMap.size(); I != E; ++I) { |
799 | const WinEHTryBlockMapEntry &TBME = FuncInfo.TryBlockMap[I]; |
800 | |
801 | MCSymbol *HandlerMapXData = nullptr; |
802 | if (!TBME.HandlerArray.empty()) |
803 | HandlerMapXData = |
804 | Asm->OutContext.getOrCreateSymbol(Name: Twine("$handlerMap$" ) |
805 | .concat(Suffix: Twine(I)) |
806 | .concat(Suffix: "$" ) |
807 | .concat(Suffix: FuncLinkageName)); |
808 | HandlerMaps.push_back(Elt: HandlerMapXData); |
809 | |
810 | // TBMEs should form intervals. |
811 | assert(0 <= TBME.TryLow && "bad trymap interval" ); |
812 | assert(TBME.TryLow <= TBME.TryHigh && "bad trymap interval" ); |
813 | assert(TBME.TryHigh < TBME.CatchHigh && "bad trymap interval" ); |
814 | assert(TBME.CatchHigh < int(FuncInfo.CxxUnwindMap.size()) && |
815 | "bad trymap interval" ); |
816 | |
817 | AddComment("TryLow" ); |
818 | OS.emitInt32(Value: TBME.TryLow); |
819 | |
820 | AddComment("TryHigh" ); |
821 | OS.emitInt32(Value: TBME.TryHigh); |
822 | |
823 | AddComment("CatchHigh" ); |
824 | OS.emitInt32(Value: TBME.CatchHigh); |
825 | |
826 | AddComment("NumCatches" ); |
827 | OS.emitInt32(Value: TBME.HandlerArray.size()); |
828 | |
829 | AddComment("HandlerArray" ); |
830 | OS.emitValue(Value: create32bitRef(Value: HandlerMapXData), Size: 4); |
831 | } |
832 | |
833 | // All funclets use the same parent frame offset currently. |
834 | unsigned ParentFrameOffset = 0; |
835 | if (shouldEmitPersonality) { |
836 | const TargetFrameLowering *TFI = MF->getSubtarget().getFrameLowering(); |
837 | ParentFrameOffset = TFI->getWinEHParentFrameOffset(MF: *MF); |
838 | } |
839 | |
840 | for (size_t I = 0, E = FuncInfo.TryBlockMap.size(); I != E; ++I) { |
841 | const WinEHTryBlockMapEntry &TBME = FuncInfo.TryBlockMap[I]; |
842 | MCSymbol *HandlerMapXData = HandlerMaps[I]; |
843 | if (!HandlerMapXData) |
844 | continue; |
845 | // HandlerType { |
846 | // int32_t Adjectives; |
847 | // TypeDescriptor *Type; |
848 | // int32_t CatchObjOffset; |
849 | // void (*Handler)(); |
850 | // int32_t ParentFrameOffset; // x64 and AArch64 only |
851 | // }; |
852 | OS.emitLabel(Symbol: HandlerMapXData); |
853 | for (const WinEHHandlerType &HT : TBME.HandlerArray) { |
854 | // Get the frame escape label with the offset of the catch object. If |
855 | // the index is INT_MAX, then there is no catch object, and we should |
856 | // emit an offset of zero, indicating that no copy will occur. |
857 | const MCExpr *FrameAllocOffsetRef = nullptr; |
858 | if (HT.CatchObj.FrameIndex != INT_MAX) { |
859 | int Offset = getFrameIndexOffset(FrameIndex: HT.CatchObj.FrameIndex, FuncInfo); |
860 | assert(Offset != 0 && "Illegal offset for catch object!" ); |
861 | FrameAllocOffsetRef = MCConstantExpr::create(Value: Offset, Ctx&: Asm->OutContext); |
862 | } else { |
863 | FrameAllocOffsetRef = MCConstantExpr::create(Value: 0, Ctx&: Asm->OutContext); |
864 | } |
865 | |
866 | MCSymbol *HandlerSym = getMCSymbolForMBB( |
867 | Asm, MBB: dyn_cast_if_present<MachineBasicBlock *>(Val: HT.Handler)); |
868 | |
869 | AddComment("Adjectives" ); |
870 | OS.emitInt32(Value: HT.Adjectives); |
871 | |
872 | AddComment("Type" ); |
873 | OS.emitValue(Value: create32bitRef(GV: HT.TypeDescriptor), Size: 4); |
874 | |
875 | AddComment("CatchObjOffset" ); |
876 | OS.emitValue(Value: FrameAllocOffsetRef, Size: 4); |
877 | |
878 | AddComment("Handler" ); |
879 | OS.emitValue(Value: create32bitRef(Value: HandlerSym), Size: 4); |
880 | |
881 | if (shouldEmitPersonality) { |
882 | AddComment("ParentFrameOffset" ); |
883 | OS.emitInt32(Value: ParentFrameOffset); |
884 | } |
885 | } |
886 | } |
887 | } |
888 | |
889 | // IPToStateMapEntry { |
890 | // void *IP; |
891 | // int32_t State; |
892 | // }; |
893 | if (IPToStateXData) { |
894 | OS.emitLabel(Symbol: IPToStateXData); |
895 | for (auto &IPStatePair : IPToStateTable) { |
896 | AddComment("IP" ); |
897 | OS.emitValue(Value: IPStatePair.first, Size: 4); |
898 | AddComment("ToState" ); |
899 | OS.emitInt32(Value: IPStatePair.second); |
900 | } |
901 | } |
902 | } |
903 | |
904 | void WinException::computeIP2StateTable( |
905 | const MachineFunction *MF, const WinEHFuncInfo &FuncInfo, |
906 | SmallVectorImpl<std::pair<const MCExpr *, int>> &IPToStateTable) { |
907 | |
908 | for (MachineFunction::const_iterator FuncletStart = MF->begin(), |
909 | FuncletEnd = MF->begin(), |
910 | End = MF->end(); |
911 | FuncletStart != End; FuncletStart = FuncletEnd) { |
912 | // Find the end of the funclet |
913 | while (++FuncletEnd != End) { |
914 | if (FuncletEnd->isEHFuncletEntry()) { |
915 | break; |
916 | } |
917 | } |
918 | |
919 | // Don't emit ip2state entries for cleanup funclets. Any interesting |
920 | // exceptional actions in cleanups must be handled in a separate IR |
921 | // function. |
922 | if (FuncletStart->isCleanupFuncletEntry()) |
923 | continue; |
924 | |
925 | MCSymbol *StartLabel; |
926 | int BaseState; |
927 | if (FuncletStart == MF->begin()) { |
928 | BaseState = NullState; |
929 | StartLabel = Asm->getFunctionBegin(); |
930 | } else { |
931 | auto *FuncletPad = |
932 | cast<FuncletPadInst>(Val: FuncletStart->getBasicBlock()->getFirstNonPHI()); |
933 | assert(FuncInfo.FuncletBaseStateMap.count(FuncletPad) != 0); |
934 | BaseState = FuncInfo.FuncletBaseStateMap.find(Val: FuncletPad)->second; |
935 | StartLabel = getMCSymbolForMBB(Asm, MBB: &*FuncletStart); |
936 | } |
937 | assert(StartLabel && "need local function start label" ); |
938 | IPToStateTable.push_back( |
939 | Elt: std::make_pair(x: create32bitRef(Value: StartLabel), y&: BaseState)); |
940 | |
941 | for (const auto &StateChange : InvokeStateChangeIterator::range( |
942 | EHInfo: FuncInfo, Begin: FuncletStart, End: FuncletEnd, BaseState)) { |
943 | // Compute the label to report as the start of this entry; use the EH |
944 | // start label for the invoke if we have one, otherwise (this is a call |
945 | // which may unwind to our caller and does not have an EH start label, so) |
946 | // use the previous end label. |
947 | const MCSymbol *ChangeLabel = StateChange.NewStartLabel; |
948 | if (!ChangeLabel) |
949 | ChangeLabel = StateChange.PreviousEndLabel; |
950 | // Emit an entry indicating that PCs after 'Label' have this EH state. |
951 | // NOTE: On ARM architectures, the StateFromIp automatically takes into |
952 | // account that the return address is after the call instruction (whose EH |
953 | // state we should be using), but on other platforms we need to +1 to the |
954 | // label so that we are using the correct EH state. |
955 | const MCExpr *LabelExpression = (isAArch64 || isThumb) |
956 | ? getLabel(Label: ChangeLabel) |
957 | : getLabelPlusOne(Label: ChangeLabel); |
958 | IPToStateTable.push_back( |
959 | Elt: std::make_pair(x&: LabelExpression, y: StateChange.NewState)); |
960 | // FIXME: assert that NewState is between CatchLow and CatchHigh. |
961 | } |
962 | } |
963 | } |
964 | |
965 | void WinException::emitEHRegistrationOffsetLabel(const WinEHFuncInfo &FuncInfo, |
966 | StringRef FLinkageName) { |
967 | // Outlined helpers called by the EH runtime need to know the offset of the EH |
968 | // registration in order to recover the parent frame pointer. Now that we know |
969 | // we've code generated the parent, we can emit the label assignment that |
970 | // those helpers use to get the offset of the registration node. |
971 | |
972 | // Compute the parent frame offset. The EHRegNodeFrameIndex will be invalid if |
973 | // after optimization all the invokes were eliminated. We still need to emit |
974 | // the parent frame offset label, but it should be garbage and should never be |
975 | // used. |
976 | int64_t Offset = 0; |
977 | int FI = FuncInfo.EHRegNodeFrameIndex; |
978 | if (FI != INT_MAX) { |
979 | const TargetFrameLowering *TFI = Asm->MF->getSubtarget().getFrameLowering(); |
980 | Offset = TFI->getNonLocalFrameIndexReference(MF: *Asm->MF, FI).getFixed(); |
981 | } |
982 | |
983 | MCContext &Ctx = Asm->OutContext; |
984 | MCSymbol *ParentFrameOffset = |
985 | Ctx.getOrCreateParentFrameOffsetSymbol(FuncName: FLinkageName); |
986 | Asm->OutStreamer->emitAssignment(Symbol: ParentFrameOffset, |
987 | Value: MCConstantExpr::create(Value: Offset, Ctx)); |
988 | } |
989 | |
990 | /// Emit the language-specific data that _except_handler3 and 4 expect. This is |
991 | /// functionally equivalent to the __C_specific_handler table, except it is |
992 | /// indexed by state number instead of IP. |
993 | void WinException::emitExceptHandlerTable(const MachineFunction *MF) { |
994 | MCStreamer &OS = *Asm->OutStreamer; |
995 | const Function &F = MF->getFunction(); |
996 | StringRef FLinkageName = GlobalValue::dropLLVMManglingEscape(Name: F.getName()); |
997 | |
998 | bool VerboseAsm = OS.isVerboseAsm(); |
999 | auto = [&](const Twine &) { |
1000 | if (VerboseAsm) |
1001 | OS.AddComment(T: Comment); |
1002 | }; |
1003 | |
1004 | const WinEHFuncInfo &FuncInfo = *MF->getWinEHFuncInfo(); |
1005 | emitEHRegistrationOffsetLabel(FuncInfo, FLinkageName); |
1006 | |
1007 | // Emit the __ehtable label that we use for llvm.x86.seh.lsda. |
1008 | MCSymbol *LSDALabel = Asm->OutContext.getOrCreateLSDASymbol(FuncName: FLinkageName); |
1009 | OS.emitValueToAlignment(Alignment: Align(4)); |
1010 | OS.emitLabel(Symbol: LSDALabel); |
1011 | |
1012 | const auto *Per = cast<Function>(Val: F.getPersonalityFn()->stripPointerCasts()); |
1013 | StringRef PerName = Per->getName(); |
1014 | int BaseState = -1; |
1015 | if (PerName == "_except_handler4" ) { |
1016 | // The LSDA for _except_handler4 starts with this struct, followed by the |
1017 | // scope table: |
1018 | // |
1019 | // struct EH4ScopeTable { |
1020 | // int32_t GSCookieOffset; |
1021 | // int32_t GSCookieXOROffset; |
1022 | // int32_t EHCookieOffset; |
1023 | // int32_t EHCookieXOROffset; |
1024 | // ScopeTableEntry ScopeRecord[]; |
1025 | // }; |
1026 | // |
1027 | // Offsets are %ebp relative. |
1028 | // |
1029 | // The GS cookie is present only if the function needs stack protection. |
1030 | // GSCookieOffset = -2 means that GS cookie is not used. |
1031 | // |
1032 | // The EH cookie is always present. |
1033 | // |
1034 | // Check is done the following way: |
1035 | // (ebp+CookieXOROffset) ^ [ebp+CookieOffset] == _security_cookie |
1036 | |
1037 | // Retrieve the Guard Stack slot. |
1038 | int GSCookieOffset = -2; |
1039 | const MachineFrameInfo &MFI = MF->getFrameInfo(); |
1040 | if (MFI.hasStackProtectorIndex()) { |
1041 | Register UnusedReg; |
1042 | const TargetFrameLowering *TFI = MF->getSubtarget().getFrameLowering(); |
1043 | int SSPIdx = MFI.getStackProtectorIndex(); |
1044 | GSCookieOffset = |
1045 | TFI->getFrameIndexReference(MF: *MF, FI: SSPIdx, FrameReg&: UnusedReg).getFixed(); |
1046 | } |
1047 | |
1048 | // Retrieve the EH Guard slot. |
1049 | // TODO(etienneb): Get rid of this value and change it for and assertion. |
1050 | int EHCookieOffset = 9999; |
1051 | if (FuncInfo.EHGuardFrameIndex != INT_MAX) { |
1052 | Register UnusedReg; |
1053 | const TargetFrameLowering *TFI = MF->getSubtarget().getFrameLowering(); |
1054 | int EHGuardIdx = FuncInfo.EHGuardFrameIndex; |
1055 | EHCookieOffset = |
1056 | TFI->getFrameIndexReference(MF: *MF, FI: EHGuardIdx, FrameReg&: UnusedReg).getFixed(); |
1057 | } |
1058 | |
1059 | AddComment("GSCookieOffset" ); |
1060 | OS.emitInt32(Value: GSCookieOffset); |
1061 | AddComment("GSCookieXOROffset" ); |
1062 | OS.emitInt32(Value: 0); |
1063 | AddComment("EHCookieOffset" ); |
1064 | OS.emitInt32(Value: EHCookieOffset); |
1065 | AddComment("EHCookieXOROffset" ); |
1066 | OS.emitInt32(Value: 0); |
1067 | BaseState = -2; |
1068 | } |
1069 | |
1070 | assert(!FuncInfo.SEHUnwindMap.empty()); |
1071 | for (const SEHUnwindMapEntry &UME : FuncInfo.SEHUnwindMap) { |
1072 | auto *Handler = cast<MachineBasicBlock *>(Val: UME.Handler); |
1073 | const MCSymbol *ExceptOrFinally = |
1074 | UME.IsFinally ? getMCSymbolForMBB(Asm, MBB: Handler) : Handler->getSymbol(); |
1075 | // -1 is usually the base state for "unwind to caller", but for |
1076 | // _except_handler4 it's -2. Do that replacement here if necessary. |
1077 | int ToState = UME.ToState == -1 ? BaseState : UME.ToState; |
1078 | AddComment("ToState" ); |
1079 | OS.emitInt32(Value: ToState); |
1080 | AddComment(UME.IsFinally ? "Null" : "FilterFunction" ); |
1081 | OS.emitValue(Value: create32bitRef(GV: UME.Filter), Size: 4); |
1082 | AddComment(UME.IsFinally ? "FinallyFunclet" : "ExceptionHandler" ); |
1083 | OS.emitValue(Value: create32bitRef(Value: ExceptOrFinally), Size: 4); |
1084 | } |
1085 | } |
1086 | |
1087 | static int getTryRank(const WinEHFuncInfo &FuncInfo, int State) { |
1088 | int Rank = 0; |
1089 | while (State != -1) { |
1090 | ++Rank; |
1091 | State = FuncInfo.ClrEHUnwindMap[State].TryParentState; |
1092 | } |
1093 | return Rank; |
1094 | } |
1095 | |
1096 | static int getTryAncestor(const WinEHFuncInfo &FuncInfo, int Left, int Right) { |
1097 | int LeftRank = getTryRank(FuncInfo, State: Left); |
1098 | int RightRank = getTryRank(FuncInfo, State: Right); |
1099 | |
1100 | while (LeftRank < RightRank) { |
1101 | Right = FuncInfo.ClrEHUnwindMap[Right].TryParentState; |
1102 | --RightRank; |
1103 | } |
1104 | |
1105 | while (RightRank < LeftRank) { |
1106 | Left = FuncInfo.ClrEHUnwindMap[Left].TryParentState; |
1107 | --LeftRank; |
1108 | } |
1109 | |
1110 | while (Left != Right) { |
1111 | Left = FuncInfo.ClrEHUnwindMap[Left].TryParentState; |
1112 | Right = FuncInfo.ClrEHUnwindMap[Right].TryParentState; |
1113 | } |
1114 | |
1115 | return Left; |
1116 | } |
1117 | |
1118 | void WinException::emitCLRExceptionTable(const MachineFunction *MF) { |
1119 | // CLR EH "states" are really just IDs that identify handlers/funclets; |
1120 | // states, handlers, and funclets all have 1:1 mappings between them, and a |
1121 | // handler/funclet's "state" is its index in the ClrEHUnwindMap. |
1122 | MCStreamer &OS = *Asm->OutStreamer; |
1123 | const WinEHFuncInfo &FuncInfo = *MF->getWinEHFuncInfo(); |
1124 | MCSymbol *FuncBeginSym = Asm->getFunctionBegin(); |
1125 | MCSymbol *FuncEndSym = Asm->getFunctionEnd(); |
1126 | |
1127 | // A ClrClause describes a protected region. |
1128 | struct ClrClause { |
1129 | const MCSymbol *StartLabel; // Start of protected region |
1130 | const MCSymbol *EndLabel; // End of protected region |
1131 | int State; // Index of handler protecting the protected region |
1132 | int EnclosingState; // Index of funclet enclosing the protected region |
1133 | }; |
1134 | SmallVector<ClrClause, 8> Clauses; |
1135 | |
1136 | // Build a map from handler MBBs to their corresponding states (i.e. their |
1137 | // indices in the ClrEHUnwindMap). |
1138 | int NumStates = FuncInfo.ClrEHUnwindMap.size(); |
1139 | assert(NumStates > 0 && "Don't need exception table!" ); |
1140 | DenseMap<const MachineBasicBlock *, int> HandlerStates; |
1141 | for (int State = 0; State < NumStates; ++State) { |
1142 | MachineBasicBlock *HandlerBlock = |
1143 | cast<MachineBasicBlock *>(Val: FuncInfo.ClrEHUnwindMap[State].Handler); |
1144 | HandlerStates[HandlerBlock] = State; |
1145 | // Use this loop through all handlers to verify our assumption (used in |
1146 | // the MinEnclosingState computation) that enclosing funclets have lower |
1147 | // state numbers than their enclosed funclets. |
1148 | assert(FuncInfo.ClrEHUnwindMap[State].HandlerParentState < State && |
1149 | "ill-formed state numbering" ); |
1150 | } |
1151 | // Map the main function to the NullState. |
1152 | HandlerStates[&MF->front()] = NullState; |
1153 | |
1154 | // Write out a sentinel indicating the end of the standard (Windows) xdata |
1155 | // and the start of the additional (CLR) info. |
1156 | OS.emitInt32(Value: 0xffffffff); |
1157 | // Write out the number of funclets |
1158 | OS.emitInt32(Value: NumStates); |
1159 | |
1160 | // Walk the machine blocks/instrs, computing and emitting a few things: |
1161 | // 1. Emit a list of the offsets to each handler entry, in lexical order. |
1162 | // 2. Compute a map (EndSymbolMap) from each funclet to the symbol at its end. |
1163 | // 3. Compute the list of ClrClauses, in the required order (inner before |
1164 | // outer, earlier before later; the order by which a forward scan with |
1165 | // early termination will find the innermost enclosing clause covering |
1166 | // a given address). |
1167 | // 4. A map (MinClauseMap) from each handler index to the index of the |
1168 | // outermost funclet/function which contains a try clause targeting the |
1169 | // key handler. This will be used to determine IsDuplicate-ness when |
1170 | // emitting ClrClauses. The NullState value is used to indicate that the |
1171 | // top-level function contains a try clause targeting the key handler. |
1172 | // HandlerStack is a stack of (PendingStartLabel, PendingState) pairs for |
1173 | // try regions we entered before entering the PendingState try but which |
1174 | // we haven't yet exited. |
1175 | SmallVector<std::pair<const MCSymbol *, int>, 4> HandlerStack; |
1176 | // EndSymbolMap and MinClauseMap are maps described above. |
1177 | std::unique_ptr<MCSymbol *[]> EndSymbolMap(new MCSymbol *[NumStates]); |
1178 | SmallVector<int, 4> MinClauseMap((size_t)NumStates, NumStates); |
1179 | |
1180 | // Visit the root function and each funclet. |
1181 | for (MachineFunction::const_iterator FuncletStart = MF->begin(), |
1182 | FuncletEnd = MF->begin(), |
1183 | End = MF->end(); |
1184 | FuncletStart != End; FuncletStart = FuncletEnd) { |
1185 | int FuncletState = HandlerStates[&*FuncletStart]; |
1186 | // Find the end of the funclet |
1187 | MCSymbol *EndSymbol = FuncEndSym; |
1188 | while (++FuncletEnd != End) { |
1189 | if (FuncletEnd->isEHFuncletEntry()) { |
1190 | EndSymbol = getMCSymbolForMBB(Asm, MBB: &*FuncletEnd); |
1191 | break; |
1192 | } |
1193 | } |
1194 | // Emit the function/funclet end and, if this is a funclet (and not the |
1195 | // root function), record it in the EndSymbolMap. |
1196 | OS.emitValue(Value: getOffset(OffsetOf: EndSymbol, OffsetFrom: FuncBeginSym), Size: 4); |
1197 | if (FuncletState != NullState) { |
1198 | // Record the end of the handler. |
1199 | EndSymbolMap[FuncletState] = EndSymbol; |
1200 | } |
1201 | |
1202 | // Walk the state changes in this function/funclet and compute its clauses. |
1203 | // Funclets always start in the null state. |
1204 | const MCSymbol *CurrentStartLabel = nullptr; |
1205 | int CurrentState = NullState; |
1206 | assert(HandlerStack.empty()); |
1207 | for (const auto &StateChange : |
1208 | InvokeStateChangeIterator::range(EHInfo: FuncInfo, Begin: FuncletStart, End: FuncletEnd)) { |
1209 | // Close any try regions we're not still under |
1210 | int StillPendingState = |
1211 | getTryAncestor(FuncInfo, Left: CurrentState, Right: StateChange.NewState); |
1212 | while (CurrentState != StillPendingState) { |
1213 | assert(CurrentState != NullState && |
1214 | "Failed to find still-pending state!" ); |
1215 | // Close the pending clause |
1216 | Clauses.push_back(Elt: {.StartLabel: CurrentStartLabel, .EndLabel: StateChange.PreviousEndLabel, |
1217 | .State: CurrentState, .EnclosingState: FuncletState}); |
1218 | // Now the next-outer try region is current |
1219 | CurrentState = FuncInfo.ClrEHUnwindMap[CurrentState].TryParentState; |
1220 | // Pop the new start label from the handler stack if we've exited all |
1221 | // inner try regions of the corresponding try region. |
1222 | if (HandlerStack.back().second == CurrentState) |
1223 | CurrentStartLabel = HandlerStack.pop_back_val().first; |
1224 | } |
1225 | |
1226 | if (StateChange.NewState != CurrentState) { |
1227 | // For each clause we're starting, update the MinClauseMap so we can |
1228 | // know which is the topmost funclet containing a clause targeting |
1229 | // it. |
1230 | for (int EnteredState = StateChange.NewState; |
1231 | EnteredState != CurrentState; |
1232 | EnteredState = |
1233 | FuncInfo.ClrEHUnwindMap[EnteredState].TryParentState) { |
1234 | int &MinEnclosingState = MinClauseMap[EnteredState]; |
1235 | if (FuncletState < MinEnclosingState) |
1236 | MinEnclosingState = FuncletState; |
1237 | } |
1238 | // Save the previous current start/label on the stack and update to |
1239 | // the newly-current start/state. |
1240 | HandlerStack.emplace_back(Args&: CurrentStartLabel, Args&: CurrentState); |
1241 | CurrentStartLabel = StateChange.NewStartLabel; |
1242 | CurrentState = StateChange.NewState; |
1243 | } |
1244 | } |
1245 | assert(HandlerStack.empty()); |
1246 | } |
1247 | |
1248 | // Now emit the clause info, starting with the number of clauses. |
1249 | OS.emitInt32(Value: Clauses.size()); |
1250 | for (ClrClause &Clause : Clauses) { |
1251 | // Emit a CORINFO_EH_CLAUSE : |
1252 | /* |
1253 | struct CORINFO_EH_CLAUSE |
1254 | { |
1255 | CORINFO_EH_CLAUSE_FLAGS Flags; // actually a CorExceptionFlag |
1256 | DWORD TryOffset; |
1257 | DWORD TryLength; // actually TryEndOffset |
1258 | DWORD HandlerOffset; |
1259 | DWORD HandlerLength; // actually HandlerEndOffset |
1260 | union |
1261 | { |
1262 | DWORD ClassToken; // use for catch clauses |
1263 | DWORD FilterOffset; // use for filter clauses |
1264 | }; |
1265 | }; |
1266 | |
1267 | enum CORINFO_EH_CLAUSE_FLAGS |
1268 | { |
1269 | CORINFO_EH_CLAUSE_NONE = 0, |
1270 | CORINFO_EH_CLAUSE_FILTER = 0x0001, // This clause is for a filter |
1271 | CORINFO_EH_CLAUSE_FINALLY = 0x0002, // This clause is a finally clause |
1272 | CORINFO_EH_CLAUSE_FAULT = 0x0004, // This clause is a fault clause |
1273 | }; |
1274 | typedef enum CorExceptionFlag |
1275 | { |
1276 | COR_ILEXCEPTION_CLAUSE_NONE, |
1277 | COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001, // This is a filter clause |
1278 | COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002, // This is a finally clause |
1279 | COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004, // This is a fault clause |
1280 | COR_ILEXCEPTION_CLAUSE_DUPLICATED = 0x0008, // duplicated clause. This |
1281 | // clause was duplicated |
1282 | // to a funclet which was |
1283 | // pulled out of line |
1284 | } CorExceptionFlag; |
1285 | */ |
1286 | // Add 1 to the start/end of the EH clause; the IP associated with a |
1287 | // call when the runtime does its scan is the IP of the next instruction |
1288 | // (the one to which control will return after the call), so we need |
1289 | // to add 1 to the end of the clause to cover that offset. We also add |
1290 | // 1 to the start of the clause to make sure that the ranges reported |
1291 | // for all clauses are disjoint. Note that we'll need some additional |
1292 | // logic when machine traps are supported, since in that case the IP |
1293 | // that the runtime uses is the offset of the faulting instruction |
1294 | // itself; if such an instruction immediately follows a call but the |
1295 | // two belong to different clauses, we'll need to insert a nop between |
1296 | // them so the runtime can distinguish the point to which the call will |
1297 | // return from the point at which the fault occurs. |
1298 | |
1299 | const MCExpr *ClauseBegin = |
1300 | getOffsetPlusOne(OffsetOf: Clause.StartLabel, OffsetFrom: FuncBeginSym); |
1301 | const MCExpr *ClauseEnd = getOffsetPlusOne(OffsetOf: Clause.EndLabel, OffsetFrom: FuncBeginSym); |
1302 | |
1303 | const ClrEHUnwindMapEntry &Entry = FuncInfo.ClrEHUnwindMap[Clause.State]; |
1304 | MachineBasicBlock *HandlerBlock = cast<MachineBasicBlock *>(Val: Entry.Handler); |
1305 | MCSymbol *BeginSym = getMCSymbolForMBB(Asm, MBB: HandlerBlock); |
1306 | const MCExpr *HandlerBegin = getOffset(OffsetOf: BeginSym, OffsetFrom: FuncBeginSym); |
1307 | MCSymbol *EndSym = EndSymbolMap[Clause.State]; |
1308 | const MCExpr *HandlerEnd = getOffset(OffsetOf: EndSym, OffsetFrom: FuncBeginSym); |
1309 | |
1310 | uint32_t Flags = 0; |
1311 | switch (Entry.HandlerType) { |
1312 | case ClrHandlerType::Catch: |
1313 | // Leaving bits 0-2 clear indicates catch. |
1314 | break; |
1315 | case ClrHandlerType::Filter: |
1316 | Flags |= 1; |
1317 | break; |
1318 | case ClrHandlerType::Finally: |
1319 | Flags |= 2; |
1320 | break; |
1321 | case ClrHandlerType::Fault: |
1322 | Flags |= 4; |
1323 | break; |
1324 | } |
1325 | if (Clause.EnclosingState != MinClauseMap[Clause.State]) { |
1326 | // This is a "duplicate" clause; the handler needs to be entered from a |
1327 | // frame above the one holding the invoke. |
1328 | assert(Clause.EnclosingState > MinClauseMap[Clause.State]); |
1329 | Flags |= 8; |
1330 | } |
1331 | OS.emitInt32(Value: Flags); |
1332 | |
1333 | // Write the clause start/end |
1334 | OS.emitValue(Value: ClauseBegin, Size: 4); |
1335 | OS.emitValue(Value: ClauseEnd, Size: 4); |
1336 | |
1337 | // Write out the handler start/end |
1338 | OS.emitValue(Value: HandlerBegin, Size: 4); |
1339 | OS.emitValue(Value: HandlerEnd, Size: 4); |
1340 | |
1341 | // Write out the type token or filter offset |
1342 | assert(Entry.HandlerType != ClrHandlerType::Filter && "NYI: filters" ); |
1343 | OS.emitInt32(Value: Entry.TypeToken); |
1344 | } |
1345 | } |
1346 | |