mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-27 15:41:46 +00:00
[lldb] Support recursive record types in CTF
Support recursive record types in CTF, for example a struct that contains a pointer to itself: struct S { struct S *n; }; We are now more lazy when creating LLDB types. When encountering a record type (struct or union) we create a forward declaration and only complete it when requested. Differential revision: https://reviews.llvm.org/D156498
This commit is contained in:
parent
c835dcaccf
commit
12f3d97fc6
@ -503,26 +503,59 @@ SymbolFileCTF::CreateFunction(const CTFFunction &ctf_function) {
|
||||
llvm::Expected<lldb::TypeSP>
|
||||
SymbolFileCTF::CreateRecord(const CTFRecord &ctf_record) {
|
||||
const clang::TagTypeKind tag_kind = TranslateRecordKind(ctf_record.kind);
|
||||
|
||||
CompilerType record_type =
|
||||
m_ast->CreateRecordType(nullptr, OptionalClangModuleID(), eAccessPublic,
|
||||
ctf_record.name.data(), tag_kind, eLanguageTypeC);
|
||||
|
||||
m_ast->StartTagDeclarationDefinition(record_type);
|
||||
for (const CTFRecord::Field &field : ctf_record.fields) {
|
||||
if (Type *field_type = ResolveTypeUID(field.type)) {
|
||||
const uint32_t field_size = field_type->GetByteSize(nullptr).value_or(0);
|
||||
TypeSystemClang::AddFieldToRecordType(record_type, field.name,
|
||||
field_type->GetFullCompilerType(),
|
||||
eAccessPublic, field_size);
|
||||
}
|
||||
}
|
||||
m_ast->CompleteTagDeclarationDefinition(record_type);
|
||||
|
||||
m_compiler_types[record_type.GetOpaqueQualType()] = &ctf_record;
|
||||
Declaration decl;
|
||||
return MakeType(ctf_record.uid, ConstString(ctf_record.name), ctf_record.size,
|
||||
nullptr, LLDB_INVALID_UID, lldb_private::Type::eEncodingIsUID,
|
||||
decl, record_type, lldb_private::Type::ResolveState::Full);
|
||||
decl, record_type, lldb_private::Type::ResolveState::Forward);
|
||||
}
|
||||
|
||||
bool SymbolFileCTF::CompleteType(CompilerType &compiler_type) {
|
||||
// Check if we have a CTF type for the given incomplete compiler type.
|
||||
auto it = m_compiler_types.find(compiler_type.GetOpaqueQualType());
|
||||
if (it == m_compiler_types.end())
|
||||
return false;
|
||||
|
||||
const CTFType *ctf_type = it->second;
|
||||
assert(ctf_type && "m_compiler_types should only contain valid CTF types");
|
||||
|
||||
// We only support resolving record types.
|
||||
assert(ctf_type->kind == CTFType::Kind::eStruct ||
|
||||
ctf_type->kind == CTFType::Kind::eUnion);
|
||||
|
||||
// Cast to the appropriate CTF type.
|
||||
const CTFRecord *ctf_record = static_cast<const CTFRecord *>(ctf_type);
|
||||
|
||||
// If any of the fields are incomplete, we cannot complete the type.
|
||||
for (const CTFRecord::Field &field : ctf_record->fields) {
|
||||
if (!ResolveTypeUID(field.type)) {
|
||||
LLDB_LOG(GetLog(LLDBLog::Symbols),
|
||||
"Cannot complete type {0} because field {1} is incomplete",
|
||||
ctf_type->uid, field.type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Complete the record type.
|
||||
m_ast->StartTagDeclarationDefinition(compiler_type);
|
||||
for (const CTFRecord::Field &field : ctf_record->fields) {
|
||||
Type *field_type = ResolveTypeUID(field.type);
|
||||
assert(field_type && "field must be complete");
|
||||
const uint32_t field_size = field_type->GetByteSize(nullptr).value_or(0);
|
||||
TypeSystemClang::AddFieldToRecordType(compiler_type, field.name,
|
||||
field_type->GetFullCompilerType(),
|
||||
eAccessPublic, field_size);
|
||||
}
|
||||
m_ast->CompleteTagDeclarationDefinition(compiler_type);
|
||||
|
||||
// Now that the compiler type is no longer incomplete we don't need to
|
||||
// remember it anymore.
|
||||
m_compiler_types.erase(compiler_type.GetOpaqueQualType());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::TypeSP>
|
||||
@ -960,7 +993,6 @@ lldb_private::Type *SymbolFileCTF::ResolveTypeUID(lldb::user_id_t type_uid) {
|
||||
if (!ctf_type)
|
||||
return nullptr;
|
||||
|
||||
m_types[type_uid] = TypeSP();
|
||||
Log *log = GetLog(LLDBLog::Symbols);
|
||||
|
||||
llvm::Expected<TypeSP> type_or_error = CreateType(ctf_type);
|
||||
|
@ -93,7 +93,7 @@ public:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool CompleteType(CompilerType &compiler_type) override { return false; }
|
||||
bool CompleteType(CompilerType &compiler_type) override;
|
||||
|
||||
uint32_t ResolveSymbolContext(const lldb_private::Address &so_addr,
|
||||
lldb::SymbolContextItem resolve_scope,
|
||||
@ -247,6 +247,11 @@ private:
|
||||
|
||||
std::vector<std::unique_ptr<CTFType>> m_ctf_types;
|
||||
|
||||
/// To complete types, we need a way to map (imcomplete) compiler types back
|
||||
/// to parsed CTF types.
|
||||
llvm::DenseMap<lldb::opaque_compiler_type_t, const CTFType *>
|
||||
m_compiler_types;
|
||||
|
||||
llvm::DenseMap<lldb::user_id_t, lldb::TypeSP> m_types;
|
||||
|
||||
std::vector<lldb::FunctionSP> m_functions;
|
||||
|
@ -91,3 +91,5 @@ class TestCTF(TestBase):
|
||||
"}",
|
||||
],
|
||||
)
|
||||
|
||||
self.expect("type lookup RecursiveStruct", substrs=["RecursiveStruct *n;"])
|
||||
|
@ -36,9 +36,14 @@ struct LargeStruct {
|
||||
int b;
|
||||
};
|
||||
|
||||
struct RecursiveStruct {
|
||||
struct RecursiveStruct *n;
|
||||
};
|
||||
|
||||
MyStructT foo;
|
||||
struct ForwardDecl *forward;
|
||||
struct LargeStruct bar;
|
||||
struct RecursiveStruct ke;
|
||||
|
||||
void populate(MyInt i) {
|
||||
foo.n.i = i;
|
||||
@ -52,6 +57,7 @@ void populate(MyInt i) {
|
||||
foo.f = NULL;
|
||||
forward = NULL;
|
||||
bar.b = i;
|
||||
ke.n = NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
Loading…
Reference in New Issue
Block a user