Author: Vladislav Dzhidzhoev Date: 2026-02-09T16:23:42+01:00 New Revision: 90691641e6419c285671f183628a0e16792df43c
URL: https://github.com/llvm/llvm-project/commit/90691641e6419c285671f183628a0e16792df43c DIFF: https://github.com/llvm/llvm-project/commit/90691641e6419c285671f183628a0e16792df43c.diff LOG: [llvm][DebugInfo] Avoid attaching retained nodes to unrelated subprograms in DIBuilder (#180294) Fix a regression introduced by https://github.com/llvm/llvm-project/pull/165032, where DIBuilder could attach local metadata nodes to the wrong subprogram during finalization. DIBuilder records freshly created local variables, labels, and types in `DIBuilder::SubprogramTrackedNodes`, and later attaches them to their parent subprogram's retainedNodes in `finalizeSubprogram()`. However, a temporary local type created via `createReplaceableCompositeType()` may later be replaced by a type with a different scope. DIBuilder does not currently verify that the scopes of the original and replacement types match. As a result, local types can be incorrectly attached to the retainedNodes of an unrelated subprogram. This issue is observable in clang with limited debug info mode (see `clang/test/DebugInfo/CXX/ctor-homing-local-type.cpp`). This patch updates `DIBuilder::finalizeSubprogram()` to verify that tracked metadata nodes still belong to the subprogram being finalized, and avoids adding nodes whose scopes no longer match to retainedNodes field of an unrelated subprogram. Added: clang/test/DebugInfo/CXX/ctor-homing-local-type.cpp Modified: llvm/lib/IR/DIBuilder.cpp llvm/unittests/IR/IRBuilderTest.cpp Removed: ################################################################################ diff --git a/clang/test/DebugInfo/CXX/ctor-homing-local-type.cpp b/clang/test/DebugInfo/CXX/ctor-homing-local-type.cpp new file mode 100644 index 0000000000000..ba9e482c56f30 --- /dev/null +++ b/clang/test/DebugInfo/CXX/ctor-homing-local-type.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O0 -disable-llvm-passes %s -o - \ +// RUN: | FileCheck %s + +// When compiling this with limited debug info, a replaceable forward declaration DICompositeType +// for "n" is created in the scope of base object constructor (C2) of class l, and it's not immediately +// replaced with a distinct node (the type is not "completed"). +// Later, it gets replaced with distinct definition DICompositeType, which is created in the scope of +// complete object constructor (C1). +// +// In contrast to that, in standalone debug info mode, the complete definition DICompositeType +// for "n" is created sooner, right in the context of C2. +// +// Check that DIBuilder processes the limited debug info case correctly, and doesn't add the same +// local type to retainedNodes fields of both DISubprograms (C1 and C2). + +// CHECK: ![[C2:[0-9]+]] = distinct !DISubprogram(name: "l", linkageName: "_ZN1lC2Ev", {{.*}}, retainedNodes: ![[EMPTY:[0-9]+]]) +// CHECK: ![[EMPTY]] = !{} +// CHECK: ![[N:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "n", +// CHECK: ![[C1:[0-9]+]] = distinct !DISubprogram(name: "l", linkageName: "_ZN1lC1Ev", {{.*}}, retainedNodes: ![[RN:[0-9]+]]) +// CHECK: ![[RN]] = !{![[N]]} + +template <class d> +struct k { + void i() { + new d; + } +}; + +struct l { + l(); +}; + +l::l() { + struct n {}; + k<n> m; + m.i(); +} diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index ec6fd8e8b895a..38cf3f552b83d 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -53,10 +53,23 @@ void DIBuilder::trackIfUnresolved(MDNode *N) { void DIBuilder::finalizeSubprogram(DISubprogram *SP) { auto PN = SubprogramTrackedNodes.find(SP); - if (PN != SubprogramTrackedNodes.end()) - SP->replaceRetainedNodes( - MDTuple::get(VMContext, SmallVector<Metadata *, 16>(PN->second.begin(), - PN->second.end()))); + if (PN == SubprogramTrackedNodes.end()) + return; + + SmallVector<Metadata *, 16> RetainedNodes; + for (MDNode *N : PN->second) { + // If the tracked node N was temporary, and the DIBuilder user replaced it + // with a node that does not belong to SP or is non-local, do not add N to + // SP's retainedNodes list. + DILocalScope *Scope = dyn_cast_or_null<DILocalScope>( + DISubprogram::getRawRetainedNodeScope(N)); + if (!Scope || Scope->getSubprogram() != SP) + continue; + + RetainedNodes.push_back(N); + } + + SP->replaceRetainedNodes(MDTuple::get(VMContext, RetainedNodes)); } void DIBuilder::finalize() { diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp index 2d21e6f8b7148..4361594050668 100644 --- a/llvm/unittests/IR/IRBuilderTest.cpp +++ b/llvm/unittests/IR/IRBuilderTest.cpp @@ -1368,4 +1368,51 @@ TEST_F(IRBuilderTest, CTAD) { IRBuilder Builder7(BB, BB->end()); static_assert(std::is_same_v<decltype(Builder7), IRBuilder<>>); } + +TEST_F(IRBuilderTest, finalizeSubprogram) { + IRBuilder<> Builder(BB); + DIBuilder DIB(*M); + auto File = DIB.createFile("main.c", "/"); + auto CU = DIB.createCompileUnit( + DISourceLanguageName(dwarf::DW_LANG_C_plus_plus), File, "clang", + /*isOptimized=*/true, /*Flags=*/"", + /*Runtime Version=*/0); + auto FuncType = DIB.createSubroutineType(DIB.getOrCreateTypeArray({})); + auto FooSP = DIB.createFunction( + CU, "foo", /*LinkageName=*/"", File, + /*LineNo=*/1, FuncType, /*ScopeLine=*/2, DINode::FlagZero, + DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized); + + F->setSubprogram(FooSP); + AllocaInst *I = Builder.CreateAlloca(Builder.getInt8Ty()); + ReturnInst *R = Builder.CreateRetVoid(); + I->setDebugLoc(DILocation::get(Ctx, 3, 2, FooSP)); + R->setDebugLoc(DILocation::get(Ctx, 4, 2, FooSP)); + + auto BarSP = DIB.createFunction( + CU, "bar", /*LinkageName=*/"", File, + /*LineNo=*/1, FuncType, /*ScopeLine=*/2, DINode::FlagZero, + DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized); + + // Create a temporary structure in scope of FooSP. + llvm::TempDIType ForwardDeclaredType = + llvm::TempDIType(DIB.createReplaceableCompositeType( + llvm::dwarf::DW_TAG_structure_type, "MyType", FooSP, File, 0, 0, 8, 8, + {}, "UniqueIdentifier")); + + // Instantiate the real structure in scope of BarSP. + DICompositeType *Type = DIB.createStructType( + BarSP, "MyType", File, 0, 8, 8, {}, {}, {}, 0, {}, "UniqueIdentifier"); + // Replace the temporary type with the real type. + DIB.replaceTemporary(std::move(ForwardDeclaredType), Type); + + DIB.finalize(); + EXPECT_FALSE(verifyModule(*M)); + + // After finalization, MyType should appear in retainedNodes of BarSP, + // not in FooSP's. + EXPECT_EQ(BarSP->getRetainedNodes().size(), 1u); + EXPECT_EQ(BarSP->getRetainedNodes()[0], Type); + EXPECT_TRUE(FooSP->getRetainedNodes().empty()); +} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
