Files
swift-mirror/lib/AST/ImportCache.cpp
Jordan Rose 853caa66d4 [AST] Split FileUnit and its subclasses out of Module.h
Most of AST, Parse, and Sema deal with FileUnits regularly, but SIL
and IRGen certainly don't. Split FileUnit out into its own header to
cut down on recompilation times when something changes.

No functionality change.
2019-09-17 17:54:41 -07:00

303 lines
9.4 KiB
C++

//===--- ImportCache.cpp - Caching the import graph -------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines interfaces for querying the module import graph in an
// efficient manner.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/DenseSet.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/FileUnit.h"
#include "swift/AST/ImportCache.h"
#include "swift/AST/Module.h"
using namespace swift;
using namespace namelookup;
ImportSet::ImportSet(bool hasHeaderImportModule,
ArrayRef<ModuleDecl::ImportedModule> topLevelImports,
ArrayRef<ModuleDecl::ImportedModule> transitiveImports)
: HasHeaderImportModule(hasHeaderImportModule),
NumTopLevelImports(topLevelImports.size()),
NumTransitiveImports(transitiveImports.size()) {
auto buffer = getTrailingObjects<ModuleDecl::ImportedModule>();
std::uninitialized_copy(topLevelImports.begin(), topLevelImports.end(),
buffer);
std::uninitialized_copy(transitiveImports.begin(), transitiveImports.end(),
buffer + topLevelImports.size());
#ifndef NDEBUG
llvm::SmallDenseSet<ModuleDecl::ImportedModule, 8> unique;
for (auto import : topLevelImports) {
auto result = unique.insert(import).second;
assert(result && "Duplicate imports in import set");
}
for (auto import : transitiveImports) {
auto result = unique.insert(import).second;
assert(result && "Duplicate imports in import set");
}
#endif
}
void ImportSet::Profile(
llvm::FoldingSetNodeID &ID,
ArrayRef<ModuleDecl::ImportedModule> topLevelImports) {
ID.AddInteger(topLevelImports.size());
for (auto import : topLevelImports) {
ID.AddInteger(import.first.size());
for (auto accessPathElt : import.first) {
ID.AddPointer(accessPathElt.first.getAsOpaquePointer());
}
ID.AddPointer(import.second);
}
}
static void collectExports(ModuleDecl::ImportedModule next,
SmallVectorImpl<ModuleDecl::ImportedModule> &stack) {
SmallVector<ModuleDecl::ImportedModule, 4> exports;
next.second->getImportedModulesForLookup(exports);
for (auto exported : exports) {
if (next.first.empty())
stack.push_back(exported);
else if (exported.first.empty()) {
exported.first = next.first;
stack.push_back(exported);
} else if (ModuleDecl::isSameAccessPath(next.first, exported.first)) {
stack.push_back(exported);
}
}
}
ImportSet &
ImportCache::getImportSet(ASTContext &ctx,
ArrayRef<ModuleDecl::ImportedModule> imports) {
bool hasHeaderImportModule = false;
ModuleDecl *headerImportModule = nullptr;
if (auto *loader = ctx.getClangModuleLoader())
headerImportModule = loader->getImportedHeaderModule();
SmallVector<ModuleDecl::ImportedModule, 4> topLevelImports;
SmallVector<ModuleDecl::ImportedModule, 4> transitiveImports;
llvm::SmallDenseSet<ModuleDecl::ImportedModule, 32> visited;
for (auto next : imports) {
if (!visited.insert(next).second)
continue;
topLevelImports.push_back(next);
if (next.second == headerImportModule)
hasHeaderImportModule = true;
}
void *InsertPos = nullptr;
llvm::FoldingSetNodeID ID;
ImportSet::Profile(ID, topLevelImports);
if (ImportSet *result = ImportSets.FindNodeOrInsertPos(ID, InsertPos)) {
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ImportSetFoldHit++;
return *result;
}
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ImportSetFoldMiss++;
SmallVector<ModuleDecl::ImportedModule, 4> stack;
for (auto next : topLevelImports) {
collectExports(next, stack);
}
while (!stack.empty()) {
auto next = stack.pop_back_val();
if (!visited.insert(next).second)
continue;
transitiveImports.push_back(next);
if (next.second == headerImportModule)
hasHeaderImportModule = true;
collectExports(next, stack);
}
// Find the insert position again, in case the above traversal invalidated
// the folding set via re-entrant calls to getImportSet() from
// getImportedModulesForLookup().
if (ImportSet *result = ImportSets.FindNodeOrInsertPos(ID, InsertPos))
return *result;
void *mem = ctx.Allocate(
sizeof(ImportSet) +
sizeof(ModuleDecl::ImportedModule) * topLevelImports.size() +
sizeof(ModuleDecl::ImportedModule) * transitiveImports.size(),
alignof(ImportSet), AllocationArena::Permanent);
auto *result = new (mem) ImportSet(hasHeaderImportModule,
topLevelImports,
transitiveImports);
ImportSets.InsertNode(result, InsertPos);
return *result;
}
ImportSet &ImportCache::getImportSet(const DeclContext *dc) {
dc = dc->getModuleScopeContext();
auto *file = dyn_cast<FileUnit>(dc);
auto *mod = dc->getParentModule();
if (!file)
dc = mod;
auto &ctx = mod->getASTContext();
auto found = ImportSetForDC.find(dc);
if (found != ImportSetForDC.end()) {
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ImportSetCacheHit++;
return *found->second;
}
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ImportSetCacheMiss++;
SmallVector<ModuleDecl::ImportedModule, 4> imports;
imports.emplace_back(ModuleDecl::AccessPathTy(), mod);
if (file) {
ModuleDecl::ImportFilter importFilter;
importFilter |= ModuleDecl::ImportFilterKind::Private;
importFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
file->getImportedModules(imports, importFilter);
}
auto &result = getImportSet(ctx, imports);
ImportSetForDC[dc] = &result;
return result;
}
ArrayRef<ModuleDecl::AccessPathTy> ImportCache::allocateArray(
ASTContext &ctx,
SmallVectorImpl<ModuleDecl::AccessPathTy> &results) {
if (results.empty())
return ArrayRef<ModuleDecl::AccessPathTy>();
else if (results.size() == 1 && results[0].empty())
return {&EmptyAccessPath, 1};
else
return ctx.AllocateCopy(results);
}
ArrayRef<ModuleDecl::AccessPathTy>
ImportCache::getAllVisibleAccessPaths(const ModuleDecl *mod,
const DeclContext *dc) {
dc = dc->getModuleScopeContext();
auto &ctx = mod->getASTContext();
auto key = std::make_pair(mod, dc);
auto found = VisibilityCache.find(key);
if (found != VisibilityCache.end()) {
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ModuleVisibilityCacheHit++;
return found->second;
}
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ModuleVisibilityCacheMiss++;
SmallVector<ModuleDecl::AccessPathTy, 1> accessPaths;
for (auto next : getImportSet(dc).getAllImports()) {
// If we found 'mod', record the access path.
if (next.second == mod) {
// Make sure the list of access paths is unique.
if (!llvm::is_contained(accessPaths, next.first))
accessPaths.push_back(next.first);
}
}
auto result = allocateArray(ctx, accessPaths);
VisibilityCache[key] = result;
return result;
}
ArrayRef<ModuleDecl::AccessPathTy>
ImportCache::getAllAccessPathsNotShadowedBy(const ModuleDecl *mod,
const ModuleDecl *other,
const DeclContext *dc) {
dc = dc->getModuleScopeContext();
auto *currentMod = dc->getParentModule();
auto &ctx = currentMod->getASTContext();
// Fast path.
if (currentMod == other)
return ArrayRef<ModuleDecl::AccessPathTy>();
auto key = std::make_tuple(mod, other, dc);
auto found = ShadowCache.find(key);
if (found != ShadowCache.end()) {
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ModuleShadowCacheHit++;
return found->second;
}
if (ctx.Stats)
ctx.Stats->getFrontendCounters().ModuleShadowCacheMiss++;
SmallVector<ModuleDecl::ImportedModule, 4> stack;
llvm::SmallDenseSet<ModuleDecl::ImportedModule, 32> visited;
stack.emplace_back(ModuleDecl::AccessPathTy(), currentMod);
if (auto *file = dyn_cast<FileUnit>(dc)) {
ModuleDecl::ImportFilter importFilter;
importFilter |= ModuleDecl::ImportFilterKind::Private;
importFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
file->getImportedModules(stack, importFilter);
}
SmallVector<ModuleDecl::AccessPathTy, 4> accessPaths;
while (!stack.empty()) {
auto next = stack.pop_back_val();
// Don't visit a module more than once.
if (!visited.insert(next).second)
continue;
// Don't visit the 'other' module's re-exports.
if (next.second == other)
continue;
// If we found 'mod' via some access path, remember the access
// path.
if (next.second == mod) {
// Make sure the list of access paths is unique.
if (!llvm::is_contained(accessPaths, next.first))
accessPaths.push_back(next.first);
}
collectExports(next, stack);
}
auto result = allocateArray(ctx, accessPaths);
ShadowCache[key] = result;
return result;
};
ArrayRef<ModuleDecl::ImportedModule>
swift::namelookup::getAllImports(const DeclContext *dc) {
return dc->getASTContext().getImportCache().getImportSet(dc)
.getAllImports();
}