Merge pull request #79035 from swiftlang/elsh/pcmo-imports

Package CMO: Enable serializing decls imported with `@_spiOnly` or `package import`.
This commit is contained in:
Ellie Shin
2025-01-30 13:15:33 -08:00
committed by GitHub
4 changed files with 119 additions and 34 deletions

View File

@@ -16,6 +16,7 @@
#define DEBUG_TYPE "cross-module-serialization-setup"
#include "swift/AST/Module.h"
#include "swift/AST/ImportCache.h"
#include "swift/Basic/Assertions.h"
#include "swift/IRGen/TBDGen.h"
#include "swift/SIL/ApplySite.h"
@@ -103,6 +104,11 @@ private:
bool canSerializeType(CanType type);
bool canSerializeDecl(NominalTypeDecl *decl);
/// Check whether decls imported with certain access levels or attributes
/// can be serialized.
/// The \p ctxt can e.g. be a NominalType or the context of a function.
bool checkImports(DeclContext *ctxt) const;
bool canUseFromInline(DeclContext *declCtxt);
bool canUseFromInline(SILFunction *func);
@@ -745,7 +751,12 @@ static bool couldBeLinkedStatically(DeclContext *funcCtxt, SILModule &module) {
// The stdlib module is always linked dynamically.
if (funcModule == module.getASTContext().getStdlibModule())
return false;
// An sdk or system module should be linked dynamically.
if (isPackageCMOEnabled(module.getSwiftModule()) &&
funcModule->isNonUserModule())
return false;
// Conservatively assume the function is in a statically linked module.
return true;
}
@@ -755,7 +766,7 @@ bool CrossModuleOptimization::canUseFromInline(DeclContext *declCtxt) {
if (everything)
return true;
if (!M.getSwiftModule()->canBeUsedForCrossModuleOptimization(declCtxt))
if (!checkImports(declCtxt))
return false;
/// If we are emitting a TBD file, the TBD file only contains public symbols
@@ -771,6 +782,52 @@ bool CrossModuleOptimization::canUseFromInline(DeclContext *declCtxt) {
return true;
}
bool CrossModuleOptimization::checkImports(DeclContext *ctxt) const {
ModuleDecl *moduleOfCtxt = ctxt->getParentModule();
// If the context defined in the same module - or is the same module, it's
// fine.
if (moduleOfCtxt == M.getSwiftModule())
return true;
ModuleDecl::ImportFilter filter;
if (isPackageCMOEnabled(M.getSwiftModule())) {
// If Package CMO is enabled, decls imported with `package import`
// or `@_spiOnly import` into this module should be allowed to be
// serialized. They are used in decls with `package` or higher
// access level, with or without @_spi; a client of this module
// should be able to access them directly if in the same package.
filter = { ModuleDecl::ImportFilterKind::ImplementationOnly };
} else {
// See if context is imported in a "regular" way, i.e. not with
// @_implementationOnly, `package import` or @_spiOnly.
filter = {
ModuleDecl::ImportFilterKind::ImplementationOnly,
ModuleDecl::ImportFilterKind::PackageOnly,
ModuleDecl::ImportFilterKind::SPIOnly
};
}
SmallVector<ImportedModule, 4> results;
M.getSwiftModule()->getImportedModules(results, filter);
auto &imports = M.getSwiftModule()->getASTContext().getImportCache();
for (auto &desc : results) {
if (imports.isImportedBy(moduleOfCtxt, desc.importedModule)) {
// E.g. `@_implementationOnly import QuartzCore_Private.CALayerPrivate`
// imports `Foundation` as its transitive dependency module; use of a
// a `public` decl in `Foundation` such as `IndexSet` in a function
// signature should not block serialization in Package CMO given the
// function has `package` or higher access level.
if (isPackageCMOEnabled(M.getSwiftModule()) &&
moduleOfCtxt->isNonUserModule())
continue;
return false;
}
}
return true;
}
/// Returns true if the function \p func can be used from a serialized function.
bool CrossModuleOptimization::canUseFromInline(SILFunction *function) {
if (everything)