Author: David Blaikie Date: 2021-09-23T19:58:32-07:00 New Revision: 25ac0d3c73d68c017546eb622ba7632c6b581bfb
URL: https://github.com/llvm/llvm-project/commit/25ac0d3c73d68c017546eb622ba7632c6b581bfb DIFF: https://github.com/llvm/llvm-project/commit/25ac0d3c73d68c017546eb622ba7632c6b581bfb.diff LOG: DebugInfo: Implement the -gsimple-template-names functionality This excludes certain names that can't be rebuilt from the available DWARF: * Atomic types - no DWARF differentiating int from atomic int. * Vector types - enough DWARF (an attribute on the array type) to do this, but I haven't written the extra code to add the attributes required for this * Lambdas - ambiguous with any other unnamed class * Unnamed classes/enums - would need column info for the type in addition to file/line number * noexcept function types - not encoded in DWARF Added: clang/test/CodeGenCXX/debug-info-simple-template-names.cpp Modified: clang/lib/CodeGen/CGDebugInfo.cpp Removed: ################################################################################ diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 5889647e2de96..7119402dcb64f 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -25,6 +25,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" @@ -4855,11 +4856,173 @@ llvm::DIGlobalVariableExpression *CGDebugInfo::CollectAnonRecordDecls( return GVE; } +namespace { +struct ReconstitutableType : public RecursiveASTVisitor<ReconstitutableType> { + bool Reconstitutable = true; + bool VisitVectorType(VectorType *FT) { + Reconstitutable = false; + return false; + } + bool VisitAtomicType(AtomicType *FT) { + Reconstitutable = false; + return false; + } + bool TraverseEnumType(EnumType *ET) { + // Unnamed enums can't be reconstituted due to a lack of column info we + // produce in the DWARF, so we can't get Clang's full name back. + if (const auto *ED = dyn_cast<EnumDecl>(ET->getDecl())) { + if (!ED->getIdentifier()) { + Reconstitutable = false; + return false; + } + } + return true; + } + bool VisitFunctionProtoType(FunctionProtoType *FT) { + // noexcept is not encoded in DWARF, so the reversi + Reconstitutable &= !isNoexceptExceptionSpec(FT->getExceptionSpecType()); + return !Reconstitutable; + } + bool TraverseRecordType(RecordType *RT) { + // Unnamed classes/lambdas can't be reconstituted due to a lack of column + // info we produce in the DWARF, so we can't get Clang's full name back. + // But so long as it's not one of those, it doesn't matter if some sub-type + // of the record (a template parameter) can't be reconstituted - because the + // un-reconstitutable type itself will carry its own name. + const auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl()); + if (!RD) + return true; + if (RD->isLambda() || !RD->getIdentifier()) { + Reconstitutable = false; + return false; + } + return true; + } +}; +} // anonymous namespace + +// Test whether a type name could be rebuilt from emitted debug info. +static bool IsReconstitutableType(QualType QT) { + ReconstitutableType T; + T.TraverseType(QT); + return T.Reconstitutable; +} + std::string CGDebugInfo::GetName(const Decl *D, bool Qualified) const { std::string Name; llvm::raw_string_ostream OS(Name); - if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) - ND->getNameForDiagnostic(OS, getPrintingPolicy(), Qualified); + const NamedDecl *ND = dyn_cast<NamedDecl>(D); + if (!ND) + return Name; + codegenoptions::DebugTemplateNamesKind TemplateNamesKind = + CGM.getCodeGenOpts().getDebugSimpleTemplateNames(); + Optional<TemplateArgs> Args; + + bool IsOperatorOverload = false; // isa<CXXConversionDecl>(ND); + if (auto *RD = dyn_cast<CXXRecordDecl>(ND)) { + Args = GetTemplateArgs(RD); + } else if (auto *FD = dyn_cast<FunctionDecl>(ND)) { + Args = GetTemplateArgs(FD); + auto NameKind = ND->getDeclName().getNameKind(); + IsOperatorOverload |= + NameKind == DeclarationName::CXXOperatorName || + NameKind == DeclarationName::CXXConversionFunctionName; + } else if (auto *VD = dyn_cast<VarDecl>(ND)) { + Args = GetTemplateArgs(VD); + } + std::function<bool(ArrayRef<TemplateArgument>)> HasReconstitutableArgs = + [&](ArrayRef<TemplateArgument> Args) { + return llvm::all_of(Args, [&](const TemplateArgument &TA) { + switch (TA.getKind()) { + case TemplateArgument::Template: + // Easy to reconstitute - the value of the parameter in the debug + // info is the string name of the template. (so the template name + // itself won't benefit from any name rebuilding, but that's a + // representational limitation - maybe DWARF could be + // changed/improved to use some more structural representation) + return true; + case TemplateArgument::Declaration: + // Reference and pointer non-type template parameters point to + // variables, functions, etc and their value is, at best (for + // variables) represented as an address - not a reference to the + // DWARF describing the variable/function/etc. This makes it hard, + // possibly impossible to rebuild the original name - looking up the + // address in the executable file's symbol table would be needed. + return false; + case TemplateArgument::NullPtr: + // These could be rebuilt, but figured they're close enough to the + // declaration case, and not worth rebuilding. + return false; + case TemplateArgument::Pack: + // A pack is invalid if any of the elements of the pack are invalid. + return HasReconstitutableArgs(TA.getPackAsArray()); + case TemplateArgument::Integral: + // Larger integers get encoded as DWARF blocks which are a bit + // harder to parse back into a large integer, etc - so punting on + // this for now. Re-parsing the integers back into APInt is probably + // feasible some day. + return TA.getAsIntegral().getBitWidth() <= 64; + case TemplateArgument::Type: + return IsReconstitutableType(TA.getAsType()); + default: + llvm_unreachable("Other, unresolved, template arguments should " + "not be seen here"); + } + }); + }; + // A conversion operator presents complications/ambiguity if there's a + // conversion to class template that is itself a template, eg: + // template<typename T> + // operator ns::t1<T, int>(); + // This should be named, eg: "operator ns::t1<float, int><float>" + // (ignoring clang bug that means this is currently "operator t1<float>") + // but if the arguments were stripped, the consumer couldn't diff erentiate + // whether the template argument list for the conversion type was the + // function's argument list (& no reconstitution was needed) or not. + // This could be handled if reconstitutable names had a separate attribute + // annotating them as such - this would remove the ambiguity. + // + // Alternatively the template argument list could be parsed enough to check + // whether there's one list or two, then compare that with the DWARF + // description of the return type and the template argument lists to determine + // how many lists there should be and if one is missing it could be assumed(?) + // to be the function's template argument list & then be rebuilt. + // + // Other operator overloads that aren't conversion operators could be + // reconstituted but would require a bit more nuance about detecting the + // diff erence between these diff erent operators during that rebuilding. + bool Reconstitutable = + Args && HasReconstitutableArgs(Args->Args) && !IsOperatorOverload; + + PrintingPolicy PP = getPrintingPolicy(); + + if (TemplateNamesKind == codegenoptions::DebugTemplateNamesKind::Full || + !Reconstitutable) { + ND->getNameForDiagnostic(OS, PP, Qualified); + } else { + bool Mangled = + TemplateNamesKind == codegenoptions::DebugTemplateNamesKind::Mangled; + // check if it's a template + if (Mangled) + OS << "_STN"; + + OS << ND->getDeclName(); + std::string EncodedOriginalName; + llvm::raw_string_ostream EncodedOriginalNameOS(EncodedOriginalName); + EncodedOriginalNameOS << ND->getDeclName(); + + if (Mangled) { + OS << "|"; + printTemplateArgumentList(OS, Args->Args, PP); + printTemplateArgumentList(EncodedOriginalNameOS, Args->Args, PP); +#ifndef NDEBUG + std::string CanonicalOriginalName; + llvm::raw_string_ostream OriginalOS(CanonicalOriginalName); + ND->getNameForDiagnostic(OriginalOS, PP, Qualified); + assert(EncodedOriginalNameOS.str() == OriginalOS.str()); +#endif + } + } return Name; } diff --git a/clang/test/CodeGenCXX/debug-info-simple-template-names.cpp b/clang/test/CodeGenCXX/debug-info-simple-template-names.cpp new file mode 100644 index 0000000000000..a73c2982791e5 --- /dev/null +++ b/clang/test/CodeGenCXX/debug-info-simple-template-names.cpp @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -emit-llvm -triple x86_64-unknown_unknown -debug-info-kind=limited -gsimple-template-names=mangled %s -o - -w -std=c++17 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-unknown_unknown -debug-info-kind=limited -gsimple-template-names=simple %s -o - -w -std=c++17 | FileCheck --check-prefix=SIMPLE --implicit-check-not=_STN %s +// RUN: %clang_cc1 -emit-llvm -triple x86_64-unknown_unknown -debug-info-kind=limited %s -o - -w -std=c++17 | FileCheck --check-prefix=FULL --implicit-check-not=_STN %s + +template <typename... T> +void f1() {} +template <typename T, T V> +void f2() {} +template <typename... T> +struct t1 {}; +extern int x; +int x; +struct t2 { + template <typename T = float> + operator t1<int>() { __builtin_unreachable(); } +}; +template <template <typename...> class T> +void f3() {} +void f() { + // Basic examples of simplifiable/rebuildable names + f1<>(); + // CHECK: !DISubprogram(name: "_STNf1|<>", + // SIMPLE: !DISubprogram(name: "f1", + // FULL: !DISubprogram(name: "f1<>", + f1<int>(); + // CHECK: !DISubprogram(name: "_STNf1|<int>", + f1<void()>(); + // CHECK: !DISubprogram(name: "_STNf1|<void ()>", + f2<int, 42>(); + // CHECK: !DISubprogram(name: "_STNf2|<int, 42>", + + // Check that even though the nested name can't be rebuilt, it'll carry its + // full name and the outer name can be rebuilt from that. + f1<t1<void() noexcept>>(); + // CHECK: !DISubprogram(name: "_STNf1|<t1<void () noexcept> >", + + // Vector array types are encoded in DWARF but the decoding in llvm-dwarfdump + // isn't implemented yet. + f1<__attribute__((__vector_size__((sizeof(int) * 2)))) int>(); + // CHECK: !DISubprogram(name: "f1<__attribute__((__vector_size__(2 * sizeof(int)))) int>", + + // noexcept is part of function types in C++17 onwards, but not encoded in + // DWARF + f1<void() noexcept>(); + // CHECK: !DISubprogram(name: "f1<void () noexcept>", + + // Unnamed entities (lambdas, structs/classes, enums) can't be fully rebuilt + // since we don't emit the column number. Also lambdas and unnamed classes are + // ambiguous with each other - there's no DWARF that designates a lambda as + // anything other than another unnamed class/struct. + auto A = [] {}; + f1<decltype(A)>(); + // CHECK: !DISubprogram(name: "f1<(lambda at {{.*}}debug-info-simple-template-names.cpp:[[# @LINE - 2]]:12)>", + struct { + } unnamed_struct; + f1<decltype(unnamed_struct)>(); + // CHECK: !DISubprogram(name: "f1<(unnamed struct at {{.*}}CodeGenCXX/debug-info-simple-template-names.cpp:[[# @LINE - 3]]:3)>", + enum {} unnamed_enum; + f1<decltype(unnamed_enum)>(); + // CHECK: !DISubprogram(name: "f1<(unnamed enum at {{.*}}debug-info-simple-template-names.cpp:[[# @LINE - 2]]:3)>", + + // Declarations can't readily be reversed as the value in the DWARF only + // contains the address of the value - we'd have to do symbol lookup to find + // the name of that value (& rely on it not having been stripped out, etc). + f2<int *, &x>(); + // CHECK: !DISubprogram(name: "f2<int *, &x>", + + // We could probably handle \/ this case, but since it's a small subset of + // pointer typed non-type-template parameters which can't be handled it + // doesn't seem high priority. + f2<decltype(nullptr), nullptr>(); + // CHECK: !DISubprogram(name: "f2<std::nullptr_t, nullptr>", + + // These larger constants are encoded as data blocks which makes them a bit + // harder to re-render. I think they might be missing sign information, or at + // maybe it's just a question of doing APInt things to render such large + // values. Punting on this for now. + f2<__int128, ((__int128)9223372036854775807) * 2>(); + // CHECK: !DISubprogram(name: "f2<__int128, (__int128)18446744073709551614>", + + t2().operator t1<int>(); + // FIXME: This should be something like "operator t1<int><float>" + // CHECK: !DISubprogram(name: "operator t1<float>", + + // Function pointer non-type-template parameters currently don't get any DWARF + // value (GCC doesn't provide one either) and even if there was a value, if + // it's like variable/pointer non-type template parameters, it couldn't be + // rebuilt anyway (see the note above for details on that) so we don't have to + // worry about seeing conversion operators as parameters to other templates. + + f3<t1>(); + // CHECK: !DISubprogram(name: "_STNf3|<t1>", +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits