diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -223,6 +223,7 @@ uint64_t bit_size = 0; uint64_t bit_offset = 0; bool is_bitfield = false; + clang::FieldDecl *field_decl = nullptr; FieldInfo() = default; @@ -275,6 +276,10 @@ const ParsedDWARFTypeAttributes &attrs); lldb::TypeSP ParsePointerToMemberType(const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs); + void FixupBaseClasses( + std::vector> &base_classes, + const lldb_private::ClangASTImporter::LayoutInfo &layout_info, + long byte_offset); /// Parses a DW_TAG_inheritance DIE into a base/super class. /// diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -1366,6 +1366,28 @@ return nullptr; } +void DWARFASTParserClang::FixupBaseClasses( + std::vector> &base_classes, + const ClangASTImporter::LayoutInfo &layout_info, long byte_offset) { + for (auto it = base_classes.rbegin(); it != base_classes.rend(); ++it) { + clang::CXXRecordDecl *prev_base_decl = + (*it)->getType()->getAsCXXRecordDecl(); + // We've already marked this class, exit. + if (prev_base_decl->isEmpty()) + break; + auto it_layout = layout_info.base_offsets.find(prev_base_decl); + if (it_layout == layout_info.base_offsets.end()) + continue; + // We found a normal base class, exit. + if (it_layout->second.getQuantity() < byte_offset) + break; + prev_base_decl->markEmpty(); + for (auto *field : prev_base_decl->fields()) + field->addAttr(clang::NoUniqueAddressAttr::Create(m_ast.getASTContext(), + clang::SourceRange())); + } +} + void DWARFASTParserClang::ParseInheritance( const DWARFDIE &die, const DWARFDIE &parent_die, const CompilerType class_clang_type, const AccessType default_accessibility, @@ -1460,8 +1482,6 @@ if (!result) return; - base_classes.push_back(std::move(result)); - if (is_virtual) { // Do not specify any offset for virtual inheritance. The DWARF // produced by clang doesn't give us a constant offset, but gives @@ -1476,10 +1496,18 @@ // be removed from LayoutRecordType() in the external // AST source in clang. } else { + // DWARF doesn't have any representation for [[no_unique_address]] + // attribute. Empty base classes with [[no_unique_address]] fields + // confuse lldb and prevent construction of object memory layout. + // To fix this we scan base classes in reverse order to determine + // overlapping offsets. Wnen found we consider such class as empty + // base with all fields having [[no_unique_address]] attribute. + FixupBaseClasses(base_classes, layout_info, member_byte_offset); layout_info.base_offsets.insert(std::make_pair( ast->GetAsCXXRecordDecl(base_class_clang_type.GetOpaqueQualType()), clang::CharUnits::fromQuantity(member_byte_offset))); } + base_classes.push_back(std::move(result)); } TypeSP DWARFASTParserClang::UpdateSymbolContextScopeForType( @@ -2991,7 +3019,7 @@ attrs.bit_size); m_ast.SetMetadataAsUserID(field_decl, die.GetID()); - + last_field_info.field_decl = field_decl; layout_info.field_offsets.insert( std::make_pair(field_decl, field_bit_offset)); } @@ -3007,6 +3035,7 @@ return false; FieldInfo last_field_info; + std::vector seen_fields; ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); auto ts = class_clang_type.GetTypeSystem(); @@ -3025,6 +3054,21 @@ case DW_TAG_member: ParseSingleMember(die, parent_die, class_clang_type, default_accessibility, layout_info, last_field_info); + if (!last_field_info.field_decl) + break; + for (auto it = seen_fields.rbegin(); it != seen_fields.rend(); ++it) { + FieldInfo &fi = *it; + if (fi.field_decl->hasAttr()) + break; + if (fi.bit_offset >= last_field_info.bit_offset) + fi.field_decl->addAttr(clang::NoUniqueAddressAttr::Create( + ast->getASTContext(), clang::SourceRange())); + else + break; + } + FixupBaseClasses(base_classes, layout_info, + last_field_info.bit_offset / 8); + seen_fields.push_back(last_field_info); break; case DW_TAG_subprogram: diff --git a/lldb/test/API/lang/cpp/no_unique_address/Makefile b/lldb/test/API/lang/cpp/no_unique_address/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/no_unique_address/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/no_unique_address/TestNoUniqueAddress.py b/lldb/test/API/lang/cpp/no_unique_address/TestNoUniqueAddress.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/no_unique_address/TestNoUniqueAddress.py @@ -0,0 +1,28 @@ +""" +Test that we correctly handle [[no_unique_address]] attribute. +""" + +import lldb + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestInlineNamespace(TestBase): + + def test(self): + self.build() + + lldbutil.run_to_source_breakpoint(self, + "// Set breakpoint here.", lldb.SBFileSpec("main.cpp")) + + self.expect_expr("_f1.a", result_type="long", result_value="42") + self.expect_expr("_f1.b", result_type="long", result_value="52") + self.expect_expr("_f2.v", result_type="long", result_value="42") + self.expect_expr("_f3.v", result_type="long", result_value="42") + self.expect_expr("_f4.v", result_type="long", result_value="42") + self.expect_expr("_f5.v1", result_type="long", result_value="42") + self.expect_expr("_f5.v2", result_type="long", result_value="52") + self.expect_expr("_f6.v1", result_type="long", result_value="42") + self.expect_expr("_f6.v2", result_type="long", result_value="52") diff --git a/lldb/test/API/lang/cpp/no_unique_address/main.cpp b/lldb/test/API/lang/cpp/no_unique_address/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/lang/cpp/no_unique_address/main.cpp @@ -0,0 +1,67 @@ +struct C +{ + long c,d; +}; + +struct Q +{ + long h; +}; + +struct D +{ +}; + +struct B +{ + [[no_unique_address]] D x; +}; + +struct E +{ + [[no_unique_address]] D x; +}; + +struct Foo1 : B,E,C +{ + long a = 42,b = 52; +} _f1; + +struct Foo2 : B,E +{ + long v = 42; +} _f2; + +struct Foo3 : C,B,E +{ + long v = 42; +} _f3; + +struct Foo4 : B,C,E,Q +{ + long v = 42; +} _f4; + +struct Foo5 : B,C,E +{ + [[no_unique_address]] D x1; + [[no_unique_address]] D x2; + long v1 = 42; + [[no_unique_address]] D y1; + [[no_unique_address]] D y2; + long v2 = 52; + [[no_unique_address]] D z1; + [[no_unique_address]] D z2; +} _f5; + +struct Foo6 : B,E +{ + long v1 = 42; + [[no_unique_address]] D y1; + [[no_unique_address]] D y2; + long v2 = 52; +} _f6; + +int main() { + return 0; // Set breakpoint here. +}