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

Reply via email to