diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFTypePrinter.h @@ -0,0 +1,98 @@ +//===- DWARFTypePrinter.h ---------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_DWARF_DWARFTYPEPRINTER_H +#define LLVM_DEBUGINFO_DWARF_DWARFTYPEPRINTER_H + +#include "llvm/DebugInfo/DWARF/DWARFDie.h" + +namespace llvm { +class raw_ostream; + +// FIXME: We should have pretty printers per language. Currently we print +// everything as if it was C++ and fall back to the TAG type name. + +/// This class helps to dump type DIE into the out stream. +class DWARFTypePrinter { +public: + DWARFTypePrinter(raw_ostream &OS) : OS(OS) {} + + /// Dump qualified name of the specified DIE \p D. + void appendQualifiedName(DWARFDie D); + + /// Recursively dump the DIE \p D type name when applicable. + void appendUnqualifiedName(DWARFDie D, + std::string *OriginalFullName = nullptr); + +protected: + /// Dump the name encoded in the specified type tag \p T. + void appendTypeTagName(dwarf::Tag T); + + /// Dump array type for the specified DIE \p D. + void appendArrayType(const DWARFDie &D); + + /// Returns referenced DIE if \p D is a const or volatile type. + DWARFDie skipQualifiers(DWARFDie D); + + /// Returns true if brackets should be dumped for the specified DIE \p D. + bool needsBrackets(DWARFDie D); + + /// Dump type name of the DIE \p Inner and pointer-like sign(*,&,&&). + void appendPointerLikeTypeBefore(DWARFDie Inner, StringRef Ptr); + + /// Dump unqualified name of the specified DIE \p D. + DWARFDie appendUnqualifiedNameBefore(DWARFDie D, + std::string *OriginalFullName = nullptr); + + /// Dump unqualified name of the specified DIE \p D. + void appendUnqualifiedNameAfter(DWARFDie D, DWARFDie Inner, + bool SkipFirstParamIfArtificial = false); + + /// Dump scope and name of the specified DIE \p D. + DWARFDie appendQualifiedNameBefore(DWARFDie D); + + /// Dump template parameters of the specified DIE \p D. + bool appendTemplateParameters(DWARFDie D, bool *FirstParameter = nullptr); + + /// Decompose specified DIE \p N. + /// \p T will be set to the type which \p N references. + /// \p C will be set to the type which const \p N or \p T references. + /// \p V will be set to the type which volatile \p N or \p T references. + void decomposeConstVolatile(DWARFDie &N, DWARFDie &T, DWARFDie &C, + DWARFDie &V); + + /// Dump "const volatile" qualifier after name of DIE \p N. + void appendConstVolatileQualifierAfter(DWARFDie N); + + /// Dump "const volatile" qualifier before name of DIE \p N. + void appendConstVolatileQualifierBefore(DWARFDie N); + + /// Dump subroutine name after name of DIE \p D. + void appendSubroutineNameAfter(DWARFDie D, DWARFDie Inner, + bool SkipFirstParamIfArtificial, bool Const, + bool Volatile); + + /// Dump scope of DIE \p D. + void appendScopes(DWARFDie D); + + DWARFDie resolveReferencedType(DWARFDie D, + dwarf::Attribute Attr = dwarf::DW_AT_type) { + return D.getAttributeValueAsReferencedDie(Attr).resolveTypeUnitReference(); + } + + DWARFDie resolveReferencedType(DWARFDie D, DWARFFormValue F) { + return D.getAttributeValueAsReferencedDie(F).resolveTypeUnitReference(); + } + + raw_ostream &OS; + bool Word = true; + bool EndedWithTemplate = false; +}; +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_DWARF_DWARFTYPEPRINTER_H diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -23,6 +23,7 @@ DWARFGdbIndex.cpp DWARFListTable.cpp DWARFLocationExpression.cpp + DWARFTypePrinter.cpp DWARFTypeUnit.cpp DWARFUnitIndex.cpp DWARFUnit.cpp diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -17,6 +17,7 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/DataExtractor.h" @@ -106,586 +107,10 @@ .print(OS, DumpOpts, MRI, U); } -static DWARFDie resolveReferencedType(DWARFDie D, - dwarf::Attribute Attr = DW_AT_type) { - return D.getAttributeValueAsReferencedDie(Attr).resolveTypeUnitReference(); -} static DWARFDie resolveReferencedType(DWARFDie D, DWARFFormValue F) { return D.getAttributeValueAsReferencedDie(F).resolveTypeUnitReference(); } -namespace { - -// FIXME: We should have pretty printers per language. Currently we print -// everything as if it was C++ and fall back to the TAG type name. -struct DWARFTypePrinter { - raw_ostream &OS; - bool Word = true; - bool EndedWithTemplate = false; - - DWARFTypePrinter(raw_ostream &OS) : OS(OS) {} - - /// Dump the name encoded in the type tag. - void appendTypeTagName(dwarf::Tag T) { - StringRef TagStr = TagString(T); - static constexpr StringRef Prefix = "DW_TAG_"; - static constexpr StringRef Suffix = "_type"; - if (!TagStr.startswith(Prefix) || !TagStr.endswith(Suffix)) - return; - OS << TagStr.substr(Prefix.size(), - TagStr.size() - (Prefix.size() + Suffix.size())) - << " "; - } - - void appendArrayType(const DWARFDie &D) { - for (const DWARFDie &C : D.children()) { - if (C.getTag() != DW_TAG_subrange_type) - continue; - Optional LB; - Optional Count; - Optional UB; - Optional DefaultLB; - if (Optional L = C.find(DW_AT_lower_bound)) - LB = L->getAsUnsignedConstant(); - if (Optional CountV = C.find(DW_AT_count)) - Count = CountV->getAsUnsignedConstant(); - if (Optional UpperV = C.find(DW_AT_upper_bound)) - UB = UpperV->getAsUnsignedConstant(); - if (Optional LV = - D.getDwarfUnit()->getUnitDIE().find(DW_AT_language)) - if (Optional LC = LV->getAsUnsignedConstant()) - if ((DefaultLB = - LanguageLowerBound(static_cast(*LC)))) - if (LB && *LB == *DefaultLB) - LB = None; - if (!LB && !Count && !UB) - OS << "[]"; - else if (!LB && (Count || UB) && DefaultLB) - OS << '[' << (Count ? *Count : *UB - *DefaultLB + 1) << ']'; - else { - OS << "[["; - if (LB) - OS << *LB; - else - OS << '?'; - OS << ", "; - if (Count) - if (LB) - OS << *LB + *Count; - else - OS << "? + " << *Count; - else if (UB) - OS << *UB + 1; - else - OS << '?'; - OS << ")]"; - } - } - EndedWithTemplate = false; - } - - DWARFDie skipQualifiers(DWARFDie D) { - while (D && (D.getTag() == DW_TAG_const_type || - D.getTag() == DW_TAG_volatile_type)) - D = resolveReferencedType(D); - return D; - } - - bool needsParens(DWARFDie D) { - D = skipQualifiers(D); - return D && (D.getTag() == DW_TAG_subroutine_type || D.getTag() == DW_TAG_array_type); - } - - void appendPointerLikeTypeBefore(DWARFDie D, DWARFDie Inner, StringRef Ptr) { - appendQualifiedNameBefore(Inner); - if (Word) - OS << ' '; - if (needsParens(Inner)) - OS << '('; - OS << Ptr; - Word = false; - EndedWithTemplate = false; - } - - DWARFDie - appendUnqualifiedNameBefore(DWARFDie D, - std::string *OriginalFullName = nullptr) { - Word = true; - if (!D) { - OS << "void"; - return DWARFDie(); - } - DWARFDie InnerDIE; - auto Inner = [&] { return InnerDIE = resolveReferencedType(D); }; - const dwarf::Tag T = D.getTag(); - switch (T) { - case DW_TAG_pointer_type: { - appendPointerLikeTypeBefore(D, Inner(), "*"); - break; - } - case DW_TAG_subroutine_type: { - appendQualifiedNameBefore(Inner()); - if (Word) { - OS << ' '; - } - Word = false; - break; - } - case DW_TAG_array_type: { - appendQualifiedNameBefore(Inner()); - break; - } - case DW_TAG_reference_type: - appendPointerLikeTypeBefore(D, Inner(), "&"); - break; - case DW_TAG_rvalue_reference_type: - appendPointerLikeTypeBefore(D, Inner(), "&&"); - break; - case DW_TAG_ptr_to_member_type: { - appendQualifiedNameBefore(Inner()); - if (needsParens(InnerDIE)) - OS << '('; - else if (Word) - OS << ' '; - if (DWARFDie Cont = resolveReferencedType(D, DW_AT_containing_type)) { - appendQualifiedName(Cont); - OS << "::"; - } - OS << "*"; - Word = false; - break; - } - case DW_TAG_const_type: - case DW_TAG_volatile_type: - appendConstVolatileQualifierBefore(D); - break; - case DW_TAG_namespace: { - if (const char *Name = dwarf::toString(D.find(DW_AT_name), nullptr)) - OS << Name; - else - OS << "(anonymous namespace)"; - break; - } - case DW_TAG_unspecified_type: { - StringRef TypeName = D.getShortName(); - if (TypeName == "decltype(nullptr)") - TypeName = "std::nullptr_t"; - Word = true; - OS << TypeName; - EndedWithTemplate = false; - break; - } - /* - case DW_TAG_structure_type: - case DW_TAG_class_type: - case DW_TAG_enumeration_type: - case DW_TAG_base_type: - */ - default: { - const char *NamePtr = dwarf::toString(D.find(DW_AT_name), nullptr); - if (!NamePtr) { - appendTypeTagName(D.getTag()); - return DWARFDie(); - } - Word = true; - StringRef Name = NamePtr; - static constexpr StringRef MangledPrefix = "_STN"; - if (Name.startswith(MangledPrefix)) { - Name = Name.drop_front(MangledPrefix.size()); - auto Separator = Name.find('|'); - assert(Separator != StringRef::npos); - StringRef BaseName = Name.substr(0, Separator); - StringRef TemplateArgs = Name.substr(Separator + 1); - if (OriginalFullName) - *OriginalFullName = (BaseName + TemplateArgs).str(); - Name = BaseName; - } else - EndedWithTemplate = Name.endswith(">"); - OS << Name; - // This check would be insufficient for operator overloads like - // "operator>>" - but for now Clang doesn't try to simplify them, so this - // is OK. Add more nuanced operator overload handling here if/when needed. - if (Name.endswith(">")) - break; - if (!appendTemplateParameters(D)) - break; - - if (EndedWithTemplate) - OS << ' '; - OS << '>'; - EndedWithTemplate = true; - Word = true; - break; - } - } - return InnerDIE; - } - - void appendUnqualifiedNameAfter(DWARFDie D, DWARFDie Inner, - bool SkipFirstParamIfArtificial = false) { - if (!D) - return; - switch (D.getTag()) { - case DW_TAG_subroutine_type: { - appendSubroutineNameAfter(D, Inner, SkipFirstParamIfArtificial, false, - false); - break; - } - case DW_TAG_array_type: { - appendArrayType(D); - break; - } - case DW_TAG_const_type: - case DW_TAG_volatile_type: - appendConstVolatileQualifierAfter(D); - break; - case DW_TAG_ptr_to_member_type: - case DW_TAG_reference_type: - case DW_TAG_rvalue_reference_type: - case DW_TAG_pointer_type: { - if (needsParens(Inner)) - OS << ')'; - appendUnqualifiedNameAfter(Inner, resolveReferencedType(Inner), - /*SkipFirstParamIfArtificial=*/D.getTag() == - DW_TAG_ptr_to_member_type); - break; - } - /* - case DW_TAG_structure_type: - case DW_TAG_class_type: - case DW_TAG_enumeration_type: - case DW_TAG_base_type: - case DW_TAG_namespace: - */ - default: - break; - } - } - - void appendQualifiedName(DWARFDie D) { - if (D) - appendScopes(D.getParent()); - appendUnqualifiedName(D); - } - DWARFDie appendQualifiedNameBefore(DWARFDie D) { - if (D) - appendScopes(D.getParent()); - return appendUnqualifiedNameBefore(D); - } - bool appendTemplateParameters(DWARFDie D, bool *FirstParameter = nullptr) { - bool FirstParameterValue = true; - bool IsTemplate = false; - if (!FirstParameter) - FirstParameter = &FirstParameterValue; - for (const DWARFDie &C : D) { - auto Sep = [&] { - if (*FirstParameter) - OS << '<'; - else - OS << ", "; - IsTemplate = true; - EndedWithTemplate = false; - *FirstParameter = false; - }; - if (C.getTag() == dwarf::DW_TAG_GNU_template_parameter_pack) { - IsTemplate = true; - appendTemplateParameters(C, FirstParameter); - } - if (C.getTag() == dwarf::DW_TAG_template_value_parameter) { - DWARFDie T = resolveReferencedType(C); - Sep(); - if (T.getTag() == DW_TAG_enumeration_type) { - auto V = C.find(DW_AT_const_value); - bool FoundEnumerator = false; - for (const DWARFDie &Enumerator : T) { - auto EV = Enumerator.find(DW_AT_const_value); - if (V && EV && - V->getAsSignedConstant() == EV->getAsSignedConstant()) { - if (T.find(DW_AT_enum_class)) { - appendQualifiedName(T); - OS << "::"; - } else - appendScopes(T.getParent()); - OS << Enumerator.getShortName(); - FoundEnumerator = true; - break; - } - } - if (FoundEnumerator) - continue; - OS << '('; - appendQualifiedName(T); - OS << ')'; - OS << to_string(*V->getAsSignedConstant()); - continue; - } - // /Maybe/ we could do pointer type parameters, looking for the - // symbol in the ELF symbol table to get back to the variable... - // but probably not worth it. - if (T.getTag() == DW_TAG_pointer_type) - continue; - const char *RawName = dwarf::toString(T.find(DW_AT_name), nullptr); - assert(RawName); - StringRef Name = RawName; - auto V = C.find(DW_AT_const_value); - bool IsQualifiedChar = false; - if (Name == "bool") { - OS << (*V->getAsUnsignedConstant() ? "true" : "false"); - } else if (Name == "short") { - OS << "(short)"; - OS << to_string(*V->getAsSignedConstant()); - } else if (Name == "unsigned short") { - OS << "(unsigned short)"; - OS << to_string(*V->getAsSignedConstant()); - } else if (Name == "int") - OS << to_string(*V->getAsSignedConstant()); - else if (Name == "long") { - OS << to_string(*V->getAsSignedConstant()); - OS << "L"; - } else if (Name == "long long") { - OS << to_string(*V->getAsSignedConstant()); - OS << "LL"; - } else if (Name == "unsigned int") { - OS << to_string(*V->getAsUnsignedConstant()); - OS << "U"; - } else if (Name == "unsigned long") { - OS << to_string(*V->getAsUnsignedConstant()); - OS << "UL"; - } else if (Name == "unsigned long long") { - OS << to_string(*V->getAsUnsignedConstant()); - OS << "ULL"; - } else if (Name == "char" || - (IsQualifiedChar = - (Name == "unsigned char" || Name == "signed char"))) { - // FIXME: check T's DW_AT_type to see if it's signed or not (since - // char signedness is implementation defined). - auto Val = *V->getAsSignedConstant(); - // Copied/hacked up from Clang's CharacterLiteral::print - incomplete - // (doesn't actually support different character types/widths, sign - // handling's not done, and doesn't correctly test if a character is - // printable or needs to use a numeric escape sequence instead) - if (IsQualifiedChar) { - OS << '('; - OS << Name; - OS << ')'; - } - switch (Val) { - case '\\': - OS << "'\\\\'"; - break; - case '\'': - OS << "'\\''"; - break; - case '\a': - // TODO: K&R: the meaning of '\\a' is different in traditional C - OS << "'\\a'"; - break; - case '\b': - OS << "'\\b'"; - break; - case '\f': - OS << "'\\f'"; - break; - case '\n': - OS << "'\\n'"; - break; - case '\r': - OS << "'\\r'"; - break; - case '\t': - OS << "'\\t'"; - break; - case '\v': - OS << "'\\v'"; - break; - default: - if ((Val & ~0xFFu) == ~0xFFu) - Val &= 0xFFu; - if (Val < 127 && Val >= 32) { - OS << "'"; - OS << (char)Val; - OS << "'"; - } else if (Val < 256) - OS << to_string(llvm::format("'\\x%02x'", Val)); - else if (Val <= 0xFFFF) - OS << to_string(llvm::format("'\\u%04x'", Val)); - else - OS << to_string(llvm::format("'\\U%08x'", Val)); - } - } - continue; - } - if (C.getTag() == dwarf::DW_TAG_GNU_template_template_param) { - const char *RawName = - dwarf::toString(C.find(DW_AT_GNU_template_name), nullptr); - assert(RawName); - StringRef Name = RawName; - Sep(); - OS << Name; - continue; - } - if (C.getTag() != dwarf::DW_TAG_template_type_parameter) - continue; - auto TypeAttr = C.find(DW_AT_type); - Sep(); - appendQualifiedName(TypeAttr ? resolveReferencedType(C, *TypeAttr) - : DWARFDie()); - } - if (IsTemplate && *FirstParameter && FirstParameter == &FirstParameterValue) - OS << '<'; - return IsTemplate; - } - void decomposeConstVolatile(DWARFDie &N, DWARFDie &T, DWARFDie &C, - DWARFDie &V) { - (N.getTag() == DW_TAG_const_type ? C : V) = N; - T = resolveReferencedType(N); - if (T) { - auto Tag = T.getTag(); - if (Tag == DW_TAG_const_type) { - C = T; - T = resolveReferencedType(T); - } else if (Tag == DW_TAG_volatile_type) { - V = T; - T = resolveReferencedType(T); - } - } - } - void appendConstVolatileQualifierAfter(DWARFDie N) { - DWARFDie C; - DWARFDie V; - DWARFDie T; - decomposeConstVolatile(N, T, C, V); - if (T && T.getTag() == DW_TAG_subroutine_type) - appendSubroutineNameAfter(T, resolveReferencedType(T), false, C.isValid(), - V.isValid()); - else - appendUnqualifiedNameAfter(T, resolveReferencedType(T)); - } - void appendConstVolatileQualifierBefore(DWARFDie N) { - DWARFDie C; - DWARFDie V; - DWARFDie T; - decomposeConstVolatile(N, T, C, V); - bool Subroutine = T && T.getTag() == DW_TAG_subroutine_type; - DWARFDie A = T; - while (A && A.getTag() == DW_TAG_array_type) - A = resolveReferencedType(A); - bool Leading = - (!A || (A.getTag() != DW_TAG_pointer_type && - A.getTag() != llvm::dwarf::DW_TAG_ptr_to_member_type)) && - !Subroutine; - if (Leading) { - if (C) - OS << "const "; - if (V) - OS << "volatile "; - } - appendQualifiedNameBefore(T); - if (!Leading && !Subroutine) { - Word = true; - if (C) - OS << "const"; - if (V) { - if (C) - OS << ' '; - OS << "volatile"; - } - } - } - - /// Recursively append the DIE type name when applicable. - void appendUnqualifiedName(DWARFDie D, - std::string *OriginalFullName = nullptr) { - // FIXME: We should have pretty printers per language. Currently we print - // everything as if it was C++ and fall back to the TAG type name. - DWARFDie Inner = appendUnqualifiedNameBefore(D, OriginalFullName); - appendUnqualifiedNameAfter(D, Inner); - } - - void appendSubroutineNameAfter(DWARFDie D, DWARFDie Inner, - bool SkipFirstParamIfArtificial, bool Const, - bool Volatile) { - DWARFDie FirstParamIfArtificial; - OS << '('; - EndedWithTemplate = false; - bool First = true; - bool RealFirst = true; - for (DWARFDie P : D) { - if (P.getTag() != DW_TAG_formal_parameter && - P.getTag() != DW_TAG_unspecified_parameters) - return; - DWARFDie T = resolveReferencedType(P); - if (SkipFirstParamIfArtificial && RealFirst && P.find(DW_AT_artificial)) { - FirstParamIfArtificial = T; - RealFirst = false; - continue; - } - if (!First) { - OS << ", "; - } - First = false; - if (P.getTag() == DW_TAG_unspecified_parameters) - OS << "..."; - else - appendQualifiedName(T); - } - EndedWithTemplate = false; - OS << ')'; - if (FirstParamIfArtificial) { - if (DWARFDie P = FirstParamIfArtificial) { - if (P.getTag() == DW_TAG_pointer_type) { - DWARFDie C; - DWARFDie V; - auto CVStep = [&](DWARFDie CV) { - if (DWARFDie U = resolveReferencedType(CV)) { - if (U.getTag() == DW_TAG_const_type) - return C = U; - if (U.getTag() == DW_TAG_volatile_type) - return V = U; - } - return DWARFDie(); - }; - if (DWARFDie CV = CVStep(P)) { - CVStep(CV); - } - if (C) - OS << " const"; - if (V) - OS << " volatile"; - } - } - } else { - if (Const) - OS << " const"; - if (Volatile) - OS << " volatile"; - } - if (D.find(DW_AT_reference)) - OS << " &"; - if (D.find(DW_AT_rvalue_reference)) - OS << " &&"; - appendUnqualifiedNameAfter(Inner, resolveReferencedType(Inner)); - } - void appendScopes(DWARFDie D) { - if (D.getTag() == DW_TAG_compile_unit) - return; - if (D.getTag() == DW_TAG_type_unit) - return; - if (D.getTag() == DW_TAG_skeleton_unit) - return; - if (D.getTag() == DW_TAG_subprogram) - return; - if (D.getTag() == DW_TAG_lexical_block) - return; - D = D.resolveTypeUnitReference(); - if (DWARFDie P = D.getParent()) - appendScopes(P); - appendUnqualifiedName(D); - OS << "::"; - } -}; -} // anonymous namespace - static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, const DWARFAttribute &AttrValue, unsigned Indent, DIDumpOptions DumpOpts) { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFTypePrinter.cpp @@ -0,0 +1,580 @@ +//===- DWARFTypePrinter.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +using namespace llvm; +using namespace dwarf; + +void DWARFTypePrinter::appendTypeTagName(dwarf::Tag T) { + StringRef TagStr = TagString(T); + static constexpr StringRef Prefix = "DW_TAG_"; + static constexpr StringRef Suffix = "_type"; + if (!TagStr.startswith(Prefix) || !TagStr.endswith(Suffix)) + return; + OS << TagStr.substr(Prefix.size(), + TagStr.size() - (Prefix.size() + Suffix.size())) + << " "; +} + +void DWARFTypePrinter::appendArrayType(const DWARFDie &D) { + for (const DWARFDie &C : D.children()) { + if (C.getTag() != DW_TAG_subrange_type) + continue; + Optional LB; + Optional Count; + Optional UB; + Optional DefaultLB; + if (Optional L = C.find(DW_AT_lower_bound)) + LB = L->getAsUnsignedConstant(); + if (Optional CountV = C.find(DW_AT_count)) + Count = CountV->getAsUnsignedConstant(); + if (Optional UpperV = C.find(DW_AT_upper_bound)) + UB = UpperV->getAsUnsignedConstant(); + if (Optional LV = + D.getDwarfUnit()->getUnitDIE().find(DW_AT_language)) + if (Optional LC = LV->getAsUnsignedConstant()) + if ((DefaultLB = + LanguageLowerBound(static_cast(*LC)))) + if (LB && *LB == *DefaultLB) + LB = None; + if (!LB && !Count && !UB) + OS << "[]"; + else if (!LB && (Count || UB) && DefaultLB) + OS << '[' << (Count ? *Count : *UB - *DefaultLB + 1) << ']'; + else { + OS << "[["; + if (LB) + OS << *LB; + else + OS << '?'; + OS << ", "; + if (Count) + if (LB) + OS << *LB + *Count; + else + OS << "? + " << *Count; + else if (UB) + OS << *UB + 1; + else + OS << '?'; + OS << ")]"; + } + } + EndedWithTemplate = false; +} + +DWARFDie DWARFTypePrinter::skipQualifiers(DWARFDie D) { + while (D && (D.getTag() == DW_TAG_const_type || + D.getTag() == DW_TAG_volatile_type)) + D = resolveReferencedType(D); + return D; +} + +bool DWARFTypePrinter::needsBrackets(DWARFDie D) { + D = skipQualifiers(D); + return D && (D.getTag() == DW_TAG_subroutine_type || + D.getTag() == DW_TAG_array_type); +} + +void DWARFTypePrinter::appendPointerLikeTypeBefore(DWARFDie Inner, + StringRef Ptr) { + appendQualifiedNameBefore(Inner); + if (Word) + OS << ' '; + if (needsBrackets(Inner)) + OS << '('; + OS << Ptr; + Word = false; + EndedWithTemplate = false; +} + +DWARFDie +DWARFTypePrinter::appendUnqualifiedNameBefore(DWARFDie D, + std::string *OriginalFullName) { + Word = true; + if (!D) { + OS << "void"; + return DWARFDie(); + } + DWARFDie InnerDIE; + auto Inner = [&] { return InnerDIE = resolveReferencedType(D); }; + const dwarf::Tag T = D.getTag(); + switch (T) { + case DW_TAG_pointer_type: { + appendPointerLikeTypeBefore(Inner(), "*"); + break; + } + case DW_TAG_subroutine_type: { + appendQualifiedNameBefore(Inner()); + if (Word) { + OS << ' '; + } + Word = false; + break; + } + case DW_TAG_array_type: { + appendQualifiedNameBefore(Inner()); + break; + } + case DW_TAG_reference_type: + appendPointerLikeTypeBefore(Inner(), "&"); + break; + case DW_TAG_rvalue_reference_type: + appendPointerLikeTypeBefore(Inner(), "&&"); + break; + case DW_TAG_ptr_to_member_type: { + appendQualifiedNameBefore(Inner()); + if (needsBrackets(InnerDIE)) + OS << '('; + else if (Word) + OS << ' '; + if (DWARFDie Cont = resolveReferencedType(D, DW_AT_containing_type)) { + appendQualifiedName(Cont); + OS << "::"; + } + OS << "*"; + Word = false; + break; + } + case DW_TAG_const_type: + case DW_TAG_volatile_type: + appendConstVolatileQualifierBefore(D); + break; + case DW_TAG_namespace: { + if (const char *Name = dwarf::toString(D.find(DW_AT_name), nullptr)) + OS << Name; + else + OS << "(anonymous namespace)"; + break; + } + case DW_TAG_unspecified_type: { + StringRef TypeName = D.getShortName(); + if (TypeName == "decltype(nullptr)") + TypeName = "std::nullptr_t"; + Word = true; + OS << TypeName; + EndedWithTemplate = false; + break; + } + /* + case DW_TAG_structure_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + case DW_TAG_base_type: + */ + default: { + const char *NamePtr = dwarf::toString(D.find(DW_AT_name), nullptr); + if (!NamePtr) { + appendTypeTagName(D.getTag()); + return DWARFDie(); + } + Word = true; + StringRef Name = NamePtr; + static constexpr StringRef MangledPrefix = "_STN"; + if (Name.startswith(MangledPrefix)) { + Name = Name.drop_front(MangledPrefix.size()); + auto Separator = Name.find('|'); + assert(Separator != StringRef::npos); + StringRef BaseName = Name.substr(0, Separator); + StringRef TemplateArgs = Name.substr(Separator + 1); + if (OriginalFullName) + *OriginalFullName = (BaseName + TemplateArgs).str(); + Name = BaseName; + } else + EndedWithTemplate = Name.endswith(">"); + OS << Name; + // This check would be insufficient for operator overloads like + // "operator>>" - but for now Clang doesn't try to simplify them, so this + // is OK. Add more nuanced operator overload handling here if/when needed. + if (Name.endswith(">")) + break; + if (!appendTemplateParameters(D)) + break; + + if (EndedWithTemplate) + OS << ' '; + OS << '>'; + EndedWithTemplate = true; + Word = true; + break; + } + } + return InnerDIE; +} + +void DWARFTypePrinter::appendUnqualifiedNameAfter( + DWARFDie D, DWARFDie Inner, bool SkipFirstParamIfArtificial) { + if (!D) + return; + switch (D.getTag()) { + case DW_TAG_subroutine_type: { + appendSubroutineNameAfter(D, Inner, SkipFirstParamIfArtificial, false, + false); + break; + } + case DW_TAG_array_type: { + appendArrayType(D); + break; + } + case DW_TAG_const_type: + case DW_TAG_volatile_type: + appendConstVolatileQualifierAfter(D); + break; + case DW_TAG_ptr_to_member_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_pointer_type: { + if (needsBrackets(Inner)) + OS << ')'; + appendUnqualifiedNameAfter(Inner, resolveReferencedType(Inner), + /*SkipFirstParamIfArtificial=*/D.getTag() == + DW_TAG_ptr_to_member_type); + break; + } + /* + case DW_TAG_structure_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + case DW_TAG_base_type: + case DW_TAG_namespace: + */ + default: + break; + } +} + +void DWARFTypePrinter::appendQualifiedName(DWARFDie D) { + if (D) + appendScopes(D.getParent()); + appendUnqualifiedName(D); +} + +DWARFDie DWARFTypePrinter::appendQualifiedNameBefore(DWARFDie D) { + if (D) + appendScopes(D.getParent()); + return appendUnqualifiedNameBefore(D); +} + +bool DWARFTypePrinter::appendTemplateParameters(DWARFDie D, + bool *FirstParameter) { + bool FirstParameterValue = true; + bool IsTemplate = false; + if (!FirstParameter) + FirstParameter = &FirstParameterValue; + for (const DWARFDie &C : D) { + auto Sep = [&] { + if (*FirstParameter) + OS << '<'; + else + OS << ", "; + IsTemplate = true; + EndedWithTemplate = false; + *FirstParameter = false; + }; + if (C.getTag() == dwarf::DW_TAG_GNU_template_parameter_pack) { + IsTemplate = true; + appendTemplateParameters(C, FirstParameter); + } + if (C.getTag() == dwarf::DW_TAG_template_value_parameter) { + DWARFDie T = resolveReferencedType(C); + Sep(); + if (T.getTag() == DW_TAG_enumeration_type) { + auto V = C.find(DW_AT_const_value); + bool FoundEnumerator = false; + for (const DWARFDie &Enumerator : T) { + auto EV = Enumerator.find(DW_AT_const_value); + if (V && EV && + V->getAsSignedConstant() == EV->getAsSignedConstant()) { + if (T.find(DW_AT_enum_class)) { + appendQualifiedName(T); + OS << "::"; + } else + appendScopes(T.getParent()); + OS << Enumerator.getShortName(); + FoundEnumerator = true; + break; + } + } + if (FoundEnumerator) + continue; + OS << '('; + appendQualifiedName(T); + OS << ')'; + OS << to_string(*V->getAsSignedConstant()); + continue; + } + // /Maybe/ we could do pointer type parameters, looking for the + // symbol in the ELF symbol table to get back to the variable... + // but probably not worth it. + if (T.getTag() == DW_TAG_pointer_type) + continue; + const char *RawName = dwarf::toString(T.find(DW_AT_name), nullptr); + assert(RawName); + StringRef Name = RawName; + auto V = C.find(DW_AT_const_value); + bool IsQualifiedChar = false; + if (Name == "bool") { + OS << (*V->getAsUnsignedConstant() ? "true" : "false"); + } else if (Name == "short") { + OS << "(short)"; + OS << to_string(*V->getAsSignedConstant()); + } else if (Name == "unsigned short") { + OS << "(unsigned short)"; + OS << to_string(*V->getAsSignedConstant()); + } else if (Name == "int") + OS << to_string(*V->getAsSignedConstant()); + else if (Name == "long") { + OS << to_string(*V->getAsSignedConstant()); + OS << "L"; + } else if (Name == "long long") { + OS << to_string(*V->getAsSignedConstant()); + OS << "LL"; + } else if (Name == "unsigned int") { + OS << to_string(*V->getAsUnsignedConstant()); + OS << "U"; + } else if (Name == "unsigned long") { + OS << to_string(*V->getAsUnsignedConstant()); + OS << "UL"; + } else if (Name == "unsigned long long") { + OS << to_string(*V->getAsUnsignedConstant()); + OS << "ULL"; + } else if (Name == "char" || + (IsQualifiedChar = + (Name == "unsigned char" || Name == "signed char"))) { + // FIXME: check T's DW_AT_type to see if it's signed or not (since + // char signedness is implementation defined). + auto Val = *V->getAsSignedConstant(); + // Copied/hacked up from Clang's CharacterLiteral::print - incomplete + // (doesn't actually support different character types/widths, sign + // handling's not done, and doesn't correctly test if a character is + // printable or needs to use a numeric escape sequence instead) + if (IsQualifiedChar) { + OS << '('; + OS << Name; + OS << ')'; + } + switch (Val) { + case '\\': + OS << "'\\\\'"; + break; + case '\'': + OS << "'\\''"; + break; + case '\a': + // TODO: K&R: the meaning of '\\a' is different in traditional C + OS << "'\\a'"; + break; + case '\b': + OS << "'\\b'"; + break; + case '\f': + OS << "'\\f'"; + break; + case '\n': + OS << "'\\n'"; + break; + case '\r': + OS << "'\\r'"; + break; + case '\t': + OS << "'\\t'"; + break; + case '\v': + OS << "'\\v'"; + break; + default: + if ((Val & ~0xFFu) == ~0xFFu) + Val &= 0xFFu; + if (Val < 127 && Val >= 32) { + OS << "'"; + OS << (char)Val; + OS << "'"; + } else if (Val < 256) + OS << to_string(llvm::format("'\\x%02x'", Val)); + else if (Val <= 0xFFFF) + OS << to_string(llvm::format("'\\u%04x'", Val)); + else + OS << to_string(llvm::format("'\\U%08x'", Val)); + } + } + continue; + } + if (C.getTag() == dwarf::DW_TAG_GNU_template_template_param) { + const char *RawName = + dwarf::toString(C.find(DW_AT_GNU_template_name), nullptr); + assert(RawName); + StringRef Name = RawName; + Sep(); + OS << Name; + continue; + } + if (C.getTag() != dwarf::DW_TAG_template_type_parameter) + continue; + auto TypeAttr = C.find(DW_AT_type); + Sep(); + appendQualifiedName(TypeAttr ? resolveReferencedType(C, *TypeAttr) + : DWARFDie()); + } + if (IsTemplate && *FirstParameter && FirstParameter == &FirstParameterValue) + OS << '<'; + return IsTemplate; +} + +void DWARFTypePrinter::decomposeConstVolatile(DWARFDie &N, DWARFDie &T, + DWARFDie &C, DWARFDie &V) { + (N.getTag() == DW_TAG_const_type ? C : V) = N; + T = resolveReferencedType(N); + if (T) { + auto Tag = T.getTag(); + if (Tag == DW_TAG_const_type) { + C = T; + T = resolveReferencedType(T); + } else if (Tag == DW_TAG_volatile_type) { + V = T; + T = resolveReferencedType(T); + } + } +} + +void DWARFTypePrinter::appendConstVolatileQualifierAfter(DWARFDie N) { + DWARFDie C; + DWARFDie V; + DWARFDie T; + decomposeConstVolatile(N, T, C, V); + if (T && T.getTag() == DW_TAG_subroutine_type) + appendSubroutineNameAfter(T, resolveReferencedType(T), false, C.isValid(), + V.isValid()); + else + appendUnqualifiedNameAfter(T, resolveReferencedType(T)); +} + +void DWARFTypePrinter::appendConstVolatileQualifierBefore(DWARFDie N) { + DWARFDie C; + DWARFDie V; + DWARFDie T; + decomposeConstVolatile(N, T, C, V); + bool Subroutine = T && T.getTag() == DW_TAG_subroutine_type; + DWARFDie A = T; + while (A && A.getTag() == DW_TAG_array_type) + A = resolveReferencedType(A); + bool Leading = + (!A || (A.getTag() != DW_TAG_pointer_type && + A.getTag() != llvm::dwarf::DW_TAG_ptr_to_member_type)) && + !Subroutine; + if (Leading) { + if (C) + OS << "const "; + if (V) + OS << "volatile "; + } + appendQualifiedNameBefore(T); + if (!Leading && !Subroutine) { + Word = true; + if (C) + OS << "const"; + if (V) { + if (C) + OS << ' '; + OS << "volatile"; + } + } +} + +void DWARFTypePrinter::appendUnqualifiedName(DWARFDie D, + std::string *OriginalFullName) { + // FIXME: We should have pretty printers per language. Currently we print + // everything as if it was C++ and fall back to the TAG type name. + DWARFDie Inner = appendUnqualifiedNameBefore(D, OriginalFullName); + appendUnqualifiedNameAfter(D, Inner); +} + +void DWARFTypePrinter::appendSubroutineNameAfter( + DWARFDie D, DWARFDie Inner, bool SkipFirstParamIfArtificial, bool Const, + bool Volatile) { + DWARFDie FirstParamIfArtificial; + OS << '('; + EndedWithTemplate = false; + bool First = true; + bool RealFirst = true; + for (DWARFDie P : D) { + if (P.getTag() != DW_TAG_formal_parameter && + P.getTag() != DW_TAG_unspecified_parameters) + return; + DWARFDie T = resolveReferencedType(P); + if (SkipFirstParamIfArtificial && RealFirst && P.find(DW_AT_artificial)) { + FirstParamIfArtificial = T; + RealFirst = false; + continue; + } + if (!First) { + OS << ", "; + } + First = false; + if (P.getTag() == DW_TAG_unspecified_parameters) + OS << "..."; + else + appendQualifiedName(T); + } + EndedWithTemplate = false; + OS << ')'; + if (FirstParamIfArtificial) { + if (DWARFDie P = FirstParamIfArtificial) { + if (P.getTag() == DW_TAG_pointer_type) { + DWARFDie C; + DWARFDie V; + auto CVStep = [&](DWARFDie CV) { + if (DWARFDie U = resolveReferencedType(CV)) { + if (U.getTag() == DW_TAG_const_type) + return C = U; + if (U.getTag() == DW_TAG_volatile_type) + return V = U; + } + return DWARFDie(); + }; + if (DWARFDie CV = CVStep(P)) { + CVStep(CV); + } + if (C) + OS << " const"; + if (V) + OS << " volatile"; + } + } + } else { + if (Const) + OS << " const"; + if (Volatile) + OS << " volatile"; + } + if (D.find(DW_AT_reference)) + OS << " &"; + if (D.find(DW_AT_rvalue_reference)) + OS << " &&"; + appendUnqualifiedNameAfter(Inner, resolveReferencedType(Inner)); +} + +void DWARFTypePrinter::appendScopes(DWARFDie D) { + if (D.getTag() == DW_TAG_compile_unit) + return; + if (D.getTag() == DW_TAG_type_unit) + return; + if (D.getTag() == DW_TAG_skeleton_unit) + return; + if (D.getTag() == DW_TAG_subprogram) + return; + if (D.getTag() == DW_TAG_lexical_block) + return; + D = D.resolveTypeUnitReference(); + if (DWARFDie P = D.getParent()) + appendScopes(P); + appendUnqualifiedName(D); + OS << "::"; +}