Skip to content

Commit 15c8085

Browse files
authored
Reland "[lldb][DWARFASTParserClang] Fetch constant value from variable defintion if available" (#71800)
This patch relands #71004 which was reverted because the clang change it depends on was reverted. In addition to the original patch, this PR includes a change to `SymbolFileDWARF::ParseVariableDIE` to support CU-level variable definitions that don't have locations, but represent a constant value. Previously, when debug-maps were available, we would assume that a variable with "static lifetime" (which in this case means "has a linkage name") has a valid address, which isn't the case for non-locationed constants. We could omit this additional change if we stopped attaching linkage names to global non-locationed constants. Original commit message: """ #71780 proposes moving the `DW_AT_const_value` on inline static members from the declaration DIE to the definition DIE. This patch makes sure the LLDB's expression evaluator can continue to support static initialisers even if the declaration doesn't have a `DW_AT_const_value` anymore. Previously the expression evaluator would find the constant for a VarDecl from its declaration `DW_TAG_member` DIE. In cases where the initialiser was specified out-of-class, LLDB could find it during symbol resolution. However, neither of those will work for constants, since we don't have a constant attribute on the declaration anymore and we don't have constants in the symbol table. """ Depends on: * #71780
1 parent d4912e8 commit 15c8085

File tree

8 files changed

+164
-9
lines changed

8 files changed

+164
-9
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

+61-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "lldb/Symbol/SymbolFile.h"
3232
#include "lldb/Symbol/TypeList.h"
3333
#include "lldb/Symbol/TypeMap.h"
34+
#include "lldb/Symbol/VariableList.h"
3435
#include "lldb/Target/Language.h"
3536
#include "lldb/Utility/LLDBAssert.h"
3637
#include "lldb/Utility/Log.h"
@@ -139,6 +140,53 @@ static bool ShouldIgnoreArtificialField(llvm::StringRef FieldName) {
139140
return FieldName.starts_with("_vptr$")
140141
// gdb emit vtable pointer as "_vptr.classname"
141142
|| FieldName.starts_with("_vptr.");
143+
144+
std::optional<DWARFFormValue>
145+
DWARFASTParserClang::FindConstantOnVariableDefinition(DWARFDIE die) {
146+
assert(die.Tag() == llvm::dwarf::DW_TAG_member);
147+
148+
auto *dwarf = die.GetDWARF();
149+
if (!dwarf)
150+
return {};
151+
152+
ConstString name{die.GetName()};
153+
if (!name)
154+
return {};
155+
156+
auto *CU = die.GetCU();
157+
if (!CU)
158+
return {};
159+
160+
DWARFASTParser *dwarf_ast = dwarf->GetDWARFParser(*CU);
161+
auto parent_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die);
162+
163+
// Make sure we populate the GetDieToVariable cache.
164+
VariableList variables;
165+
dwarf->FindGlobalVariables(name, parent_decl_ctx, UINT_MAX, variables);
166+
167+
// The cache contains the variable definition whose DW_AT_specification
168+
// points to our declaration DIE. Look up that definition using our
169+
// declaration.
170+
auto const &die_to_var = dwarf->GetDIEToVariable();
171+
auto it = die_to_var.find(die.GetDIE());
172+
if (it == die_to_var.end())
173+
return {};
174+
175+
auto var_sp = it->getSecond();
176+
assert(var_sp != nullptr);
177+
178+
if (!var_sp->GetLocationIsConstantValueData())
179+
return {};
180+
181+
auto def = dwarf->GetDIE(var_sp->GetID());
182+
auto def_attrs = def.GetAttributes();
183+
DWARFFormValue form_value;
184+
if (!def_attrs.ExtractFormValueAtIndex(
185+
def_attrs.FindAttributeIndex(llvm::dwarf::DW_AT_const_value),
186+
form_value))
187+
return {};
188+
189+
return form_value;
142190
}
143191

144192
TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc,
@@ -2914,9 +2962,21 @@ void DWARFASTParserClang::ParseSingleMember(
29142962

29152963
bool unused;
29162964
// TODO: Support float/double static members as well.
2917-
if (!attrs.const_value_form || !ct.IsIntegerOrEnumerationType(unused))
2965+
if (!ct.IsIntegerOrEnumerationType(unused))
29182966
return;
29192967

2968+
// Newer versions of Clang don't emit the DW_AT_const_value
2969+
// on the declaration of an inline static data member. Instead
2970+
// it's attached to the definition DIE. If that's the case,
2971+
// try and fetch it.
2972+
if (!attrs.const_value_form) {
2973+
auto maybe_form_value = FindConstantOnVariableDefinition(die);
2974+
if (!maybe_form_value)
2975+
return;
2976+
2977+
attrs.const_value_form = *maybe_form_value;
2978+
}
2979+
29202980
llvm::Expected<llvm::APInt> const_value_or_err =
29212981
ExtractIntFromFormValue(ct, *attrs.const_value_form);
29222982
if (!const_value_or_err) {

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h

+11
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,17 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
373373
lldb_private::CompilerType &class_clang_type,
374374
const lldb::AccessType default_accesibility,
375375
lldb_private::ClangASTImporter::LayoutInfo &layout_info);
376+
377+
/// Tries to find the definition DW_TAG_variable DIE of the the specified
378+
/// DW_TAG_member 'die'. If such definition exists, returns the
379+
/// DW_AT_const_value of that definition if available. Returns std::nullopt
380+
/// otherwise.
381+
///
382+
/// In newer versions of clang, DW_AT_const_value attributes are not attached
383+
/// to the declaration of a inline static data-member anymore, but rather on
384+
/// its definition. This function is used to locate said constant.
385+
std::optional<lldb_private::plugin::dwarf::DWARFFormValue>
386+
FindConstantOnVariableDefinition(lldb_private::plugin::dwarf::DWARFDIE die);
376387
};
377388

378389
/// Parsed form of all attributes that are relevant for type reconstruction.

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -3564,9 +3564,8 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
35643564
}
35653565

35663566
// Prefer DW_AT_location over DW_AT_const_value. Both can be emitted e.g.
3567-
// for static constexpr member variables -- DW_AT_const_value will be
3568-
// present in the class declaration and DW_AT_location in the DIE defining
3569-
// the member.
3567+
// for static constexpr member variables -- DW_AT_const_value and
3568+
// DW_AT_location will both be present in the DIE defining the member.
35703569
bool location_is_const_value_data =
35713570
const_value_form.IsValid() && !location_form.IsValid();
35723571

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,11 @@ class SymbolFileDWARF : public SymbolFileCommon {
343343
return m_forward_decl_compiler_type_to_die;
344344
}
345345

346+
typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::VariableSP>
347+
DIEToVariableSP;
348+
349+
virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; }
350+
346351
virtual UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap();
347352

348353
bool ClassOrStructIsVirtual(const DWARFDIE &die);
@@ -362,9 +367,6 @@ class SymbolFileDWARF : public SymbolFileCommon {
362367
Type *ResolveTypeUID(const DIERef &die_ref);
363368

364369
protected:
365-
typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::VariableSP>
366-
DIEToVariableSP;
367-
368370
SymbolFileDWARF(const SymbolFileDWARF &) = delete;
369371
const SymbolFileDWARF &operator=(const SymbolFileDWARF &) = delete;
370372

@@ -488,8 +490,6 @@ class SymbolFileDWARF : public SymbolFileCommon {
488490

489491
void UpdateExternalModuleListIfNeeded();
490492

491-
virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; }
492-
493493
void BuildCuTranslationTable();
494494
std::optional<uint32_t> GetDWARFUnitIndex(uint32_t cu_idx);
495495

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,10 @@ SymbolFileDWARFDwo::GetDIE(const DIERef &die_ref) {
142142
return DebugInfo().GetDIE(die_ref);
143143
return GetBaseSymbolFile().GetDIE(die_ref);
144144
}
145+
146+
void SymbolFileDWARFDwo::FindGlobalVariables(
147+
ConstString name, const CompilerDeclContext &parent_decl_ctx,
148+
uint32_t max_matches, VariableList &variables) {
149+
GetBaseSymbolFile().FindGlobalVariables(name, parent_decl_ctx, max_matches,
150+
variables);
151+
}

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ class SymbolFileDWARFDwo : public SymbolFileDWARF {
5151
lldb::offset_t &offset,
5252
std::vector<Value> &stack) const override;
5353

54+
void FindGlobalVariables(ConstString name,
55+
const CompilerDeclContext &parent_decl_ctx,
56+
uint32_t max_matches,
57+
VariableList &variables) override;
58+
5459
protected:
5560
DIEToTypePtr &GetDIEToType() override;
5661

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

+53
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,38 @@ def test_class_with_only_const_static(self):
114114

115115
self.expect_expr("ClassWithOnlyConstStatic::member", result_value="3")
116116

117+
def check_global_var(self, name: str, expect_type, expect_val):
118+
var_list = self.target().FindGlobalVariables(name, lldb.UINT32_MAX)
119+
self.assertEqual(len(var_list), 1)
120+
varobj = var_list[0]
121+
self.assertEqual(varobj.type.name, expect_type)
122+
self.assertEqual(varobj.value, expect_val)
123+
124+
# For debug-info produced by older versions of clang, inline static data members
125+
# wouldn't get indexed into the Names accelerator table preventing LLDB from finding
126+
# them.
127+
@expectedFailureAll(compiler=["clang"], compiler_version=["<", "18.0"])
128+
def test_inline_static_members(self):
129+
self.build()
130+
lldbutil.run_to_source_breakpoint(
131+
self, "// break here", lldb.SBFileSpec("main.cpp")
132+
)
133+
134+
self.check_global_var("A::int_val", "const int", "1")
135+
self.check_global_var("A::int_val_with_address", "const int", "2")
136+
self.check_global_var("A::bool_val", "const bool", "true")
137+
self.check_global_var("A::enum_val", "Enum", "enum_case2")
138+
self.check_global_var("A::enum_bool_val", "EnumBool", "enum_bool_case1")
139+
self.check_global_var("A::scoped_enum_val", "ScopedEnum", "scoped_enum_case2")
140+
141+
self.check_global_var("ClassWithOnlyConstStatic::member", "const int", "3")
142+
143+
self.check_global_var("ClassWithConstexprs::member", "const int", "2")
144+
self.check_global_var("ClassWithConstexprs::enum_val", "Enum", "enum_case2")
145+
self.check_global_var(
146+
"ClassWithConstexprs::scoped_enum_val", "ScopedEnum", "scoped_enum_case2"
147+
)
148+
117149
# With older versions of Clang, LLDB fails to evaluate classes with only
118150
# constexpr members when dsymutil is enabled
119151
@expectedFailureAll(
@@ -139,3 +171,24 @@ def test_class_with_only_constexpr_static(self):
139171
self.expect_expr(
140172
"ClassWithEnumAlias::enum_alias_alias", result_value="scoped_enum_case1"
141173
)
174+
175+
def test_shadowed_static_inline_members(self):
176+
"""Tests that the expression evaluator and SBAPI can both
177+
correctly determine the requested inline static variable
178+
in the presence of multiple variables of the same name."""
179+
180+
self.build()
181+
lldbutil.run_to_name_breakpoint(self, "bar")
182+
183+
self.check_global_var("ns::Foo::mem", "const int", "10")
184+
185+
self.expect_expr("mem", result_value="10")
186+
self.expect_expr("Foo::mem", result_value="10")
187+
self.expect_expr("ns::Foo::mem", result_value="10")
188+
self.expect_expr("::Foo::mem", result_value="-29")
189+
190+
@expectedFailureAll(bugnumber="target var doesn't honour global namespace")
191+
def test_shadowed_static_inline_members_xfail(self):
192+
self.build()
193+
lldbutil.run_to_name_breakpoint(self, "bar")
194+
self.check_global_var("::Foo::mem", "const int", "-29")

lldb/test/API/lang/cpp/const_static_integral_member/main.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@ struct ClassWithEnumAlias {
8989
ScopedEnum::scoped_enum_case1;
9090
};
9191

92+
namespace ns {
93+
struct Foo {
94+
constexpr static int mem = 10;
95+
96+
void bar() { return; }
97+
};
98+
} // namespace ns
99+
100+
struct Foo {
101+
constexpr static int mem = -29;
102+
};
103+
104+
int func() {
105+
Foo f1;
106+
ns::Foo f2;
107+
f2.bar();
108+
return ns::Foo::mem + Foo::mem;
109+
}
110+
92111
int main() {
93112
A a;
94113

@@ -124,6 +143,7 @@ int main() {
124143

125144
auto enum_alias_val = ClassWithEnumAlias::enum_alias;
126145
auto enum_alias_alias_val = ClassWithEnumAlias::enum_alias_alias;
146+
auto ret = func();
127147

128148
return 0; // break here
129149
}

0 commit comments

Comments
 (0)