Skip to content

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

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 2 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
57 changes: 56 additions & 1 deletion clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,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 @@ -3718,6 +3719,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 @@ -3765,7 +3767,8 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const EnumType *Ty) {

llvm::DIFile *DefUnit = getOrCreateFile(ED->getLocation());
unsigned Line = getLineNumber(ED->getLocation());
llvm::DIScope *EnumContext = getDeclContextDescriptor(ED);
llvm::DIScope *EnumContext =
PickCompositeTypeScope(getDeclContextDescriptor(ED), Identifier);
llvm::DIType *ClassTy = getOrCreateType(ED->getIntegerType(), DefUnit);
return DBuilder.createEnumerationType(
EnumContext, ED->getName(), DefUnit, Line, Size, Align, EltArray, ClassTy,
Expand Down Expand Up @@ -4097,6 +4100,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());

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 @@ -4134,6 +4188,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 @@ -501,6 +501,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);

/// Emit debug info for an extern function being called.
/// This is needed for call site debug info.
void EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke,
Expand Down
79 changes: 79 additions & 0 deletions clang/test/CodeGenCXX/debug-info-local-types.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -O0 -emit-llvm \
// RUN: -disable-llvm-passes -debug-info-kind=limited | FileCheck %s
//
// Test that types declared inside functions, that receive an "identifier"
// field used for ODR-uniquing, are placed inside the declaration DISubprogram
// for the function rather than the definition DISubprogram. This avoids
// later problems with distinct types in distinct DISubprograms being
// inadvertantly unique'd; see github PR 75385.
//
// NB: The types below are marked distinct, but other portions of LLVM
// force-unique them at a later date, see the enableDebugTypeODRUniquing
// feature. Clang doesn't enable that itself; instead this test ensures a safe
// representation of the types is produced.
//
// The check-lines below are not strictly in order of hierachy, so here's a
// diagram of what's desired:
//
// DIFile
// |
// Decl-DISubprogram "foo"
// / \
// / \
// Def-DISubprogram "foo" DICompositeType "bar"
// |
// |
// Decl-DISubprogram "get_a"
// / |
// / |
// Def-DISubprogram "get_a" DICompositeType "baz"
// |
// |
// {Def,Decl}-DISubprogram "get_b"

// CHECK: ![[FILENUM:[0-9]+]] = !DIFile(filename: "{{.*}}debug-info-local-types.cpp",

// CHECK: ![[BARSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar", scope: ![[FOOFUNC:[0-9]+]], file: ![[FILENUM]],
// CHECK-SAME: identifier: "_ZTSZ3foovE3bar")

// CHECK: ![[FOOFUNC]] = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: ![[FILENUM]], file: ![[FILENUM]],
//// Test to ensure that this is _not_ a definition, therefore a decl.
// CHECK-SAME: spFlags: 0)

// CHECK: ![[GETADECL:[0-9]+]] = !DISubprogram(name: "get_a", scope: ![[BARSTRUCT]], file: ![[FILENUM]],
//// Test to ensure that this is _not_ a definition, therefore a decl.
// CHECK-SAME: spFlags: 0)

// CHECK: ![[BAZSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "baz", scope: ![[GETADECL]], file: ![[FILENUM]],
// CHECK-SAME: identifier: "_ZTSZZ3foovEN3bar5get_aEvE3baz")
// CHECK: distinct !DISubprogram(name: "get_b",
// CHECK-SAME: scope: ![[BAZSTRUCT]], file: ![[FILENUM]],

inline int foo() {
class bar {
private:
int a = 0;
public:
int get_a() {
class baz {
private:
int b = 0;
public:
int get_b() {
return b;
}
};

static baz xyzzy;
return a + xyzzy.get_b();
}
};

static bar baz;
return baz.get_a();
}

int a() {
return foo();
}

124 changes: 124 additions & 0 deletions llvm/test/DebugInfo/local-odr-types-hierarchy.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
; RUN: opt %s -o - -S | FileCheck %s

; Paired with clang/test/CodeGenCXX/debug-info-local-types.cpp, this round-trips
; debug-info metadata for types that are ODR-uniqued, to ensure that the type
; hierachy does not change. See the enableDebugTypeODRUniquing feature: types
; that have the "identifier" field set will be unique'd based on their name,
; even if the "distinct" flag is set. Clang doesn't enable that itself, but opt
; does, therefore we pass the metadata through opt to check it doesn't change
; the type hiearchy.
;
; The check-lines below are not strictly in order of hierachy, so here's a
; diagram of what's desired:
;
; DIFile
; |
; Decl-DISubprogram "foo"
; / \
; / \
; Def-DISubprogram "foo" DICompositeType "bar"
; |
; |
; Decl-DISubprogram "get_a"
; / |
; / |
; Def-DISubprogram "get_a" DICompositeType "baz"
; |
; |
; {Def,Decl}-DISubprogram "get_b"
;
; The declaration DISubprograms are unique'd, and the DICompositeTypes should
; be in those scopes rather than the definition DISubprograms.

; CHECK: ![[FILENUM:[0-9]+]] = !DIFile(filename: "{{.*}}debug-info-local-types.cpp",

; CHECK: ![[BARSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar", scope: ![[FOOFUNC:[0-9]+]], file: ![[FILENUM]],
; CHECK-SAME: identifier: "_ZTSZ3foovE3bar")

; CHECK: ![[FOOFUNC]] = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: ![[FILENUM]], file: ![[FILENUM]],
;; Test to ensure that this is _not_ a definition, therefore a decl.
; CHECK-SAME: spFlags: 0)

; CHECK: ![[GETADECL:[0-9]+]] = !DISubprogram(name: "get_a", scope: ![[BARSTRUCT]], file: ![[FILENUM]],
;; Test to ensure that this is _not_ a definition, therefore a decl.
; CHECK-SAME: spFlags: 0)

; CHECK: ![[BAZSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "baz", scope: ![[GETADECL]], file: ![[FILENUM]],
; CHECK-SAME: identifier: "_ZTSZZ3foovEN3bar5get_aEvE3baz")
; CHECK: distinct !DISubprogram(name: "get_b",
; CHECK-SAME: scope: ![[BAZSTRUCT]], file: ![[FILENUM]],

%class.bar = type { i32 }
%class.baz = type { i32 }

$_Z3foov = comdat any

$_ZZ3foovEN3bar5get_aEv = comdat any

$_ZZZ3foovEN3bar5get_aEvEN3baz5get_bEv = comdat any

$_ZZ3foovE3baz = comdat any

$_ZZZ3foovEN3bar5get_aEvE5xyzzy = comdat any

@_ZZ3foovE3baz = linkonce_odr global %class.bar zeroinitializer, comdat, align 4, !dbg !0
@_ZZZ3foovEN3bar5get_aEvE5xyzzy = linkonce_odr global %class.baz zeroinitializer, comdat, align 4, !dbg !10

define dso_local noundef i32 @_Z1av() !dbg !32 {
entry:
unreachable
}

define linkonce_odr noundef i32 @_Z3foov() comdat !dbg !2 {
entry:
unreachable
}

define linkonce_odr noundef i32 @_ZZ3foovEN3bar5get_aEv(ptr noundef nonnull align 4 dereferenceable(4) %this) comdat align 2 !dbg !12 {
entry:
unreachable
}

define linkonce_odr noundef i32 @_ZZZ3foovEN3bar5get_aEvEN3baz5get_bEv(ptr noundef nonnull align 4 dereferenceable(4) %this) comdat align 2 !dbg !33 {
entry:
unreachable
}

!llvm.dbg.cu = !{!7}
!llvm.module.flags = !{!29, !30}
!llvm.ident = !{!31}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "baz", scope: !2, file: !3, line: 71, type: !13, isLocal: false, isDefinition: true)
!2 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 51, type: !4, scopeLine: 51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, declaration: !14)
!3 = !DIFile(filename: "debug-info-local-types.cpp", directory: ".")
!4 = !DISubroutineType(types: !5)
!5 = !{!6}
!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!7 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !8, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
!8 = !DIFile(filename: "<stdin>", directory: ".")
!9 = !{!0, !10}
!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
!11 = distinct !DIGlobalVariable(name: "xyzzy", scope: !12, file: !3, line: 66, type: !22, isLocal: false, isDefinition: true)
!12 = distinct !DISubprogram(name: "get_a", linkageName: "_ZZ3foovEN3bar5get_aEv", scope: !13, file: !3, line: 56, type: !18, scopeLine: 56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, declaration: !17, retainedNodes: !21)
!13 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar", scope: !14, file: !3, line: 52, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !15, identifier: "_ZTSZ3foovE3bar")
!14 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 51, type: !4, scopeLine: 51, flags: DIFlagPrototyped, spFlags: 0)
!15 = !{!16, !17}
!16 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !13, file: !3, line: 54, baseType: !6, size: 32)
!17 = !DISubprogram(name: "get_a", scope: !13, file: !3, line: 56, type: !18, scopeLine: 56, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0)
!18 = !DISubroutineType(types: !19)
!19 = !{!6, !20}
!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
!21 = !{}
!22 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "baz", scope: !17, file: !3, line: 57, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !23, identifier: "_ZTSZZ3foovEN3bar5get_aEvE3baz")
!23 = !{!24, !25}
!24 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !22, file: !3, line: 59, baseType: !6, size: 32)
!25 = !DISubprogram(name: "get_b", scope: !22, file: !3, line: 61, type: !26, scopeLine: 61, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0)
!26 = !DISubroutineType(types: !27)
!27 = !{!6, !28}
!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
!29 = !{i32 2, !"Debug Info Version", i32 3}
!30 = !{i32 1, !"wchar_size", i32 4}
!31 = !{!"clang"}
!32 = distinct !DISubprogram(name: "a", linkageName: "_Z1av", scope: !3, file: !3, line: 75, type: !4, scopeLine: 75, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7)
!33 = distinct !DISubprogram(name: "get_b", linkageName: "_ZZZ3foovEN3bar5get_aEvEN3baz5get_bEv", scope: !22, file: !3, line: 61, type: !26, scopeLine: 61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, declaration: !25, retainedNodes: !21)
Loading