diff --git a/clang/include/clang/Basic/DebugOptions.def b/clang/include/clang/Basic/DebugOptions.def index 7cd3edf08a17e..b94f6aef9ac60 100644 --- a/clang/include/clang/Basic/DebugOptions.def +++ b/clang/include/clang/Basic/DebugOptions.def @@ -129,6 +129,9 @@ DEBUGOPT(CodeViewCommandLine, 1, 0) /// Whether emit extra debug info for sample pgo profile collection. DEBUGOPT(DebugInfoForProfiling, 1, 0) +/// Whether to emit DW_TAG_template_alias for template aliases. +DEBUGOPT(DebugTemplateAlias, 1, 0) + /// Whether to emit .debug_gnu_pubnames section instead of .debug_pubnames. DEBUGOPT(DebugNameTable, 2, 0) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 8c38acb72362c..d682a24ab3da5 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4309,6 +4309,8 @@ def gsplit_dwarf_EQ : Joined<["-"], "gsplit-dwarf=">, Group, Values<"split,single">; def gno_split_dwarf : Flag<["-"], "gno-split-dwarf">, Group, Visibility<[ClangOption, CLOption, DXCOption]>; +def gtemplate_alias : Flag<["-"], "gtemplate-alias">, Group, Visibility<[ClangOption, CC1Option]>; +def gno_template_alias : Flag<["-"], "gno-template-alias">, Group, Visibility<[ClangOption]>; def gsimple_template_names : Flag<["-"], "gsimple-template-names">, Group; def gsimple_template_names_EQ : Joined<["-"], "gsimple-template-names=">, diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 8c284c332171a..539ded5cca5e1 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1313,6 +1313,44 @@ llvm::DIType *CGDebugInfo::CreateType(const BlockPointerType *Ty, return DBuilder.createPointerType(EltTy, Size); } +static llvm::SmallVector +GetTemplateArgs(const TemplateDecl *TD, const TemplateSpecializationType *Ty) { + assert(Ty->isTypeAlias()); + // TemplateSpecializationType doesn't know if its template args are + // being substituted into a parameter pack. We can find out if that's + // the case now by inspecting the TypeAliasTemplateDecl template + // parameters. Insert Ty's template args into SpecArgs, bundling args + // passed to a parameter pack into a TemplateArgument::Pack. It also + // doesn't know the value of any defaulted args, so collect those now + // too. + SmallVector SpecArgs; + ArrayRef SubstArgs = Ty->template_arguments(); + for (const NamedDecl *Param : TD->getTemplateParameters()->asArray()) { + // If Param is a parameter pack, pack the remaining arguments. + if (Param->isParameterPack()) { + SpecArgs.push_back(TemplateArgument(SubstArgs)); + break; + } + + // Skip defaulted args. + // FIXME: Ideally, we wouldn't do this. We can read the default values + // for each parameter. However, defaulted arguments which are dependent + // values or dependent types can't (easily?) be resolved here. + if (SubstArgs.empty()) { + // If SubstArgs is now empty (we're taking from it each iteration) and + // this template parameter isn't a pack, then that should mean we're + // using default values for the remaining template parameters (after + // which there may be an empty pack too which we will ignore). + break; + } + + // Take the next argument. + SpecArgs.push_back(SubstArgs.front()); + SubstArgs = SubstArgs.drop_front(); + } + return SpecArgs; +} + llvm::DIType *CGDebugInfo::CreateType(const TemplateSpecializationType *Ty, llvm::DIFile *Unit) { assert(Ty->isTypeAlias()); @@ -1332,6 +1370,31 @@ llvm::DIType *CGDebugInfo::CreateType(const TemplateSpecializationType *Ty, auto PP = getPrintingPolicy(); Ty->getTemplateName().print(OS, PP, TemplateName::Qualified::None); + SourceLocation Loc = AliasDecl->getLocation(); + + if (CGM.getCodeGenOpts().DebugTemplateAlias) { + auto ArgVector = ::GetTemplateArgs(TD, Ty); + TemplateArgs Args = {TD->getTemplateParameters(), ArgVector}; + + // FIXME: Respect DebugTemplateNameKind::Mangled, e.g. by using GetName. + // Note we can't use GetName without additional work: TypeAliasTemplateDecl + // doesn't have instantiation information, so + // TypeAliasTemplateDecl::getNameForDiagnostic wouldn't have access to the + // template args. + std::string Name; + llvm::raw_string_ostream OS(Name); + TD->getNameForDiagnostic(OS, PP, /*Qualified=*/false); + if (CGM.getCodeGenOpts().getDebugSimpleTemplateNames() != + llvm::codegenoptions::DebugTemplateNamesKind::Simple || + !HasReconstitutableArgs(Args.Args)) + printTemplateArgumentList(OS, Args.Args, PP); + + llvm::DIDerivedType *AliasTy = DBuilder.createTemplateAlias( + Src, Name, getOrCreateFile(Loc), getLineNumber(Loc), + getDeclContextDescriptor(AliasDecl), CollectTemplateParams(Args, Unit)); + return AliasTy; + } + // Disable PrintCanonicalTypes here because we want // the DW_AT_name to benefit from the TypePrinter's ability // to skip defaulted template arguments. @@ -1343,8 +1406,6 @@ llvm::DIType *CGDebugInfo::CreateType(const TemplateSpecializationType *Ty, PP.PrintCanonicalTypes = false; printTemplateArgumentList(OS, Ty->template_arguments(), PP, TD->getTemplateParameters()); - - SourceLocation Loc = AliasDecl->getLocation(); return DBuilder.createTypedef(Src, OS.str(), getOrCreateFile(Loc), getLineNumber(Loc), getDeclContextDescriptor(AliasDecl)); @@ -5363,6 +5424,54 @@ static bool IsReconstitutableType(QualType QT) { return T.Reconstitutable; } +bool CGDebugInfo::HasReconstitutableArgs( + ArrayRef Args) const { + 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. 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 && + IsReconstitutableType(TA.getIntegralType()); + case TemplateArgument::StructuralValue: + return false; + case TemplateArgument::Type: + return IsReconstitutableType(TA.getAsType()); + case TemplateArgument::Expression: + return IsReconstitutableType(TA.getAsExpr()->getType()); + default: + llvm_unreachable("Other, unresolved, template arguments should " + "not be seen here"); + } + }); +} + std::string CGDebugInfo::GetName(const Decl *D, bool Qualified) const { std::string Name; llvm::raw_string_ostream OS(Name); @@ -5389,49 +5498,7 @@ std::string CGDebugInfo::GetName(const Decl *D, bool Qualified) const { } else if (auto *VD = dyn_cast(ND)) { Args = GetTemplateArgs(VD); } - std::function)> HasReconstitutableArgs = - [&](ArrayRef 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 && - IsReconstitutableType(TA.getIntegralType()); - case TemplateArgument::StructuralValue: - return false; - 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 diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 7b60e94555d06..d6db4d711366a 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -626,7 +626,8 @@ class CGDebugInfo { llvm::DIType *WrappedType; }; - std::string GetName(const Decl*, bool Qualified = false) const; + bool HasReconstitutableArgs(ArrayRef Args) const; + std::string GetName(const Decl *, bool Qualified = false) const; /// Build up structure info for the byref. See \a BuildByRefType. BlockByRefType EmitTypeForVarWithBlocksAttr(const VarDecl *VD, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 096ed14f95704..456ea74caadb0 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4632,6 +4632,21 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, const llvm::Triple &T, } } + // Emit DW_TAG_template_alias for template aliases? True by default for SCE. + bool UseDebugTemplateAlias = + DebuggerTuning == llvm::DebuggerKind::SCE && RequestedDWARFVersion >= 5; + if (const auto *DebugTemplateAlias = Args.getLastArg( + options::OPT_gtemplate_alias, options::OPT_gno_template_alias)) { + // DW_TAG_template_alias is only supported from DWARFv5 but if a user + // asks for it we should let them have it (if the target supports it). + if (checkDebugInfoOption(DebugTemplateAlias, Args, D, TC)) { + const auto &Opt = DebugTemplateAlias->getOption(); + UseDebugTemplateAlias = Opt.matches(options::OPT_gtemplate_alias); + } + } + if (UseDebugTemplateAlias) + CmdArgs.push_back("-gtemplate-alias"); + if (const Arg *A = Args.getLastArg(options::OPT_gsrc_hash_EQ)) { StringRef v = A->getValue(); CmdArgs.push_back(Args.MakeArgString("-gsrc-hash=" + v)); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 1f1f5440ddd75..5531e938e0f4f 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1556,6 +1556,9 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts, llvm::DICompileUnit::DebugNameTableKind::Default)) GenerateArg(Consumer, OPT_gpubnames); + if (Opts.DebugTemplateAlias) + GenerateArg(Consumer, OPT_gtemplate_alias); + auto TNK = Opts.getDebugSimpleTemplateNames(); if (TNK != llvm::codegenoptions::DebugTemplateNamesKind::Full) { if (TNK == llvm::codegenoptions::DebugTemplateNamesKind::Simple) @@ -1827,6 +1830,8 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, Opts.BinutilsVersion = std::string(Args.getLastArgValue(OPT_fbinutils_version_EQ)); + Opts.DebugTemplateAlias = Args.hasArg(OPT_gtemplate_alias); + Opts.DebugNameTable = static_cast( Args.hasArg(OPT_ggnu_pubnames) ? llvm::DICompileUnit::DebugNameTableKind::GNU diff --git a/clang/test/CodeGenCXX/debug-info-alias.cpp b/clang/test/CodeGenCXX/debug-info-alias.cpp index 3d3f87ed1f6fa..bf2dbee465959 100644 --- a/clang/test/CodeGenCXX/debug-info-alias.cpp +++ b/clang/test/CodeGenCXX/debug-info-alias.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -g -std=c++11 -S -emit-llvm %s -o - | FileCheck %s +// RUN: %clang -g -gno-template-alias -std=c++11 -S -emit-llvm %s -o - | FileCheck %s template struct foo { diff --git a/clang/test/CodeGenCXX/defaulted-template-alias.cpp b/clang/test/CodeGenCXX/defaulted-template-alias.cpp new file mode 100644 index 0000000000000..a038aa0d9dc20 --- /dev/null +++ b/clang/test/CodeGenCXX/defaulted-template-alias.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=standalone -gtemplate-alias %s -gsimple-template-names=simple \ +// RUN: | FileCheck %s + +//// Check that -gtemplate-alias causes DW_TAG_template_alias emission for +//// template aliases with default parameter values. See template-alias.cpp for +//// more template alias tests. +//// FIXME: We currently do not emit defaulted arguments. + +template +struct X { + char m; +}; + +template +struct Y { + char n; +}; + +template class T = Y, int I = 5, typename... Ts> +using A = X; + +//// We should be able to emit type alias metadata which describes all the +//// values, including the defaulted parameters and empty parameter pack. +A a; + +// CHECK: !DIDerivedType(tag: DW_TAG_template_alias, name: "A", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]], extraData: ![[extraData:[0-9]+]]) +// CHECK: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", +// CHECK: ![[int:[0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK: ![[extraData]] = !{![[NonDefault:[0-9]+]]} +// CHECK: ![[NonDefault]] = !DITemplateTypeParameter(name: "NonDefault", type: ![[int]]) + +//// FIXME: Ideally, we would describe the deafulted args, like this: +// : ![[extraData]] = !{![[NonDefault:[0-9]+]], ![[T:[0-9]+]], ![[I:[0-9]+]], ![[Ts:[0-9]+]]} +// : ![[NonDefault]] = !DITemplateTypeParameter(name: "NonDefault", type: ![[int]]) +// : ![[T]] = !DITemplateValueParameter(tag: DW_TAG_GNU_template_template_param, name: "T", defaulted: true, value: !"Y") +// : ![[I]] = !DITemplateValueParameter(name: "I", type: ![[int]], defaulted: true, value: i32 5) +// : ![[Ts]] = !DITemplateValueParameter(tag: DW_TAG_GNU_template_parameter_pack, name: "Ts", value: ![[types:[0-9]+]]) +// : ![[types]] = !{} diff --git a/clang/test/CodeGenCXX/template-alias.cpp b/clang/test/CodeGenCXX/template-alias.cpp new file mode 100644 index 0000000000000..256ed693aa2fe --- /dev/null +++ b/clang/test/CodeGenCXX/template-alias.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=standalone -gtemplate-alias %s -gsimple-template-names=simple \ +// RUN: | FileCheck %s --check-prefixes=ALIAS-SIMPLE,ALIAS-ALL + +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=standalone -gtemplate-alias %s -gsimple-template-names=mangled \ +// RUN: | FileCheck %s --check-prefixes=ALIAS-MANGLED,ALIAS-ALL + +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=standalone -gtemplate-alias %s \ +// RUN: | FileCheck %s --check-prefixes=ALIAS-FULL,ALIAS-ALL + +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=standalone %s \ +// RUN: | FileCheck %s --check-prefixes=TYPEDEF + + +//// Check that -gtemplate-alias causes DW_TAG_template_alias emission for +//// template aliases, and that respects gsimple-template-names. +//// +//// Test type and value template parameters. + +template +struct X { + Y m1 = Z; +}; + +template +using A = X; + +A a; + + +// ALIAS-SIMPLE: !DIDerivedType(tag: DW_TAG_template_alias, name: "A", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]], extraData: ![[extraData:[0-9]+]]) +// ALIAS-SIMPLE: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", + +// FIXME: Mangled name is wrong (not a regression). +// ALIAS-MANGLED: !DIDerivedType(tag: DW_TAG_template_alias, name: "A", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]], extraData: ![[extraData:[0-9]+]]) +// ALIAS-MANGLED: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "_STN|X|", + +// ALIAS-FULL: !DIDerivedType(tag: DW_TAG_template_alias, name: "A", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]], extraData: ![[extraData:[0-9]+]]) +// ALIAS-FULL: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", + +// ALIAS-ALL: ![[int:[0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// ALIAS-ALL: ![[extraData]] = !{![[B:[0-9]+]], ![[C:[0-9]+]]} +// ALIAS-ALL: ![[B]] = !DITemplateTypeParameter(name: "B", type: ![[int]]) +// ALIAS-ALL: ![[C]] = !DITemplateValueParameter(name: "C", type: ![[int]], value: i32 5) + +// TYPEDEF: !DIDerivedType(tag: DW_TAG_typedef, name: "A", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]]) +// TYPEDEF: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", +// TYPEDEF: ![[int:[0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) diff --git a/clang/test/CodeGenCXX/variadic-template-alias.cpp b/clang/test/CodeGenCXX/variadic-template-alias.cpp new file mode 100644 index 0000000000000..b4340d60aa6ad --- /dev/null +++ b/clang/test/CodeGenCXX/variadic-template-alias.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple x86_64-unk-unk -o - -emit-llvm -debug-info-kind=standalone -gtemplate-alias %s -gsimple-template-names=simple \ +// RUN: | FileCheck %s + +//// Check that -gtemplate-alias causes DW_TAG_template_alias emission for +//// variadic template aliases. See template-alias.cpp for more template alias +//// tests. + +template +struct X { + Y m1 = Z; +}; + +template +using A = X; + +A<5, int> a; + +// CHECK: !DIDerivedType(tag: DW_TAG_template_alias, name: "A", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]], extraData: ![[extraData:[0-9]+]]) +// CHECK: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", +// CHECK: ![[int:[0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK: ![[extraData]] = !{![[I:[0-9]+]], ![[Ts:[0-9]+]]} +// CHECK: ![[I]] = !DITemplateValueParameter(name: "I", type: ![[int]], value: i32 5) +// CHECK: ![[Ts]] = !DITemplateValueParameter(tag: DW_TAG_GNU_template_parameter_pack, name: "Ts", value: ![[types:[0-9]+]]) +// CHECK: ![[types]] = !{![[int_template_param:[0-9]+]]} +// CHECK: ![[int_template_param]] = !DITemplateTypeParameter(type: ![[int]]) diff --git a/clang/test/Driver/debug-options.c b/clang/test/Driver/debug-options.c index e4809511ac91a..a6acfe88a3861 100644 --- a/clang/test/Driver/debug-options.c +++ b/clang/test/Driver/debug-options.c @@ -465,3 +465,13 @@ // MANGLED_TEMP_NAMES: error: unknown argument '-gsimple-template-names=mangled'; did you mean '-Xclang -gsimple-template-names=mangled' // RUN: %clang -### -target x86_64 -c -g %s 2>&1 | FileCheck --check-prefix=FULL_TEMP_NAMES --implicit-check-not=debug-forward-template-params %s // FULL_TEMP_NAMES-NOT: -gsimple-template-names + +//// Test -g[no-]template-alias (enabled by default with SCE debugger tuning and DWARFv5). +// RUN: %clang -### -target x86_64 -c -gdwarf-5 -gsce %s 2>&1 | FileCheck %s --check-prefixes=TEMPLATE-ALIAS +// RUN: %clang -### -target x86_64 -c -gdwarf-4 -gsce %s 2>&1 | FileCheck %s --check-prefixes=NO-TEMPLATE-ALIAS +// RUN: %clang -### -target x86_64 -c -gdwarf-5 -gsce -gtemplate-alias %s 2>&1 | FileCheck %s --check-prefixes=TEMPLATE-ALIAS +// RUN: %clang -### -target x86_64 -c -gdwarf-5 -gsce -gno-template-alias %s 2>&1 | FileCheck %s --check-prefixes=NO-TEMPLATE-ALIAS +// RUN: %clang -### -target x86_64 -c -gdwarf-5 -gtemplate-alias %s 2>&1 | FileCheck %s --check-prefixes=TEMPLATE-ALIAS +// RUN: %clang -### -target x86_64 -c -gdwarf-5 -gtemplate-alias -gno-template-alias %s 2>&1 | FileCheck %s --check-prefixes=NO-TEMPLATE-ALIAS +// TEMPLATE-ALIAS: "-gtemplate-alias" +// NO-TEMPLATE-ALIAS-NOT: "-gtemplate-alias"