Skip to content

[DebugInfo] Place local ODR-uniqued types in decl DISubprograms #119001

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
54 changes: 54 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,7 @@ CGDebugInfo::getOrCreateRecordFwdDecl(const RecordType *Ty,
// Don't include a linkage name in line tables only.
if (CGM.getCodeGenOpts().hasReducedDebugInfo())
Identifier = getTypeIdentifier(Ty, CGM, TheCU);
Ctx = PickCompositeTypeScope(Ctx, Identifier);
llvm::DICompositeType *RetTy = DBuilder.createReplaceableCompositeType(
getTagForRecord(RD), RDName, Ctx, DefUnit, Line, 0, Size, Align, Flags,
Identifier);
Expand Down Expand Up @@ -3534,6 +3535,7 @@ llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) {
// FwdDecl with the second and then replace the second with
// complete type.
llvm::DIScope *EDContext = getDeclContextDescriptor(ED);
EDContext = PickCompositeTypeScope(EDContext, Identifier);
llvm::DIFile *DefUnit = getOrCreateFile(ED->getLocation());
llvm::TempDIScope TmpContext(DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_enumeration_type, "", TheCU, DefUnit, 0));
Expand Down Expand Up @@ -3901,6 +3903,57 @@ CGDebugInfo::getOrCreateLimitedType(const RecordType *Ty) {
return Res;
}

llvm::DIScope *CGDebugInfo::PickCompositeTypeScope(llvm::DIScope *S,
StringRef Identifier) {
using llvm::DISubprogram;

// Only adjust the scope for composite types placed into functions.
if (!isa<DISubprogram>(S))
return S;

// We must adjust the scope if the ODR-name of the type is set.
if (Identifier.empty())
return S;

// This type has an ODR-name, and might be de-duplicated during LTO. It needs
// to be placed in the unique declaration of the function, not a (potentially
// duplicated) definition.
DISubprogram *SP = cast<DISubprogram>(S);
if (DISubprogram *Decl = SP->getDeclaration())
return Decl;

// There is no declaration -- we must produce one and retrofit it to the
// existing definition. Assume that we can just harvest the existing
// information, clear the definition flag and set as decl.
DISubprogram::DISPFlags SPFlags = SP->getSPFlags();
SPFlags &= ~DISubprogram::SPFlagDefinition;

llvm::DINode::DIFlags Flags = SP->getFlags();
Flags &= ~llvm::DINode::FlagAllCallsDescribed;

#ifdef EXPENSIVE_CHECKS
// If we're looking to be really rigorous and avoid a hard-to-debug mishap,
// make sure that there aren't any function definitions in the scope chain.
llvm::DIScope *ToCheck = SP->getScope();
do {
// We should terminate at a DIFile rather than a DICompileUnit -- we're
// not fully unique across LTO otherwise.
assert(!isa<llvm::DICompileUnit>(ToCheck));
if (auto *DISP = dyn_cast<DISubprogram>(ToCheck))
assert(!(DISP->getSPFlags() & DISubprogram::SPFlagDefinition));
ToCheck = ToCheck->getScope();
} while (ToCheck);
#endif

DISubprogram *DeclSP = DBuilder.createFunction(
SP->getScope(), SP->getName(), SP->getLinkageName(), SP->getFile(),
SP->getLine(), SP->getType(), SP->getScopeLine(), Flags, SPFlags,
SP->getTemplateParams(), nullptr, nullptr, SP->getAnnotations());
Comment on lines +3948 to +3951
Copy link
Member

@dzhidzhoev dzhidzhoev May 14, 2025

Choose a reason for hiding this comment

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

I've noticed that in the LLVM IR output of clang/test/CodeGenCXX/debug-info-local-types.cpp, !DICompositeType(tag: DW_TAG_class_type, name: "bar" is not attached to the retainedNodes of its scope (declaration DISubprogram for "foo").

Is it the desired behavior for us?

The reason is that DIBuilder::finalizeSubprogram() is never called for DeclSP. We could track it and finalize it in CGDebugInfo::finalize(), or we could try something like this #139914.


SP->replaceDeclaration(DeclSP);
return DeclSP;
}

// TODO: Currently used for context chains when limiting debug info.
llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
RecordDecl *RD = Ty->getDecl();
Expand Down Expand Up @@ -3938,6 +3991,7 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
auto Align = getTypeAlignIfRequired(Ty, CGM.getContext());

SmallString<256> Identifier = getTypeIdentifier(Ty, CGM, TheCU);
RDContext = PickCompositeTypeScope(RDContext, Identifier);

// Explicitly record the calling convention and export symbols for C++
// records.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,12 @@ class CGDebugInfo {
void EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
QualType FnType, llvm::Function *Fn = nullptr);

/// Select the appropriate scope for a composite type, redirecting certain
/// types into declaration DISubprograms rather than definition DISubprograms.
/// This avoids certain types that LLVM can unique based on their name being
/// put in a distinct-storage context.
llvm::DIScope *PickCompositeTypeScope(llvm::DIScope *S, StringRef Identifier);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a doxygen comment that explains the purpose of this function?


/// Emit debug info for an extern function being called.
/// This is needed for call site debug info.
void EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke,
Expand Down
16 changes: 8 additions & 8 deletions clang/test/CodeGen/debug-info-codeview-unnamed.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ int main(int argc, char* argv[], char* arge[]) {
//
struct { int bar; } one = {42};
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one"
// LINUX-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
// LINUX-SAME: )
// LINUX: [[TYPE_OF_ONE]] = distinct !DICompositeType(
// LINUX: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: )
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one"
// LINUX-SAME: type: [[TYPE_OF_ONE]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one"
// MSVC-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
// MSVC-SAME: )
// MSVC: [[TYPE_OF_ONE]] = distinct !DICompositeType
// MSVC: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType
// MSVC-SAME: tag: DW_TAG_structure_type
// MSVC-NOT: name:
// MSVC-NOT: identifier:
// MSVC-SAME: )
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one"
// MSVC-SAME: type: [[TYPE_OF_ONE]]
// MSVC-SAME: )

return 0;
}
16 changes: 9 additions & 7 deletions clang/test/CodeGen/debug-info-unused-types.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ void quux(void) {
// CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
// CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "bar"
// CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAR"
// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], [[TYPE6:![0-9]+]], {{![0-9]+}}, [[TYPE7:![0-9]+]], [[TYPE2]], [[TYPE8:![0-9]+]]}
// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int"
// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz"
// CHECK: [[TYPE7]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y"
// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], [[TYPE4:![0-9]+]], {{![0-9]+}}}
// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int"
// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz"
// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]]
// CHECK: [[SPRETNODES]] = !{[[TYPE5:![0-9]+]], [[TYPE6:![0-9]+]], [[TYPE8:![0-9]+]]}
// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y"
// CHECK: [[TYPE6]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
// CHECK: [[TYPE7:![0-9]+]] = !DIEnumerator(name: "Z"
// CHECK: [[TYPE8]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "w"

// Check that debug info is not emitted for the typedef, struct, enum, and
Expand Down
14 changes: 8 additions & 6 deletions clang/test/CodeGen/debug-info-unused-types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ void quux() {
// CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
// CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "baz"
// CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAZ"
// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], {{![0-9]+}}, [[TYPE6:![0-9]+]], [[TYPE2]]}
// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo"
// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar"
// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y"
// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], {{![0-9]+}}}
// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo"
// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar"
// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]]
// CHECK: [[SPRETNODES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]]}
// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y", scope: [[SP]]
// CHECK: [[TYPE5]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z", scope: [[SP]]
// CHECK: [[TYPE6:![0-9]+]] = !DIEnumerator(name: "Z"

// NODBG-NOT: !DI{{CompositeType|Enumerator|DerivedType}}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenCXX/debug-info-access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class B : public A {
static int public_static;

protected:
// CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_using",{{.*}} line: [[@LINE+3]],{{.*}} flags: DIFlagProtected)
// CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_typedef",{{.*}} line: [[@LINE+1]],{{.*}} flags: DIFlagProtected)
typedef int prot_typedef;
// CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_using",{{.*}} line: [[@LINE+1]],{{.*}} flags: DIFlagProtected)
using prot_using = prot_typedef;
prot_using prot_member;

Expand Down
12 changes: 6 additions & 6 deletions clang/test/CodeGenCXX/debug-info-anon-union-vars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ void instantiate(int x) {
// CHECK: !DIGlobalVariable(name: "b",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true
// CHECK: !DIGlobalVariable(name: "result", {{.*}} isLocal: false, isDefinition: true
// CHECK: !DIGlobalVariable(name: "value", {{.*}} isLocal: false, isDefinition: true
// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial
// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial
// CHECK: !DILocalVariable(
// CHECK-NOT: name:
// CHECK: type: ![[UNION:[0-9]+]]
// CHECK: ![[UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type,
// CHECK: ![[UNION:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_union_type,
// CHECK-NOT: name:
// CHECK: elements
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "i", scope: ![[UNION]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "c", scope: ![[UNION]],
// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial
// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial
// CHECK: !DILocalVariable(
// CHECK-NOT: name:
// CHECK: type: ![[UNION]]
63 changes: 36 additions & 27 deletions clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,54 @@

int main(int argc, char* argv[], char* arge[]) {
//
// LINUX: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: )

//
// LINUX: [[TYPE_OF_TWO:![0-9]+]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: )
//

//
// LINUX: [[TYPE_OF_THREE:![0-9]+]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-SAME: name: "named"
// LINUX-NOT: identifier:
// LINUX-SAME: )

//
// LINUX: [[TYPE_OF_FOUR:![0-9]+]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_class_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: )

// In CodeView, the LF_MFUNCTION entry for "bar()" refers to the forward
// reference of the unnamed struct. Visual Studio requires a unique
// identifier to match the LF_STRUCTURE forward reference to the definition.
//
struct { void bar() {} } one;
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one"
// LINUX-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
// LINUX-SAME: )
// LINUX: [[TYPE_OF_ONE]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: type: [[TYPE_OF_ONE]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one"
// MSVC-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
// MSVC-SAME: )
//
// MSVC: [[TYPE_OF_ONE]] = distinct !DICompositeType
// MSVC-SAME: tag: DW_TAG_structure_type
// MSVC-SAME: name: "<unnamed-type-one>"
// MSVC-SAME: identifier: ".?AU<unnamed-type-one>@?1??main@@9@"
// MSVC-SAME: )


// In CodeView, the LF_POINTER entry for "ptr2unnamed" refers to the forward
// reference of the unnamed struct. Visual Studio requires a unique
// identifier to match the LF_STRUCTURE forward reference to the definition.
Expand All @@ -36,49 +59,39 @@ int main(int argc, char* argv[], char* arge[]) {
int decltype(two)::*ptr2unnamed = &decltype(two)::bar;
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "two"
// LINUX-SAME: type: [[TYPE_OF_TWO:![0-9]+]]
// LINUX-SAME: )
// LINUX: [[TYPE_OF_TWO]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: type: [[TYPE_OF_TWO]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "two"
// MSVC-SAME: type: [[TYPE_OF_TWO:![0-9]+]]
// MSVC-SAME: )
//
// MSVC: [[TYPE_OF_TWO]] = distinct !DICompositeType
// MSVC-SAME: tag: DW_TAG_structure_type
// MSVC-SAME: name: "<unnamed-type-two>"
// MSVC-SAME: identifier: ".?AU<unnamed-type-two>@?2??main@@9@"
// MSVC-SAME: )


// In DWARF, named structures which are not externally visibile do not
// require an identifier. In CodeView, named structures are given an
// identifier.
//
struct named { int bar; int named::* p2mem; } three = { 42, &named::bar };
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "three"
// LINUX-SAME: type: [[TYPE_OF_THREE:![0-9]+]]
// LINUX-SAME: )
// LINUX: [[TYPE_OF_THREE]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-SAME: name: "named"
// LINUX-NOT: identifier:
// LINUX-SAME: type: [[TYPE_OF_THREE]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "three"
// MSVC-SAME: type: [[TYPE_OF_THREE:![0-9]+]]
// MSVC-SAME: )
//
// MSVC: [[TYPE_OF_THREE]] = distinct !DICompositeType
// MSVC-SAME: tag: DW_TAG_structure_type
// MSVC-SAME: name: "named"
// MSVC-SAME: identifier: ".?AUnamed@?1??main@@9@"
// MSVC-SAME: )


// In CodeView, the LF_MFUNCTION entry for the lambda "operator()" routine
// refers to the forward reference of the unnamed LF_CLASS for the lambda.
// Visual Studio requires a unique identifier to match the forward reference
Expand All @@ -87,17 +100,13 @@ int main(int argc, char* argv[], char* arge[]) {
auto four = [argc](int i) -> int { return argc == i ? 1 : 0; };
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "four"
// LINUX-SAME: type: [[TYPE_OF_FOUR:![0-9]+]]
// LINUX-SAME: )
// LINUX: [[TYPE_OF_FOUR]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_class_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: type: [[TYPE_OF_FOUR]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "four"
// MSVC-SAME: type: [[TYPE_OF_FOUR:![0-9]+]]
// MSVC-SAME: )
//
// MSVC: [[TYPE_OF_FOUR]] = distinct !DICompositeType
// MSVC-SAME: tag: DW_TAG_class_type
// MSVC-SAME: name: "<lambda_0>"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ void test() {
// CHECK-SAME: name: "<lambda_2_1>",
c.lambda_params();

// CHECK: !DISubprogram(name: "operator()", scope: ![[LAMBDA1:[0-9]+]],
// CHECK: ![[LAMBDA1]] = !DICompositeType(tag: DW_TAG_class_type,
// CHECK: ![[LAMBDA1:[0-9]+]] = !DICompositeType(tag: DW_TAG_class_type,
// CHECK-SAME: name: "<lambda_1>",
// CHECK-SAME: flags: DIFlagFwdDecl
// CHECK: !DISubprogram(name: "operator()", scope: ![[LAMBDA1]],
c.lambda2();
}
Loading
Loading