Skip to content

[Clang] Emit DW_TAG_template_alias for template aliases #87623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DebugOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -4308,6 +4308,8 @@ def gsplit_dwarf_EQ : Joined<["-"], "gsplit-dwarf=">, Group<g_flags_Group>,
Values<"split,single">;
def gno_split_dwarf : Flag<["-"], "gno-split-dwarf">, Group<g_flags_Group>,
Visibility<[ClangOption, CLOption, DXCOption]>;
def gtemplate_alias : Flag<["-"], "gtemplate-alias">, Group<g_flags_Group>, Visibility<[ClangOption, CC1Option]>;
def gno_template_alias : Flag<["-"], "gno-template-alias">, Group<g_flags_Group>, Visibility<[ClangOption]>;
def gsimple_template_names : Flag<["-"], "gsimple-template-names">, Group<g_flags_Group>;
def gsimple_template_names_EQ
: Joined<["-"], "gsimple-template-names=">,
Expand Down
157 changes: 112 additions & 45 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,44 @@ llvm::DIType *CGDebugInfo::CreateType(const BlockPointerType *Ty,
return DBuilder.createPointerType(EltTy, Size);
}

static llvm::SmallVector<TemplateArgument>
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<TemplateArgument> 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());
Expand All @@ -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.
Expand All @@ -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));
Expand Down Expand Up @@ -5363,6 +5424,54 @@ static bool IsReconstitutableType(QualType QT) {
return T.Reconstitutable;
}

bool CGDebugInfo::HasReconstitutableArgs(
ArrayRef<TemplateArgument> 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);
Expand All @@ -5389,49 +5498,7 @@ std::string CGDebugInfo::GetName(const Decl *D, bool Qualified) const {
} 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 &&
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<typename T>
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,8 @@ class CGDebugInfo {
llvm::DIType *WrappedType;
};

std::string GetName(const Decl*, bool Qualified = false) const;
bool HasReconstitutableArgs(ArrayRef<TemplateArgument> 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,
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take out the diags here, if someone explicitly asks for something in debug info we do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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));
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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<unsigned>(
Args.hasArg(OPT_ggnu_pubnames)
? llvm::DICompileUnit::DebugNameTableKind::GNU
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenCXX/debug-info-alias.cpp
Original file line number Diff line number Diff line change
@@ -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<typename T>
struct foo {
Expand Down
38 changes: 38 additions & 0 deletions clang/test/CodeGenCXX/defaulted-template-alias.cpp
Original file line number Diff line number Diff line change
@@ -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<typename T>
struct X {
char m;
};

template<typename T>
struct Y {
char n;
};

template <typename NonDefault, template <typename C> class T = Y, int I = 5, typename... Ts>
using A = X<NonDefault>;

//// We should be able to emit type alias metadata which describes all the
//// values, including the defaulted parameters and empty parameter pack.
A<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]] = !{![[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]+]]}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Is it worth turning these into CHECK-NOT? So when this is fixed in the future we remember that we have these tests ready

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the test will already start failing if this gets fixed because the current extraData metadata tuple contents check will fail (it'll have more than one element). I'm still happy to add CHECK-NOTs if you'd like, just thought I'd raise this in case it changes your stance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah right, that makes sense, happy keeping it as is

// : ![[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]] = !{}
47 changes: 47 additions & 0 deletions clang/test/CodeGenCXX/template-alias.cpp
Original file line number Diff line number Diff line change
@@ -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<typename Y, int Z>
struct X {
Y m1 = Z;
};

template<typename B, int C>
using A = X<B, C>;

A<int, 5> 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<int, 5>", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]], extraData: ![[extraData:[0-9]+]])
// ALIAS-MANGLED: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "_STN|X|<int, 5>",

// ALIAS-FULL: !DIDerivedType(tag: DW_TAG_template_alias, name: "A<int, 5>", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]], extraData: ![[extraData:[0-9]+]])
// ALIAS-FULL: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X<int, 5>",

// 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<int, 5>", file: ![[#]], line: [[#]], baseType: ![[baseType:[0-9]+]])
// TYPEDEF: ![[baseType]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X<int, 5>",
// TYPEDEF: ![[int:[0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
25 changes: 25 additions & 0 deletions clang/test/CodeGenCXX/variadic-template-alias.cpp
Original file line number Diff line number Diff line change
@@ -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<typename Y, int Z>
struct X {
Y m1 = Z;
};

template<int I, typename... Ts>
using A = X<Ts..., I>;

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]])
10 changes: 10 additions & 0 deletions clang/test/Driver/debug-options.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--target

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gave clang: error: unknown argument '--target'; did you mean '-target'?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh Coulda sworn that was changed... okay.

// 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"