Skip to content

Commit d81d61b

Browse files
committed
[lldb][Type Completion] Load C++ methods lazily from DWARF
Note, this is a no-op when the LLDB setting `plugin.typesystem.clang.experimental-redecl-completion` is not set (which is currently the default). So this patch has no affect unless the user explicitly opts into it. The type-completion rework (aka redecl-completion) implemented in swiftlang#8222 comes with a large performance penalty, since we now eagerly complete `RecordType`s. Completing a `RecordType` previously unconditionally resolved all member function of said type. With redecl-completion completion, however, this meant we were now pulling in many more definitions than needed. Without redecl-completion, this isn't a problem, since importing method parameters is cheap (they are imported minimally), so we wouldn't notice that we always resolved all member functions. This patch tries to load methods lazily when in redecl-completion mode. We do this by introducing a new `ExternalASTSource::FindExternalVisibleMethods` API which Clang parses a member access expression. The callback into LLDB will do a `FindFunctions` call for all methods with the method name we're looking for, filters out any mismatches, and lets Clang continue with it's parsing. We still load following methods eagerly: 1. virtual functions: currently overrides are resolved in `CompleteRecordType` 2. operators: currently I couldn't find a point at which Clang can call into LLDB here to facilitate lazy loading 3. ctors/dtors: same reason as (2) In our benchmark harness, we saw this patch bring down redecl-completion expression evaluation on-par with top-of-tree expression evaluation.
1 parent d5b392e commit d81d61b

File tree

7 files changed

+170
-4
lines changed

7 files changed

+170
-4
lines changed

clang/include/clang/AST/ExternalASTSource.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
150150
virtual bool
151151
FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name);
152152

153+
virtual bool FindExternalVisibleMethodsByName(const DeclContext *DC,
154+
DeclarationName Name) {
155+
return false;
156+
}
157+
153158
/// Ensures that the table of all visible declarations inside this
154159
/// context is up to date.
155160
///

lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "ClangDeclVendor.h"
1212
#include "ClangModulesDeclVendor.h"
1313

14+
#include "NameSearchContext.h"
1415
#include "lldb/Core/Module.h"
1516
#include "lldb/Core/ModuleList.h"
1617
#include "lldb/Symbol/CompilerDeclContext.h"
@@ -21,6 +22,7 @@
2122
#include "lldb/Utility/LLDBLog.h"
2223
#include "lldb/Utility/Log.h"
2324
#include "clang/AST/ASTContext.h"
25+
#include "clang/AST/DeclCXX.h"
2426
#include "clang/Basic/SourceManager.h"
2527

2628
#include "Plugins/ExpressionParser/Clang/ClangUtil.h"
@@ -615,6 +617,118 @@ bool ClangASTSource::IgnoreName(const ConstString name,
615617
name_string_ref.starts_with("_$");
616618
}
617619

620+
bool ClangASTSource::FindExternalVisibleMethodsByName(
621+
const clang::DeclContext *DC, clang::DeclarationName Name) {
622+
if (!TypeSystemClang::UseRedeclCompletion())
623+
return true;
624+
625+
SmallVector<clang::NamedDecl *> decls;
626+
NameSearchContext context(*m_clang_ast_context, decls, Name, DC);
627+
FindExternalVisibleMethods(context);
628+
629+
return true;
630+
}
631+
632+
void ClangASTSource::FindExternalVisibleMethods(NameSearchContext &context) {
633+
assert(m_ast_context);
634+
635+
const ConstString name(context.m_decl_name.getAsString().c_str());
636+
CompilerDeclContext namespace_decl;
637+
FindExternalVisibleMethods(context, lldb::ModuleSP(), namespace_decl);
638+
}
639+
640+
bool ClangASTSource::CompilerDeclContextsMatch(
641+
CompilerDeclContext candidate_decl_ctx, DeclContext const *context,
642+
TypeSystemClang &ts) {
643+
auto CDC1 = candidate_decl_ctx.GetTypeSystem()->DeclContextGetCompilerContext(
644+
candidate_decl_ctx.GetOpaqueDeclContext());
645+
assert(CDC1.size() > 1); // Member functions have at least 2 entries (1
646+
// for method name, 1 for partent class)
647+
CDC1.pop_back(); // drop last entry (which is function name)
648+
const auto CDC2 = ts.DeclContextGetCompilerContext(
649+
const_cast<clang::DeclContext *>(context));
650+
651+
// Quick by-name check of the entire context hierarchy.
652+
if (CDC1 == CDC2)
653+
return true;
654+
655+
// Otherwise, check whether the 'candidate_decl_ctx' is a base class of
656+
// 'context'.
657+
auto const *candidate_context =
658+
(static_cast<clang::DeclContext *>(
659+
candidate_decl_ctx.GetOpaqueDeclContext()))
660+
->getParent();
661+
662+
auto const *candidate_cxx_record =
663+
dyn_cast<clang::CXXRecordDecl>(candidate_context);
664+
if (!candidate_cxx_record)
665+
return false;
666+
667+
auto const *cxx_record = dyn_cast<clang::CXXRecordDecl>(context);
668+
if (!cxx_record)
669+
return false;
670+
671+
// cxx_record comes from user expression AST. So we need to get origin
672+
// to compare against candidate_context.
673+
auto orig = GetDeclOrigin(cxx_record);
674+
if (!orig.Valid())
675+
return false;
676+
677+
if (llvm::cast<CXXRecordDecl>(orig.decl)->isDerivedFrom(candidate_cxx_record))
678+
return true;
679+
680+
return false;
681+
}
682+
683+
void ClangASTSource::FindExternalVisibleMethods(
684+
NameSearchContext &context, lldb::ModuleSP module_sp,
685+
CompilerDeclContext &namespace_decl) {
686+
assert(m_ast_context);
687+
688+
SymbolContextList sc_list;
689+
const ConstString name(context.m_decl_name.getAsString().c_str());
690+
if (!m_target)
691+
return;
692+
693+
if (context.m_found_type)
694+
return;
695+
696+
ModuleFunctionSearchOptions function_options;
697+
function_options.include_inlines = false;
698+
function_options.include_symbols = false;
699+
m_target->GetImages().FindFunctions(name, lldb::eFunctionNameTypeMethod,
700+
function_options, sc_list);
701+
702+
auto num_matches = sc_list.GetSize();
703+
if (num_matches > 0) {
704+
for (const SymbolContext &sym_ctx : sc_list) {
705+
assert(sym_ctx.function);
706+
CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext();
707+
708+
assert(decl_ctx);
709+
assert(decl_ctx.IsClassMethod());
710+
711+
if (!CompilerDeclContextsMatch(decl_ctx, context.m_decl_context,
712+
context.m_clang_ts))
713+
continue;
714+
715+
clang::CXXMethodDecl *src_method = llvm::cast<CXXMethodDecl>(
716+
static_cast<clang::DeclContext *>(decl_ctx.GetOpaqueDeclContext()));
717+
Decl *copied_decl = CopyDecl(src_method);
718+
719+
if (!copied_decl)
720+
continue;
721+
722+
CXXMethodDecl *copied_method_decl = dyn_cast<CXXMethodDecl>(copied_decl);
723+
724+
if (!copied_method_decl)
725+
continue;
726+
727+
context.AddNamedDecl(copied_method_decl);
728+
}
729+
}
730+
}
731+
618732
void ClangASTSource::FindExternalVisibleDecls(
619733
NameSearchContext &context, lldb::ModuleSP module_sp,
620734
CompilerDeclContext &namespace_decl) {

lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ class ClangASTSource : public ImporterBackedASTSource,
8787
bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
8888
clang::DeclarationName Name) override;
8989

90+
bool FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
91+
clang::DeclarationName Name) override;
92+
9093
/// Enumerate all Decls in a given lexical context.
9194
///
9295
/// \param[in] DC
@@ -208,6 +211,7 @@ class ClangASTSource : public ImporterBackedASTSource,
208211
/// \param[in] context
209212
/// The NameSearchContext to use when filing results.
210213
virtual void FindExternalVisibleDecls(NameSearchContext &context);
214+
virtual void FindExternalVisibleMethods(NameSearchContext &context);
211215

212216
clang::Sema *getSema();
213217

@@ -230,6 +234,12 @@ class ClangASTSource : public ImporterBackedASTSource,
230234
return m_original.FindExternalVisibleDeclsByName(DC, Name);
231235
}
232236

237+
bool
238+
FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
239+
clang::DeclarationName Name) override {
240+
return m_original.FindExternalVisibleMethodsByName(DC, Name);
241+
}
242+
233243
void FindExternalLexicalDecls(
234244
const clang::DeclContext *DC,
235245
llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
@@ -299,6 +309,9 @@ class ClangASTSource : public ImporterBackedASTSource,
299309
void FindExternalVisibleDecls(NameSearchContext &context,
300310
lldb::ModuleSP module,
301311
CompilerDeclContext &namespace_decl);
312+
void FindExternalVisibleMethods(NameSearchContext &context,
313+
lldb::ModuleSP module,
314+
CompilerDeclContext &namespace_decl);
302315

303316
/// Find all Objective-C methods matching a given selector.
304317
///
@@ -375,6 +388,10 @@ class ClangASTSource : public ImporterBackedASTSource,
375388
NameSearchContext &context,
376389
DeclFromUser<const clang::ObjCInterfaceDecl> &origin_iface_decl);
377390

391+
bool CompilerDeclContextsMatch(CompilerDeclContext candidate_decl_ctx,
392+
clang::DeclContext const *context,
393+
TypeSystemClang &ts);
394+
378395
protected:
379396
bool FindObjCMethodDeclsWithOrigin(
380397
NameSearchContext &context,

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,6 +1335,13 @@ void ClangExpressionDeclMap::LookupFunction(
13351335
}
13361336
}
13371337

1338+
void ClangExpressionDeclMap::FindExternalVisibleMethods(
1339+
NameSearchContext &context) {
1340+
assert(m_ast_context);
1341+
1342+
ClangASTSource::FindExternalVisibleMethods(context);
1343+
}
1344+
13381345
void ClangExpressionDeclMap::FindExternalVisibleDecls(
13391346
NameSearchContext &context, lldb::ModuleSP module_sp,
13401347
const CompilerDeclContext &namespace_decl) {

lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "ClangASTSource.h"
1919
#include "ClangExpressionVariable.h"
2020

21+
#include "Plugins/ExpressionParser/Clang/NameSearchContext.h"
2122
#include "lldb/Core/Value.h"
2223
#include "lldb/Expression/Materializer.h"
2324
#include "lldb/Symbol/SymbolContext.h"
@@ -266,6 +267,7 @@ class ClangExpressionDeclMap : public ClangASTSource {
266267
/// \param[in] context
267268
/// The NameSearchContext that can construct Decls for this name.
268269
void FindExternalVisibleDecls(NameSearchContext &context) override;
270+
void FindExternalVisibleMethods(NameSearchContext &context) override;
269271

270272
/// Find all entities matching a given name in a given module/namespace,
271273
/// using a NameSearchContext to make Decls for them.

lldb/source/Plugins/ExpressionParser/Clang/ClangExternalASTSourceCallbacks.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ class ClangExternalASTSourceCallbacks : public ImporterBackedASTSource {
4343
bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
4444
clang::DeclarationName Name) override;
4545

46+
bool FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
47+
clang::DeclarationName Name) override {
48+
return false;
49+
}
50+
4651
void CompleteType(clang::TagDecl *tag_decl) override;
4752

4853
void CompleteType(clang::ObjCInterfaceDecl *objc_decl) override;

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,13 +2226,29 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
22262226
contained_type_dies, delayed_properties,
22272227
default_accessibility, layout_info);
22282228

2229-
// Now parse any methods if there were any...
2230-
for (const DWARFDIE &die : member_function_dies)
2231-
dwarf->ResolveType(die);
2232-
22332229
const bool type_is_objc_object_or_interface =
22342230
TypeSystemClang::IsObjCObjectOrInterfaceType(clang_type);
22352231

2232+
// Now parse any methods if there were any...
2233+
for (const DWARFDIE &mem : member_function_dies) {
2234+
if (!TypeSystemClang::UseRedeclCompletion() ||
2235+
type_is_objc_object_or_interface) {
2236+
dwarf->ResolveType(mem);
2237+
continue;
2238+
}
2239+
2240+
ConstString mem_name(mem.GetName());
2241+
ConstString die_name(die.GetName());
2242+
const bool is_ctor = mem_name == die_name;
2243+
const bool is_virtual_method =
2244+
mem.GetAttributeValueAsUnsigned(
2245+
DW_AT_virtuality, DW_VIRTUALITY_none) > DW_VIRTUALITY_none;
2246+
const bool is_operator = mem_name.GetStringRef().starts_with("operator");
2247+
2248+
if (is_ctor || is_operator || is_virtual_method)
2249+
dwarf->ResolveType(mem);
2250+
}
2251+
22362252
if (type_is_objc_object_or_interface) {
22372253
ConstString class_name(clang_type.GetTypeName());
22382254
if (class_name) {

0 commit comments

Comments
 (0)