Skip to content

Commit

Permalink
[SIL] Add a new attribute [serialized_for_package] to support
Browse files Browse the repository at this point in the history
package-wide resilience domain if Package CMO is enabled.

The purpose of the attribute includes:
- Indicates that certain types such as loadable types are
allowed in serialized functions in resiliently built module
if the optimization is enabled, which are otherwise disallowed.
- Used during SIL deserialization to determine whether such
functions are allowed.
- Used to determine if a callee can be inlined into a caller
that's serialized without package-cmo, e.g. with an explicit
annotation like @inlinable, where the callee was serialized
due to package-cmo.

Resolves rdar://127870822
  • Loading branch information
elsh committed May 15, 2024
1 parent 1b848ac commit 2d81d0f
Show file tree
Hide file tree
Showing 14 changed files with 405 additions and 277 deletions.
20 changes: 20 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,22 @@ class SILFunction
"too few bits for Serialized storage");
}

/// A [serialized_for_package] attribute is used to indicate that a function
/// is [serialized] because of package-cmo optimization.
/// Package-cmo allows serializing a function containing a loadable type in
/// a resiliently built module, which is normally illegal. During SIL deserialization,
/// this attribute can be used to check whether a loaded function that was serialized
/// can be allowed to have loadable types. This attribute is also used to determine
/// if a callee can be inlined into a caller that's serialized without package-cmo, for
/// example, by explicitly annotating the caller decl with `@inlinable`.
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
13 changes: 11 additions & 2 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3341,6 +3341,14 @@ 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 +3536,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 +3848,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 +4027,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
29 changes: 16 additions & 13 deletions lib/SIL/IR/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2324,31 +2324,34 @@ namespace {
if (D->isResilient()) {
// If the type is resilient and defined in our module, make a note of
// that, since our lowering now depends on the resilience expansion.
// The same should happen if the type was resilient and serialized in
// another module in the same package with package-cmo enabled, which
// treats those modules to be in the same resilience domain.
auto declModule = D->getModuleContext();
bool sameModule = (declModule == &TC.M);
if (sameModule)
bool serializedPackage = declModule != &TC.M &&
declModule->inSamePackage(&TC.M) &&
declModule->isResilient() &&
declModule->serializePackageEnabled();
auto inSameResilienceDomain = sameModule || serializedPackage;
if (inSameResilienceDomain)
properties.addSubobject(RecursiveProperties::forResilient());

// If the type is in a different module, or if we're using a minimal
// If the type is in a different module and not in the same package
// resilience domain (with package-cmo), or if we're using a minimal
// expansion, the type is address only and completely opaque to us.
// However, this is not true if the different module is in the same
// package and package serialization is enabled (resilience expansion
// is maximal), e.g. in case of package-cmo.
//
// Note: if the type is in a different module, the lowering does
// not depend on the resilience expansion, so we do not need to set
// the isResilient() flag above.
bool serializedPackage = declModule->inSamePackage(&TC.M) &&
declModule->isResilient() &&
declModule->serializePackageEnabled();
if ((!sameModule && !serializedPackage) ||
// Note: if the type is in a different module and not in the same
// package resilience domain, the lowering does not depend on the
// resilience expansion, so we do not need to set the isResilient()
// flag above.
if (!inSameResilienceDomain ||
Expansion.getResilienceExpansion() ==
ResilienceExpansion::Minimal) {
properties.addSubobject(RecursiveProperties::forOpaque());
return true;
}
}

return false;
}

Expand Down
44 changes: 25 additions & 19 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 All @@ -676,10 +677,9 @@ static bool parseDeclSILOptional(
SILFunction **usedAdHocRequirementWitness, Identifier *objCReplacementFor,
SILFunction::Purpose *specialPurpose, Inline_t *inlineStrategy,
OptimizationMode *optimizationMode, PerformanceConstraints *perfConstraints,
bool *isPerformanceConstraint,
bool *markedAsUsed, StringRef *section, bool *isLet, bool *isWeakImported,
bool *needStackProtection, AvailabilityContext *availability,
bool *isWithoutActuallyEscapingThunk,
bool *isPerformanceConstraint, bool *markedAsUsed, StringRef *section,
bool *isLet, bool *isWeakImported, bool *needStackProtection,
AvailabilityContext *availability, bool *isWithoutActuallyEscapingThunk,
SmallVectorImpl<std::string> *Semantics,
SmallVectorImpl<ParsedSpecAttr> *SpecAttrs, ValueDecl **ClangDecl,
EffectsKind *MRK, SILParser &SP, SILModule &M) {
Expand All @@ -697,6 +697,9 @@ 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,17 +7142,17 @@ bool SILParserState::parseDeclSIL(Parser &P) {
Identifier objCReplacementFor;
if (parseSILLinkage(FnLinkage, P) ||
parseDeclSILOptional(
&isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA,
&hasResultDependsOnSelf, &isThunk, &isDynamic, &isDistributed,
&isRuntimeAccessible, &forceEnableLexicalLifetimes,
&isTransparent, &isSerialized, &isSerializedForPackage, &isCanonical,
&hasOwnershipSSA, &hasResultDependsOnSelf, &isThunk, &isDynamic,
&isDistributed, &isRuntimeAccessible, &forceEnableLexicalLifetimes,
&useStackForPackMetadata, &hasUnsafeNonEscapableResult,
&isExactSelfClass, &DynamicallyReplacedFunction,
&AdHocWitnessFunction, &objCReplacementFor, &specialPurpose,
&inlineStrategy, &optimizationMode, &perfConstr,
&isPerformanceConstraint, &markedAsUsed,
&section, nullptr, &isWeakImported, &needStackProtection,
&availability, &isWithoutActuallyEscapingThunk, &Semantics,
&SpecAttrs, &ClangDecl, &MRK, FunctionState, M) ||
&isPerformanceConstraint, &markedAsUsed, &section, nullptr,
&isWeakImported, &needStackProtection, &availability,
&isWithoutActuallyEscapingThunk, &Semantics, &SpecAttrs, &ClangDecl,
&MRK, FunctionState, M) ||
P.parseToken(tok::at_sign, diag::expected_sil_function_name) ||
P.parseIdentifier(FnName, FnNameLoc, /*diagnoseDollarPrefix=*/false,
diag::expected_sil_function_name) ||
Expand All @@ -7173,6 +7177,8 @@ 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 @@ -7399,10 +7405,9 @@ bool SILParserState::parseSILGlobal(Parser &P) {
parseDeclSILOptional(nullptr, &isSerialized, 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) ||
nullptr, &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 @@ -7454,7 +7459,7 @@ bool SILParserState::parseSILProperty(Parser &P) {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, SP, M))
nullptr, nullptr, nullptr, nullptr, SP, M))
return true;

ValueDecl *VD;
Expand Down Expand Up @@ -7524,7 +7529,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 @@ -7647,7 +7652,8 @@ bool SILParserState::parseSILMoveOnlyDeinit(Parser &parser) {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, moveOnlyDeinitTableState, M))
nullptr, nullptr, nullptr, nullptr,
moveOnlyDeinitTableState, M))
return true;

// Parse the class name.
Expand Down Expand Up @@ -8134,7 +8140,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, WitnessState, M))
nullptr, nullptr, nullptr, nullptr, WitnessState, M))
return true;

// Parse the protocol conformance.
Expand Down
Loading

0 comments on commit 2d81d0f

Please sign in to comment.