[Lldb-commits] [lldb] [LLDB] FindLibCppStdFunctionCallableInfo improvements (PR #111892)
David Mentler via lldb-commits
lldb-commits at lists.llvm.org
Thu Oct 10 11:52:58 PDT 2024
https://github.com/mentlerd created https://github.com/llvm/llvm-project/pull/111892
Work in progress attempt at fixing #111291. Looking for maintainer feedback/direction from @Michael137
>From b1e6178b1130135262884d99262716fcc0ada86e Mon Sep 17 00:00:00 2001
From: David Mentler <david.mentler at shapr3d.com>
Date: Mon, 7 Oct 2024 21:46:50 +0200
Subject: [PATCH 1/4] Make existing tests break
---
.../data-formatter-stl/libcxx/function/main.cpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp
index ef7c97470652fc..b17aba3fbbc70e 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/function/main.cpp
@@ -1,5 +1,10 @@
#include <functional>
+template<typename = bool, typename = int>
+struct Dummy {
+ // Used to make lambda host function's symbol more complex
+};
+
int foo(int x, int y) {
return x + y - 1;
}
@@ -18,7 +23,7 @@ struct Bar {
}
} ;
-int foo2() {
+int foo2(Dummy<> dummy = {}) {
auto f = [](int x) {
return x+1;
};
>From d976756f47781d58c0367835a56b5b8e9130191b Mon Sep 17 00:00:00 2001
From: David Mentler <david.mentler at shapr3d.com>
Date: Wed, 9 Oct 2024 20:40:03 +0200
Subject: [PATCH 2/4] Expose conversion from DeclContext to Decl
---
lldb/include/lldb/Symbol/CompilerDeclContext.h | 2 ++
lldb/include/lldb/Symbol/TypeSystem.h | 4 ++++
lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 7 +++++++
lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h | 2 ++
lldb/source/Symbol/CompilerDeclContext.cpp | 6 ++++++
5 files changed, 21 insertions(+)
diff --git a/lldb/include/lldb/Symbol/CompilerDeclContext.h b/lldb/include/lldb/Symbol/CompilerDeclContext.h
index 89b4a9787688bc..3954ddd8e52087 100644
--- a/lldb/include/lldb/Symbol/CompilerDeclContext.h
+++ b/lldb/include/lldb/Symbol/CompilerDeclContext.h
@@ -110,6 +110,8 @@ class CompilerDeclContext {
ConstString GetScopeQualifiedName() const;
+ CompilerDecl GetDecl() const;
+
private:
TypeSystem *m_type_system = nullptr;
void *m_opaque_decl_ctx = nullptr;
diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 7d48f9b316138c..4b531a83dd5fbb 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -134,6 +134,10 @@ class TypeSystem : public PluginInterface,
virtual lldb::LanguageType DeclContextGetLanguage(void *opaque_decl_ctx) = 0;
+ virtual CompilerDecl DeclContextGetDecl(void *opaque_decl_ctx) {
+ return CompilerDecl();
+ }
+
/// Returns the direct parent context of specified type
virtual CompilerDeclContext
GetCompilerDeclContextForType(const CompilerType &type);
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index fe0c53a7e9a3ea..678ac8d65331b7 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -9595,6 +9595,13 @@ TypeSystemClang::DeclContextGetLanguage(void *opaque_decl_ctx) {
return eLanguageTypeUnknown;
}
+CompilerDecl TypeSystemClang::DeclContextGetDecl(void *opaque_decl_ctx) {
+ if (auto *decl_ctx = (clang::DeclContext *)opaque_decl_ctx)
+ if (auto* decl = dyn_cast_or_null<clang::Decl>(decl_ctx))
+ return CompilerDecl(this, decl);
+ return CompilerDecl();
+}
+
static bool IsClangDeclContext(const CompilerDeclContext &dc) {
return dc.IsValid() && isa<TypeSystemClang>(dc.GetTypeSystem());
}
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index e39aedec7e3902..e7ac6e320f20b7 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -595,6 +595,8 @@ class TypeSystemClang : public TypeSystem {
lldb::LanguageType DeclContextGetLanguage(void *opaque_decl_ctx) override;
+ CompilerDecl DeclContextGetDecl(void *opaque_decl_ctx) override;
+
std::vector<lldb_private::CompilerContext>
DeclContextGetCompilerContext(void *opaque_decl_ctx) override;
diff --git a/lldb/source/Symbol/CompilerDeclContext.cpp b/lldb/source/Symbol/CompilerDeclContext.cpp
index b40a08e9b1953d..7a72c4301b0b5c 100644
--- a/lldb/source/Symbol/CompilerDeclContext.cpp
+++ b/lldb/source/Symbol/CompilerDeclContext.cpp
@@ -34,6 +34,12 @@ ConstString CompilerDeclContext::GetScopeQualifiedName() const {
return ConstString();
}
+CompilerDecl CompilerDeclContext::GetDecl() const {
+ if (IsValid())
+ return m_type_system->DeclContextGetDecl(m_opaque_decl_ctx);
+ return CompilerDecl();
+}
+
bool CompilerDeclContext::IsClassMethod() {
if (IsValid())
return m_type_system->DeclContextIsClassMethod(m_opaque_decl_ctx);
>From 2e75bb0aac608d626fba5878315e9118bfcf9c8a Mon Sep 17 00:00:00 2001
From: David Mentler <david.mentler at shapr3d.com>
Date: Wed, 9 Oct 2024 20:41:04 +0200
Subject: [PATCH 3/4] IsPolymorphicClass already checks for record types and
handles pointers to typenames
---
.../CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp | 8 --------
1 file changed, 8 deletions(-)
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
index 4c547afe30fe81..4ac1e565cb2619 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp
@@ -191,14 +191,6 @@ llvm::Error ItaniumABILanguageRuntime::TypeHasVTable(CompilerType type) {
type = pointee_type;
}
- // Make sure this is a class or a struct first by checking the type class
- // bitfield that gets returned.
- if ((type.GetTypeClass() & (eTypeClassStruct | eTypeClassClass)) == 0) {
- return llvm::createStringError(std::errc::invalid_argument,
- "type \"%s\" is not a class or struct or a pointer to one",
- original_type.GetTypeName().AsCString("<invalid>"));
- }
-
// Check if the type has virtual functions by asking it if it is polymorphic.
if (!type.IsPolymorphicClass()) {
return llvm::createStringError(std::errc::invalid_argument,
>From 248be8c68221edc03ee0691b3b2c83eda0789324 Mon Sep 17 00:00:00 2001
From: David Mentler <david.mentler at shapr3d.com>
Date: Wed, 9 Oct 2024 22:23:48 +0200
Subject: [PATCH 4/4] Digging for wrapped callable works
---
.../CPlusPlus/CPPLanguageRuntime.cpp | 132 +++++++++++++++++-
1 file changed, 131 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index faa05e8f834ea1..7167cd4dc75f90 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -174,6 +174,137 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
if (!valobj_sp)
return optional_info;
+ // std::function has many variants, try to disambiguate
+ ValueObjectSP func_as_base_ptr;
+ {
+ ValueObjectSP outer_f = valobj_sp->GetChildMemberWithName("__f_");
+
+ if (!outer_f)
+ return optional_info; // Unrecognized implementation
+
+ if (outer_f->IsPointerType()) {
+ // git: 3e519524c118651123eecf60c2bbc5d65ad9bac3
+ //
+ // class function<_Rp()> {
+ // aligned_storage<3*sizeof(void*)>::type __buf_;
+ // __base<_Rp>* __f_;
+ // }
+
+ func_as_base_ptr = std::move(outer_f);
+ } else if (auto inner_f = outer_f->GetChildMemberWithName("__f_")) {
+ // git: 050b064f15ee56ee0b42c9b957a3dd0f32532394
+ //
+ // class function<_Rp(_ArgTypes...)> {
+ // __value_func<_Rp(_ArgTypes...)> __f_;
+ // }
+ //
+ // class __value_func<_Rp(_ArgTypes...)> {
+ // aligned_storage<3 * sizeof(void*)>::type __buf_;
+ // __base<_Rp(_ArgTypes...)>* __f_;
+ // }
+
+ func_as_base_ptr = std::move(inner_f);
+ } else
+ return optional_info; // Unrecognized implementation
+ }
+
+ // __base<...> is a pure virtual class with an interface to create/copy/destroy/invoke
+ // the underlying value. This interface is implemented by partial specializations of the
+ // __func<_Fp, _Alloc, ...> template where _Fp is the wrapped functor object
+ Status status;
+ ValueObjectSP func_as_base = func_as_base_ptr->Dereference(status);
+ if (status.Fail())
+ return optional_info;
+
+ // First we'll try to extract the __func<...> template instantiation's type by looking up
+ // the declarations of the member function pointers in it's vtable
+ CompilerType func_type;
+ {
+ ValueObjectSP vtable = func_as_base->GetVTable();
+
+ llvm::Expected<uint32_t> num_entries = vtable->GetNumChildren();
+ if (num_entries.takeError())
+ return optional_info;
+
+ // __base is pure virtual, __func is final. All member function pointers are equally
+ // good candidates to find the enclosing class.
+ //
+ // In practice the first two vtable entries point to artificial destructors which the
+ // type system refuses to elaborate as their artificial specifications are not added
+ // to the enclosing class' declaration context. This causes various warnings, and dont
+ // get us any closer to the concrete type thus we skip them.
+ for (uint32_t idx = 2; idx < *num_entries; idx++) {
+ ValueObjectSP entry = vtable->GetChildAtIndex(idx);
+
+ // Points to a potentially interesting member function
+ addr_t mfunc_load_addr = entry->GetValueAsUnsigned(0);
+ if (!mfunc_load_addr)
+ continue;
+
+ Address mfunc_symbol_addr;
+ if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, mfunc_symbol_addr))
+ continue;
+
+ Function* func = mfunc_symbol_addr.CalculateSymbolContextFunction();
+ if (!func)
+ continue;
+
+ CompilerDeclContext mfunc_decl_ctx = func->GetDeclContext();
+ if (!mfunc_decl_ctx.IsClassMethod())
+ continue;
+
+ // Member functions are contained in their enclosing class' decl context
+ CompilerDeclContext mfunc_parent = mfunc_decl_ctx.GetDecl().GetDeclContext();
+ if (!mfunc_parent.IsValid())
+ continue;
+
+ func_type = mfunc_parent.GetDecl().GetType();
+ break;
+ }
+ }
+
+ // Regardless of what std::function wraps we are looking for the load address of a function to call
+ std::optional<addr_t> target_func_load_addr;
+
+ if (CompilerType callable_type = func_type.GetTypeTemplateArgument(0)) {
+ if (callable_type.IsFunctionPointerType() || callable_type.IsMemberFunctionPointerType()) {
+ // TODO: The previous implementation just does raw pointer arithmetic and reads
+ // 'a pointer' to a function right after the vtable.
+ //
+ // What is the preferred approach? Go digging for the compressed_pair.first in __func
+ // or assume layout citing ABI compatibility requirements?
+ } else if (callable_type.IsRecordType()) {
+ // Target is a lambda, or a generic callable. Search for a single operator() overload
+ std::optional<ConstString> mangled_func_name;
+
+ for (uint32_t idx = 0; idx < callable_type.GetNumMemberFunctions(); idx++) {
+ TypeMemberFunctionImpl mfunc = callable_type.GetMemberFunctionAtIndex(idx);
+
+ if (mfunc.GetKind() != eMemberFunctionKindInstanceMethod)
+ continue;
+
+ if (mfunc.GetName() != "operator()")
+ continue;
+
+ if (mangled_func_name)
+ return optional_info; // Cannot resolve ambiguous target
+
+ mangled_func_name = mfunc.GetMangledName();
+ }
+
+ // TODO: The SymbolFile did a bunch of work to reconstruct `callable_type`,
+ // including it's member functions. Surely it knows there they are loaded?
+ }
+ } else {
+ // TODO: What if we don't have debug info for callable_type? Do we fallback to
+ // treating the std::function as wrapping a function/member function pointer
+ // due to lack of options, or give up to avoid guessing wrong?
+ }
+
+ if (!target_func_load_addr)
+ return optional_info;
+
+
// Member __f_ has type __base*, the contents of which will hold:
// 1) a vtable entry which may hold type information needed to discover the
// lambda being called
@@ -232,7 +363,6 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
return optional_info;
uint32_t address_size = process->GetAddressByteSize();
- Status status;
// First item pointed to by __f_ should be the pointer to the vtable for
// a __base object.
More information about the lldb-commits
mailing list