1 | #include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h" |
2 | #include "llvm/DebugInfo/DWARF/DWARFDie.h" |
3 | #include "llvm/DebugInfo/DWARF/DWARFUnit.h" |
4 | #include "llvm/Support/ScopedPrinter.h" |
5 | namespace llvm { |
6 | using namespace dwarf; |
7 | void DWARFTypePrinter::appendTypeTagName(dwarf::Tag T) { |
8 | StringRef TagStr = TagString(Tag: T); |
9 | static constexpr StringRef Prefix = "DW_TAG_" ; |
10 | static constexpr StringRef Suffix = "_type" ; |
11 | if (!TagStr.starts_with(Prefix) || !TagStr.ends_with(Suffix)) |
12 | return; |
13 | OS << TagStr.substr(Start: Prefix.size(), |
14 | N: TagStr.size() - (Prefix.size() + Suffix.size())) |
15 | << " " ; |
16 | } |
17 | |
18 | void DWARFTypePrinter::appendArrayType(const DWARFDie &D) { |
19 | for (const DWARFDie &C : D.children()) { |
20 | if (C.getTag() != DW_TAG_subrange_type) |
21 | continue; |
22 | std::optional<uint64_t> LB; |
23 | std::optional<uint64_t> Count; |
24 | std::optional<uint64_t> UB; |
25 | std::optional<unsigned> DefaultLB; |
26 | if (std::optional<DWARFFormValue> L = C.find(Attr: DW_AT_lower_bound)) |
27 | LB = L->getAsUnsignedConstant(); |
28 | if (std::optional<DWARFFormValue> CountV = C.find(Attr: DW_AT_count)) |
29 | Count = CountV->getAsUnsignedConstant(); |
30 | if (std::optional<DWARFFormValue> UpperV = C.find(Attr: DW_AT_upper_bound)) |
31 | UB = UpperV->getAsUnsignedConstant(); |
32 | if (std::optional<DWARFFormValue> LV = |
33 | D.getDwarfUnit()->getUnitDIE().find(Attr: DW_AT_language)) |
34 | if (std::optional<uint64_t> LC = LV->getAsUnsignedConstant()) |
35 | if ((DefaultLB = |
36 | LanguageLowerBound(L: static_cast<dwarf::SourceLanguage>(*LC)))) |
37 | if (LB && *LB == *DefaultLB) |
38 | LB = std::nullopt; |
39 | if (!LB && !Count && !UB) |
40 | OS << "[]" ; |
41 | else if (!LB && (Count || UB) && DefaultLB) |
42 | OS << '[' << (Count ? *Count : *UB - *DefaultLB + 1) << ']'; |
43 | else { |
44 | OS << "[[" ; |
45 | if (LB) |
46 | OS << *LB; |
47 | else |
48 | OS << '?'; |
49 | OS << ", " ; |
50 | if (Count) |
51 | if (LB) |
52 | OS << *LB + *Count; |
53 | else |
54 | OS << "? + " << *Count; |
55 | else if (UB) |
56 | OS << *UB + 1; |
57 | else |
58 | OS << '?'; |
59 | OS << ")]" ; |
60 | } |
61 | } |
62 | EndedWithTemplate = false; |
63 | } |
64 | |
65 | static DWARFDie resolveReferencedType(DWARFDie D, |
66 | dwarf::Attribute Attr = DW_AT_type) { |
67 | return D.getAttributeValueAsReferencedDie(Attr).resolveTypeUnitReference(); |
68 | } |
69 | static DWARFDie resolveReferencedType(DWARFDie D, DWARFFormValue F) { |
70 | return D.getAttributeValueAsReferencedDie(V: F).resolveTypeUnitReference(); |
71 | } |
72 | DWARFDie DWARFTypePrinter::skipQualifiers(DWARFDie D) { |
73 | while (D && (D.getTag() == DW_TAG_const_type || |
74 | D.getTag() == DW_TAG_volatile_type)) |
75 | D = resolveReferencedType(D); |
76 | return D; |
77 | } |
78 | |
79 | bool DWARFTypePrinter::needsParens(DWARFDie D) { |
80 | D = skipQualifiers(D); |
81 | return D && (D.getTag() == DW_TAG_subroutine_type || |
82 | D.getTag() == DW_TAG_array_type); |
83 | } |
84 | |
85 | void DWARFTypePrinter::appendPointerLikeTypeBefore(DWARFDie D, DWARFDie Inner, |
86 | StringRef Ptr) { |
87 | appendQualifiedNameBefore(D: Inner); |
88 | if (Word) |
89 | OS << ' '; |
90 | if (needsParens(D: Inner)) |
91 | OS << '('; |
92 | OS << Ptr; |
93 | Word = false; |
94 | EndedWithTemplate = false; |
95 | } |
96 | |
97 | DWARFDie |
98 | DWARFTypePrinter::appendUnqualifiedNameBefore(DWARFDie D, |
99 | std::string *OriginalFullName) { |
100 | Word = true; |
101 | if (!D) { |
102 | OS << "void" ; |
103 | return DWARFDie(); |
104 | } |
105 | DWARFDie InnerDIE; |
106 | auto Inner = [&] { return InnerDIE = resolveReferencedType(D); }; |
107 | const dwarf::Tag T = D.getTag(); |
108 | switch (T) { |
109 | case DW_TAG_pointer_type: { |
110 | appendPointerLikeTypeBefore(D, Inner: Inner(), Ptr: "*" ); |
111 | break; |
112 | } |
113 | case DW_TAG_subroutine_type: { |
114 | appendQualifiedNameBefore(D: Inner()); |
115 | if (Word) { |
116 | OS << ' '; |
117 | } |
118 | Word = false; |
119 | break; |
120 | } |
121 | case DW_TAG_array_type: { |
122 | appendQualifiedNameBefore(D: Inner()); |
123 | break; |
124 | } |
125 | case DW_TAG_reference_type: |
126 | appendPointerLikeTypeBefore(D, Inner: Inner(), Ptr: "&" ); |
127 | break; |
128 | case DW_TAG_rvalue_reference_type: |
129 | appendPointerLikeTypeBefore(D, Inner: Inner(), Ptr: "&&" ); |
130 | break; |
131 | case DW_TAG_ptr_to_member_type: { |
132 | appendQualifiedNameBefore(D: Inner()); |
133 | if (needsParens(D: InnerDIE)) |
134 | OS << '('; |
135 | else if (Word) |
136 | OS << ' '; |
137 | if (DWARFDie Cont = resolveReferencedType(D, Attr: DW_AT_containing_type)) { |
138 | appendQualifiedName(D: Cont); |
139 | EndedWithTemplate = false; |
140 | OS << "::" ; |
141 | } |
142 | OS << "*" ; |
143 | Word = false; |
144 | break; |
145 | } |
146 | case DW_TAG_LLVM_ptrauth_type: |
147 | appendQualifiedNameBefore(D: Inner()); |
148 | break; |
149 | case DW_TAG_const_type: |
150 | case DW_TAG_volatile_type: |
151 | appendConstVolatileQualifierBefore(N: D); |
152 | break; |
153 | case DW_TAG_namespace: { |
154 | if (const char *Name = dwarf::toString(V: D.find(Attr: DW_AT_name), Default: nullptr)) |
155 | OS << Name; |
156 | else |
157 | OS << "(anonymous namespace)" ; |
158 | break; |
159 | } |
160 | case DW_TAG_unspecified_type: { |
161 | StringRef TypeName = D.getShortName(); |
162 | if (TypeName == "decltype(nullptr)" ) |
163 | TypeName = "std::nullptr_t" ; |
164 | Word = true; |
165 | OS << TypeName; |
166 | EndedWithTemplate = false; |
167 | break; |
168 | } |
169 | /* |
170 | case DW_TAG_structure_type: |
171 | case DW_TAG_class_type: |
172 | case DW_TAG_enumeration_type: |
173 | case DW_TAG_base_type: |
174 | */ |
175 | default: { |
176 | const char *NamePtr = dwarf::toString(V: D.find(Attr: DW_AT_name), Default: nullptr); |
177 | if (!NamePtr) { |
178 | appendTypeTagName(T: D.getTag()); |
179 | return DWARFDie(); |
180 | } |
181 | Word = true; |
182 | StringRef Name = NamePtr; |
183 | static constexpr StringRef MangledPrefix = "_STN|" ; |
184 | if (Name.consume_front(Prefix: MangledPrefix)) { |
185 | auto Separator = Name.find(C: '|'); |
186 | assert(Separator != StringRef::npos); |
187 | StringRef BaseName = Name.substr(Start: 0, N: Separator); |
188 | StringRef TemplateArgs = Name.substr(Start: Separator + 1); |
189 | if (OriginalFullName) |
190 | *OriginalFullName = (BaseName + TemplateArgs).str(); |
191 | Name = BaseName; |
192 | } else |
193 | EndedWithTemplate = Name.ends_with(Suffix: ">" ); |
194 | OS << Name; |
195 | // This check would be insufficient for operator overloads like |
196 | // "operator>>" - but for now Clang doesn't try to simplify them, so this |
197 | // is OK. Add more nuanced operator overload handling here if/when needed. |
198 | if (Name.ends_with(Suffix: ">" )) |
199 | break; |
200 | if (!appendTemplateParameters(D)) |
201 | break; |
202 | |
203 | if (EndedWithTemplate) |
204 | OS << ' '; |
205 | OS << '>'; |
206 | EndedWithTemplate = true; |
207 | Word = true; |
208 | break; |
209 | } |
210 | } |
211 | return InnerDIE; |
212 | } |
213 | |
214 | void DWARFTypePrinter::appendUnqualifiedNameAfter( |
215 | DWARFDie D, DWARFDie Inner, bool SkipFirstParamIfArtificial) { |
216 | if (!D) |
217 | return; |
218 | switch (D.getTag()) { |
219 | case DW_TAG_subroutine_type: { |
220 | appendSubroutineNameAfter(D, Inner, SkipFirstParamIfArtificial, Const: false, |
221 | Volatile: false); |
222 | break; |
223 | } |
224 | case DW_TAG_array_type: { |
225 | appendArrayType(D); |
226 | break; |
227 | } |
228 | case DW_TAG_const_type: |
229 | case DW_TAG_volatile_type: |
230 | appendConstVolatileQualifierAfter(N: D); |
231 | break; |
232 | case DW_TAG_ptr_to_member_type: |
233 | case DW_TAG_reference_type: |
234 | case DW_TAG_rvalue_reference_type: |
235 | case DW_TAG_pointer_type: { |
236 | if (needsParens(D: Inner)) |
237 | OS << ')'; |
238 | appendUnqualifiedNameAfter(D: Inner, Inner: resolveReferencedType(D: Inner), |
239 | /*SkipFirstParamIfArtificial=*/D.getTag() == |
240 | DW_TAG_ptr_to_member_type); |
241 | break; |
242 | } |
243 | case DW_TAG_LLVM_ptrauth_type: { |
244 | auto getValOrNull = [&](dwarf::Attribute Attr) -> uint64_t { |
245 | if (auto Form = D.find(Attr)) |
246 | return *Form->getAsUnsignedConstant(); |
247 | return 0; |
248 | }; |
249 | SmallVector<const char *, 2> optionsVec; |
250 | if (getValOrNull(DW_AT_LLVM_ptrauth_isa_pointer)) |
251 | optionsVec.push_back(Elt: "isa-pointer" ); |
252 | if (getValOrNull(DW_AT_LLVM_ptrauth_authenticates_null_values)) |
253 | optionsVec.push_back(Elt: "authenticates-null-values" ); |
254 | if (auto AuthenticationMode = |
255 | D.find(Attr: DW_AT_LLVM_ptrauth_authentication_mode)) { |
256 | switch (*AuthenticationMode->getAsUnsignedConstant()) { |
257 | case 0: |
258 | case 1: |
259 | optionsVec.push_back(Elt: "strip" ); |
260 | break; |
261 | case 2: |
262 | optionsVec.push_back(Elt: "sign-and-strip" ); |
263 | break; |
264 | default: |
265 | // Default authentication policy |
266 | break; |
267 | } |
268 | } |
269 | std::string options; |
270 | for (const auto *option : optionsVec) { |
271 | if (options.size()) |
272 | options += "," ; |
273 | options += option; |
274 | } |
275 | if (options.size()) |
276 | options = ", \"" + options + "\"" ; |
277 | std::string PtrauthString; |
278 | llvm::raw_string_ostream PtrauthStream(PtrauthString); |
279 | PtrauthStream |
280 | << "__ptrauth(" << getValOrNull(DW_AT_LLVM_ptrauth_key) << ", " |
281 | << getValOrNull(DW_AT_LLVM_ptrauth_address_discriminated) << ", 0x0" |
282 | << utohexstr(X: getValOrNull(DW_AT_LLVM_ptrauth_extra_discriminator), LowerCase: true) |
283 | << options << ")" ; |
284 | OS << PtrauthStream.str(); |
285 | break; |
286 | } |
287 | /* |
288 | case DW_TAG_structure_type: |
289 | case DW_TAG_class_type: |
290 | case DW_TAG_enumeration_type: |
291 | case DW_TAG_base_type: |
292 | case DW_TAG_namespace: |
293 | */ |
294 | default: |
295 | break; |
296 | } |
297 | } |
298 | |
299 | /// Returns True if the DIE TAG is one of the ones that is scopped. |
300 | static bool scopedTAGs(dwarf::Tag Tag) { |
301 | switch (Tag) { |
302 | case dwarf::DW_TAG_structure_type: |
303 | case dwarf::DW_TAG_class_type: |
304 | case dwarf::DW_TAG_union_type: |
305 | case dwarf::DW_TAG_namespace: |
306 | case dwarf::DW_TAG_enumeration_type: |
307 | return true; |
308 | default: |
309 | break; |
310 | } |
311 | return false; |
312 | } |
313 | void DWARFTypePrinter::appendQualifiedName(DWARFDie D) { |
314 | if (D && scopedTAGs(Tag: D.getTag())) |
315 | appendScopes(D: D.getParent()); |
316 | appendUnqualifiedName(D); |
317 | } |
318 | DWARFDie DWARFTypePrinter::appendQualifiedNameBefore(DWARFDie D) { |
319 | if (D && scopedTAGs(Tag: D.getTag())) |
320 | appendScopes(D: D.getParent()); |
321 | return appendUnqualifiedNameBefore(D); |
322 | } |
323 | bool DWARFTypePrinter::appendTemplateParameters(DWARFDie D, |
324 | bool *FirstParameter) { |
325 | bool FirstParameterValue = true; |
326 | bool IsTemplate = false; |
327 | if (!FirstParameter) |
328 | FirstParameter = &FirstParameterValue; |
329 | for (const DWARFDie &C : D) { |
330 | auto Sep = [&] { |
331 | if (*FirstParameter) |
332 | OS << '<'; |
333 | else |
334 | OS << ", " ; |
335 | IsTemplate = true; |
336 | EndedWithTemplate = false; |
337 | *FirstParameter = false; |
338 | }; |
339 | if (C.getTag() == dwarf::DW_TAG_GNU_template_parameter_pack) { |
340 | IsTemplate = true; |
341 | appendTemplateParameters(D: C, FirstParameter); |
342 | } |
343 | if (C.getTag() == dwarf::DW_TAG_template_value_parameter) { |
344 | DWARFDie T = resolveReferencedType(D: C); |
345 | Sep(); |
346 | if (T.getTag() == DW_TAG_enumeration_type) { |
347 | OS << '('; |
348 | appendQualifiedName(D: T); |
349 | OS << ')'; |
350 | auto V = C.find(Attr: DW_AT_const_value); |
351 | OS << std::to_string(val: *V->getAsSignedConstant()); |
352 | continue; |
353 | } |
354 | // /Maybe/ we could do pointer/reference type parameters, looking for the |
355 | // symbol in the ELF symbol table to get back to the variable... |
356 | // but probably not worth it. |
357 | if (T.getTag() == DW_TAG_pointer_type || |
358 | T.getTag() == DW_TAG_reference_type) |
359 | continue; |
360 | const char *RawName = dwarf::toString(V: T.find(Attr: DW_AT_name), Default: nullptr); |
361 | assert(RawName); |
362 | StringRef Name = RawName; |
363 | auto V = C.find(Attr: DW_AT_const_value); |
364 | bool IsQualifiedChar = false; |
365 | if (Name == "bool" ) { |
366 | OS << (*V->getAsUnsignedConstant() ? "true" : "false" ); |
367 | } else if (Name == "short" ) { |
368 | OS << "(short)" ; |
369 | OS << std::to_string(val: *V->getAsSignedConstant()); |
370 | } else if (Name == "unsigned short" ) { |
371 | OS << "(unsigned short)" ; |
372 | OS << std::to_string(val: *V->getAsSignedConstant()); |
373 | } else if (Name == "int" ) |
374 | OS << std::to_string(val: *V->getAsSignedConstant()); |
375 | else if (Name == "long" ) { |
376 | OS << std::to_string(val: *V->getAsSignedConstant()); |
377 | OS << "L" ; |
378 | } else if (Name == "long long" ) { |
379 | OS << std::to_string(val: *V->getAsSignedConstant()); |
380 | OS << "LL" ; |
381 | } else if (Name == "unsigned int" ) { |
382 | OS << std::to_string(val: *V->getAsUnsignedConstant()); |
383 | OS << "U" ; |
384 | } else if (Name == "unsigned long" ) { |
385 | OS << std::to_string(val: *V->getAsUnsignedConstant()); |
386 | OS << "UL" ; |
387 | } else if (Name == "unsigned long long" ) { |
388 | OS << std::to_string(val: *V->getAsUnsignedConstant()); |
389 | OS << "ULL" ; |
390 | } else if (Name == "char" || |
391 | (IsQualifiedChar = |
392 | (Name == "unsigned char" || Name == "signed char" ))) { |
393 | // FIXME: check T's DW_AT_type to see if it's signed or not (since |
394 | // char signedness is implementation defined). |
395 | auto Val = *V->getAsSignedConstant(); |
396 | // Copied/hacked up from Clang's CharacterLiteral::print - incomplete |
397 | // (doesn't actually support different character types/widths, sign |
398 | // handling's not done, and doesn't correctly test if a character is |
399 | // printable or needs to use a numeric escape sequence instead) |
400 | if (IsQualifiedChar) { |
401 | OS << '('; |
402 | OS << Name; |
403 | OS << ')'; |
404 | } |
405 | switch (Val) { |
406 | case '\\': |
407 | OS << "'\\\\'" ; |
408 | break; |
409 | case '\'': |
410 | OS << "'\\''" ; |
411 | break; |
412 | case '\a': |
413 | // TODO: K&R: the meaning of '\\a' is different in traditional C |
414 | OS << "'\\a'" ; |
415 | break; |
416 | case '\b': |
417 | OS << "'\\b'" ; |
418 | break; |
419 | case '\f': |
420 | OS << "'\\f'" ; |
421 | break; |
422 | case '\n': |
423 | OS << "'\\n'" ; |
424 | break; |
425 | case '\r': |
426 | OS << "'\\r'" ; |
427 | break; |
428 | case '\t': |
429 | OS << "'\\t'" ; |
430 | break; |
431 | case '\v': |
432 | OS << "'\\v'" ; |
433 | break; |
434 | default: |
435 | if ((Val & ~0xFFu) == ~0xFFu) |
436 | Val &= 0xFFu; |
437 | if (Val < 127 && Val >= 32) { |
438 | OS << "'" ; |
439 | OS << (char)Val; |
440 | OS << "'" ; |
441 | } else if (Val < 256) |
442 | OS << llvm::format(Fmt: "'\\x%02" PRIx64 "'" , Vals: Val); |
443 | else if (Val <= 0xFFFF) |
444 | OS << llvm::format(Fmt: "'\\u%04" PRIx64 "'" , Vals: Val); |
445 | else |
446 | OS << llvm::format(Fmt: "'\\U%08" PRIx64 "'" , Vals: Val); |
447 | } |
448 | } |
449 | continue; |
450 | } |
451 | if (C.getTag() == dwarf::DW_TAG_GNU_template_template_param) { |
452 | const char *RawName = |
453 | dwarf::toString(V: C.find(Attr: DW_AT_GNU_template_name), Default: nullptr); |
454 | assert(RawName); |
455 | StringRef Name = RawName; |
456 | Sep(); |
457 | OS << Name; |
458 | continue; |
459 | } |
460 | if (C.getTag() != dwarf::DW_TAG_template_type_parameter) |
461 | continue; |
462 | auto TypeAttr = C.find(Attr: DW_AT_type); |
463 | Sep(); |
464 | appendQualifiedName(D: TypeAttr ? resolveReferencedType(D: C, F: *TypeAttr) |
465 | : DWARFDie()); |
466 | } |
467 | if (IsTemplate && *FirstParameter && FirstParameter == &FirstParameterValue) { |
468 | OS << '<'; |
469 | EndedWithTemplate = false; |
470 | } |
471 | return IsTemplate; |
472 | } |
473 | void DWARFTypePrinter::decomposeConstVolatile(DWARFDie &N, DWARFDie &T, |
474 | DWARFDie &C, DWARFDie &V) { |
475 | (N.getTag() == DW_TAG_const_type ? C : V) = N; |
476 | T = resolveReferencedType(D: N); |
477 | if (T) { |
478 | auto Tag = T.getTag(); |
479 | if (Tag == DW_TAG_const_type) { |
480 | C = T; |
481 | T = resolveReferencedType(D: T); |
482 | } else if (Tag == DW_TAG_volatile_type) { |
483 | V = T; |
484 | T = resolveReferencedType(D: T); |
485 | } |
486 | } |
487 | } |
488 | void DWARFTypePrinter::appendConstVolatileQualifierAfter(DWARFDie N) { |
489 | DWARFDie C; |
490 | DWARFDie V; |
491 | DWARFDie T; |
492 | decomposeConstVolatile(N, T, C, V); |
493 | if (T && T.getTag() == DW_TAG_subroutine_type) |
494 | appendSubroutineNameAfter(D: T, Inner: resolveReferencedType(D: T), SkipFirstParamIfArtificial: false, Const: C.isValid(), |
495 | Volatile: V.isValid()); |
496 | else |
497 | appendUnqualifiedNameAfter(D: T, Inner: resolveReferencedType(D: T)); |
498 | } |
499 | void DWARFTypePrinter::appendConstVolatileQualifierBefore(DWARFDie N) { |
500 | DWARFDie C; |
501 | DWARFDie V; |
502 | DWARFDie T; |
503 | decomposeConstVolatile(N, T, C, V); |
504 | bool Subroutine = T && T.getTag() == DW_TAG_subroutine_type; |
505 | DWARFDie A = T; |
506 | while (A && A.getTag() == DW_TAG_array_type) |
507 | A = resolveReferencedType(D: A); |
508 | bool Leading = |
509 | (!A || (A.getTag() != DW_TAG_pointer_type && |
510 | A.getTag() != llvm::dwarf::DW_TAG_ptr_to_member_type)) && |
511 | !Subroutine; |
512 | if (Leading) { |
513 | if (C) |
514 | OS << "const " ; |
515 | if (V) |
516 | OS << "volatile " ; |
517 | } |
518 | appendQualifiedNameBefore(D: T); |
519 | if (!Leading && !Subroutine) { |
520 | Word = true; |
521 | if (C) |
522 | OS << "const" ; |
523 | if (V) { |
524 | if (C) |
525 | OS << ' '; |
526 | OS << "volatile" ; |
527 | } |
528 | } |
529 | } |
530 | void DWARFTypePrinter::appendUnqualifiedName(DWARFDie D, |
531 | std::string *OriginalFullName) { |
532 | // FIXME: We should have pretty printers per language. Currently we print |
533 | // everything as if it was C++ and fall back to the TAG type name. |
534 | DWARFDie Inner = appendUnqualifiedNameBefore(D, OriginalFullName); |
535 | appendUnqualifiedNameAfter(D, Inner); |
536 | } |
537 | void DWARFTypePrinter::appendSubroutineNameAfter( |
538 | DWARFDie D, DWARFDie Inner, bool SkipFirstParamIfArtificial, bool Const, |
539 | bool Volatile) { |
540 | DWARFDie FirstParamIfArtificial; |
541 | OS << '('; |
542 | EndedWithTemplate = false; |
543 | bool First = true; |
544 | bool RealFirst = true; |
545 | for (DWARFDie P : D) { |
546 | if (P.getTag() != DW_TAG_formal_parameter && |
547 | P.getTag() != DW_TAG_unspecified_parameters) |
548 | return; |
549 | DWARFDie T = resolveReferencedType(D: P); |
550 | if (SkipFirstParamIfArtificial && RealFirst && P.find(Attr: DW_AT_artificial)) { |
551 | FirstParamIfArtificial = T; |
552 | RealFirst = false; |
553 | continue; |
554 | } |
555 | if (!First) { |
556 | OS << ", " ; |
557 | } |
558 | First = false; |
559 | if (P.getTag() == DW_TAG_unspecified_parameters) |
560 | OS << "..." ; |
561 | else |
562 | appendQualifiedName(D: T); |
563 | } |
564 | EndedWithTemplate = false; |
565 | OS << ')'; |
566 | if (FirstParamIfArtificial) { |
567 | if (DWARFDie P = FirstParamIfArtificial) { |
568 | if (P.getTag() == DW_TAG_pointer_type) { |
569 | auto CVStep = [&](DWARFDie CV) { |
570 | if (DWARFDie U = resolveReferencedType(D: CV)) { |
571 | Const |= U.getTag() == DW_TAG_const_type; |
572 | Volatile |= U.getTag() == DW_TAG_volatile_type; |
573 | return U; |
574 | } |
575 | return DWARFDie(); |
576 | }; |
577 | if (DWARFDie CV = CVStep(P)) { |
578 | CVStep(CV); |
579 | } |
580 | } |
581 | } |
582 | } |
583 | |
584 | if (auto CC = D.find(Attr: DW_AT_calling_convention)) { |
585 | switch (*CC->getAsUnsignedConstant()) { |
586 | case CallingConvention::DW_CC_BORLAND_stdcall: |
587 | OS << " __attribute__((stdcall))" ; |
588 | break; |
589 | case CallingConvention::DW_CC_BORLAND_msfastcall: |
590 | OS << " __attribute__((fastcall))" ; |
591 | break; |
592 | case CallingConvention::DW_CC_BORLAND_thiscall: |
593 | OS << " __attribute__((thiscall))" ; |
594 | break; |
595 | case CallingConvention::DW_CC_LLVM_vectorcall: |
596 | OS << " __attribute__((vectorcall))" ; |
597 | break; |
598 | case CallingConvention::DW_CC_BORLAND_pascal: |
599 | OS << " __attribute__((pascal))" ; |
600 | break; |
601 | case CallingConvention::DW_CC_LLVM_Win64: |
602 | OS << " __attribute__((ms_abi))" ; |
603 | break; |
604 | case CallingConvention::DW_CC_LLVM_X86_64SysV: |
605 | OS << " __attribute__((sysv_abi))" ; |
606 | break; |
607 | case CallingConvention::DW_CC_LLVM_AAPCS: |
608 | // AArch64VectorCall missing? |
609 | OS << " __attribute__((pcs(\"aapcs\")))" ; |
610 | break; |
611 | case CallingConvention::DW_CC_LLVM_AAPCS_VFP: |
612 | OS << " __attribute__((pcs(\"aapcs-vfp\")))" ; |
613 | break; |
614 | case CallingConvention::DW_CC_LLVM_IntelOclBicc: |
615 | OS << " __attribute__((intel_ocl_bicc))" ; |
616 | break; |
617 | case CallingConvention::DW_CC_LLVM_SpirFunction: |
618 | case CallingConvention::DW_CC_LLVM_OpenCLKernel: |
619 | // These aren't available as attributes, but maybe we should still |
620 | // render them somehow? (Clang doesn't render them, but that's an issue |
621 | // for template names too - since then the DWARF names of templates |
622 | // instantiated with function types with these calling conventions won't |
623 | // have distinct names - so we'd need to fix that too) |
624 | break; |
625 | case CallingConvention::DW_CC_LLVM_Swift: |
626 | // SwiftAsync missing |
627 | OS << " __attribute__((swiftcall))" ; |
628 | break; |
629 | case CallingConvention::DW_CC_LLVM_PreserveMost: |
630 | OS << " __attribute__((preserve_most))" ; |
631 | break; |
632 | case CallingConvention::DW_CC_LLVM_PreserveAll: |
633 | OS << " __attribute__((preserve_all))" ; |
634 | break; |
635 | case CallingConvention::DW_CC_LLVM_PreserveNone: |
636 | OS << " __attribute__((preserve_none))" ; |
637 | break; |
638 | case CallingConvention::DW_CC_LLVM_X86RegCall: |
639 | OS << " __attribute__((regcall))" ; |
640 | break; |
641 | case CallingConvention::DW_CC_LLVM_M68kRTD: |
642 | OS << " __attribute__((m68k_rtd))" ; |
643 | break; |
644 | } |
645 | } |
646 | |
647 | if (Const) |
648 | OS << " const" ; |
649 | if (Volatile) |
650 | OS << " volatile" ; |
651 | if (D.find(Attr: DW_AT_reference)) |
652 | OS << " &" ; |
653 | if (D.find(Attr: DW_AT_rvalue_reference)) |
654 | OS << " &&" ; |
655 | |
656 | appendUnqualifiedNameAfter(D: Inner, Inner: resolveReferencedType(D: Inner)); |
657 | } |
658 | void DWARFTypePrinter::appendScopes(DWARFDie D) { |
659 | if (D.getTag() == DW_TAG_compile_unit) |
660 | return; |
661 | if (D.getTag() == DW_TAG_type_unit) |
662 | return; |
663 | if (D.getTag() == DW_TAG_skeleton_unit) |
664 | return; |
665 | if (D.getTag() == DW_TAG_subprogram) |
666 | return; |
667 | if (D.getTag() == DW_TAG_lexical_block) |
668 | return; |
669 | D = D.resolveTypeUnitReference(); |
670 | if (DWARFDie P = D.getParent()) |
671 | appendScopes(D: P); |
672 | appendUnqualifiedName(D); |
673 | OS << "::" ; |
674 | } |
675 | } // namespace llvm |
676 | |