From 7a5441002c9c3603957a5d580163fb162a82734b Mon Sep 17 00:00:00 2001 From: Vladislav Dzhidzhoev Date: Wed, 12 Nov 2025 21:04:21 +0100 Subject: [PATCH 1/3] [clang][DebugInfo] Clear retained nodes list of a vararg trunk's DISubprogram This fixes an issue reported in https://github.com/llvm/llvm-project/pull/166855#issuecomment-3518604073, that had been revealed after https://github.com/llvm/llvm-project/pull/166855 was merged. `CodeGenFunction::GenerateVarArgsThunk` creates thunks for vararg functions by cloning and modifying the function. It is different from `CodeGenFunction::generateThunk`, which is used for Itanium ABI. According to https://reviews.llvm.org/D39396, `CodeGenFunction::GenerateVarArgsThunk` may be called before metadata nodes are resolved. So, it tries to avoid remapping DISubprogram and all metadata nodes it references inside `CloneFunction()` by manually cloning DISubprogram. When optimization level is not OptNone, all local variables are saved in DISubprogram's retainedNodes field. When `CodeGenFunction::GenerateVarArgsThunk` clones such DISubprogram without remapping, it produces a subprogram with incorrectly-scoped retained nodes. It triggers Verifier checks added in https://github.com/llvm/llvm-project/pull/166855. To solve that, retained nodes list of a cloned DISubprogram is cleared. --- clang/lib/CodeGen/CGVTables.cpp | 3 +++ clang/test/CodeGenCXX/tmp-md-nodes1.cpp | 16 ++++++++++++++++ clang/test/CodeGenCXX/tmp-md-nodes2.cpp | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index e14e883a55ac5..736d1e4d2f1f5 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -125,6 +125,9 @@ static void resolveTopLevelMetadata(llvm::Function *Fn, if (!DIS) return; auto *NewDIS = llvm::MDNode::replaceWithDistinct(DIS->clone()); + // As DISubprogram remapping is avoided, clear retained nodes list of + // cloned DISubprogram from retained nodes local to original DISubprogram. + NewDIS->replaceRetainedNodes(llvm::MDTuple::get(Fn->getContext(), {})); VMap.MD()[DIS].reset(NewDIS); // Find all llvm.dbg.declare intrinsics and resolve the DILocalVariable nodes diff --git a/clang/test/CodeGenCXX/tmp-md-nodes1.cpp b/clang/test/CodeGenCXX/tmp-md-nodes1.cpp index 524b2c08c1ad5..f39dca3edaed1 100644 --- a/clang/test/CodeGenCXX/tmp-md-nodes1.cpp +++ b/clang/test/CodeGenCXX/tmp-md-nodes1.cpp @@ -2,6 +2,14 @@ // RUN: %clang_cc1 -O0 -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm %s -o - | \ // RUN: FileCheck %s +// Trigger GenerateVarArgsThunk. +// RUN: %clang_cc1 -O0 -triple riscv64-linux-gnu -debug-info-kind=limited -emit-llvm %s -o - | \ +// RUN: FileCheck %s + +// Check that retainedNodes are properly maintained at function cloning. +// RUN: %clang_cc1 -O1 -triple riscv64-linux-gnu -debug-info-kind=limited -emit-llvm %s -o - | \ +// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-DI + // This test simply checks that the varargs thunk is created. The failing test // case asserts. @@ -16,3 +24,11 @@ struct CharlieImpl : Charlie, Alpha { } delta; // CHECK: define {{.*}} void @_ZThn{{[48]}}_N11CharlieImpl5bravoEz( + +// CHECK-DI: distinct !DISubprogram({{.*}}, linkageName: "_ZN11CharlieImpl5bravoEz", {{.*}}, retainedNodes: [[RN1:![0-9]+]] +// A non-empty retainedNodes list of original DISubprogram. +// CHECK-DI: [[RN1]] = !{!{{.*}}} + +// CHECK-DI: distinct !DISubprogram({{.*}}, linkageName: "_ZN11CharlieImpl5bravoEz", {{.*}}, retainedNodes: [[EMPTY:![0-9]+]] +// An empty retainedNodes list of cloned DISubprogram. +// CHECK-DI: [[EMPTY]] = !{} diff --git a/clang/test/CodeGenCXX/tmp-md-nodes2.cpp b/clang/test/CodeGenCXX/tmp-md-nodes2.cpp index 8500cf3c42393..0c323ae4f58aa 100644 --- a/clang/test/CodeGenCXX/tmp-md-nodes2.cpp +++ b/clang/test/CodeGenCXX/tmp-md-nodes2.cpp @@ -2,6 +2,14 @@ // RUN: %clang_cc1 -O0 -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm %s -o - | \ // RUN: FileCheck %s +// Trigger GenerateVarArgsThunk. +// RUN: %clang_cc1 -O0 -triple riscv64-linux-gnu -debug-info-kind=limited -emit-llvm %s -o - | \ +// RUN: FileCheck %s + +// Check that retainedNodes are properly maintained at function cloning. +// RUN: %clang_cc1 -O1 -triple riscv64-linux-gnu -debug-info-kind=limited -emit-llvm %s -o - | \ +// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-DI + // This test simply checks that the varargs thunk is created. The failing test // case asserts. @@ -31,3 +39,11 @@ BOOL CBdVfsImpl::ReqCacheHint( CMsgAgent* p_ma, CACHE_HINT hint, ... ) { } // CHECK: define {{.*}} @_ZThn{{[48]}}_N10CBdVfsImpl12ReqCacheHintEP9CMsgAgentN3CFs10CACHE_HINTEz( + +// An empty retainedNodes list of cloned DISubprogram. +// CHECK-DI: [[EMPTY:![0-9]+]] = !{} +// CHECK-DI: distinct !DISubprogram({{.*}}, linkageName: "_ZN10CBdVfsImpl12ReqCacheHintEP9CMsgAgentN3CFs10CACHE_HINTEz", {{.*}}, retainedNodes: [[RN1:![0-9]+]] +// A non-empty retainedNodes list of original DISubprogram. +// CHECK-DI: [[RN1]] = !{!{{.*}}} + +// CHECK-DI: distinct !DISubprogram({{.*}}, linkageName: "_ZN10CBdVfsImpl12ReqCacheHintEP9CMsgAgentN3CFs10CACHE_HINTEz", {{.*}}, retainedNodes: [[EMPTY]] From 80f09c9842777673fa296b28fe6520a3b022cf1a Mon Sep 17 00:00:00 2001 From: Vladislav Dzhidzhoev Date: Thu, 13 Nov 2025 16:27:41 +0100 Subject: [PATCH 2/3] Add comment --- clang/lib/CodeGen/CGVTables.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index 736d1e4d2f1f5..71121177d8395 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -127,6 +127,9 @@ static void resolveTopLevelMetadata(llvm::Function *Fn, auto *NewDIS = llvm::MDNode::replaceWithDistinct(DIS->clone()); // As DISubprogram remapping is avoided, clear retained nodes list of // cloned DISubprogram from retained nodes local to original DISubprogram. + // FIXME: Information about optimized-out variables, labels, + // and local entities may be lost here, as retained nodes are not + // remapped. NewDIS->replaceRetainedNodes(llvm::MDTuple::get(Fn->getContext(), {})); VMap.MD()[DIS].reset(NewDIS); From d09b7b1cb7c077550d9471a0ce31029133a0eb04 Mon Sep 17 00:00:00 2001 From: Vladislav Dzhidzhoev Date: Fri, 14 Nov 2025 09:51:36 +0100 Subject: [PATCH 3/3] Update comment --- clang/lib/CodeGen/CGVTables.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index 71121177d8395..3fbac308a9178 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -127,9 +127,8 @@ static void resolveTopLevelMetadata(llvm::Function *Fn, auto *NewDIS = llvm::MDNode::replaceWithDistinct(DIS->clone()); // As DISubprogram remapping is avoided, clear retained nodes list of // cloned DISubprogram from retained nodes local to original DISubprogram. - // FIXME: Information about optimized-out variables, labels, - // and local entities may be lost here, as retained nodes are not - // remapped. + // FIXME: Thunk function signature is produced wrong in DWARF, as retained + // nodes are not remapped. NewDIS->replaceRetainedNodes(llvm::MDTuple::get(Fn->getContext(), {})); VMap.MD()[DIS].reset(NewDIS);