diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 5772c07154144..5d435ff1e20a7 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -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); @@ -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)); @@ -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, @@ -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(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(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(ToCheck)); + if (auto *DISP = dyn_cast(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(); @@ -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. diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index 855881744237c..fc51cea5bc1f6 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -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, diff --git a/clang/test/CodeGenCXX/debug-info-local-types.cpp b/clang/test/CodeGenCXX/debug-info-local-types.cpp new file mode 100644 index 0000000000000..5f3a0efa7526d --- /dev/null +++ b/clang/test/CodeGenCXX/debug-info-local-types.cpp @@ -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(); +} + diff --git a/llvm/test/DebugInfo/local-odr-types-hierarchy.ll b/llvm/test/DebugInfo/local-odr-types-hierarchy.ll new file mode 100644 index 0000000000000..3c37816b68650 --- /dev/null +++ b/llvm/test/DebugInfo/local-odr-types-hierarchy.ll @@ -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: "", 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)