1//===-- LVElement.cpp -----------------------------------------------------===//
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 implements the LVElement class.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/DebugInfo/LogicalView/Core/LVElement.h"
14#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
15#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
16#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
17#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
18
19using namespace llvm;
20using namespace llvm::codeview;
21using namespace llvm::logicalview;
22
23#define DEBUG_TYPE "Element"
24
25LVElementDispatch LVElement::Dispatch = {
26 {LVElementKind::Discarded, &LVElement::getIsDiscarded},
27 {LVElementKind::Global, &LVElement::getIsGlobalReference},
28 {LVElementKind::Optimized, &LVElement::getIsOptimized}};
29
30LVType *LVElement::getTypeAsType() const {
31 return ElementType && ElementType->getIsType()
32 ? static_cast<LVType *>(ElementType)
33 : nullptr;
34}
35
36LVScope *LVElement::getTypeAsScope() const {
37 return ElementType && ElementType->getIsScope()
38 ? static_cast<LVScope *>(ElementType)
39 : nullptr;
40}
41
42// Set the element type.
43void LVElement::setGenericType(LVElement *Element) {
44 if (!Element->isTemplateParam()) {
45 setType(Element);
46 return;
47 }
48 // For template parameters, the instance type can be a type or a scope.
49 if (options().getAttributeArgument()) {
50 if (Element->getIsKindType())
51 setType(Element->getTypeAsType());
52 else if (Element->getIsKindScope())
53 setType(Element->getTypeAsScope());
54 } else
55 setType(Element);
56}
57
58// Discriminator as string.
59std::string LVElement::discriminatorAsString() const {
60 uint32_t Discriminator = getDiscriminator();
61 std::string String;
62 raw_string_ostream Stream(String);
63 if (Discriminator && options().getAttributeDiscriminator())
64 Stream << "," << Discriminator;
65 return String;
66}
67
68// Get the type as a string.
69StringRef LVElement::typeAsString() const {
70 return getHasType() ? getTypeName() : typeVoid();
71}
72
73// Get name for element type.
74StringRef LVElement::getTypeName() const {
75 return ElementType ? ElementType->getName() : StringRef();
76}
77
78static size_t getStringIndex(StringRef Name) {
79 // Convert the name to Unified format ('\' have been converted into '/').
80 std::string Pathname(transformPath(Path: Name));
81
82 // Depending on the --attribute=filename and --attribute=pathname command
83 // line options, use the basename or the full pathname as the name.
84 if (!options().getAttributePathname()) {
85 // Get the basename by ignoring any prefix up to the last slash ('/').
86 StringRef Basename = Pathname;
87 size_t Pos = Basename.rfind(C: '/');
88 if (Pos != std::string::npos)
89 Basename = Basename.substr(Start: Pos + 1);
90 return getStringPool().getIndex(Key: Basename);
91 }
92
93 return getStringPool().getIndex(Key: Pathname);
94}
95
96void LVElement::setName(StringRef ElementName) {
97 // In the case of Root or Compile Unit, get index for the flatted out name.
98 NameIndex = getTransformName() ? getStringIndex(Name: ElementName)
99 : getStringPool().getIndex(Key: ElementName);
100}
101
102void LVElement::setFilename(StringRef Filename) {
103 // Get index for the flattened out filename.
104 FilenameIndex = getStringIndex(Name: Filename);
105}
106
107void LVElement::setInnerComponent(StringRef Name) {
108 if (Name.size()) {
109 StringRef InnerComponent;
110 std::tie(args: std::ignore, args&: InnerComponent) = getInnerComponent(Name);
111 setName(InnerComponent);
112 }
113}
114
115// Return the string representation of a DIE offset.
116std::string LVElement::typeOffsetAsString() const {
117 if (options().getAttributeOffset()) {
118 LVElement *Element = getType();
119 return hexSquareString(Value: Element ? Element->getOffset() : 0);
120 }
121 return {};
122}
123
124StringRef LVElement::accessibilityString(uint32_t Access) const {
125 uint32_t Value = getAccessibilityCode();
126 switch (Value ? Value : Access) {
127 case dwarf::DW_ACCESS_public:
128 return "public";
129 case dwarf::DW_ACCESS_protected:
130 return "protected";
131 case dwarf::DW_ACCESS_private:
132 return "private";
133 default:
134 return StringRef();
135 }
136}
137
138std::optional<uint32_t> LVElement::getAccessibilityCode(MemberAccess Access) {
139 switch (Access) {
140 case MemberAccess::Private:
141 return dwarf::DW_ACCESS_private;
142 case MemberAccess::Protected:
143 return dwarf::DW_ACCESS_protected;
144 case MemberAccess::Public:
145 return dwarf::DW_ACCESS_public;
146 default:
147 return std::nullopt;
148 }
149}
150
151StringRef LVElement::externalString() const {
152 return getIsExternal() ? "extern" : StringRef();
153}
154
155StringRef LVElement::inlineCodeString(uint32_t Code) const {
156 uint32_t Value = getInlineCode();
157 switch (Value ? Value : Code) {
158 case dwarf::DW_INL_not_inlined:
159 return "not_inlined";
160 case dwarf::DW_INL_inlined:
161 return "inlined";
162 case dwarf::DW_INL_declared_not_inlined:
163 return "declared_not_inlined";
164 case dwarf::DW_INL_declared_inlined:
165 return "declared_inlined";
166 default:
167 return StringRef();
168 }
169}
170
171StringRef LVElement::virtualityString(uint32_t Virtuality) const {
172 uint32_t Value = getVirtualityCode();
173 switch (Value ? Value : Virtuality) {
174 case dwarf::DW_VIRTUALITY_none:
175 return StringRef();
176 case dwarf::DW_VIRTUALITY_virtual:
177 return "virtual";
178 case dwarf::DW_VIRTUALITY_pure_virtual:
179 return "pure virtual";
180 default:
181 return StringRef();
182 }
183}
184
185std::optional<uint32_t> LVElement::getVirtualityCode(MethodKind Virtuality) {
186 switch (Virtuality) {
187 case MethodKind::Virtual:
188 return dwarf::DW_VIRTUALITY_virtual;
189 case MethodKind::PureVirtual:
190 return dwarf::DW_VIRTUALITY_pure_virtual;
191 case MethodKind::IntroducingVirtual:
192 case MethodKind::PureIntroducingVirtual:
193 // No direct equivalents in DWARF. Assume Virtual.
194 return dwarf::DW_VIRTUALITY_virtual;
195 default:
196 return std::nullopt;
197 }
198}
199
200void LVElement::resolve() {
201 if (getIsResolved())
202 return;
203 setIsResolved();
204
205 resolveReferences();
206 resolveParents();
207 resolveExtra();
208 resolveName();
209}
210
211// Set File/Line using the specification element.
212void LVElement::setFileLine(LVElement *Specification) {
213 // In the case of inlined functions, the correct scope must be associated
214 // with the file and line information of the outline version.
215 if (!isLined()) {
216 setLineNumber(Specification->getLineNumber());
217 setIsLineFromReference();
218 }
219 if (!isFiled()) {
220 setFilenameIndex(Specification->getFilenameIndex());
221 setIsFileFromReference();
222 }
223}
224
225void LVElement::resolveName() {
226 // Set the qualified name if requested.
227 if (options().getAttributeQualified())
228 resolveQualifiedName();
229
230 setIsResolvedName();
231}
232
233// Resolve any parents.
234void LVElement::resolveParents() {
235 if (isRoot() || isCompileUnit())
236 return;
237
238 LVScope *Parent = getParentScope();
239 if (Parent && !Parent->getIsCompileUnit())
240 Parent->resolve();
241}
242
243// Generate a name for unnamed elements.
244void LVElement::generateName(std::string &Prefix) const {
245 LVScope *Scope = getParentScope();
246 if (!Scope)
247 return;
248
249 // Use its parent name and any line information.
250 Prefix.append(str: std::string(Scope->getName()));
251 Prefix.append(s: "::");
252 Prefix.append(str: isLined() ? lineNumberAsString(/*ShowZero=*/true) : "?");
253
254 // Remove any whitespaces.
255 llvm::erase_if(C&: Prefix, P: ::isspace);
256}
257
258// Generate a name for unnamed elements.
259void LVElement::generateName() {
260 setIsAnonymous();
261 std::string Name;
262 generateName(Prefix&: Name);
263 setName(Name);
264 setIsGeneratedName();
265}
266
267void LVElement::updateLevel(LVScope *Parent, bool Moved) {
268 setLevel(Parent->getLevel() + 1);
269 if (Moved)
270 setHasMoved();
271}
272
273// Generate the full name for the element, to include special qualifiers.
274void LVElement::resolveFullname(LVElement *BaseType, StringRef Name) {
275 // For the following sample code,
276 // void *p;
277 // some compilers do not generate an attribute for the associated type:
278 // DW_TAG_variable
279 // DW_AT_name 'p'
280 // DW_AT_type $1
281 // ...
282 // $1: DW_TAG_pointer_type
283 // ...
284 // For those cases, generate the implicit 'void' type.
285 StringRef BaseTypename = BaseType ? BaseType->getName() : emptyString();
286 bool GetBaseTypename = false;
287 bool UseBaseTypename = true;
288 bool UseNameText = true;
289
290 switch (getTag()) {
291 case dwarf::DW_TAG_pointer_type: // "*";
292 if (!BaseType)
293 BaseTypename = typeVoid();
294 break;
295 case dwarf::DW_TAG_const_type: // "const"
296 case dwarf::DW_TAG_ptr_to_member_type: // "*"
297 case dwarf::DW_TAG_rvalue_reference_type: // "&&"
298 case dwarf::DW_TAG_reference_type: // "&"
299 case dwarf::DW_TAG_restrict_type: // "restrict"
300 case dwarf::DW_TAG_volatile_type: // "volatile"
301 case dwarf::DW_TAG_unaligned: // "unaligned"
302 break;
303 case dwarf::DW_TAG_base_type:
304 case dwarf::DW_TAG_compile_unit:
305 case dwarf::DW_TAG_class_type:
306 case dwarf::DW_TAG_enumerator:
307 case dwarf::DW_TAG_namespace:
308 case dwarf::DW_TAG_skeleton_unit:
309 case dwarf::DW_TAG_structure_type:
310 case dwarf::DW_TAG_union_type:
311 case dwarf::DW_TAG_unspecified_type:
312 case dwarf::DW_TAG_GNU_template_parameter_pack:
313 GetBaseTypename = true;
314 break;
315 case dwarf::DW_TAG_array_type:
316 case dwarf::DW_TAG_call_site:
317 case dwarf::DW_TAG_entry_point:
318 case dwarf::DW_TAG_enumeration_type:
319 case dwarf::DW_TAG_GNU_call_site:
320 case dwarf::DW_TAG_imported_module:
321 case dwarf::DW_TAG_imported_declaration:
322 case dwarf::DW_TAG_inlined_subroutine:
323 case dwarf::DW_TAG_label:
324 case dwarf::DW_TAG_subprogram:
325 case dwarf::DW_TAG_subrange_type:
326 case dwarf::DW_TAG_subroutine_type:
327 case dwarf::DW_TAG_typedef:
328 GetBaseTypename = true;
329 UseBaseTypename = false;
330 break;
331 case dwarf::DW_TAG_template_type_parameter:
332 case dwarf::DW_TAG_template_value_parameter:
333 UseBaseTypename = false;
334 break;
335 case dwarf::DW_TAG_GNU_template_template_param:
336 break;
337 case dwarf::DW_TAG_catch_block:
338 case dwarf::DW_TAG_lexical_block:
339 case dwarf::DW_TAG_try_block:
340 UseNameText = false;
341 break;
342 default:
343 llvm_unreachable("Invalid type.");
344 return;
345 break;
346 }
347
348 // Overwrite if no given value. 'Name' is empty when resolving for scopes
349 // and symbols. In the case of types, it represents the type base name.
350 if (Name.empty() && GetBaseTypename)
351 Name = getName();
352
353 // Concatenate the elements to get the full type name.
354 // Type will be: base_parent + pre + base + parent + post.
355 std::string Fullname;
356
357 if (UseNameText && Name.size())
358 Fullname.append(str: std::string(Name));
359 if (UseBaseTypename && BaseTypename.size()) {
360 if (UseNameText && Name.size())
361 Fullname.append(s: " ");
362 Fullname.append(str: std::string(BaseTypename));
363 }
364
365 // For a better and consistent layout, check if the generated name
366 // contains double space sequences.
367 assert((Fullname.find(" ", 0) == std::string::npos) &&
368 "Extra double spaces in name.");
369
370 LLVM_DEBUG({ dbgs() << "Fullname = '" << Fullname << "'\n"; });
371 setName(Fullname);
372}
373
374void LVElement::setFile(LVElement *Reference) {
375 if (!options().getAttributeAnySource())
376 return;
377
378 // At this point, any existing reference to another element, have been
379 // resolved and the file ID extracted from the DI entry.
380 if (Reference)
381 setFileLine(Reference);
382
383 // The file information is used to show the source file for any element
384 // and display any new source file in relation to its parent element.
385 // a) Elements that are not inlined.
386 // - We record the DW_AT_decl_line and DW_AT_decl_file.
387 // b) Elements that are inlined.
388 // - We record the DW_AT_decl_line and DW_AT_decl_file.
389 // - We record the DW_AT_call_line and DW_AT_call_file.
390 // For both cases, we use the DW_AT_decl_file value to detect any changes
391 // in the source filename containing the element. Changes on this value
392 // indicates that the element being printed is not contained in the
393 // previous printed filename.
394
395 // The source files are indexed starting at 0, but DW_AT_decl_file defines
396 // that 0 means no file; a value of 1 means the 0th entry.
397 size_t Index = 0;
398
399 // An element with no source file information will use the reference
400 // attribute (DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension)
401 // to update its information.
402 if (getIsFileFromReference() && Reference) {
403 Index = Reference->getFilenameIndex();
404 if (Reference->getInvalidFilename())
405 setInvalidFilename();
406 setFilenameIndex(Index);
407 return;
408 }
409
410 // The source files are indexed starting at 0, but DW_AT_decl_file
411 // defines that 0 means no file; a value of 1 means the 0th entry.
412 Index = getFilenameIndex();
413 if (Index) {
414 StringRef Filename = getReader().getFilename(Object: this, Index);
415 Filename.size() ? setFilename(Filename) : setInvalidFilename();
416 }
417}
418
419LVScope *LVElement::traverseParents(LVScopeGetFunction GetFunction) const {
420 LVScope *Parent = getParentScope();
421 while (Parent && !(Parent->*GetFunction)())
422 Parent = Parent->getParentScope();
423 return Parent;
424}
425
426LVScope *LVElement::getFunctionParent() const {
427 return traverseParents(GetFunction: &LVScope::getIsFunction);
428}
429
430LVScope *LVElement::getCompileUnitParent() const {
431 return traverseParents(GetFunction: &LVScope::getIsCompileUnit);
432}
433
434// Resolve the qualified name to include the parent hierarchy names.
435void LVElement::resolveQualifiedName() {
436 if (!getIsReferencedType() || isBase() || getQualifiedResolved() ||
437 !getIncludeInPrint())
438 return;
439
440 std::string Name;
441
442 // Get the qualified name, excluding the Compile Unit.
443 LVScope *Parent = getParentScope();
444 if (Parent && !Parent->getIsRoot()) {
445 while (Parent && !Parent->getIsCompileUnit()) {
446 Name.insert(pos: 0, s: "::");
447 if (Parent->isNamed())
448 Name.insert(pos1: 0, str: std::string(Parent->getName()));
449 else {
450 std::string Temp;
451 Parent->generateName(Prefix&: Temp);
452 Name.insert(pos1: 0, str: Temp);
453 }
454 Parent = Parent->getParentScope();
455 }
456 }
457
458 if (Name.size()) {
459 setQualifiedName(Name);
460 setQualifiedResolved();
461 }
462 LLVM_DEBUG({
463 dbgs() << "Offset: " << hexSquareString(getOffset())
464 << ", Kind: " << formattedKind(kind())
465 << ", Name: " << formattedName(getName())
466 << ", QualifiedName: " << formattedName(Name) << "\n";
467 });
468}
469
470bool LVElement::referenceMatch(const LVElement *Element) const {
471 return (getHasReference() && Element->getHasReference()) ||
472 (!getHasReference() && !Element->getHasReference());
473}
474
475bool LVElement::equals(const LVElement *Element) const {
476 // The minimum factors that must be the same for an equality are:
477 // line number, level, name, qualified name and filename.
478 LLVM_DEBUG({
479 dbgs() << "\n[Element::equals]\n";
480 if (options().getAttributeOffset()) {
481 dbgs() << "Reference: " << hexSquareString(getOffset()) << "\n";
482 dbgs() << "Target : " << hexSquareString(Element->getOffset()) << "\n";
483 }
484 dbgs() << "Reference: "
485 << "Kind = " << formattedKind(kind()) << ", "
486 << "Name = " << formattedName(getName()) << ", "
487 << "Qualified = " << formattedName(getQualifiedName()) << "\n"
488 << "Target : "
489 << "Kind = " << formattedKind(Element->kind()) << ", "
490 << "Name = " << formattedName(Element->getName()) << ", "
491 << "Qualified = " << formattedName(Element->getQualifiedName())
492 << "\n"
493 << "Reference: "
494 << "NameIndex = " << getNameIndex() << ", "
495 << "QualifiedNameIndex = " << getQualifiedNameIndex() << ", "
496 << "FilenameIndex = " << getFilenameIndex() << "\n"
497 << "Target : "
498 << "NameIndex = " << Element->getNameIndex() << ", "
499 << "QualifiedNameIndex = " << Element->getQualifiedNameIndex()
500 << ", "
501 << "FilenameIndex = " << Element->getFilenameIndex() << "\n";
502 });
503 if ((getLineNumber() != Element->getLineNumber()) ||
504 (getLevel() != Element->getLevel()))
505 return false;
506
507 if ((getQualifiedNameIndex() != Element->getQualifiedNameIndex()) ||
508 (getNameIndex() != Element->getNameIndex()) ||
509 (getFilenameIndex() != Element->getFilenameIndex()))
510 return false;
511
512 if (!getType() && !Element->getType())
513 return true;
514 if (getType() && Element->getType())
515 return getType()->equals(Element: Element->getType());
516 return false;
517}
518
519// Print the FileName Index.
520void LVElement::printFileIndex(raw_ostream &OS, bool Full) const {
521 if (options().getPrintFormatting() && options().getAttributeAnySource() &&
522 getFilenameIndex()) {
523
524 // Check if there is a change in the File ID sequence.
525 size_t Index = getFilenameIndex();
526 if (options().changeFilenameIndex(Index)) {
527 // Just to keep a nice layout.
528 OS << "\n";
529 printAttributes(OS, /*Full=*/false);
530
531 OS << " {Source} ";
532 if (getInvalidFilename())
533 OS << format(Fmt: "[0x%08x]\n", Vals: Index);
534 else
535 OS << formattedName(Name: getPathname()) << "\n";
536 }
537 }
538}
539
540void LVElement::printReference(raw_ostream &OS, bool Full,
541 LVElement *Parent) const {
542 if (options().getPrintFormatting() && options().getAttributeReference())
543 printAttributes(OS, Full, Name: "{Reference} ", Parent,
544 Value: referenceAsString(LineNumber: getLineNumber(), /*Spaces=*/false),
545 /*UseQuotes=*/false, /*PrintRef=*/true);
546}
547
548void LVElement::printLinkageName(raw_ostream &OS, bool Full,
549 LVElement *Parent) const {
550 if (options().getPrintFormatting() && options().getAttributeLinkage()) {
551 printAttributes(OS, Full, Name: "{Linkage} ", Parent, Value: getLinkageName(),
552 /*UseQuotes=*/true, /*PrintRef=*/false);
553 }
554}
555
556void LVElement::printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent,
557 LVScope *Scope) const {
558 if (options().getPrintFormatting() && options().getAttributeLinkage()) {
559 LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);
560 std::string Text = (Twine(" 0x") + Twine::utohexstr(Val: SectionIndex) +
561 Twine(" '") + Twine(getLinkageName()) + Twine("'"))
562 .str();
563 printAttributes(OS, Full, Name: "{Linkage} ", Parent, Value: Text,
564 /*UseQuotes=*/false, /*PrintRef=*/false);
565 }
566}
567