zequanwu updated this revision to Diff 465565.
zequanwu marked an inline comment as done.
zequanwu added a comment.
Updated to handle the nested anonymous union and struct situation.
It's done by:
1. Create the record layout with `Field` which could be a field member or an
anonymous struct or an anonymous union. If it's struct/union, it has `fields`
to refer to its members.
2. Create the AST from the record layout above.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D134849/new/
https://reviews.llvm.org/D134849
Files:
lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
lldb/test/Shell/SymbolFile/NativePDB/Inputs/class_layout.lldbinit
lldb/test/Shell/SymbolFile/NativePDB/class_layout.cpp
lldb/test/Shell/SymbolFile/NativePDB/global-classes.cpp
lldb/test/Shell/SymbolFile/NativePDB/packed_class_layout.cpp
lldb/test/Shell/SymbolFile/NativePDB/tag-types.cpp
Index: lldb/test/Shell/SymbolFile/NativePDB/tag-types.cpp
===================================================================
--- lldb/test/Shell/SymbolFile/NativePDB/tag-types.cpp
+++ lldb/test/Shell/SymbolFile/NativePDB/tag-types.cpp
@@ -242,6 +242,7 @@
// CHECK-NEXT: (lldb) type lookup -- Derived
// CHECK-NEXT: class Derived : public Class {
// CHECK-NEXT: public:
+// CHECK-NEXT: Derived();
// CHECK-NEXT: Derived &Reference;
// CHECK-NEXT: OneMember Member;
// CHECK-NEXT: const OneMember ConstMember;
Index: lldb/test/Shell/SymbolFile/NativePDB/packed_class_layout.cpp
===================================================================
--- lldb/test/Shell/SymbolFile/NativePDB/packed_class_layout.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// clang-format off
-// REQUIRES: lld, x86
-
-// Make sure class layout is correct.
-// RUN: %clang_cl --target=x86_64-windows-msvc -Od -Z7 -c /Fo%t.obj -- %s
-// RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb
-// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe \
-// RUN: -o "expr a" -o "expr b.c" -o "expr b.u.c" -o "expr b.u.i" -o "exit" | FileCheck %s
-
-// CHECK: (lldb) expr a
-// CHECK-NEXT: (A) $0 = (d1 = 'a', d2 = 1, d3 = 2, d4 = 'b')
-// CHECK-NEXT: (lldb) expr b.c
-// CHECK-NEXT: (char) $1 = 'a'
-// CHECK-NEXT: (lldb) expr b.u.c
-// CHECK-NEXT: (char[2]) $2 = "b"
-// CHECK-NEXT: (lldb) expr b.u.i
-// CHECK-NEXT: (int) $3 = 98
-
-struct __attribute__((packed, aligned(1))) A {
- char d1;
- int d2;
- int d3;
- char d4;
-};
-
-struct __attribute__((packed, aligned(1))) B {
- char c;
- union {
- char c[2];
- int i;
- } u;
-};
-
-A a = {'a', 1, 2, 'b'};
-B b = {'a', {"b"}};
-
-int main() {
- return 0;
-}
Index: lldb/test/Shell/SymbolFile/NativePDB/global-classes.cpp
===================================================================
--- lldb/test/Shell/SymbolFile/NativePDB/global-classes.cpp
+++ lldb/test/Shell/SymbolFile/NativePDB/global-classes.cpp
@@ -311,41 +311,41 @@
// CHECK: | `-EnumConstantDecl {{.*}} B 'EnumType'
// CHECK: |-CXXRecordDecl {{.*}} struct DerivedClass definition
// CHECK: | |-public 'BaseClass<int>'
-// CHECK: | |-FieldDecl {{.*}} DerivedMember 'int'
-// CHECK: | `-CXXConstructorDecl {{.*}} DerivedClass 'void (int, int)'
-// CHECK: | |-ParmVarDecl {{.*}} 'int'
-// CHECK: | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | |-CXXConstructorDecl {{.*}} DerivedClass 'void (int, int)'
+// CHECK: | | |-ParmVarDecl {{.*}} 'int'
+// CHECK: | | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | `-FieldDecl {{.*}} DerivedMember 'int'
// CHECK: |-VarDecl {{.*}} DC 'const DerivedClass'
// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<int> definition
-// CHECK: | |-FieldDecl {{.*}} BaseMember 'int'
-// CHECK: | `-CXXMethodDecl {{.*}} BaseClass 'void (int)'
-// CHECK: | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | |-CXXMethodDecl {{.*}} BaseClass 'void (int)'
+// CHECK: | | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
// CHECK: |-CXXRecordDecl {{.*}} struct EBO definition
// CHECK: | |-public 'EmptyBase'
-// CHECK: | |-FieldDecl {{.*}} Member 'int'
-// CHECK: | `-CXXConstructorDecl {{.*}} EBO 'void (int)'
-// CHECK: | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | |-CXXConstructorDecl {{.*}} EBO 'void (int)'
+// CHECK: | | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | `-FieldDecl {{.*}} Member 'int'
// CHECK: |-VarDecl {{.*}} EBOC 'const EBO'
// CHECK: |-CXXRecordDecl {{.*}} struct EmptyBase definition
// CHECK: |-CXXRecordDecl {{.*}} struct PaddedBases definition
// CHECK: | |-public 'BaseClass<char>'
// CHECK: | |-public 'BaseClass<short>'
// CHECK: | |-public 'BaseClass<int>'
-// CHECK: | |-FieldDecl {{.*}} DerivedMember 'long long'
-// CHECK: | `-CXXConstructorDecl {{.*}} PaddedBases 'void (char, short, int, long long)'
-// CHECK: | |-ParmVarDecl {{.*}} 'char'
-// CHECK: | |-ParmVarDecl {{.*}} 'short'
-// CHECK: | |-ParmVarDecl {{.*}} 'int'
-// CHECK: | `-ParmVarDecl {{.*}} 'long long'
+// CHECK: | |-CXXConstructorDecl {{.*}} PaddedBases 'void (char, short, int, long long)'
+// CHECK: | | |-ParmVarDecl {{.*}} 'char'
+// CHECK: | | |-ParmVarDecl {{.*}} 'short'
+// CHECK: | | |-ParmVarDecl {{.*}} 'int'
+// CHECK: | | `-ParmVarDecl {{.*}} 'long long'
+// CHECK: | `-FieldDecl {{.*}} DerivedMember 'long long'
// CHECK: |-VarDecl {{.*}} PBC 'const PaddedBases'
// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<char> definition
-// CHECK: | |-FieldDecl {{.*}} BaseMember 'int'
-// CHECK: | `-CXXMethodDecl {{.*}} BaseClass 'void (int)'
-// CHECK: | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | |-CXXMethodDecl {{.*}} BaseClass 'void (int)'
+// CHECK: | | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<short> definition
-// CHECK: | |-FieldDecl {{.*}} BaseMember 'int'
-// CHECK: | `-CXXMethodDecl {{.*}} BaseClass 'void (int)'
-// CHECK: | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | |-CXXMethodDecl {{.*}} BaseClass 'void (int)'
+// CHECK: | | `-ParmVarDecl {{.*}} 'int'
+// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
// CHECK: |-CXXRecordDecl {{.*}} struct <unnamed-type-UnnamedClassInstance> definition
// CHECK: | |-FieldDecl {{.*}} x 'int'
// CHECK: | `-FieldDecl {{.*}} EBOC 'EBO'
Index: lldb/test/Shell/SymbolFile/NativePDB/class_layout.cpp
===================================================================
--- /dev/null
+++ lldb/test/Shell/SymbolFile/NativePDB/class_layout.cpp
@@ -0,0 +1,184 @@
+// clang-format off
+// REQUIRES: lld, x86
+
+// Make sure class layout is correct.
+// RUN: %clang_cl --target=x86_64-windows-msvc -Od -Z7 -c /Fo%t.obj -- %s
+// RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb
+// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \
+// RUN: %p/Inputs/class_layout.lldbinit 2>&1 | FileCheck %s
+
+// CHECK: (lldb) expr a
+// CHECK-NEXT: (A) $0 = (d1 = 'a', d2 = 1, d3 = 2, d4 = 'b')
+// CHECK-NEXT: (lldb) expr b.c
+// CHECK-NEXT: (char) $1 = 'a'
+// CHECK-NEXT: (lldb) expr b.u.c
+// CHECK-NEXT: (char[2]) $2 = "b"
+// CHECK-NEXT: (lldb) expr b.u.i
+// CHECK-NEXT: (int) $3 = 98
+// CHECK-NEXT: (lldb) expr c
+// CHECK-NEXT: (C) $4 = {
+// CHECK-NEXT: c = 'a'
+// CHECK-NEXT: x = 65
+// CHECK-NEXT: = {
+// CHECK-NEXT: = {
+// CHECK-NEXT: c1 = 'b'
+// CHECK-NEXT: = {
+// CHECK-NEXT: = {
+// CHECK-NEXT: s3 = {
+// CHECK-NEXT: x = ([0] = 66, [1] = 67, [2] = 68)
+// CHECK-NEXT: }
+// CHECK-NEXT: c3 = 'c'
+// CHECK-NEXT: }
+// CHECK-NEXT: = {
+// CHECK-NEXT: c4 = 'B'
+// CHECK-NEXT: s4 = {
+// CHECK-NEXT: x = ([0] = 67, [1] = 68, [2] = 99)
+// CHECK-NEXT: }
+// CHECK-NEXT: s1 = {
+// CHECK-NEXT: x = ([0] = 69, [1] = 70, [2] = 71)
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: = {
+// CHECK-NEXT: s2 = {
+// CHECK-NEXT: x = ([0] = 98, [1] = 66, [2] = 67)
+// CHECK-NEXT: }
+// CHECK-NEXT: c2 = 'D'
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK: target module dump ast
+// CHECK: |-CXXRecordDecl {{.*}} <<invalid sloc>> <invalid sloc> struct C definition
+// CHECK-NEXT: | |-DefinitionData pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
+// CHECK-NEXT: | | |-DefaultConstructor exists trivial needs_implicit
+// CHECK-NEXT: | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | |-MoveConstructor exists simple trivial needs_implicit
+// CHECK-NEXT: | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | |-MoveAssignment exists simple trivial needs_implicit
+// CHECK-NEXT: | | `-Destructor simple irrelevant trivial needs_implicit
+// CHECK-NEXT: | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> c 'char'
+// CHECK-NEXT: | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> x 'int'
+// CHECK-NEXT: | |-CXXRecordDecl {{.*}} <<invalid sloc>> <invalid sloc> union definition
+// CHECK-NEXT: | | |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor
+// CHECK-NEXT: | | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
+// CHECK-NEXT: | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit
+// CHECK-NEXT: | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | |-MoveAssignment exists simple trivial needs_implicit
+// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit
+// CHECK-NEXT: | | |-CXXRecordDecl {{.*}} <<invalid sloc>> <invalid sloc> struct definition
+// CHECK-NEXT: | | | |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
+// CHECK-NEXT: | | | | |-DefaultConstructor exists trivial needs_implicit
+// CHECK-NEXT: | | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | |-MoveConstructor exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | |-MoveAssignment exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | `-Destructor simple irrelevant trivial needs_implicit
+// CHECK-NEXT: | | | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> c1 'char'
+// CHECK-NEXT: | | | |-CXXRecordDecl {{.*}} <<invalid sloc>> <invalid sloc> union definition
+// CHECK-NEXT: | | | | |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor
+// CHECK-NEXT: | | | | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
+// CHECK-NEXT: | | | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | | |-MoveConstructor exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | | |-MoveAssignment exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | | `-Destructor simple irrelevant trivial needs_implicit
+// CHECK-NEXT: | | | | |-CXXRecordDecl {{.*}} <<invalid sloc>> <invalid sloc> struct definition
+// CHECK-NEXT: | | | | | |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
+// CHECK-NEXT: | | | | | | |-DefaultConstructor exists trivial needs_implicit
+// CHECK-NEXT: | | | | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | | | |-MoveConstructor exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | | | |-MoveAssignment exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | | | `-Destructor simple irrelevant trivial needs_implicit
+// CHECK-NEXT: | | | | | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> s3 'S3'
+// CHECK-NEXT: | | | | | `-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> c3 'char'
+// CHECK-NEXT: | | | | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous struct)'
+// CHECK-NEXT: | | | | |-CXXRecordDecl {{.*}} <<invalid sloc>> <invalid sloc> struct definition
+// CHECK-NEXT: | | | | | |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
+// CHECK-NEXT: | | | | | | |-DefaultConstructor exists trivial needs_implicit
+// CHECK-NEXT: | | | | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | | | |-MoveConstructor exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | | | |-MoveAssignment exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | | | `-Destructor simple irrelevant trivial needs_implicit
+// CHECK-NEXT: | | | | | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> c4 'char'
+// CHECK-NEXT: | | | | | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> s4 'S3'
+// CHECK-NEXT: | | | | | `-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> s1 'S3'
+// CHECK-NEXT: | | | | `-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous struct)'
+// CHECK-NEXT: | | | `-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous union)'
+// CHECK-NEXT: | | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous struct)'
+// CHECK-NEXT: | | |-CXXRecordDecl {{.*}} <<invalid sloc>> <invalid sloc> struct definition
+// CHECK-NEXT: | | | |-DefinitionData is_anonymous pass_in_registers aggregate standard_layout trivially_copyable pod trivial literal
+// CHECK-NEXT: | | | | |-DefaultConstructor exists trivial needs_implicit
+// CHECK-NEXT: | | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | |-MoveConstructor exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
+// CHECK-NEXT: | | | | |-MoveAssignment exists simple trivial needs_implicit
+// CHECK-NEXT: | | | | `-Destructor simple irrelevant trivial needs_implicit
+// CHECK-NEXT: | | | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> s2 'S3'
+// CHECK-NEXT: | | | `-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> c2 'char'
+// CHECK-NEXT: | | `-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous struct)'
+// CHECK-NEXT: | |-FieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous union)'
+// CHECK-NEXT: | |-IndirectFieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous struct)'
+// CHECK-NEXT: | | |-Field {{.*}} '' 'C::(anonymous union)'
+// CHECK-NEXT: | | `-Field {{.*}} '' 'C::(anonymous struct)'
+// CHECK-NEXT: | `-IndirectFieldDecl {{.*}} <<invalid sloc>> <invalid sloc> implicit 'C::(anonymous struct)'
+// CHECK-NEXT: | |-Field {{.*}} '' 'C::(anonymous union)'
+// CHECK-NEXT: | `-Field {{.*}} '' 'C::(anonymous struct)'
+
+
+// Test packed stuct layout.
+struct __attribute__((packed, aligned(1))) A {
+ char d1;
+ int d2;
+ int d3;
+ char d4;
+};
+
+struct __attribute__((packed, aligned(1))) B {
+ char c;
+ union {
+ char c[2];
+ int i;
+ } u;
+};
+
+// Test struct layout with anonymous union/struct.
+struct S3 {
+ short x[3];
+};
+
+struct C {
+ char c;
+ int x;
+ union {
+ struct {
+ char c1;
+ union {
+ struct {
+ S3 s3;
+ char c3;
+ };
+ struct {
+ char c4;
+ S3 s4;
+ };
+ };
+ S3 s1;
+ };
+ struct {
+ S3 s2;
+ char c2;
+ };
+ };
+};
+
+A a = {'a', 1, 2, 'b'};
+B b = {'a', {"b"}};
+C c = {.c = 'a', .x = 65, .c1 = 'b', .s3 = {66, 67, 68}, .c3 = 'c', .s1={69, 70, 71}};
+
+int main() {
+ return 0;
+}
Index: lldb/test/Shell/SymbolFile/NativePDB/Inputs/class_layout.lldbinit
===================================================================
--- /dev/null
+++ lldb/test/Shell/SymbolFile/NativePDB/Inputs/class_layout.lldbinit
@@ -0,0 +1,7 @@
+expr a
+expr b.c
+expr b.u.c
+expr b.u.i
+expr c
+target module dump ast
+exit
Index: lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
===================================================================
--- lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
+++ lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h
@@ -9,13 +9,13 @@
#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H
+#include "PdbAstBuilder.h"
+#include "PdbSymUid.h"
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
-#include "PdbSymUid.h"
-
namespace clang {
class CXXBaseSpecifier;
class QualType;
@@ -47,6 +47,35 @@
llvm::codeview::EnumRecord er;
} m_cvr;
+ struct Field;
+ using FieldSP = std::shared_ptr<Field>;
+
+ struct Field {
+ enum Kind { FIELD, STRUCT, UNION } kind;
+ // Following are only used for field.
+ llvm::StringRef name;
+ uint64_t bit_offset;
+ uint64_t bit_size;
+ clang::QualType qt;
+ lldb::AccessType access;
+ uint32_t bitfield_width;
+ // Following are Only used for struct or union.
+ uint64_t base_offset;
+ llvm::SmallVector<FieldSP, 1> fields;
+
+ Field(Kind kind) : kind(kind) {}
+ Field(llvm::StringRef name, uint64_t bit_offset, uint64_t bit_size,
+ clang::QualType qt, lldb::AccessType access, uint32_t bitfield_width)
+ : kind(FIELD), name(name), bit_offset(bit_offset), bit_size(bit_size),
+ qt(qt), access(access), bitfield_width(bitfield_width) {}
+ void ConvertToStruct() {
+ kind = STRUCT;
+ base_offset = bit_offset;
+ fields.push_back(std::make_shared<Field>(name, bit_offset, bit_size, qt,
+ access, bitfield_width));
+ }
+ };
+
PdbTypeSymId m_id;
CompilerType &m_derived_ct;
clang::TagDecl &m_tag_decl;
@@ -54,14 +83,20 @@
PdbIndex &m_index;
std::vector<IndexedBase> m_bases;
ClangASTImporter::LayoutInfo m_layout;
+ llvm::DenseMap<clang::Decl *, DeclStatus> &m_decl_to_status;
llvm::DenseMap<lldb::opaque_compiler_type_t,
llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>>
&m_cxx_record_map;
+ std::map<uint64_t, llvm::SmallVector<FieldSP, 1>> m_fields;
+ // llvm::SmallVector<llvm::codeview::DataMemberRecord, 1> m_fields;
+ uint64_t start_offset = UINT64_MAX;
+ bool m_is_struct;
public:
UdtRecordCompleter(
PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl,
PdbAstBuilder &ast_builder, PdbIndex &index,
+ llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status,
llvm::DenseMap<lldb::opaque_compiler_type_t,
llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>,
8>> &cxx_record_map);
@@ -82,6 +117,11 @@
llvm::codeview::MemberAccess access,
llvm::codeview::MethodOptions options,
llvm::codeview::MemberAttributes attrs);
+ void FinishFields();
+ uint64_t AddField(TypeSystemClang &clang, Field *field, uint64_t bit_offset,
+ CompilerType parent_ct,
+ ClangASTImporter::LayoutInfo &parent_layout,
+ clang::DeclContext *decl_ctx);
};
} // namespace npdb
Index: lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
+++ lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp
@@ -6,14 +6,17 @@
#include "PdbUtil.h"
#include "Plugins/ExpressionParser/Clang/ClangASTImporter.h"
+#include "Plugins/ExpressionParser/Clang/ClangASTMetadata.h"
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "lldb/Core/Address.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Utility/LLDBAssert.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeIndex.h"
@@ -32,12 +35,14 @@
UdtRecordCompleter::UdtRecordCompleter(
PdbTypeSymId id, CompilerType &derived_ct, clang::TagDecl &tag_decl,
PdbAstBuilder &ast_builder, PdbIndex &index,
+ llvm::DenseMap<clang::Decl *, DeclStatus> &decl_to_status,
llvm::DenseMap<lldb::opaque_compiler_type_t,
llvm::SmallSet<std::pair<llvm::StringRef, CompilerType>, 8>>
&cxx_record_map)
: m_id(id), m_derived_ct(derived_ct), m_tag_decl(tag_decl),
m_ast_builder(ast_builder), m_index(index),
- m_cxx_record_map(cxx_record_map) {
+ m_decl_to_status(decl_to_status), m_cxx_record_map(cxx_record_map),
+ m_is_struct(false) {
CVType cvt = m_index.tpi().getType(m_id.index);
switch (cvt.kind()) {
case LF_ENUM:
@@ -51,6 +56,7 @@
case LF_STRUCTURE:
llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(cvt, m_cvr.cr));
m_layout.bit_size = m_cvr.cr.getSize() * 8;
+ m_is_struct = true;
break;
default:
llvm_unreachable("unreachable!");
@@ -247,17 +253,15 @@
if (member_qt.isNull())
return Error::success();
m_ast_builder.CompleteType(member_qt);
-
lldb::AccessType access = TranslateMemberAccess(data_member.getAccess());
-
- clang::FieldDecl *decl = TypeSystemClang::AddFieldToRecordType(
- m_derived_ct, data_member.Name, m_ast_builder.ToCompilerType(member_qt),
- access, bitfield_width);
- // FIXME: Add a PdbSymUid namespace for field list members and update
- // the m_uid_to_decl map with this decl.
-
- m_layout.field_offsets.insert(std::make_pair(decl, offset));
-
+ size_t field_size =
+ bitfield_width ? bitfield_width : GetSizeOfType(ti, m_index.tpi()) * 8;
+ if (field_size == 0)
+ return Error::success();
+ m_fields[offset].push_back(std::make_shared<Field>(
+ data_member.Name, offset, field_size, member_qt, access, bitfield_width));
+ if (start_offset > offset)
+ start_offset = offset;
return Error::success();
}
@@ -310,6 +314,7 @@
clang.TransferBaseClasses(m_derived_ct.GetOpaqueQualType(), std::move(bases));
clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType());
+ FinishFields();
TypeSystemClang::BuildIndirectFields(m_derived_ct);
TypeSystemClang::CompleteTagDeclarationDefinition(m_derived_ct);
@@ -317,3 +322,129 @@
m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, m_layout);
}
}
+
+uint64_t
+UdtRecordCompleter::AddField(TypeSystemClang &clang, Field *field,
+ uint64_t bit_offset, CompilerType parent_ct,
+ ClangASTImporter::LayoutInfo &parent_layout,
+ clang::DeclContext *parent_decl_ctx) {
+ static lldb::user_id_t anonymous_id = LLDB_INVALID_UID - 1;
+ clang::FieldDecl *field_decl = nullptr;
+ uint64_t bit_size = 0;
+ switch (field->kind) {
+ case Field::FIELD: {
+ field_decl = TypeSystemClang::AddFieldToRecordType(
+ parent_ct, field->name, m_ast_builder.ToCompilerType(field->qt),
+ field->access, field->bitfield_width);
+ bit_size = field->bit_size;
+ break;
+ };
+ case Field::STRUCT:
+ case Field::UNION: {
+ clang::TagTypeKind kind = field->kind == Field::STRUCT
+ ? clang::TagTypeKind::TTK_Struct
+ : clang::TagTypeKind::TTK_Union;
+ ClangASTMetadata metadata;
+ metadata.SetUserID(anonymous_id);
+ metadata.SetIsDynamicCXXType(false);
+ CompilerType record_ct = clang.CreateRecordType(
+ parent_decl_ctx, OptionalClangModuleID(), lldb::eAccessPublic, "", kind,
+ lldb::eLanguageTypeC_plus_plus, &metadata);
+ TypeSystemClang::StartTagDeclarationDefinition(record_ct);
+ ClangASTImporter::LayoutInfo layout;
+ clang::DeclContext *decl_ctx = clang.GetDeclContextForType(record_ct);
+ for (const auto &member : field->fields) {
+ uint64_t member_offset = field->kind == Field::STRUCT
+ ? member->bit_offset - field->base_offset
+ : 0;
+ uint64_t member_bit_size = AddField(clang, member.get(), member_offset,
+ record_ct, layout, decl_ctx);
+ if (field->kind == Field::STRUCT)
+ bit_size = std::max(bit_size, member_offset + member_bit_size);
+ else
+ bit_size = std::max(bit_size, member_bit_size);
+ }
+ layout.bit_size = bit_size;
+ TypeSystemClang::CompleteTagDeclarationDefinition(record_ct);
+ clang::RecordDecl *record_decl = clang.GetAsRecordDecl(record_ct);
+ m_ast_builder.GetClangASTImporter().SetRecordLayout(record_decl, layout);
+ field_decl = TypeSystemClang::AddFieldToRecordType(
+ parent_ct, "", record_ct, lldb::eAccessPublic, 0);
+ // Mark this record decl as completed.
+ DeclStatus status;
+ status.resolved = true;
+ status.uid = anonymous_id--;
+ m_decl_to_status.insert({record_decl, status});
+ break;
+ };
+ }
+ // FIXME: Add a PdbSymUid namespace for field list members and update
+ // the m_uid_to_decl map with this decl.
+ parent_layout.field_offsets.insert({field_decl, bit_offset});
+ return bit_size;
+}
+
+void UdtRecordCompleter::FinishFields() {
+ TypeSystemClang &clang = m_ast_builder.clang();
+ clang::DeclContext *decl_ctx =
+ m_ast_builder.GetOrCreateDeclContextForUid(m_id);
+
+ // For anonymous unions in a struct, msvc generated pdb doesn't have the
+ // entity for that union. So, we need to construct anonymous union and struct
+ // based on field offsets. The final AST is likely not matching the exact
+ // original AST, but the memory layout is preseved.
+
+ // The end offset to a field/struct/union that ends at the offset.
+ std::map<uint64_t, Field*> end_offset_map;
+ Field record(m_is_struct ? Field::STRUCT : Field::UNION);
+ for (const auto &pair: m_fields) {
+ uint64_t offset = pair.first;
+ const auto &fields = pair.second;
+ lldbassert(offset >= start_offset);
+ Field* parent = &record;
+ if (offset > start_offset) {
+ // Find the field with largest end offset that is <= new offset.
+ lldbassert(!end_offset_map.empty());
+ auto iter = end_offset_map.lower_bound(offset);
+ if (iter == end_offset_map.end())
+ --iter;
+ else if (iter->first > offset) {
+ if (iter == end_offset_map.begin())
+ continue;
+ --iter;
+ }
+ parent = iter->second;
+ }
+ // If it's a field, then the field is inside a union, so we can safely
+ // increase its size by converting it to a struct to hold multiple fields.
+ if (parent->kind == Field::FIELD)
+ parent->ConvertToStruct();
+
+ if (fields.size() == 1) {
+ uint64_t end_offset = offset + fields.back()->bit_size;
+ parent->fields.push_back(fields.back());
+ end_offset_map[end_offset] = parent;
+ } else {
+ if (parent->kind == Field::STRUCT) {
+ FieldSP anonymous_union = std::make_shared<Field>(Field::UNION);
+ for (const auto &field : fields) {
+ anonymous_union->fields.push_back(field);
+ end_offset_map[offset + field->bit_size] = field.get();
+ }
+ anonymous_union->bit_offset = offset;
+ parent->fields.push_back(anonymous_union);
+ } else {
+ for (const auto &field : fields) {
+ parent->fields.push_back(field);
+ end_offset_map[offset + field->bit_size] = field.get();
+ }
+ }
+ }
+ }
+ // Maybe we should check the construsted record size with the size in pdb. If
+ // they mismatch, it might be pdb has fields info missing.
+ for (const auto &field : record.fields) {
+ AddField(clang, field.get(), field->bit_offset, m_derived_ct, m_layout,
+ decl_ctx);
+ }
+}
Index: lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -1104,6 +1104,11 @@
return GetSizeOfTypeInternal<ClassRecord>(cvt);
case LF_UNION:
return GetSizeOfTypeInternal<UnionRecord>(cvt);
+ case LF_BITFIELD: {
+ BitFieldRecord record;
+ llvm::cantFail(TypeDeserializer::deserializeAs<BitFieldRecord>(cvt, record));
+ return GetSizeOfType({record.Type}, tpi);
+ }
default:
break;
}
Index: lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
+++ lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
@@ -477,7 +477,7 @@
// Visit all members of this class, then perform any finalization necessary
// to complete the class.
CompilerType ct = ToCompilerType(tag_qt);
- UdtRecordCompleter completer(best_ti, ct, tag, *this, index,
+ UdtRecordCompleter completer(best_ti, ct, tag, *this, index, m_decl_to_status,
m_cxx_record_map);
llvm::Error error =
llvm::codeview::visitMemberRecordStream(field_list.Data, completer);
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits