[clang] [llvm] Add support for template as type parameter (PR #127654)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 19 07:20:46 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-debuginfo
@llvm/pr-subscribers-clang
Author: ykhatav (ykhatav)
<details>
<summary>Changes</summary>
For template classes with type parameters, the information of which member variables are of a "templated type" is lost.
The debugger cannot differentiate between "templated" types and "hardcoded" types. This is because both types have the same debug information, so there is no way that gdb can distinguish between them. This patch adds support for templates as type by representing the template type as a derived type.
---
Full diff: https://github.com/llvm/llvm-project/pull/127654.diff
10 Files Affected:
- (modified) clang/include/clang/Basic/CodeGenOptions.def (+1)
- (modified) clang/include/clang/Driver/Options.td (+5)
- (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+16-3)
- (modified) clang/lib/CodeGen/CGDebugInfo.h (+2)
- (added) clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp (+52)
- (modified) llvm/include/llvm/IR/DIBuilder.h (+6)
- (modified) llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp (+2)
- (modified) llvm/lib/IR/DIBuilder.cpp (+9)
- (modified) llvm/lib/IR/Verifier.cpp (+6-2)
- (added) llvm/test/DebugInfo/X86/template-as-type-param.ll (+119)
``````````diff
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 386652d2efa9e..79c3df2e43eef 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -468,6 +468,7 @@ CODEGENOPT(CtorDtorReturnThis, 1, 0)
/// Enables emitting Import Call sections on supported targets that can be used
/// by the Windows kernel to enable import call optimization.
CODEGENOPT(ImportCallOptimization, 1, 0)
+CODEGENOPT(DebugTemplateParameterAsType, 1, 0)
/// FIXME: Make DebugOptions its own top-level .def file.
#include "DebugOptions.def"
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index d8123cc39fdc9..32a88d42dde49 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9028,3 +9028,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
Group<m_Group>,
HelpText<"Enable the wasm-opt optimizer (default)">,
MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
+defm debug_template_parameter_as_type
+ : BoolFOption<"debug-template-parameter-as-type",
+ CodeGenOpts<"DebugTemplateParameterAsType">,
+ DefaultFalse, PosFlag<SetTrue>, NegFlag<SetFalse>,
+ BothFlags<[], [CC1Option]>>;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index db595796c067e..ce2d34dcd4ce3 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1003,6 +1003,13 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) {
return DBuilder.createBasicType(BTName, Size, Encoding);
}
+llvm::DIType *CGDebugInfo::CreateType(const SubstTemplateTypeParmType *Ty,
+ llvm::DIFile *U) {
+ llvm::DIType *debugType = getOrCreateType(Ty->getReplacementType(), U);
+ return DBuilder.createTemplateTypeParameterAsType(
+ U, Ty->getReplacedParameter()->getName(), debugType);
+}
+
llvm::DIType *CGDebugInfo::CreateType(const BitIntType *Ty) {
StringRef Name = Ty->isUnsigned() ? "unsigned _BitInt" : "_BitInt";
@@ -3611,7 +3618,8 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
/*Scope=*/TrapSP, /*InlinedAt=*/TrapLocation);
}
-static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
+static QualType UnwrapTypeForDebugInfo(QualType T, const CodeGenModule &CGM) {
+ const ASTContext &C = CGM.getContext();
Qualifiers Quals;
do {
Qualifiers InnerQuals = T.getLocalQualifiers();
@@ -3664,6 +3672,8 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
T = cast<MacroQualifiedType>(T)->getUnderlyingType();
break;
case Type::SubstTemplateTypeParm:
+ if (CGM.getCodeGenOpts().DebugTemplateParameterAsType)
+ return C.getQualifiedType(T.getTypePtr(), Quals);
T = cast<SubstTemplateTypeParmType>(T)->getReplacementType();
break;
case Type::Auto:
@@ -3690,7 +3700,7 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
}
llvm::DIType *CGDebugInfo::getTypeOrNull(QualType Ty) {
- assert(Ty == UnwrapTypeForDebugInfo(Ty, CGM.getContext()));
+ assert(Ty == UnwrapTypeForDebugInfo(Ty, CGM));
auto It = TypeCache.find(Ty.getAsOpaquePtr());
if (It != TypeCache.end()) {
// Verify that the debug info still exists.
@@ -3729,7 +3739,7 @@ llvm::DIType *CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile *Unit) {
});
// Unwrap the type as needed for debug information.
- Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext());
+ Ty = UnwrapTypeForDebugInfo(Ty, CGM);
if (auto *T = getTypeOrNull(Ty))
return T;
@@ -3866,6 +3876,9 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
case Type::Decltype:
case Type::PackIndexing:
case Type::UnaryTransform:
+ if (Ty->getTypeClass() == Type::SubstTemplateTypeParm &&
+ CGM.getCodeGenOpts().DebugTemplateParameterAsType)
+ return CreateType(cast<SubstTemplateTypeParmType>(Ty), Unit);
break;
}
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 38f73eca561b7..5e28fd5bf9039 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -228,6 +228,8 @@ class CGDebugInfo {
llvm::DIType *CreateType(const MemberPointerType *Ty, llvm::DIFile *F);
llvm::DIType *CreateType(const AtomicType *Ty, llvm::DIFile *F);
llvm::DIType *CreateType(const PipeType *Ty, llvm::DIFile *F);
+ llvm::DIType *CreateType(const SubstTemplateTypeParmType *Ty,
+ llvm::DIFile *F);
/// Get enumeration type.
llvm::DIType *CreateEnumType(const EnumType *Ty);
llvm::DIType *CreateTypeDefinition(const EnumType *Ty);
diff --git a/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp b/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp
new file mode 100644
index 0000000000000..3422ce8f1e2bd
--- /dev/null
+++ b/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited -fdebug-template-parameter-as-type -triple x86_64-apple-darwin %s -o - | FileCheck %s
+
+
+template <typename T>
+struct TClass {
+ TClass();
+ void foo();
+ T val_;
+ int val2_;
+};
+
+template <typename T>
+void TClass<T>::foo() {
+ T tVar = 1;
+ T* pT = &tVar;
+ tVar++;
+}
+
+template <typename T>
+T bar(T tp) {
+ return tp;
+}
+
+int main () {
+ TClass<int> a;
+ a.val2_ = 3;
+ a.foo();
+ auto A = bar(42);
+ TClass<double> b;
+ return 0;
+}
+
+// CHECK: [[INT:![0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TClass<int>"
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val_",{{.*}}baseType: [[TPARAM:![0-9]+]]
+// CHECK: [[TPARAM]] = !DIDerivedType(tag: DW_TAG_template_type_parameter, name: "T", {{.*}}baseType: [[INT]])
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val2_",{{.*}}baseType: [[INT]]
+
+// CHECK: !DILocalVariable(name: "A",{{.*}}type: [[TPARAM]])
+
+// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TClass<double>"
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val_",{{.*}}baseType: [[TPARAM2:![0-9]+]]
+// CHECK: [[TPARAM2]] = !DIDerivedType(tag: DW_TAG_template_type_parameter, name: "T", {{.*}}baseType: [[DOUBLE:![0-9]+]])
+// CHECK: [[DOUBLE]] = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
+
+// CHECK: distinct !DISubprogram(name: "foo"
+// CHECK: !DILocalVariable(name: "tVar",{{.*}}type: [[TPARAM]])
+// CHECK: !DILocalVariable(name: "pT",{{.*}}type: [[TPTR:![0-9]+]]
+// CHECK: [[TPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[TPARAM]]
+
+// CHECK: distinct !DISubprogram(name: "bar<int>"
+// CHECK: !DILocalVariable(name: "tp",{{.*}}type: [[TPARAM]])
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index 6c479415b9ed2..bb9b9490fdcdd 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -550,6 +550,12 @@ namespace llvm {
StringRef Name,
DIType *Ty,
bool IsDefault);
+ /// \param Scope Scope in which this type is defined.
+ /// \param Name Type parameter name.
+ /// \param Ty Parameter type.
+ DIDerivedType *createTemplateTypeParameterAsType(DIScope *Scope,
+ StringRef Name,
+ DIType *Ty);
/// Create debugging information for template
/// value parameter.
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 0a8a1ad38c959..8557d833dfb0e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -520,6 +520,8 @@ void DwarfUnit::addTemplateParams(DIE &Buffer, DINodeArray TParams) {
constructTemplateTypeParameterDIE(Buffer, TTP);
else if (auto *TVP = dyn_cast<DITemplateValueParameter>(Element))
constructTemplateValueParameterDIE(Buffer, TVP);
+ else if (auto *TDT = dyn_cast<DIDerivedType>(Element))
+ createTypeDIE(TDT->getScope(), Buffer, TDT);
}
}
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 8f9462ab46d88..fc50077ba8736 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -458,6 +458,15 @@ DIBuilder::createObjCProperty(StringRef Name, DIFile *File, unsigned LineNumber,
SetterName, PropertyAttributes, Ty);
}
+DIDerivedType *DIBuilder::createTemplateTypeParameterAsType(DIScope *Context,
+ StringRef Name,
+ DIType *Ty) {
+ return DIDerivedType::get(VMContext, dwarf::DW_TAG_template_type_parameter,
+ Name, nullptr, 0, Context, Ty, 0, 0, 0,
+ std::nullopt, std::nullopt, DINode::FlagZero,
+ nullptr);
+}
+
DITemplateTypeParameter *
DIBuilder::createTemplateTypeParameter(DIScope *Context, StringRef Name,
DIType *Ty, bool isDefault) {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 8432779c107de..669c3370d9fc4 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1241,6 +1241,7 @@ void Verifier::visitDIDerivedType(const DIDerivedType &N) {
N.getTag() == dwarf::DW_TAG_inheritance ||
N.getTag() == dwarf::DW_TAG_friend ||
N.getTag() == dwarf::DW_TAG_set_type ||
+ N.getTag() == dwarf::DW_TAG_template_type_parameter ||
N.getTag() == dwarf::DW_TAG_template_alias,
"invalid tag", &N);
if (N.getTag() == dwarf::DW_TAG_ptr_to_member_type) {
@@ -1288,8 +1289,11 @@ void Verifier::visitTemplateParams(const MDNode &N, const Metadata &RawParams) {
auto *Params = dyn_cast<MDTuple>(&RawParams);
CheckDI(Params, "invalid template params", &N, &RawParams);
for (Metadata *Op : Params->operands()) {
- CheckDI(Op && isa<DITemplateParameter>(Op), "invalid template parameter",
- &N, Params, Op);
+ CheckDI(((Op) && (isa<DITemplateParameter>(Op))) ||
+ ((isa<DIDerivedType>(Op)) &&
+ (dyn_cast<DIDerivedType>(Op)->getTag() ==
+ dwarf::DW_TAG_template_type_parameter)),
+ "invalid template parameter", &N, Params, Op);
}
}
diff --git a/llvm/test/DebugInfo/X86/template-as-type-param.ll b/llvm/test/DebugInfo/X86/template-as-type-param.ll
new file mode 100644
index 0000000000000..25eaeb92fec3e
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/template-as-type-param.ll
@@ -0,0 +1,119 @@
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu %s -o %t -filetype=obj
+; RUN: llvm-dwarfdump %t | FileCheck %s
+;Source code for the IR below:
+;template <typename T>
+;struct A
+;{
+; A () : val_ (), val2_ () { }
+; T val_;
+; int val2_;
+;};
+
+;int main (void)
+;{
+; A<int> a;
+; a.val2_ = 3;
+; return 0;
+;}
+
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("A<int>")
+; CHECK-NEXT: DW_AT_byte_size (0x08)
+; CHECK-NEXT: DW_AT_decl_file ("test.cpp")
+; CHECK-NEXT: DW_AT_decl_line (2)
+
+; CHECK-NOT: NULL
+
+; CHECK:[[TEMPLATE:0x[0-9a-f]*]]: DW_TAG_template_type_parameter
+; CHECK-NEXT: DW_AT_type {{.*}} "int"
+; CHECK-NEXT: DW_AT_name ("T")
+
+; CHECK: DW_TAG_member
+; CHECK-NEXT: DW_AT_name ("val_")
+; CHECK-NEXT: DW_AT_type ([[TEMPLATE]] "T")
+
+; CHECK: DW_TAG_member
+; CHECK-NEXT: DW_AT_name ("val2_")
+; CHECK-NEXT: DW_AT_type {{.*}} "int"
+; ModuleID = 'test.cpp'
+source_filename = "test.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.A = type { i32, i32 }
+
+$_ZN1AIiEC2Ev = comdat any
+
+; Function Attrs: mustprogress noinline norecurse optnone uwtable
+define dso_local noundef i32 @main() #0 !dbg !22 {
+entry:
+ %retval = alloca i32, align 4
+ %a = alloca %struct.A, align 4
+ store i32 0, ptr %retval, align 4
+ #dbg_declare(ptr %a, !26, !DIExpression(), !27)
+ call void @_ZN1AIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %a), !dbg !27
+ %val2_ = getelementptr inbounds nuw %struct.A, ptr %a, i32 0, i32 1, !dbg !28
+ store i32 3, ptr %val2_, align 4, !dbg !29
+ ret i32 0, !dbg !30
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone uwtable
+define linkonce_odr dso_local void @_ZN1AIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this) unnamed_addr #1 comdat align 2 !dbg !31 {
+entry:
+ %this.addr = alloca ptr, align 8
+ store ptr %this, ptr %this.addr, align 8
+ #dbg_declare(ptr %this.addr, !32, !DIExpression(), !34)
+ %this1 = load ptr, ptr %this.addr, align 8
+ %val_ = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 0, !dbg !35
+ store i32 0, ptr %val_, align 4, !dbg !35
+ %val2_ = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 1, !dbg !36
+ store i32 0, ptr %val2_, align 4, !dbg !36
+ ret void, !dbg !37
+}
+
+attributes #0 = { mustprogress noinline norecurse optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { mustprogress noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!14, !15, !16, !17, !18, !19, !20}
+!llvm.ident = !{!21}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "", checksumkind: CSK_MD5, checksum: "451371997e00e9e85d610a4e9d44a9b5")
+!2 = !{!3}
+!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A<int>", file: !1, line: 2, size: 64, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !4, templateParams: !12, identifier: "_ZTS1AIiE")
+!4 = !{!5, !7, !8}
+!5 = !DIDerivedType(tag: DW_TAG_member, name: "val_", scope: !3, file: !1, line: 4, baseType: !38, size: 32)
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !DIDerivedType(tag: DW_TAG_member, name: "val2_", scope: !3, file: !1, line: 5, baseType: !6, size: 32, offset: 32)
+!8 = !DISubprogram(name: "A", scope: !3, file: !1, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null, !11}
+!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!12 = !{!38}
+!13 = !DITemplateTypeParameter(name: "T", type: !6)
+!14 = !{i32 7, !"Dwarf Version", i32 5}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{i32 8, !"PIC Level", i32 2}
+!18 = !{i32 7, !"PIE Level", i32 2}
+!19 = !{i32 7, !"uwtable", i32 2}
+!20 = !{i32 7, !"frame-pointer", i32 2}
+!21 = !{!"clang"}
+!22 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 7, type: !23, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !25)
+!23 = !DISubroutineType(types: !24)
+!24 = !{!6}
+!25 = !{}
+!26 = !DILocalVariable(name: "a", scope: !22, file: !1, line: 8, type: !3)
+!27 = !DILocation(line: 8, column: 8, scope: !22)
+!28 = !DILocation(line: 9, column: 3, scope: !22)
+!29 = !DILocation(line: 9, column: 9, scope: !22)
+!30 = !DILocation(line: 10, column: 1, scope: !22)
+!31 = distinct !DISubprogram(name: "A", linkageName: "_ZN1AIiEC2Ev", scope: !3, file: !1, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !8, retainedNodes: !25)
+!32 = !DILocalVariable(name: "this", arg: 1, scope: !31, type: !33, flags: DIFlagArtificial | DIFlagObjectPointer)
+!33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64)
+!34 = !DILocation(line: 0, scope: !31)
+!35 = !DILocation(line: 3, column: 8, scope: !31)
+!36 = !DILocation(line: 3, column: 17, scope: !31)
+!37 = !DILocation(line: 3, column: 28, scope: !31)
+!38 = !DIDerivedType(tag: DW_TAG_template_type_parameter,name:"T",scope:!3, baseType: !6)
``````````
</details>
https://github.com/llvm/llvm-project/pull/127654
More information about the cfe-commits
mailing list