From 470de2bbec7f5fdd199775aa99c68ac76f4d5c74 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 6 Nov 2023 11:34:09 +0000 Subject: [PATCH] Reland "[lldb][DWARFASTParserClang] Fetch constant value from variable defintion if available (#71004)" https://github.com/llvm/llvm-project/pull/70639 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. **Testing** * If https://github.com/llvm/llvm-project/pull/70639 were to land without this patch then most of the `TestConstStaticIntegralMember.py` would start failing --- .../SymbolFile/DWARF/DWARFASTParserClang.cpp | 63 ++++++++++++++++++- .../SymbolFile/DWARF/DWARFASTParserClang.h | 11 ++++ .../SymbolFile/DWARF/SymbolFileDWARF.h | 10 +-- .../SymbolFile/DWARF/SymbolFileDWARFDwo.cpp | 7 +++ .../SymbolFile/DWARF/SymbolFileDWARFDwo.h | 5 ++ .../TestConstStaticIntegralMember.py | 21 +++++++ .../cpp/const_static_integral_member/main.cpp | 20 ++++++ 7 files changed, 131 insertions(+), 6 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 3174c18c97d888..4e41858674467a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -31,6 +31,7 @@ #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeMap.h" +#include "lldb/Symbol/VariableList.h" #include "lldb/Target/Language.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Log.h" @@ -133,6 +134,54 @@ static lldb::ModuleSP GetContainingClangModule(const DWARFDIE &die) { return lldb::ModuleSP(); } +std::optional +DWARFASTParserClang::FindConstantOnVariableDefinition(DWARFDIE die) { + assert(die.Tag() == llvm::dwarf::DW_TAG_member); + + auto *dwarf = die.GetDWARF(); + if (!dwarf) + return {}; + + ConstString name{die.GetName()}; + if (!name) + return {}; + + auto *CU = die.GetCU(); + if (!CU) + return {}; + + DWARFASTParser *dwarf_ast = dwarf->GetDWARFParser(*CU); + auto parent_decl_ctx = dwarf_ast->GetDeclContextContainingUIDFromDWARF(die); + + // Make sure we populate the GetDieToVariable cache. + VariableList variables; + dwarf->FindGlobalVariables(name, parent_decl_ctx, UINT_MAX, variables); + + // The cache contains the variable definition whose DW_AT_specification + // points to our declaration DIE. Look up that definition using our + // declaration. + auto const &die_to_var = dwarf->GetDIEToVariable(); + auto it = die_to_var.find(die.GetDIE()); + if (it == die_to_var.end()) + return {}; + + auto var_sp = it->getSecond(); + assert(var_sp != nullptr); + + if (!var_sp->GetLocationIsConstantValueData()) + return {}; + + auto def = dwarf->GetDIE(var_sp->GetID()); + auto def_attrs = def.GetAttributes(); + DWARFFormValue form_value; + if (!def_attrs.ExtractFormValueAtIndex( + def_attrs.FindAttributeIndex(llvm::dwarf::DW_AT_const_value), + form_value)) + return {}; + + return form_value; +} + TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc, const DWARFDIE &die, Log *log) { @@ -2906,9 +2955,21 @@ void DWARFASTParserClang::ParseSingleMember( bool unused; // TODO: Support float/double static members as well. - if (!attrs.const_value_form || !ct.IsIntegerOrEnumerationType(unused)) + if (!ct.IsIntegerOrEnumerationType(unused)) return; + // Newer versions of Clang don't emit the DW_AT_const_value + // on the declaration of an inline static data member. Instead + // it's attached to the definition DIE. If that's the case, + // try and fetch it. + if (!attrs.const_value_form) { + auto maybe_form_value = FindConstantOnVariableDefinition(die); + if (!maybe_form_value) + return; + + attrs.const_value_form = *maybe_form_value; + } + llvm::Expected const_value_or_err = ExtractIntFromFormValue(ct, *attrs.const_value_form); if (!const_value_or_err) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index c381c58fba7426..21fd6f9d7980ef 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -373,6 +373,17 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser { lldb_private::CompilerType &class_clang_type, const lldb::AccessType default_accesibility, lldb_private::ClangASTImporter::LayoutInfo &layout_info); + + /// Tries to find the definition DW_TAG_variable DIE of the the specified + /// DW_TAG_member 'die'. If such definition exists, returns the + /// DW_AT_const_value of that definition if available. Returns std::nullopt + /// otherwise. + /// + /// In newer versions of clang, DW_AT_const_value attributes are not attached + /// to the declaration of a inline static data-member anymore, but rather on + /// its definition. This function is used to locate said constant. + std::optional + FindConstantOnVariableDefinition(lldb_private::plugin::dwarf::DWARFDIE die); }; /// Parsed form of all attributes that are relevant for type reconstruction. diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 28430ccb87924b..e6efbba7e24990 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -343,6 +343,11 @@ class SymbolFileDWARF : public SymbolFileCommon { return m_forward_decl_compiler_type_to_die; } + typedef llvm::DenseMap + DIEToVariableSP; + + virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; } + virtual UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap(); bool ClassOrStructIsVirtual(const DWARFDIE &die); @@ -362,9 +367,6 @@ class SymbolFileDWARF : public SymbolFileCommon { Type *ResolveTypeUID(const DIERef &die_ref); protected: - typedef llvm::DenseMap - DIEToVariableSP; - SymbolFileDWARF(const SymbolFileDWARF &) = delete; const SymbolFileDWARF &operator=(const SymbolFileDWARF &) = delete; @@ -488,8 +490,6 @@ class SymbolFileDWARF : public SymbolFileCommon { void UpdateExternalModuleListIfNeeded(); - virtual DIEToVariableSP &GetDIEToVariable() { return m_die_to_variable_sp; } - void BuildCuTranslationTable(); std::optional GetDWARFUnitIndex(uint32_t cu_idx); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp index 223af281cd57db..ca698a84a9146d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp @@ -142,3 +142,10 @@ SymbolFileDWARFDwo::GetDIE(const DIERef &die_ref) { return DebugInfo().GetDIE(die_ref); return GetBaseSymbolFile().GetDIE(die_ref); } + +void SymbolFileDWARFDwo::FindGlobalVariables( + ConstString name, const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, VariableList &variables) { + GetBaseSymbolFile().FindGlobalVariables(name, parent_decl_ctx, max_matches, + variables); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h index fa8376507d6a89..9f5950e51b0c18 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h @@ -51,6 +51,11 @@ class SymbolFileDWARFDwo : public SymbolFileDWARF { lldb::offset_t &offset, std::vector &stack) const override; + void FindGlobalVariables(ConstString name, + const CompilerDeclContext &parent_decl_ctx, + uint32_t max_matches, + VariableList &variables) override; + protected: DIEToTypePtr &GetDIEToType() override; diff --git a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py index 78ea23ac8f7061..555ff750b97c9f 100644 --- a/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py +++ b/lldb/test/API/lang/cpp/const_static_integral_member/TestConstStaticIntegralMember.py @@ -139,3 +139,24 @@ def test_class_with_only_constexpr_static(self): self.expect_expr( "ClassWithEnumAlias::enum_alias_alias", result_value="scoped_enum_case1" ) + + def test_shadowed_static_inline_members(self): + """Tests that the expression evaluator and SBAPI can both + correctly determine the requested inline static variable + in the presence of multiple variables of the same name.""" + + self.build() + lldbutil.run_to_name_breakpoint(self, "bar") + + self.check_global_var("ns::Foo::mem", "const int", "10") + + self.expect_expr("mem", result_value="10") + self.expect_expr("Foo::mem", result_value="10") + self.expect_expr("ns::Foo::mem", result_value="10") + self.expect_expr("::Foo::mem", result_value="-29") + + @expectedFailureAll(bugnumber="target var doesn't honour global namespace") + def test_shadowed_static_inline_members_xfail(self): + self.build() + lldbutil.run_to_name_breakpoint(self, "bar") + self.check_global_var("::Foo::mem", "const int", "-29") diff --git a/lldb/test/API/lang/cpp/const_static_integral_member/main.cpp b/lldb/test/API/lang/cpp/const_static_integral_member/main.cpp index 4275f471df6aed..fb0b49e2d7aad7 100644 --- a/lldb/test/API/lang/cpp/const_static_integral_member/main.cpp +++ b/lldb/test/API/lang/cpp/const_static_integral_member/main.cpp @@ -89,6 +89,25 @@ struct ClassWithEnumAlias { ScopedEnum::scoped_enum_case1; }; +namespace ns { +struct Foo { + constexpr static int mem = 10; + + void bar() { return; } +}; +} // namespace ns + +struct Foo { + constexpr static int mem = -29; +}; + +int func() { + Foo f1; + ns::Foo f2; + f2.bar(); + return ns::Foo::mem + Foo::mem; +} + int main() { A a; @@ -124,6 +143,7 @@ int main() { auto enum_alias_val = ClassWithEnumAlias::enum_alias; auto enum_alias_alias_val = ClassWithEnumAlias::enum_alias_alias; + auto ret = func(); return 0; // break here }