Skip to content

[6.0] Cherry-pick a couple of compile-time performance optimizations #76151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions include/swift/AST/FileUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,6 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
const ModuleDecl *importedModule,
llvm::SmallSetVector<Identifier, 4> &spiGroups) const {};

/// Checks whether this file imports \c module as \c @_weakLinked.
virtual bool importsModuleAsWeakLinked(const ModuleDecl *module) const {
// For source files, this should be overridden to inspect the import
// declarations in the file. Other kinds of file units, like serialized
// modules, can just use this default implementation since the @_weakLinked
// attribute is not transitive. If module C is imported @_weakLinked by
// module B, that does not imply that module A imports module C @_weakLinked
// if it imports module B.
return false;
}

virtual std::optional<Fingerprint>
loadFingerprint(const IterableDeclContext *IDC) const {
return std::nullopt;
Expand Down
12 changes: 12 additions & 0 deletions include/swift/AST/ImportCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,18 @@ class alignas(ImportedModule) ImportCache {
llvm::DenseMap<std::tuple<const ModuleDecl *,
const DeclContext *>,
bool> SwiftOnlyCache;
llvm::DenseMap<const ModuleDecl *, ArrayRef<ModuleDecl *>> WeakCache;

ImportPath::Access EmptyAccessPath;

ArrayRef<ImportPath::Access> allocateArray(
ASTContext &ctx,
SmallVectorImpl<ImportPath::Access> &results);

ArrayRef<ModuleDecl *> allocateArray(
ASTContext &ctx,
llvm::SetVector<ModuleDecl *> &results);

ImportSet &getImportSet(ASTContext &ctx,
ArrayRef<ImportedModule> topLevelImports);

Expand Down Expand Up @@ -167,6 +172,13 @@ class alignas(ImportedModule) ImportCache {
const ModuleDecl *other,
const DeclContext *dc);

/// Returns all weak-linked imported modules.
ArrayRef<ModuleDecl *>
getWeakImports(const ModuleDecl *mod);

bool isWeakImportedBy(const ModuleDecl *mod,
const ModuleDecl *from);

/// This is a hack to cope with main file parsing and REPL parsing, where
/// we can add ImportDecls after import resolution.
void clear() {
Expand Down
3 changes: 0 additions & 3 deletions include/swift/AST/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,6 @@ class SourceFile final : public FileUnit {
const ModuleDecl *importedModule,
llvm::SmallSetVector<Identifier, 4> &spiGroups) const override;

/// Is \p module imported as \c @_weakLinked by this file?
bool importsModuleAsWeakLinked(const ModuleDecl *module) const override;

// Is \p targetDecl accessible as an explicitly imported SPI from this file?
bool isImportedAsSPI(const ValueDecl *targetDecl) const;

Expand Down
54 changes: 54 additions & 0 deletions lib/AST/ImportCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/AST/FileUnit.h"
#include "swift/AST/ImportCache.h"
#include "swift/AST/Module.h"
#include "swift/AST/SourceFile.h"

using namespace swift;
using namespace namelookup;
Expand Down Expand Up @@ -391,3 +392,56 @@ swift::namelookup::getAllImports(const DeclContext *dc) {
return dc->getASTContext().getImportCache().getImportSet(dc)
.getAllImports();
}

ArrayRef<ModuleDecl *> ImportCache::allocateArray(
ASTContext &ctx,
llvm::SetVector<ModuleDecl *> &results) {
if (results.empty())
return {};
else
return ctx.AllocateCopy(results.getArrayRef());
}

ArrayRef<ModuleDecl *>
ImportCache::getWeakImports(const ModuleDecl *mod) {
auto found = WeakCache.find(mod);
if (found != WeakCache.end())
return found->second;

llvm::SetVector<ModuleDecl *> result;

for (auto file : mod->getFiles()) {
auto *sf = dyn_cast<SourceFile>(file);
// Other kinds of file units, like serialized modules, can just use this
// default implementation since the @_weakLinked attribute is not
// transitive. If module C is imported @_weakLinked by module B, that does
// not imply that module A imports module C @_weakLinked if it imports
// module B.
if (!sf)
continue;

for (auto &import : sf->getImports()) {
if (!import.options.contains(ImportFlags::WeakLinked))
continue;

ModuleDecl *importedModule = import.module.importedModule;
result.insert(importedModule);

auto reexportedModules = getImportSet(importedModule).getAllImports();
for (auto reexportedModule : reexportedModules) {
result.insert(reexportedModule.importedModule);
}
}
}

auto resultArray = allocateArray(mod->getASTContext(), result);
WeakCache[mod] = resultArray;
return resultArray;
}

bool ImportCache::isWeakImportedBy(const ModuleDecl *mod,
const ModuleDecl *from) {
auto weakImports = getWeakImports(from);
return std::find(weakImports.begin(), weakImports.end(), mod)
!= weakImports.end();
}
67 changes: 23 additions & 44 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2136,45 +2136,57 @@ bool ModuleDecl::registerEntryPointFile(
}

void ModuleDecl::collectLinkLibraries(LinkLibraryCallback callback) const {
// FIXME: The proper way to do this depends on the decls used.
FORWARD(collectLinkLibraries, (callback));
}
bool hasSourceFile = false;

for (auto *file : getFiles()) {
if (isa<SourceFile>(file)) {
hasSourceFile = true;
} else {
file->collectLinkLibraries(callback);
}

if (auto *synth = file->getSynthesizedFile()) {
synth->collectLinkLibraries(callback);
}
}

if (!hasSourceFile)
return;

void
SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const {
llvm::SmallDenseSet<ModuleDecl *, 32> visited;
SmallVector<ImportedModule, 32> stack;

ModuleDecl::ImportFilter filter = {
ModuleDecl::ImportFilterKind::Exported,
ModuleDecl::ImportFilterKind::Default};

auto *topLevel = getParentModule();

ModuleDecl::ImportFilter topLevelFilter = filter;
topLevelFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;
topLevelFilter |= ModuleDecl::ImportFilterKind::InternalOrBelow;
topLevelFilter |= ModuleDecl::ImportFilterKind::PackageOnly,
topLevelFilter |= ModuleDecl::ImportFilterKind::SPIOnly;
topLevel->getImportedModules(stack, topLevelFilter);
getImportedModules(stack, topLevelFilter);

// Make sure the top-level module is first; we want pre-order-ish traversal.
stack.emplace_back(ImportPath::Access(), topLevel);
stack.emplace_back(ImportPath::Access(), const_cast<ModuleDecl *>(this));

while (!stack.empty()) {
auto next = stack.pop_back_val().importedModule;

if (!visited.insert(next).second)
continue;

if (next->getName() != getParentModule()->getName()) {
if (next->getName() != getName()) {
next->collectLinkLibraries(callback);
}

next->getImportedModules(stack, filter);
}
}

void
SourceFile::collectLinkLibraries(ModuleDecl::LinkLibraryCallback callback) const {}

bool ModuleDecl::walk(ASTWalker &Walker) {
llvm::SaveAndRestore<ASTWalker::ParentTy> SAR(Walker.Parent, this);
for (auto SF : getFiles())
Expand Down Expand Up @@ -3090,35 +3102,6 @@ bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const {
return false;
}

bool SourceFile::importsModuleAsWeakLinked(const ModuleDecl *module) const {
for (auto &import : *Imports) {
if (!import.options.contains(ImportFlags::WeakLinked))
continue;

const ModuleDecl *importedModule = import.module.importedModule;
if (module == importedModule)
return true;

// Also check whether the target module is actually the underlyingClang
// module for this @_weakLinked import.
const ModuleDecl *clangModule =
importedModule->getUnderlyingModuleIfOverlay();
if (module == clangModule)
return true;

// Traverse the exported modules of this weakly-linked module to ensure
// that we weak-link declarations from its exported peers.
SmallVector<ImportedModule, 8> reexportedModules;
importedModule->getImportedModules(reexportedModules,
ModuleDecl::ImportFilterKind::Exported);
for (const ImportedModule &reexportedModule : reexportedModules) {
if (module == reexportedModule.importedModule)
return true;
}
}
return false;
}

bool ModuleDecl::isImportedAsSPI(const SpecializeAttr *attr,
const ValueDecl *targetDecl) const {
auto declSPIGroups = attr->getSPIGroups();
Expand Down Expand Up @@ -3150,11 +3133,7 @@ bool ModuleDecl::isImportedAsSPI(Identifier spiGroup,
}

bool ModuleDecl::isImportedAsWeakLinked(const ModuleDecl *module) const {
for (auto file : getFiles()) {
if (file->importsModuleAsWeakLinked(module))
return true;
}
return false;
return getASTContext().getImportCache().isWeakImportedBy(module, this);
}

bool Decl::isSPI() const {
Expand Down
97 changes: 0 additions & 97 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,103 +469,6 @@ void IRGenModule::emitSourceFile(SourceFile &SF) {
emitGlobalDecl(localDecl);
for (auto *opaqueDecl : SF.getOpaqueReturnTypeDecls())
maybeEmitOpaqueTypeDecl(opaqueDecl);

SF.collectLinkLibraries([this](LinkLibrary linkLib) {
this->addLinkLibrary(linkLib);
});

if (ObjCInterop)
this->addLinkLibrary(LinkLibrary("objc", LibraryKind::Library));

// If C++ interop is enabled, add -lc++ on Darwin and -lstdc++ on linux.
// Also link with C++ bridging utility module (Cxx) and C++ stdlib overlay
// (std) if available.
if (Context.LangOpts.EnableCXXInterop) {
const llvm::Triple &target = Context.LangOpts.Target;
if (target.isOSDarwin())
this->addLinkLibrary(LinkLibrary("c++", LibraryKind::Library));
else if (target.isOSLinux())
this->addLinkLibrary(LinkLibrary("stdc++", LibraryKind::Library));

// Do not try to link Cxx with itself.
if (!getSwiftModule()->getName().is("Cxx")) {
bool isStatic = false;
if (const auto *M = Context.getModuleByName("Cxx"))
isStatic = M->isStaticLibrary();
this->addLinkLibrary(LinkLibrary(target.isOSWindows() && isStatic
? "libswiftCxx"
: "swiftCxx",
LibraryKind::Library));
}

// Do not try to link CxxStdlib with the C++ standard library, Cxx or
// itself.
if (llvm::none_of(llvm::ArrayRef{"Cxx", "CxxStdlib", "std"},
[M = getSwiftModule()->getName().str()](StringRef Name) {
return M == Name;
})) {
// Only link with CxxStdlib on platforms where the overlay is available.
switch (target.getOS()) {
case llvm::Triple::Linux:
if (!target.isAndroid())
this->addLinkLibrary(LinkLibrary("swiftCxxStdlib",
LibraryKind::Library));
break;
case llvm::Triple::Win32: {
bool isStatic = Context.getModuleByName("CxxStdlib")->isStaticLibrary();
this->addLinkLibrary(
LinkLibrary(isStatic ? "libswiftCxxStdlib" : "swiftCxxStdlib",
LibraryKind::Library));
break;
}
default:
if (target.isOSDarwin())
this->addLinkLibrary(LinkLibrary("swiftCxxStdlib",
LibraryKind::Library));
break;
}
}
}

// FIXME: It'd be better to have the driver invocation or build system that
// executes the linker introduce these compatibility libraries, since at
// that point we know whether we're building an executable, which is the only
// place where the compatibility libraries take effect. For the benefit of
// build systems that build Swift code, but don't use Swift to drive
// the linker, we can also use autolinking to pull in the compatibility
// libraries. This may however cause the library to get pulled in in
// situations where it isn't useful, such as for dylibs, though this is
// harmless aside from code size.
if (!IRGen.Opts.UseJIT && !Context.LangOpts.hasFeature(Feature::Embedded)) {
auto addBackDeployLib = [&](llvm::VersionTuple version,
StringRef libraryName, bool forceLoad) {
std::optional<llvm::VersionTuple> compatibilityVersion;
if (libraryName == "swiftCompatibilityDynamicReplacements") {
compatibilityVersion = IRGen.Opts.
AutolinkRuntimeCompatibilityDynamicReplacementLibraryVersion;
} else if (libraryName == "swiftCompatibilityConcurrency") {
compatibilityVersion =
IRGen.Opts.AutolinkRuntimeCompatibilityConcurrencyLibraryVersion;
} else {
compatibilityVersion = IRGen.Opts.
AutolinkRuntimeCompatibilityLibraryVersion;
}

if (!compatibilityVersion)
return;

if (*compatibilityVersion > version)
return;

this->addLinkLibrary(LinkLibrary(libraryName,
LibraryKind::Library,
forceLoad));
};

#define BACK_DEPLOYMENT_LIB(Version, Filter, LibraryName, ForceLoad) \
addBackDeployLib(llvm::VersionTuple Version, LibraryName, ForceLoad);
#include "swift/Frontend/BackDeploymentLibs.def"
}
}

/// Emit all the top-level code in the synthesized file unit.
Expand Down
16 changes: 7 additions & 9 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1180,13 +1180,11 @@ GeneratedModule IRGenRequest::evaluate(Evaluator &evaluator,
if (auto *synthSFU = file->getSynthesizedFile()) {
IGM.emitSynthesizedFileUnit(*synthSFU);
}
} else {
file->collectLinkLibraries([&IGM](LinkLibrary LinkLib) {
IGM.addLinkLibrary(LinkLib);
});
}
}

IGM.addLinkLibraries();

// Okay, emit any definitions that we suddenly need.
irgen.emitLazyDefinitions();

Expand Down Expand Up @@ -1401,7 +1399,6 @@ static void performParallelIRGeneration(IRGenDescriptor desc) {
IRGenModule *IGM = new IRGenModule(
irgen, std::move(targetMachine), nextSF, desc.ModuleName, *OutputIter++,
nextSF->getFilename(), nextSF->getPrivateDiscriminator().str());
IGMcreated = true;

initLLVMModule(*IGM, *SILMod);
if (!DidRunSILCodeGenPreparePasses) {
Expand All @@ -1412,6 +1409,11 @@ static void performParallelIRGeneration(IRGenDescriptor desc) {
}

(void)layoutStringsEnabled(*IGM, /*diagnose*/ true);

// Only need to do this once.
if (!IGMcreated)
IGM->addLinkLibraries();
IGMcreated = true;
}

if (!IGMcreated) {
Expand All @@ -1434,10 +1436,6 @@ static void performParallelIRGeneration(IRGenDescriptor desc) {
CurrentIGMPtr IGM = irgen.getGenModule(synthSFU);
IGM->emitSynthesizedFileUnit(*synthSFU);
}
} else {
File->collectLinkLibraries([&](LinkLibrary LinkLib) {
irgen.getPrimaryIGM()->addLinkLibrary(LinkLib);
});
}
}

Expand Down
Loading