1 | //===- CodeGen/AsmPrinter/EHStreamer.cpp - Exception Directive Streamer ---===// |
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 exception info into assembly files. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "EHStreamer.h" |
14 | #include "llvm/ADT/SmallVector.h" |
15 | #include "llvm/ADT/Twine.h" |
16 | #include "llvm/BinaryFormat/Dwarf.h" |
17 | #include "llvm/CodeGen/AsmPrinter.h" |
18 | #include "llvm/CodeGen/MachineFunction.h" |
19 | #include "llvm/CodeGen/MachineInstr.h" |
20 | #include "llvm/CodeGen/MachineOperand.h" |
21 | #include "llvm/IR/Function.h" |
22 | #include "llvm/MC/MCAsmInfo.h" |
23 | #include "llvm/MC/MCContext.h" |
24 | #include "llvm/MC/MCStreamer.h" |
25 | #include "llvm/MC/MCSymbol.h" |
26 | #include "llvm/MC/MCTargetOptions.h" |
27 | #include "llvm/Support/Casting.h" |
28 | #include "llvm/Support/LEB128.h" |
29 | #include "llvm/Target/TargetLoweringObjectFile.h" |
30 | #include <algorithm> |
31 | #include <cassert> |
32 | #include <cstdint> |
33 | #include <vector> |
34 | |
35 | using namespace llvm; |
36 | |
37 | EHStreamer::EHStreamer(AsmPrinter *A) : Asm(A), MMI(Asm->MMI) {} |
38 | |
39 | EHStreamer::~EHStreamer() = default; |
40 | |
41 | /// How many leading type ids two landing pads have in common. |
42 | unsigned EHStreamer::sharedTypeIDs(const LandingPadInfo *L, |
43 | const LandingPadInfo *R) { |
44 | const std::vector<int> &LIds = L->TypeIds, &RIds = R->TypeIds; |
45 | return std::mismatch(first1: LIds.begin(), last1: LIds.end(), first2: RIds.begin(), last2: RIds.end()) |
46 | .first - |
47 | LIds.begin(); |
48 | } |
49 | |
50 | /// Compute the actions table and gather the first action index for each landing |
51 | /// pad site. |
52 | void EHStreamer::computeActionsTable( |
53 | const SmallVectorImpl<const LandingPadInfo *> &LandingPads, |
54 | SmallVectorImpl<ActionEntry> &Actions, |
55 | SmallVectorImpl<unsigned> &FirstActions) { |
56 | // The action table follows the call-site table in the LSDA. The individual |
57 | // records are of two types: |
58 | // |
59 | // * Catch clause |
60 | // * Exception specification |
61 | // |
62 | // The two record kinds have the same format, with only small differences. |
63 | // They are distinguished by the "switch value" field: Catch clauses |
64 | // (TypeInfos) have strictly positive switch values, and exception |
65 | // specifications (FilterIds) have strictly negative switch values. Value 0 |
66 | // indicates a catch-all clause. |
67 | // |
68 | // Negative type IDs index into FilterIds. Positive type IDs index into |
69 | // TypeInfos. The value written for a positive type ID is just the type ID |
70 | // itself. For a negative type ID, however, the value written is the |
71 | // (negative) byte offset of the corresponding FilterIds entry. The byte |
72 | // offset is usually equal to the type ID (because the FilterIds entries are |
73 | // written using a variable width encoding, which outputs one byte per entry |
74 | // as long as the value written is not too large) but can differ. This kind |
75 | // of complication does not occur for positive type IDs because type infos are |
76 | // output using a fixed width encoding. FilterOffsets[i] holds the byte |
77 | // offset corresponding to FilterIds[i]. |
78 | |
79 | const std::vector<unsigned> &FilterIds = Asm->MF->getFilterIds(); |
80 | SmallVector<int, 16> FilterOffsets; |
81 | FilterOffsets.reserve(N: FilterIds.size()); |
82 | int Offset = -1; |
83 | |
84 | for (unsigned FilterId : FilterIds) { |
85 | FilterOffsets.push_back(Elt: Offset); |
86 | Offset -= getULEB128Size(Value: FilterId); |
87 | } |
88 | |
89 | FirstActions.reserve(N: LandingPads.size()); |
90 | |
91 | int FirstAction = 0; |
92 | unsigned SizeActions = 0; // Total size of all action entries for a function |
93 | const LandingPadInfo *PrevLPI = nullptr; |
94 | |
95 | for (const LandingPadInfo *LPI : LandingPads) { |
96 | const std::vector<int> &TypeIds = LPI->TypeIds; |
97 | unsigned NumShared = PrevLPI ? sharedTypeIDs(L: LPI, R: PrevLPI) : 0; |
98 | unsigned SizeSiteActions = 0; // Total size of all entries for a landingpad |
99 | |
100 | if (NumShared < TypeIds.size()) { |
101 | // Size of one action entry (typeid + next action) |
102 | unsigned SizeActionEntry = 0; |
103 | unsigned PrevAction = (unsigned)-1; |
104 | |
105 | if (NumShared) { |
106 | unsigned SizePrevIds = PrevLPI->TypeIds.size(); |
107 | assert(Actions.size()); |
108 | PrevAction = Actions.size() - 1; |
109 | SizeActionEntry = getSLEB128Size(Value: Actions[PrevAction].NextAction) + |
110 | getSLEB128Size(Value: Actions[PrevAction].ValueForTypeID); |
111 | |
112 | for (unsigned j = NumShared; j != SizePrevIds; ++j) { |
113 | assert(PrevAction != (unsigned)-1 && "PrevAction is invalid!" ); |
114 | SizeActionEntry -= getSLEB128Size(Value: Actions[PrevAction].ValueForTypeID); |
115 | SizeActionEntry += -Actions[PrevAction].NextAction; |
116 | PrevAction = Actions[PrevAction].Previous; |
117 | } |
118 | } |
119 | |
120 | // Compute the actions. |
121 | for (unsigned J = NumShared, M = TypeIds.size(); J != M; ++J) { |
122 | int TypeID = TypeIds[J]; |
123 | assert(-1 - TypeID < (int)FilterOffsets.size() && "Unknown filter id!" ); |
124 | int ValueForTypeID = |
125 | isFilterEHSelector(Selector: TypeID) ? FilterOffsets[-1 - TypeID] : TypeID; |
126 | unsigned SizeTypeID = getSLEB128Size(Value: ValueForTypeID); |
127 | |
128 | int NextAction = SizeActionEntry ? -(SizeActionEntry + SizeTypeID) : 0; |
129 | SizeActionEntry = SizeTypeID + getSLEB128Size(Value: NextAction); |
130 | SizeSiteActions += SizeActionEntry; |
131 | |
132 | ActionEntry Action = { .ValueForTypeID: ValueForTypeID, .NextAction: NextAction, .Previous: PrevAction }; |
133 | Actions.push_back(Elt: Action); |
134 | PrevAction = Actions.size() - 1; |
135 | } |
136 | |
137 | // Record the first action of the landing pad site. |
138 | FirstAction = SizeActions + SizeSiteActions - SizeActionEntry + 1; |
139 | } // else identical - re-use previous FirstAction |
140 | |
141 | // Information used when creating the call-site table. The action record |
142 | // field of the call site record is the offset of the first associated |
143 | // action record, relative to the start of the actions table. This value is |
144 | // biased by 1 (1 indicating the start of the actions table), and 0 |
145 | // indicates that there are no actions. |
146 | FirstActions.push_back(Elt: FirstAction); |
147 | |
148 | // Compute this sites contribution to size. |
149 | SizeActions += SizeSiteActions; |
150 | |
151 | PrevLPI = LPI; |
152 | } |
153 | } |
154 | |
155 | /// Return `true' if this is a call to a function marked `nounwind'. Return |
156 | /// `false' otherwise. |
157 | bool EHStreamer::callToNoUnwindFunction(const MachineInstr *MI) { |
158 | assert(MI->isCall() && "This should be a call instruction!" ); |
159 | |
160 | bool MarkedNoUnwind = false; |
161 | bool SawFunc = false; |
162 | |
163 | for (const MachineOperand &MO : MI->operands()) { |
164 | if (!MO.isGlobal()) continue; |
165 | |
166 | const Function *F = dyn_cast<Function>(Val: MO.getGlobal()); |
167 | if (!F) continue; |
168 | |
169 | if (SawFunc) { |
170 | // Be conservative. If we have more than one function operand for this |
171 | // call, then we can't make the assumption that it's the callee and |
172 | // not a parameter to the call. |
173 | // |
174 | // FIXME: Determine if there's a way to say that `F' is the callee or |
175 | // parameter. |
176 | MarkedNoUnwind = false; |
177 | break; |
178 | } |
179 | |
180 | MarkedNoUnwind = F->doesNotThrow(); |
181 | SawFunc = true; |
182 | } |
183 | |
184 | return MarkedNoUnwind; |
185 | } |
186 | |
187 | void EHStreamer::computePadMap( |
188 | const SmallVectorImpl<const LandingPadInfo *> &LandingPads, |
189 | RangeMapType &PadMap) { |
190 | // Invokes and nounwind calls have entries in PadMap (due to being bracketed |
191 | // by try-range labels when lowered). Ordinary calls do not, so appropriate |
192 | // try-ranges for them need be deduced so we can put them in the LSDA. |
193 | for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { |
194 | const LandingPadInfo *LandingPad = LandingPads[i]; |
195 | for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { |
196 | MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; |
197 | MCSymbol *EndLabel = LandingPad->BeginLabels[j]; |
198 | // If we have deleted the code for a given invoke after registering it in |
199 | // the LandingPad label list, the associated symbols will not have been |
200 | // emitted. In that case, ignore this callsite entry. |
201 | if (!BeginLabel->isDefined() || !EndLabel->isDefined()) |
202 | continue; |
203 | assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!" ); |
204 | PadRange P = { .PadIndex: i, .RangeIndex: j }; |
205 | PadMap[BeginLabel] = P; |
206 | } |
207 | } |
208 | } |
209 | |
210 | /// Compute the call-site table. The entry for an invoke has a try-range |
211 | /// containing the call, a non-zero landing pad, and an appropriate action. The |
212 | /// entry for an ordinary call has a try-range containing the call and zero for |
213 | /// the landing pad and the action. Calls marked 'nounwind' have no entry and |
214 | /// must not be contained in the try-range of any entry - they form gaps in the |
215 | /// table. Entries must be ordered by try-range address. |
216 | /// |
217 | /// Call-sites are split into one or more call-site ranges associated with |
218 | /// different sections of the function. |
219 | /// |
220 | /// - Without -basic-block-sections, all call-sites are grouped into one |
221 | /// call-site-range corresponding to the function section. |
222 | /// |
223 | /// - With -basic-block-sections, one call-site range is created for each |
224 | /// section, with its FragmentBeginLabel and FragmentEndLabel respectively |
225 | // set to the beginning and ending of the corresponding section and its |
226 | // ExceptionLabel set to the exception symbol dedicated for this section. |
227 | // Later, one LSDA header will be emitted for each call-site range with its |
228 | // call-sites following. The action table and type info table will be |
229 | // shared across all ranges. |
230 | void EHStreamer::computeCallSiteTable( |
231 | SmallVectorImpl<CallSiteEntry> &CallSites, |
232 | SmallVectorImpl<CallSiteRange> &CallSiteRanges, |
233 | const SmallVectorImpl<const LandingPadInfo *> &LandingPads, |
234 | const SmallVectorImpl<unsigned> &FirstActions) { |
235 | RangeMapType PadMap; |
236 | computePadMap(LandingPads, PadMap); |
237 | |
238 | // The end label of the previous invoke or nounwind try-range. |
239 | MCSymbol *LastLabel = Asm->getFunctionBegin(); |
240 | |
241 | // Whether there is a potentially throwing instruction (currently this means |
242 | // an ordinary call) between the end of the previous try-range and now. |
243 | bool SawPotentiallyThrowing = false; |
244 | |
245 | // Whether the last CallSite entry was for an invoke. |
246 | bool PreviousIsInvoke = false; |
247 | |
248 | bool IsSJLJ = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::SjLj; |
249 | |
250 | // Visit all instructions in order of address. |
251 | for (const auto &MBB : *Asm->MF) { |
252 | if (&MBB == &Asm->MF->front() || MBB.isBeginSection()) { |
253 | // We start a call-site range upon function entry and at the beginning of |
254 | // every basic block section. |
255 | CallSiteRanges.push_back( |
256 | Elt: {.FragmentBeginLabel: Asm->MBBSectionRanges[MBB.getSectionID()].BeginLabel, |
257 | .FragmentEndLabel: Asm->MBBSectionRanges[MBB.getSectionID()].EndLabel, |
258 | .ExceptionLabel: Asm->getMBBExceptionSym(MBB), .CallSiteBeginIdx: CallSites.size()}); |
259 | PreviousIsInvoke = false; |
260 | SawPotentiallyThrowing = false; |
261 | LastLabel = nullptr; |
262 | } |
263 | |
264 | if (MBB.isEHPad()) |
265 | CallSiteRanges.back().IsLPRange = true; |
266 | |
267 | for (const auto &MI : MBB) { |
268 | if (!MI.isEHLabel()) { |
269 | if (MI.isCall()) |
270 | SawPotentiallyThrowing |= !callToNoUnwindFunction(MI: &MI); |
271 | continue; |
272 | } |
273 | |
274 | // End of the previous try-range? |
275 | MCSymbol *BeginLabel = MI.getOperand(i: 0).getMCSymbol(); |
276 | if (BeginLabel == LastLabel) |
277 | SawPotentiallyThrowing = false; |
278 | |
279 | // Beginning of a new try-range? |
280 | RangeMapType::const_iterator L = PadMap.find(Val: BeginLabel); |
281 | if (L == PadMap.end()) |
282 | // Nope, it was just some random label. |
283 | continue; |
284 | |
285 | const PadRange &P = L->second; |
286 | const LandingPadInfo *LandingPad = LandingPads[P.PadIndex]; |
287 | assert(BeginLabel == LandingPad->BeginLabels[P.RangeIndex] && |
288 | "Inconsistent landing pad map!" ); |
289 | |
290 | // For Dwarf and AIX exception handling (SjLj handling doesn't use this). |
291 | // If some instruction between the previous try-range and this one may |
292 | // throw, create a call-site entry with no landing pad for the region |
293 | // between the try-ranges. |
294 | if (SawPotentiallyThrowing && |
295 | (Asm->MAI->usesCFIForEH() || |
296 | Asm->MAI->getExceptionHandlingType() == ExceptionHandling::AIX)) { |
297 | CallSites.push_back(Elt: {.BeginLabel: LastLabel, .EndLabel: BeginLabel, .LPad: nullptr, .Action: 0}); |
298 | PreviousIsInvoke = false; |
299 | } |
300 | |
301 | LastLabel = LandingPad->EndLabels[P.RangeIndex]; |
302 | assert(BeginLabel && LastLabel && "Invalid landing pad!" ); |
303 | |
304 | if (!LandingPad->LandingPadLabel) { |
305 | // Create a gap. |
306 | PreviousIsInvoke = false; |
307 | } else { |
308 | // This try-range is for an invoke. |
309 | CallSiteEntry Site = { |
310 | .BeginLabel: BeginLabel, |
311 | .EndLabel: LastLabel, |
312 | .LPad: LandingPad, |
313 | .Action: FirstActions[P.PadIndex] |
314 | }; |
315 | |
316 | // Try to merge with the previous call-site. SJLJ doesn't do this |
317 | if (PreviousIsInvoke && !IsSJLJ) { |
318 | CallSiteEntry &Prev = CallSites.back(); |
319 | if (Site.LPad == Prev.LPad && Site.Action == Prev.Action) { |
320 | // Extend the range of the previous entry. |
321 | Prev.EndLabel = Site.EndLabel; |
322 | continue; |
323 | } |
324 | } |
325 | |
326 | // Otherwise, create a new call-site. |
327 | if (!IsSJLJ) |
328 | CallSites.push_back(Elt: Site); |
329 | else { |
330 | // SjLj EH must maintain the call sites in the order assigned |
331 | // to them by the SjLjPrepare pass. |
332 | unsigned SiteNo = Asm->MF->getCallSiteBeginLabel(BeginLabel); |
333 | if (CallSites.size() < SiteNo) |
334 | CallSites.resize(N: SiteNo); |
335 | CallSites[SiteNo - 1] = Site; |
336 | } |
337 | PreviousIsInvoke = true; |
338 | } |
339 | } |
340 | |
341 | // We end the call-site range upon function exit and at the end of every |
342 | // basic block section. |
343 | if (&MBB == &Asm->MF->back() || MBB.isEndSection()) { |
344 | // If some instruction between the previous try-range and the end of the |
345 | // function may throw, create a call-site entry with no landing pad for |
346 | // the region following the try-range. |
347 | if (SawPotentiallyThrowing && !IsSJLJ) { |
348 | CallSiteEntry Site = {.BeginLabel: LastLabel, .EndLabel: CallSiteRanges.back().FragmentEndLabel, |
349 | .LPad: nullptr, .Action: 0}; |
350 | CallSites.push_back(Elt: Site); |
351 | SawPotentiallyThrowing = false; |
352 | } |
353 | CallSiteRanges.back().CallSiteEndIdx = CallSites.size(); |
354 | } |
355 | } |
356 | } |
357 | |
358 | /// Emit landing pads and actions. |
359 | /// |
360 | /// The general organization of the table is complex, but the basic concepts are |
361 | /// easy. First there is a header which describes the location and organization |
362 | /// of the three components that follow. |
363 | /// |
364 | /// 1. The landing pad site information describes the range of code covered by |
365 | /// the try. In our case it's an accumulation of the ranges covered by the |
366 | /// invokes in the try. There is also a reference to the landing pad that |
367 | /// handles the exception once processed. Finally an index into the actions |
368 | /// table. |
369 | /// 2. The action table, in our case, is composed of pairs of type IDs and next |
370 | /// action offset. Starting with the action index from the landing pad |
371 | /// site, each type ID is checked for a match to the current exception. If |
372 | /// it matches then the exception and type id are passed on to the landing |
373 | /// pad. Otherwise the next action is looked up. This chain is terminated |
374 | /// with a next action of zero. If no type id is found then the frame is |
375 | /// unwound and handling continues. |
376 | /// 3. Type ID table contains references to all the C++ typeinfo for all |
377 | /// catches in the function. This tables is reverse indexed base 1. |
378 | /// |
379 | /// Returns the starting symbol of an exception table. |
380 | MCSymbol *EHStreamer::emitExceptionTable() { |
381 | const MachineFunction *MF = Asm->MF; |
382 | const std::vector<const GlobalValue *> &TypeInfos = MF->getTypeInfos(); |
383 | const std::vector<unsigned> &FilterIds = MF->getFilterIds(); |
384 | const std::vector<LandingPadInfo> &PadInfos = MF->getLandingPads(); |
385 | |
386 | // Sort the landing pads in order of their type ids. This is used to fold |
387 | // duplicate actions. |
388 | SmallVector<const LandingPadInfo *, 64> LandingPads; |
389 | LandingPads.reserve(N: PadInfos.size()); |
390 | |
391 | for (const LandingPadInfo &LPI : PadInfos) { |
392 | // If a landing-pad has an associated label, but the label wasn't ever |
393 | // emitted, then skip it. (This can occur if the landingpad's MBB was |
394 | // deleted). |
395 | if (LPI.LandingPadLabel && !LPI.LandingPadLabel->isDefined()) |
396 | continue; |
397 | LandingPads.push_back(Elt: &LPI); |
398 | } |
399 | |
400 | // Order landing pads lexicographically by type id. |
401 | llvm::sort(C&: LandingPads, Comp: [](const LandingPadInfo *L, const LandingPadInfo *R) { |
402 | return L->TypeIds < R->TypeIds; |
403 | }); |
404 | |
405 | // Compute the actions table and gather the first action index for each |
406 | // landing pad site. |
407 | SmallVector<ActionEntry, 32> Actions; |
408 | SmallVector<unsigned, 64> FirstActions; |
409 | computeActionsTable(LandingPads, Actions, FirstActions); |
410 | |
411 | // Compute the call-site table and call-site ranges. Normally, there is only |
412 | // one call-site-range which covers the whole function. With |
413 | // -basic-block-sections, there is one call-site-range per basic block |
414 | // section. |
415 | SmallVector<CallSiteEntry, 64> CallSites; |
416 | SmallVector<CallSiteRange, 4> CallSiteRanges; |
417 | computeCallSiteTable(CallSites, CallSiteRanges, LandingPads, FirstActions); |
418 | |
419 | bool IsSJLJ = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::SjLj; |
420 | bool IsWasm = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::Wasm; |
421 | bool HasLEB128Directives = Asm->MAI->hasLEB128Directives(); |
422 | unsigned CallSiteEncoding = |
423 | IsSJLJ ? static_cast<unsigned>(dwarf::DW_EH_PE_udata4) : |
424 | Asm->getObjFileLowering().getCallSiteEncoding(); |
425 | bool HaveTTData = !TypeInfos.empty() || !FilterIds.empty(); |
426 | |
427 | // Type infos. |
428 | MCSection *LSDASection = Asm->getObjFileLowering().getSectionForLSDA( |
429 | MF->getFunction(), *Asm->CurrentFnSym, Asm->TM); |
430 | unsigned TTypeEncoding; |
431 | |
432 | if (!HaveTTData) { |
433 | // If there is no TypeInfo, then we just explicitly say that we're omitting |
434 | // that bit. |
435 | TTypeEncoding = dwarf::DW_EH_PE_omit; |
436 | } else { |
437 | // Okay, we have actual filters or typeinfos to emit. As such, we need to |
438 | // pick a type encoding for them. We're about to emit a list of pointers to |
439 | // typeinfo objects at the end of the LSDA. However, unless we're in static |
440 | // mode, this reference will require a relocation by the dynamic linker. |
441 | // |
442 | // Because of this, we have a couple of options: |
443 | // |
444 | // 1) If we are in -static mode, we can always use an absolute reference |
445 | // from the LSDA, because the static linker will resolve it. |
446 | // |
447 | // 2) Otherwise, if the LSDA section is writable, we can output the direct |
448 | // reference to the typeinfo and allow the dynamic linker to relocate |
449 | // it. Since it is in a writable section, the dynamic linker won't |
450 | // have a problem. |
451 | // |
452 | // 3) Finally, if we're in PIC mode and the LDSA section isn't writable, |
453 | // we need to use some form of indirection. For example, on Darwin, |
454 | // we can output a statically-relocatable reference to a dyld stub. The |
455 | // offset to the stub is constant, but the contents are in a section |
456 | // that is updated by the dynamic linker. This is easy enough, but we |
457 | // need to tell the personality function of the unwinder to indirect |
458 | // through the dyld stub. |
459 | // |
460 | // FIXME: When (3) is actually implemented, we'll have to emit the stubs |
461 | // somewhere. This predicate should be moved to a shared location that is |
462 | // in target-independent code. |
463 | // |
464 | TTypeEncoding = Asm->getObjFileLowering().getTTypeEncoding(); |
465 | } |
466 | |
467 | // Begin the exception table. |
468 | // Sometimes we want not to emit the data into separate section (e.g. ARM |
469 | // EHABI). In this case LSDASection will be NULL. |
470 | if (LSDASection) |
471 | Asm->OutStreamer->switchSection(Section: LSDASection); |
472 | Asm->emitAlignment(Alignment: Align(4)); |
473 | |
474 | // Emit the LSDA. |
475 | MCSymbol *GCCETSym = |
476 | Asm->OutContext.getOrCreateSymbol(Name: Twine("GCC_except_table" )+ |
477 | Twine(Asm->getFunctionNumber())); |
478 | Asm->OutStreamer->emitLabel(Symbol: GCCETSym); |
479 | MCSymbol *CstEndLabel = Asm->createTempSymbol( |
480 | Name: CallSiteRanges.size() > 1 ? "action_table_base" : "cst_end" ); |
481 | |
482 | MCSymbol *TTBaseLabel = nullptr; |
483 | if (HaveTTData) |
484 | TTBaseLabel = Asm->createTempSymbol(Name: "ttbase" ); |
485 | |
486 | const bool VerboseAsm = Asm->OutStreamer->isVerboseAsm(); |
487 | |
488 | // Helper for emitting references (offsets) for type table and the end of the |
489 | // call-site table (which marks the beginning of the action table). |
490 | // * For Itanium, these references will be emitted for every callsite range. |
491 | // * For SJLJ and Wasm, they will be emitted only once in the LSDA header. |
492 | auto EmitTypeTableRefAndCallSiteTableEndRef = [&]() { |
493 | Asm->emitEncodingByte(Val: TTypeEncoding, Desc: "@TType" ); |
494 | if (HaveTTData) { |
495 | // N.B.: There is a dependency loop between the size of the TTBase uleb128 |
496 | // here and the amount of padding before the aligned type table. The |
497 | // assembler must sometimes pad this uleb128 or insert extra padding |
498 | // before the type table. See PR35809 or GNU as bug 4029. |
499 | MCSymbol *TTBaseRefLabel = Asm->createTempSymbol(Name: "ttbaseref" ); |
500 | Asm->emitLabelDifferenceAsULEB128(Hi: TTBaseLabel, Lo: TTBaseRefLabel); |
501 | Asm->OutStreamer->emitLabel(Symbol: TTBaseRefLabel); |
502 | } |
503 | |
504 | // The Action table follows the call-site table. So we emit the |
505 | // label difference from here (start of the call-site table for SJLJ and |
506 | // Wasm, and start of a call-site range for Itanium) to the end of the |
507 | // whole call-site table (end of the last call-site range for Itanium). |
508 | MCSymbol *CstBeginLabel = Asm->createTempSymbol(Name: "cst_begin" ); |
509 | Asm->emitEncodingByte(Val: CallSiteEncoding, Desc: "Call site" ); |
510 | Asm->emitLabelDifferenceAsULEB128(Hi: CstEndLabel, Lo: CstBeginLabel); |
511 | Asm->OutStreamer->emitLabel(Symbol: CstBeginLabel); |
512 | }; |
513 | |
514 | // An alternative path to EmitTypeTableRefAndCallSiteTableEndRef. |
515 | // For some platforms, the system assembler does not accept the form of |
516 | // `.uleb128 label2 - label1`. In those situations, we would need to calculate |
517 | // the size between label1 and label2 manually. |
518 | // In this case, we would need to calculate the LSDA size and the call |
519 | // site table size. |
520 | auto EmitTypeTableOffsetAndCallSiteTableOffset = [&]() { |
521 | assert(CallSiteEncoding == dwarf::DW_EH_PE_udata4 && !HasLEB128Directives && |
522 | "Targets supporting .uleb128 do not need to take this path." ); |
523 | if (CallSiteRanges.size() > 1) |
524 | report_fatal_error( |
525 | reason: "-fbasic-block-sections is not yet supported on " |
526 | "platforms that do not have general LEB128 directive support." ); |
527 | |
528 | uint64_t CallSiteTableSize = 0; |
529 | const CallSiteRange &CSRange = CallSiteRanges.back(); |
530 | for (size_t CallSiteIdx = CSRange.CallSiteBeginIdx; |
531 | CallSiteIdx < CSRange.CallSiteEndIdx; ++CallSiteIdx) { |
532 | const CallSiteEntry &S = CallSites[CallSiteIdx]; |
533 | // Each call site entry consists of 3 udata4 fields (12 bytes) and |
534 | // 1 ULEB128 field. |
535 | CallSiteTableSize += 12 + getULEB128Size(Value: S.Action); |
536 | assert(isUInt<32>(CallSiteTableSize) && "CallSiteTableSize overflows." ); |
537 | } |
538 | |
539 | Asm->emitEncodingByte(Val: TTypeEncoding, Desc: "@TType" ); |
540 | if (HaveTTData) { |
541 | const unsigned ByteSizeOfCallSiteOffset = |
542 | getULEB128Size(Value: CallSiteTableSize); |
543 | uint64_t ActionTableSize = 0; |
544 | for (const ActionEntry &Action : Actions) { |
545 | // Each action entry consists of two SLEB128 fields. |
546 | ActionTableSize += getSLEB128Size(Value: Action.ValueForTypeID) + |
547 | getSLEB128Size(Value: Action.NextAction); |
548 | assert(isUInt<32>(ActionTableSize) && "ActionTableSize overflows." ); |
549 | } |
550 | |
551 | const unsigned TypeInfoSize = |
552 | Asm->GetSizeOfEncodedValue(Encoding: TTypeEncoding) * MF->getTypeInfos().size(); |
553 | |
554 | const uint64_t LSDASizeBeforeAlign = |
555 | 1 // Call site encoding byte. |
556 | + ByteSizeOfCallSiteOffset // ULEB128 encoding of CallSiteTableSize. |
557 | + CallSiteTableSize // Call site table content. |
558 | + ActionTableSize; // Action table content. |
559 | |
560 | const uint64_t LSDASizeWithoutAlign = LSDASizeBeforeAlign + TypeInfoSize; |
561 | const unsigned ByteSizeOfLSDAWithoutAlign = |
562 | getULEB128Size(Value: LSDASizeWithoutAlign); |
563 | const uint64_t DisplacementBeforeAlign = |
564 | 2 // LPStartEncoding and TypeTableEncoding. |
565 | + ByteSizeOfLSDAWithoutAlign + LSDASizeBeforeAlign; |
566 | |
567 | // The type info area starts with 4 byte alignment. |
568 | const unsigned NeedAlignVal = (4 - DisplacementBeforeAlign % 4) % 4; |
569 | uint64_t LSDASizeWithAlign = LSDASizeWithoutAlign + NeedAlignVal; |
570 | const unsigned ByteSizeOfLSDAWithAlign = |
571 | getULEB128Size(Value: LSDASizeWithAlign); |
572 | |
573 | // The LSDASizeWithAlign could use 1 byte less padding for alignment |
574 | // when the data we use to represent the LSDA Size "needs" to be 1 byte |
575 | // larger than the one previously calculated without alignment. |
576 | if (ByteSizeOfLSDAWithAlign > ByteSizeOfLSDAWithoutAlign) |
577 | LSDASizeWithAlign -= 1; |
578 | |
579 | Asm->OutStreamer->emitULEB128IntValue(Value: LSDASizeWithAlign, |
580 | PadTo: ByteSizeOfLSDAWithAlign); |
581 | } |
582 | |
583 | Asm->emitEncodingByte(Val: CallSiteEncoding, Desc: "Call site" ); |
584 | Asm->OutStreamer->emitULEB128IntValue(Value: CallSiteTableSize); |
585 | }; |
586 | |
587 | // SjLj / Wasm Exception handling |
588 | if (IsSJLJ || IsWasm) { |
589 | Asm->OutStreamer->emitLabel(Symbol: Asm->getMBBExceptionSym(MBB: Asm->MF->front())); |
590 | |
591 | // emit the LSDA header. |
592 | Asm->emitEncodingByte(Val: dwarf::DW_EH_PE_omit, Desc: "@LPStart" ); |
593 | EmitTypeTableRefAndCallSiteTableEndRef(); |
594 | |
595 | unsigned idx = 0; |
596 | for (SmallVectorImpl<CallSiteEntry>::const_iterator |
597 | I = CallSites.begin(), E = CallSites.end(); I != E; ++I, ++idx) { |
598 | const CallSiteEntry &S = *I; |
599 | |
600 | // Index of the call site entry. |
601 | if (VerboseAsm) { |
602 | Asm->OutStreamer->AddComment(T: ">> Call Site " + Twine(idx) + " <<" ); |
603 | Asm->OutStreamer->AddComment(T: " On exception at call site " +Twine(idx)); |
604 | } |
605 | Asm->emitULEB128(Value: idx); |
606 | |
607 | // Offset of the first associated action record, relative to the start of |
608 | // the action table. This value is biased by 1 (1 indicates the start of |
609 | // the action table), and 0 indicates that there are no actions. |
610 | if (VerboseAsm) { |
611 | if (S.Action == 0) |
612 | Asm->OutStreamer->AddComment(T: " Action: cleanup" ); |
613 | else |
614 | Asm->OutStreamer->AddComment(T: " Action: " + |
615 | Twine((S.Action - 1) / 2 + 1)); |
616 | } |
617 | Asm->emitULEB128(Value: S.Action); |
618 | } |
619 | Asm->OutStreamer->emitLabel(Symbol: CstEndLabel); |
620 | } else { |
621 | // Itanium LSDA exception handling |
622 | |
623 | // The call-site table is a list of all call sites that may throw an |
624 | // exception (including C++ 'throw' statements) in the procedure |
625 | // fragment. It immediately follows the LSDA header. Each entry indicates, |
626 | // for a given call, the first corresponding action record and corresponding |
627 | // landing pad. |
628 | // |
629 | // The table begins with the number of bytes, stored as an LEB128 |
630 | // compressed, unsigned integer. The records immediately follow the record |
631 | // count. They are sorted in increasing call-site address. Each record |
632 | // indicates: |
633 | // |
634 | // * The position of the call-site. |
635 | // * The position of the landing pad. |
636 | // * The first action record for that call site. |
637 | // |
638 | // A missing entry in the call-site table indicates that a call is not |
639 | // supposed to throw. |
640 | |
641 | assert(CallSiteRanges.size() != 0 && "No call-site ranges!" ); |
642 | |
643 | // There should be only one call-site range which includes all the landing |
644 | // pads. Find that call-site range here. |
645 | const CallSiteRange *LandingPadRange = nullptr; |
646 | for (const CallSiteRange &CSRange : CallSiteRanges) { |
647 | if (CSRange.IsLPRange) { |
648 | assert(LandingPadRange == nullptr && |
649 | "All landing pads must be in a single callsite range." ); |
650 | LandingPadRange = &CSRange; |
651 | } |
652 | } |
653 | |
654 | // The call-site table is split into its call-site ranges, each being |
655 | // emitted as: |
656 | // [ LPStartEncoding | LPStart ] |
657 | // [ TypeTableEncoding | TypeTableOffset ] |
658 | // [ CallSiteEncoding | CallSiteTableEndOffset ] |
659 | // cst_begin -> { call-site entries contained in this range } |
660 | // |
661 | // and is followed by the next call-site range. |
662 | // |
663 | // For each call-site range, CallSiteTableEndOffset is computed as the |
664 | // difference between cst_begin of that range and the last call-site-table's |
665 | // end label. This offset is used to find the action table. |
666 | |
667 | unsigned Entry = 0; |
668 | for (const CallSiteRange &CSRange : CallSiteRanges) { |
669 | if (CSRange.CallSiteBeginIdx != 0) { |
670 | // Align the call-site range for all ranges except the first. The |
671 | // first range is already aligned due to the exception table alignment. |
672 | Asm->emitAlignment(Alignment: Align(4)); |
673 | } |
674 | Asm->OutStreamer->emitLabel(Symbol: CSRange.ExceptionLabel); |
675 | |
676 | // Emit the LSDA header. |
677 | // LPStart is omitted if either we have a single call-site range (in which |
678 | // case the function entry is treated as @LPStart) or if this function has |
679 | // no landing pads (in which case @LPStart is undefined). |
680 | if (CallSiteRanges.size() == 1 || LandingPadRange == nullptr) { |
681 | Asm->emitEncodingByte(Val: dwarf::DW_EH_PE_omit, Desc: "@LPStart" ); |
682 | } else if (!Asm->isPositionIndependent()) { |
683 | // For more than one call-site ranges, LPStart must be explicitly |
684 | // specified. |
685 | // For non-PIC we can simply use the absolute value. |
686 | Asm->emitEncodingByte(Val: dwarf::DW_EH_PE_absptr, Desc: "@LPStart" ); |
687 | Asm->OutStreamer->emitSymbolValue(Sym: LandingPadRange->FragmentBeginLabel, |
688 | Size: Asm->MAI->getCodePointerSize()); |
689 | } else { |
690 | // For PIC mode, we Emit a PC-relative address for LPStart. |
691 | Asm->emitEncodingByte(Val: dwarf::DW_EH_PE_pcrel, Desc: "@LPStart" ); |
692 | MCContext &Context = Asm->OutStreamer->getContext(); |
693 | MCSymbol *Dot = Context.createTempSymbol(); |
694 | Asm->OutStreamer->emitLabel(Symbol: Dot); |
695 | Asm->OutStreamer->emitValue( |
696 | Value: MCBinaryExpr::createSub( |
697 | LHS: MCSymbolRefExpr::create(Symbol: LandingPadRange->FragmentBeginLabel, |
698 | Ctx&: Context), |
699 | RHS: MCSymbolRefExpr::create(Symbol: Dot, Ctx&: Context), Ctx&: Context), |
700 | Size: Asm->MAI->getCodePointerSize()); |
701 | } |
702 | |
703 | if (HasLEB128Directives) |
704 | EmitTypeTableRefAndCallSiteTableEndRef(); |
705 | else |
706 | EmitTypeTableOffsetAndCallSiteTableOffset(); |
707 | |
708 | for (size_t CallSiteIdx = CSRange.CallSiteBeginIdx; |
709 | CallSiteIdx != CSRange.CallSiteEndIdx; ++CallSiteIdx) { |
710 | const CallSiteEntry &S = CallSites[CallSiteIdx]; |
711 | |
712 | MCSymbol *EHFuncBeginSym = CSRange.FragmentBeginLabel; |
713 | MCSymbol *EHFuncEndSym = CSRange.FragmentEndLabel; |
714 | |
715 | MCSymbol *BeginLabel = S.BeginLabel; |
716 | if (!BeginLabel) |
717 | BeginLabel = EHFuncBeginSym; |
718 | MCSymbol *EndLabel = S.EndLabel; |
719 | if (!EndLabel) |
720 | EndLabel = EHFuncEndSym; |
721 | |
722 | // Offset of the call site relative to the start of the procedure. |
723 | if (VerboseAsm) |
724 | Asm->OutStreamer->AddComment(T: ">> Call Site " + Twine(++Entry) + |
725 | " <<" ); |
726 | Asm->emitCallSiteOffset(Hi: BeginLabel, Lo: EHFuncBeginSym, Encoding: CallSiteEncoding); |
727 | if (VerboseAsm) |
728 | Asm->OutStreamer->AddComment(T: Twine(" Call between " ) + |
729 | BeginLabel->getName() + " and " + |
730 | EndLabel->getName()); |
731 | Asm->emitCallSiteOffset(Hi: EndLabel, Lo: BeginLabel, Encoding: CallSiteEncoding); |
732 | |
733 | // Offset of the landing pad relative to the start of the landing pad |
734 | // fragment. |
735 | if (!S.LPad) { |
736 | if (VerboseAsm) |
737 | Asm->OutStreamer->AddComment(T: " has no landing pad" ); |
738 | Asm->emitCallSiteValue(Value: 0, Encoding: CallSiteEncoding); |
739 | } else { |
740 | if (VerboseAsm) |
741 | Asm->OutStreamer->AddComment(T: Twine(" jumps to " ) + |
742 | S.LPad->LandingPadLabel->getName()); |
743 | Asm->emitCallSiteOffset(Hi: S.LPad->LandingPadLabel, |
744 | Lo: LandingPadRange->FragmentBeginLabel, |
745 | Encoding: CallSiteEncoding); |
746 | } |
747 | |
748 | // Offset of the first associated action record, relative to the start |
749 | // of the action table. This value is biased by 1 (1 indicates the start |
750 | // of the action table), and 0 indicates that there are no actions. |
751 | if (VerboseAsm) { |
752 | if (S.Action == 0) |
753 | Asm->OutStreamer->AddComment(T: " On action: cleanup" ); |
754 | else |
755 | Asm->OutStreamer->AddComment(T: " On action: " + |
756 | Twine((S.Action - 1) / 2 + 1)); |
757 | } |
758 | Asm->emitULEB128(Value: S.Action); |
759 | } |
760 | } |
761 | Asm->OutStreamer->emitLabel(Symbol: CstEndLabel); |
762 | } |
763 | |
764 | // Emit the Action Table. |
765 | int Entry = 0; |
766 | for (const ActionEntry &Action : Actions) { |
767 | if (VerboseAsm) { |
768 | // Emit comments that decode the action table. |
769 | Asm->OutStreamer->AddComment(T: ">> Action Record " + Twine(++Entry) + " <<" ); |
770 | } |
771 | |
772 | // Type Filter |
773 | // |
774 | // Used by the runtime to match the type of the thrown exception to the |
775 | // type of the catch clauses or the types in the exception specification. |
776 | if (VerboseAsm) { |
777 | if (Action.ValueForTypeID > 0) |
778 | Asm->OutStreamer->AddComment(T: " Catch TypeInfo " + |
779 | Twine(Action.ValueForTypeID)); |
780 | else if (Action.ValueForTypeID < 0) |
781 | Asm->OutStreamer->AddComment(T: " Filter TypeInfo " + |
782 | Twine(Action.ValueForTypeID)); |
783 | else |
784 | Asm->OutStreamer->AddComment(T: " Cleanup" ); |
785 | } |
786 | Asm->emitSLEB128(Value: Action.ValueForTypeID); |
787 | |
788 | // Action Record |
789 | if (VerboseAsm) { |
790 | if (Action.Previous == unsigned(-1)) { |
791 | Asm->OutStreamer->AddComment(T: " No further actions" ); |
792 | } else { |
793 | Asm->OutStreamer->AddComment(T: " Continue to action " + |
794 | Twine(Action.Previous + 1)); |
795 | } |
796 | } |
797 | Asm->emitSLEB128(Value: Action.NextAction); |
798 | } |
799 | |
800 | if (HaveTTData) { |
801 | Asm->emitAlignment(Alignment: Align(4)); |
802 | emitTypeInfos(TTypeEncoding, TTBaseLabel); |
803 | } |
804 | |
805 | Asm->emitAlignment(Alignment: Align(4)); |
806 | return GCCETSym; |
807 | } |
808 | |
809 | void EHStreamer::emitTypeInfos(unsigned TTypeEncoding, MCSymbol *TTBaseLabel) { |
810 | const MachineFunction *MF = Asm->MF; |
811 | const std::vector<const GlobalValue *> &TypeInfos = MF->getTypeInfos(); |
812 | const std::vector<unsigned> &FilterIds = MF->getFilterIds(); |
813 | |
814 | const bool VerboseAsm = Asm->OutStreamer->isVerboseAsm(); |
815 | |
816 | int Entry = 0; |
817 | // Emit the Catch TypeInfos. |
818 | if (VerboseAsm && !TypeInfos.empty()) { |
819 | Asm->OutStreamer->AddComment(T: ">> Catch TypeInfos <<" ); |
820 | Asm->OutStreamer->addBlankLine(); |
821 | Entry = TypeInfos.size(); |
822 | } |
823 | |
824 | for (const GlobalValue *GV : llvm::reverse(C: TypeInfos)) { |
825 | if (VerboseAsm) |
826 | Asm->OutStreamer->AddComment(T: "TypeInfo " + Twine(Entry--)); |
827 | Asm->emitTTypeReference(GV, Encoding: TTypeEncoding); |
828 | } |
829 | |
830 | Asm->OutStreamer->emitLabel(Symbol: TTBaseLabel); |
831 | |
832 | // Emit the Exception Specifications. |
833 | if (VerboseAsm && !FilterIds.empty()) { |
834 | Asm->OutStreamer->AddComment(T: ">> Filter TypeInfos <<" ); |
835 | Asm->OutStreamer->addBlankLine(); |
836 | Entry = 0; |
837 | } |
838 | for (std::vector<unsigned>::const_iterator |
839 | I = FilterIds.begin(), E = FilterIds.end(); I < E; ++I) { |
840 | unsigned TypeID = *I; |
841 | if (VerboseAsm) { |
842 | --Entry; |
843 | if (isFilterEHSelector(Selector: TypeID)) |
844 | Asm->OutStreamer->AddComment(T: "FilterInfo " + Twine(Entry)); |
845 | } |
846 | |
847 | Asm->emitULEB128(Value: TypeID); |
848 | } |
849 | } |
850 | |