Skip to content

Commit e42e9fc

Browse files
Merge pull request #217 from adrian-prantl/56582727-master
SwiftDWARFImporterDelegate: Cache type lookups to avoid searching the…
2 parents 520eecb + 820ab4a commit e42e9fc

File tree

10 files changed

+242
-32
lines changed

10 files changed

+242
-32
lines changed

lldb/include/lldb/Symbol/SwiftASTContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ class SwiftASTContext : public TypeSystem {
125125

126126
~SwiftASTContext();
127127

128+
const std::string &GetDescription() const;
129+
128130
// PluginInterface functions
129131
ConstString GetPluginName() override;
130132

@@ -313,6 +315,7 @@ class SwiftASTContext : public TypeSystem {
313315
CompilerType ImportType(CompilerType &type, Status &error);
314316

315317
swift::ClangImporter *GetClangImporter();
318+
swift::DWARFImporterDelegate *GetDWARFImporterDelegate();
316319

317320
struct TupleElement {
318321
ConstString element_name;

lldb/packages/Python/lldbsuite/test/lang/swift/dwarfimporter/Objective-C/TestSwiftDWARFImporterObjC.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def build(self):
3939
@swiftTest
4040
def test_dwarf_importer(self):
4141
self.runCmd("settings set symbols.use-swift-dwarfimporter true")
42+
4243
self.build()
4344
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
4445
self, 'break here', lldb.SBFileSpec('main.swift'))
@@ -58,5 +59,5 @@ def test_dwarf_importer(self):
5859
# This is a Clang type, since Clang doesn't generate DWARF for protocols.
5960
self.expect("target var -d no-dyn proto", substrs=["(id)", "proto"])
6061
# This is a Swift type.
61-
self.expect("target var -d run proto", substrs=["(ProtoImpl?)", "proto"])
62+
self.expect("target var -d run proto", substrs=["(ProtoImpl)", "proto"])
6263
self.expect("target var -O proto", substrs=["<ProtoImpl"])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module ObjCModule {
2+
header "objc-header.h"
3+
export *
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* -*- ObjC -*- */
2+
@import Foundation;
3+
4+
@interface ObjCClass : NSObject
5+
- (instancetype)init;
6+
- (NSString *)getString;
7+
- (id)getMangled;
8+
- (id)getRawname;
9+
@end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Foundation
2+
3+
/// This is an object exposed to Objective-C as "_Tt7Library10SwiftClassC".
4+
@objc public class MangledSwiftClass : NSObject {
5+
@objc public func getString() -> NSString { return "Hello from Swift!" }
6+
}
7+
8+
/// This is an object exposed to Objective-C as "RawNameSwiftClass".
9+
@objc(RawNameSwiftClass) public class RawNameSwiftClass : NSObject {
10+
@objc public func getString() -> NSString { return "Hello from Swift!" }
11+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
LEVEL = ../../../../make
2+
3+
MAKE_GMODULES := YES
4+
SWIFT_OBJC_INTEROP = 1
5+
SWIFT_SOURCES := main.swift
6+
OBJC_SOURCES := objc.m
7+
CFLAGS_EXTRAS = -I$(BUILDDIR)/include
8+
SWIFTFLAGS_EXTRAS = -I$(BUILDDIR)/include
9+
LD_EXTRAS := libLibrary.dylib
10+
11+
include $(LEVEL)/Makefile.rules
12+
13+
objc.o: libLibrary.dylib
14+
15+
libLibrary.dylib: Library.swift
16+
$(MAKE) -f $(MAKEFILE_RULES) \
17+
DYLIB_SWIFT_SOURCES=Library.swift \
18+
DYLIB_NAME=Library \
19+
SWIFTFLAGS_EXTRAS="-emit-objc-header-path $(BUILDDIR)/include/Library-Swift.h"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
import lldbsuite.test.lldbtest as lldbtest
4+
import lldbsuite.test.lldbutil as lldbutil
5+
import os
6+
import unittest2
7+
8+
9+
class TestSwiftDWARFImporter_Swift(lldbtest.TestBase):
10+
11+
mydir = lldbtest.TestBase.compute_mydir(__file__)
12+
13+
def build(self):
14+
include = self.getBuildArtifact('include')
15+
inputs = self.getSourcePath(os.path.join('Inputs', 'Modules'))
16+
lldbutil.mkdir_p(include)
17+
import shutil
18+
for f in ['module.modulemap', 'objc-header.h']:
19+
shutil.copyfile(os.path.join(inputs, f), os.path.join(include, f))
20+
21+
super(TestSwiftDWARFImporter_Swift, self).build()
22+
23+
# Remove the header files to thwart ClangImporter.
24+
self.assertTrue(os.path.isdir(include))
25+
shutil.rmtree(include)
26+
27+
@skipUnlessDarwin
28+
@swiftTest
29+
def test(self):
30+
self.runCmd("settings set symbols.use-swift-dwarfimporter true")
31+
self.build()
32+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
33+
self, 'break here', lldb.SBFileSpec('main.swift'))
34+
35+
log = self.getBuildArtifact("types.log")
36+
self.runCmd('log enable lldb types -f "%s"' % log)
37+
38+
self.expect("target var -d run myobj", substrs=["(ObjCClass)"])
39+
40+
found = 0
41+
response = 0
42+
logfile = open(log, "r")
43+
for line in logfile:
44+
if 'SwiftDWARFImporterDelegate::lookupValue("ObjCClass")' in line:
45+
found += 1
46+
elif found == 1 and response == 0 and 'SwiftDWARFImporterDelegate' in line:
47+
self.assertTrue('from debug info' in line, line)
48+
response += 1
49+
elif found == 2 and response == 1 and 'SwiftDWARFImporterDelegate' in line:
50+
self.assertTrue('found in cache' in line, line)
51+
response += 1
52+
self.assertEqual(found, 2)
53+
self.assertEqual(response, 2)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import ObjCModule
2+
3+
func use<T>(_ t : T) {}
4+
5+
guard let obj = ObjCClass() else { exit(1) }
6+
let myobj = obj
7+
let mangled = obj.getMangled()!
8+
let rawname = obj.getRawname()!
9+
let s = obj.getString()
10+
use(s) // break here
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
@import ObjCModule;
2+
#include "Library-Swift.h"
3+
4+
@implementation ObjCClass {
5+
id mangled_swift_obj;
6+
id rawname_swift_obj;
7+
}
8+
9+
- (instancetype)init {
10+
self = [super init];
11+
if (self) {
12+
mangled_swift_obj = [MangledSwiftClass new];
13+
rawname_swift_obj = [RawNameSwiftClass new];
14+
}
15+
return self;
16+
}
17+
18+
- (id)getMangled {
19+
return mangled_swift_obj;
20+
}
21+
22+
- (id)getRawname {
23+
return rawname_swift_obj;
24+
}
25+
26+
- (NSString *)getString {
27+
return [mangled_swift_obj getString];
28+
}
29+
30+
@end
31+

lldb/source/Symbol/SwiftASTContext.cpp

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,8 @@ SwiftASTContext::~SwiftASTContext() {
921921
}
922922
}
923923

924+
const std::string &SwiftASTContext::GetDescription() const { return m_description; }
925+
924926
ConstString SwiftASTContext::GetPluginNameStatic() {
925927
return ConstString("swift");
926928
}
@@ -3163,6 +3165,11 @@ class StoringDiagnosticConsumer : public swift::DiagnosticConsumer {
31633165
/// Clang AST to ClangImporter to import the type into Swift.
31643166
class SwiftDWARFImporterDelegate : public swift::DWARFImporterDelegate {
31653167
SwiftASTContext &m_swift_ast_ctx;
3168+
using ModuleAndName = std::pair<const char *, const char *>;
3169+
/// Caches successful lookups for the scratch context.
3170+
llvm::DenseMap<ModuleAndName, llvm::SmallVector<clang::QualType, 1>>
3171+
m_decl_cache;
3172+
std::string m_description;
31663173

31673174
/// Used to filter out types with mismatching kinds.
31683175
bool HasTypeKind(TypeSP clang_type_sp, swift::ClangTypeKind kind) {
@@ -3230,9 +3237,42 @@ class SwiftDWARFImporterDelegate : public swift::DWARFImporterDelegate {
32303237
}
32313238
}
32323239

3240+
/// Import \p qual_type from one clang ASTContext to another and
3241+
/// add it to \p results if successful.
3242+
void importType(clang::QualType qual_type, clang::ASTContext &from_ctx,
3243+
clang::ASTContext &to_ctx,
3244+
llvm::Optional<swift::ClangTypeKind> kind,
3245+
llvm::SmallVectorImpl<clang::Decl *> &results) {
3246+
clang::FileSystemOptions file_system_options;
3247+
clang::FileManager file_manager(
3248+
file_system_options, FileSystem::Instance().GetVirtualFileSystem());
3249+
clang::ASTImporter importer(to_ctx, file_manager, from_ctx, file_manager,
3250+
false);
3251+
llvm::Expected<clang::QualType> clang_type(importer.Import(qual_type));
3252+
if (!clang_type) {
3253+
llvm::consumeError(clang_type.takeError());
3254+
return;
3255+
}
3256+
3257+
// Retrieve the imported type's Decl.
3258+
if (kind) {
3259+
if (clang::Decl *clang_decl = GetDeclForTypeAndKind(*clang_type, *kind))
3260+
results.push_back(clang_decl);
3261+
} else {
3262+
swift::ClangTypeKind kinds[] = {
3263+
swift::ClangTypeKind::Typedef, // =swift::ClangTypeKind::ObjCClass,
3264+
swift::ClangTypeKind::Tag, swift::ClangTypeKind::ObjCProtocol};
3265+
for (auto kind : kinds)
3266+
if (clang::Decl *clang_decl = GetDeclForTypeAndKind(*clang_type, kind))
3267+
results.push_back(clang_decl);
3268+
}
3269+
}
3270+
32333271
public:
32343272
SwiftDWARFImporterDelegate(SwiftASTContext &swift_ast_ctx)
3235-
: m_swift_ast_ctx(swift_ast_ctx) {}
3273+
: m_swift_ast_ctx(swift_ast_ctx),
3274+
m_description(swift_ast_ctx.GetDescription() +
3275+
"::SwiftDWARFImporterDelegate") {}
32363276

32373277
/// Look up a clang::Decl by name.
32383278
///
@@ -3291,6 +3331,8 @@ class SwiftDWARFImporterDelegate : public swift::DWARFImporterDelegate {
32913331
void lookupValue(StringRef name, llvm::Optional<swift::ClangTypeKind> kind,
32923332
StringRef inModule,
32933333
llvm::SmallVectorImpl<clang::Decl *> &results) override {
3334+
LOG_PRINTF(LIBLLDB_LOG_TYPES, "(\"%s\")", name.str().c_str());
3335+
32943336
// We will not find any Swift types in the Clang compile units.
32953337
if (SwiftLanguageRuntime::IsSwiftMangledName(name.str().c_str()))
32963338
return;
@@ -3301,15 +3343,16 @@ class SwiftDWARFImporterDelegate : public swift::DWARFImporterDelegate {
33013343

33023344
// Find the type in the debug info.
33033345
TypeMap clang_types;
3346+
ConstString name_cs(name);
3347+
ConstString module_cs(inModule);
33043348

33053349
llvm::SmallVector<CompilerContext, 3> decl_context;
33063350
// Perform a lookup in a specific module, if requested.
33073351
if (!inModule.empty())
3308-
decl_context.push_back(
3309-
{CompilerContextKind::Module, ConstString(inModule)});
3352+
decl_context.push_back({CompilerContextKind::Module, module_cs});
33103353
// Swift doesn't keep track of submodules.
33113354
decl_context.push_back({CompilerContextKind::AnyModule, ConstString()});
3312-
decl_context.push_back({GetCompilerContextKind(kind), ConstString(name)});
3355+
decl_context.push_back({GetCompilerContextKind(kind), name_cs});
33133356
auto search = [&](Module &module) {
33143357
return module.FindTypes(decl_context,
33153358
ClangASTContext::GetSupportedLanguagesForTypes(),
@@ -3318,16 +3361,46 @@ class SwiftDWARFImporterDelegate : public swift::DWARFImporterDelegate {
33183361
if (Module *module = m_swift_ast_ctx.GetModule())
33193362
search(*module);
33203363
else if (TargetSP target_sp = m_swift_ast_ctx.GetTarget().lock()) {
3321-
// In a scratch context, search everywhere.
3364+
// In a scratch context, check the module's DWARFImporterDelegates first.
3365+
//
3366+
// It's a common pattern that a type is revisited immediately
3367+
// after looking it up in a per-module context in the scratch
3368+
// context for dynamic type resolution.
33223369
auto images = target_sp->GetImages();
3323-
for (size_t i = 0; i != images.GetSize(); ++i)
3324-
if (search(*images.GetModuleAtIndex(i)))
3325-
break;
3370+
for (size_t i = 0; i != images.GetSize(); ++i) {
3371+
auto module_sp = images.GetModuleAtIndex(i);
3372+
auto ts = module_sp->GetTypeSystemForLanguage(lldb::eLanguageTypeSwift);
3373+
if (!ts) {
3374+
llvm::consumeError(ts.takeError());
3375+
continue;
3376+
}
3377+
auto *swift_ast_ctx = static_cast<SwiftASTContext *>(&*ts);
3378+
auto *dwarf_imp = static_cast<SwiftDWARFImporterDelegate *>(
3379+
swift_ast_ctx->GetDWARFImporterDelegate());
3380+
if (!dwarf_imp)
3381+
continue;
3382+
auto it = dwarf_imp->m_decl_cache.find(
3383+
{module_cs.GetCString(), name_cs.GetCString()});
3384+
if (it == dwarf_imp->m_decl_cache.end())
3385+
continue;
3386+
3387+
auto *from_clang_importer = swift_ast_ctx->GetClangImporter();
3388+
if (!from_clang_importer)
3389+
continue;
3390+
auto &from_ctx = from_clang_importer->getClangASTContext();
3391+
auto &to_ctx = clang_importer->getClangASTContext();
3392+
for (clang::QualType qual_type : it->second)
3393+
importType(qual_type, from_ctx, to_ctx, kind, results);
3394+
}
3395+
LOG_PRINTF(LIBLLDB_LOG_TYPES, "%d types found in cache.", results.size());
3396+
3397+
// TODO: Otherwise, the correct thing to do is to invoke
3398+
// search() on all modules. In practice, however, this is
3399+
// prohibitively expensive, so we need to do something
3400+
// more targeted.
3401+
return;
33263402
}
33273403

3328-
clang::FileSystemOptions file_system_options;
3329-
clang::FileManager file_manager(
3330-
file_system_options, FileSystem::Instance().GetVirtualFileSystem());
33313404
clang_types.ForEach([&](lldb::TypeSP &clang_type_sp) {
33323405
if (!clang_type_sp)
33333406
return true;
@@ -3350,29 +3423,19 @@ class SwiftDWARFImporterDelegate : public swift::DWARFImporterDelegate {
33503423
clang::ASTContext *from_ctx = type_system->getASTContext();
33513424
if (!from_ctx)
33523425
return true;
3353-
clang::ASTImporter importer(to_ctx, file_manager, *from_ctx, file_manager,
3354-
false);
3355-
llvm::Expected<clang::QualType> clang_type(
3356-
importer.Import(ClangUtil::GetQualType(compiler_type)));
3357-
if (!clang_type) {
3358-
llvm::consumeError(clang_type.takeError());
3359-
return true;
3360-
}
33613426

3362-
// Retrieve the imported type's Decl.
3363-
if (kind) {
3364-
if (clang::Decl *clang_decl = GetDeclForTypeAndKind(*clang_type, *kind))
3365-
results.push_back(clang_decl);
3366-
} else {
3367-
swift::ClangTypeKind kinds[] = {
3368-
swift::ClangTypeKind::Typedef, // =swift::ClangTypeKind::ObjCClass,
3369-
swift::ClangTypeKind::Tag, swift::ClangTypeKind::ObjCProtocol};
3370-
for (auto kind : kinds)
3371-
if (clang::Decl *clang_decl = GetDeclForTypeAndKind(*clang_type, kind))
3372-
results.push_back(clang_decl);
3373-
}
3427+
clang::QualType qual_type = ClangUtil::GetQualType(compiler_type);
3428+
importType(qual_type, *from_ctx, to_ctx, kind, results);
3429+
3430+
// If this is a module context, cache the result for the scratch context.
3431+
if (m_swift_ast_ctx.GetModule())
3432+
m_decl_cache[{module_cs.GetCString(), name_cs.GetCString()}].push_back(
3433+
qual_type);
3434+
33743435
return true;
33753436
});
3437+
3438+
LOG_PRINTF(LIBLLDB_LOG_TYPES, "%d types from debug info.", results.size());
33763439
}
33773440
};
33783441
} // namespace lldb_private
@@ -3552,6 +3615,12 @@ swift::ClangImporter *SwiftASTContext::GetClangImporter() {
35523615
return m_clang_importer;
35533616
}
35543617

3618+
swift::DWARFImporterDelegate *SwiftASTContext::GetDWARFImporterDelegate() {
3619+
VALID_OR_RETURN(nullptr);
3620+
3621+
return m_dwarf_importer_delegate_up.get();
3622+
}
3623+
35553624
bool SwiftASTContext::AddClangArgument(std::string clang_arg, bool unique) {
35563625
if (clang_arg.empty())
35573626
return false;

0 commit comments

Comments
 (0)