Skip to content

Commit 5cf09dd

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 5c75cfb commit 5cf09dd

File tree

9 files changed

+200
-10
lines changed

9 files changed

+200
-10
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
///

clang/lib/Sema/SemaExprMember.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,13 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
715715
}
716716
}
717717

718+
if (ExternalASTSource *Source =
719+
DC->getParentASTContext().getExternalSource()) {
720+
if (auto LookupName = R.getLookupName()) {
721+
Source->FindExternalVisibleMethodsByName(DC, LookupName);
722+
}
723+
}
724+
718725
// The record definition is complete, now look up the member.
719726
SemaRef.LookupQualifiedName(R, DC, SS);
720727

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

Lines changed: 121 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,125 @@ 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+
646+
// Member functions have at least 2 entries (1
647+
// for method name, 1 for parent class)
648+
assert(CDC1.size() > 1);
649+
650+
// drop last entry (which is function name)
651+
CDC1.pop_back();
652+
653+
const auto CDC2 = ts.DeclContextGetCompilerContext(
654+
const_cast<clang::DeclContext *>(context));
655+
656+
// Quick by-name check of the entire context hierarchy.
657+
if (CDC1 == CDC2)
658+
return true;
659+
660+
// Otherwise, check whether the 'candidate_decl_ctx' is a base class of
661+
// 'context'.
662+
auto const *candidate_context =
663+
(static_cast<clang::DeclContext *>(
664+
candidate_decl_ctx.GetOpaqueDeclContext()))
665+
->getParent();
666+
667+
auto const *candidate_cxx_record =
668+
dyn_cast<clang::CXXRecordDecl>(candidate_context);
669+
if (!candidate_cxx_record)
670+
return false;
671+
672+
auto const *cxx_record = dyn_cast<clang::CXXRecordDecl>(context);
673+
if (!cxx_record)
674+
return false;
675+
676+
// cxx_record comes from user expression AST. So we need to get origin
677+
// to compare against candidate_context.
678+
auto orig = GetDeclOrigin(cxx_record);
679+
if (!orig.Valid())
680+
return false;
681+
682+
if (llvm::cast<CXXRecordDecl>(orig.decl)->isDerivedFrom(candidate_cxx_record))
683+
return true;
684+
685+
return false;
686+
}
687+
688+
void ClangASTSource::FindExternalVisibleMethods(
689+
NameSearchContext &context, lldb::ModuleSP module_sp,
690+
CompilerDeclContext &namespace_decl) {
691+
assert(m_ast_context);
692+
693+
SymbolContextList sc_list;
694+
const ConstString name(context.m_decl_name.getAsString().c_str());
695+
if (!m_target)
696+
return;
697+
698+
if (context.m_found_type)
699+
return;
700+
701+
ModuleFunctionSearchOptions function_options;
702+
function_options.include_inlines = false;
703+
function_options.include_symbols = false;
704+
m_target->GetImages().FindFunctions(name, lldb::eFunctionNameTypeMethod,
705+
function_options, sc_list);
706+
707+
auto num_matches = sc_list.GetSize();
708+
if (num_matches == 0)
709+
return;
710+
711+
for (const SymbolContext &sym_ctx : sc_list) {
712+
assert(sym_ctx.function);
713+
CompilerDeclContext decl_ctx = sym_ctx.function->GetDeclContext();
714+
if (!decl_ctx)
715+
continue;
716+
717+
assert(decl_ctx.IsClassMethod());
718+
719+
if (!CompilerDeclContextsMatch(decl_ctx, context.m_decl_context,
720+
context.m_clang_ts))
721+
continue;
722+
723+
clang::CXXMethodDecl *src_method = llvm::cast<CXXMethodDecl>(
724+
static_cast<clang::DeclContext *>(decl_ctx.GetOpaqueDeclContext()));
725+
Decl *copied_decl = CopyDecl(src_method);
726+
727+
if (!copied_decl)
728+
continue;
729+
730+
CXXMethodDecl *copied_method_decl = dyn_cast<CXXMethodDecl>(copied_decl);
731+
732+
if (!copied_method_decl)
733+
continue;
734+
735+
context.AddNamedDecl(copied_method_decl);
736+
}
737+
}
738+
618739
void ClangASTSource::FindExternalVisibleDecls(
619740
NameSearchContext &context, lldb::ModuleSP module_sp,
620741
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
@@ -197,6 +200,7 @@ class ClangASTSource : public ImporterBackedASTSource,
197200
/// \param[in] context
198201
/// The NameSearchContext to use when filing results.
199202
virtual void FindExternalVisibleDecls(NameSearchContext &context);
203+
virtual void FindExternalVisibleMethods(NameSearchContext &context);
200204

201205
clang::Sema *getSema();
202206

@@ -219,6 +223,12 @@ class ClangASTSource : public ImporterBackedASTSource,
219223
return m_original.FindExternalVisibleDeclsByName(DC, Name);
220224
}
221225

226+
bool
227+
FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
228+
clang::DeclarationName Name) override {
229+
return m_original.FindExternalVisibleMethodsByName(DC, Name);
230+
}
231+
222232
void FindExternalLexicalDecls(
223233
const clang::DeclContext *DC,
224234
llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
@@ -288,6 +298,9 @@ class ClangASTSource : public ImporterBackedASTSource,
288298
void FindExternalVisibleDecls(NameSearchContext &context,
289299
lldb::ModuleSP module,
290300
CompilerDeclContext &namespace_decl);
301+
void FindExternalVisibleMethods(NameSearchContext &context,
302+
lldb::ModuleSP module,
303+
CompilerDeclContext &namespace_decl);
291304

292305
/// Find all Objective-C methods matching a given selector.
293306
///
@@ -364,6 +377,10 @@ class ClangASTSource : public ImporterBackedASTSource,
364377
NameSearchContext &context,
365378
DeclFromUser<const clang::ObjCInterfaceDecl> &origin_iface_decl);
366379

380+
bool CompilerDeclContextsMatch(CompilerDeclContext candidate_decl_ctx,
381+
clang::DeclContext const *context,
382+
TypeSystemClang &ts);
383+
367384
protected:
368385
bool FindObjCMethodDeclsWithOrigin(
369386
NameSearchContext &context,

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

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

1340+
void ClangExpressionDeclMap::FindExternalVisibleMethods(
1341+
NameSearchContext &context) {
1342+
assert(m_ast_context);
1343+
1344+
ClangASTSource::FindExternalVisibleMethods(context);
1345+
}
1346+
13401347
void ClangExpressionDeclMap::FindExternalVisibleDecls(
13411348
NameSearchContext &context, lldb::ModuleSP module_sp,
13421349
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
@@ -37,6 +37,11 @@ class ClangExternalASTSourceCallbacks : public ImporterBackedASTSource {
3737
bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC,
3838
clang::DeclarationName Name) override;
3939

40+
bool FindExternalVisibleMethodsByName(const clang::DeclContext *DC,
41+
clang::DeclarationName Name) override {
42+
return false;
43+
}
44+
4045
void CompleteType(clang::TagDecl *tag_decl) override;
4146

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

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,8 +2346,34 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
23462346
default_accessibility, layout_info);
23472347

23482348
// Now parse any methods if there were any...
2349-
for (const DWARFDIE &die : member_function_dies)
2350-
dwarf->ResolveType(die);
2349+
for (const DWARFDIE &mem : member_function_dies) {
2350+
if (!TypeSystemClang::UseRedeclCompletion() ||
2351+
type_is_objc_object_or_interface) {
2352+
dwarf->ResolveType(mem);
2353+
continue;
2354+
}
2355+
2356+
ConstString mem_name(mem.GetName());
2357+
ConstString die_name(die.GetName());
2358+
const bool is_ctor = mem_name == die_name;
2359+
const bool is_virtual_method =
2360+
mem.GetAttributeValueAsUnsigned(
2361+
DW_AT_virtuality, DW_VIRTUALITY_none) > DW_VIRTUALITY_none;
2362+
const bool is_operator = mem_name.GetStringRef().starts_with("operator");
2363+
const bool is_static_method =
2364+
mem.GetFirstChild().GetAttributeValueAsUnsigned(DW_AT_artificial,
2365+
0) == 0;
2366+
2367+
// FIXME: With RedeclCompletion, we currently don't have a good
2368+
// way to call `FindExternalVisibleMethods` from Clang
2369+
// for static functions, constructors or operators.
2370+
// So resolve them now.
2371+
//
2372+
// We want to resolve virtual methods now too because
2373+
// we set the method overrides below.
2374+
if (is_ctor || is_operator || is_virtual_method || is_static_method)
2375+
dwarf->ResolveType(mem);
2376+
}
23512377

23522378
if (type_is_objc_object_or_interface) {
23532379
ConstString class_name(clang_type.GetTypeName());

lldb/test/API/lang/cpp/function-ref-qualifiers/TestCppFunctionRefQualifiers.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ def test(self):
3434
self.expect_expr("Foo{}.func()", result_type="int64_t", result_value="3")
3535

3636
self.filecheck("target modules dump ast", __file__)
37-
# CHECK: |-CXXMethodDecl {{.*}} func 'uint32_t () const &'
38-
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
39-
# CHECK-NEXT: |-CXXMethodDecl {{.*}} func 'int64_t () const &&'
40-
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
41-
# CHECK-NEXT: |-CXXMethodDecl {{.*}} func 'uint32_t () &'
42-
# CHECK-NEXT: | `-AsmLabelAttr {{.*}}
43-
# CHECK-NEXT: `-CXXMethodDecl {{.*}} func 'int64_t () &&'
44-
# CHECK-NEXT: `-AsmLabelAttr {{.*}}
37+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'uint32_t () const &'
38+
# CHECK-DAG: `-AsmLabelAttr {{.*}}
39+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'int64_t () const &&'
40+
# CHECK-DAG: `-AsmLabelAttr {{.*}}
41+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'uint32_t () &'
42+
# CHECK-DAG: `-AsmLabelAttr {{.*}}
43+
# CHECK-DAG: CXXMethodDecl {{.*}} func 'int64_t () &&'
44+
# CHECK-DAG: `-AsmLabelAttr {{.*}}

0 commit comments

Comments
 (0)