Skip to content

Commit 7c3707a

Browse files
committed
Reland "clang][DebugInfo] Emit global variable definitions for static data members with constant initializers (#70639)"
When an LLDB user asks for the value of a static data member, LLDB starts by searching the Names accelerator table for the corresponding variable definition DIE. For static data members with out-of-class definitions that works fine, because those get represented as global variables with a location and making them eligible to be added to the Names table. However, in-class definitions won’t get indexed because we usually don't emit global variables for them. So in DWARF we end up with a single `DW_TAG_member` that usually holds the constant initializer. But we don't get a corresponding CU-level `DW_TAG_variable` like we do for out-of-class definitions. To make it more convenient for debuggers to get to the value of inline static data members, this patch makes sure we emit definitions for static variables with constant initializers the same way we do for other static variables. This also aligns Clang closer to GCC, which produces CU-level definitions for inline statics and also emits these into `.debug_pubnames`. The implementation keeps track of newly created static data members. Then in `CGDebugInfo::finalize`, we emit a global `DW_TAG_variable` with a `DW_AT_const_value` for any of those declarations that didn't end up with a definition in the `DeclCache`. The newly emitted `DW_TAG_variable` will look as follows: ``` 0x0000007b: DW_TAG_structure_type DW_AT_calling_convention (DW_CC_pass_by_value) DW_AT_name ("Foo") ... 0x0000008d: DW_TAG_member DW_AT_name ("i") DW_AT_type (0x00000062 "const int") DW_AT_external (true) DW_AT_declaration (true) DW_AT_const_value (4) Newly added vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 0x0000009a: DW_TAG_variable DW_AT_specification (0x0000008d "i") DW_AT_const_value (4) DW_AT_linkage_name ("_ZN2t2IiE1iIfEE") ``` This patch also drops the `DW_AT_const_value` off of the declaration since we now always have it on the definition. This ensures that the `DWARFParallelLinker` can type-merge class with static members where we couldn't attach the constant on the declaration in some CUs.
1 parent 3c11ac5 commit 7c3707a

File tree

7 files changed

+197
-42
lines changed

7 files changed

+197
-42
lines changed

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,22 +1677,13 @@ CGDebugInfo::CreateRecordStaticField(const VarDecl *Var, llvm::DIType *RecordTy,
16771677

16781678
unsigned LineNumber = getLineNumber(Var->getLocation());
16791679
StringRef VName = Var->getName();
1680-
llvm::Constant *C = nullptr;
1681-
if (Var->getInit()) {
1682-
const APValue *Value = Var->evaluateValue();
1683-
if (Value) {
1684-
if (Value->isInt())
1685-
C = llvm::ConstantInt::get(CGM.getLLVMContext(), Value->getInt());
1686-
if (Value->isFloat())
1687-
C = llvm::ConstantFP::get(CGM.getLLVMContext(), Value->getFloat());
1688-
}
1689-
}
16901680

16911681
llvm::DINode::DIFlags Flags = getAccessFlag(Var->getAccess(), RD);
16921682
auto Align = getDeclAlignIfRequired(Var, CGM.getContext());
16931683
llvm::DIDerivedType *GV = DBuilder.createStaticMemberType(
1694-
RecordTy, VName, VUnit, LineNumber, VTy, Flags, C, Align);
1684+
RecordTy, VName, VUnit, LineNumber, VTy, Flags, /* Val */ nullptr, Align);
16951685
StaticDataMemberCache[Var->getCanonicalDecl()].reset(GV);
1686+
StaticDataMemberDefinitionsToEmit.push_back(Var->getCanonicalDecl());
16961687
return GV;
16971688
}
16981689

@@ -5596,6 +5587,39 @@ void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, const APValue &Init) {
55965587
TemplateParameters, Align));
55975588
}
55985589

5590+
void CGDebugInfo::EmitGlobalVariable(const VarDecl *VD) {
5591+
assert(VD->hasInit());
5592+
assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
5593+
if (VD->hasAttr<NoDebugAttr>())
5594+
return;
5595+
5596+
auto &GV = DeclCache[VD];
5597+
if (GV)
5598+
return;
5599+
5600+
auto const *InitVal = VD->evaluateValue();
5601+
if (!InitVal)
5602+
return;
5603+
5604+
llvm::DIFile *Unit = nullptr;
5605+
llvm::DIScope *DContext = nullptr;
5606+
unsigned LineNo;
5607+
StringRef DeclName, LinkageName;
5608+
QualType T;
5609+
llvm::MDTuple *TemplateParameters = nullptr;
5610+
collectVarDeclProps(VD, Unit, LineNo, T, DeclName, LinkageName,
5611+
TemplateParameters, DContext);
5612+
5613+
auto Align = getDeclAlignIfRequired(VD, CGM.getContext());
5614+
llvm::DINodeArray Annotations = CollectBTFDeclTagAnnotations(VD);
5615+
llvm::DIExpression *InitExpr = createConstantValueExpression(VD, *InitVal);
5616+
5617+
GV.reset(DBuilder.createGlobalVariableExpression(
5618+
TheCU, DeclName, LinkageName, Unit, LineNo, getOrCreateType(T, Unit),
5619+
true, true, InitExpr, getOrCreateStaticDataMemberDeclarationOrNull(VD),
5620+
TemplateParameters, Align, Annotations));
5621+
}
5622+
55995623
void CGDebugInfo::EmitExternalVariable(llvm::GlobalVariable *Var,
56005624
const VarDecl *D) {
56015625
assert(CGM.getCodeGenOpts().hasReducedDebugInfo());
@@ -5866,6 +5890,18 @@ void CGDebugInfo::finalize() {
58665890
DBuilder.replaceTemporary(std::move(FwdDecl), cast<llvm::MDNode>(Repl));
58675891
}
58685892

5893+
for (auto const *VD : StaticDataMemberDefinitionsToEmit) {
5894+
assert(VD->isStaticDataMember());
5895+
5896+
if (DeclCache.contains(VD))
5897+
continue;
5898+
5899+
if (!VD->hasInit())
5900+
continue;
5901+
5902+
EmitGlobalVariable(VD);
5903+
}
5904+
58695905
// We keep our own list of retained types, because we need to look
58705906
// up the final type in the type cache.
58715907
for (auto &RT : RetainedTypes)

clang/lib/CodeGen/CGDebugInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ class CGDebugInfo {
161161
llvm::DenseMap<const Decl *, llvm::TypedTrackingMDRef<llvm::DIDerivedType>>
162162
StaticDataMemberCache;
163163

164+
/// Keeps track of static data members for which we should emit a definition.
165+
std::vector<const VarDecl *> StaticDataMemberDefinitionsToEmit;
166+
164167
using ParamDecl2StmtTy = llvm::DenseMap<const ParmVarDecl *, const Stmt *>;
165168
using Param2DILocTy =
166169
llvm::DenseMap<const ParmVarDecl *, llvm::DILocalVariable *>;
@@ -526,6 +529,9 @@ class CGDebugInfo {
526529
/// Emit a constant global variable's debug info.
527530
void EmitGlobalVariable(const ValueDecl *VD, const APValue &Init);
528531

532+
/// Emit debug-info for a variable with a constant initializer.
533+
void EmitGlobalVariable(const VarDecl *VD);
534+
529535
/// Emit information about an external variable.
530536
void EmitExternalVariable(llvm::GlobalVariable *GV, const VarDecl *Decl);
531537

clang/test/CodeGenCXX/debug-info-class.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,19 @@ int main(int argc, char **argv) {
116116
// CHECK-SAME: DIFlagFwdDecl
117117
// CHECK-NOT: identifier:
118118
// CHECK-SAME: ){{$}}
119+
120+
// CHECK: !DIGlobalVariableExpression(var: ![[HDR_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 52, DW_OP_stack_value))
121+
// CHECK: ![[HDR_VAR]] = distinct !DIGlobalVariable(name: "HdrSize",
122+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[HDR_VAR_DECL:[0-9]+]])
123+
// CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int"
124+
// CHECK: ![[HDR_VAR_DECL]] = !DIDerivedType(tag: DW_TAG_member, name: "HdrSize"
125+
126+
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A"
127+
119128
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "I"
120129
// CHECK-NOT: DIFlagFwdDecl
121130
// CHECK-SAME: ){{$}}
122131

123-
// CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int"
124132
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
125133
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "bar"
126134
// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "baz"
@@ -186,8 +194,5 @@ int main(int argc, char **argv) {
186194
// CHECK: [[G_INNER_I]] = !DIDerivedType(tag: DW_TAG_member, name: "j"
187195
// CHECK-SAME: baseType: ![[INT]]
188196

189-
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "A"
190-
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "HdrSize"
191-
//
192197
// CHECK: ![[EXCEPTLOC]] = !DILocation(line: 100,
193198
// CHECK: ![[RETLOC]] = !DILocation(line: 99,
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// RUN: %clangxx -target arm64-apple-macosx11.0.0 -g %s -emit-llvm -S -o - | FileCheck --check-prefixes=CHECK %s
2+
3+
enum class Enum : int {
4+
VAL = -1
5+
};
6+
7+
struct Empty {};
8+
9+
constexpr auto func() { return 25; }
10+
11+
struct Foo {
12+
static constexpr int cexpr_int_with_addr = func();
13+
static constexpr int cexpr_int2 = func() + 1;
14+
static constexpr float cexpr_float = 2.0 + 1.0;
15+
static constexpr Enum cexpr_enum = Enum::VAL;
16+
static constexpr Empty cexpr_struct_with_addr{};
17+
static inline Enum inline_enum = Enum::VAL;
18+
19+
template<typename T>
20+
static constexpr T cexpr_template{};
21+
};
22+
23+
int main() {
24+
Foo f;
25+
26+
// Force global variable definitions to be emitted.
27+
(void)&Foo::cexpr_int_with_addr;
28+
(void)&Foo::cexpr_struct_with_addr;
29+
30+
return Foo::cexpr_int_with_addr + Foo::cexpr_float
31+
+ (int)Foo::cexpr_enum + Foo::cexpr_template<short>;
32+
}
33+
34+
// CHECK: @{{.*}}cexpr_int_with_addr{{.*}} =
35+
// CHECK-SAME: !dbg ![[INT_GLOBAL:[0-9]+]]
36+
37+
// CHECK: @{{.*}}cexpr_struct_with_addr{{.*}} =
38+
// CHECK-SAME !dbg ![[EMPTY_GLOBAL:[0-9]+]]
39+
40+
// CHECK: !DIGlobalVariableExpression(var: ![[INT_VAR:[0-9]+]], expr: !DIExpression())
41+
// CHECK: ![[INT_VAR]] = distinct !DIGlobalVariable(name: "cexpr_int_with_addr", linkageName:
42+
// CHECK-SAME: isLocal: false, isDefinition: true, declaration: ![[INT_DECL:[0-9]+]])
43+
44+
// CHECK: ![[INT_DECL]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_int_with_addr",
45+
// CHECK-SAME: flags: DIFlagStaticMember
46+
// CHECK-NOT: extraData:
47+
48+
// CHECK: ![[INT_DECL2:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_int2",
49+
// CHECK-SAME: flags: DIFlagStaticMember
50+
// CHECK-NOT: extraData:
51+
52+
// CHECK: ![[FLOAT_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_float",
53+
// CHECK-SAME: flags: DIFlagStaticMember
54+
// CHECK-NOT: extraData:
55+
56+
// CHECK: ![[ENUM_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_enum",
57+
// CHECK-SAME: flags: DIFlagStaticMember
58+
// CHECK-NOT: extraData:
59+
60+
// CHECK: ![[EMPTY_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_struct_with_addr",
61+
// CHECK-SAME: flags: DIFlagStaticMember
62+
// CHECK-NOT: extraData:
63+
64+
// CHECK: ![[IENUM_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "inline_enum",
65+
// CHECK-SAME: flags: DIFlagStaticMember
66+
// CHECK-NOT: extraData:
67+
68+
// CHECK: ![[TEMPLATE_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "cexpr_template",
69+
// CHECK-SAME: flags: DIFlagStaticMember
70+
// CHECK-NOT: extraData:
71+
72+
// CHECK: !DIGlobalVariableExpression(var: ![[EMPTY_VAR:[0-9]+]], expr: !DIExpression())
73+
// CHECK: ![[EMPTY_VAR]] = distinct !DIGlobalVariable(name: "cexpr_struct_with_addr", linkageName:
74+
// CHECK-SAME: isLocal: false, isDefinition: true, declaration: ![[EMPTY_DECL]])
75+
76+
// CHECK: !DIGlobalVariableExpression(var: ![[INT_VAR2:[0-9]+]], expr: !DIExpression(DW_OP_constu, 26, DW_OP_stack_value))
77+
// CHECK: ![[INT_VAR2]] = distinct !DIGlobalVariable(name: "cexpr_int2", linkageName:
78+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[INT_DECL2]])
79+
80+
// CHECK: !DIGlobalVariableExpression(var: ![[FLOAT_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, {{.*}}, DW_OP_stack_value))
81+
// CHECK: ![[FLOAT_VAR]] = distinct !DIGlobalVariable(name: "cexpr_float", linkageName:
82+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[FLOAT_DECL]])
83+
84+
// CHECK: !DIGlobalVariableExpression(var: ![[ENUM_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, {{.*}}, DW_OP_stack_value))
85+
// CHECK: ![[ENUM_VAR]] = distinct !DIGlobalVariable(name: "cexpr_enum", linkageName:
86+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[ENUM_DECL]])
87+
88+
// CHECK: !DIGlobalVariableExpression(var: ![[IENUM_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, {{.*}}, DW_OP_stack_value))
89+
// CHECK: ![[IENUM_VAR]] = distinct !DIGlobalVariable(name: "inline_enum", linkageName:
90+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[IENUM_DECL]])
91+
92+
// CHECK: !DIGlobalVariableExpression(var: ![[TEMPLATE_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 0, DW_OP_stack_value))
93+
// CHECK: ![[TEMPLATE_VAR]] = distinct !DIGlobalVariable(name: "cexpr_template", linkageName:
94+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[TEMPLATE_DECL]], templateParams: ![[TEMPLATE_PARMS:[0-9]+]])

clang/test/CodeGenCXX/debug-info-static-member.cpp

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,32 +63,32 @@ int C::a = 4;
6363
// CHECK-NOT: offset:
6464
// CHECK-SAME: flags: DIFlagStaticMember)
6565
//
66-
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "const_a"
67-
// CHECK-NOT: size:
68-
// CHECK-NOT: align:
69-
// CHECK-NOT: offset:
70-
// CHECK-SAME: flags: DIFlagStaticMember,
71-
// CHECK-SAME: extraData: i1 true)
72-
73-
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "const_b"
74-
// CHECK-NOT: size:
75-
// CHECK-NOT: align:
76-
// CHECK-NOT: offset:
77-
// CHECK-SAME: flags: DIFlagProtected | DIFlagStaticMember,
78-
// CHECK-SAME: extraData: float 0x{{.*}})
66+
// CHECK: ![[CONST_A_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "const_a"
67+
// CHECK-NOT: size:
68+
// CHECK-NOT: align:
69+
// CHECK-NOT: offset:
70+
// CHECK-SAME: flags: DIFlagStaticMember
71+
// CHECK-NOT: extraData:
72+
73+
// CHECK: ![[CONST_B_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "const_b"
74+
// CHECK-NOT: size:
75+
// CHECK-NOT: align:
76+
// CHECK-NOT: offset:
77+
// CHECK-SAME: flags: DIFlagProtected | DIFlagStaticMember
78+
// CHECK-NOT: extraData:
7979

8080
// CHECK: ![[DECL_C:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "c"
8181
// CHECK-NOT: size:
8282
// CHECK-NOT: align:
8383
// CHECK-NOT: offset:
8484
// CHECK-SAME: flags: DIFlagPublic | DIFlagStaticMember)
8585
//
86-
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "const_c"
87-
// CHECK-NOT: size:
88-
// CHECK-NOT: align:
89-
// CHECK-NOT: offset:
90-
// CHECK-SAME: flags: DIFlagPublic | DIFlagStaticMember,
91-
// CHECK-SAME: extraData: i32 18)
86+
// CHECK: ![[CONST_C_DECL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "const_c"
87+
// CHECK-NOT: size:
88+
// CHECK-NOT: align:
89+
// CHECK-NOT: offset:
90+
// CHECK-SAME: flags: DIFlagPublic | DIFlagStaticMember
91+
// CHECK-NOT: extraData:
9292
//
9393
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "x_a"
9494
// CHECK-SAME: flags: DIFlagPublic | DIFlagStaticMember)
@@ -144,7 +144,7 @@ struct V {
144144
// const_va is not emitted for MS targets.
145145
// NOT-MS: !DIDerivedType(tag: DW_TAG_member, name: "const_va",
146146
// NOT-MS-SAME: line: [[@LINE-5]]
147-
// NOT-MS-SAME: extraData: i32 42
147+
// NOT-MS-NOT: extraData:
148148
const int V::const_va;
149149

150150
namespace x {
@@ -156,3 +156,15 @@ struct y {
156156
};
157157
int y::z;
158158
}
159+
160+
// CHECK: !DIGlobalVariableExpression(var: ![[CONST_A_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 1, DW_OP_stack_value))
161+
// CHECK: ![[CONST_A_VAR]] = distinct !DIGlobalVariable(name: "const_a"
162+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[CONST_A_DECL]])
163+
164+
// CHECK: !DIGlobalVariableExpression(var: ![[CONST_B_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, {{.*}}, DW_OP_stack_value))
165+
// CHECK: ![[CONST_B_VAR]] = distinct !DIGlobalVariable(name: "const_b"
166+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[CONST_B_DECL]])
167+
168+
// CHECK: !DIGlobalVariableExpression(var: ![[CONST_C_VAR:[0-9]+]], expr: !DIExpression(DW_OP_constu, 18, DW_OP_stack_value))
169+
// CHECK: ![[CONST_C_VAR]] = distinct !DIGlobalVariable(name: "const_c"
170+
// CHECK-SAME: isLocal: true, isDefinition: true, declaration: ![[CONST_C_DECL]])

lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,10 @@ def test(self):
102102
# it does not crash.
103103
self.expect("image lookup -t A")
104104

105-
# dsymutil strips the debug info for classes that only have const static
106-
# data members without a definition namespace scope.
107-
@expectedFailureAll(debug_info=["dsym"])
105+
# For debug-info produced by older versions of clang, dsymutil strips the
106+
# debug info for classes that only have const static data members without
107+
# definitions.
108+
@expectedFailureAll(compiler=["clang"], compiler_version=["<", "18.0"])
108109
def test_class_with_only_const_static(self):
109110
self.build()
110111
lldbutil.run_to_source_breakpoint(

lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ def verify_values(self, verify_dict, actual, varref_dict=None, expression=None):
8787
def verify_variables(self, verify_dict, variables, varref_dict=None):
8888
for variable in variables:
8989
name = variable["name"]
90-
self.assertIn(
91-
name, verify_dict, 'variable "%s" in verify dictionary' % (name)
92-
)
93-
self.verify_values(verify_dict[name], variable, varref_dict)
90+
if not name.startswith("std::"):
91+
self.assertIn(
92+
name, verify_dict, 'variable "%s" in verify dictionary' % (name)
93+
)
94+
self.verify_values(verify_dict[name], variable, varref_dict)
9495

9596
def darwin_dwarf_missing_obj(self, initCommands):
9697
self.build(debug_info="dwarf")

0 commit comments

Comments
 (0)