[clang] [llvm] [clang][DebugInfo] Allow function-local statics to be scoped within a lexical block (PR #213153)
Vladislav Dzhidzhoev via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 31 08:23:34 PDT 2026
https://github.com/dzhidzhoev updated https://github.com/llvm/llvm-project/pull/213153
>From 747a9f607f019f1316a60e2426712b7d5f002367 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Wed, 29 Jul 2026 22:35:09 +0200
Subject: [PATCH 1/2] [clang][DebugInfo] Allow function-local statics to be
scoped within a lexical block
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is a follow-up for https://github.com/llvm/llvm-project/pull/187927.
Currently, static local variables are created in DISubprogram scopes.
With this change, DIGlobalVariables for static locals are created in
the corresponding DILexicalBlocks. It helps debug info consumers to
distinguish betweeen static locals with the same name declared in
different lexical blocks.
Fixes https://github.com/llvm/llvm-project/issues/19612.
This change has already been reviewed in https://reviews.llvm.org/D125694,
but it was quite a while ago, so I’m opening a new PR for visibility.
Authored-by: Kristina Bessonova <kbessonova at accesssoftek.com>
---
clang/lib/CodeGen/CGDebugInfo.cpp | 8 +++
.../X86/debug-info-static-locals.cpp | 56 +++++++++++++++++++
2 files changed, 64 insertions(+)
create mode 100644 clang/test/DebugInfo/X86/debug-info-static-locals.cpp
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index f155761a1d998..4cf448041e246 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -4627,6 +4627,14 @@ void CGDebugInfo::collectVarDeclProps(const VarDecl *VD, llvm::DIFile *&Unit,
TemplateParameters = nullptr;
}
+ // Get context for static locals (that are technically globals) the same way
+ // we do for "local" locals -- by using current lexical block.
+ if (VD->isStaticLocal()) {
+ assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!");
+ VDContext = LexicalBlockStack.back();
+ return;
+ }
+
// Since we emit declarations (DW_AT_members) for static members, place the
// definition of those static members in the namespace they were declared in
// in the source code (the lexical decl context).
diff --git a/clang/test/DebugInfo/X86/debug-info-static-locals.cpp b/clang/test/DebugInfo/X86/debug-info-static-locals.cpp
new file mode 100644
index 0000000000000..2671564d25816
--- /dev/null
+++ b/clang/test/DebugInfo/X86/debug-info-static-locals.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -emit-llvm -debug-info-kind=limited %s -o - -O0 | FileCheck %s
+
+// Test that static local variables are emitted in correct scopes.
+
+void test() {
+ static int bar = 2;
+ {
+ static int bar = 1;
+ {
+ static int bar = 0;
+ }
+ }
+}
+
+// Automatic local variables in braceless arms of
+// if statement are placed in the same DILexicalBlock.
+// Test that the behavior for static locals is the same as
+// for automatic variables in that case.
+void test_braceless(int x) {
+ if (x)
+ static int foo = 30;
+ else
+ static int foo = 40;
+ if (x)
+#line 100
+ int foobar = 50;
+ else
+#line 200
+ int foobar = 60;
+}
+
+// CHECK: ![[FS_GVE:[0-9]+]] = !DIGlobalVariableExpression(var: ![[FS_GV:[0-9]+]]
+// CHECK: ![[FS_GV]] = distinct !DIGlobalVariable(name: "bar", scope: ![[FSCOPE:[0-9]+]]
+// CHECK: ![[FSCOPE]] = distinct !DISubprogram(name: "test"
+// CHECK-SAME: retainedNodes: ![[FS_DECLS:[0-9]+]]
+// CHECK: ![[FS_DECLS]] = !{![[FS_GVE]], ![[LB1_GVE:[0-9]+]], ![[LB2_GVE:[0-9]+]]}
+// CHECK: ![[LB1_GVE]] = !DIGlobalVariableExpression(var: ![[LB1_GV:[0-9]+]]
+// CHECK: ![[LB1_GV]] = distinct !DIGlobalVariable(name: "bar", scope: ![[LB1SCOPE:[0-9]+]]
+// CHECK: ![[LB1SCOPE]] = distinct !DILexicalBlock(scope: ![[FSCOPE]]
+// CHECK: ![[LB2_GVE]] = !DIGlobalVariableExpression(var: ![[LB2_GV:[0-9]+]]
+// CHECK: ![[LB2_GV]] = distinct !DIGlobalVariable(name: "bar", scope: ![[LB2SCOPE:[0-9]+]]
+// CHECK: ![[LB2SCOPE]] = distinct !DILexicalBlock(scope: ![[LB1SCOPE]]
+
+// CHECK: ![[B_LB1_GVE:[0-9]+]] = !DIGlobalVariableExpression(var: ![[B_LB1_GV:[0-9]+]]
+// CHECK: ![[B_LB1_GV]] = distinct !DIGlobalVariable(name: "foo", scope: ![[B_LBSCOPE:[0-9]+]]
+// CHECK: ![[B_LBSCOPE]] = distinct !DILexicalBlock(scope: ![[BSCOPE:[0-9]+]]
+// CHECK: ![[BSCOPE]] = distinct !DISubprogram(name: "test_braceless"
+// CHECK-SAME: retainedNodes: ![[B_DECLS:[0-9]+]]
+// CHECK: ![[B_DECLS]] = !{![[B_LB1_GVE]], ![[B_LB2_GVE:[0-9]+]]}
+// CHECK: ![[B_LB2_GVE]] = !DIGlobalVariableExpression(var: ![[B_LB2_GV:[0-9]+]]
+
+// CHECK: ![[LOCAL_VAR_SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: ![[BSCOPE]]
+// CHECK: ![[LOCAL_VAR1:[0-9]+]] = !DILocalVariable(name: "foobar", scope: ![[LOCAL_VAR_SCOPE]]
+// CHECK-SAME: line: 100
+// CHECK: ![[LOCAL_VAR2:[0-9]+]] = !DILocalVariable(name: "foobar", scope: ![[LOCAL_VAR_SCOPE]]
+// CHECK-SAME: line: 200
>From 5e5aa43d0d8078e8a8d263ef1c90de2790e5873b Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Fri, 31 Jul 2026 16:35:04 +0200
Subject: [PATCH 2/2] Add tests
---
.../X86/debug-info-thread-locals.cpp | 25 +++++++++++
.../dexter-tests/static-locals-in-lb.c | 43 +++++++++++++++++++
2 files changed, 68 insertions(+)
create mode 100644 clang/test/DebugInfo/X86/debug-info-thread-locals.cpp
create mode 100644 cross-project-tests/debuginfo-tests/dexter-tests/static-locals-in-lb.c
diff --git a/clang/test/DebugInfo/X86/debug-info-thread-locals.cpp b/clang/test/DebugInfo/X86/debug-info-thread-locals.cpp
new file mode 100644
index 0000000000000..507b8d9762a01
--- /dev/null
+++ b/clang/test/DebugInfo/X86/debug-info-thread-locals.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -emit-llvm -debug-info-kind=limited %s -o - -O0 | FileCheck %s
+
+// Test that thread-local variables are emitted in correct scopes.
+
+void test() {
+ thread_local int bar = 2;
+ {
+ thread_local int bar = 1;
+ {
+ thread_local int bar = 0;
+ }
+ }
+}
+
+// CHECK: ![[FS_GVE:[0-9]+]] = !DIGlobalVariableExpression(var: ![[FS_GV:[0-9]+]]
+// CHECK: ![[FS_GV]] = distinct !DIGlobalVariable(name: "bar", scope: ![[FSCOPE:[0-9]+]]
+// CHECK: ![[FSCOPE]] = distinct !DISubprogram(name: "test"
+// CHECK-SAME: retainedNodes: ![[FS_DECLS:[0-9]+]]
+// CHECK: ![[FS_DECLS]] = !{![[FS_GVE]], ![[LB1_GVE:[0-9]+]], ![[LB2_GVE:[0-9]+]]}
+// CHECK: ![[LB1_GVE]] = !DIGlobalVariableExpression(var: ![[LB1_GV:[0-9]+]]
+// CHECK: ![[LB1_GV]] = distinct !DIGlobalVariable(name: "bar", scope: ![[LB1SCOPE:[0-9]+]]
+// CHECK: ![[LB1SCOPE]] = distinct !DILexicalBlock(scope: ![[FSCOPE]]
+// CHECK: ![[LB2_GVE]] = !DIGlobalVariableExpression(var: ![[LB2_GV:[0-9]+]]
+// CHECK: ![[LB2_GV]] = distinct !DIGlobalVariable(name: "bar", scope: ![[LB2SCOPE:[0-9]+]]
+// CHECK: ![[LB2SCOPE]] = distinct !DILexicalBlock(scope: ![[LB1SCOPE]]
diff --git a/cross-project-tests/debuginfo-tests/dexter-tests/static-locals-in-lb.c b/cross-project-tests/debuginfo-tests/dexter-tests/static-locals-in-lb.c
new file mode 100644
index 0000000000000..d93d61707b61e
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/dexter-tests/static-locals-in-lb.c
@@ -0,0 +1,43 @@
+// This test case verifies that function-local static variables with the same name
+// declared in different lexical blocks are read correctly in the debugger.
+// REQUIRES: lldb
+// UNSUPPORTED: system-windows
+//
+// RUN: %clang -O0 -glldb %s -o %t
+// RUN: %dexter -w --binary %t %dexter_lldb_args -- %s | FileCheck %s
+
+int test(int x) {
+ int result;
+ if (x > 0) {
+ static int y = 1;
+ result = x + y; // !dex_label pos
+ } else {
+ static int y = 2;
+ result = x - y; // !dex_label neg
+ }
+ return result; // !dex_label ret
+}
+
+int main(int argc, const char **argv) {
+ test(5);
+ test(-3);
+ return 0;
+}
+
+// CHECK-DAG: seen_values: 2
+// CHECK-DAG: correct_steps: 2
+// CHECK-DAG: unexpected_value_steps: 0
+// CHECK-DAG: missing_var_steps: 2
+// CHECK-DAG: correct_step_coverage: 50.0%
+
+/*
+---
+!where {lines: !label pos}:
+ !value y: 1
+!where {lines: !label neg}:
+ !value y: 2
+# The value of y's should not be available out of declaration scopes.
+!where {lines: !label ret}:
+ !value y: null
+...
+*/
More information about the cfe-commits
mailing list