Skip to content

Commit

Permalink
[SIL] Add a new attribute [serialized_for_package].
Browse files Browse the repository at this point in the history
If Package CMO is enabled, we now add a new attribute to
a serialized function, which can then be used during
deserialization into a client module to determine the
resilience expansion of the function.

Resolves rdar://127870822
  • Loading branch information
elsh committed May 10, 2024
1 parent 1b848ac commit 0363b99
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 91 deletions.
11 changes: 11 additions & 0 deletions include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,10 @@ class SILFunction
/// The function's serialized attribute.
bool Serialized : 1;

/// [serialized_for_package] attribute if package serialization
/// is enabled.
bool SerializedForPackage : 1;

/// Specifies if this function is a thunk or a reabstraction thunk.
///
/// The inliner uses this information to avoid inlining (non-trivial)
Expand Down Expand Up @@ -1138,6 +1142,13 @@ class SILFunction
"too few bits for Serialized storage");
}

IsSerializedForPackage_t isSerializedForPackage() const {
return IsSerializedForPackage_t(SerializedForPackage);
}
void setSerializedForPackage(IsSerializedForPackage_t isSerializedForPackage) {
SerializedForPackage = isSerializedForPackage;
}

/// Get this function's thunk attribute.
IsThunk_t isThunk() const { return IsThunk_t(Thunk); }
void setThunk(IsThunk_t isThunk) {
Expand Down
5 changes: 5 additions & 0 deletions include/swift/SIL/SILLinkage.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ enum IsSerialized_t : unsigned char {
IsSerialized
};

enum IsSerializedForPackage_t : unsigned char {
IsNotSerializedForPackage,
IsSerializedForPackage
};

/// The scope in which a subclassable class can be subclassed.
enum class SubclassScope : uint8_t {
/// This class can be subclassed in other modules.
Expand Down
14 changes: 12 additions & 2 deletions lib/SIL/IR/SILFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,12 +523,22 @@ ResilienceExpansion SILFunction::getResilienceExpansion() const {
// source and is never used outside of its package;
// Even if the module is built resiliently, return
// maximal expansion here so aggregate types can be
// loadable in the same resilient domain (from a client
// module in the same package.
// treated as loadable in the same resilient domain
// (across modules in the same package).
if (getModule().getSwiftModule()->serializePackageEnabled() &&
getModule().getSwiftModule()->isResilient())
return ResilienceExpansion::Maximal;

// If a function definition is in another module, and
// it was serialized due to package serialization opt,
// a new attribute [serialized_for_package] is added
// to the definition site. During deserialization, this
// attribute is preserved if the current module is in
// the same package, thus should be in the same resilience
// domain.
if (isSerializedForPackage() == IsSerializedForPackage)
return ResilienceExpansion::Maximal;

return (isSerialized()
? ResilienceExpansion::Minimal
: ResilienceExpansion::Maximal);
Expand Down
10 changes: 8 additions & 2 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3341,6 +3341,11 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
case IsSerialized: OS << "[serialized] "; break;
}

switch (isSerializedForPackage()) {
case IsNotSerializedForPackage: break;
case IsSerializedForPackage: OS << "[serialized_for_package] "; break;
}

switch (isThunk()) {
case IsNotThunk: break;
case IsThunk: OS << "[thunk] "; break;
Expand Down Expand Up @@ -3528,7 +3533,7 @@ void SILGlobalVariable::print(llvm::raw_ostream &OS, bool Verbose) const {

if (isSerialized())
OS << "[serialized] ";

if (isLet())
OS << "[let] ";

Expand Down Expand Up @@ -3840,7 +3845,7 @@ void SILProperty::print(SILPrintContext &Ctx) const {
OS << "sil_property ";
if (isSerialized())
OS << "[serialized] ";

OS << '#';
printValueDecl(getDecl(), OS);
if (auto sig = getDecl()->getInnermostDeclContext()
Expand Down Expand Up @@ -4019,6 +4024,7 @@ void SILVTable::print(llvm::raw_ostream &OS, bool Verbose) const {
OS << "sil_vtable ";
if (isSerialized())
OS << "[serialized] ";

if (SILType classTy = getClassType()) {
OS << classTy;
} else {
Expand Down
30 changes: 18 additions & 12 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,8 @@ void SILParser::convertRequirements(ArrayRef<RequirementRepr> From,
}

static bool parseDeclSILOptional(
bool *isTransparent, IsSerialized_t *isSerialized, bool *isCanonical,
bool *isTransparent, IsSerialized_t *isSerialized,
IsSerializedForPackage_t *isSerializedForPackage, bool *isCanonical,
bool *hasOwnershipSSA, bool *hasResultDependsOnSelf, IsThunk_t *isThunk,
IsDynamicallyReplaceable_t *isDynamic, IsDistributed_t *isDistributed,
IsRuntimeAccessible_t *isRuntimeAccessible,
Expand Down Expand Up @@ -697,6 +698,8 @@ static bool parseDeclSILOptional(
*isTransparent = true;
else if (isSerialized && SP.P.Tok.getText() == "serialized")
*isSerialized = IsSerialized;
else if (isSerializedForPackage && SP.P.Tok.getText() == "serialized_for_package")
*isSerializedForPackage = IsSerializedForPackage;
else if (isDynamic && SP.P.Tok.getText() == "dynamically_replacable")
*isDynamic = IsDynamic;
else if (isDistributed && SP.P.Tok.getText() == "distributed")
Expand Down Expand Up @@ -7106,6 +7109,7 @@ bool SILParserState::parseDeclSIL(Parser &P) {

bool isTransparent = false;
IsSerialized_t isSerialized = IsNotSerialized;
IsSerializedForPackage_t isSerializedForPackage = IsNotSerializedForPackage;
bool isCanonical = false;
IsDynamicallyReplaceable_t isDynamic = IsNotDynamic;
IsDistributed_t isDistributed = IsNotDistributed;
Expand Down Expand Up @@ -7138,8 +7142,9 @@ bool SILParserState::parseDeclSIL(Parser &P) {
Identifier objCReplacementFor;
if (parseSILLinkage(FnLinkage, P) ||
parseDeclSILOptional(
&isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA,
&hasResultDependsOnSelf, &isThunk, &isDynamic, &isDistributed,
&isTransparent, &isSerialized, &isSerializedForPackage,
&isCanonical, &hasOwnershipSSA, &hasResultDependsOnSelf,
&isThunk, &isDynamic, &isDistributed,
&isRuntimeAccessible, &forceEnableLexicalLifetimes,
&useStackForPackMetadata, &hasUnsafeNonEscapableResult,
&isExactSelfClass, &DynamicallyReplacedFunction,
Expand Down Expand Up @@ -7173,6 +7178,7 @@ bool SILParserState::parseDeclSIL(Parser &P) {
FunctionState.F->setBare(IsBare);
FunctionState.F->setTransparent(IsTransparent_t(isTransparent));
FunctionState.F->setSerialized(IsSerialized_t(isSerialized));
FunctionState.F->setSerializedForPackage(IsSerializedForPackage_t(isSerializedForPackage));
FunctionState.F->setWasDeserializedCanonical(isCanonical);
if (!hasOwnershipSSA)
FunctionState.F->setOwnershipEliminated();
Expand Down Expand Up @@ -7396,13 +7402,12 @@ bool SILParserState::parseSILGlobal(Parser &P) {

SILParser State(P);
if (parseSILLinkage(GlobalLinkage, P) ||
parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, &isLet,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, State, M) ||
&isLet, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, State, M) ||
P.parseToken(tok::at_sign, diag::expected_sil_value_name) ||
P.parseIdentifier(GlobalName, NameLoc, /*diagnoseDollarPrefix=*/false,
diag::expected_sil_value_name) ||
Expand Down Expand Up @@ -7449,12 +7454,13 @@ bool SILParserState::parseSILProperty(Parser &P) {
SILParser SP(P);

IsSerialized_t Serialized = IsNotSerialized;
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, SP, M))
SP, M))
return true;

ValueDecl *VD;
Expand Down Expand Up @@ -7524,7 +7530,7 @@ bool SILParserState::parseSILVTable(Parser &P) {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, VTableState, M))
nullptr, nullptr, nullptr, nullptr, VTableState, M))
return true;


Expand Down Expand Up @@ -7642,7 +7648,7 @@ bool SILParserState::parseSILMoveOnlyDeinit(Parser &parser) {
SILParser moveOnlyDeinitTableState(parser);

IsSerialized_t Serialized = IsNotSerialized;
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
Expand Down Expand Up @@ -8129,7 +8135,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) {
parseSILLinkage(Linkage, P);

IsSerialized_t isSerialized = IsNotSerialized;
if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
Expand Down
12 changes: 10 additions & 2 deletions lib/SILOptimizer/IPO/CrossModuleOptimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,17 +571,25 @@ bool CrossModuleOptimization::shouldSerialize(SILFunction *function) {
return true;
}

static void addSerializedForPackageAttrIfEnabled(SILFunction *f, const SILModule &mod) {
auto packageOptIn = mod.getOptions().EnableSerializePackage &&
mod.getSwiftModule()->isResilient();
f->setSerializedForPackage(packageOptIn ? IsSerializedForPackage : IsNotSerializedForPackage);
}

/// Serialize \p function and recursively all referenced functions which are
/// marked in \p canSerializeFlags.
void CrossModuleOptimization::serializeFunction(SILFunction *function,
const FunctionFlags &canSerializeFlags) {
if (function->isSerialized())
if (function->isSerialized()) {
addSerializedForPackageAttrIfEnabled(function, M);
return;

}
if (!canSerializeFlags.lookup(function))
return;

function->setSerialized(IsSerialized);
addSerializedForPackageAttrIfEnabled(function, M);

for (SILBasicBlock &block : *function) {
for (SILInstruction &inst : block) {
Expand Down
34 changes: 24 additions & 10 deletions lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,16 +563,16 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
IdentifierID replacedFunctionID;
IdentifierID usedAdHocWitnessFunctionID;
GenericSignatureID genericSigID;
unsigned rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy,
unsigned rawLinkage, isTransparent, isSerialized, isSerializedForPackage,
isThunk, isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy,
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
isDistributed, isRuntimeAccessible, forceEnableLexicalLifetimes;
ArrayRef<uint64_t> SemanticsIDs;
SILFunctionLayout::readRecord(
scratch, rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy,
scratch, rawLinkage, isTransparent, isSerialized, isSerializedForPackage,
isThunk, isWithoutActuallyEscapingThunk, specialPurpose, inlineStrategy,
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
Expand Down Expand Up @@ -656,13 +656,26 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,

fn->setSerialized(IsSerialized_t(isSerialized));

// If fn was serialized in a module with package serialization
// enabled, a new attribute [serialized_for_package] was added
// to its definition. Preserve the attribute here if the current
// module is in the same package, and use it to determine the
// resilience expansion for this function.
auto loadedModule = getFile()->getParentModule();
if (isSerializedForPackage == IsSerializedForPackage &&
loadedModule->isResilient() &&
loadedModule != SILMod.getSwiftModule() &&
loadedModule->serializePackageEnabled() &&
loadedModule->inSamePackage(SILMod.getSwiftModule()))
fn->setSerializedForPackage(IsSerializedForPackage);

// If the serialized function comes from the same module, we're merging
// modules, and can update the linkage directly. This is needed to
// correctly update the linkage for forward declarations to entities defined
// in another file of the same module – we want to ensure the linkage
// reflects the fact that the entity isn't really external and shouldn't be
// dropped from the resulting merged module.
if (getFile()->getParentModule() == SILMod.getSwiftModule())
if (loadedModule == SILMod.getSwiftModule())
fn->setLinkage(linkage);

// Don't override the transparency or linkage of a function with
Expand Down Expand Up @@ -703,6 +716,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
fn->setLinkage(linkage);
fn->setTransparent(IsTransparent_t(isTransparent == 1));
fn->setSerialized(IsSerialized_t(isSerialized));
fn->setSerializedForPackage(IsSerializedForPackage_t(isSerializedForPackage));
fn->setThunk(IsThunk_t(isThunk));
fn->setWithoutActuallyEscapingThunk(bool(isWithoutActuallyEscapingThunk));
fn->setInlineStrategy(Inline_t(inlineStrategy));
Expand Down Expand Up @@ -3520,16 +3534,16 @@ bool SILDeserializer::hasSILFunction(StringRef Name,
IdentifierID replacedFunctionID;
IdentifierID usedAdHocWitnessFunctionID;
GenericSignatureID genericSigID;
unsigned rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy,
unsigned rawLinkage, isTransparent, isSerialized, isSerializedForPackage,
isThunk, isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numSpecAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
isDistributed, isRuntimeAccessible, forceEnableLexicalLifetimes;
ArrayRef<uint64_t> SemanticsIDs;
SILFunctionLayout::readRecord(
scratch, rawLinkage, isTransparent, isSerialized, isThunk,
isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy,
scratch, rawLinkage, isTransparent, isSerialized, isSerializedForPackage,
isThunk, isWithoutActuallyEscapingThunk, isGlobal, inlineStrategy,
optimizationMode, perfConstr, subclassScope, hasCReferences, effect,
numSpecAttrs, hasQualifiedOwnership, isWeakImported,
LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass,
Expand Down Expand Up @@ -3981,7 +3995,7 @@ SILProperty *SILDeserializer::readProperty(DeclID PId) {
DeclID StorageID;
ArrayRef<uint64_t> ComponentValues;
PropertyLayout::readRecord(scratch, StorageID, Serialized, ComponentValues);

auto decl = cast<AbstractStorageDecl>(MF->getDecl(StorageID));
unsigned ComponentValueIndex = 0;
auto component = readKeyPathComponent(ComponentValues, ComponentValueIndex);
Expand Down
2 changes: 1 addition & 1 deletion lib/Serialization/ModuleFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 872; // SerializePackageEnabled
const uint16_t SWIFTMODULE_VERSION_MINOR = 873; // [serialized_for_package] for SILFunctionLayout

/// A standard hash seed used for all string hashes in a serialized module.
///
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/SILFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ namespace sil_block {
BCRecordLayout<SIL_FUNCTION, SILLinkageField,
BCFixed<1>, // transparent
BCFixed<1>, // serialized
BCFixed<1>, // serializedForPackage
BCFixed<2>, // thunks: signature optimized/reabstraction
BCFixed<1>, // without_actually_escaping
BCFixed<3>, // specialPurpose
Expand Down
1 change: 1 addition & 0 deletions lib/Serialization/SerializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) {
SILFunctionLayout::emitRecord(
Out, ScratchRecord, abbrCode, toStableSILLinkage(Linkage),
(unsigned)F.isTransparent(), (unsigned)F.isSerialized(),
(unsigned)F.isSerializedForPackage(),
(unsigned)F.isThunk(), (unsigned)F.isWithoutActuallyEscapingThunk(),
(unsigned)F.getSpecialPurpose(), (unsigned)F.getInlineStrategy(),
(unsigned)F.getOptimizationMode(), (unsigned)F.getPerfConstraints(),
Expand Down
Loading

0 comments on commit 0363b99

Please sign in to comment.