mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Embedded] Introduce DeferredCodeGen feature.
Introduce an experimental feature DeferredCodeGen, that defers the generation of LLVM IR (and therefore object code) for all entities within an Embedded Swift module unless they have explicitly requested to not be emitted into the client (e.g., with `@_neverEmitIntoClient`). This feature is meant to generalize and subsume -emit-empty-object-file, relying on lazy emission of entities rather than abruptly ending the compilation pipeline before emitting any IR. Part of rdar://158363967.
This commit is contained in:
@@ -752,7 +752,7 @@ protected:
|
||||
HasAnyUnavailableDuringLoweringValues : 1
|
||||
);
|
||||
|
||||
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
|
||||
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+8,
|
||||
/// If the module is compiled as static library.
|
||||
StaticLibrary : 1,
|
||||
|
||||
@@ -821,7 +821,10 @@ protected:
|
||||
SerializePackageEnabled : 1,
|
||||
|
||||
/// Whether this module has enabled strict memory safety checking.
|
||||
StrictMemorySafety : 1
|
||||
StrictMemorySafety : 1,
|
||||
|
||||
/// Whether this module uses deferred code generation in Embedded Swift.
|
||||
DeferredCodeGen : 1
|
||||
);
|
||||
|
||||
SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2,
|
||||
|
||||
@@ -818,7 +818,7 @@ public:
|
||||
Bits.ModuleDecl.IsConcurrencyChecked = value;
|
||||
}
|
||||
|
||||
/// Whether this module has enable strict memory safety checking.
|
||||
/// Whether this module has enabled strict memory safety checking.
|
||||
bool strictMemorySafety() const {
|
||||
return Bits.ModuleDecl.StrictMemorySafety;
|
||||
}
|
||||
@@ -827,6 +827,15 @@ public:
|
||||
Bits.ModuleDecl.StrictMemorySafety = value;
|
||||
}
|
||||
|
||||
/// Whether this module uses deferred code generation.
|
||||
bool deferredCodeGen() const {
|
||||
return Bits.ModuleDecl.DeferredCodeGen;
|
||||
}
|
||||
|
||||
void setDeferredCodeGen(bool value = true) {
|
||||
Bits.ModuleDecl.DeferredCodeGen = value;
|
||||
}
|
||||
|
||||
bool isObjCNameLookupCachePopulated() const {
|
||||
return Bits.ModuleDecl.ObjCNameLookupCachePopulated;
|
||||
}
|
||||
|
||||
@@ -324,6 +324,12 @@ EXPERIMENTAL_FEATURE(KeyPathWithMethodMembers, false)
|
||||
// Whether to enable @_used and @_section attributes
|
||||
EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true)
|
||||
|
||||
// Whether to emit an Embedded Swift module with "deferred" code generation,
|
||||
// meaning that the only code that will be emitted into the object file is
|
||||
// code that was marked as "never emit into client". For everything else,
|
||||
// Swift still store the SIL and emit it into the client only when used.
|
||||
EXPERIMENTAL_FEATURE(DeferredCodeGen, true)
|
||||
|
||||
// Whether to compile scripts lazily in immediate mode
|
||||
EXPERIMENTAL_FEATURE(LazyImmediate, false)
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ class ExtendedValidationInfo {
|
||||
unsigned AllowNonResilientAccess: 1;
|
||||
unsigned SerializePackageEnabled: 1;
|
||||
unsigned StrictMemorySafety: 1;
|
||||
unsigned DeferredCodeGen: 1;
|
||||
} Bits;
|
||||
|
||||
public:
|
||||
@@ -252,6 +253,13 @@ public:
|
||||
Bits.StrictMemorySafety = val;
|
||||
}
|
||||
|
||||
bool deferredCodeGen() const {
|
||||
return Bits.DeferredCodeGen;
|
||||
}
|
||||
void setDeferredCodeGen(bool val = true) {
|
||||
Bits.DeferredCodeGen = val;
|
||||
}
|
||||
|
||||
bool hasCxxInteroperability() const { return Bits.HasCxxInteroperability; }
|
||||
void setHasCxxInteroperability(bool val) {
|
||||
Bits.HasCxxInteroperability = val;
|
||||
|
||||
@@ -84,6 +84,7 @@ UNINTERESTING_FEATURE(CodeItemMacros)
|
||||
UNINTERESTING_FEATURE(PreambleMacros)
|
||||
UNINTERESTING_FEATURE(TupleConformances)
|
||||
UNINTERESTING_FEATURE(SymbolLinkageMarkers)
|
||||
UNINTERESTING_FEATURE(DeferredCodeGen)
|
||||
UNINTERESTING_FEATURE(LazyImmediate)
|
||||
UNINTERESTING_FEATURE(MoveOnlyClasses)
|
||||
UNINTERESTING_FEATURE(NoImplicitCopy)
|
||||
|
||||
@@ -784,6 +784,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx,
|
||||
Bits.ModuleDecl.AllowNonResilientAccess = 0;
|
||||
Bits.ModuleDecl.SerializePackageEnabled = 0;
|
||||
Bits.ModuleDecl.StrictMemorySafety = 0;
|
||||
Bits.ModuleDecl.DeferredCodeGen = 0;
|
||||
|
||||
// Populate the module's files.
|
||||
SmallVector<FileUnit *, 2> files;
|
||||
|
||||
@@ -1511,6 +1511,9 @@ ModuleDecl *CompilerInstance::getMainModule() const {
|
||||
MainModule->setSerializePackageEnabled();
|
||||
if (Invocation.getLangOptions().hasFeature(Feature::StrictMemorySafety))
|
||||
MainModule->setStrictMemorySafety(true);
|
||||
if (Invocation.getLangOptions().hasFeature(Feature::Embedded) &&
|
||||
Invocation.getLangOptions().hasFeature(Feature::DeferredCodeGen))
|
||||
MainModule->setDeferredCodeGen(true);
|
||||
|
||||
configureAvailabilityDomains(getASTContext(),
|
||||
Invocation.getFrontendOptions(), MainModule);
|
||||
|
||||
@@ -1112,10 +1112,20 @@ bool SILDeclRef::declHasNonUniqueDefinition(const ValueDecl *decl) {
|
||||
if (decl->isNeverEmittedIntoClient())
|
||||
return false;
|
||||
|
||||
// If the declaration is not from the main module, treat its definition as
|
||||
// non-unique.
|
||||
/// @_alwaysEmitIntoClient means that we have a non-unique definition.
|
||||
if (decl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
|
||||
return true;
|
||||
|
||||
auto module = decl->getModuleContext();
|
||||
auto &ctx = module->getASTContext();
|
||||
|
||||
/// With deferred code generation, declarations are emitted as late as
|
||||
/// possible, so they must have non-unique definitions.
|
||||
if (module->deferredCodeGen())
|
||||
return true;
|
||||
|
||||
// If the declaration is not from the main module, treat its definition as
|
||||
// non-unique.
|
||||
return module != ctx.MainModule && ctx.MainModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -710,6 +710,9 @@ public:
|
||||
/// \c true if this module was built with strict memory safety.
|
||||
bool strictMemorySafety() const { return Core->strictMemorySafety(); }
|
||||
|
||||
/// \c true if this module uses deferred code generation.
|
||||
bool deferredCodeGen() const { return Core->deferredCodeGen(); }
|
||||
|
||||
/// Associates this module file with the AST node representing it.
|
||||
///
|
||||
/// Checks that the file is compatible with the AST module it's being loaded
|
||||
|
||||
@@ -226,6 +226,9 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
|
||||
case options_block::STRICT_MEMORY_SAFETY:
|
||||
extendedInfo.setStrictMemorySafety(true);
|
||||
break;
|
||||
case options_block::DEFERRED_CODE_GEN:
|
||||
extendedInfo.setDeferredCodeGen(true);
|
||||
break;
|
||||
default:
|
||||
// Unknown options record, possibly for use by a future version of the
|
||||
// module format.
|
||||
@@ -1520,6 +1523,7 @@ ModuleFileSharedCore::ModuleFileSharedCore(
|
||||
Bits.AllowNonResilientAccess = extInfo.allowNonResilientAccess();
|
||||
Bits.SerializePackageEnabled = extInfo.serializePackageEnabled();
|
||||
Bits.StrictMemorySafety = extInfo.strictMemorySafety();
|
||||
Bits.DeferredCodeGen = extInfo.deferredCodeGen();
|
||||
MiscVersion = info.miscVersion;
|
||||
SDKVersion = info.sdkVersion;
|
||||
ModuleABIName = extInfo.getModuleABIName();
|
||||
|
||||
@@ -421,8 +421,11 @@ private:
|
||||
/// Whether this module enabled strict memory safety.
|
||||
unsigned StrictMemorySafety : 1;
|
||||
|
||||
/// Whether this module used deferred code generation.
|
||||
unsigned DeferredCodeGen : 1;
|
||||
|
||||
// Explicitly pad out to the next word boundary.
|
||||
unsigned : 2;
|
||||
unsigned : 1;
|
||||
} Bits = {};
|
||||
static_assert(sizeof(ModuleBits) <= 8, "The bit set should be small");
|
||||
|
||||
@@ -696,6 +699,8 @@ public:
|
||||
|
||||
bool strictMemorySafety() const { return Bits.StrictMemorySafety; }
|
||||
|
||||
bool deferredCodeGen() const { return Bits.DeferredCodeGen; }
|
||||
|
||||
/// How should \p dependency be loaded for a transitive import via \c this?
|
||||
///
|
||||
/// If \p importNonPublicDependencies, more transitive dependencies
|
||||
|
||||
@@ -987,7 +987,8 @@ namespace options_block {
|
||||
CXX_STDLIB_KIND,
|
||||
PUBLIC_MODULE_NAME,
|
||||
SWIFT_INTERFACE_COMPILER_VERSION,
|
||||
STRICT_MEMORY_SAFETY
|
||||
STRICT_MEMORY_SAFETY,
|
||||
DEFERRED_CODE_GEN,
|
||||
};
|
||||
|
||||
using SDKPathLayout = BCRecordLayout<
|
||||
@@ -1088,6 +1089,10 @@ namespace options_block {
|
||||
STRICT_MEMORY_SAFETY
|
||||
>;
|
||||
|
||||
using DeferredCodeGenLayout = BCRecordLayout<
|
||||
DEFERRED_CODE_GEN
|
||||
>;
|
||||
|
||||
using PublicModuleNameLayout = BCRecordLayout<
|
||||
PUBLIC_MODULE_NAME,
|
||||
BCBlob
|
||||
|
||||
@@ -864,6 +864,7 @@ void Serializer::writeBlockInfoBlock() {
|
||||
BLOCK_RECORD(options_block, ALLOW_NON_RESILIENT_ACCESS);
|
||||
BLOCK_RECORD(options_block, SERIALIZE_PACKAGE_ENABLED);
|
||||
BLOCK_RECORD(options_block, STRICT_MEMORY_SAFETY);
|
||||
BLOCK_RECORD(options_block, DEFERRED_CODE_GEN);
|
||||
BLOCK_RECORD(options_block, CXX_STDLIB_KIND);
|
||||
BLOCK_RECORD(options_block, PUBLIC_MODULE_NAME);
|
||||
BLOCK_RECORD(options_block, SWIFT_INTERFACE_COMPILER_VERSION);
|
||||
@@ -1177,6 +1178,11 @@ void Serializer::writeHeader() {
|
||||
StrictMemorySafety.emit(ScratchRecord);
|
||||
}
|
||||
|
||||
if (M->deferredCodeGen()) {
|
||||
options_block::DeferredCodeGenLayout DeferredCodeGen(Out);
|
||||
DeferredCodeGen.emit(ScratchRecord);
|
||||
}
|
||||
|
||||
if (M->hasCxxInteroperability()) {
|
||||
options_block::HasCxxInteroperabilityEnabledLayout
|
||||
CxxInteroperabilityEnabled(Out);
|
||||
|
||||
@@ -976,6 +976,8 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
|
||||
M.setIsConcurrencyChecked();
|
||||
if (loadedModuleFile->strictMemorySafety())
|
||||
M.setStrictMemorySafety();
|
||||
if (loadedModuleFile->deferredCodeGen())
|
||||
M.setDeferredCodeGen();
|
||||
if (loadedModuleFile->hasCxxInteroperability()) {
|
||||
M.setHasCxxInteroperability();
|
||||
M.setCXXStdlibKind(loadedModuleFile->getCXXStdlibKind());
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
// RUN: %target-run %t/Application | %FileCheck %s
|
||||
|
||||
// Test #2: Root is an "intermediate" library for everything
|
||||
// RUN: %target-swift-frontend -c -emit-module -o %t/Root.o %t/Root.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -emit-module -o %t/Root.o %t/Root.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientA.o %t/ClientA.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientB.o %t/ClientB.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/Application.o %t/Application.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
@@ -41,26 +41,26 @@
|
||||
|
||||
// Test #3: ClientA as an "intermediate" library
|
||||
// RUN: %target-swift-frontend -c -emit-module -o %t/Root.o %t/Root.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientA.o %t/ClientA.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientA.o %t/ClientA.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientB.o %t/ClientB.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/Application.o %t/Application.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-clang %target-clang-resource-dir-opt %t/Root.o %t/ClientA.o %t/ClientB.o %t/Application.o -o %t/Application
|
||||
// RUN: %target-run %t/Application | %FileCheck %s
|
||||
|
||||
// Test #4: Root and ClientA as "intermediate" libraries
|
||||
// RUN: %target-swift-frontend -c -emit-module -o %t/Root.o %t/Root.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientA.o %t/ClientA.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -emit-module -o %t/Root.o %t/Root.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientA.o %t/ClientA.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientB.o %t/ClientB.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/Application.o %t/Application.swift -enable-experimental-feature Embedded -parse-as-library
|
||||
// RUN: %target-clang %target-clang-resource-dir-opt %t/Root.o %t/ClientA.o %t/ClientB.o %t/Application.o -o %t/Application
|
||||
// RUN: %target-run %t/Application | %FileCheck %s
|
||||
|
||||
// Test #%: All "intermediate", all the time. Main drives code generation
|
||||
// TODO: This requires -emit-empty-object-file to still emit the main symbol.
|
||||
// RUN: %target-swift-frontend -c -emit-module -o %t/Root.o %t/Root.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientA.o %t/ClientA.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientB.o %t/ClientB.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/Application.o %t/Application.swift -enable-experimental-feature Embedded -emit-empty-object-file -parse-as-library
|
||||
// TODO: @main needs to drive the generation of code here.
|
||||
// RUN: %target-swift-frontend -c -emit-module -o %t/Root.o %t/Root.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientA.o %t/ClientA.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/ClientB.o %t/ClientB.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN: %target-swift-frontend -c -I %t -emit-module -o %t/Application.o %t/Application.swift -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -parse-as-library
|
||||
// RUN-TODO: %target-clang %target-clang-resource-dir-opt %t/Root.o %t/ClientA.o %t/ClientB.o %t/Application.o -o %t/Application
|
||||
// RUN-TODO: %target-run %t/Application | %FileCheck %s
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
// REQUIRES: swift_in_compiler
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: swift_feature_Embedded
|
||||
// REQUIRES: swift_feature_DeferredCodeGen
|
||||
|
||||
//--- Root.swift
|
||||
struct Point {
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
// Library module
|
||||
|
||||
// SIL checking
|
||||
// RUN: %target-swift-frontend %t/Library.swift -parse-as-library -entry-point-function-name Library_main -enable-experimental-feature Embedded -emit-empty-object-file -emit-sil -emit-module-path %t/Modules/Library.swiftmodule -o - | %FileCheck -check-prefix LIBRARY-SIL %s
|
||||
// RUN: %target-swift-frontend %t/Library.swift -parse-as-library -entry-point-function-name Library_main -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -emit-sil -emit-module-path %t/Modules/Library.swiftmodule -o - | %FileCheck -check-prefix LIBRARY-SIL %s
|
||||
|
||||
// IR checking to ensure we get the right weak symbols.
|
||||
// RUN: %target-swift-frontend %t/Library.swift -parse-as-library -entry-point-function-name Library_main -enable-experimental-feature Embedded -emit-empty-object-file -emit-ir -o - | %FileCheck -check-prefix LIBRARY-IR --dump-input-filter all %s
|
||||
// RUN: %target-swift-frontend %t/Library.swift -parse-as-library -entry-point-function-name Library_main -enable-experimental-feature Embedded -enable-experimental-feature DeferredCodeGen -emit-ir -o - | %FileCheck -check-prefix LIBRARY-IR --dump-input-filter all %s
|
||||
|
||||
// Application module
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
|
||||
// REQUIRES: swift_in_compiler
|
||||
// REQUIRES: swift_feature_Embedded
|
||||
// REQUIRES: swift_feature_DeferredCodeGen
|
||||
|
||||
//--- Library.swift
|
||||
|
||||
// TODO: These are expected once "emit empty object file" becomes "be lazy".
|
||||
// LIBRARY-IR-NOT: @"$es23_swiftEmptyArrayStorageSi_S3itvp" = linkonce_odr {{(protected |dllexport )?}}global
|
||||
// LIBRARY-IR-NOT: @"$es16_emptyBoxStorageSi_Sitvp" = linkonce_odr {{(protected |dllexport )?}}global
|
||||
// TODO: Once global variables can be emitted lazily, these should be -NOT
|
||||
// again, then show up in the application binary if we use them.
|
||||
// LIBRARY-IR: @"$es23_swiftEmptyArrayStorageSi_S3itvp" = weak_odr {{(protected |dllexport )?}}global
|
||||
// LIBRARY-IR: @"$es16_emptyBoxStorageSi_Sitvp" = weak_odr {{(protected |dllexport )?}}global
|
||||
|
||||
// LIBRARY-IR-NOT: define {{.*}}@"$e7Library5helloSaySiGyF"()
|
||||
public func hello() -> [Int] {
|
||||
@@ -47,13 +49,17 @@ private func throughPrivate() -> [Int] {
|
||||
// LIBRARY-IR-NOT: unnecessary
|
||||
public func unnecessary() -> Int64 { 5 }
|
||||
|
||||
// LIBRARY-IR: define {{.*}} @"$e7Library14unusedYetThere
|
||||
@_neverEmitIntoClient
|
||||
public func unusedYetThere() -> Int64 { 5 }
|
||||
|
||||
// LIBRARY-IR-NOT: define swiftcc
|
||||
// LIBRARY-IR-NOT: define hidden swiftcc
|
||||
|
||||
// LIBRARY-IR-NOT: define {{.*}} @"$es27_allocateUninitializedArrayySayxG_BptBwlFSi_Tg5"
|
||||
|
||||
// LIBRARY-SIL: sil [ossa] @$e7Library5helloSaySiGyF
|
||||
// LIBRARY-SIL: sil [ossa] @$e7Library8getArraySaySiGyF : $@convention(thin) () -> @owned Array<Int> {
|
||||
// LIBRARY-SIL: sil @$e7Library5helloSaySiGyF
|
||||
// LIBRARY-SIL: sil @$e7Library8getArraySaySiGyF : $@convention(thin) () -> @owned Array<Int> {
|
||||
|
||||
//--- Application.swift
|
||||
import Library
|
||||
|
||||
Reference in New Issue
Block a user