[Lldb-commits] [lldb] [LLDB] FindLibCppStdFunctionCallableInfo improvements (PR #111892)
David Mentler via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 11 12:00:37 PDT 2024
================
@@ -174,6 +174,233 @@ 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;
+ Address func_method_addr;
+ {
+ 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;
+
+ if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, func_method_addr))
+ continue;
+
+ Function* func = func_method_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;
+ }
+ }
+
+ CompilerType callable_type = func_type.GetTypeTemplateArgument(0);
+ if (!callable_type)
+ return optional_info;
+
+ // Now that the __func is a known type we can dig for the wrapped callable
+ ValueObjectSP callable;
+ {
+ // class __func<_Fp, _Alloc, _Rp(_ArgTypes...)> : __base<_Rp(_ArgTypes...)> {
+ // __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> __f_;
+ // }
+ //
+ // class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> {
+ // __compressed_pair<_Fp, _Ap> __f_;
+ // }
+ //
+ // class __compressed_pair : __compressed_pair_elem<_T1, 0>,
+ // __compressed_pair_elem<_T2, 1> {
+ // }
+ //
+ // struct __compressed_pair_elem {
+ // _Tp __value_;
+ // }
+ ValueObjectSP alloc_func = func_as_base->Cast(func_type);
+ if (!alloc_func)
+ return optional_info;
+
+ ValueObjectSP pair = alloc_func->GetChildAtNamePath({"__f_", "__f_"});
+ if (!pair)
+ return optional_info;
+
+ if (callable_type.IsRecordType() && callable_type.GetNumFields() == 0) {
+ // callable_type is an empty class, and has been optimized away! Serve a dummy
+ callable = valobj_sp->CreateValueObjectFromAddress("__value_",
+ pair->GetLoadAddress(),
+ pair->GetExecutionContextRef(),
+ callable_type);
----------------
mentlerd wrote:
The code snippets above are from the SDK shipped with Xcode 16. There `__compressed_pair_elem` has a specialization for storing classes which are completely empty which is an empty class.
In that case empty base class elision kicks in, and leaves me with a `__compressed_pair` with no base classes and no fields.
Eventually I would like to make a synthetic children provider for `std::function` which would show the wrapped object. I figured even if it is empty it would be worthy to show a dummy of it so the UI can immediately show the underlying C++ type as well.
---
As far as I am aware Apple's libc++ implementation is slightly different than the one on this repo. What is the policy for such situations? Are you OK with me also supporting their layout or is that something more fitting of contributing to https://github.com/swiftlang/llvm-project?
https://github.com/llvm/llvm-project/pull/111892
More information about the lldb-commits
mailing list