aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/DebugInfo/LogicalView/Core
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:04 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:11 +0000
commite3b557809604d036af6e00c60f012c2025b59a5e (patch)
tree8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/lib/DebugInfo/LogicalView/Core
parent08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff)
Diffstat (limited to 'llvm/lib/DebugInfo/LogicalView/Core')
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp428
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp530
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp222
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp675
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp169
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp579
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp158
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp304
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp2100
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp113
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp56
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp449
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp524
13 files changed, 6307 insertions, 0 deletions
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp
new file mode 100644
index 000000000000..65baf52ffb44
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp
@@ -0,0 +1,428 @@
+//===-- LVCompare.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVCompare class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include <tuple>
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Compare"
+
+namespace {
+
+enum class LVCompareItem { Scope, Symbol, Type, Line, Total };
+enum class LVCompareIndex { Header, Expected, Missing, Added };
+using LVCompareEntry = std::tuple<const char *, unsigned, unsigned, unsigned>;
+using LVCompareInfo = std::map<LVCompareItem, LVCompareEntry>;
+LVCompareInfo Results = {
+ {LVCompareItem::Line, LVCompareEntry("Lines", 0, 0, 0)},
+ {LVCompareItem::Scope, LVCompareEntry("Scopes", 0, 0, 0)},
+ {LVCompareItem::Symbol, LVCompareEntry("Symbols", 0, 0, 0)},
+ {LVCompareItem::Type, LVCompareEntry("Types", 0, 0, 0)},
+ {LVCompareItem::Total, LVCompareEntry("Total", 0, 0, 0)}};
+static LVCompareInfo::iterator IterTotal = Results.end();
+
+constexpr unsigned getHeader() {
+ return static_cast<unsigned>(LVCompareIndex::Header);
+}
+constexpr unsigned getExpected() {
+ return static_cast<unsigned>(LVCompareIndex::Expected);
+}
+constexpr unsigned getMissing() {
+ return static_cast<unsigned>(LVCompareIndex::Missing);
+}
+constexpr unsigned getAdded() {
+ return static_cast<unsigned>(LVCompareIndex::Added);
+}
+
+LVCompare *CurrentComparator = nullptr;
+
+void zeroResults() {
+ // In case the same reader instance is used.
+ for (LVCompareInfo::reference Entry : Results) {
+ std::get<getExpected()>(Entry.second) = 0;
+ std::get<getMissing()>(Entry.second) = 0;
+ std::get<getAdded()>(Entry.second) = 0;
+ }
+ IterTotal = Results.find(LVCompareItem::Total);
+ assert(IterTotal != Results.end());
+}
+
+LVCompareInfo::iterator getResultsEntry(LVElement *Element) {
+ LVCompareItem Kind;
+ if (Element->getIsLine())
+ Kind = LVCompareItem::Line;
+ else if (Element->getIsScope())
+ Kind = LVCompareItem::Scope;
+ else if (Element->getIsSymbol())
+ Kind = LVCompareItem::Symbol;
+ else
+ Kind = LVCompareItem::Type;
+
+ // Used to update the expected, missing or added entry for the given kind.
+ LVCompareInfo::iterator Iter = Results.find(Kind);
+ assert(Iter != Results.end());
+ return Iter;
+}
+
+void updateExpected(LVElement *Element) {
+ LVCompareInfo::iterator Iter = getResultsEntry(Element);
+ // Update total for expected.
+ ++std::get<getExpected()>(IterTotal->second);
+ // Update total for specific element kind.
+ ++std::get<getExpected()>(Iter->second);
+}
+
+void updateMissingOrAdded(LVElement *Element, LVComparePass Pass) {
+ LVCompareInfo::iterator Iter = getResultsEntry(Element);
+ if (Pass == LVComparePass::Missing) {
+ ++std::get<getMissing()>(IterTotal->second);
+ ++std::get<getMissing()>(Iter->second);
+ } else {
+ ++std::get<getAdded()>(IterTotal->second);
+ ++std::get<getAdded()>(Iter->second);
+ }
+}
+
+} // namespace
+
+LVCompare &LVCompare::getInstance() {
+ static LVCompare DefaultComparator(outs());
+ return CurrentComparator ? *CurrentComparator : DefaultComparator;
+}
+
+void LVCompare::setInstance(LVCompare *Comparator) {
+ CurrentComparator = Comparator;
+}
+
+LVCompare::LVCompare(raw_ostream &OS) : OS(OS) {
+ PrintLines = options().getPrintLines();
+ PrintSymbols = options().getPrintSymbols();
+ PrintTypes = options().getPrintTypes();
+ PrintScopes =
+ options().getPrintScopes() || PrintLines || PrintSymbols || PrintTypes;
+}
+
+Error LVCompare::execute(LVReader *ReferenceReader, LVReader *TargetReader) {
+ setInstance(this);
+ // In the case of added elements, the 'Reference' reader will be modified;
+ // those elements will be added to it. Update the current reader instance.
+ LVReader::setInstance(ReferenceReader);
+
+ auto PrintHeader = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) {
+ LLVM_DEBUG({
+ dbgs() << "[Reference] " << LHS->getName() << "\n"
+ << "[Target] " << RHS->getName() << "\n";
+ });
+ OS << "\nReference: " << formattedName(LHS->getName()) << "\n"
+ << "Target: " << formattedName(RHS->getName()) << "\n";
+ };
+
+ // We traverse the given scopes tree ('Reference' and 'Target') twice.
+ // The first time we look for missing items from the 'Reference' and the
+ // second time we look for items added to the 'Target'.
+ // The comparison test includes the name, lexical level, type, source
+ // location, etc.
+ LVScopeRoot *ReferenceRoot = ReferenceReader->getScopesRoot();
+ LVScopeRoot *TargetRoot = TargetReader->getScopesRoot();
+ ReferenceRoot->setIsInCompare();
+ TargetRoot->setIsInCompare();
+
+ // Reset possible previous results.
+ zeroResults();
+
+ if (options().getCompareContext()) {
+ // Perform a logical view comparison as a whole unit. We start at the
+ // root reference; at each scope an equal test is applied to its children.
+ // If a difference is found, the current path is marked as missing.
+ auto CompareViews = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) -> Error {
+ LHS->markMissingParents(RHS, /*TraverseChildren=*/true);
+ if (LHS->getIsMissingLink() && options().getReportAnyView()) {
+ // As we are printing a missing tree, enable formatting.
+ options().setPrintFormatting();
+ OS << "\nMissing Tree:\n";
+ if (Error Err = LHS->doPrint(/*Split=*/false, /*Match=*/false,
+ /*Print=*/true, OS))
+ return Err;
+ options().resetPrintFormatting();
+ }
+
+ return Error::success();
+ };
+
+ // If the user has requested printing details for the comparison, we
+ // disable the indentation and the added/missing tags ('+'/'-'), as the
+ // details are just a list of elements.
+ options().resetPrintFormatting();
+
+ PrintHeader(ReferenceRoot, TargetRoot);
+ Reader = ReferenceReader;
+ if (Error Err = CompareViews(ReferenceRoot, TargetRoot))
+ return Err;
+ FirstMissing = true;
+ ReferenceRoot->report(LVComparePass::Missing);
+
+ PrintHeader(TargetRoot, ReferenceRoot);
+ Reader = TargetReader;
+ if (Error Err = CompareViews(TargetRoot, ReferenceRoot))
+ return Err;
+ FirstMissing = true;
+ TargetRoot->report(LVComparePass::Added);
+
+ options().setPrintFormatting();
+
+ // Display a summary with the elements missing and/or added.
+ printSummary();
+ } else {
+ // Perform logical elements comparison. An equal test is apply to each
+ // element. If a difference is found, the reference element is marked as
+ // 'missing'.
+ // The final comparison result will show the 'Reference' scopes tree,
+ // having both missing and added elements.
+ using LVScopeLink = std::map<LVScope *, LVScope *>;
+ LVScopeLink ScopeLinks;
+ auto CompareReaders = [&](LVReader *LHS, LVReader *RHS, LVElements &Set,
+ LVComparePass Pass) -> Error {
+ auto FindMatch = [&](auto &References, auto &Targets,
+ const char *Category) -> Error {
+ LVElements Elements;
+ for (LVElement *Reference : References) {
+ // Report elements that can be printed; ignore logical elements that
+ // have qualifiers.
+ if (Reference->getIncludeInPrint()) {
+ if (Pass == LVComparePass::Missing)
+ updateExpected(Reference);
+ Reference->setIsInCompare();
+ LVElement *CurrentTarget = nullptr;
+ if (std::any_of(Targets.begin(), Targets.end(),
+ [&](auto Target) -> bool {
+ CurrentTarget = Target;
+ return Reference->equals(Target);
+ })) {
+ if (Pass == LVComparePass::Missing && Reference->getIsScope()) {
+ // If the elements being compared are scopes and are a match,
+ // they are recorded, to be used when creating the augmented
+ // tree, as insertion points for the "added" items.
+ ScopeLinks.emplace(static_cast<LVScope *>(CurrentTarget),
+ static_cast<LVScope *>(Reference));
+ }
+ } else {
+ // Element is missing or added.
+ Pass == LVComparePass::Missing ? Reference->setIsMissing()
+ : Reference->setIsAdded();
+ Elements.push_back(Reference);
+ updateMissingOrAdded(Reference, Pass);
+ // Record missing/added element.
+ addPassEntry(Reader, Reference, Pass);
+ }
+ }
+ }
+ if (Pass == LVComparePass::Added)
+ // Record all the current missing elements for this category.
+ Set.insert(Set.end(), Elements.begin(), Elements.end());
+ if (options().getReportList()) {
+ if (Elements.size()) {
+ OS << "\n(" << Elements.size() << ") "
+ << (Pass == LVComparePass::Missing ? "Missing" : "Added") << " "
+ << Category << ":\n";
+ for (const LVElement *Element : Elements) {
+ if (Error Err = Element->doPrint(/*Split=*/false, /*Match=*/false,
+ /*Print=*/true, OS))
+ return Err;
+ }
+ }
+ }
+
+ return Error::success();
+ };
+
+ // First compare the scopes, so they will be inserted at the front of
+ // the missing elements list. When they are moved, their children are
+ // moved as well and no additional work is required.
+ if (options().getCompareScopes())
+ if (Error Err = FindMatch(LHS->getScopes(), RHS->getScopes(), "Scopes"))
+ return Err;
+ if (options().getCompareSymbols())
+ if (Error Err =
+ FindMatch(LHS->getSymbols(), RHS->getSymbols(), "Symbols"))
+ return Err;
+ if (options().getCompareTypes())
+ if (Error Err = FindMatch(LHS->getTypes(), RHS->getTypes(), "Types"))
+ return Err;
+ if (options().getCompareLines())
+ if (Error Err = FindMatch(LHS->getLines(), RHS->getLines(), "Lines"))
+ return Err;
+
+ return Error::success();
+ };
+
+ // If the user has requested printing details for the comparison, we
+ // disable the indentation and the added/missing tags ('+'/'-'), as the
+ // details are just a list of elements.
+ options().resetPrintFormatting();
+
+ PrintHeader(ReferenceRoot, TargetRoot);
+ // Include the root in the expected count.
+ updateExpected(ReferenceRoot);
+
+ LVElements ElementsToAdd;
+ Reader = ReferenceReader;
+ if (Error Err = CompareReaders(ReferenceReader, TargetReader, ElementsToAdd,
+ LVComparePass::Missing))
+ return Err;
+ Reader = TargetReader;
+ if (Error Err = CompareReaders(TargetReader, ReferenceReader, ElementsToAdd,
+ LVComparePass::Added))
+ return Err;
+
+ LLVM_DEBUG({
+ dbgs() << "\nReference/Target Scope links:\n";
+ for (LVScopeLink::const_reference Entry : ScopeLinks)
+ dbgs() << "Source: " << hexSquareString(Entry.first->getOffset()) << " "
+ << "Destination: " << hexSquareString(Entry.second->getOffset())
+ << "\n";
+ dbgs() << "\n";
+ });
+
+ // Add the 'missing' elements from the 'Target' into the 'Reference'.
+ // First insert the missing scopes, as they include any missing children.
+ LVScope *Parent = nullptr;
+ for (LVElement *Element : ElementsToAdd) {
+ LLVM_DEBUG({
+ dbgs() << "Element to Insert: " << hexSquareString(Element->getOffset())
+ << ", Parent: "
+ << hexSquareString(Element->getParentScope()->getOffset())
+ << "\n";
+ });
+ // Skip already inserted elements. They were inserted, if their parents
+ // were missing. When inserting them, all the children are moved.
+ if (Element->getHasMoved())
+ continue;
+
+ // We need to find an insertion point in the reference scopes tree.
+ Parent = Element->getParentScope();
+ if (ScopeLinks.find(Parent) != ScopeLinks.end()) {
+ LVScope *InsertionPoint = ScopeLinks[Parent];
+ LLVM_DEBUG({
+ dbgs() << "Inserted at: "
+ << hexSquareString(InsertionPoint->getOffset()) << "\n";
+ });
+ if (Parent->removeElement(Element)) {
+ // Be sure we have a current compile unit.
+ getReader().setCompileUnit(InsertionPoint->getCompileUnitParent());
+ InsertionPoint->addElement(Element);
+ Element->updateLevel(InsertionPoint, /*Moved=*/true);
+ }
+ }
+ }
+
+ options().setPrintFormatting();
+
+ // Display the augmented reference scopes tree.
+ if (options().getReportAnyView())
+ if (Error Err = ReferenceReader->doPrint())
+ return Err;
+
+ LLVM_DEBUG({
+ dbgs() << "\nModified Reference Reader";
+ if (Error Err = ReferenceReader->doPrint())
+ return Err;
+ dbgs() << "\nModified Target Reader";
+ if (Error Err = TargetReader->doPrint())
+ return Err;
+ });
+
+ // Display a summary with the elements missing and/or added.
+ printSummary();
+ }
+
+ return Error::success();
+}
+
+void LVCompare::printCurrentStack() {
+ for (const LVScope *Scope : ScopeStack) {
+ Scope->printAttributes(OS);
+ OS << Scope->lineNumberAsString(/*ShowZero=*/true) << " " << Scope->kind()
+ << " " << formattedName(Scope->getName()) << "\n";
+ }
+}
+
+void LVCompare::printItem(LVElement *Element, LVComparePass Pass) {
+ // Record expected, missing, added.
+ updateExpected(Element);
+ updateMissingOrAdded(Element, Pass);
+
+ // Record missing/added element.
+ if (Element->getIsMissing())
+ addPassEntry(Reader, Element, Pass);
+
+ if ((!PrintLines && Element->getIsLine()) ||
+ (!PrintScopes && Element->getIsScope()) ||
+ (!PrintSymbols && Element->getIsSymbol()) ||
+ (!PrintTypes && Element->getIsType()))
+ return;
+
+ if (Element->getIsMissing()) {
+ if (FirstMissing) {
+ OS << "\n";
+ FirstMissing = false;
+ }
+
+ StringRef Kind = Element->kind();
+ StringRef Name =
+ Element->getIsLine() ? Element->getPathname() : Element->getName();
+ StringRef Status = (Pass == LVComparePass::Missing) ? "Missing" : "Added";
+ OS << Status << " " << Kind << " '" << Name << "'";
+ if (Element->getLineNumber() > 0)
+ OS << " at line " << Element->getLineNumber();
+ OS << "\n";
+
+ if (options().getReportList()) {
+ printCurrentStack();
+ Element->printAttributes(OS);
+ OS << Element->lineNumberAsString(/*ShowZero=*/true) << " " << Kind << " "
+ << Name << "\n";
+ }
+ }
+}
+
+void LVCompare::printSummary() const {
+ if (!options().getPrintSummary())
+ return;
+ std::string Separator = std::string(40, '-');
+ auto PrintSeparator = [&]() { OS << Separator << "\n"; };
+ auto PrintHeadingRow = [&](const char *T, const char *U, const char *V,
+ const char *W) {
+ OS << format("%-9s%9s %9s %9s\n", T, U, V, W);
+ };
+ auto PrintDataRow = [&](const char *T, unsigned U, unsigned V, unsigned W) {
+ OS << format("%-9s%9d %9d %9d\n", T, U, V, W);
+ };
+
+ OS << "\n";
+ PrintSeparator();
+ PrintHeadingRow("Element", "Expected", "Missing", "Added");
+ PrintSeparator();
+ for (LVCompareInfo::reference Entry : Results) {
+ if (Entry.first == LVCompareItem::Total)
+ PrintSeparator();
+ PrintDataRow(std::get<getHeader()>(Entry.second),
+ std::get<getExpected()>(Entry.second),
+ std::get<getMissing()>(Entry.second),
+ std::get<getAdded()>(Entry.second));
+ }
+}
+
+void LVCompare::print(raw_ostream &OS) const { OS << "LVCompare\n"; }
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp
new file mode 100644
index 000000000000..a320752befc4
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp
@@ -0,0 +1,530 @@
+//===-- LVElement.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVElement class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVElement.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Element"
+
+LVElementDispatch LVElement::Dispatch = {
+ {LVElementKind::Discarded, &LVElement::getIsDiscarded},
+ {LVElementKind::Global, &LVElement::getIsGlobalReference},
+ {LVElementKind::Optimized, &LVElement::getIsOptimized}};
+
+LVType *LVElement::getTypeAsType() const {
+ return ElementType && ElementType->getIsType()
+ ? static_cast<LVType *>(ElementType)
+ : nullptr;
+}
+
+LVScope *LVElement::getTypeAsScope() const {
+ return ElementType && ElementType->getIsScope()
+ ? static_cast<LVScope *>(ElementType)
+ : nullptr;
+}
+
+// Set the element type.
+void LVElement::setGenericType(LVElement *Element) {
+ if (!Element->isTemplateParam()) {
+ setType(Element);
+ return;
+ }
+ // For template parameters, the instance type can be a type or a scope.
+ if (options().getAttributeArgument()) {
+ if (Element->getIsKindType())
+ setType(Element->getTypeAsType());
+ else if (Element->getIsKindScope())
+ setType(Element->getTypeAsScope());
+ } else
+ setType(Element);
+}
+
+// Discriminator as string.
+std::string LVElement::discriminatorAsString() const {
+ uint32_t Discriminator = getDiscriminator();
+ std::string String;
+ raw_string_ostream Stream(String);
+ if (Discriminator && options().getAttributeDiscriminator())
+ Stream << "," << Discriminator;
+ return String;
+}
+
+// Get the type as a string.
+StringRef LVElement::typeAsString() const {
+ return getHasType() ? getTypeName() : typeVoid();
+}
+
+// Get name for element type.
+StringRef LVElement::getTypeName() const {
+ return ElementType ? ElementType->getName() : StringRef();
+}
+
+static size_t getStringIndex(StringRef Name) {
+ // Convert the name to Unified format ('\' have been converted into '/').
+ std::string Pathname(transformPath(Name));
+
+ // Depending on the --attribute=filename and --attribute=pathname command
+ // line options, use the basename or the full pathname as the name.
+ if (!options().getAttributePathname()) {
+ // Get the basename by ignoring any prefix up to the last slash ('/').
+ StringRef Basename = Pathname;
+ size_t Pos = Basename.rfind('/');
+ if (Pos != std::string::npos)
+ Basename = Basename.substr(Pos + 1);
+ return getStringPool().getIndex(Basename);
+ }
+
+ return getStringPool().getIndex(Pathname);
+}
+
+void LVElement::setName(StringRef ElementName) {
+ // In the case of Root or Compile Unit, get index for the flatted out name.
+ NameIndex = getTransformName() ? getStringIndex(ElementName)
+ : getStringPool().getIndex(ElementName);
+}
+
+void LVElement::setFilename(StringRef Filename) {
+ // Get index for the flattened out filename.
+ FilenameIndex = getStringIndex(Filename);
+}
+
+// Return the string representation of a DIE offset.
+std::string LVElement::typeOffsetAsString() const {
+ if (options().getAttributeOffset()) {
+ LVElement *Element = getType();
+ return hexSquareString(Element ? Element->getOffset() : 0);
+ }
+ return {};
+}
+
+StringRef LVElement::accessibilityString(uint32_t Access) const {
+ uint32_t Value = getAccessibilityCode();
+ switch (Value ? Value : Access) {
+ case dwarf::DW_ACCESS_public:
+ return "public";
+ case dwarf::DW_ACCESS_protected:
+ return "protected";
+ case dwarf::DW_ACCESS_private:
+ return "private";
+ default:
+ return StringRef();
+ }
+}
+
+StringRef LVElement::externalString() const {
+ return getIsExternal() ? "extern" : StringRef();
+}
+
+StringRef LVElement::inlineCodeString(uint32_t Code) const {
+ uint32_t Value = getInlineCode();
+ switch (Value ? Value : Code) {
+ case dwarf::DW_INL_not_inlined:
+ return "not_inlined";
+ case dwarf::DW_INL_inlined:
+ return "inlined";
+ case dwarf::DW_INL_declared_not_inlined:
+ return "declared_not_inlined";
+ case dwarf::DW_INL_declared_inlined:
+ return "declared_inlined";
+ default:
+ return StringRef();
+ }
+}
+
+StringRef LVElement::virtualityString(uint32_t Virtuality) const {
+ uint32_t Value = getVirtualityCode();
+ switch (Value ? Value : Virtuality) {
+ case dwarf::DW_VIRTUALITY_none:
+ return StringRef();
+ case dwarf::DW_VIRTUALITY_virtual:
+ return "virtual";
+ case dwarf::DW_VIRTUALITY_pure_virtual:
+ return "pure virtual";
+ default:
+ return StringRef();
+ }
+}
+
+void LVElement::resolve() {
+ if (getIsResolved())
+ return;
+ setIsResolved();
+
+ resolveReferences();
+ resolveParents();
+ resolveExtra();
+ resolveName();
+}
+
+// Set File/Line using the specification element.
+void LVElement::setFileLine(LVElement *Specification) {
+ // In the case of inlined functions, the correct scope must be associated
+ // with the file and line information of the outline version.
+ if (!isLined()) {
+ setLineNumber(Specification->getLineNumber());
+ setIsLineFromReference();
+ }
+ if (!isFiled()) {
+ setFilenameIndex(Specification->getFilenameIndex());
+ setIsFileFromReference();
+ }
+}
+
+void LVElement::resolveName() {
+ // Set the qualified name if requested.
+ if (options().getAttributeQualified())
+ resolveQualifiedName();
+
+ setIsResolvedName();
+}
+
+// Resolve any parents.
+void LVElement::resolveParents() {
+ if (isRoot() || isCompileUnit())
+ return;
+
+ LVScope *Parent = getParentScope();
+ if (Parent && !Parent->getIsCompileUnit())
+ Parent->resolve();
+}
+
+// Generate a name for unnamed elements.
+void LVElement::generateName(std::string &Prefix) const {
+ LVScope *Scope = getParentScope();
+ if (!Scope)
+ return;
+
+ // Use its parent name and any line information.
+ Prefix.append(std::string(Scope->getName()));
+ Prefix.append("::");
+ Prefix.append(isLined() ? lineNumberAsString(/*ShowZero=*/true) : "?");
+
+ // Remove any whitespaces.
+ Prefix.erase(std::remove_if(Prefix.begin(), Prefix.end(), ::isspace),
+ Prefix.end());
+}
+
+// Generate a name for unnamed elements.
+void LVElement::generateName() {
+ setIsAnonymous();
+ std::string Name;
+ generateName(Name);
+ setName(Name);
+ setIsGeneratedName();
+}
+
+void LVElement::updateLevel(LVScope *Parent, bool Moved) {
+ setLevel(Parent->getLevel() + 1);
+ if (Moved)
+ setHasMoved();
+}
+
+// Generate the full name for the element, to include special qualifiers.
+void LVElement::resolveFullname(LVElement *BaseType, StringRef Name) {
+ // For the following sample code,
+ // void *p;
+ // some compilers do not generate an attribute for the associated type:
+ // DW_TAG_variable
+ // DW_AT_name 'p'
+ // DW_AT_type $1
+ // ...
+ // $1: DW_TAG_pointer_type
+ // ...
+ // For those cases, generate the implicit 'void' type.
+ StringRef BaseTypename = BaseType ? BaseType->getName() : emptyString();
+ bool GetBaseTypename = false;
+ bool UseBaseTypename = true;
+ bool UseNameText = true;
+
+ switch (getTag()) {
+ case dwarf::DW_TAG_pointer_type: // "*";
+ if (!BaseType)
+ BaseTypename = typeVoid();
+ break;
+ case dwarf::DW_TAG_const_type: // "const"
+ case dwarf::DW_TAG_ptr_to_member_type: // "*"
+ case dwarf::DW_TAG_rvalue_reference_type: // "&&"
+ case dwarf::DW_TAG_reference_type: // "&"
+ case dwarf::DW_TAG_restrict_type: // "restrict"
+ case dwarf::DW_TAG_volatile_type: // "volatile"
+ case dwarf::DW_TAG_unaligned: // "unaligned"
+ break;
+ case dwarf::DW_TAG_base_type:
+ case dwarf::DW_TAG_compile_unit:
+ case dwarf::DW_TAG_class_type:
+ case dwarf::DW_TAG_enumerator:
+ case dwarf::DW_TAG_namespace:
+ case dwarf::DW_TAG_skeleton_unit:
+ case dwarf::DW_TAG_structure_type:
+ case dwarf::DW_TAG_union_type:
+ case dwarf::DW_TAG_unspecified_type:
+ case dwarf::DW_TAG_GNU_template_parameter_pack:
+ GetBaseTypename = true;
+ break;
+ case dwarf::DW_TAG_array_type:
+ case dwarf::DW_TAG_call_site:
+ case dwarf::DW_TAG_entry_point:
+ case dwarf::DW_TAG_enumeration_type:
+ case dwarf::DW_TAG_GNU_call_site:
+ case dwarf::DW_TAG_imported_module:
+ case dwarf::DW_TAG_imported_declaration:
+ case dwarf::DW_TAG_inlined_subroutine:
+ case dwarf::DW_TAG_label:
+ case dwarf::DW_TAG_subprogram:
+ case dwarf::DW_TAG_subrange_type:
+ case dwarf::DW_TAG_subroutine_type:
+ case dwarf::DW_TAG_typedef:
+ GetBaseTypename = true;
+ UseBaseTypename = false;
+ break;
+ case dwarf::DW_TAG_template_type_parameter:
+ case dwarf::DW_TAG_template_value_parameter:
+ UseBaseTypename = false;
+ break;
+ case dwarf::DW_TAG_GNU_template_template_param:
+ break;
+ case dwarf::DW_TAG_catch_block:
+ case dwarf::DW_TAG_lexical_block:
+ case dwarf::DW_TAG_try_block:
+ UseNameText = false;
+ break;
+ default:
+ llvm_unreachable("Invalid type.");
+ return;
+ break;
+ }
+
+ // Overwrite if no given value. 'Name' is empty when resolving for scopes
+ // and symbols. In the case of types, it represents the type base name.
+ if (Name.empty() && GetBaseTypename)
+ Name = getName();
+
+ // Concatenate the elements to get the full type name.
+ // Type will be: base_parent + pre + base + parent + post.
+ std::string Fullname;
+
+ if (UseNameText && Name.size())
+ Fullname.append(std::string(Name));
+ if (UseBaseTypename && BaseTypename.size()) {
+ if (UseNameText && Name.size())
+ Fullname.append(" ");
+ Fullname.append(std::string(BaseTypename));
+ }
+
+ // For a better and consistent layout, check if the generated name
+ // contains double space sequences.
+ assert((Fullname.find(" ", 0) == std::string::npos) &&
+ "Extra double spaces in name.");
+
+ LLVM_DEBUG({ dbgs() << "Fullname = '" << Fullname << "'\n"; });
+ setName(Fullname);
+}
+
+void LVElement::setFile(LVElement *Reference) {
+ if (!options().getAttributeAnySource())
+ return;
+
+ // At this point, any existing reference to another element, have been
+ // resolved and the file ID extracted from the DI entry.
+ if (Reference)
+ setFileLine(Reference);
+
+ // The file information is used to show the source file for any element
+ // and display any new source file in relation to its parent element.
+ // a) Elements that are not inlined.
+ // - We record the DW_AT_decl_line and DW_AT_decl_file.
+ // b) Elements that are inlined.
+ // - We record the DW_AT_decl_line and DW_AT_decl_file.
+ // - We record the DW_AT_call_line and DW_AT_call_file.
+ // For both cases, we use the DW_AT_decl_file value to detect any changes
+ // in the source filename containing the element. Changes on this value
+ // indicates that the element being printed is not contained in the
+ // previous printed filename.
+
+ // The source files are indexed starting at 0, but DW_AT_decl_file defines
+ // that 0 means no file; a value of 1 means the 0th entry.
+ size_t Index = 0;
+
+ // An element with no source file information will use the reference
+ // attribute (DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension)
+ // to update its information.
+ if (getIsFileFromReference() && Reference) {
+ Index = Reference->getFilenameIndex();
+ if (Reference->getInvalidFilename())
+ setInvalidFilename();
+ setFilenameIndex(Index);
+ return;
+ }
+
+ // The source files are indexed starting at 0, but DW_AT_decl_file
+ // defines that 0 means no file; a value of 1 means the 0th entry.
+ Index = getFilenameIndex();
+ if (Index) {
+ StringRef Filename = getReader().getFilename(this, Index);
+ Filename.size() ? setFilename(Filename) : setInvalidFilename();
+ }
+}
+
+LVScope *LVElement::traverseParents(LVScopeGetFunction GetFunction) const {
+ LVScope *Parent = getParentScope();
+ while (Parent && !(Parent->*GetFunction)())
+ Parent = Parent->getParentScope();
+ return Parent;
+}
+
+LVScope *LVElement::getFunctionParent() const {
+ return traverseParents(&LVScope::getIsFunction);
+}
+
+LVScope *LVElement::getCompileUnitParent() const {
+ return traverseParents(&LVScope::getIsCompileUnit);
+}
+
+// Resolve the qualified name to include the parent hierarchy names.
+void LVElement::resolveQualifiedName() {
+ if (!getIsReferencedType() || isBase() || getQualifiedResolved() ||
+ !getIncludeInPrint())
+ return;
+
+ std::string Name;
+
+ // Get the qualified name, excluding the Compile Unit.
+ LVScope *Parent = getParentScope();
+ if (Parent && !Parent->getIsRoot()) {
+ while (Parent && !Parent->getIsCompileUnit()) {
+ Name.insert(0, "::");
+ if (Parent->isNamed())
+ Name.insert(0, std::string(Parent->getName()));
+ else {
+ std::string Temp;
+ Parent->generateName(Temp);
+ Name.insert(0, Temp);
+ }
+ Parent = Parent->getParentScope();
+ }
+ }
+
+ if (Name.size()) {
+ setQualifiedName(Name);
+ setQualifiedResolved();
+ }
+ LLVM_DEBUG({
+ dbgs() << "Offset: " << hexSquareString(getOffset())
+ << ", Kind: " << formattedKind(kind())
+ << ", Name: " << formattedName(getName())
+ << ", QualifiedName: " << formattedName(Name) << "\n";
+ });
+}
+
+bool LVElement::referenceMatch(const LVElement *Element) const {
+ return (getHasReference() && Element->getHasReference()) ||
+ (!getHasReference() && !Element->getHasReference());
+}
+
+bool LVElement::equals(const LVElement *Element) const {
+ // The minimum factors that must be the same for an equality are:
+ // line number, level, name, qualified name and filename.
+ LLVM_DEBUG({
+ dbgs() << "\n[Element::equals]\n";
+ if (options().getAttributeOffset()) {
+ dbgs() << "Reference: " << hexSquareString(getOffset()) << "\n";
+ dbgs() << "Target : " << hexSquareString(Element->getOffset()) << "\n";
+ }
+ dbgs() << "Reference: "
+ << "Kind = " << formattedKind(kind()) << ", "
+ << "Name = " << formattedName(getName()) << ", "
+ << "Qualified = " << formattedName(getQualifiedName()) << "\n"
+ << "Target : "
+ << "Kind = " << formattedKind(Element->kind()) << ", "
+ << "Name = " << formattedName(Element->getName()) << ", "
+ << "Qualified = " << formattedName(Element->getQualifiedName())
+ << "\n"
+ << "Reference: "
+ << "NameIndex = " << getNameIndex() << ", "
+ << "QualifiedNameIndex = " << getQualifiedNameIndex() << ", "
+ << "FilenameIndex = " << getFilenameIndex() << "\n"
+ << "Target : "
+ << "NameIndex = " << Element->getNameIndex() << ", "
+ << "QualifiedNameIndex = " << Element->getQualifiedNameIndex()
+ << ", "
+ << "FilenameIndex = " << Element->getFilenameIndex() << "\n";
+ });
+ if ((getLineNumber() != Element->getLineNumber()) ||
+ (getLevel() != Element->getLevel()))
+ return false;
+
+ if ((getQualifiedNameIndex() != Element->getQualifiedNameIndex()) ||
+ (getNameIndex() != Element->getNameIndex()) ||
+ (getFilenameIndex() != Element->getFilenameIndex()))
+ return false;
+
+ if (!getType() && !Element->getType())
+ return true;
+ if (getType() && Element->getType())
+ return getType()->equals(Element->getType());
+ return false;
+}
+
+// Print the FileName Index.
+void LVElement::printFileIndex(raw_ostream &OS, bool Full) const {
+ if (options().getPrintFormatting() && options().getAttributeAnySource() &&
+ getFilenameIndex()) {
+
+ // Check if there is a change in the File ID sequence.
+ size_t Index = getFilenameIndex();
+ if (options().changeFilenameIndex(Index)) {
+ // Just to keep a nice layout.
+ OS << "\n";
+ printAttributes(OS, /*Full=*/false);
+
+ OS << " {Source} ";
+ if (getInvalidFilename())
+ OS << format("[0x%08x]\n", Index);
+ else
+ OS << formattedName(getPathname()) << "\n";
+ }
+ }
+}
+
+void LVElement::printReference(raw_ostream &OS, bool Full,
+ LVElement *Parent) const {
+ if (options().getPrintFormatting() && options().getAttributeReference())
+ printAttributes(OS, Full, "{Reference} ", Parent,
+ referenceAsString(getLineNumber(), /*Spaces=*/false),
+ /*UseQuotes=*/false, /*PrintRef=*/true);
+}
+
+void LVElement::printLinkageName(raw_ostream &OS, bool Full,
+ LVElement *Parent) const {
+ if (options().getPrintFormatting() && options().getAttributeLinkage()) {
+ printAttributes(OS, Full, "{Linkage} ", Parent, getLinkageName(),
+ /*UseQuotes=*/true, /*PrintRef=*/false);
+ }
+}
+
+void LVElement::printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent,
+ LVScope *Scope) const {
+ if (options().getPrintFormatting() && options().getAttributeLinkage()) {
+ LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);
+ std::string Text = (Twine(" 0x") + Twine::utohexstr(SectionIndex) +
+ Twine(" '") + Twine(getLinkageName()) + Twine("'"))
+ .str();
+ printAttributes(OS, Full, "{Linkage} ", Parent, Text,
+ /*UseQuotes=*/false, /*PrintRef=*/false);
+ }
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp
new file mode 100644
index 000000000000..c3810d282abc
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp
@@ -0,0 +1,222 @@
+//===-- LVLine.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVLine class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Line"
+
+namespace {
+const char *const KindBasicBlock = "BasicBlock";
+const char *const KindDiscriminator = "Discriminator";
+const char *const KindEndSequence = "EndSequence";
+const char *const KindEpilogueBegin = "EpilogueBegin";
+const char *const KindLineDebug = "Line";
+const char *const KindLineSource = "Code";
+const char *const KindNewStatement = "NewStatement";
+const char *const KindPrologueEnd = "PrologueEnd";
+const char *const KindUndefined = "Undefined";
+const char *const KindAlwaysStepInto = "AlwaysStepInto"; // CodeView
+const char *const KindNeverStepInto = "NeverStepInto"; // CodeView
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Logical line.
+//===----------------------------------------------------------------------===//
+// Return a string representation for the line kind.
+const char *LVLine::kind() const {
+ const char *Kind = KindUndefined;
+ if (getIsLineDebug())
+ Kind = KindLineDebug;
+ else if (getIsLineAssembler())
+ Kind = KindLineSource;
+ return Kind;
+}
+
+LVLineDispatch LVLine::Dispatch = {
+ {LVLineKind::IsBasicBlock, &LVLine::getIsBasicBlock},
+ {LVLineKind::IsDiscriminator, &LVLine::getIsDiscriminator},
+ {LVLineKind::IsEndSequence, &LVLine::getIsEndSequence},
+ {LVLineKind::IsLineDebug, &LVLine::getIsLineDebug},
+ {LVLineKind::IsLineAssembler, &LVLine::getIsLineAssembler},
+ {LVLineKind::IsNewStatement, &LVLine::getIsNewStatement},
+ {LVLineKind::IsEpilogueBegin, &LVLine::getIsEpilogueBegin},
+ {LVLineKind::IsPrologueEnd, &LVLine::getIsPrologueEnd},
+ {LVLineKind::IsAlwaysStepInto, &LVLine::getIsAlwaysStepInto},
+ {LVLineKind::IsNeverStepInto, &LVLine::getIsNeverStepInto}};
+
+// String used as padding for printing elements with no line number.
+std::string LVLine::noLineAsString(bool ShowZero) const {
+ if (options().getInternalNone())
+ return LVObject::noLineAsString(ShowZero);
+ return (ShowZero || options().getAttributeZero()) ? (" 0 ")
+ : (" - ");
+}
+
+void LVLine::markMissingParents(const LVLines *References,
+ const LVLines *Targets) {
+ if (!(References && Targets))
+ return;
+
+ LLVM_DEBUG({
+ dbgs() << "\n[LVLine::markMissingParents]\n";
+ for (const LVLine *Reference : *References)
+ dbgs() << "References: "
+ << "Kind = " << formattedKind(Reference->kind()) << ", "
+ << "Line = " << Reference->getLineNumber() << "\n";
+ for (const LVLine *Target : *Targets)
+ dbgs() << "Targets : "
+ << "Kind = " << formattedKind(Target->kind()) << ", "
+ << "Line = " << Target->getLineNumber() << "\n";
+ });
+
+ for (LVLine *Reference : *References) {
+ LLVM_DEBUG({
+ dbgs() << "Search Reference: Line = " << Reference->getLineNumber()
+ << "\n";
+ });
+ if (!Reference->findIn(Targets))
+ Reference->markBranchAsMissing();
+ }
+}
+
+LVLine *LVLine::findIn(const LVLines *Targets) const {
+ if (!Targets)
+ return nullptr;
+
+ LLVM_DEBUG({
+ dbgs() << "\n[LVLine::findIn]\n"
+ << "Reference: "
+ << "Level = " << getLevel() << ", "
+ << "Kind = " << formattedKind(kind()) << ", "
+ << "Line = " << getLineNumber() << "\n";
+ for (const LVLine *Target : *Targets)
+ dbgs() << "Target : "
+ << "Level = " << Target->getLevel() << ", "
+ << "Kind = " << formattedKind(Target->kind()) << ", "
+ << "Line = " << Target->getLineNumber() << "\n";
+ });
+
+ for (LVLine *Line : *Targets)
+ if (equals(Line))
+ return Line;
+
+ return nullptr;
+}
+
+bool LVLine::equals(const LVLine *Line) const {
+ return LVElement::equals(Line);
+}
+
+bool LVLine::equals(const LVLines *References, const LVLines *Targets) {
+ if (!References && !Targets)
+ return true;
+ if (References && Targets && References->size() == Targets->size()) {
+ for (const LVLine *Reference : *References)
+ if (!Reference->findIn(Targets))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+void LVLine::report(LVComparePass Pass) {
+ getComparator().printItem(this, Pass);
+}
+
+void LVLine::print(raw_ostream &OS, bool Full) const {
+ if (getReader().doPrintLine(this)) {
+ getReaderCompileUnit()->incrementPrintedLines();
+ LVElement::print(OS, Full);
+ printExtra(OS, Full);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF line record.
+//===----------------------------------------------------------------------===//
+std::string LVLineDebug::statesInfo(bool Formatted) const {
+ // Returns the DWARF extra qualifiers.
+ std::string String;
+ raw_string_ostream Stream(String);
+
+ std::string Separator = Formatted ? " " : "";
+ if (getIsNewStatement()) {
+ Stream << Separator << "{" << KindNewStatement << "}";
+ Separator = " ";
+ }
+ if (getIsDiscriminator()) {
+ Stream << Separator << "{" << KindDiscriminator << "}";
+ Separator = " ";
+ }
+ if (getIsBasicBlock()) {
+ Stream << Separator << "{" << KindBasicBlock << "}";
+ Separator = " ";
+ }
+ if (getIsEndSequence()) {
+ Stream << Separator << "{" << KindEndSequence << "}";
+ Separator = " ";
+ }
+ if (getIsEpilogueBegin()) {
+ Stream << Separator << "{" << KindEpilogueBegin << "}";
+ Separator = " ";
+ }
+ if (getIsPrologueEnd()) {
+ Stream << Separator << "{" << KindPrologueEnd << "}";
+ Separator = " ";
+ }
+ if (getIsAlwaysStepInto()) {
+ Stream << Separator << "{" << KindAlwaysStepInto << "}";
+ Separator = " ";
+ }
+ if (getIsNeverStepInto()) {
+ Stream << Separator << "{" << KindNeverStepInto << "}";
+ Separator = " ";
+ }
+
+ return String;
+}
+
+bool LVLineDebug::equals(const LVLine *Line) const {
+ if (!LVLine::equals(Line))
+ return false;
+ return getFilenameIndex() == Line->getFilenameIndex();
+}
+
+void LVLineDebug::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind());
+
+ if (options().getAttributeQualifier()) {
+ // The qualifier includes the states information and the source filename
+ // that contains the line element.
+ OS << statesInfo(/*Formatted=*/true);
+ OS << " " << formattedName(getPathname());
+ }
+ OS << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// Assembler line extracted from the ELF .text section.
+//===----------------------------------------------------------------------===//
+bool LVLineAssembler::equals(const LVLine *Line) const {
+ return LVLine::equals(Line);
+}
+
+void LVLineAssembler::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind());
+ OS << " " << formattedName(getName());
+ OS << "\n";
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
new file mode 100644
index 000000000000..115b903c6c7f
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
@@ -0,0 +1,675 @@
+//===-- LVLocation.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVOperation and LVLocation classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Location"
+
+void LVOperation::print(raw_ostream &OS, bool Full) const {}
+
+// Identify the most common type of operations and print them using a high
+// level format, trying to isolate the DWARF complexity.
+std::string LVOperation::getOperandsDWARFInfo() {
+ std::string String;
+ raw_string_ostream Stream(String);
+
+ auto PrintRegisterInfo = [&](LVSmall Code) {
+ //-----------------------------------------------------------------------
+ // 2.5.1.1 Literal encodings.
+ //-----------------------------------------------------------------------
+ if (dwarf::DW_OP_lit0 <= Code && Code <= dwarf::DW_OP_lit31) {
+ Stream << format("lit%d", Code - dwarf::DW_OP_lit0);
+ return;
+ }
+
+ //-----------------------------------------------------------------------
+ // 2.5.1.2 Register values.
+ //-----------------------------------------------------------------------
+ if (dwarf::DW_OP_breg0 <= Code && Code <= dwarf::DW_OP_breg31) {
+ std::string RegisterName(getReader().getRegisterName(Code, Operands));
+ Stream << format("breg%d+%d%s", Code - dwarf::DW_OP_breg0, Operands[0],
+ RegisterName.c_str());
+ return;
+ }
+
+ //-----------------------------------------------------------------------
+ // 2.6.1.1.3 Register location descriptions.
+ //-----------------------------------------------------------------------
+ if (dwarf::DW_OP_reg0 <= Code && Code <= dwarf::DW_OP_reg31) {
+ std::string RegisterName(getReader().getRegisterName(Code, Operands));
+ Stream << format("reg%d%s", Code - dwarf::DW_OP_reg0,
+ RegisterName.c_str());
+ return;
+ }
+
+ Stream << format("#0x%02x ", Code) << hexString(Operands[0]) << " "
+ << hexString(Operands[1]) << "#";
+ };
+
+ switch (Opcode) {
+ //-------------------------------------------------------------------------
+ // 2.5.1.1 Literal encodings.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_addr:
+ Stream << "addr " << hexString(Operands[0]);
+ break;
+ case dwarf::DW_OP_constu:
+ case dwarf::DW_OP_const1u:
+ case dwarf::DW_OP_const2u:
+ case dwarf::DW_OP_const4u:
+ case dwarf::DW_OP_const8u:
+ Stream << "const_u " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_consts:
+ case dwarf::DW_OP_const1s:
+ case dwarf::DW_OP_const2s:
+ case dwarf::DW_OP_const4s:
+ case dwarf::DW_OP_const8s:
+ Stream << "const_s " << int(Operands[0]);
+ break;
+ case dwarf::DW_OP_addrx:
+ Stream << "addrx " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_constx:
+ Stream << "constx " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_const_type:
+ Stream << "TODO: DW_OP_const_type";
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.5.1.2 Register values.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_fbreg:
+ Stream << "fbreg " << int(Operands[0]);
+ break;
+ case dwarf::DW_OP_bregx: {
+ std::string RegisterName(getReader().getRegisterName(Opcode, Operands));
+ Stream << format("bregx %d%s+%d", Operands[0], RegisterName.c_str(),
+ unsigned(Operands[1]));
+ break;
+ }
+ case dwarf::DW_OP_regval_type: {
+ std::string RegisterName(getReader().getRegisterName(Opcode, Operands));
+ Stream << format("regval_type %d%s+%d", Operands[0], RegisterName.c_str(),
+ unsigned(Operands[1]));
+ break;
+ }
+
+ //-------------------------------------------------------------------------
+ // 2.5.1.3 Stack operations.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_dup:
+ Stream << "dup";
+ break;
+ case dwarf::DW_OP_drop:
+ Stream << "drop";
+ break;
+ case dwarf::DW_OP_pick:
+ Stream << "pick " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_over:
+ Stream << "over";
+ break;
+ case dwarf::DW_OP_swap:
+ Stream << "swap";
+ break;
+ case dwarf::DW_OP_rot:
+ Stream << "rot";
+ break;
+ case dwarf::DW_OP_deref:
+ Stream << "deref";
+ break;
+ case dwarf::DW_OP_deref_size:
+ Stream << "deref_size " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_deref_type:
+ Stream << "deref_type " << unsigned(Operands[0]) << " DIE offset "
+ << hexString(Operands[1]);
+ break;
+ case dwarf::DW_OP_xderef:
+ Stream << "xderef";
+ break;
+ case dwarf::DW_OP_xderef_size:
+ Stream << "xderef_size " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_xderef_type:
+ Stream << "xderef_type " << unsigned(Operands[0]) << " DIE offset "
+ << hexString(Operands[1]);
+ break;
+ case dwarf::DW_OP_push_object_address:
+ Stream << "push_object_address";
+ break;
+ case dwarf::DW_OP_form_tls_address:
+ Stream << "form_tls_address " << hexString(Operands[0]);
+ break;
+ case dwarf::DW_OP_call_frame_cfa:
+ Stream << "call_frame_cfa";
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.5.1.4 Arithmetic and Logical Operations.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_abs:
+ Stream << "abs";
+ break;
+ case dwarf::DW_OP_and:
+ Stream << "and";
+ break;
+ case dwarf::DW_OP_div:
+ Stream << "div";
+ break;
+ case dwarf::DW_OP_minus:
+ Stream << "minus";
+ break;
+ case dwarf::DW_OP_mod:
+ Stream << "mod";
+ break;
+ case dwarf::DW_OP_mul:
+ Stream << "mul";
+ break;
+ case dwarf::DW_OP_neg:
+ Stream << "neg";
+ break;
+ case dwarf::DW_OP_not:
+ Stream << "not";
+ break;
+ case dwarf::DW_OP_or:
+ Stream << "or";
+ break;
+ case dwarf::DW_OP_plus:
+ Stream << "plus";
+ break;
+ case dwarf::DW_OP_plus_uconst:
+ Stream << "plus_uconst " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_shl:
+ Stream << "shl";
+ break;
+ case dwarf::DW_OP_shr:
+ Stream << "shr";
+ break;
+ case dwarf::DW_OP_shra:
+ Stream << "shra";
+ break;
+ case dwarf::DW_OP_xor:
+ Stream << "xor";
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.5.1.5 Control Flow Operations.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_le:
+ Stream << "le";
+ break;
+ case dwarf::DW_OP_ge:
+ Stream << "ge";
+ break;
+ case dwarf::DW_OP_eq:
+ Stream << "eq";
+ break;
+ case dwarf::DW_OP_lt:
+ Stream << "lt";
+ break;
+ case dwarf::DW_OP_gt:
+ Stream << "gt";
+ break;
+ case dwarf::DW_OP_ne:
+ Stream << "ne";
+ break;
+ case dwarf::DW_OP_skip:
+ Stream << "skip " << signed(Operands[0]);
+ break;
+ case dwarf::DW_OP_bra:
+ Stream << "bra " << signed(Operands[0]);
+ break;
+ case dwarf::DW_OP_call2:
+ Stream << "call2 DIE offset " << hexString(Operands[0]);
+ break;
+ case dwarf::DW_OP_call4:
+ Stream << "call4 DIE offset " << hexString(Operands[0]);
+ break;
+ case dwarf::DW_OP_call_ref:
+ Stream << "call_ref DIE offset " << hexString(Operands[0]);
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.5.1.6 Type Conversions.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_convert:
+ Stream << "convert DIE offset " << hexString(Operands[0]);
+ break;
+ case dwarf::DW_OP_reinterpret:
+ Stream << "reinterpret DIE offset " << hexString(Operands[0]);
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.5.1.7 Special Operations.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_nop:
+ Stream << "nop";
+ break;
+ case dwarf::DW_OP_entry_value:
+ Stream << "TODO: DW_OP_entry_value";
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.6.1.1.3 Register location descriptions.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_regx:
+ Stream << "regx" << getReader().getRegisterName(Opcode, Operands);
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.6.1.1.4 Implicit location descriptions.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_stack_value:
+ Stream << "stack_value";
+ break;
+ case dwarf::DW_OP_implicit_value:
+ Stream << "TODO: DW_OP_implicit_value";
+ break;
+ case dwarf::DW_OP_implicit_pointer:
+ Stream << "implicit_pointer DIE offset " << hexString(Operands[0]) << " "
+ << int(Operands[1]);
+ break;
+
+ //-------------------------------------------------------------------------
+ // 2.6.1.2 Composite location descriptions.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_piece:
+ Stream << "piece " << int(Operands[0]);
+ break;
+ case dwarf::DW_OP_bit_piece:
+ Stream << "bit_piece " << int(Operands[0]) << " offset "
+ << int(Operands[1]);
+ break;
+
+ //-------------------------------------------------------------------------
+ // GNU extensions.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_GNU_entry_value:
+ Stream << "gnu_entry_value ";
+ PrintRegisterInfo(dwarf::DW_OP_reg0);
+ break;
+ case dwarf::DW_OP_GNU_push_tls_address:
+ Stream << "gnu_push_tls_address " << hexString(Operands[0]);
+ break;
+ case dwarf::DW_OP_GNU_addr_index:
+ Stream << "gnu_addr_index " << unsigned(Operands[0]);
+ break;
+ case dwarf::DW_OP_GNU_const_index:
+ Stream << "gnu_const_index " << unsigned(Operands[0]);
+ break;
+
+ //-------------------------------------------------------------------------
+ // Member location.
+ //-------------------------------------------------------------------------
+ case LVLocationMemberOffset:
+ Stream << "offset " << int(Operands[0]);
+ break;
+
+ //-------------------------------------------------------------------------
+ // Missing location.
+ //-------------------------------------------------------------------------
+ case dwarf::DW_OP_hi_user:
+ Stream << "missing";
+ break;
+
+ //-------------------------------------------------------------------------
+ // Register values.
+ //-------------------------------------------------------------------------
+ default:
+ PrintRegisterInfo(Opcode);
+ break;
+ }
+
+ return String;
+}
+
+// Identify the most common type of operations and print them using a high
+// level format, trying to isolate the CodeView complexity.
+std::string LVOperation::getOperandsCodeViewInfo() {
+ std::string String;
+ raw_string_ostream Stream(String);
+
+ // Get original CodeView operation code.
+ uint16_t OperationCode = getCodeViewOperationCode(Opcode);
+
+ switch (OperationCode) {
+ // Operands: [Offset, 0].
+ case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL:
+ Stream << "frame_pointer_rel " << int(Operands[0]);
+ break;
+ case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
+ Stream << "frame_pointer_rel_full_scope " << int(Operands[0]);
+ break;
+
+ // Operands: [Register, 0].
+ case codeview::SymbolKind::S_DEFRANGE_REGISTER:
+ Stream << "register " << getReader().getRegisterName(Opcode, Operands);
+ break;
+ case codeview::SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER:
+ Stream << "subfield_register "
+ << getReader().getRegisterName(Opcode, Operands);
+ break;
+
+ // Operands: [Register, Offset].
+ case codeview::SymbolKind::S_DEFRANGE_REGISTER_REL:
+ Stream << "register_rel " << getReader().getRegisterName(Opcode, Operands)
+ << " offset " << int(Operands[1]);
+ break;
+
+ // Operands: [Program, 0].
+ case codeview::SymbolKind::S_DEFRANGE:
+ Stream << "frame " << int(Operands[0]);
+ break;
+ case codeview::SymbolKind::S_DEFRANGE_SUBFIELD:
+ Stream << "subfield " << int(Operands[0]);
+ break;
+
+ default:
+ Stream << format("#0x%02x: ", Opcode) << hexString(Operands[0]) << " "
+ << hexString(Operands[1]) << "#";
+ break;
+ }
+
+ return String;
+}
+
+namespace {
+const char *const KindBaseClassOffset = "BaseClassOffset";
+const char *const KindBaseClassStep = "BaseClassStep";
+const char *const KindClassOffset = "ClassOffset";
+const char *const KindFixedAddress = "FixedAddress";
+const char *const KindMissingInfo = "Missing";
+const char *const KindOperation = "Operation";
+const char *const KindOperationList = "OperationList";
+const char *const KindRegister = "Register";
+const char *const KindUndefined = "Undefined";
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// DWARF location information.
+//===----------------------------------------------------------------------===//
+const char *LVLocation::kind() const {
+ const char *Kind = KindUndefined;
+ if (getIsBaseClassOffset())
+ Kind = KindBaseClassOffset;
+ else if (getIsBaseClassStep())
+ Kind = KindBaseClassStep;
+ else if (getIsClassOffset())
+ Kind = KindClassOffset;
+ else if (getIsFixedAddress())
+ Kind = KindFixedAddress;
+ else if (getIsGapEntry())
+ Kind = KindMissingInfo;
+ else if (getIsOperation())
+ Kind = KindOperation;
+ else if (getIsOperationList())
+ Kind = KindOperationList;
+ else if (getIsRegister())
+ Kind = KindRegister;
+ return Kind;
+}
+
+std::string LVLocation::getIntervalInfo() const {
+ static const char *const Question = "?";
+ std::string String;
+ raw_string_ostream Stream(String);
+ if (getIsAddressRange())
+ Stream << "{Range}";
+
+ auto PrintLine = [&](const LVLine *Line) {
+ if (Line) {
+ std::string TheLine;
+ TheLine = Line->lineNumberAsStringStripped();
+ Stream << TheLine.c_str();
+ } else {
+ Stream << Question;
+ }
+ };
+
+ Stream << " Lines ";
+ PrintLine(getLowerLine());
+ Stream << ":";
+ PrintLine(getUpperLine());
+
+ if (options().getAttributeOffset())
+ // Print the active range (low pc and high pc).
+ Stream << " [" << hexString(getLowerAddress()) << ":"
+ << hexString(getUpperAddress()) << "]";
+
+ return String;
+}
+
+// Validate the ranges associated with the location.
+bool LVLocation::validateRanges() {
+ // Traverse the locations and validate them against the address to line
+ // mapping in the current compile unit. Record those invalid ranges.
+ // A valid range must meet the following conditions:
+ // a) line(lopc) <= line(hipc)
+ // b) line(lopc) and line(hipc) are valid.
+
+ if (!hasAssociatedRange())
+ return true;
+
+ LVLineRange Range = getReaderCompileUnit()->lineRange(this);
+ LVLine *LowLine = Range.first;
+ LVLine *HighLine = Range.second;
+ if (LowLine)
+ setLowerLine(LowLine);
+ else {
+ setIsInvalidLower();
+ return false;
+ }
+ if (HighLine)
+ setUpperLine(HighLine);
+ else {
+ setIsInvalidUpper();
+ return false;
+ }
+ // Check for a valid interval.
+ if (LowLine->getLineNumber() > HighLine->getLineNumber()) {
+ setIsInvalidRange();
+ return false;
+ }
+
+ return true;
+}
+
+bool LVLocation::calculateCoverage(LVLocations *Locations, unsigned &Factor,
+ float &Percentage) {
+ if (!options().getAttributeCoverage() && !Locations)
+ return false;
+
+ // Calculate the coverage depending on the kind of location. We have
+ // the simple and composed locations.
+ if (Locations->size() == 1) {
+ // Simple: fixed address, class offset, stack offset.
+ LVLocation *Location = Locations->front();
+ // Some types of locations do not have specific kind. Now is the time
+ // to set those types, depending on the operation type.
+ Location->updateKind();
+ if (Location->getIsLocationSimple()) {
+ Factor = 100;
+ Percentage = 100;
+ return true;
+ }
+ }
+
+ // Composed locations.
+ LVAddress LowerAddress = 0;
+ LVAddress UpperAddress = 0;
+ for (const LVLocation *Location : *Locations)
+ // Do not include locations representing a gap.
+ if (!Location->getIsGapEntry()) {
+ LowerAddress = Location->getLowerAddress();
+ UpperAddress = Location->getUpperAddress();
+ Factor += (UpperAddress > LowerAddress) ? UpperAddress - LowerAddress
+ : LowerAddress - UpperAddress;
+ }
+
+ Percentage = 0;
+ return false;
+}
+
+void LVLocation::printRaw(raw_ostream &OS, bool Full) const {
+ // Print the active range (low pc and high pc).
+ OS << " [" << hexString(getLowerAddress()) << ":"
+ << hexString(getUpperAddress()) << "]\n";
+ // Print any DWARF operations.
+ printRawExtra(OS, Full);
+}
+
+void LVLocation::printInterval(raw_ostream &OS, bool Full) const {
+ if (hasAssociatedRange())
+ OS << getIntervalInfo();
+}
+
+void LVLocation::print(raw_ostream &OS, bool Full) const {
+ if (getReader().doPrintLocation(this)) {
+ LVObject::print(OS, Full);
+ printExtra(OS, Full);
+ }
+}
+
+void LVLocation::printExtra(raw_ostream &OS, bool Full) const {
+ printInterval(OS, Full);
+ OS << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF location for a symbol.
+//===----------------------------------------------------------------------===//
+// Add a Location Entry.
+void LVLocationSymbol::addObject(LVAddress LowPC, LVAddress HighPC,
+ LVUnsigned SectionOffset,
+ uint64_t LocDescOffset) {
+ setLowerAddress(LowPC);
+ setUpperAddress(HighPC);
+
+ // Record the offset where the location information begins.
+ setOffset(LocDescOffset ? LocDescOffset : SectionOffset);
+
+ // A -1 HighPC value, indicates no range.
+ if (HighPC == LVAddress(UINT64_MAX))
+ setIsDiscardedRange();
+
+ // Update the location kind, using the DWARF attribute.
+ setKind();
+}
+
+// Add a Location Record.
+void LVLocationSymbol::addObject(LVSmall Opcode, LVUnsigned Operand1,
+ LVUnsigned Operand2) {
+ if (!Entries)
+ Entries = new LVAutoOperations();
+ Entries->emplace_back(new LVOperation(Opcode, Operand1, Operand2));
+}
+
+// Based on the DWARF attribute, define the location kind.
+void LVLocation::setKind() {
+ switch (getAttr()) {
+ case dwarf::DW_AT_data_member_location:
+ setIsClassOffset();
+ break;
+ case dwarf::DW_AT_location:
+ // Depending on the operand, we have a fixed address.
+ setIsFixedAddress();
+ break;
+ default:
+ break;
+ }
+ // For those symbols with absolute location information, ignore any
+ // gaps in their location description; that is the case with absolute
+ // memory addresses and members located at specific offsets.
+ if (hasAssociatedRange())
+ getParentSymbol()->setFillGaps();
+}
+
+void LVLocationSymbol::updateKind() {
+ // Update the location type for simple ones.
+ if (Entries && Entries->size() == 1) {
+ LVOperation *Operation = Entries->front();
+ if (dwarf::DW_OP_fbreg == Operation->getOpcode())
+ setIsStackOffset();
+ }
+}
+
+void LVLocationSymbol::printRawExtra(raw_ostream &OS, bool Full) const {
+ if (Entries)
+ for (const LVOperation *Operation : *Entries)
+ Operation->print(OS, Full);
+}
+
+// Print location (formatted version).
+void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) {
+ if (!Locations || Locations->empty())
+ return;
+
+ // Print the symbol coverage.
+ if (options().getAttributeCoverage()) {
+ // The location entries are contained within a symbol. Get a location,
+ // to access basic information about indentation, parent, etc.
+ LVLocation *Location = Locations->front();
+ LVSymbol *Symbol = Location->getParentSymbol();
+ float Percentage = Symbol->getCoveragePercentage();
+
+ // The coverage is dependent on the kind of location.
+ std::string String;
+ raw_string_ostream Stream(String);
+ Stream << format("%.2f%%", Percentage);
+ if (!Location->getIsLocationSimple())
+ Stream << format(" (%d/%d)", Symbol->getCoverageFactor(),
+ Symbol->getParentScope()->getCoverageFactor());
+ Symbol->printAttributes(OS, Full, "{Coverage} ", Symbol, StringRef(String),
+ /*UseQuotes=*/false,
+ /*PrintRef=*/false);
+ }
+
+ // Print the symbol location, including the missing entries.
+ if (getReader().doPrintLocation(/*Location=*/nullptr))
+ for (const LVLocation *Location : *Locations)
+ Location->print(OS, Full);
+}
+
+void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const {
+ OS << "{Location}";
+ if (getIsCallSite())
+ OS << " -> CallSite";
+ printInterval(OS, Full);
+ OS << "\n";
+
+ // Print location entries.
+ if (Full && Entries) {
+ bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation();
+ std::stringstream Stream;
+ std::string Leading = "";
+ for (LVOperation *Operation : *Entries) {
+ Stream << Leading
+ << (CodeViewLocation ? Operation->getOperandsCodeViewInfo()
+ : Operation->getOperandsDWARFInfo());
+ Leading = ", ";
+ }
+ printAttributes(OS, Full, "{Entry} ", const_cast<LVLocationSymbol *>(this),
+ StringRef(Stream.str()),
+ /*UseQuotes=*/false,
+ /*PrintRef=*/false);
+ }
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp
new file mode 100644
index 000000000000..75acbf3225e0
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp
@@ -0,0 +1,169 @@
+//===-- LVObject.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVObject class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVObject.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+#include <iomanip>
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Object"
+
+#ifndef NDEBUG
+uint64_t LVObject::GID = 0;
+#endif
+
+StringRef llvm::logicalview::typeNone() { return StringRef(); }
+StringRef llvm::logicalview::typeVoid() { return "void"; }
+StringRef llvm::logicalview::typeInt() { return "int"; }
+StringRef llvm::logicalview::typeUnknown() { return "?"; }
+StringRef llvm::logicalview::emptyString() { return StringRef(); }
+
+// Get a string representing the indentation level.
+std::string LVObject::indentAsString(LVLevel Level) const {
+ return std::string(Level * 2, ' ');
+}
+
+// Get a string representing the indentation level.
+std::string LVObject::indentAsString() const {
+ return (options().getPrintFormatting() || options().getPrintOffset())
+ ? indentAsString(ScopeLevel)
+ : "";
+}
+
+// String used as padding for printing objects with no line number.
+std::string LVObject::noLineAsString(bool ShowZero) const {
+ return std::string(8, ' ');
+}
+
+// Get a string representation for the given number and discriminator.
+std::string LVObject::lineAsString(uint32_t LineNumber, LVHalf Discriminator,
+ bool ShowZero) const {
+ // The representation is formatted as:
+ // a) line number (xxxxx) and discriminator (yy): 'xxxxx,yy'
+ // b) Only line number (xxxxx): 'xxxxx '
+ // c) No line number: ' '
+ std::stringstream Stream;
+ if (LineNumber) {
+ if (Discriminator && options().getAttributeDiscriminator())
+ Stream << std::setw(5) << LineNumber << "," << std::left << std::setw(2)
+ << Discriminator;
+ else
+ Stream << std::setw(5) << LineNumber << " ";
+ } else
+ Stream << noLineAsString(ShowZero);
+
+ if (options().getInternalNone())
+ Stream.str(noLineAsString(ShowZero));
+
+ return Stream.str();
+}
+
+// Same as 'LineString' but with stripped whitespaces.
+std::string LVObject::lineNumberAsStringStripped(bool ShowZero) const {
+ return std::string(StringRef(lineNumberAsString(ShowZero)).trim());
+}
+
+std::string LVObject::referenceAsString(uint32_t LineNumber,
+ bool Spaces) const {
+ std::string String;
+ raw_string_ostream Stream(String);
+ if (LineNumber)
+ Stream << "@" << LineNumber << (Spaces ? " " : "");
+
+ return String;
+}
+
+void LVObject::setParent(LVScope *Scope) {
+ Parent.Scope = Scope;
+ setLevel(Scope->getLevel() + 1);
+}
+void LVObject::setParent(LVSymbol *Symbol) {
+ Parent.Symbol = Symbol;
+ setLevel(Symbol->getLevel() + 1);
+}
+
+void LVObject::markBranchAsMissing() {
+ // Mark the current object as 'missing'; then traverse the parents chain
+ // marking them as 'special missing' to indicate a missing branch. They
+ // can not be marked as missing, because will generate incorrect reports.
+ LVObject *Parent = this;
+ Parent->setIsMissing();
+ while (Parent) {
+ Parent->setIsMissingLink();
+ Parent = Parent->getParent();
+ }
+}
+
+Error LVObject::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS,
+ bool Full) const {
+ print(OS, Full);
+ return Error::success();
+}
+
+void LVObject::printAttributes(raw_ostream &OS, bool Full, StringRef Name,
+ LVObject *Parent, StringRef Value,
+ bool UseQuotes, bool PrintRef) const {
+ // The current object will be the enclosing scope, use its offset and level.
+ LVObject Object(*Parent);
+ Object.setLevel(Parent->getLevel() + 1);
+ Object.setLineNumber(0);
+ Object.printAttributes(OS, Full);
+
+ // Print the line.
+ std::string TheLineNumber(Object.lineNumberAsString());
+ std::string TheIndentation(Object.indentAsString());
+ OS << format(" %5s %s ", TheLineNumber.c_str(), TheIndentation.c_str());
+
+ OS << Name;
+ if (PrintRef && options().getAttributeOffset())
+ OS << hexSquareString(getOffset());
+ if (UseQuotes)
+ OS << formattedName(Value) << "\n";
+ else
+ OS << Value << "\n";
+}
+
+void LVObject::printAttributes(raw_ostream &OS, bool Full) const {
+#ifndef NDEBUG
+ if (options().getInternalID())
+ OS << hexSquareString(getID());
+#endif
+ if (options().getCompareExecute() &&
+ (options().getAttributeAdded() || options().getAttributeMissing()))
+ OS << (getIsAdded() ? '+' : getIsMissing() ? '-' : ' ');
+ if (options().getAttributeOffset())
+ OS << hexSquareString(getOffset());
+ if (options().getAttributeLevel()) {
+ std::stringstream Stream;
+ Stream.str(std::string());
+ Stream << "[" << std::setfill('0') << std::setw(3) << getLevel() << "]";
+ std::string TheLevel(Stream.str());
+ OS << TheLevel;
+ }
+ if (options().getAttributeGlobal())
+ OS << (getIsGlobalReference() ? 'X' : ' ');
+}
+
+void LVObject::print(raw_ostream &OS, bool Full) const {
+ printFileIndex(OS, Full);
+ printAttributes(OS, Full);
+
+ // Print the line and any discriminator.
+ std::stringstream Stream;
+ Stream << " " << std::setw(5) << lineNumberAsString() << " "
+ << indentAsString() << " ";
+ OS << Stream.str();
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
new file mode 100644
index 000000000000..265237ee21dc
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp
@@ -0,0 +1,579 @@
+//===-- LVOptions.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVOptions class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/Support/Errc.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Options"
+
+//===----------------------------------------------------------------------===//
+// Options extracted from the command line.
+//===----------------------------------------------------------------------===//
+static LVOptions Options;
+LVOptions *LVOptions::getOptions() { return &Options; }
+void LVOptions::setOptions(LVOptions *CmdOptions) { Options = *CmdOptions; }
+
+void LVOptions::resolveDependencies() {
+ // Attributes that are classified as standard options.
+ auto StandardAttributes = [&]() {
+ // Set the 'standard' attribute to indicate its associated attributes.
+ setAttributeStandard();
+
+ setAttributeBase();
+ setAttributeCoverage();
+ setAttributeDirectories();
+ setAttributeDiscriminator();
+ setAttributeFilename();
+ setAttributeFiles();
+ setAttributeFormat();
+ setAttributeLevel();
+ setAttributeProducer();
+ setAttributePublics();
+ setAttributeRange();
+ setAttributeReference();
+ setAttributeZero();
+ };
+
+ // Attributes that are classified as extended options.
+ auto ExtendedAttributes = [&]() {
+ // Set the 'extended' attribute to indicate its associated attributes.
+ setAttributeExtended();
+
+ setAttributeArgument();
+ setAttributeDiscarded();
+ setAttributeEncoded();
+ setAttributeGaps();
+ setAttributeGenerated();
+ setAttributeGlobal();
+ setAttributeInserted();
+ setAttributeLinkage();
+ setAttributeLocal();
+ setAttributeLocation();
+ setAttributeOffset();
+ setAttributePathname();
+ setAttributeQualified();
+ setAttributeQualifier();
+ setAttributeRegister();
+ setAttributeSubrange();
+ setAttributeSystem();
+ setAttributeTypename();
+ };
+
+ // '--Attribute=standard' settings.
+ if (getAttributeStandard())
+ StandardAttributes();
+
+ // '--Attribute=extended' settings.
+ if (getAttributeExtended())
+ ExtendedAttributes();
+
+ // '--Attribute=all' settings.
+ if (getAttributeAll()) {
+ StandardAttributes();
+ ExtendedAttributes();
+ }
+
+ // '--attribute=pathname' supersedes '--attribute=filename'.
+ if (getAttributePathname())
+ resetAttributeFilename();
+
+ // Assume '--output=text' as default
+ if (!getOutputText() && !getOutputJson())
+ setOutputText();
+
+ // '--output=all' settings.
+ if (getOutputAll()) {
+ setOutputJson();
+ setOutputSplit();
+ setOutputText();
+ }
+
+ // A view split folder was specified.
+ if (getOutputFolder().length())
+ setOutputSplit();
+
+ // Always use the full pathname with splitted output.
+ if (getOutputSplit())
+ setAttributePathname();
+
+ // '--print=elements' settings.
+ if (getPrintElements()) {
+ setPrintInstructions();
+ setPrintLines();
+ setPrintScopes();
+ setPrintSymbols();
+ setPrintTypes();
+ }
+
+ // '--print=all' settings.
+ if (getPrintAll()) {
+ setPrintInstructions();
+ setPrintLines();
+ setPrintScopes();
+ setPrintSizes();
+ setPrintSymbols();
+ setPrintSummary();
+ setPrintTypes();
+ setPrintWarnings();
+ }
+
+ // '--warning=all' settings.
+ if (getWarningAll()) {
+ setWarningCoverages();
+ setWarningLines();
+ setWarningLocations();
+ setWarningRanges();
+ }
+
+ // '--internal=all' settings.
+ if (getInternalAll()) {
+ setInternalCmdline();
+ setInternalID();
+ setInternalIntegrity();
+ setInternalNone();
+ setInternalTag();
+ }
+
+ // '--compare=all' settings.
+ if (getCompareAll()) {
+ setCompareLines();
+ setCompareScopes();
+ setCompareSymbols();
+ setCompareTypes();
+ }
+
+ // Compare the scopes if a request for compare symbols, types, lines.
+ if (getCompareLines() || getCompareSymbols() || getCompareTypes())
+ setCompareScopes();
+
+ // Generic request for comparison.
+ if (getCompareScopes())
+ setCompareExecute();
+
+ // Print any logical line (debug or instruction).
+ if (getPrintInstructions() || getPrintLines())
+ setPrintAnyLine();
+
+ // Print any logical element (line, scope, symbol or type).
+ if (getPrintAnyLine() || getPrintScopes() || getPrintSymbols() ||
+ getPrintTypes())
+ setPrintAnyElement();
+
+ // Print 'sizes' or 'summary'.
+ if (getPrintSizes() && getPrintSummary())
+ setPrintSizesSummary();
+
+ // Generic request for printing.
+ if (getPrintAll() || getPrintAnyElement() || getPrintSizesSummary() ||
+ getPrintWarnings())
+ setPrintExecute();
+
+ // '--reports=all' settings.
+ if (getReportAll()) {
+ setReportChildren();
+ setReportList();
+ setReportParents();
+ setReportView();
+ }
+
+ // '--report=view' is a shortcut for '--report=parents,children'.
+ if (getReportView()) {
+ setReportChildren();
+ setReportParents();
+ }
+
+ // The report will include: Parents or Children.
+ if (getReportParents() || getReportChildren() || getReportView())
+ setReportAnyView();
+
+ // The report will include: List or Parents or Children.
+ if (getReportList() || getReportAnyView())
+ setReportExecute();
+
+ // If a view or element comparison has been requested, the following options
+ // must be set, in order to get a correct compare:
+ // 1) Sort the CUs, to get a fast compare.
+ // 2) Encode template instantiations, so the names include template
+ // parameter information.
+ // 3) Include qualified types.
+ // 4) Include any inserted abstract references.
+ // 5) For added/missing elements add the '+' or '-' tags.
+ if (getCompareExecute()) {
+ resetPrintExecute();
+ setComparePrint();
+ setSortMode(LVSortMode::Line);
+ setAttributeAdded();
+ setAttributeArgument();
+ setAttributeEncoded();
+ setAttributeInserted();
+ setAttributeMissing();
+ setAttributeQualified();
+ }
+
+ // Enable formatting for printing (indentation, print children).
+ setPrintFormatting();
+
+ // These attributes are dependent on the capture of location information.
+ if (getAttributeCoverage() || getAttributeGaps() || getAttributeRegister())
+ setAttributeLocation();
+
+ // Location information is only relevant when printing symbols.
+ if (!getPrintSymbols()) {
+ resetAttributeCoverage();
+ resetAttributeGaps();
+ resetAttributeLocation();
+ resetAttributeRegister();
+ }
+
+ // Quick check for printing any element source information.
+ if (getAttributeFilename() || getAttributePathname())
+ setAttributeAnySource();
+
+ // Quick check for printing any location information.
+ if (getAttributeLocation() || getAttributeRange())
+ setAttributeAnyLocation();
+
+ if (getAttributeRange() || getPrintAnyLine())
+ setGeneralCollectRanges();
+
+ calculateIndentationSize();
+
+ // Print collected command line options.
+ LLVM_DEBUG({ dump(); });
+}
+
+void LVOptions::calculateIndentationSize() {
+#ifndef NDEBUG
+ if (getInternalID()) {
+ std::string String = hexSquareString(0);
+ IndentationSize += String.length();
+ }
+#endif
+ if (getCompareExecute() && (getAttributeAdded() || getAttributeMissing()))
+ ++IndentationSize;
+ if (getAttributeOffset()) {
+ std::string String = hexSquareString(0);
+ IndentationSize += String.length();
+ }
+ if (getAttributeLevel()) {
+ std::stringstream Stream;
+ Stream.str(std::string());
+ Stream << "[" << std::setfill('0') << std::setw(3) << 0 << "]";
+ IndentationSize += Stream.tellp();
+ }
+ if (getAttributeGlobal())
+ ++IndentationSize;
+}
+
+// Print the current values for all the options, after the dependencies
+// has been resolved.
+void LVOptions::print(raw_ostream &OS) const {
+ // --attribute
+ OS << "** Attributes **\n"
+ << "All: " << getAttributeAll() << ", "
+ << "Argument: " << getAttributeArgument() << ", "
+ << "Base: " << getAttributeBase() << ", "
+ << "Coverage: " << getAttributeCoverage() << "\n"
+ << "Directories: " << getAttributeDirectories() << ", "
+ << "Discarded: " << getAttributeDiscarded() << ", "
+ << "Discriminator: " << getAttributeDiscriminator() << ", "
+ << "Encoded: " << getAttributeEncoded() << "\n"
+ << "Extended: " << getAttributeExtended() << ", "
+ << "Filename: " << getAttributeFilename() << ", "
+ << "Files: " << getAttributeFiles() << ", "
+ << "Format: " << getAttributeFormat() << "\n"
+ << "Gaps: " << getAttributeGaps() << ", "
+ << "Generated: " << getAttributeGenerated() << ", "
+ << "Global: " << getAttributeGlobal() << ", "
+ << "Inserted: " << getAttributeInserted() << "\n"
+ << "Level: " << getAttributeLevel() << ", "
+ << "Linkage: " << getAttributeLinkage() << ", "
+ << "Local: " << getAttributeLocal() << ", "
+ << "Location: " << getAttributeLocation() << "\n"
+ << "Offset: " << getAttributeOffset() << ", "
+ << "Pathname: " << getAttributePathname() << ", "
+ << "Producer: " << getAttributeProducer() << ", "
+ << "Publics: " << getAttributePublics() << "\n"
+ << "Qualified: " << getAttributeQualified() << ", "
+ << "Qualifier: " << getAttributeQualifier() << ", "
+ << "Range: " << getAttributeRange() << ", "
+ << "Reference: " << getAttributeReference() << "\n"
+ << "Register: " << getAttributeRegister() << ", "
+ << "Standard: " << getAttributeStandard() << ", "
+ << "Subrange: " << getAttributeSubrange() << ", "
+ << "System: " << getAttributeSystem() << "\n"
+ << "Typename: " << getAttributeTypename() << ", "
+ << "Underlying: " << getAttributeUnderlying() << ", "
+ << "Zero: " << getAttributeZero() << "\n";
+ OS << "Added: " << getAttributeAdded() << ", "
+ << "AnyLocation: " << getAttributeAnyLocation() << ", "
+ << "AnySource: " << getAttributeAnySource() << ", "
+ << "Missing: " << getAttributeMissing() << "\n"
+ << "\n";
+
+ // --compare
+ OS << "** Compare **\n"
+ << "All: " << getCompareAll() << ", "
+ << "Lines: " << getCompareLines() << ", "
+ << "Scopes: " << getCompareScopes() << ", "
+ << "Symbols: " << getCompareSymbols() << ", "
+ << "Types: " << getCompareTypes() << "\n";
+ OS << "Context: " << getCompareContext() << ", "
+ << "Execute: " << getCompareExecute() << ", "
+ << "Print: " << getComparePrint() << "\n"
+ << "\n";
+
+ // --print
+ OS << "** Print **\n"
+ << "All: " << getPrintAll() << ", "
+ << "Elements: " << getPrintElements() << ", "
+ << "Instructions: " << getPrintInstructions() << ", "
+ << "Lines: " << getPrintLines() << "\n"
+ << "Scopes: " << getPrintScopes() << ", "
+ << "Sizes: " << getPrintSizes() << ", "
+ << "Summary: " << getPrintSummary() << ", "
+ << "Symbols: " << getPrintSymbols() << "\n"
+ << "Types: " << getPrintTypes() << ", "
+ << "Warnings: " << getPrintWarnings() << "\n";
+ OS << "AnyElemeny: " << getPrintAnyElement() << ", "
+ << "AnyLine: " << getPrintAnyLine() << ", "
+ << "Execute: " << getPrintExecute() << ", "
+ << "Formatting: " << getPrintFormatting() << "\n"
+ << "Offset: " << getPrintOffset() << ", "
+ << "SizesSummary: " << getPrintSizesSummary() << "\n"
+ << "\n";
+
+ // --report
+ OS << "** Report **\n"
+ << "All: " << getReportAll() << ", "
+ << "Children: " << getReportChildren() << ", "
+ << "List: " << getReportList() << ", "
+ << "Parents: " << getReportParents() << ", "
+ << "View: " << getReportView() << "\n";
+ OS << "AnyView: " << getReportAnyView() << ", "
+ << "Execute: " << getReportExecute() << "\n"
+ << "\n";
+
+ // --select
+ OS << "** Select **\n"
+ << "IgnoreCase: " << getSelectIgnoreCase() << ", "
+ << "UseRegex: " << getSelectUseRegex() << ", "
+ << "Execute: " << getSelectExecute() << ", "
+ << "GenericKind: " << getSelectGenericKind() << "\n"
+ << "GenericPattern: " << getSelectGenericPattern() << ", "
+ << "OffsetPattern: " << getSelectOffsetPattern() << "\n"
+ << "\n";
+
+ // --warning
+ OS << "** Warning **\n"
+ << "All: " << getWarningAll() << ", "
+ << "Coverage: " << getWarningCoverages() << ", "
+ << "Lines: " << getWarningLines() << ", "
+ << "Locations: " << getWarningLocations() << ", "
+ << "Ranges: " << getWarningRanges() << "\n"
+ << "\n";
+
+ // --internal
+ OS << "** Internal **\n"
+ << "All: " << Options.getInternalAll() << ", "
+ << "Cmdline: " << Options.getInternalCmdline() << ", "
+ << "ID: " << Options.getInternalID() << ", "
+ << "Integrity: " << Options.getInternalIntegrity() << ", "
+ << "None: " << Options.getInternalNone() << "\n"
+ << "Tag: " << Options.getInternalTag() << "\n"
+ << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// Logical element selection using patterns.
+//===----------------------------------------------------------------------===//
+LVPatterns *LVPatterns::getPatterns() {
+ static LVPatterns Patterns;
+ return &Patterns;
+}
+
+Error LVPatterns::createMatchEntry(LVMatchInfo &Filters, StringRef Pattern,
+ bool IgnoreCase, bool UseRegex) {
+ LVMatch Match;
+ // Process pattern as regular expression.
+ if (UseRegex) {
+ Match.Pattern = std::string(Pattern);
+ if (Pattern.size()) {
+ Match.RE = std::make_shared<Regex>(Pattern, IgnoreCase ? Regex::IgnoreCase
+ : Regex::NoFlags);
+ std::string Error;
+ if (!Match.RE->isValid(Error))
+ return createStringError(errc::invalid_argument,
+ "Error in regular expression: %s",
+ Error.c_str());
+
+ Match.Mode = LVMatchMode::Regex;
+ Filters.push_back(Match);
+ return Error::success();
+ }
+ }
+
+ // Process pattern as an exact string match, depending on the case.
+ Match.Pattern = std::string(Pattern);
+ if (Match.Pattern.size()) {
+ Match.Mode = IgnoreCase ? LVMatchMode::NoCase : LVMatchMode::Match;
+ Filters.push_back(Match);
+ }
+
+ return Error::success();
+}
+
+void LVPatterns::addGenericPatterns(StringSet<> &Patterns) {
+ addPatterns(Patterns, GenericMatchInfo);
+ if (GenericMatchInfo.size()) {
+ options().setSelectGenericPattern();
+ options().setSelectExecute();
+ }
+}
+
+void LVPatterns::addOffsetPatterns(const LVOffsetSet &Patterns) {
+ for (const LVOffset &Entry : Patterns)
+ OffsetMatchInfo.push_back(Entry);
+ if (OffsetMatchInfo.size()) {
+ options().setSelectOffsetPattern();
+ options().setSelectExecute();
+ }
+}
+
+void LVPatterns::addPatterns(StringSet<> &Patterns, LVMatchInfo &Filters) {
+ bool IgnoreCase = options().getSelectIgnoreCase();
+ bool UseRegex = options().getSelectUseRegex();
+ for (const StringSet<>::value_type &Entry : Patterns) {
+ StringRef Pattern = Entry.first();
+ if (Error Err = createMatchEntry(Filters, Pattern, IgnoreCase, UseRegex))
+ consumeError(std::move(Err));
+ }
+
+ LLVM_DEBUG({
+ dbgs() << "\nPattern Information:\n";
+ for (LVMatch &Match : Filters)
+ dbgs() << "Mode: "
+ << (Match.Mode == LVMatchMode::Match ? "Match" : "Regex")
+ << " Pattern: '" << Match.Pattern << "'\n";
+ });
+}
+
+void LVPatterns::addElement(LVElement *Element) {
+ // Mark any element that matches a given pattern.
+ Element->setIsMatched();
+ options().setSelectExecute();
+ if (options().getReportList())
+ getReaderCompileUnit()->addMatched(Element);
+ if (options().getReportAnyView()) {
+ getReaderCompileUnit()->addMatched(Element->getIsScope()
+ ? static_cast<LVScope *>(Element)
+ : Element->getParentScope());
+ // Mark element as matched.
+ if (!Element->getIsScope())
+ Element->setHasPattern();
+ }
+}
+
+void LVPatterns::updateReportOptions() {
+ if (ElementRequest.size() || LineRequest.size() || ScopeRequest.size() ||
+ SymbolRequest.size() || TypeRequest.size()) {
+ options().setSelectGenericKind();
+ options().setSelectExecute();
+ }
+
+ // If we have selected requests and there are no specified report options,
+ // assume the 'details' option.
+ if (options().getSelectExecute() && !options().getReportExecute()) {
+ options().setReportExecute();
+ options().setReportList();
+ }
+}
+
+// Match a general pattern.
+bool LVPatterns::matchPattern(StringRef Input, const LVMatchInfo &MatchInfo) {
+ bool Matched = false;
+ // Do not match an empty 'Input'.
+ if (Input.empty())
+ return Matched;
+ // Traverse all match specifications.
+ for (const LVMatch &Match : MatchInfo) {
+ switch (Match.Mode) {
+ case LVMatchMode::Match:
+ Matched = Input.equals(Match.Pattern);
+ break;
+ case LVMatchMode::NoCase:
+ Matched = Input.equals_insensitive(Match.Pattern);
+ break;
+ case LVMatchMode::Regex:
+ Matched = Match.RE->match(Input);
+ break;
+ default:
+ break;
+ }
+ // Return if we have a match.
+ if (Matched)
+ return true;
+ }
+ return Matched;
+}
+
+bool LVPatterns::printElement(const LVLine *Line) const {
+ return (options().getPrintLines() && Line->getIsLineDebug()) ||
+ (options().getPrintInstructions() && Line->getIsLineAssembler());
+}
+
+bool LVPatterns::printObject(const LVLocation *Location) const {
+ if (options().getAttributeAll())
+ return true;
+ bool DoPrint = options().getAttributeAnyLocation();
+ // Consider the case of filler locations.
+ if (DoPrint && Location && Location->getIsGapEntry())
+ DoPrint = options().getAttributeGaps();
+ return DoPrint;
+}
+
+bool LVPatterns::printElement(const LVScope *Scope) const {
+ // A scope will be printed depending on the following rules:
+ // - Request to print scopes.
+ // - Request to print any of its children.
+ // - If the scope is Root or CompileUnit:
+ // Request to print summary, sizes or warnings.
+ return options().getPrintScopes() ||
+ (options().getPrintSymbols() && Scope->getHasSymbols()) ||
+ (options().getPrintAnyLine() && Scope->getHasLines()) ||
+ (options().getPrintTypes() && Scope->getHasTypes()) ||
+ ((options().getPrintSizesSummary() || options().getPrintWarnings()) &&
+ (Scope->getIsRoot() || Scope->getIsCompileUnit()));
+}
+
+bool LVPatterns::printElement(const LVSymbol *Symbol) const {
+ // Print compiler generated symbols only if command line option.
+ if (Symbol->getIsArtificial())
+ return options().getAttributeGenerated() && options().getPrintSymbols();
+ return options().getPrintSymbols();
+}
+
+bool LVPatterns::printElement(const LVType *Type) const {
+ // Print array subranges only if print types is requested.
+ if (Type->getIsSubrange())
+ return options().getAttributeSubrange() && options().getPrintTypes();
+ return options().getPrintTypes();
+}
+
+void LVPatterns::print(raw_ostream &OS) const {
+ OS << "LVPatterns\n";
+ LLVM_DEBUG(dbgs() << "Print Patterns\n");
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp
new file mode 100644
index 000000000000..b6003fcb8b93
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp
@@ -0,0 +1,158 @@
+//===-- LVRange.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVRange class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVRange.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Range"
+
+void LVRange::startSearch() {
+ RangesTree.clear();
+
+ LLVM_DEBUG({ dbgs() << "\nRanges Tree entries:\n"; });
+
+ // Traverse the ranges and store them into the interval tree.
+ for (LVRangeEntry &RangeEntry : RangeEntries) {
+ LLVM_DEBUG({
+ LVScope *Scope = RangeEntry.scope();
+ dbgs() << "Scope: " << format_decimal(Scope->getLevel(), 5) << " "
+ << "Range: [" << hexValue(RangeEntry.lower()) << ":"
+ << hexValue(RangeEntry.upper()) << "]\n";
+ });
+
+ RangesTree.insert(RangeEntry.lower(), RangeEntry.upper(),
+ RangeEntry.scope());
+ }
+
+ // Create the interval tree.
+ RangesTree.create();
+
+ LLVM_DEBUG({
+ dbgs() << "\nRanges Tree:\n";
+ RangesTree.print(dbgs());
+ });
+}
+
+// Add the pair in an ascending order, with the smallest ranges at the
+// start; in that way, enclosing scopes ranges are at the end of the
+// list; we assume that low <= high.
+void LVRange::addEntry(LVScope *Scope, LVAddress LowerAddress,
+ LVAddress UpperAddress) {
+ // We assume the low <= high.
+ if (LowerAddress > UpperAddress)
+ std::swap(LowerAddress, UpperAddress);
+
+ // Record the lowest and highest seen addresses.
+ if (LowerAddress < Lower)
+ Lower = LowerAddress;
+ if (UpperAddress > Upper)
+ Upper = UpperAddress;
+
+ // Just add the scope and range pair, in no particular order.
+ RangeEntries.emplace_back(LowerAddress, UpperAddress, Scope);
+}
+
+void LVRange::addEntry(LVScope *Scope) {
+ assert(Scope && "Scope must not be nullptr");
+ // Traverse the ranges and update the ranges set only if the ranges
+ // values are not already recorded.
+ if (const LVLocations *Locations = Scope->getRanges())
+ for (const LVLocation *Location : *Locations) {
+ LVAddress LowPC = Location->getLowerAddress();
+ LVAddress HighPC = Location->getUpperAddress();
+ if (!hasEntry(LowPC, HighPC))
+ // Add the pair of addresses.
+ addEntry(Scope, LowPC, HighPC);
+ }
+}
+
+// Get the scope associated with the input address.
+LVScope *LVRange::getEntry(LVAddress Address) const {
+ LLVM_DEBUG({ dbgs() << format("Searching: 0x%08x\nFound: ", Address); });
+
+ LVScope *Target = nullptr;
+ LVLevel TargetLevel = 0;
+ LVLevel Level = 0;
+ LVScope *Scope = nullptr;
+ for (LVRangesTree::find_iterator Iter = RangesTree.find(Address),
+ End = RangesTree.find_end();
+ Iter != End; ++Iter) {
+ LLVM_DEBUG(
+ { dbgs() << format("[0x%08x,0x%08x] ", Iter->left(), Iter->right()); });
+ Scope = Iter->value();
+ Level = Scope->getLevel();
+ if (Level > TargetLevel) {
+ TargetLevel = Level;
+ Target = Scope;
+ }
+ }
+
+ LLVM_DEBUG({ dbgs() << (Scope ? "\n" : "None\n"); });
+
+ return Target;
+}
+
+// Find the associated Scope for the given ranges values.
+LVScope *LVRange::getEntry(LVAddress LowerAddress,
+ LVAddress UpperAddress) const {
+ for (const LVRangeEntry &RangeEntry : RangeEntries)
+ if (LowerAddress >= RangeEntry.lower() && UpperAddress < RangeEntry.upper())
+ return RangeEntry.scope();
+ return nullptr;
+}
+
+// True if the range addresses contain the pair [LowerAddress, UpperAddress].
+bool LVRange::hasEntry(LVAddress LowerAddress, LVAddress UpperAddress) const {
+ for (const LVRangeEntry &RangeEntry : RangeEntries)
+ if (LowerAddress == RangeEntry.lower() &&
+ UpperAddress == RangeEntry.upper())
+ return true;
+ return false;
+}
+
+// Sort the range elements for the whole Compile Unit.
+void LVRange::sort() {
+ auto CompareRangeEntry = [](const LVRangeEntry &lhs,
+ const LVRangeEntry &rhs) -> bool {
+ if (lhs.lower() < rhs.lower())
+ return true;
+
+ // If the lower address is the same, use the upper address value in
+ // order to put first the smallest interval.
+ if (lhs.lower() == rhs.lower())
+ return lhs.upper() < rhs.upper();
+
+ return false;
+ };
+
+ // Sort the ranges using low address and range size.
+ std::stable_sort(RangeEntries.begin(), RangeEntries.end(), CompareRangeEntry);
+}
+
+void LVRange::print(raw_ostream &OS, bool Full) const {
+ size_t Indentation = 0;
+ for (const LVRangeEntry &RangeEntry : RangeEntries) {
+ LVScope *Scope = RangeEntry.scope();
+ Scope->printAttributes(OS, Full);
+ Indentation = options().indentationSize();
+ if (Indentation)
+ OS << " ";
+ OS << format("[0x%08x,0x%08x] ", RangeEntry.lower(), RangeEntry.upper())
+ << formattedKind(Scope->kind()) << " " << formattedName(Scope->getName())
+ << "\n";
+ }
+ printExtra(OS, Full);
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
new file mode 100644
index 000000000000..88f66cf2093b
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp
@@ -0,0 +1,304 @@
+//===-- LVReader.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVReader class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <tuple>
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Reader"
+
+// Detect elements that are inserted more than once at different scopes,
+// causing a crash on the reader destruction, as the element is already
+// deleted from other scope. Helper for CodeView reader.
+bool checkIntegrityScopesTree(LVScope *Root) {
+ using LVDuplicateEntry = std::tuple<LVElement *, LVScope *, LVScope *>;
+ using LVDuplicate = std::vector<LVDuplicateEntry>;
+ LVDuplicate Duplicate;
+
+ using LVIntegrity = std::map<LVElement *, LVScope *>;
+ LVIntegrity Integrity;
+
+ // Add the given element to the integrity map.
+ auto AddElement = [&](LVElement *Element, LVScope *Scope) {
+ LVIntegrity::iterator Iter = Integrity.find(Element);
+ if (Iter == Integrity.end())
+ Integrity.emplace(Element, Scope);
+ else
+ // We found a duplicate.
+ Duplicate.emplace_back(Element, Scope, Iter->second);
+ };
+
+ // Recursively add all the elements in the scope.
+ std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) {
+ auto Traverse = [&](const auto *Set) {
+ if (Set)
+ for (const auto &Entry : *Set)
+ AddElement(Entry, Parent);
+ };
+ if (const LVScopes *Scopes = Parent->getScopes()) {
+ for (LVScope *Scope : *Scopes) {
+ AddElement(Scope, Parent);
+ TraverseScope(Scope);
+ }
+ }
+ Traverse(Parent->getSymbols());
+ Traverse(Parent->getTypes());
+ Traverse(Parent->getLines());
+ };
+
+ // Start traversing the scopes root and print any duplicates.
+ TraverseScope(Root);
+ bool PassIntegrity = true;
+ if (Duplicate.size()) {
+ std::stable_sort(begin(Duplicate), end(Duplicate),
+ [](const auto &l, const auto &r) {
+ return std::get<0>(l)->getID() < std::get<0>(r)->getID();
+ });
+
+ auto PrintIndex = [](unsigned Index) {
+ if (Index)
+ dbgs() << format("%8d: ", Index);
+ else
+ dbgs() << format("%8c: ", ' ');
+ };
+ auto PrintElement = [&](LVElement *Element, unsigned Index = 0) {
+ PrintIndex(Index);
+ std::string ElementName(Element->getName());
+ dbgs() << format("%15s ID=0x%08x '%s'\n", Element->kind(),
+ Element->getID(), ElementName.c_str());
+ };
+
+ std::string RootName(Root->getName());
+ dbgs() << formatv("{0}\n", fmt_repeat('=', 72));
+ dbgs() << format("Root: '%s'\nDuplicated elements: %d\n", RootName.c_str(),
+ Duplicate.size());
+ dbgs() << formatv("{0}\n", fmt_repeat('=', 72));
+
+ unsigned Index = 0;
+ for (const LVDuplicateEntry &Entry : Duplicate) {
+ LVElement *Element;
+ LVScope *First;
+ LVScope *Second;
+ std::tie(Element, First, Second) = Entry;
+ dbgs() << formatv("\n{0}\n", fmt_repeat('-', 72));
+ PrintElement(Element, ++Index);
+ PrintElement(First);
+ PrintElement(Second);
+ dbgs() << formatv("{0}\n", fmt_repeat('-', 72));
+ }
+ PassIntegrity = false;
+ }
+ return PassIntegrity;
+}
+
+//===----------------------------------------------------------------------===//
+// Class to represent a split context.
+//===----------------------------------------------------------------------===//
+Error LVSplitContext::createSplitFolder(StringRef Where) {
+ // The 'location' will represent the root directory for the output created
+ // by the context. It will contain the different CUs files, that will be
+ // extracted from a single ELF.
+ Location = std::string(Where);
+
+ // Add a trailing slash, if there is none.
+ size_t Pos = Location.find_last_of('/');
+ if (Location.length() != Pos + 1)
+ Location.append("/");
+
+ // Make sure the new directory exists, creating it if necessary.
+ if (std::error_code EC = llvm::sys::fs::create_directories(Location))
+ return createStringError(EC, "Error: could not create directory %s",
+ Location.c_str());
+
+ return Error::success();
+}
+
+std::error_code LVSplitContext::open(std::string ContextName,
+ std::string Extension, raw_ostream &OS) {
+ assert(OutputFile == nullptr && "OutputFile already set.");
+
+ // Transforms '/', '\', '.', ':' into '_'.
+ std::string Name(flattenedFilePath(ContextName));
+ Name.append(Extension);
+ // Add the split context location folder name.
+ if (!Location.empty())
+ Name.insert(0, Location);
+
+ std::error_code EC;
+ OutputFile = std::make_unique<ToolOutputFile>(Name, EC, sys::fs::OF_None);
+ if (EC)
+ return EC;
+
+ // Don't remove output file.
+ OutputFile->keep();
+ return std::error_code();
+}
+
+LVReader *CurrentReader = nullptr;
+LVReader &LVReader::getInstance() {
+ if (CurrentReader)
+ return *CurrentReader;
+ outs() << "Invalid instance reader.\n";
+ llvm_unreachable("Invalid instance reader.");
+}
+void LVReader::setInstance(LVReader *Reader) { CurrentReader = Reader; }
+
+Error LVReader::createSplitFolder() {
+ if (OutputSplit) {
+ // If the '--output=split' was specified, but no '--split-folder'
+ // option, use the input file as base for the split location.
+ if (options().getOutputFolder().empty())
+ options().setOutputFolder(getFilename().str() + "_cus");
+
+ SmallString<128> SplitFolder;
+ SplitFolder = options().getOutputFolder();
+ sys::fs::make_absolute(SplitFolder);
+
+ // Return error if unable to create a split context location.
+ if (Error Err = SplitContext.createSplitFolder(SplitFolder))
+ return Err;
+
+ OS << "\nSplit View Location: '" << SplitContext.getLocation() << "'\n";
+ }
+
+ return Error::success();
+}
+
+// Get the filename for given object.
+StringRef LVReader::getFilename(LVObject *Object, size_t Index) const {
+ if (CompileUnits.size()) {
+ // Get Compile Unit for the given object.
+ LVCompileUnits::const_iterator Iter =
+ std::prev(CompileUnits.lower_bound(Object->getOffset()));
+ if (Iter != CompileUnits.end())
+ return Iter->second->getFilename(Index);
+ }
+
+ return CompileUnit ? CompileUnit->getFilename(Index) : StringRef();
+}
+
+// The Reader is the module that creates the logical view using the debug
+// information contained in the binary file specified in the command line.
+// This is the main entry point for the Reader and performs the following
+// steps:
+// - Process any patterns collected from the '--select' options.
+// - For each compile unit in the debug information:
+// * Create the logical elements (scopes, symbols, types, lines).
+// * Collect debug ranges and debug locations.
+// * Move the collected logical lines to their associated scopes.
+// - Once all the compile units have been processed, traverse the scopes
+// tree in order to:
+// * Calculate symbol coverage.
+// * Detect invalid ranges and locations.
+// * "resolve" the logical elements. During this pass, the names and
+// file information are updated, to reflect any dependency with other
+// logical elements.
+Error LVReader::doLoad() {
+ // Set current Reader instance.
+ setInstance(this);
+
+ // Before any scopes creation, process any pattern specified by the
+ // --select and --select-offsets options.
+ patterns().addGenericPatterns(options().Select.Generic);
+ patterns().addOffsetPatterns(options().Select.Offsets);
+
+ // Add any specific element printing requests based on the element kind.
+ patterns().addRequest(options().Select.Elements);
+ patterns().addRequest(options().Select.Lines);
+ patterns().addRequest(options().Select.Scopes);
+ patterns().addRequest(options().Select.Symbols);
+ patterns().addRequest(options().Select.Types);
+
+ // Once we have processed the requests for any particular kind of elements,
+ // we need to update the report options, in order to have a default value.
+ patterns().updateReportOptions();
+
+ // Delegate the scope tree creation to the specific reader.
+ if (Error Err = createScopes())
+ return Err;
+
+ if (options().getInternalIntegrity() && !checkIntegrityScopesTree(Root))
+ return llvm::make_error<StringError>("Duplicated elements in Scopes Tree",
+ inconvertibleErrorCode());
+
+ // Calculate symbol coverage and detect invalid debug locations and ranges.
+ Root->processRangeInformation();
+
+ // As the elements can depend on elements from a different compile unit,
+ // information such as name and file/line source information needs to be
+ // updated.
+ Root->resolveElements();
+
+ sortScopes();
+ return Error::success();
+}
+
+// Default handler for a generic reader.
+Error LVReader::doPrint() {
+ // Set current Reader instance.
+ setInstance(this);
+
+ // Check for any '--report' request.
+ if (options().getReportExecute()) {
+ // Requested details.
+ if (options().getReportList())
+ if (Error Err = printMatchedElements(/*UseMatchedElements=*/true))
+ return Err;
+ // Requested only children.
+ if (options().getReportChildren() && !options().getReportParents())
+ if (Error Err = printMatchedElements(/*UseMatchedElements=*/false))
+ return Err;
+ // Requested (parents) or (parents and children).
+ if (options().getReportParents() || options().getReportView())
+ if (Error Err = printScopes())
+ return Err;
+
+ return Error::success();
+ }
+
+ return printScopes();
+}
+
+Error LVReader::printScopes() {
+ if (bool DoPrint =
+ (options().getPrintExecute() || options().getComparePrint())) {
+ if (Error Err = createSplitFolder())
+ return Err;
+
+ // Start printing from the root.
+ bool DoMatch = options().getSelectGenericPattern() ||
+ options().getSelectGenericKind() ||
+ options().getSelectOffsetPattern();
+ return Root->doPrint(OutputSplit, DoMatch, DoPrint, OS);
+ }
+
+ return Error::success();
+}
+
+Error LVReader::printMatchedElements(bool UseMatchedElements) {
+ if (Error Err = createSplitFolder())
+ return Err;
+
+ return Root->doPrintMatches(OutputSplit, OS, UseMatchedElements);
+}
+
+void LVReader::print(raw_ostream &OS) const {
+ OS << "LVReader\n";
+ LLVM_DEBUG(dbgs() << "PrintReader\n");
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
new file mode 100644
index 000000000000..fb503f3d3e7e
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp
@@ -0,0 +1,2100 @@
+//===-- LVScope.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVScope class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVLine.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVRange.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Scope"
+
+namespace {
+const char *const KindArray = "Array";
+const char *const KindBlock = "Block";
+const char *const KindCallSite = "CallSite";
+const char *const KindClass = "Class";
+const char *const KindCompileUnit = "CompileUnit";
+const char *const KindEnumeration = "Enumeration";
+const char *const KindFile = "File";
+const char *const KindFunction = "Function";
+const char *const KindInlinedFunction = "InlinedFunction";
+const char *const KindNamespace = "Namespace";
+const char *const KindStruct = "Struct";
+const char *const KindTemplateAlias = "TemplateAlias";
+const char *const KindTemplatePack = "TemplatePack";
+const char *const KindUndefined = "Undefined";
+const char *const KindUnion = "Union";
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// DWARF lexical block, such as: namespace, function, compile unit, module, etc.
+//===----------------------------------------------------------------------===//
+LVScope::~LVScope() {
+ delete Types;
+ delete Symbols;
+ delete Scopes;
+ delete Lines;
+ delete Ranges;
+ delete Children;
+}
+
+// Return a string representation for the scope kind.
+const char *LVScope::kind() const {
+ const char *Kind = KindUndefined;
+ if (getIsArray())
+ Kind = KindArray;
+ else if (getIsBlock())
+ Kind = KindBlock;
+ else if (getIsCallSite())
+ Kind = KindCallSite;
+ else if (getIsCompileUnit())
+ Kind = KindCompileUnit;
+ else if (getIsEnumeration())
+ Kind = KindEnumeration;
+ else if (getIsInlinedFunction())
+ Kind = KindInlinedFunction;
+ else if (getIsNamespace())
+ Kind = KindNamespace;
+ else if (getIsTemplatePack())
+ Kind = KindTemplatePack;
+ else if (getIsRoot())
+ Kind = KindFile;
+ else if (getIsTemplateAlias())
+ Kind = KindTemplateAlias;
+ else if (getIsClass())
+ Kind = KindClass;
+ else if (getIsFunction())
+ Kind = KindFunction;
+ else if (getIsStructure())
+ Kind = KindStruct;
+ else if (getIsUnion())
+ Kind = KindUnion;
+ return Kind;
+}
+
+LVScopeDispatch LVScope::Dispatch = {
+ {LVScopeKind::IsAggregate, &LVScope::getIsAggregate},
+ {LVScopeKind::IsArray, &LVScope::getIsArray},
+ {LVScopeKind::IsBlock, &LVScope::getIsBlock},
+ {LVScopeKind::IsCallSite, &LVScope::getIsCallSite},
+ {LVScopeKind::IsCatchBlock, &LVScope::getIsCatchBlock},
+ {LVScopeKind::IsClass, &LVScope::getIsClass},
+ {LVScopeKind::IsCompileUnit, &LVScope::getIsCompileUnit},
+ {LVScopeKind::IsEntryPoint, &LVScope::getIsEntryPoint},
+ {LVScopeKind::IsEnumeration, &LVScope::getIsEnumeration},
+ {LVScopeKind::IsFunction, &LVScope::getIsFunction},
+ {LVScopeKind::IsFunctionType, &LVScope::getIsFunctionType},
+ {LVScopeKind::IsInlinedFunction, &LVScope::getIsInlinedFunction},
+ {LVScopeKind::IsLabel, &LVScope::getIsLabel},
+ {LVScopeKind::IsLexicalBlock, &LVScope::getIsLexicalBlock},
+ {LVScopeKind::IsNamespace, &LVScope::getIsNamespace},
+ {LVScopeKind::IsRoot, &LVScope::getIsRoot},
+ {LVScopeKind::IsStructure, &LVScope::getIsStructure},
+ {LVScopeKind::IsTemplate, &LVScope::getIsTemplate},
+ {LVScopeKind::IsTemplateAlias, &LVScope::getIsTemplateAlias},
+ {LVScopeKind::IsTemplatePack, &LVScope::getIsTemplatePack},
+ {LVScopeKind::IsTryBlock, &LVScope::getIsTryBlock},
+ {LVScopeKind::IsUnion, &LVScope::getIsUnion}};
+
+void LVScope::addToChildren(LVElement *Element) {
+ if (!Children)
+ Children = new LVElements();
+ Children->push_back(Element);
+}
+
+void LVScope::addElement(LVElement *Element) {
+ assert(Element && "Invalid element.");
+ if (Element->getIsType())
+ addElement(static_cast<LVType *>(Element));
+ else if (Element->getIsScope())
+ addElement(static_cast<LVScope *>(Element));
+ else if (Element->getIsSymbol())
+ addElement(static_cast<LVSymbol *>(Element));
+ else if (Element->getIsLine())
+ addElement(static_cast<LVLine *>(Element));
+ else
+ llvm_unreachable("Invalid Element.");
+}
+
+// Adds the line info item to the ones stored in the scope.
+void LVScope::addElement(LVLine *Line) {
+ assert(Line && "Invalid line.");
+ assert(!Line->getParent() && "Line already inserted");
+ if (!Lines)
+ Lines = new LVAutoLines();
+
+ // Add it to parent.
+ Lines->push_back(Line);
+ Line->setParent(this);
+
+ // Notify the reader about the new element being added.
+ getReaderCompileUnit()->addedElement(Line);
+
+ // All logical elements added to the children, are sorted by any of the
+ // following criterias: offset, name, line number, kind.
+ // Do not add the line records to the children, as they represent the
+ // logical view for the text section and any sorting will not preserve
+ // the original sequence.
+
+ // Indicate that this tree branch has lines.
+ traverseParents(&LVScope::getHasLines, &LVScope::setHasLines);
+}
+
+// Add a location.
+void LVScope::addObject(LVLocation *Location) {
+ assert(Location && "Invalid location.");
+ assert(!Location->getParent() && "Location already inserted");
+ if (!Ranges)
+ Ranges = new LVAutoLocations();
+
+ // Add it to parent.
+ Location->setParent(this);
+ Location->setOffset(getOffset());
+
+ Ranges->push_back(Location);
+ setHasRanges();
+}
+
+// Adds the scope to the child scopes and sets the parent in the child.
+void LVScope::addElement(LVScope *Scope) {
+ assert(Scope && "Invalid scope.");
+ assert(!Scope->getParent() && "Scope already inserted");
+ if (!Scopes)
+ Scopes = new LVAutoScopes();
+
+ // Add it to parent.
+ Scopes->push_back(Scope);
+ addToChildren(Scope);
+ Scope->setParent(this);
+
+ // Notify the reader about the new element being added.
+ getReaderCompileUnit()->addedElement(Scope);
+
+ // If the element is a global reference, mark its parent as having global
+ // references; that information is used, to print only those branches
+ // with global references.
+ if (Scope->getIsGlobalReference())
+ traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals);
+ else
+ traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals);
+
+ // Indicate that this tree branch has scopes.
+ traverseParents(&LVScope::getHasScopes, &LVScope::setHasScopes);
+}
+
+// Adds a symbol to the ones stored in the scope.
+void LVScope::addElement(LVSymbol *Symbol) {
+ assert(Symbol && "Invalid symbol.");
+ assert(!Symbol->getParent() && "Symbol already inserted");
+ if (!Symbols)
+ Symbols = new LVAutoSymbols();
+
+ // Add it to parent.
+ Symbols->push_back(Symbol);
+ addToChildren(Symbol);
+ Symbol->setParent(this);
+
+ // Notify the reader about the new element being added.
+ getReaderCompileUnit()->addedElement(Symbol);
+
+ // If the element is a global reference, mark its parent as having global
+ // references; that information is used, to print only those branches
+ // with global references.
+ if (Symbol->getIsGlobalReference())
+ traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals);
+ else
+ traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals);
+
+ // Indicate that this tree branch has symbols.
+ traverseParents(&LVScope::getHasSymbols, &LVScope::setHasSymbols);
+}
+
+// Adds a type to the ones stored in the scope.
+void LVScope::addElement(LVType *Type) {
+ assert(Type && "Invalid type.");
+ assert(!Type->getParent() && "Type already inserted");
+ if (!Types)
+ Types = new LVAutoTypes();
+
+ // Add it to parent.
+ Types->push_back(Type);
+ addToChildren(Type);
+ Type->setParent(this);
+
+ // Notify the reader about the new element being added.
+ getReaderCompileUnit()->addedElement(Type);
+
+ // If the element is a global reference, mark its parent as having global
+ // references; that information is used, to print only those branches
+ // with global references.
+ if (Type->getIsGlobalReference())
+ traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals);
+ else
+ traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals);
+
+ // Indicate that this tree branch has types.
+ traverseParents(&LVScope::getHasTypes, &LVScope::setHasTypes);
+}
+
+// Add a pair of ranges.
+void LVScope::addObject(LVAddress LowerAddress, LVAddress UpperAddress) {
+ // Pack the ranges into a Location object.
+ LVLocation *Location = new LVLocation();
+ Location->setLowerAddress(LowerAddress);
+ Location->setUpperAddress(UpperAddress);
+ Location->setIsAddressRange();
+
+ addObject(Location);
+}
+
+bool LVScope::removeElement(LVElement *Element) {
+ auto Predicate = [Element](LVElement *Item) -> bool {
+ return Item == Element;
+ };
+ auto RemoveElement = [Element, Predicate](auto &Container) -> bool {
+ auto Iter = std::remove_if(Container->begin(), Container->end(), Predicate);
+ if (Iter != Container->end()) {
+ Container->erase(Iter, Container->end());
+ Element->resetParent();
+ return true;
+ }
+ return false;
+ };
+
+ // As 'children' contains only (scopes, symbols and types), check if the
+ // element we are deleting is a line.
+ if (Element->getIsLine())
+ return RemoveElement(Lines);
+
+ if (RemoveElement(Children)) {
+ if (Element->getIsSymbol())
+ return RemoveElement(Symbols);
+ if (Element->getIsType())
+ return RemoveElement(Types);
+ if (Element->getIsScope())
+ return RemoveElement(Scopes);
+ llvm_unreachable("Invalid element.");
+ }
+
+ return false;
+}
+
+void LVScope::addMissingElements(LVScope *Reference) {
+ setAddedMissing();
+ if (!Reference)
+ return;
+
+ // Get abstract symbols for the given scope reference.
+ const LVSymbols *ReferenceSymbols = Reference->getSymbols();
+ if (!ReferenceSymbols)
+ return;
+
+ LVSymbols References;
+ References.append(ReferenceSymbols->begin(), ReferenceSymbols->end());
+
+ auto RemoveSymbol = [&](LVSymbols &Symbols, LVSymbol *Symbol) {
+ LVSymbols::iterator Iter = std::remove_if(
+ Symbols.begin(), Symbols.end(),
+ [Symbol](LVSymbol *Item) -> bool { return Item == Symbol; });
+ if (Iter != Symbols.end())
+ Symbols.erase(Iter, Symbols.end());
+ };
+
+ // Erase abstract symbols already in this scope from the collection of
+ // symbols in the referenced scope.
+ if (getSymbols())
+ for (const LVSymbol *Symbol : *getSymbols())
+ if (Symbol->getHasReferenceAbstract())
+ RemoveSymbol(References, Symbol->getReference());
+
+ // If we have elements left in 'References', those are the elements that
+ // need to be inserted in the current scope.
+ if (References.size()) {
+ LLVM_DEBUG({
+ dbgs() << "Insert Missing Inlined Elements\n"
+ << "Offset = " << hexSquareString(getOffset()) << " "
+ << "Abstract = " << hexSquareString(Reference->getOffset())
+ << "\n";
+ });
+ for (LVSymbol *Reference : References) {
+ LLVM_DEBUG({
+ dbgs() << "Missing Offset = " << hexSquareString(Reference->getOffset())
+ << "\n";
+ });
+ // We can't clone the abstract origin reference, as it contain extra
+ // information that is incorrect for the element to be inserted.
+ // As the symbol being added does not exist in the debug section,
+ // use its parent scope offset, to indicate its DIE location.
+ LVSymbol *Symbol = new LVSymbol();
+ addElement(Symbol);
+ Symbol->setOffset(getOffset());
+ Symbol->setIsOptimized();
+ Symbol->setReference(Reference);
+
+ // The symbol can be a constant, parameter or variable.
+ if (Reference->getIsConstant())
+ Symbol->setIsConstant();
+ else if (Reference->getIsParameter())
+ Symbol->setIsParameter();
+ else if (Reference->getIsVariable())
+ Symbol->setIsVariable();
+ else
+ llvm_unreachable("Invalid symbol kind.");
+ }
+ }
+}
+
+void LVScope::updateLevel(LVScope *Parent, bool Moved) {
+ // Update the level for the element itself and all its children, using the
+ // given scope parent as reference.
+ setLevel(Parent->getLevel() + 1);
+
+ // Update the children.
+ if (Children)
+ for (LVElement *Element : *Children)
+ Element->updateLevel(this, Moved);
+
+ // Update any lines.
+ if (Lines)
+ for (LVLine *Line : *Lines)
+ Line->updateLevel(this, Moved);
+}
+
+void LVScope::resolve() {
+ if (getIsResolved())
+ return;
+
+ // Resolve the element itself.
+ LVElement::resolve();
+
+ // Resolve the children.
+ if (Children)
+ for (LVElement *Element : *Children) {
+ if (getIsGlobalReference())
+ // If the scope is a global reference, mark all its children as well.
+ Element->setIsGlobalReference();
+ Element->resolve();
+ }
+}
+
+void LVScope::resolveName() {
+ if (getIsResolvedName())
+ return;
+ setIsResolvedName();
+
+ // If the scope is a template, resolve the template parameters and get
+ // the name for the template with the encoded arguments.
+ if (getIsTemplate())
+ resolveTemplate();
+ else {
+ if (LVElement *BaseType = getType()) {
+ BaseType->resolveName();
+ resolveFullname(BaseType);
+ }
+ }
+
+ // In the case of unnamed scopes, try to generate a name for it, using
+ // the parents name and the line information. In the case of compiler
+ // generated functions, use its linkage name if is available.
+ if (!isNamed()) {
+ if (getIsArtificial())
+ setName(getLinkageName());
+ else
+ generateName();
+ }
+
+ LVElement::resolveName();
+
+ // Resolve any given pattern.
+ patterns().resolvePatternMatch(this);
+}
+
+void LVScope::resolveReferences() {
+ // The scopes can have the following references to other elements:
+ // A type:
+ // DW_AT_type -> Type or Scope
+ // DW_AT_import -> Type
+ // A Reference:
+ // DW_AT_specification -> Scope
+ // DW_AT_abstract_origin -> Scope
+ // DW_AT_extension -> Scope
+
+ // Resolve any referenced scope.
+ LVScope *Reference = getReference();
+ if (Reference) {
+ Reference->resolve();
+ // Recursively resolve the scope names.
+ resolveReferencesChain();
+ }
+
+ // Set the file/line information using the Debug Information entry.
+ setFile(Reference);
+
+ // Resolve any referenced type or scope.
+ if (LVElement *Element = getType())
+ Element->resolve();
+}
+
+void LVScope::resolveElements() {
+ // The current element represents the Root. Traverse each Compile Unit.
+ if (!Scopes)
+ return;
+
+ for (LVScope *Scope : *Scopes) {
+ LVScopeCompileUnit *CompileUnit = static_cast<LVScopeCompileUnit *>(Scope);
+ getReader().setCompileUnit(CompileUnit);
+ CompileUnit->resolve();
+ // Propagate any matching information into the scopes tree.
+ CompileUnit->propagatePatternMatch();
+ }
+}
+
+StringRef LVScope::resolveReferencesChain() {
+ // If the scope has a DW_AT_specification or DW_AT_abstract_origin,
+ // follow the chain to resolve the name from those references.
+ if (getHasReference() && !isNamed())
+ setName(getReference()->resolveReferencesChain());
+
+ return getName();
+}
+
+// Get template parameter types.
+bool LVScope::getTemplateParameterTypes(LVTypes &Params) {
+ // Traverse the scope types and populate the given container with those
+ // types that are template parameters; that container will be used by
+ // 'encodeTemplateArguments' to resolve them.
+ if (const LVTypes *Types = getTypes())
+ for (LVType *Type : *Types)
+ if (Type->getIsTemplateParam()) {
+ Type->resolve();
+ Params.push_back(Type);
+ }
+
+ return !Params.empty();
+}
+
+// Resolve the template parameters/arguments relationship.
+void LVScope::resolveTemplate() {
+ if (getIsTemplateResolved())
+ return;
+ setIsTemplateResolved();
+
+ // Check if we need to encode the template arguments.
+ if (options().getAttributeEncoded()) {
+ LVTypes Params;
+ if (getTemplateParameterTypes(Params)) {
+ std::string EncodedArgs;
+ // Encode the arguments as part of the template name and update the
+ // template name, to reflect the encoded parameters.
+ encodeTemplateArguments(EncodedArgs, &Params);
+ setEncodedArgs(EncodedArgs);
+ }
+ }
+}
+
+// Get the qualified name for the template.
+void LVScope::getQualifiedName(std::string &QualifiedName) const {
+ if (getIsRoot() || getIsCompileUnit())
+ return;
+
+ if (LVScope *Parent = getParentScope())
+ Parent->getQualifiedName(QualifiedName);
+ if (!QualifiedName.empty())
+ QualifiedName.append("::");
+ QualifiedName.append(std::string(getName()));
+}
+
+// Encode the template arguments as part of the template name.
+void LVScope::encodeTemplateArguments(std::string &Name) const {
+ // Qualify only when we are expanding parameters that are template
+ // instances; the debugger will assume the current scope symbol as
+ // the qualifying tag for the symbol being generated, which gives:
+ // namespace std {
+ // ...
+ // set<float,std::less<float>,std::allocator<float>>
+ // ...
+ // }
+ // The 'set' symbol is assumed to have the qualified tag 'std'.
+
+ // We are resolving a template parameter which is another template. If
+ // it is already resolved, just get the qualified name and return.
+ std::string BaseName;
+ getQualifiedName(BaseName);
+ if (getIsTemplateResolved())
+ Name.append(BaseName);
+}
+
+void LVScope::encodeTemplateArguments(std::string &Name,
+ const LVTypes *Types) const {
+ // The encoded string will start with the scope name.
+ Name.append("<");
+
+ // The list of types are the template parameters.
+ if (Types) {
+ bool AddComma = false;
+ for (const LVType *Type : *Types) {
+ if (AddComma)
+ Name.append(", ");
+ Type->encodeTemplateArgument(Name);
+ AddComma = true;
+ }
+ }
+
+ Name.append(">");
+}
+
+bool LVScope::resolvePrinting() const {
+ // The warnings collected during the scope creation as per compile unit.
+ // If there is a request for printing warnings, always print its associate
+ // Compile Unit.
+ if (options().getPrintWarnings() && (getIsRoot() || getIsCompileUnit()))
+ return true;
+
+ // In selection mode, always print the root scope regardless of the
+ // number of matched elements. If no matches, the root by itself will
+ // indicate no matches.
+ if (options().getSelectExecute()) {
+ return getIsRoot() || getIsCompileUnit() || getHasPattern();
+ }
+
+ bool Globals = options().getAttributeGlobal();
+ bool Locals = options().getAttributeLocal();
+ if ((Globals && Locals) || (!Globals && !Locals)) {
+ // Print both Global and Local.
+ } else {
+ // Check for Global or Local Objects.
+ if ((Globals && !(getHasGlobals() || getIsGlobalReference())) ||
+ (Locals && !(getHasLocals() || !getIsGlobalReference())))
+ return false;
+ }
+
+ // For the case of functions, skip it if is compiler generated.
+ if (getIsFunction() && getIsArtificial() &&
+ !options().getAttributeGenerated())
+ return false;
+
+ return true;
+}
+
+Error LVScope::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS,
+ bool Full) const {
+ // During a view output splitting, use the output stream created by the
+ // split context, then switch to the reader output stream.
+ raw_ostream *StreamSplit = &OS;
+
+ // If 'Split', we use the scope name (CU name) as the ouput file; the
+ // delimiters in the pathname, must be replaced by a normal character.
+ if (getIsCompileUnit()) {
+ getReader().setCompileUnit(const_cast<LVScope *>(this));
+ if (Split) {
+ std::string ScopeName(getName());
+ if (std::error_code EC =
+ getReaderSplitContext().open(ScopeName, ".txt", OS))
+ return createStringError(EC, "Unable to create split output file %s",
+ ScopeName.c_str());
+ StreamSplit = static_cast<raw_ostream *>(&getReaderSplitContext().os());
+ }
+ }
+
+ // Ignore discarded or stripped scopes (functions).
+ bool DoPrint = (options().getAttributeDiscarded()) ? true : !getIsDiscarded();
+
+ // If we are in compare mode, the only conditions are related to the
+ // element being missing. In the case of elements comparison, we print the
+ // augmented view, that includes added elements.
+ // In print mode, we check other conditions, such as local, global, etc.
+ if (DoPrint) {
+ DoPrint =
+ getIsInCompare() ? options().getReportExecute() : resolvePrinting();
+ }
+
+ // At this point we have checked for very specific options, to decide if the
+ // element will be printed. Include the caller's test for element general
+ // print.
+ DoPrint = DoPrint && (Print || options().getOutputSplit());
+
+ if (DoPrint) {
+ // Print the element itself.
+ print(*StreamSplit, Full);
+
+ // Check if we have reached the requested lexical level specified in the
+ // command line options. Input file is level zero and the CU is level 1.
+ if ((getIsRoot() || options().getPrintAnyElement()) &&
+ options().getPrintFormatting() &&
+ getLevel() < options().getOutputLevel()) {
+ // Print the children.
+ if (Children)
+ for (const LVElement *Element : *Children) {
+ if (Match && !Element->getHasPattern())
+ continue;
+ if (Error Err =
+ Element->doPrint(Split, Match, Print, *StreamSplit, Full))
+ return Err;
+ }
+
+ // Print the line records.
+ if (Lines)
+ for (const LVLine *Line : *Lines) {
+ if (Match && !Line->getHasPattern())
+ continue;
+ if (Error Err =
+ Line->doPrint(Split, Match, Print, *StreamSplit, Full))
+ return Err;
+ }
+
+ // Print the warnings.
+ if (options().getPrintWarnings())
+ printWarnings(*StreamSplit, Full);
+ }
+ }
+
+ // Done printing the compile unit. Print any requested summary and
+ // restore the original output context.
+ if (getIsCompileUnit()) {
+ if (options().getPrintSummary())
+ printSummary(*StreamSplit);
+ if (options().getPrintSizes())
+ printSizes(*StreamSplit);
+ if (Split) {
+ getReaderSplitContext().close();
+ StreamSplit = &getReader().outputStream();
+ }
+ }
+
+ if (getIsRoot() && options().getPrintWarnings()) {
+ getReader().printRecords(*StreamSplit);
+ }
+
+ return Error::success();
+}
+
+void LVScope::sort() {
+ // Preserve the lines order as they are associated with user code.
+ LVSortFunction SortFunction = getSortFunction();
+ if (SortFunction) {
+ std::function<void(LVScope * Parent, LVSortFunction SortFunction)> Sort =
+ [&](LVScope *Parent, LVSortFunction SortFunction) {
+ auto Traverse = [&](auto *Set, LVSortFunction SortFunction) {
+ if (Set)
+ std::stable_sort(Set->begin(), Set->end(), SortFunction);
+ };
+ Traverse(Parent->Types, SortFunction);
+ Traverse(Parent->Symbols, SortFunction);
+ Traverse(Parent->Scopes, SortFunction);
+ Traverse(Parent->Ranges, compareRange);
+ Traverse(Parent->Children, SortFunction);
+
+ if (Parent->Scopes)
+ for (LVScope *Scope : *Parent->Scopes)
+ Sort(Scope, SortFunction);
+ };
+
+ // Start traversing the scopes root and transform the element name.
+ Sort(this, SortFunction);
+ }
+}
+
+void LVScope::traverseParents(LVScopeGetFunction GetFunction,
+ LVScopeSetFunction SetFunction) {
+ // Traverse the parent tree.
+ LVScope *Parent = this;
+ while (Parent) {
+ // Terminates if the 'SetFunction' has been already executed.
+ if ((Parent->*GetFunction)())
+ break;
+ (Parent->*SetFunction)();
+ Parent = Parent->getParentScope();
+ }
+}
+
+void LVScope::traverseParentsAndChildren(LVObjectGetFunction GetFunction,
+ LVObjectSetFunction SetFunction) {
+ if (options().getReportParents()) {
+ // First traverse the parent tree.
+ LVScope *Parent = this;
+ while (Parent) {
+ // Terminates if the 'SetFunction' has been already executed.
+ if ((Parent->*GetFunction)())
+ break;
+ (Parent->*SetFunction)();
+ Parent = Parent->getParentScope();
+ }
+ }
+
+ std::function<void(LVScope * Scope)> TraverseChildren = [&](LVScope *Scope) {
+ auto Traverse = [&](const auto *Set) {
+ if (Set)
+ for (const auto &Entry : *Set)
+ (Entry->*SetFunction)();
+ };
+
+ (Scope->*SetFunction)();
+
+ Traverse(Scope->getTypes());
+ Traverse(Scope->getSymbols());
+ Traverse(Scope->getLines());
+
+ if (const LVScopes *Scopes = Scope->getScopes())
+ for (LVScope *Scope : *Scopes)
+ TraverseChildren(Scope);
+ };
+
+ if (options().getReportChildren())
+ TraverseChildren(this);
+}
+
+// Traverse the symbol location ranges and for each range:
+// - Apply the 'ValidLocation' validation criteria.
+// - Add any failed range to the 'LocationList'.
+// - Calculate location coverage.
+void LVScope::getLocations(LVLocations &LocationList,
+ LVValidLocation ValidLocation, bool RecordInvalid) {
+ // Traverse scopes and symbols.
+ if (Symbols)
+ for (LVSymbol *Symbol : *Symbols)
+ Symbol->getLocations(LocationList, ValidLocation, RecordInvalid);
+ if (Scopes)
+ for (LVScope *Scope : *Scopes)
+ Scope->getLocations(LocationList, ValidLocation, RecordInvalid);
+}
+
+// Traverse the scope ranges and for each range:
+// - Apply the 'ValidLocation' validation criteria.
+// - Add any failed range to the 'LocationList'.
+// - Calculate location coverage.
+void LVScope::getRanges(LVLocations &LocationList,
+ LVValidLocation ValidLocation, bool RecordInvalid) {
+ // Ignore discarded or stripped scopes (functions).
+ if (getIsDiscarded())
+ return;
+
+ // Process the ranges for current scope.
+ if (Ranges) {
+ for (LVLocation *Location : *Ranges) {
+ // Add the invalid location object.
+ if (!(Location->*ValidLocation)() && RecordInvalid)
+ LocationList.push_back(Location);
+ }
+
+ // Calculate coverage factor.
+ calculateCoverage();
+ }
+
+ // Traverse the scopes.
+ if (Scopes)
+ for (LVScope *Scope : *Scopes)
+ Scope->getRanges(LocationList, ValidLocation, RecordInvalid);
+}
+
+// Get all the ranges associated with scopes.
+void LVScope::getRanges(LVRange &RangeList) {
+ // Ignore discarded or stripped scopes (functions).
+ if (getIsDiscarded())
+ return;
+
+ if (Ranges)
+ RangeList.addEntry(this);
+ if (Scopes)
+ for (LVScope *Scope : *Scopes)
+ Scope->getRanges(RangeList);
+}
+
+LVScope *LVScope::outermostParent(LVAddress Address) {
+ LVScope *Parent = this;
+ while (Parent) {
+ const LVLocations *ParentRanges = Parent->getRanges();
+ if (ParentRanges)
+ for (const LVLocation *Location : *ParentRanges)
+ if (Location->getLowerAddress() <= Address)
+ return Parent;
+ Parent = Parent->getParentScope();
+ }
+ return Parent;
+}
+
+LVScope *LVScope::findIn(const LVScopes *Targets) const {
+ if (!Targets)
+ return nullptr;
+
+ // In the case of overloaded functions, sometimes the DWARF used to
+ // describe them, does not give suficient information. Try to find a
+ // perfect match or mark them as possible conflicts.
+ LVScopes Candidates;
+ for (LVScope *Target : *Targets)
+ if (LVScope::equals(Target))
+ Candidates.push_back(Target);
+
+ LLVM_DEBUG({
+ if (!Candidates.empty()) {
+ dbgs() << "\n[LVScope::findIn]\n"
+ << "Reference: "
+ << "Offset = " << hexSquareString(getOffset()) << ", "
+ << "Level = " << getLevel() << ", "
+ << "Kind = " << formattedKind(kind()) << ", "
+ << "Name = " << formattedName(getName()) << "\n";
+ for (const LVScope *Candidate : Candidates)
+ dbgs() << "Candidate: "
+ << "Offset = " << hexSquareString(Candidate->getOffset()) << ", "
+ << "Level = " << Candidate->getLevel() << ", "
+ << "Kind = " << formattedKind(Candidate->kind()) << ", "
+ << "Name = " << formattedName(Candidate->getName()) << "\n";
+ }
+ });
+
+ if (!Candidates.empty())
+ return (Candidates.size() == 1)
+ ? (equals(Candidates[0]) ? Candidates[0] : nullptr)
+ : findEqualScope(&Candidates);
+
+ return nullptr;
+}
+
+bool LVScope::equalNumberOfChildren(const LVScope *Scope) const {
+ // Same number of children. Take into account which elements are requested
+ // to be included in the comparison.
+ return !(
+ (options().getCompareScopes() && scopeCount() != Scope->scopeCount()) ||
+ (options().getCompareSymbols() &&
+ symbolCount() != Scope->symbolCount()) ||
+ (options().getCompareTypes() && typeCount() != Scope->typeCount()) ||
+ (options().getCompareLines() && lineCount() != Scope->lineCount()));
+}
+
+void LVScope::markMissingParents(const LVScope *Target, bool TraverseChildren) {
+ auto SetCompareState = [&](auto *Container) {
+ if (Container)
+ for (auto *Entry : *Container)
+ Entry->setIsInCompare();
+ };
+ SetCompareState(Types);
+ SetCompareState(Symbols);
+ SetCompareState(Lines);
+ SetCompareState(Scopes);
+
+ // At this point, we are ready to start comparing the current scope, once
+ // the compare bits have been set.
+ if (options().getCompareTypes() && getTypes() && Target->getTypes())
+ LVType::markMissingParents(getTypes(), Target->getTypes());
+ if (options().getCompareSymbols() && getSymbols() && Target->getSymbols())
+ LVSymbol::markMissingParents(getSymbols(), Target->getSymbols());
+ if (options().getCompareLines() && getLines() && Target->getLines())
+ LVLine::markMissingParents(getLines(), Target->getLines());
+ if (getScopes() && Target->getScopes())
+ LVScope::markMissingParents(getScopes(), Target->getScopes(),
+ TraverseChildren);
+}
+
+void LVScope::markMissingParents(const LVScopes *References,
+ const LVScopes *Targets,
+ bool TraverseChildren) {
+ if (!(References && Targets))
+ return;
+
+ LLVM_DEBUG({
+ dbgs() << "\n[LVScope::markMissingParents]\n";
+ for (const LVScope *Reference : *References)
+ dbgs() << "References: "
+ << "Offset = " << hexSquareString(Reference->getOffset()) << ", "
+ << "Level = " << Reference->getLevel() << ", "
+ << "Kind = " << formattedKind(Reference->kind()) << ", "
+ << "Name = " << formattedName(Reference->getName()) << "\n";
+ for (const LVScope *Target : *Targets)
+ dbgs() << "Targets : "
+ << "Offset = " << hexSquareString(Target->getOffset()) << ", "
+ << "Level = " << Target->getLevel() << ", "
+ << "Kind = " << formattedKind(Target->kind()) << ", "
+ << "Name = " << formattedName(Target->getName()) << "\n";
+ });
+
+ for (LVScope *Reference : *References) {
+ // Don't process 'Block' scopes, as we can't identify them.
+ if (Reference->getIsBlock() || Reference->getIsGeneratedName())
+ continue;
+
+ LLVM_DEBUG({
+ dbgs() << "\nSearch Reference: "
+ << "Offset = " << hexSquareString(Reference->getOffset()) << " "
+ << "Name = " << formattedName(Reference->getName()) << "\n";
+ });
+ LVScope *Target = Reference->findIn(Targets);
+ if (Target) {
+ LLVM_DEBUG({
+ dbgs() << "\nFound Target: "
+ << "Offset = " << hexSquareString(Target->getOffset()) << " "
+ << "Name = " << formattedName(Target->getName()) << "\n";
+ });
+ if (TraverseChildren)
+ Reference->markMissingParents(Target, TraverseChildren);
+ } else {
+ LLVM_DEBUG({
+ dbgs() << "Missing Reference: "
+ << "Offset = " << hexSquareString(Reference->getOffset()) << " "
+ << "Name = " << formattedName(Reference->getName()) << "\n";
+ });
+ Reference->markBranchAsMissing();
+ }
+ }
+}
+
+bool LVScope::equals(const LVScope *Scope) const {
+ if (!LVElement::equals(Scope))
+ return false;
+ // For lexical scopes, check if their parents are the same.
+ if (getIsLexicalBlock() && Scope->getIsLexicalBlock())
+ return getParentScope()->equals(Scope->getParentScope());
+ return true;
+}
+
+LVScope *LVScope::findEqualScope(const LVScopes *Scopes) const {
+ assert(Scopes && "Scopes must not be nullptr");
+ for (LVScope *Scope : *Scopes)
+ if (equals(Scope))
+ return Scope;
+ return nullptr;
+}
+
+bool LVScope::equals(const LVScopes *References, const LVScopes *Targets) {
+ if (!References && !Targets)
+ return true;
+ if (References && Targets && References->size() == Targets->size()) {
+ for (const LVScope *Reference : *References)
+ if (!Reference->findIn(Targets))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+void LVScope::report(LVComparePass Pass) {
+ getComparator().printItem(this, Pass);
+ getComparator().push(this);
+ if (Children)
+ for (LVElement *Element : *Children)
+ Element->report(Pass);
+
+ if (Lines)
+ for (LVLine *Line : *Lines)
+ Line->report(Pass);
+ getComparator().pop();
+}
+
+void LVScope::printActiveRanges(raw_ostream &OS, bool Full) const {
+ if (options().getPrintFormatting() && options().getAttributeRange() &&
+ Ranges) {
+ for (const LVLocation *Location : *Ranges)
+ Location->print(OS, Full);
+ }
+}
+
+void LVScope::printEncodedArgs(raw_ostream &OS, bool Full) const {
+ if (options().getPrintFormatting() && options().getAttributeEncoded())
+ printAttributes(OS, Full, "{Encoded} ", const_cast<LVScope *>(this),
+ getEncodedArgs(), /*UseQuotes=*/false, /*PrintRef=*/false);
+}
+
+void LVScope::print(raw_ostream &OS, bool Full) const {
+ if (getIncludeInPrint() && getReader().doPrintScope(this)) {
+ // For a summary (printed elements), do not count the scope root.
+ // For a summary (selected elements) do not count a compile unit.
+ if (!(getIsRoot() || (getIsCompileUnit() && options().getSelectExecute())))
+ getReaderCompileUnit()->incrementPrintedScopes();
+ LVElement::print(OS, Full);
+ printExtra(OS, Full);
+ }
+}
+
+void LVScope::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind());
+ // Do not print any type or name for a lexical block.
+ if (!getIsBlock()) {
+ OS << " " << formattedName(getName());
+ if (!getIsAggregate())
+ OS << " -> " << typeOffsetAsString()
+ << formattedNames(getTypeQualifiedName(), typeAsString());
+ }
+ OS << "\n";
+
+ // Print any active ranges.
+ if (Full && getIsBlock())
+ printActiveRanges(OS, Full);
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF Union/Structure/Class.
+//===----------------------------------------------------------------------===//
+bool LVScopeAggregate::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+
+ if (!equalNumberOfChildren(Scope))
+ return false;
+
+ // Check if the parameters match in the case of templates.
+ if (!LVType::parametersMatch(getTypes(), Scope->getTypes()))
+ return false;
+
+ if (!isNamed() && !Scope->isNamed())
+ // In the case of unnamed union/structure/class compare the file name.
+ if (getFilenameIndex() != Scope->getFilenameIndex())
+ return false;
+
+ return true;
+}
+
+LVScope *LVScopeAggregate::findEqualScope(const LVScopes *Scopes) const {
+ assert(Scopes && "Scopes must not be nullptr");
+ for (LVScope *Scope : *Scopes)
+ if (equals(Scope))
+ return Scope;
+ return nullptr;
+}
+
+void LVScopeAggregate::printExtra(raw_ostream &OS, bool Full) const {
+ LVScope::printExtra(OS, Full);
+ if (Full) {
+ if (getIsTemplateResolved())
+ printEncodedArgs(OS, Full);
+ LVScope *Reference = getReference();
+ if (Reference)
+ Reference->printReference(OS, Full, const_cast<LVScopeAggregate *>(this));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF Template alias.
+//===----------------------------------------------------------------------===//
+bool LVScopeAlias::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+ return equalNumberOfChildren(Scope);
+}
+
+void LVScopeAlias::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> "
+ << typeOffsetAsString()
+ << formattedNames(getTypeQualifiedName(), typeAsString()) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF array (DW_TAG_array_type).
+//===----------------------------------------------------------------------===//
+void LVScopeArray::resolveExtra() {
+ // If the scope is an array, resolve the subrange entries and get those
+ // values encoded and assigned to the scope type.
+ // Encode the array subrange entries as part of the name.
+ if (getIsArrayResolved())
+ return;
+ setIsArrayResolved();
+
+ // There are 2 cases to represent the bounds information for an array:
+ // 1) DW_TAG_array_type
+ // DW_AT_type --> ref_type
+ // DW_TAG_subrange_type
+ // DW_AT_type --> ref_type (type of object)
+ // DW_AT_count --> value (number of elements in subrange)
+
+ // 2) DW_TAG_array_type
+ // DW_AT_type --> ref_type
+ // DW_TAG_subrange_type
+ // DW_AT_lower_bound --> value
+ // DW_AT_upper_bound --> value
+
+ // The idea is to represent the bounds as a string, depending on the format:
+ // 1) [count]
+ // 2) [lower][upper]
+
+ // Traverse scope types, looking for those types that are subranges.
+ LVTypes Subranges;
+ if (const LVTypes *Types = getTypes())
+ for (LVType *Type : *Types)
+ if (Type->getIsSubrange()) {
+ Type->resolve();
+ Subranges.push_back(Type);
+ }
+
+ // Use the subrange types to generate the high level name for the array.
+ // Check the type has been fully resolved.
+ if (LVElement *BaseType = getType()) {
+ BaseType->resolveName();
+ resolveFullname(BaseType);
+ }
+
+ // In 'resolveFullname' a check is done for double spaces in the type name.
+ std::stringstream ArrayInfo;
+ if (ElementType)
+ ArrayInfo << getTypeName().str() << " ";
+
+ for (const LVType *Type : Subranges) {
+ if (Type->getIsSubrangeCount())
+ // Check if we have DW_AT_count subrange style.
+ ArrayInfo << "[" << Type->getCount() << "]";
+ else {
+ // Get lower and upper subrange values.
+ unsigned LowerBound;
+ unsigned UpperBound;
+ std::tie(LowerBound, UpperBound) = Type->getBounds();
+
+ // The representation depends on the bound values. If the lower value
+ // is zero, treat the pair as the elements count. Otherwise, just use
+ // the pair, as they are representing arrays in languages other than
+ // C/C++ and the lower limit is not zero.
+ if (LowerBound)
+ ArrayInfo << "[" << LowerBound << ".." << UpperBound << "]";
+ else
+ ArrayInfo << "[" << UpperBound + 1 << "]";
+ }
+ }
+
+ // Update the scope name, to reflect the encoded subranges.
+ setName(ArrayInfo.str());
+}
+
+bool LVScopeArray::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+
+ if (!equalNumberOfChildren(Scope))
+ return false;
+
+ // Despite the arrays are encoded, to reflect the dimensions, we have to
+ // check the subranges, in order to determine if they are the same.
+ if (!LVType::equals(getTypes(), Scope->getTypes()))
+ return false;
+
+ return true;
+}
+
+void LVScopeArray::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << typeOffsetAsString()
+ << formattedName(getName()) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// An object file (single or multiple CUs).
+//===----------------------------------------------------------------------===//
+void LVScopeCompileUnit::addSize(LVScope *Scope, LVOffset Lower,
+ LVOffset Upper) {
+ LLVM_DEBUG({
+ dbgs() << format(
+ "CU [0x%08" PRIx64 "], Scope [0x%08" PRIx64 "], Range [0x%08" PRIx64
+ ":0x%08" PRIx64 "], Size = %" PRId64 "\n",
+ getOffset(), Scope->getOffset(), Lower, Upper, Upper - Lower);
+ });
+
+ // There is no need to check for a previous entry, as we are traversing the
+ // debug information in sequential order.
+ LVOffset Size = Upper - Lower;
+ Sizes[Scope] = Size;
+ if (this == Scope)
+ // Record contribution size for the compilation unit.
+ CUContributionSize = Size;
+}
+
+// Update parents and children with pattern information.
+void LVScopeCompileUnit::propagatePatternMatch() {
+ // At this stage, we have finished creating the Scopes tree and we have
+ // a list of elements that match the pattern specified in the command line.
+ // The pattern corresponds to a scope or element; mark parents and/or
+ // children as having that pattern, before any printing is done.
+ if (!options().getSelectExecute())
+ return;
+
+ if (MatchedScopes.size()) {
+ for (LVScope *Scope : MatchedScopes)
+ Scope->traverseParentsAndChildren(&LVScope::getHasPattern,
+ &LVScope::setHasPattern);
+ } else {
+ // Mark the compile unit as having a pattern to enable any requests to
+ // print sizes and summary as that information is recorded at that level.
+ setHasPattern();
+ }
+}
+
+void LVScopeCompileUnit::processRangeLocationCoverage(
+ LVValidLocation ValidLocation) {
+ if (options().getAttributeRange()) {
+ // Traverse the scopes to get scopes that have invalid ranges.
+ LVLocations Locations;
+ bool RecordInvalid = options().getWarningRanges();
+ getRanges(Locations, ValidLocation, RecordInvalid);
+
+ // Validate ranges associated with scopes.
+ if (RecordInvalid)
+ for (LVLocation *Location : Locations)
+ addInvalidRange(Location);
+ }
+
+ if (options().getAttributeLocation()) {
+ // Traverse the scopes to get locations that have invalid ranges.
+ LVLocations Locations;
+ bool RecordInvalid = options().getWarningLocations();
+ getLocations(Locations, ValidLocation, RecordInvalid);
+
+ // Validate ranges associated with locations.
+ if (RecordInvalid)
+ for (LVLocation *Location : Locations)
+ addInvalidLocation(Location);
+ }
+}
+
+void LVScopeCompileUnit::addMapping(LVLine *Line, LVSectionIndex SectionIndex) {
+ LVAddress Address = Line->getOffset();
+ SectionMappings.add(SectionIndex, Address, Line);
+}
+
+LVLine *LVScopeCompileUnit::lineLowerBound(LVAddress Address,
+ LVScope *Scope) const {
+ LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);
+ LVAddressToLine *Map = SectionMappings.findMap(SectionIndex);
+ if (!Map || Map->empty())
+ return nullptr;
+ LVAddressToLine::const_iterator Iter = Map->lower_bound(Address);
+ return (Iter != Map->end()) ? Iter->second : nullptr;
+}
+
+LVLine *LVScopeCompileUnit::lineUpperBound(LVAddress Address,
+ LVScope *Scope) const {
+ LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope);
+ LVAddressToLine *Map = SectionMappings.findMap(SectionIndex);
+ if (!Map || Map->empty())
+ return nullptr;
+ LVAddressToLine::const_iterator Iter = Map->upper_bound(Address);
+ if (Iter != Map->begin())
+ Iter = std::prev(Iter);
+ return Iter->second;
+}
+
+LVLineRange LVScopeCompileUnit::lineRange(LVLocation *Location) const {
+ // The parent of a location can be a symbol or a scope.
+ LVElement *Element = Location->getParent();
+ LVScope *Parent = Element->getIsScope() ? static_cast<LVScope *>(Element)
+ : Element->getParentScope();
+ LVLine *LowLine = lineLowerBound(Location->getLowerAddress(), Parent);
+ LVLine *HighLine = lineUpperBound(Location->getUpperAddress(), Parent);
+ return LVLineRange(LowLine, HighLine);
+}
+
+StringRef LVScopeCompileUnit::getFilename(size_t Index) const {
+ if (Index <= 0 || Index > Filenames.size())
+ return StringRef();
+ return getStringPool().getString(Filenames[Index - 1]);
+}
+
+bool LVScopeCompileUnit::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+
+ return getNameIndex() == Scope->getNameIndex();
+}
+
+void LVScopeCompileUnit::incrementPrintedLines() {
+ options().getSelectExecute() ? ++Found.Lines : ++Printed.Lines;
+}
+void LVScopeCompileUnit::incrementPrintedScopes() {
+ options().getSelectExecute() ? ++Found.Scopes : ++Printed.Scopes;
+}
+void LVScopeCompileUnit::incrementPrintedSymbols() {
+ options().getSelectExecute() ? ++Found.Symbols : ++Printed.Symbols;
+}
+void LVScopeCompileUnit::incrementPrintedTypes() {
+ options().getSelectExecute() ? ++Found.Types : ++Printed.Types;
+}
+
+// Values are used by '--summary' option (allocated).
+void LVScopeCompileUnit::increment(LVLine *Line) {
+ if (Line->getIncludeInPrint())
+ ++Allocated.Lines;
+}
+void LVScopeCompileUnit::increment(LVScope *Scope) {
+ if (Scope->getIncludeInPrint())
+ ++Allocated.Scopes;
+}
+void LVScopeCompileUnit::increment(LVSymbol *Symbol) {
+ if (Symbol->getIncludeInPrint())
+ ++Allocated.Symbols;
+}
+void LVScopeCompileUnit::increment(LVType *Type) {
+ if (Type->getIncludeInPrint())
+ ++Allocated.Types;
+}
+
+// A new element has been added to the scopes tree. Take the following steps:
+// Increase the added element counters, for printing summary.
+// During comparison notify the Reader of the new element.
+void LVScopeCompileUnit::addedElement(LVLine *Line) {
+ increment(Line);
+ getReader().notifyAddedElement(Line);
+}
+void LVScopeCompileUnit::addedElement(LVScope *Scope) {
+ increment(Scope);
+ getReader().notifyAddedElement(Scope);
+}
+void LVScopeCompileUnit::addedElement(LVSymbol *Symbol) {
+ increment(Symbol);
+ getReader().notifyAddedElement(Symbol);
+}
+void LVScopeCompileUnit::addedElement(LVType *Type) {
+ increment(Type);
+ getReader().notifyAddedElement(Type);
+}
+
+// Record unsuported DWARF tags.
+void LVScopeCompileUnit::addDebugTag(dwarf::Tag Target, LVOffset Offset) {
+ addItem<LVTagOffsetsMap, LVOffsetList, dwarf::Tag, LVOffset>(&DebugTags,
+ Target, Offset);
+}
+
+// Record elements with invalid offsets.
+void LVScopeCompileUnit::addInvalidOffset(LVOffset Offset, LVElement *Element) {
+ if (WarningOffsets.find(Offset) == WarningOffsets.end())
+ WarningOffsets.emplace(Offset, Element);
+}
+
+// Record symbols with invalid coverage values.
+void LVScopeCompileUnit::addInvalidCoverage(LVSymbol *Symbol) {
+ LVOffset Offset = Symbol->getOffset();
+ if (InvalidCoverages.find(Offset) == InvalidCoverages.end())
+ InvalidCoverages.emplace(Offset, Symbol);
+}
+
+// Record symbols with invalid locations.
+void LVScopeCompileUnit::addInvalidLocation(LVLocation *Location) {
+ addInvalidLocationOrRange(Location, Location->getParentSymbol(),
+ &InvalidLocations);
+}
+
+// Record scopes with invalid ranges.
+void LVScopeCompileUnit::addInvalidRange(LVLocation *Location) {
+ addInvalidLocationOrRange(Location, Location->getParentScope(),
+ &InvalidRanges);
+}
+
+// Record line zero.
+void LVScopeCompileUnit::addLineZero(LVLine *Line) {
+ LVScope *Scope = Line->getParentScope();
+ LVOffset Offset = Scope->getOffset();
+ addInvalidOffset(Offset, Scope);
+ addItem<LVOffsetLinesMap, LVLines, LVOffset, LVLine *>(&LinesZero, Offset,
+ Line);
+}
+
+void LVScopeCompileUnit::printLocalNames(raw_ostream &OS, bool Full) const {
+ if (!options().getPrintFormatting())
+ return;
+
+ // Calculate an indentation value, to preserve a nice layout.
+ size_t Indentation = options().indentationSize() +
+ lineNumberAsString().length() +
+ indentAsString(getLevel() + 1).length() + 3;
+
+ enum class Option { Directory, File };
+ auto PrintNames = [&](Option Action) {
+ StringRef Kind = Action == Option::Directory ? "Directory" : "File";
+ std::set<std::string> UniqueNames;
+ for (size_t Index : Filenames) {
+ // In the case of missing directory name in the .debug_line table,
+ // the returned string has a leading '/'.
+ StringRef Name = getStringPool().getString(Index);
+ size_t Pos = Name.rfind('/');
+ if (Pos != std::string::npos)
+ Name = (Action == Option::File) ? Name.substr(Pos + 1)
+ : Name.substr(0, Pos);
+ // Collect only unique names.
+ UniqueNames.insert(std::string(Name));
+ }
+ for (const std::string &Name : UniqueNames)
+ OS << std::string(Indentation, ' ') << formattedKind(Kind) << " "
+ << formattedName(Name) << "\n";
+ };
+
+ if (options().getAttributeDirectories())
+ PrintNames(Option::Directory);
+ if (options().getAttributeFiles())
+ PrintNames(Option::File);
+ if (options().getAttributePublics()) {
+ StringRef Kind = "Public";
+ // The public names are indexed by 'LVScope *'. We want to print
+ // them by logical element address, to show the scopes layout.
+ using OffsetSorted = std::map<LVAddress, LVPublicNames::const_iterator>;
+ OffsetSorted SortedNames;
+ for (LVPublicNames::const_iterator Iter = PublicNames.begin();
+ Iter != PublicNames.end(); ++Iter)
+ SortedNames.emplace(Iter->first->getOffset(), Iter);
+
+ LVPublicNames::const_iterator Iter;
+ for (OffsetSorted::reference Entry : SortedNames) {
+ Iter = Entry.second;
+ OS << std::string(Indentation, ' ') << formattedKind(Kind) << " "
+ << formattedName((*Iter).first->getName());
+ if (options().getAttributeOffset()) {
+ LVAddress Address = (*Iter).second.first;
+ size_t Size = (*Iter).second.second;
+ OS << " [" << hexString(Address) << ":" << hexString(Address + Size)
+ << "]";
+ }
+ OS << "\n";
+ }
+ }
+}
+
+void LVScopeCompileUnit::printWarnings(raw_ostream &OS, bool Full) const {
+ auto PrintHeader = [&](const char *Header) { OS << "\n" << Header << ":\n"; };
+ auto PrintFooter = [&](auto &Set) {
+ if (Set.empty())
+ OS << "None\n";
+ };
+ auto PrintOffset = [&](unsigned &Count, LVOffset Offset) {
+ if (Count == 5) {
+ Count = 0;
+ OS << "\n";
+ }
+ ++Count;
+ OS << hexSquareString(Offset) << " ";
+ };
+ auto PrintElement = [&](const LVOffsetElementMap &Map, LVOffset Offset) {
+ LVOffsetElementMap::const_iterator Iter = Map.find(Offset);
+ LVElement *Element = Iter != Map.end() ? Iter->second : nullptr;
+ OS << "[" << hexString(Offset) << "]";
+ if (Element)
+ OS << " " << formattedKind(Element->kind()) << " "
+ << formattedName(Element->getName());
+ OS << "\n";
+ };
+ auto PrintInvalidLocations = [&](const LVOffsetLocationsMap &Map,
+ const char *Header) {
+ PrintHeader(Header);
+ for (LVOffsetLocationsMap::const_reference Entry : Map) {
+ PrintElement(WarningOffsets, Entry.first);
+ for (const LVLocation *Location : *Entry.second)
+ OS << hexSquareString(Location->getOffset()) << " "
+ << Location->getIntervalInfo() << "\n";
+ }
+ PrintFooter(Map);
+ };
+
+ if (options().getInternalTag() && getReader().isBinaryTypeELF()) {
+ PrintHeader("Unsupported DWARF Tags");
+ for (LVTagOffsetsMap::const_reference Entry : DebugTags) {
+ OS << format("\n0x%02x", (unsigned)Entry.first) << ", "
+ << dwarf::TagString(Entry.first) << "\n";
+ unsigned Count = 0;
+ for (const LVOffset &Offset : *Entry.second)
+ PrintOffset(Count, Offset);
+ OS << "\n";
+ }
+ PrintFooter(DebugTags);
+ }
+
+ if (options().getWarningCoverages()) {
+ PrintHeader("Symbols Invalid Coverages");
+ for (LVOffsetSymbolMap::const_reference Entry : InvalidCoverages) {
+ // Symbol basic information.
+ LVSymbol *Symbol = Entry.second;
+ OS << hexSquareString(Entry.first) << " {Coverage} "
+ << format("%.2f%%", Symbol->getCoveragePercentage()) << " "
+ << formattedKind(Symbol->kind()) << " "
+ << formattedName(Symbol->getName()) << "\n";
+ }
+ PrintFooter(InvalidCoverages);
+ }
+
+ if (options().getWarningLines()) {
+ PrintHeader("Lines Zero References");
+ for (LVOffsetLinesMap::const_reference Entry : LinesZero) {
+ PrintElement(WarningOffsets, Entry.first);
+ unsigned Count = 0;
+ for (const LVLine *Line : *Entry.second)
+ PrintOffset(Count, Line->getOffset());
+ OS << "\n";
+ }
+ PrintFooter(LinesZero);
+ }
+
+ if (options().getWarningLocations())
+ PrintInvalidLocations(InvalidLocations, "Invalid Location Ranges");
+
+ if (options().getWarningRanges())
+ PrintInvalidLocations(InvalidRanges, "Invalid Code Ranges");
+}
+
+void LVScopeCompileUnit::printTotals(raw_ostream &OS) const {
+ OS << "\nTotals by lexical level:\n";
+ for (size_t Index = 1; Index <= MaxSeenLevel; ++Index)
+ OS << format("[%03d]: %10d (%6.2f%%)\n", Index, Totals[Index].first,
+ Totals[Index].second);
+}
+
+void LVScopeCompileUnit::printScopeSize(const LVScope *Scope, raw_ostream &OS) {
+ LVSizesMap::const_iterator Iter = Sizes.find(Scope);
+ if (Iter != Sizes.end()) {
+ LVOffset Size = Iter->second;
+ assert(CUContributionSize && "Invalid CU contribution size.");
+ // Get a percentage rounded to two decimal digits. This avoids
+ // implementation-defined rounding inside printing functions.
+ float Percentage =
+ rint((float(Size) / CUContributionSize) * 100.0 * 100.0) / 100.0;
+ OS << format("%10" PRId64 " (%6.2f%%) : ", Size, Percentage);
+ Scope->print(OS);
+
+ // Keep record of the total sizes at each lexical level.
+ LVLevel Level = Scope->getLevel();
+ if (Level > MaxSeenLevel)
+ MaxSeenLevel = Level;
+ if (Level >= Totals.size())
+ Totals.resize(2 * Level);
+ Totals[Level].first += Size;
+ Totals[Level].second += Percentage;
+ }
+}
+
+void LVScopeCompileUnit::printSizes(raw_ostream &OS) const {
+ // Recursively print the contributions for each scope.
+ std::function<void(const LVScope *Scope)> PrintScope =
+ [&](const LVScope *Scope) {
+ // If we have selection criteria, then use only the selected scopes.
+ if (options().getSelectExecute() && options().getReportAnyView()) {
+ for (const LVScope *Scope : MatchedScopes)
+ if (Scope->getLevel() < options().getOutputLevel())
+ printScopeSize(Scope, OS);
+ return;
+ }
+ if (Scope->getLevel() < options().getOutputLevel()) {
+ if (const LVScopes *Scopes = Scope->getScopes())
+ for (const LVScope *Scope : *Scopes) {
+ printScopeSize(Scope, OS);
+ PrintScope(Scope);
+ }
+ }
+ };
+
+ bool PrintScopes = options().getPrintScopes();
+ if (!PrintScopes)
+ options().setPrintScopes();
+ getReader().setCompileUnit(const_cast<LVScopeCompileUnit *>(this));
+
+ OS << "\nScope Sizes:\n";
+ options().resetPrintFormatting();
+ options().setPrintOffset();
+
+ // Print the scopes regardless if the user has requested any scopes
+ // printing. Set the option just to allow printing the contributions.
+ printScopeSize(this, OS);
+ PrintScope(this);
+
+ // Print total scope sizes by level.
+ printTotals(OS);
+
+ options().resetPrintOffset();
+ options().setPrintFormatting();
+
+ if (!PrintScopes)
+ options().resetPrintScopes();
+}
+
+void LVScopeCompileUnit::printSummary(raw_ostream &OS) const {
+ printSummary(OS, options().getSelectExecute() ? Found : Printed, "Printed");
+}
+
+// Print summary details for the scopes tree.
+void LVScopeCompileUnit::printSummary(raw_ostream &OS, const LVCounter &Counter,
+ const char *Header) const {
+ std::string Separator = std::string(29, '-');
+ auto PrintSeparator = [&]() { OS << Separator << "\n"; };
+ auto PrintHeadingRow = [&](const char *T, const char *U, const char *V) {
+ OS << format("%-9s%9s %9s\n", T, U, V);
+ };
+ auto PrintDataRow = [&](const char *T, unsigned U, unsigned V) {
+ OS << format("%-9s%9d %9d\n", T, U, V);
+ };
+
+ OS << "\n";
+ PrintSeparator();
+ PrintHeadingRow("Element", "Total", Header);
+ PrintSeparator();
+ PrintDataRow("Scopes", Allocated.Scopes, Counter.Scopes);
+ PrintDataRow("Symbols", Allocated.Symbols, Counter.Symbols);
+ PrintDataRow("Types", Allocated.Types, Counter.Types);
+ PrintDataRow("Lines", Allocated.Lines, Counter.Lines);
+ PrintSeparator();
+ PrintDataRow(
+ "Total",
+ Allocated.Scopes + Allocated.Symbols + Allocated.Lines + Allocated.Types,
+ Counter.Scopes + Counter.Symbols + Counter.Lines + Counter.Types);
+}
+
+void LVScopeCompileUnit::printMatchedElements(raw_ostream &OS,
+ bool UseMatchedElements) {
+ LVSortFunction SortFunction = getSortFunction();
+ if (SortFunction)
+ std::stable_sort(MatchedElements.begin(), MatchedElements.end(),
+ SortFunction);
+
+ // Check the type of elements required to be printed. 'MatchedElements'
+ // contains generic elements (lines, scopes, symbols, types). If we have a
+ // request to print any generic element, then allow the normal printing.
+ if (options().getPrintAnyElement()) {
+ if (UseMatchedElements)
+ OS << "\n";
+ print(OS);
+
+ if (UseMatchedElements) {
+ // Print the details for the matched elements.
+ for (const LVElement *Element : MatchedElements)
+ Element->print(OS);
+ } else {
+ // Print the view for the matched scopes.
+ for (const LVScope *Scope : MatchedScopes) {
+ Scope->print(OS);
+ if (const LVElements *Elements = Scope->getChildren())
+ for (LVElement *Element : *Elements)
+ Element->print(OS);
+ }
+ }
+
+ // Print any requested summary.
+ if (options().getPrintSummary()) {
+ // In the case of '--report=details' the matched elements are
+ // already counted; just proceed to print any requested summary.
+ // Otherwise, count them and print the summary.
+ if (!options().getReportList()) {
+ for (LVElement *Element : MatchedElements) {
+ if (!Element->getIncludeInPrint())
+ continue;
+ if (Element->getIsType())
+ ++Found.Types;
+ else if (Element->getIsSymbol())
+ ++Found.Symbols;
+ else if (Element->getIsScope())
+ ++Found.Scopes;
+ else if (Element->getIsLine())
+ ++Found.Lines;
+ else
+ assert(Element && "Invalid element.");
+ }
+ }
+ printSummary(OS, Found, "Printed");
+ }
+ }
+
+ // Check if we have a request to print sizes for the matched elements
+ // that are scopes.
+ if (options().getPrintSizes()) {
+ OS << "\n";
+ print(OS);
+
+ OS << "\nScope Sizes:\n";
+ printScopeSize(this, OS);
+ for (LVElement *Element : MatchedElements)
+ if (Element->getIsScope())
+ // Print sizes only for scopes.
+ printScopeSize(static_cast<LVScope *>(Element), OS);
+
+ printTotals(OS);
+ }
+}
+
+void LVScopeCompileUnit::print(raw_ostream &OS, bool Full) const {
+ // Reset counters for printed and found elements.
+ const_cast<LVScopeCompileUnit *>(this)->Found.reset();
+ const_cast<LVScopeCompileUnit *>(this)->Printed.reset();
+
+ if (getReader().doPrintScope(this) && options().getPrintFormatting())
+ OS << "\n";
+
+ LVScope::print(OS, Full);
+}
+
+void LVScopeCompileUnit::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " '" << getName() << "'\n";
+ if (options().getPrintFormatting() && options().getAttributeProducer())
+ printAttributes(OS, Full, "{Producer} ",
+ const_cast<LVScopeCompileUnit *>(this), getProducer(),
+ /*UseQuotes=*/true,
+ /*PrintRef=*/false);
+
+ // Reset file index, to allow its children to print the correct filename.
+ options().resetFilenameIndex();
+
+ // Print any files, directories, public names and active ranges.
+ if (Full) {
+ printLocalNames(OS, Full);
+ printActiveRanges(OS, Full);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF enumeration (DW_TAG_enumeration_type).
+//===----------------------------------------------------------------------===//
+bool LVScopeEnumeration::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+ return equalNumberOfChildren(Scope);
+}
+
+void LVScopeEnumeration::printExtra(raw_ostream &OS, bool Full) const {
+ // Print the full type name.
+ OS << formattedKind(kind()) << " " << (getIsEnumClass() ? "class " : "")
+ << formattedName(getName());
+ if (getHasType())
+ OS << " -> " << typeOffsetAsString()
+ << formattedNames(getTypeQualifiedName(), typeAsString());
+ OS << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF formal parameter pack (DW_TAG_GNU_formal_parameter_pack).
+//===----------------------------------------------------------------------===//
+bool LVScopeFormalPack::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+ return equalNumberOfChildren(Scope);
+}
+
+void LVScopeFormalPack::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF function.
+//===----------------------------------------------------------------------===//
+void LVScopeFunction::resolveReferences() {
+ // Before we resolve any references to other elements, check if we have
+ // to insert missing elements, that have been stripped, which will help
+ // the logical view comparison.
+ if (options().getAttributeInserted() && getHasReferenceAbstract() &&
+ !getAddedMissing()) {
+ // Add missing elements at the function scope.
+ addMissingElements(getReference());
+ if (Scopes)
+ for (LVScope *Scope : *Scopes)
+ if (Scope->getHasReferenceAbstract() && !Scope->getAddedMissing())
+ Scope->addMissingElements(Scope->getReference());
+ }
+
+ LVScope::resolveReferences();
+
+ // The DWARF 'extern' attribute is generated at the class level.
+ // 0000003f DW_TAG_class_type "CLASS"
+ // 00000048 DW_TAG_subprogram "bar"
+ // DW_AT_external DW_FORM_flag_present
+ // 00000070 DW_TAG_subprogram "bar"
+ // DW_AT_specification DW_FORM_ref4 0x00000048
+ // If there is a reference linking the declaration and definition, mark
+ // the definition as extern, to facilitate the logical view comparison.
+ if (getHasReferenceSpecification()) {
+ LVScope *Reference = getReference();
+ if (Reference && Reference->getIsExternal()) {
+ Reference->resetIsExternal();
+ setIsExternal();
+ }
+ }
+
+ // Resolve the function associated type.
+ if (!getType())
+ if (LVScope *Reference = getReference())
+ setType(Reference->getType());
+}
+
+void LVScopeFunction::setName(StringRef ObjectName) {
+ LVScope::setName(ObjectName);
+ // Check for system generated functions.
+ getReader().isSystemEntry(this, ObjectName);
+}
+
+void LVScopeFunction::resolveExtra() {
+ // Check if we need to encode the template arguments.
+ if (getIsTemplate())
+ resolveTemplate();
+}
+
+bool LVScopeFunction::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+
+ // When comparing logical elements, ignore any difference in the children.
+ if (options().getCompareContext() && !equalNumberOfChildren(Scope))
+ return false;
+
+ // Check if the linkage name matches.
+ if (getLinkageNameIndex() != Scope->getLinkageNameIndex())
+ return false;
+
+ // Check if the parameters match in the case of templates.
+ if (!LVType::parametersMatch(getTypes(), Scope->getTypes()))
+ return false;
+
+ // Check if the arguments match.
+ if (!LVSymbol::parametersMatch(getSymbols(), Scope->getSymbols()))
+ return false;
+
+ // Check if the lines match.
+ if (options().getCompareLines() &&
+ !LVLine::equals(getLines(), Scope->getLines()))
+ return false;
+
+ // Check if any reference is the same.
+ if (!referenceMatch(Scope))
+ return false;
+
+ if (getReference() && !getReference()->equals(Scope->getReference()))
+ return false;
+
+ return true;
+}
+
+LVScope *LVScopeFunction::findEqualScope(const LVScopes *Scopes) const {
+ assert(Scopes && "Scopes must not be nullptr");
+ // Go through candidates and try to find a best match.
+ for (LVScope *Scope : *Scopes)
+ // Match arguments, children, lines, references.
+ if (equals(Scope))
+ return Scope;
+ return nullptr;
+}
+
+void LVScopeFunction::printExtra(raw_ostream &OS, bool Full) const {
+ LVScope *Reference = getReference();
+
+ // Inline attributes based on the reference element.
+ uint32_t InlineCode =
+ Reference ? Reference->getInlineCode() : getInlineCode();
+
+ // Accessibility depends on the parent (class, structure).
+ uint32_t AccessCode = 0;
+ if (getIsMember())
+ AccessCode = getParentScope()->getIsClass() ? dwarf::DW_ACCESS_private
+ : dwarf::DW_ACCESS_public;
+
+ std::string Attributes =
+ getIsCallSite()
+ ? ""
+ : formatAttributes(externalString(), accessibilityString(AccessCode),
+ inlineCodeString(InlineCode), virtualityString());
+
+ OS << formattedKind(kind()) << " " << Attributes << formattedName(getName())
+ << discriminatorAsString() << " -> " << typeOffsetAsString()
+ << formattedNames(getTypeQualifiedName(), typeAsString()) << "\n";
+
+ // Print any active ranges.
+ if (Full) {
+ if (getIsTemplateResolved())
+ printEncodedArgs(OS, Full);
+ printActiveRanges(OS, Full);
+ if (getLinkageNameIndex())
+ printLinkageName(OS, Full, const_cast<LVScopeFunction *>(this),
+ const_cast<LVScopeFunction *>(this));
+ if (Reference)
+ Reference->printReference(OS, Full, const_cast<LVScopeFunction *>(this));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF inlined function (DW_TAG_inlined_function).
+//===----------------------------------------------------------------------===//
+void LVScopeFunctionInlined::resolveExtra() {
+ // Check if we need to encode the template arguments.
+ if (getIsTemplate())
+ resolveTemplate();
+}
+
+bool LVScopeFunctionInlined::equals(const LVScope *Scope) const {
+ if (!LVScopeFunction::equals(Scope))
+ return false;
+
+ // Check if any reference is the same.
+ if (getHasDiscriminator() && Scope->getHasDiscriminator())
+ if (getDiscriminator() != Scope->getDiscriminator())
+ return false;
+
+ // Check the call site information.
+ if (getCallFilenameIndex() != Scope->getCallFilenameIndex() ||
+ getCallLineNumber() != Scope->getCallLineNumber())
+ return false;
+
+ return true;
+}
+
+LVScope *LVScopeFunctionInlined::findEqualScope(const LVScopes *Scopes) const {
+ return LVScopeFunction::findEqualScope(Scopes);
+}
+
+void LVScopeFunctionInlined::printExtra(raw_ostream &OS, bool Full) const {
+ LVScopeFunction::printExtra(OS, Full);
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF subroutine type.
+//===----------------------------------------------------------------------===//
+// Resolve a Subroutine Type (Callback).
+void LVScopeFunctionType::resolveExtra() {
+ if (getIsMemberPointerResolved())
+ return;
+ setIsMemberPointerResolved();
+
+ // The encoded string has the return type and the formal parameters type.
+ std::string Name(typeAsString());
+ Name.append(" (*)");
+ Name.append("(");
+
+ // Traverse the scope symbols, looking for those which are parameters.
+ if (const LVSymbols *Symbols = getSymbols()) {
+ bool AddComma = false;
+ for (LVSymbol *Symbol : *Symbols)
+ if (Symbol->getIsParameter()) {
+ Symbol->resolve();
+ if (LVElement *Type = Symbol->getType())
+ Type->resolveName();
+ if (AddComma)
+ Name.append(", ");
+ Name.append(std::string(Symbol->getTypeName()));
+ AddComma = true;
+ }
+ }
+
+ Name.append(")");
+
+ // Update the scope name, to reflect the encoded parameters.
+ setName(Name);
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF namespace (DW_TAG_namespace).
+//===----------------------------------------------------------------------===//
+bool LVScopeNamespace::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+
+ if (!equalNumberOfChildren(Scope))
+ return false;
+
+ // Check if any reference is the same.
+ if (!referenceMatch(Scope))
+ return false;
+
+ if (getReference() && !getReference()->equals(Scope->getReference()))
+ return false;
+
+ return true;
+}
+
+LVScope *LVScopeNamespace::findEqualScope(const LVScopes *Scopes) const {
+ assert(Scopes && "Scopes must not be nullptr");
+ // Go through candidates and try to find a best match.
+ for (LVScope *Scope : *Scopes)
+ if (equals(Scope))
+ return Scope;
+ return nullptr;
+}
+
+void LVScopeNamespace::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n";
+
+ // Print any active ranges.
+ if (Full) {
+ printActiveRanges(OS, Full);
+
+ if (LVScope *Reference = getReference())
+ Reference->printReference(OS, Full, const_cast<LVScopeNamespace *>(this));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// An object file (single or multiple CUs).
+//===----------------------------------------------------------------------===//
+void LVScopeRoot::processRangeInformation() {
+ if (!options().getAttributeAnyLocation())
+ return;
+
+ if (Scopes)
+ for (LVScope *Scope : *Scopes) {
+ LVScopeCompileUnit *CompileUnit =
+ static_cast<LVScopeCompileUnit *>(Scope);
+ getReader().setCompileUnit(CompileUnit);
+ CompileUnit->processRangeLocationCoverage();
+ }
+}
+
+bool LVScopeRoot::equals(const LVScope *Scope) const {
+ return LVScope::equals(Scope);
+}
+
+void LVScopeRoot::print(raw_ostream &OS, bool Full) const {
+ OS << "\nLogical View:\n";
+ LVScope::print(OS, Full);
+}
+
+void LVScopeRoot::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << "";
+ if (options().getAttributeFormat())
+ OS << " -> " << getFileFormatName();
+ OS << "\n";
+}
+
+Error LVScopeRoot::doPrintMatches(bool Split, raw_ostream &OS,
+ bool UseMatchedElements) const {
+ // During a view output splitting, use the output stream created by the
+ // split context, then switch to the reader output stream.
+ static raw_ostream *StreamSplit = &OS;
+
+ if (Scopes) {
+ if (UseMatchedElements)
+ options().resetPrintFormatting();
+ print(OS);
+
+ for (LVScope *Scope : *Scopes) {
+ getReader().setCompileUnit(const_cast<LVScope *>(Scope));
+
+ // If 'Split', we use the scope name (CU name) as the ouput file; the
+ // delimiters in the pathname, must be replaced by a normal character.
+ if (Split) {
+ std::string ScopeName(Scope->getName());
+ if (std::error_code EC =
+ getReaderSplitContext().open(ScopeName, ".txt", OS))
+ return createStringError(EC, "Unable to create split output file %s",
+ ScopeName.c_str());
+ StreamSplit = static_cast<raw_ostream *>(&getReaderSplitContext().os());
+ }
+
+ Scope->printMatchedElements(*StreamSplit, UseMatchedElements);
+
+ // Done printing the compile unit. Restore the original output context.
+ if (Split) {
+ getReaderSplitContext().close();
+ StreamSplit = &getReader().outputStream();
+ }
+ }
+ if (UseMatchedElements)
+ options().setPrintFormatting();
+ }
+
+ return Error::success();
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF template parameter pack (DW_TAG_GNU_template_parameter_pack).
+//===----------------------------------------------------------------------===//
+bool LVScopeTemplatePack::equals(const LVScope *Scope) const {
+ if (!LVScope::equals(Scope))
+ return false;
+ return equalNumberOfChildren(Scope);
+}
+
+void LVScopeTemplatePack::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n";
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp
new file mode 100644
index 000000000000..ea01671d0eb1
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp
@@ -0,0 +1,113 @@
+//===-- LVSort.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Support for LVObject sorting.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVSort.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVElement.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include <string>
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Sort"
+
+//===----------------------------------------------------------------------===//
+// Callback functions to sort objects.
+//===----------------------------------------------------------------------===//
+// Callback comparator based on kind.
+LVSortValue llvm::logicalview::compareKind(const LVObject *LHS,
+ const LVObject *RHS) {
+ return std::string(LHS->kind()) < std::string(RHS->kind());
+}
+
+// Callback comparator based on line.
+LVSortValue llvm::logicalview::compareLine(const LVObject *LHS,
+ const LVObject *RHS) {
+ return LHS->getLineNumber() < RHS->getLineNumber();
+}
+
+// Callback comparator based on name.
+LVSortValue llvm::logicalview::compareName(const LVObject *LHS,
+ const LVObject *RHS) {
+ return LHS->getName() < RHS->getName();
+}
+
+// Callback comparator based on DIE offset.
+LVSortValue llvm::logicalview::compareOffset(const LVObject *LHS,
+ const LVObject *RHS) {
+ return LHS->getOffset() < RHS->getOffset();
+}
+
+// Callback comparator for Range compare.
+LVSortValue llvm::logicalview::compareRange(const LVObject *LHS,
+ const LVObject *RHS) {
+ if (LHS->getLowerAddress() < RHS->getLowerAddress())
+ return true;
+
+ // If the lower address is the same, use the upper address value in
+ // order to put first the smallest interval.
+ if (LHS->getLowerAddress() == RHS->getLowerAddress())
+ return LHS->getUpperAddress() < RHS->getUpperAddress();
+
+ return false;
+}
+
+// Callback comparator based on multiple keys (First: Kind).
+LVSortValue llvm::logicalview::sortByKind(const LVObject *LHS,
+ const LVObject *RHS) {
+ // Order in which the object attributes are used for comparison:
+ // kind, name, line number, offset.
+ std::tuple<std::string, StringRef, uint32_t, LVOffset> Left(
+ LHS->kind(), LHS->getName(), LHS->getLineNumber(), LHS->getOffset());
+ std::tuple<std::string, StringRef, uint32_t, LVOffset> Right(
+ RHS->kind(), RHS->getName(), RHS->getLineNumber(), RHS->getOffset());
+ return Left < Right;
+}
+
+// Callback comparator based on multiple keys (First: Line).
+LVSortValue llvm::logicalview::sortByLine(const LVObject *LHS,
+ const LVObject *RHS) {
+ // Order in which the object attributes are used for comparison:
+ // line number, name, kind, offset.
+ std::tuple<uint32_t, StringRef, std::string, LVOffset> Left(
+ LHS->getLineNumber(), LHS->getName(), LHS->kind(), LHS->getOffset());
+ std::tuple<uint32_t, StringRef, std::string, LVOffset> Right(
+ RHS->getLineNumber(), RHS->getName(), RHS->kind(), RHS->getOffset());
+ return Left < Right;
+}
+
+// Callback comparator based on multiple keys (First: Name).
+LVSortValue llvm::logicalview::sortByName(const LVObject *LHS,
+ const LVObject *RHS) {
+ // Order in which the object attributes are used for comparison:
+ // name, line number, kind, offset.
+ std::tuple<StringRef, uint32_t, std::string, LVOffset> Left(
+ LHS->getName(), LHS->getLineNumber(), LHS->kind(), LHS->getOffset());
+ std::tuple<StringRef, uint32_t, std::string, LVOffset> Right(
+ RHS->getName(), RHS->getLineNumber(), RHS->kind(), RHS->getOffset());
+ return Left < Right;
+}
+
+LVSortFunction llvm::logicalview::getSortFunction() {
+ using LVSortInfo = std::map<LVSortMode, LVSortFunction>;
+ static LVSortInfo SortInfo = {
+ {LVSortMode::None, nullptr}, {LVSortMode::Kind, sortByKind},
+ {LVSortMode::Line, sortByLine}, {LVSortMode::Name, sortByName},
+ {LVSortMode::Offset, compareOffset},
+ };
+
+ LVSortFunction SortFunction = nullptr;
+ LVSortInfo::iterator Iter = SortInfo.find(options().getSortMode());
+ if (Iter != SortInfo.end())
+ SortFunction = Iter->second;
+ return SortFunction;
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp
new file mode 100644
index 000000000000..9fa1f28eb089
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp
@@ -0,0 +1,56 @@
+//===-- LVSupport.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the supporting functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h"
+#include "llvm/Support/FormatAdapters.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <iomanip>
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Support"
+
+// Perform the following transformations to the given 'Path':
+// - all characters to lowercase.
+// - '\\' into '/' (Platform independent).
+// - '//' into '/'
+std::string llvm::logicalview::transformPath(StringRef Path) {
+ std::string Name(Path);
+ std::transform(Name.begin(), Name.end(), Name.begin(), tolower);
+ std::replace(Name.begin(), Name.end(), '\\', '/');
+
+ // Remove all duplicate slashes.
+ size_t Pos = 0;
+ while ((Pos = Name.find("//", Pos)) != std::string::npos)
+ Name.erase(Pos, 1);
+
+ return Name;
+}
+
+// Convert the given 'Path' to lowercase and change any matching character
+// from 'CharSet' into '_'.
+// The characters in 'CharSet' are:
+// '/', '\', '<', '>', '.', ':', '%', '*', '?', '|', '"', ' '.
+std::string llvm::logicalview::flattenedFilePath(StringRef Path) {
+ std::string Name(Path);
+ std::transform(Name.begin(), Name.end(), Name.begin(), tolower);
+
+ const char *CharSet = "/\\<>.:%*?|\" ";
+ char *Input = Name.data();
+ while (Input && *Input) {
+ Input = strpbrk(Input, CharSet);
+ if (Input)
+ *Input++ = '_';
+ };
+ return Name;
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp
new file mode 100644
index 000000000000..82633fbc6b2e
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp
@@ -0,0 +1,449 @@
+//===-- LVSymbol.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVSymbol class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Symbol"
+
+namespace {
+const char *const KindCallSiteParameter = "CallSiteParameter";
+const char *const KindConstant = "Constant";
+const char *const KindInherits = "Inherits";
+const char *const KindMember = "Member";
+const char *const KindParameter = "Parameter";
+const char *const KindUndefined = "Undefined";
+const char *const KindUnspecified = "Unspecified";
+const char *const KindVariable = "Variable";
+} // end anonymous namespace
+
+// Return a string representation for the symbol kind.
+const char *LVSymbol::kind() const {
+ const char *Kind = KindUndefined;
+ if (getIsCallSiteParameter())
+ Kind = KindCallSiteParameter;
+ else if (getIsConstant())
+ Kind = KindConstant;
+ else if (getIsInheritance())
+ Kind = KindInherits;
+ else if (getIsMember())
+ Kind = KindMember;
+ else if (getIsParameter())
+ Kind = KindParameter;
+ else if (getIsUnspecified())
+ Kind = KindUnspecified;
+ else if (getIsVariable())
+ Kind = KindVariable;
+ return Kind;
+}
+
+LVSymbolDispatch LVSymbol::Dispatch = {
+ {LVSymbolKind::IsCallSiteParameter, &LVSymbol::getIsCallSiteParameter},
+ {LVSymbolKind::IsConstant, &LVSymbol::getIsConstant},
+ {LVSymbolKind::IsInheritance, &LVSymbol::getIsInheritance},
+ {LVSymbolKind::IsMember, &LVSymbol::getIsMember},
+ {LVSymbolKind::IsParameter, &LVSymbol::getIsParameter},
+ {LVSymbolKind::IsUnspecified, &LVSymbol::getIsUnspecified},
+ {LVSymbolKind::IsVariable, &LVSymbol::getIsVariable}};
+
+// Add a Location Entry.
+void LVSymbol::addLocation(dwarf::Attribute Attr, LVAddress LowPC,
+ LVAddress HighPC, LVUnsigned SectionOffset,
+ uint64_t LocDescOffset, bool CallSiteLocation) {
+ if (!Locations)
+ Locations = new LVAutoLocations();
+
+ // Create the location entry.
+ CurrentLocation = new LVLocationSymbol();
+ CurrentLocation->setParent(this);
+ CurrentLocation->setAttr(Attr);
+ if (CallSiteLocation)
+ CurrentLocation->setIsCallSite();
+ CurrentLocation->addObject(LowPC, HighPC, SectionOffset, LocDescOffset);
+ Locations->push_back(CurrentLocation);
+
+ // Mark the symbol as having location information.
+ setHasLocation();
+}
+
+// Add a Location Record.
+void LVSymbol::addLocationOperands(LVSmall Opcode, uint64_t Operand1,
+ uint64_t Operand2) {
+ if (CurrentLocation)
+ CurrentLocation->addObject(Opcode, Operand1, Operand2);
+}
+
+// Add a Location Entry.
+void LVSymbol::addLocationConstant(dwarf::Attribute Attr, LVUnsigned Constant,
+ uint64_t LocDescOffset) {
+ // Create a Location Entry, with the global information.
+ addLocation(Attr,
+ /*LowPC=*/0, /*HighPC=*/-1,
+ /*SectionOffset=*/0, LocDescOffset);
+
+ // Add records to Location Entry.
+ addLocationOperands(/*Opcode=*/LVLocationMemberOffset,
+ /*Operand1=*/Constant, /*Operand2=*/0);
+}
+
+LVLocations::iterator LVSymbol::addLocationGap(LVLocations::iterator Pos,
+ LVAddress LowPC,
+ LVAddress HighPC) {
+ // Create a location entry for the gap.
+ LVLocation *Gap = new LVLocationSymbol();
+ Gap->setParent(this);
+ Gap->setAttr(dwarf::DW_AT_location);
+ Gap->addObject(LowPC, HighPC,
+ /*section_offset=*/0,
+ /*locdesc_offset=*/0);
+
+ LVLocations::iterator Iter = Locations->insert(Pos, Gap);
+
+ // Add gap to Location Entry.
+ Gap->addObject(/*op=*/dwarf::DW_OP_hi_user,
+ /*opd1=*/0, /*opd2=*/0);
+
+ // Mark the entry as a gap.
+ Gap->setIsGapEntry();
+
+ return Iter;
+}
+
+void LVSymbol::fillLocationGaps() {
+ // The symbol has locations records. Fill gaps in the location list.
+ if (!getHasLocation() || !getFillGaps())
+ return;
+
+ // Get the parent range information and add dummy location entries.
+ const LVLocations *Ranges = getParentScope()->getRanges();
+ if (!Ranges)
+ return;
+
+ for (const LVLocation *Entry : *Ranges) {
+ LVAddress ParentLowPC = Entry->getLowerAddress();
+ LVAddress ParentHighPC = Entry->getUpperAddress();
+
+ // Traverse the symbol locations and for each location contained in
+ // the current parent range, insert locations for any existing gap.
+ LVLocation *Location;
+ LVAddress LowPC = 0;
+ LVAddress Marker = ParentLowPC;
+ for (LVLocations::iterator Iter = Locations->begin();
+ Iter != Locations->end(); ++Iter) {
+ Location = *Iter;
+ LowPC = Location->getLowerAddress();
+ if (LowPC != Marker) {
+ // We have a gap at [Marker,LowPC - 1].
+ Iter = addLocationGap(Iter, Marker, LowPC - 1);
+ ++Iter;
+ }
+
+ // Move to the next item in the location list.
+ Marker = Location->getUpperAddress() + 1;
+ }
+
+ // Check any gap at the end.
+ if (Marker < ParentHighPC)
+ // We have a gap at [Marker,ParentHighPC].
+ addLocationGap(Locations->end(), Marker, ParentHighPC);
+ }
+}
+
+// Get all the locations based on the valid function.
+void LVSymbol::getLocations(LVLocations &LocationList,
+ LVValidLocation ValidLocation, bool RecordInvalid) {
+ if (!Locations)
+ return;
+
+ for (LVLocation *Location : *Locations) {
+ // Add the invalid location object.
+ if (!(Location->*ValidLocation)() && RecordInvalid)
+ LocationList.push_back(Location);
+ }
+
+ // Calculate coverage factor.
+ calculateCoverage();
+}
+
+void LVSymbol::getLocations(LVLocations &LocationList) const {
+ if (!Locations)
+ return;
+
+ for (LVLocation *Location : *Locations)
+ LocationList.push_back(Location);
+}
+
+// Calculate coverage factor.
+void LVSymbol::calculateCoverage() {
+ if (!LVLocation::calculateCoverage(Locations, CoverageFactor,
+ CoveragePercentage)) {
+ LVScope *Parent = getParentScope();
+ if (Parent->getIsInlinedFunction()) {
+ // For symbols representing the inlined function parameters and its
+ // variables, get the outer most parent that contains their location
+ // lower address.
+ // The symbol can have a set of non-contiguous locations. We are using
+ // only the first location entry to get the outermost parent.
+ // If no scope contains the location, assume its enclosing parent.
+ LVScope *Scope =
+ Parent->outermostParent(Locations->front()->getLowerAddress());
+ if (Scope)
+ Parent = Scope;
+ }
+ unsigned CoverageParent = Parent->getCoverageFactor();
+ // Get a percentage rounded to two decimal digits. This avoids
+ // implementation-defined rounding inside printing functions.
+ CoveragePercentage =
+ CoverageParent
+ ? rint((double(CoverageFactor) / CoverageParent) * 100.0 * 100.0) /
+ 100.0
+ : 0;
+ // Record invalid coverage entry.
+ if (options().getWarningCoverages() && CoveragePercentage > 100)
+ getReaderCompileUnit()->addInvalidCoverage(this);
+ }
+}
+
+void LVSymbol::resolveName() {
+ if (getIsResolvedName())
+ return;
+ setIsResolvedName();
+
+ LVElement::resolveName();
+
+ // Resolve any given pattern.
+ patterns().resolvePatternMatch(this);
+}
+
+void LVSymbol::resolveReferences() {
+ // The symbols can have the following references to other elements:
+ // A Type:
+ // DW_AT_type -> Type or Scope
+ // DW_AT_import -> Type
+ // A Reference:
+ // DW_AT_specification -> Symbol
+ // DW_AT_abstract_origin -> Symbol
+ // DW_AT_extension -> Symbol
+
+ // Resolve any referenced symbol.
+ LVSymbol *Reference = getReference();
+ if (Reference) {
+ Reference->resolve();
+ // Recursively resolve the symbol names.
+ resolveReferencesChain();
+ }
+
+ // Set the file/line information using the Debug Information entry.
+ setFile(Reference);
+
+ // Resolve symbol type.
+ if (LVElement *Element = getType()) {
+ Element->resolve();
+
+ // In the case of demoted typedefs, use the underlying type.
+ if (Element->getIsTypedefReduced()) {
+ Element = Element->getType();
+ Element->resolve();
+ }
+
+ // If the type is a template parameter, get its type, which can
+ // point to a type or scope, depending on the argument instance.
+ setGenericType(Element);
+ }
+
+ // Resolve the variable associated type.
+ if (!getType() && Reference)
+ setType(Reference->getType());
+}
+
+StringRef LVSymbol::resolveReferencesChain() {
+ // If the symbol have a DW_AT_specification or DW_AT_abstract_origin,
+ // follow the chain to resolve the name from those references.
+ if (getHasReference() && !isNamed())
+ setName(getReference()->resolveReferencesChain());
+
+ return getName();
+}
+
+void LVSymbol::markMissingParents(const LVSymbols *References,
+ const LVSymbols *Targets) {
+ if (!(References && Targets))
+ return;
+
+ LLVM_DEBUG({
+ dbgs() << "\n[LVSymbol::markMissingParents]\n";
+ for (const LVSymbol *Reference : *References)
+ dbgs() << "References: "
+ << "Kind = " << formattedKind(Reference->kind()) << ", "
+ << "Name = " << formattedName(Reference->getName()) << "\n";
+ for (const LVSymbol *Target : *Targets)
+ dbgs() << "Targets : "
+ << "Kind = " << formattedKind(Target->kind()) << ", "
+ << "Name = " << formattedName(Target->getName()) << "\n";
+ });
+
+ for (LVSymbol *Reference : *References) {
+ LLVM_DEBUG({
+ dbgs() << "Search Reference: Name = "
+ << formattedName(Reference->getName()) << "\n";
+ });
+ if (!Reference->findIn(Targets))
+ Reference->markBranchAsMissing();
+ }
+}
+
+LVSymbol *LVSymbol::findIn(const LVSymbols *Targets) const {
+ if (!Targets)
+ return nullptr;
+
+ LLVM_DEBUG({
+ dbgs() << "\n[LVSymbol::findIn]\n"
+ << "Reference: "
+ << "Level = " << getLevel() << ", "
+ << "Kind = " << formattedKind(kind()) << ", "
+ << "Name = " << formattedName(getName()) << "\n";
+ for (const LVSymbol *Target : *Targets)
+ dbgs() << "Target : "
+ << "Level = " << Target->getLevel() << ", "
+ << "Kind = " << formattedKind(Target->kind()) << ", "
+ << "Name = " << formattedName(Target->getName()) << "\n";
+ });
+
+ for (LVSymbol *Target : *Targets)
+ if (equals(Target))
+ return Target;
+
+ return nullptr;
+}
+
+// Check for a match on the arguments of a function.
+bool LVSymbol::parametersMatch(const LVSymbols *References,
+ const LVSymbols *Targets) {
+ if (!References && !Targets)
+ return true;
+ if (References && Targets) {
+ LVSymbols ReferenceParams;
+ getParameters(References, &ReferenceParams);
+ LVSymbols TargetParams;
+ getParameters(Targets, &TargetParams);
+ return LVSymbol::equals(&ReferenceParams, &TargetParams);
+ }
+ return false;
+}
+
+// Return the symbols which are parameters.
+void LVSymbol::getParameters(const LVSymbols *Symbols, LVSymbols *Parameters) {
+ if (Symbols)
+ for (LVSymbol *Symbol : *Symbols)
+ if (Symbol->getIsParameter())
+ Parameters->push_back(Symbol);
+}
+
+bool LVSymbol::equals(const LVSymbol *Symbol) const {
+ if (!LVElement::equals(Symbol))
+ return false;
+
+ // Check if any reference is the same.
+ if (!referenceMatch(Symbol))
+ return false;
+
+ if (getReference() && !getReference()->equals(Symbol->getReference()))
+ return false;
+
+ return true;
+}
+
+bool LVSymbol::equals(const LVSymbols *References, const LVSymbols *Targets) {
+ if (!References && !Targets)
+ return true;
+ if (References && Targets && References->size() == Targets->size()) {
+ for (const LVSymbol *Reference : *References)
+ if (!Reference->findIn(Targets))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+void LVSymbol::report(LVComparePass Pass) {
+ getComparator().printItem(this, Pass);
+}
+
+void LVSymbol::printLocations(raw_ostream &OS, bool Full) const {
+ if (Locations)
+ for (const LVLocation *Location : *Locations)
+ Location->printRaw(OS, Full);
+}
+
+void LVSymbol::print(raw_ostream &OS, bool Full) const {
+ if (getIncludeInPrint() && getReader().doPrintSymbol(this)) {
+ getReaderCompileUnit()->incrementPrintedSymbols();
+ LVElement::print(OS, Full);
+ printExtra(OS, Full);
+ }
+}
+
+void LVSymbol::printExtra(raw_ostream &OS, bool Full) const {
+ // Accessibility depends on the parent (class, structure).
+ uint32_t AccessCode = 0;
+ if (getIsMember() || getIsInheritance())
+ AccessCode = getParentScope()->getIsClass() ? dwarf::DW_ACCESS_private
+ : dwarf::DW_ACCESS_public;
+
+ const LVSymbol *Symbol = getIsInlined() ? Reference : this;
+ std::string Attributes =
+ Symbol->getIsCallSiteParameter()
+ ? ""
+ : formatAttributes(Symbol->externalString(),
+ Symbol->accessibilityString(AccessCode),
+ virtualityString());
+
+ OS << formattedKind(Symbol->kind()) << " " << Attributes;
+ if (Symbol->getIsUnspecified())
+ OS << formattedName(Symbol->getName());
+ else {
+ if (Symbol->getIsInheritance())
+ OS << Symbol->typeOffsetAsString()
+ << formattedNames(Symbol->getTypeQualifiedName(),
+ Symbol->typeAsString());
+ else {
+ OS << formattedName(Symbol->getName());
+ // Print any bitfield information.
+ if (uint32_t Size = getBitSize())
+ OS << ":" << Size;
+ OS << " -> " << Symbol->typeOffsetAsString()
+ << formattedNames(Symbol->getTypeQualifiedName(),
+ Symbol->typeAsString());
+ }
+ }
+
+ // Print any initial value if any.
+ if (ValueIndex)
+ OS << " = " << formattedName(getValue());
+ OS << "\n";
+
+ if (Full && options().getPrintFormatting()) {
+ if (getLinkageNameIndex())
+ printLinkageName(OS, Full, const_cast<LVSymbol *>(this));
+ if (LVSymbol *Reference = getReference())
+ Reference->printReference(OS, Full, const_cast<LVSymbol *>(this));
+
+ // Print location information.
+ LVLocation::print(Locations, OS, Full);
+ }
+}
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp
new file mode 100644
index 000000000000..3d32c34ee02a
--- /dev/null
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp
@@ -0,0 +1,524 @@
+//===-- LVType.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This implements the LVType class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVReader.h"
+#include "llvm/DebugInfo/LogicalView/Core/LVScope.h"
+
+using namespace llvm;
+using namespace llvm::logicalview;
+
+#define DEBUG_TYPE "Type"
+
+namespace {
+const char *const KindBaseType = "BaseType";
+const char *const KindConst = "Const";
+const char *const KindEnumerator = "Enumerator";
+const char *const KindImport = "Import";
+const char *const KindPointer = "Pointer";
+const char *const KindPointerMember = "PointerMember";
+const char *const KindReference = "Reference";
+const char *const KindRestrict = "Restrict";
+const char *const KindRvalueReference = "RvalueReference";
+const char *const KindSubrange = "Subrange";
+const char *const KindTemplateTemplate = "TemplateTemplate";
+const char *const KindTemplateType = "TemplateType";
+const char *const KindTemplateValue = "TemplateValue";
+const char *const KindTypeAlias = "TypeAlias";
+const char *const KindUndefined = "Undefined";
+const char *const KindUnaligned = "Unaligned";
+const char *const KindUnspecified = "Unspecified";
+const char *const KindVolatile = "Volatile";
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// DWARF Type.
+//===----------------------------------------------------------------------===//
+// Return a string representation for the type kind.
+const char *LVType::kind() const {
+ const char *Kind = KindUndefined;
+ if (getIsBase())
+ Kind = KindBaseType;
+ else if (getIsConst())
+ Kind = KindConst;
+ else if (getIsEnumerator())
+ Kind = KindEnumerator;
+ else if (getIsImport())
+ Kind = KindImport;
+ else if (getIsPointerMember())
+ Kind = KindPointerMember;
+ else if (getIsPointer())
+ Kind = KindPointer;
+ else if (getIsReference())
+ Kind = KindReference;
+ else if (getIsRestrict())
+ Kind = KindRestrict;
+ else if (getIsRvalueReference())
+ Kind = KindRvalueReference;
+ else if (getIsSubrange())
+ Kind = KindSubrange;
+ else if (getIsTemplateTypeParam())
+ Kind = KindTemplateType;
+ else if (getIsTemplateValueParam())
+ Kind = KindTemplateValue;
+ else if (getIsTemplateTemplateParam())
+ Kind = KindTemplateTemplate;
+ else if (getIsTypedef())
+ Kind = KindTypeAlias;
+ else if (getIsUnaligned())
+ Kind = KindUnaligned;
+ else if (getIsUnspecified())
+ Kind = KindUnspecified;
+ else if (getIsVolatile())
+ Kind = KindVolatile;
+ return Kind;
+}
+
+LVTypeDispatch LVType::Dispatch = {
+ {LVTypeKind::IsBase, &LVType::getIsBase},
+ {LVTypeKind::IsConst, &LVType::getIsConst},
+ {LVTypeKind::IsEnumerator, &LVType::getIsEnumerator},
+ {LVTypeKind::IsImport, &LVType::getIsImport},
+ {LVTypeKind::IsImportDeclaration, &LVType::getIsImportDeclaration},
+ {LVTypeKind::IsImportModule, &LVType::getIsImportModule},
+ {LVTypeKind::IsPointer, &LVType::getIsPointer},
+ {LVTypeKind::IsPointerMember, &LVType::getIsPointerMember},
+ {LVTypeKind::IsReference, &LVType::getIsReference},
+ {LVTypeKind::IsRestrict, &LVType::getIsRestrict},
+ {LVTypeKind::IsRvalueReference, &LVType::getIsRvalueReference},
+ {LVTypeKind::IsSubrange, &LVType::getIsSubrange},
+ {LVTypeKind::IsTemplateParam, &LVType::getIsTemplateParam},
+ {LVTypeKind::IsTemplateTemplateParam, &LVType::getIsTemplateTemplateParam},
+ {LVTypeKind::IsTemplateTypeParam, &LVType::getIsTemplateTypeParam},
+ {LVTypeKind::IsTemplateValueParam, &LVType::getIsTemplateValueParam},
+ {LVTypeKind::IsTypedef, &LVType::getIsTypedef},
+ {LVTypeKind::IsUnaligned, &LVType::getIsUnaligned},
+ {LVTypeKind::IsUnspecified, &LVType::getIsUnspecified},
+ {LVTypeKind::IsVolatile, &LVType::getIsVolatile}};
+
+void LVType::resolveReferences() {
+ // Some DWARF tags are the representation of types. However, we associate
+ // some of them to scopes. The ones associated with types, do not have
+ // any reference tags, such as DW_AT_specification, DW_AT_abstract_origin,
+ // DW_AT_extension.
+
+ // Set the file/line information using the Debug Information entry.
+ setFile(/*Reference=*/nullptr);
+
+ if (LVElement *Element = getType())
+ Element->resolve();
+}
+
+void LVType::resolveName() {
+ if (getIsResolvedName())
+ return;
+ setIsResolvedName();
+
+ // The templates are recorded as normal DWARF objects relationships;
+ // the template parameters are preserved to show the types used during
+ // the instantiation; however if a compare have been requested, those
+ // parameters needs to be resolved, so no conflicts are generated.
+ // The following DWARF illustrates this issue:
+ //
+ // a) Template Parameters are preserved:
+ // {Class} 'ConstArray<AtomTable>'
+ // {Inherits} -> 'ArrayBase'
+ // {TemplateType} 'taTYPE' -> 'AtomTable'
+ // {Member} 'mData' -> '* taTYPE'
+ //
+ // b) Template Parameters are resolved:
+ // {Class} 'ConstArray<AtomTable>'
+ // {Inherits} -> 'ArrayBase'
+ // {TemplateType} 'taTYPE' -> 'AtomTable'
+ // {Member} 'mData' -> '* AtomTable'
+ //
+ // In (b), the {Member} type have been resolved to use the real type.
+
+ LVElement *BaseType = getType();
+ if (BaseType && options().getAttributeArgument())
+ if (BaseType->isTemplateParam())
+ BaseType = BaseType->getType();
+
+ if (BaseType && !BaseType->getIsResolvedName())
+ BaseType->resolveName();
+ resolveFullname(BaseType, getName());
+
+ // In the case of unnamed types, try to generate a name for it, using
+ // the parents name and the line information. Ignore the template parameters.
+ if (!isNamed() && !getIsTemplateParam())
+ generateName();
+
+ LVElement::resolveName();
+
+ // Resolve any given pattern.
+ patterns().resolvePatternMatch(this);
+}
+
+StringRef LVType::resolveReferencesChain() {
+ // The types do not have a DW_AT_specification or DW_AT_abstract_origin
+ // reference. Just return the type name.
+ return getName();
+}
+
+void LVType::markMissingParents(const LVTypes *References,
+ const LVTypes *Targets) {
+ if (!(References && Targets))
+ return;
+
+ LLVM_DEBUG({
+ dbgs() << "\n[LVType::markMissingParents]\n";
+ for (const LVType *Reference : *References)
+ dbgs() << "References: "
+ << "Kind = " << formattedKind(Reference->kind()) << ", "
+ << "Name = " << formattedName(Reference->getName()) << "\n";
+ for (const LVType *Target : *Targets)
+ dbgs() << "Targets : "
+ << "Kind = " << formattedKind(Target->kind()) << ", "
+ << "Name = " << formattedName(Target->getName()) << "\n";
+ });
+
+ for (LVType *Reference : *References) {
+ LLVM_DEBUG({
+ dbgs() << "Search Reference: Name = "
+ << formattedName(Reference->getName()) << "\n";
+ });
+ if (!Reference->findIn(Targets))
+ Reference->markBranchAsMissing();
+ }
+}
+
+LVType *LVType::findIn(const LVTypes *Targets) const {
+ if (!Targets)
+ return nullptr;
+
+ LLVM_DEBUG({
+ dbgs() << "\n[LVType::findIn]\n"
+ << "Reference: "
+ << "Level = " << getLevel() << ", "
+ << "Kind = " << formattedKind(kind()) << ", "
+ << "Name = " << formattedName(getName()) << "\n";
+ for (const LVType *Target : *Targets)
+ dbgs() << "Target : "
+ << "Level = " << Target->getLevel() << ", "
+ << "Kind = " << formattedKind(Target->kind()) << ", "
+ << "Name = " << formattedName(Target->getName()) << "\n";
+ });
+
+ for (LVType *Target : *Targets)
+ if (equals(Target))
+ return Target;
+
+ return nullptr;
+}
+
+// Check for a match on the arguments of a function.
+bool LVType::parametersMatch(const LVTypes *References,
+ const LVTypes *Targets) {
+ if (!References && !Targets)
+ return true;
+ if (References && Targets) {
+ LVTypes ReferenceTypes;
+ LVScopes ReferenceScopes;
+ getParameters(References, &ReferenceTypes, &ReferenceScopes);
+ LVTypes TargetTypes;
+ LVScopes TargetScopes;
+ getParameters(Targets, &TargetTypes, &TargetScopes);
+ if (!LVType::equals(&ReferenceTypes, &TargetTypes) ||
+ !LVScope::equals(&ReferenceScopes, &TargetScopes))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+// Return the types which are parameters.
+void LVType::getParameters(const LVTypes *Types, LVTypes *TypesParam,
+ LVScopes *ScopesParam) {
+ if (!Types)
+ return;
+
+ // During a compare task, the template parameters are expanded to
+ // point to their real types, to avoid compare conflicts.
+ for (LVType *Type : *Types) {
+ if (!Type->getIsTemplateParam())
+ continue;
+ if (options().getAttributeArgument()) {
+ LVScope *Scope = nullptr;
+ if (Type->getIsKindType())
+ Type = Type->getTypeAsType();
+ else {
+ if (Type->getIsKindScope()) {
+ Scope = Type->getTypeAsScope();
+ Type = nullptr;
+ }
+ }
+ Type ? TypesParam->push_back(Type) : ScopesParam->push_back(Scope);
+ } else
+ TypesParam->push_back(Type);
+ }
+}
+
+bool LVType::equals(const LVType *Type) const {
+ return LVElement::equals(Type);
+}
+
+bool LVType::equals(const LVTypes *References, const LVTypes *Targets) {
+ if (!References && !Targets)
+ return true;
+ if (References && Targets && References->size() == Targets->size()) {
+ for (const LVType *Reference : *References)
+ if (!Reference->findIn(Targets))
+ return false;
+ return true;
+ }
+ return false;
+}
+
+void LVType::report(LVComparePass Pass) {
+ getComparator().printItem(this, Pass);
+}
+
+void LVType::print(raw_ostream &OS, bool Full) const {
+ if (getIncludeInPrint() &&
+ (getIsReference() || getReader().doPrintType(this))) {
+ getReaderCompileUnit()->incrementPrintedTypes();
+ LVElement::print(OS, Full);
+ printExtra(OS, Full);
+ }
+}
+
+void LVType::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF typedef.
+//===----------------------------------------------------------------------===//
+// Return the underlying type for a typedef, which can be a type or scope.
+LVElement *LVTypeDefinition::getUnderlyingType() {
+ LVElement *BaseType = getTypeAsScope();
+ if (BaseType)
+ // Underlying type is a scope.
+ return BaseType;
+
+ LVType *Type = getTypeAsType();
+ assert(Type && "Type definition does not have a type.");
+
+ BaseType = Type;
+ while (Type->getIsTypedef()) {
+ BaseType = Type->getTypeAsScope();
+ if (BaseType)
+ // Underlying type is a scope.
+ return BaseType;
+
+ Type = Type->getTypeAsType();
+ if (Type)
+ BaseType = Type;
+ }
+
+ return BaseType;
+}
+
+void LVTypeDefinition::resolveExtra() {
+ // Set the reference to the typedef type.
+ if (options().getAttributeUnderlying()) {
+ setUnderlyingType(getUnderlyingType());
+ setIsTypedefReduced();
+ if (LVElement *Type = getType()) {
+ Type->resolveName();
+ resolveFullname(Type);
+ }
+ }
+
+ // For the case of typedef'd anonymous structures:
+ // typedef struct { ... } Name;
+ // Propagate the typedef name to the anonymous structure.
+ LVScope *Aggregate = getTypeAsScope();
+ if (Aggregate && Aggregate->getIsAnonymous())
+ Aggregate->setName(getName());
+}
+
+bool LVTypeDefinition::equals(const LVType *Type) const {
+ return LVType::equals(Type);
+}
+
+void LVTypeDefinition::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> "
+ << typeOffsetAsString()
+ << formattedName((getType() ? getType()->getName() : "")) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF enumerator (DW_TAG_enumerator).
+//===----------------------------------------------------------------------===//
+bool LVTypeEnumerator::equals(const LVType *Type) const {
+ return LVType::equals(Type);
+}
+
+void LVTypeEnumerator::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " '" << getName()
+ << "' = " << formattedName(getValue()) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF import (DW_TAG_imported_module / DW_TAG_imported_declaration).
+//===----------------------------------------------------------------------===//
+bool LVTypeImport::equals(const LVType *Type) const {
+ return LVType::equals(Type);
+}
+
+void LVTypeImport::printExtra(raw_ostream &OS, bool Full) const {
+ std::string Attributes =
+ formatAttributes(virtualityString(), accessibilityString());
+
+ OS << formattedKind(kind()) << " " << typeOffsetAsString() << Attributes
+ << formattedName((getType() ? getType()->getName() : "")) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DWARF Template parameter holder (type or param).
+//===----------------------------------------------------------------------===//
+LVTypeParam::LVTypeParam() : LVType() {
+ options().getAttributeTypename() ? setIncludeInPrint()
+ : resetIncludeInPrint();
+}
+
+// Encode the specific template argument.
+void LVTypeParam::encodeTemplateArgument(std::string &Name) const {
+ // The incoming type is a template parameter; we have 3 kinds of parameters:
+ // - type parameter: resolve the instance (type);
+ // - value parameter: resolve the constant value
+ // - template parameter: resolve the name of the template.
+ // If the parameter type is a template instance (STL sample), we need to
+ // expand the type (template template case). For the following variable
+ // declarations:
+ // std::type<float> a_float;
+ // std::type<int> a_int;
+ // We must generate names like:
+ // "std::type<float,std::less<float>,std::allocator<float>,false>"
+ // "std::type<int,std::less<int>,std::allocator<int>,false>"
+ // Instead of the incomplete names:
+ // "type<float,less,allocator,false>"
+ // "type<int,less,allocator,false>"
+
+ if (getIsTemplateTypeParam()) {
+ // Get the type instance recorded in the template type; it can be a
+ // reference to a type or to a scope.
+
+ if (getIsKindType()) {
+ // The argument types always are qualified.
+ Name.append(std::string(getTypeQualifiedName()));
+
+ LVType *ArgType = getTypeAsType();
+ // For template arguments that are typedefs, use the underlying type,
+ // which can be a type or scope.
+ if (ArgType->getIsTypedef()) {
+ LVObject *BaseType = ArgType->getUnderlyingType();
+ Name.append(std::string(BaseType->getName()));
+ } else {
+ Name.append(std::string(ArgType->getName()));
+ }
+ } else {
+ if (getIsKindScope()) {
+ LVScope *ArgScope = getTypeAsScope();
+ // If the scope is a template, we have to resolve that template,
+ // by recursively traversing its arguments.
+ if (ArgScope->getIsTemplate())
+ ArgScope->encodeTemplateArguments(Name);
+ else {
+ // The argument types always are qualified.
+ Name.append(std::string(getTypeQualifiedName()));
+ Name.append(std::string(ArgScope->getName()));
+ }
+ }
+ }
+ } else
+ // Template value parameter or template template parameter.
+ Name.append(getValue());
+}
+
+bool LVTypeParam::equals(const LVType *Type) const {
+ if (!LVType::equals(Type))
+ return false;
+
+ // Checks the kind of template argument.
+ if (getIsTemplateTypeParam() && Type->getIsTemplateTypeParam())
+ return getType()->equals(Type->getType());
+
+ if ((getIsTemplateValueParam() && Type->getIsTemplateValueParam()) ||
+ (getIsTemplateTemplateParam() && Type->getIsTemplateTemplateParam()))
+ return getValueIndex() == Type->getValueIndex();
+
+ return false;
+}
+
+void LVTypeParam::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> "
+ << typeOffsetAsString();
+
+ // Depending on the type of parameter, the print includes different
+ // information: type, value or reference to a template.
+ if (getIsTemplateTypeParam()) {
+ OS << formattedNames(getTypeQualifiedName(), getTypeName()) << "\n";
+ return;
+ }
+ if (getIsTemplateValueParam()) {
+ OS << formattedName(getValue()) << " " << formattedName(getName()) << "\n";
+ return;
+ }
+ if (getIsTemplateTemplateParam())
+ OS << formattedName(getValue()) << "\n";
+}
+
+//===----------------------------------------------------------------------===//
+// DW_TAG_subrange_type
+//===----------------------------------------------------------------------===//
+void LVTypeSubrange::resolveExtra() {
+ // There are 2 cases to represent the bounds information for an array:
+ // 1) DW_TAG_subrange_type
+ // DW_AT_type --> ref_type (type of count)
+ // DW_AT_count --> value (number of elements in subrange)
+
+ // 2) DW_TAG_subrange_type
+ // DW_AT_lower_bound --> value
+ // DW_AT_upper_bound --> value
+
+ // The idea is to represent the bounds as a string, depending on the format:
+ // 1) [count]
+ // 2) [lower..upper]
+
+ // Subrange information.
+ std::string String;
+
+ // Check if we have DW_AT_count subrange style.
+ if (getIsSubrangeCount())
+ // Get count subrange value. Assume 0 if missing.
+ raw_string_ostream(String) << "[" << getCount() << "]";
+ else
+ raw_string_ostream(String)
+ << "[" << getLowerBound() << ".." << getUpperBound() << "]";
+
+ setName(String);
+}
+
+bool LVTypeSubrange::equals(const LVType *Type) const {
+ if (!LVType::equals(Type))
+ return false;
+
+ return getTypeName() == Type->getTypeName() && getName() == Type->getName();
+}
+
+void LVTypeSubrange::printExtra(raw_ostream &OS, bool Full) const {
+ OS << formattedKind(kind()) << " -> " << typeOffsetAsString()
+ << formattedName(getTypeName()) << " " << formattedName(getName()) << "\n";
+}