Skip to content

Commit 25eb997

Browse files
committed
[embedded] Add basics of module serialization, importing and validation in embedded Swift.
- Add a flag to the serialized module (IsEmbeddedSwiftModule) - Check on import that the mode matches (don't allow importing non-embedded module in embedded mode and vice versa) - Drop TBD support, it's not expected to work in embedded Swift for now - Drop auto-linking backdeploy libraries, it's not expected to backdeploy embedded Swift for now - Drop prespecializations, not expected to work in embedded Swift for now - Use CMO to serialize everything when emitting an embedded Swift module - Change SILLinker to deserialize/import everything when importing an embedded Swift module - Add an IR test for importing modules - Add a deserialization validation test
1 parent 74e22dc commit 25eb997

25 files changed

+188
-21
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
663663
HasAnyUnavailableDuringLoweringValues : 1
664664
);
665665

666-
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1,
666+
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1,
667667
/// If the module is compiled as static library.
668668
StaticLibrary : 1,
669669

@@ -707,6 +707,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
707707
/// Whether this module was built with -experimental-hermetic-seal-at-link.
708708
HasHermeticSealAtLink : 1,
709709

710+
/// Whether this module was built with embedded Swift.
711+
IsEmbeddedSwiftModule : 1,
712+
710713
/// Whether this module has been compiled with comprehensive checking for
711714
/// concurrency, e.g., Sendable checking.
712715
IsConcurrencyChecked : 1,

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,12 @@ ERROR(need_hermetic_seal_to_import_module,none,
872872
"module %0 was built with -experimental-hermetic-seal-at-link, but "
873873
"current compilation does not have -experimental-hermetic-seal-at-link",
874874
(Identifier))
875+
ERROR(cannot_import_embedded_module,none,
876+
"module %0 cannot be imported because it was built with embedded Swift",
877+
(Identifier))
878+
ERROR(cannot_import_non_embedded_module,none,
879+
"module %0 cannot be imported in embedded Swift mode",
880+
(Identifier))
875881
ERROR(need_cxx_interop_to_import_module,none,
876882
"module %0 was built with C++ interoperability enabled, but "
877883
"current compilation does not enable C++ interoperability",

include/swift/AST/Module.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,14 @@ class ModuleDecl
656656
Bits.ModuleDecl.HasHermeticSealAtLink = enabled;
657657
}
658658

659+
/// Returns true if this module was built with embedded Swift
660+
bool isEmbeddedSwiftModule() const {
661+
return Bits.ModuleDecl.IsEmbeddedSwiftModule;
662+
}
663+
void setIsEmbeddedSwiftModule(bool enabled = true) {
664+
Bits.ModuleDecl.IsEmbeddedSwiftModule = enabled;
665+
}
666+
659667
/// Returns true if this module was built with C++ interoperability enabled.
660668
bool hasCxxInteroperability() const {
661669
return Bits.ModuleDecl.HasCxxInteroperability;

include/swift/Serialization/SerializationOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ namespace swift {
155155
bool DisableCrossModuleIncrementalInfo = false;
156156
bool StaticLibrary = false;
157157
bool HermeticSealAtLink = false;
158+
bool EmbeddedSwiftModule = false;
158159
bool IsOSSA = false;
159160
bool SerializeExternalDeclsOnly = false;
160161
};

include/swift/Serialization/Validation.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class ExtendedValidationInfo {
128128
unsigned IsSIB : 1;
129129
unsigned IsStaticLibrary : 1;
130130
unsigned HasHermeticSealAtLink : 1;
131+
unsigned IsEmbeddedSwiftModule : 1;
131132
unsigned IsTestable : 1;
132133
unsigned ResilienceStrategy : 2;
133134
unsigned IsImplicitDynamicEnabled : 1;
@@ -181,6 +182,10 @@ class ExtendedValidationInfo {
181182
void setHasHermeticSealAtLink(bool val) {
182183
Bits.HasHermeticSealAtLink = val;
183184
}
185+
bool isEmbeddedSwiftModule() const { return Bits.IsEmbeddedSwiftModule; }
186+
void setIsEmbeddedSwiftModule(bool val) {
187+
Bits.IsEmbeddedSwiftModule = val;
188+
}
184189
bool isTestable() const { return Bits.IsTestable; }
185190
void setIsTestable(bool val) {
186191
Bits.IsTestable = val;

lib/AST/Module.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx,
718718
Bits.ModuleDecl.IsMainModule = 0;
719719
Bits.ModuleDecl.HasIncrementalInfo = 0;
720720
Bits.ModuleDecl.HasHermeticSealAtLink = 0;
721+
Bits.ModuleDecl.IsEmbeddedSwiftModule = 0;
721722
Bits.ModuleDecl.IsConcurrencyChecked = 0;
722723
Bits.ModuleDecl.ObjCNameLookupCachePopulated = 0;
723724
Bits.ModuleDecl.HasCxxInteroperability = 0;

lib/Frontend/Frontend.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ SerializationOptions CompilerInvocation::computeSerializationOptions(
223223

224224
serializationOpts.HermeticSealAtLink = opts.HermeticSealAtLink;
225225

226+
serializationOpts.EmbeddedSwiftModule =
227+
LangOpts.hasFeature(Feature::Embedded);
228+
226229
serializationOpts.IsOSSA = getSILOptions().EnableOSSAModules;
227230

228231
serializationOpts.SerializeExternalDeclsOnly =

lib/FrontendTool/FrontendTool.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,11 @@ static bool validateTBDIfNeeded(const CompilerInvocation &Invocation,
15571557
return false;
15581558
}
15591559

1560+
// Embedded Swift does not support TBD.
1561+
if (Invocation.getLangOptions().hasFeature(Feature::Embedded)) {
1562+
return false;
1563+
}
1564+
15601565
// Cross-module optimization does not support TBD.
15611566
if (Invocation.getSILOptions().CMOMode == CrossModuleOptimizationMode::Aggressive) {
15621567
return false;

lib/IRGen/GenDecl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ void IRGenModule::emitSourceFile(SourceFile &SF) {
536536
// libraries. This may however cause the library to get pulled in in
537537
// situations where it isn't useful, such as for dylibs, though this is
538538
// harmless aside from code size.
539-
if (!IRGen.Opts.UseJIT) {
539+
if (!IRGen.Opts.UseJIT && !Context.LangOpts.hasFeature(Feature::Embedded)) {
540540
auto addBackDeployLib = [&](llvm::VersionTuple version,
541541
StringRef libraryName, bool forceLoad) {
542542
llvm::Optional<llvm::VersionTuple> compatibilityVersion;
@@ -1416,7 +1416,6 @@ void IRGenerator::emitLazyDefinitions() {
14161416
LazySpecializedTypeMetadataRecords.clear();
14171417
LazyTypeContextDescriptors.clear();
14181418
LazyOpaqueTypeDescriptors.clear();
1419-
LazyFunctionDefinitions.clear();
14201419
LazyCanonicalSpecializedMetadataAccessors.clear();
14211420
LazyMetadataAccessors.clear();
14221421
LazyWitnessTables.clear();

lib/IRGen/MetadataRequest.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3303,7 +3303,11 @@ llvm::Value *IRGenFunction::emitTypeMetadataRef(CanType type) {
33033303
MetadataResponse
33043304
IRGenFunction::emitTypeMetadataRef(CanType type,
33053305
DynamicMetadataRequest request) {
3306-
assert(!type->getASTContext().LangOpts.hasFeature(Feature::Embedded));
3306+
if (type->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
3307+
llvm::errs() << "Metadata pointer requested in embedded Swift for type "
3308+
<< type << "\n";
3309+
assert(0 && "metadata used in embedded mode");
3310+
}
33073311

33083312
type = IGM.getRuntimeReifiedType(type);
33093313
// Look through any opaque types we're allowed to.

lib/SIL/IR/Linker.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ void SILLinkerVisitor::maybeAddFunctionToWorklist(SILFunction *F,
120120
return;
121121
}
122122

123-
// In the performance pipeline, we deserialize all reachable functions.
123+
// In the performance pipeline or embedded mode, we deserialize all reachable
124+
// functions.
124125
if (isLinkAll())
125126
return deserializeAndPushToWorklist(F);
126127

@@ -435,6 +436,11 @@ void SILLinkerVisitor::process() {
435436
Fn->setSerialized(IsSerialized_t::IsNotSerialized);
436437
}
437438

439+
// TODO: This should probably be done as a separate SIL pass ("internalize")
440+
if (Fn->getModule().getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
441+
Fn->setLinkage(stripExternalFromLinkage(Fn->getLinkage()));
442+
}
443+
438444
LLVM_DEBUG(llvm::dbgs() << "Process imports in function: "
439445
<< Fn->getName() << "\n");
440446

lib/SIL/IR/Linker.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, void> {
142142

143143
/// Is the current mode link all? Link all implies we should try and link
144144
/// everything, not just transparent/shared functions.
145-
bool isLinkAll() const { return Mode == LinkingMode::LinkAll; }
145+
bool isLinkAll() const {
146+
return Mode == LinkingMode::LinkAll ||
147+
Mod.getASTContext().LangOpts.hasFeature(Feature::Embedded);
148+
}
146149

147150
void linkInVTable(ClassDecl *D);
148151

lib/SIL/IR/SILModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,10 @@ void SILModule::performOnceForPrespecializedImportedExtensions(
898898
if (prespecializedFunctionDeclsImported)
899899
return;
900900

901+
// No prespecitalizations in embedded Swift
902+
if (getASTContext().LangOpts.hasFeature(Feature::Embedded))
903+
return;
904+
901905
SmallVector<ModuleDecl *, 8> importedModules;
902906
// Add the Swift module.
903907
if (!isStdlibModule()) {

lib/SILOptimizer/IPO/CrossModuleOptimization.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
442442
if (function->hasSemanticsAttr("optimize.no.crossmodule"))
443443
return false;
444444

445-
if (SerializeEverything)
445+
// In embedded Swift we serialize everything.
446+
if (SerializeEverything ||
447+
function->getASTContext().LangOpts.hasFeature(Feature::Embedded))
446448
return true;
447449

448450
if (!conservative) {
@@ -652,15 +654,18 @@ class CrossModuleOptimizationPass: public SILModuleTransform {
652654
return;
653655

654656
bool conservative = false;
655-
switch (M.getOptions().CMOMode) {
656-
case swift::CrossModuleOptimizationMode::Off:
657-
return;
658-
case swift::CrossModuleOptimizationMode::Default:
659-
conservative = true;
660-
break;
661-
case swift::CrossModuleOptimizationMode::Aggressive:
662-
conservative = false;
663-
break;
657+
// In embedded Swift we serialize everything.
658+
if (!M.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
659+
switch (M.getOptions().CMOMode) {
660+
case swift::CrossModuleOptimizationMode::Off:
661+
return;
662+
case swift::CrossModuleOptimizationMode::Default:
663+
conservative = true;
664+
break;
665+
case swift::CrossModuleOptimizationMode::Aggressive:
666+
conservative = false;
667+
break;
668+
}
664669
}
665670

666671
CrossModuleOptimization CMO(M, conservative);

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,9 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) {
10041004
// inlinable functions from imported ones.
10051005
P.addOnonePrespecializations();
10061006

1007+
// For embedded Swift: CMO is used to serialize libraries.
1008+
P.addCrossModuleOptimization();
1009+
10071010
// First serialize the SIL if we are asked to.
10081011
P.startPipeline("Serialization");
10091012
P.addSerializeSILPass();

lib/Serialization/ModuleFile.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,11 @@ class ModuleFile
621621
return Core->Bits.HasHermeticSealAtLink;
622622
}
623623

624+
/// Whether this module was built using embedded Swift.
625+
bool isEmbeddedSwiftModule() const {
626+
return Core->Bits.IsEmbeddedSwiftModule;
627+
}
628+
624629
/// Whether this module was built with C++ interoperability enabled.
625630
bool hasCxxInteroperability() const {
626631
return Core->Bits.HasCxxInteroperability;

lib/Serialization/ModuleFileSharedCore.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ static bool readOptionsBlock(llvm::BitstreamCursor &cursor,
158158
case options_block::HAS_HERMETIC_SEAL_AT_LINK:
159159
extendedInfo.setHasHermeticSealAtLink(true);
160160
break;
161+
case options_block::IS_EMBEDDED_SWIFT_MODULE:
162+
extendedInfo.setIsEmbeddedSwiftModule(true);
163+
break;
161164
case options_block::IS_TESTABLE:
162165
extendedInfo.setIsTestable(true);
163166
break;
@@ -1405,6 +1408,7 @@ ModuleFileSharedCore::ModuleFileSharedCore(
14051408
Bits.IsSIB = extInfo.isSIB();
14061409
Bits.IsStaticLibrary = extInfo.isStaticLibrary();
14071410
Bits.HasHermeticSealAtLink = extInfo.hasHermeticSealAtLink();
1411+
Bits.IsEmbeddedSwiftModule = extInfo.isEmbeddedSwiftModule();
14081412
Bits.IsTestable = extInfo.isTestable();
14091413
Bits.ResilienceStrategy = unsigned(extInfo.getResilienceStrategy());
14101414
Bits.IsImplicitDynamicEnabled = extInfo.isImplicitDynamicEnabled();

lib/Serialization/ModuleFileSharedCore.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,9 @@ class ModuleFileSharedCore {
367367
/// Whether this module was built with -experimental-hermetic-seal-at-link.
368368
unsigned HasHermeticSealAtLink : 1;
369369

370+
/// Whether this module was built with embedded Swift.
371+
unsigned IsEmbeddedSwiftModule : 1;
372+
370373
/// Whether this module file is compiled with '-enable-testing'.
371374
unsigned IsTestable : 1;
372375

lib/Serialization/ModuleFormat.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 803; // removed initializes and accesses attributes
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 804; // embedded swift modules
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -885,6 +885,7 @@ namespace options_block {
885885
IS_SIB,
886886
IS_STATIC_LIBRARY,
887887
HAS_HERMETIC_SEAL_AT_LINK,
888+
IS_EMBEDDED_SWIFT_MODULE,
888889
IS_TESTABLE,
889890
RESILIENCE_STRATEGY,
890891
ARE_PRIVATE_IMPORTS_ENABLED,
@@ -928,6 +929,10 @@ namespace options_block {
928929
HAS_HERMETIC_SEAL_AT_LINK
929930
>;
930931

932+
using IsEmbeddedSwiftModuleLayout = BCRecordLayout<
933+
IS_EMBEDDED_SWIFT_MODULE
934+
>;
935+
931936
using IsTestableLayout = BCRecordLayout<
932937
IS_TESTABLE
933938
>;

lib/Serialization/Serialization.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,11 @@ void Serializer::writeHeader() {
10381038
HasHermeticSealAtLink.emit(ScratchRecord);
10391039
}
10401040

1041+
if (Options.EmbeddedSwiftModule) {
1042+
options_block::IsEmbeddedSwiftModuleLayout IsEmbeddedSwiftModule(Out);
1043+
IsEmbeddedSwiftModule.emit(ScratchRecord);
1044+
}
1045+
10411046
if (M->isTestingEnabled()) {
10421047
options_block::IsTestableLayout IsTestable(Out);
10431048
IsTestable.emit(ScratchRecord);

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,7 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
826826
fileUnit = new (Ctx) SerializedASTFile(M, *loadedModuleFile);
827827
M.setStaticLibrary(loadedModuleFile->isStaticLibrary());
828828
M.setHasHermeticSealAtLink(loadedModuleFile->hasHermeticSealAtLink());
829+
M.setIsEmbeddedSwiftModule(loadedModuleFile->isEmbeddedSwiftModule());
829830
if (loadedModuleFile->isTestable())
830831
M.setTestingEnabled();
831832
if (loadedModuleFile->arePrivateImportsEnabled())
@@ -916,6 +917,18 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
916917
diag::need_hermetic_seal_to_import_module, M.getName());
917918
}
918919

920+
if (M.isEmbeddedSwiftModule() &&
921+
!Ctx.LangOpts.hasFeature(Feature::Embedded)) {
922+
Ctx.Diags.diagnose(diagLoc.value_or(SourceLoc()),
923+
diag::cannot_import_embedded_module, M.getName());
924+
}
925+
926+
if (!M.isEmbeddedSwiftModule() &&
927+
Ctx.LangOpts.hasFeature(Feature::Embedded)) {
928+
Ctx.Diags.diagnose(diagLoc.value_or(SourceLoc()),
929+
diag::cannot_import_non_embedded_module, M.getName());
930+
}
931+
919932
// Non-resilient modules built with C++ interoperability enabled
920933
// are typically incompatible with clients that do not enable
921934
// C++ interoperability.

test/embedded/basic-irgen-no-stdlib.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ public func main() {
2323
start(p: Concrete())
2424
}
2525

26-
// CHECK-LABEL: declare hidden swiftcc void @"$s4main8ConcreteVACycfC"()
26+
// CHECK-LABEL: define {{.*}}void @"$s4main8ConcreteVACycfC"()
2727

28-
// CHECK-LABEL: declare hidden swiftcc void @"$s4main5start1pyx_tAA6PlayerRzlFAA8ConcreteV_Tg5"()
28+
// CHECK-LABEL: define {{.*}}void @"$s4main5start1pyx_tAA6PlayerRzlFAA8ConcreteV_Tg5"()
2929

30-
// CHECK-LABEL: define protected swiftcc void @"$s4mainAAyyF"()
30+
// CHECK-LABEL: define {{.*}}void @"$s4mainAAyyF"()
3131
// CHECK-NEXT: entry:
3232
// CHECK-NEXT: call swiftcc void @"$s4main8ConcreteVACycfC"()
3333
// CHECK-NEXT: call swiftcc void @"$s4main5start1pyx_tAA6PlayerRzlFAA8ConcreteV_Tg5"()
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %{python} %utils/split_file.py -o %t %s
3+
4+
// RUN: %target-swift-frontend -swift-version 5 -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift -parse-stdlib -enable-experimental-feature Embedded
5+
// RUN: %target-swift-frontend -swift-version 5 -emit-ir -I %t %t/Main.swift -parse-stdlib -enable-experimental-feature Embedded | %FileCheck %s
6+
7+
// TODO: investigate why windows is generating more metadata.
8+
// XFAIL: OS=windows-msvc
9+
10+
// BEGIN MyModule.swift
11+
12+
public struct Bool {}
13+
14+
public protocol Player {
15+
func play()
16+
var canPlay: Bool { get }
17+
}
18+
19+
public struct Concrete : Player {
20+
public init() { }
21+
public func play() { }
22+
public var canPlay: Bool { Bool() }
23+
}
24+
25+
public func start(p: some Player) {
26+
p.play()
27+
}
28+
29+
public func moduleMain() {
30+
start(p: Concrete())
31+
}
32+
33+
// BEGIN Main.swift
34+
35+
import MyModule
36+
37+
public func main() {
38+
moduleMain()
39+
}
40+
41+
// CHECK: define {{.*}}@main{{.*}} {
42+
// CHECK: define {{.*}}void @"$s4Main4mainyyF"{{.*}} {
43+
// CHECK: define {{.*}}void @"$s8MyModule10moduleMainyyF"{{.*}} {
44+
// CHECK: define {{.*}}void @"$s8MyModule8ConcreteVACycfC"{{.*}} {
45+
// CHECK: define {{.*}}void @"$s8MyModule5start1pyx_tAA6PlayerRzlFAA8ConcreteV_Tg5"{{.*}} {
46+
// CHECK: define {{.*}}void @"$s8MyModule8ConcreteV4playyyF"{{.*}} {

0 commit comments

Comments
 (0)