Add support for DW_AT_export_symbols for anonymous structs

Summary:
We add support for DW_AT_export_symbols to detect anonymous struct on top of the heuristics implemented in D66175
This should allow us to differentiate anonymous structs and unnamed structs.
We also fix TestTypeList.py which was incorrectly detecting an unnamed struct as an anonymous struct.

Differential Revision: https://reviews.llvm.org/D68961
This commit is contained in:
shafik 2019-10-28 14:26:54 -07:00
parent 85b718f53a
commit de2c7cab71
8 changed files with 122 additions and 14 deletions

View File

@ -261,7 +261,8 @@ public:
CompilerType CreateRecordType(clang::DeclContext *decl_ctx,
lldb::AccessType access_type, const char *name,
int kind, lldb::LanguageType language,
ClangASTMetadata *metadata = nullptr);
ClangASTMetadata *metadata = nullptr,
bool exports_symbols = false);
class TemplateParameterInfos {
public:

View File

@ -73,13 +73,17 @@ class TypeAndTypeListTestCase(TestBase):
self.assertTrue(enum_member)
self.DebugSBType(enum_member.type)
elif field.name == "my_type_is_nameless":
self.assertTrue(
self.assertFalse(
field.type.IsAnonymousType(),
"my_type_is_nameless has an anonymous type")
"my_type_is_nameless is not an anonymous type")
elif field.name == "my_type_is_named":
self.assertFalse(
field.type.IsAnonymousType(),
"my_type_is_named has a named type")
elif field.name == None:
self.assertTrue(
field.type.IsAnonymousType(),
"Nameless type is not anonymous")
# Pass an empty string. LLDB should not crash. :-)
fuzz_types = target.FindTypes(None)

View File

@ -15,6 +15,14 @@ public:
TASK_TYPE_1,
TASK_TYPE_2
} type;
// This struct is anonymous b/c it does not have a name
// and it is not unnamed class.
// Anonymous classes are a GNU extension.
struct {
int y;
};
// This struct is an unnamed class see [class.pre]p1
// http://eel.is/c++draft/class#pre-1.sentence-6
struct {
int x;
} my_type_is_nameless;

View File

@ -347,6 +347,9 @@ ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) {
case DW_AT_GNU_vector:
is_vector = form_value.Boolean();
break;
case DW_AT_export_symbols:
exports_symbols = form_value.Boolean();
break;
}
}
}
@ -1546,7 +1549,7 @@ DWARFASTParserClang::ParseStructureLikeDIE(const DWARFDIE &die,
clang_type_was_created = true;
clang_type = m_ast.CreateRecordType(
decl_ctx, attrs.accessibility, attrs.name.GetCString(), tag_decl_kind,
attrs.class_language, &metadata);
attrs.class_language, &metadata, attrs.exports_symbols);
}
}

View File

@ -181,6 +181,7 @@ struct ParsedDWARFTypeAttributes {
bool is_scoped_enum = false;
bool is_vector = false;
bool is_virtual = false;
bool exports_symbols = false;
clang::StorageClass storage = clang::SC_None;
const char *mangled_name = nullptr;
lldb_private::ConstString name;

View File

@ -1347,11 +1347,9 @@ CompilerType ClangASTContext::GetTypeForDecl(ObjCInterfaceDecl *decl) {
#pragma mark Structure, Unions, Classes
CompilerType ClangASTContext::CreateRecordType(DeclContext *decl_ctx,
AccessType access_type,
const char *name, int kind,
LanguageType language,
ClangASTMetadata *metadata) {
CompilerType ClangASTContext::CreateRecordType(
DeclContext *decl_ctx, AccessType access_type, const char *name, int kind,
LanguageType language, ClangASTMetadata *metadata, bool exports_symbols) {
ASTContext *ast = getASTContext();
assert(ast != nullptr);
@ -1402,10 +1400,7 @@ CompilerType ClangASTContext::CreateRecordType(DeclContext *decl_ctx,
// Anonymous classes is a GNU/MSVC extension that clang supports. It
// requires the anonymous class be embedded within a class. So the new
// heuristic verifies this condition.
//
// FIXME: An unnamed class within a class is also wrongly recognized as an
// anonymous struct.
if (isa<CXXRecordDecl>(decl_ctx))
if (isa<CXXRecordDecl>(decl_ctx) && exports_symbols)
decl->setAnonymousStructOrUnion(true);
}
@ -8989,7 +8984,7 @@ void ClangASTContext::DumpFromSymbolFile(Stream &s,
TypeSP type = type_list.GetTypeAtIndex(i);
if (!symbol_name.empty())
if (symbol_name.compare(type->GetName().GetStringRef()) != 0)
if (symbol_name != type->GetName().GetStringRef())
continue;
s << type->GetName().AsCString() << "\n";

View File

@ -0,0 +1,77 @@
; This test verifies that we do the right thing with DIFlagExportSymbols which is the new
; behavioir and without the DIFlagExportSymbols which is the old behavior for the given
; definitions below.
;
;```
; struct A{
; struct {
; int x;
; };
; struct {
; int y;
; };
; struct {
; int z;
; } unnamed;
; } a;
;```
;
; RUN: %clang++ -g -c -o %t.o %s
; RUN: lldb-test symbols -dump-clang-ast %t.o | FileCheck %s
; RUN: llvm-dwarfdump %t.o | FileCheck %s --check-prefix DWARFDUMP
%struct.A = type { %struct.anon, %struct.anon.0, %struct.anon.1 }
%struct.anon = type { i32 }
%struct.anon.0 = type { i32 }
%struct.anon.1 = type { i32 }
@a = global %struct.A zeroinitializer, align 4, !dbg !0
!llvm.module.flags = !{!21, !22}
!llvm.dbg.cu = !{!2}
!llvm.ident = !{!23}
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 11, type: !6, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 10.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None)
!3 = !DIFile(filename: "anon_old_new.cpp", directory: "/dir")
!4 = !{}
!5 = !{!0}
!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !3, line: 1, size: 96, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTS1A")
; CHECK: struct A definition
!7 = !{!8, !13, !17}
!8 = !DIDerivedType(tag: DW_TAG_member, scope: !6, file: !3, line: 2, baseType: !9, size: 32)
!9 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !6, file: !3, line: 2, size: 32, flags: DIFlagExportSymbols | DIFlagTypePassByValue, elements: !10, identifier: "_ZTSN1AUt_E")
; Correctly identify an anonymous class with DIFlagExportSymbols
; CHECK: struct definition
; CHECK: DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
!10 = !{!11}
!11 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !9, file: !3, line: 3, baseType: !12, size: 32)
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !DIDerivedType(tag: DW_TAG_member, scope: !6, file: !3, line: 5, baseType: !14, size: 32, offset: 32)
!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !6, file: !3, line: 5, size: 32, flags: DIFlagTypePassByValue, elements: !15, identifier: "_ZTSN1AUt0_E")
; Correctly identify an anonymous class without DIFlagExportSymbols
; This works b/c we have additional checks when we fields to A.
; CHECK: struct definition
; CHECK: DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
!15 = !{!16}
!16 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !14, file: !3, line: 6, baseType: !12, size: 32)
!17 = !DIDerivedType(tag: DW_TAG_member, name: "unnamed", scope: !6, file: !3, line: 10, baseType: !18, size: 32, offset: 64)
!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, scope: !6, file: !3, line: 8, size: 32, flags: DIFlagTypePassByValue, elements: !19, identifier: "_ZTSN1AUt1_E")
; Correctly identify an unamed class
; CHECK: struct definition
; CHECK: DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
!19 = !{!20}
!20 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !18, file: !3, line: 9, baseType: !12, size: 32)
!21 = !{i32 2, !"Dwarf Version", i32 4}
!22 = !{i32 2, !"Debug Info Version", i32 3}
!23 = !{!"clang version 10.0.0"}
; DWARFDUMP: DW_TAG_structure_type
; DWARFDUMP: DW_AT_name ("A")
;
; DWARFDUMP: DW_TAG_structure_type
; DWARFDUMP: DW_AT_export_symbols (true)
;
; DWARFDUMP: DW_TAG_structure_type
; DWARFDUMP-NOT: DW_AT_export_symbols (true)

View File

@ -0,0 +1,19 @@
// Test to verify we are corectly generating anonymous flags when parsing
// anonymous class and unnamed structs from DWARF to the a clang AST node.
// RUN: %clang++ -g -c -o %t.o %s
// RUN: lldb-test symbols -dump-clang-ast %t.o | FileCheck %s
struct A {
struct {
int x;
};
struct {
int y;
} C;
} a;
// CHECK: A::(anonymous struct)
// CHECK: |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
// CHECK: A::(anonymous struct)
// CHECK: |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal