Merge pull request #73478 from apple/elsh/pcmo-res

Support generating loadable types in serialized function when package-cmo is enabled.
This commit is contained in:
Ellie Shin
2024-05-08 16:08:13 -07:00
committed by GitHub
6 changed files with 96 additions and 53 deletions

View File

@@ -779,11 +779,7 @@ public:
return getLoweredFunctionType()->getRepresentation();
}
ResilienceExpansion getResilienceExpansion() const {
return (isSerialized()
? ResilienceExpansion::Minimal
: ResilienceExpansion::Maximal);
}
ResilienceExpansion getResilienceExpansion() const;
// Returns the type expansion context to be used inside this function.
TypeExpansionContext getTypeExpansionContext() const {

View File

@@ -517,6 +517,23 @@ bool SILFunction::isNoReturnFunction(TypeExpansionContext context) const {
.isNoReturnFunction(getModule(), context);
}
ResilienceExpansion SILFunction::getResilienceExpansion() const {
// If package serialization is enabled, we can safely
// assume that the defining .swiftmodule is built from
// 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.
if (getModule().getSwiftModule()->serializePackageEnabled() &&
getModule().getSwiftModule()->isResilient())
return ResilienceExpansion::Maximal;
return (isSerialized()
? ResilienceExpansion::Minimal
: ResilienceExpansion::Maximal);
}
const TypeLowering &
SILFunction::getTypeLowering(AbstractionPattern orig, Type subst) {
return getModule().Types.getTypeLowering(orig, subst,

View File

@@ -2324,17 +2324,25 @@ 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.
bool sameModule = (D->getModuleContext() == &TC.M);
auto declModule = D->getModuleContext();
bool sameModule = (declModule == &TC.M);
if (sameModule)
properties.addSubobject(RecursiveProperties::forResilient());
// If the type is in a different module, 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.
if (!sameModule || Expansion.getResilienceExpansion() ==
bool serializedPackage = declModule->inSamePackage(&TC.M) &&
declModule->isResilient() &&
declModule->serializePackageEnabled();
if ((!sameModule && !serializedPackage) ||
Expansion.getResilienceExpansion() ==
ResilienceExpansion::Minimal) {
properties.addSubobject(RecursiveProperties::forOpaque());
return true;

View File

@@ -158,22 +158,9 @@ namespace {
template <typename DeclType>
bool checkResilience(DeclType *D, ModuleDecl *M,
ResilienceExpansion expansion) {
auto refDeclModule = D->getModuleContext();
// Explicitly bypassed for debugging with `bypass-resilience-checks`
if (refDeclModule->getBypassResilience())
if (D->getModuleContext()->getBypassResilience())
return false;
// If package serialization is enabled with `experimental-package-cmo`,
// decls can be serialized in a resiliently built module. In such case,
// a direct access should be allowed.
auto packageSerialized = expansion == ResilienceExpansion::Minimal &&
refDeclModule->isResilient() &&
refDeclModule->allowNonResilientAccess() &&
refDeclModule->serializePackageEnabled() &&
refDeclModule->inSamePackage(M);
if (packageSerialized)
return false;
return D->isResilient(M, expansion);
}

View File

@@ -84,7 +84,7 @@ private:
bool canSerializeGlobal(SILGlobalVariable *global);
bool canSerializeType(SILType type, TypeExpansionContext typeExpCtx);
bool canSerializeType(SILType type);
bool canUseFromInline(DeclContext *declCtxt);
@@ -331,14 +331,12 @@ bool CrossModuleOptimization::canSerializeFunction(
bool CrossModuleOptimization::canSerializeInstruction(
SILInstruction *inst, FunctionFlags &canSerializeFlags, int maxDepth) {
// First check if any result or operand types prevent serialization.
auto typeExpCtx = inst->getFunction()->getTypeExpansionContext();
for (SILValue result : inst->getResults()) {
if (!canSerializeType(result->getType(), typeExpCtx))
if (!canSerializeType(result->getType()))
return false;
}
for (Operand &op : inst->getAllOperands()) {
if (!canSerializeType(op.get()->getType(), typeExpCtx))
if (!canSerializeType(op.get()->getType()))
return false;
}
@@ -437,23 +435,11 @@ bool CrossModuleOptimization::canSerializeGlobal(SILGlobalVariable *global) {
return true;
}
bool CrossModuleOptimization::canSerializeType(SILType type,
TypeExpansionContext typeExpCtx) {
bool CrossModuleOptimization::canSerializeType(SILType type) {
auto iter = typesChecked.find(type);
if (iter != typesChecked.end())
return iter->getSecond();
if (M.getSwiftModule()->isResilient()) {
auto minResilientCtx = TypeExpansionContext(ResilienceExpansion::Minimal,
typeExpCtx.getContext(),
typeExpCtx.isWholeModuleContext());
auto loadableInMinResilientCtx = M.Types.getTypeLowering(type, minResilientCtx).isLoadable();
if (!loadableInMinResilientCtx) {
typesChecked[type] = false;
return false;
}
}
bool success = !type.getASTType().findIf(
[this](Type rawSubType) {
CanType subType = rawSubType->getCanonicalType();

View File

@@ -172,43 +172,73 @@ print(prevPkgData)
//--- Lib.swift
// FIXME: handle struct_element_addr %field in resilient mode; requires non-resilience in SIL verify.
// CHECK-RES-NOT: s3Lib9PubStructV6fooVarSivg
// CHECK-RES-NOT: s3Lib9PkgStructV6fooVarSivg
// FIXME: handle `struct $PubStruct` in resilient mode; PubStruct is by-address, so fails in IsLodableOrOpaque check.
// CHECK-RES-NOT: s3Lib9PubStructV6fooVarSivs
// CHECK-RES-NOT: s3Lib9PkgStructV6fooVarSivs
public struct PubStruct {
// PubStruct.foovar.getter
// CHECK-RES-DAG: sil [serialized] [canonical] @$s3Lib9PubStructV6fooVarSivg : $@convention(method) (@in_guaranteed PubStruct) -> Int {
// CHECK-NONRES-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib9PubStructV6fooVarSivg : $@convention(method) (PubStruct) -> Int
// CHECK-NONRES-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib9PubStructV6fooVarSivM : $@yield_once @convention(method) (@inout PubStruct) -> @yields @inout Int {
// CHECK-RES-DAG: [[FIELD:%.*]] = struct_element_addr %0 : $*PubStruct, #PubStruct.fooVar
// CHECK-RES-DAG: load [[FIELD]] : $*Int
// CHECK-NONRES-DAG = struct_extract %0 : $PubStruct, #PubStruct.fooVar
// PubStruct.foovar.setter
// CHECK-RES-DAG: sil [serialized] [canonical] @$s3Lib9PubStructV6fooVarSivs : $@convention(method) (Int, @inout PubStruct) -> () {
// CHECK-NONRES-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib9PubStructV6fooVarSivs : $@convention(method) (Int, @inout PubStruct) -> () {
/// NOTE: `struct $PubStruct` in [serialized] function is legal only if package serialization is enabled.
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct $PubStruct
// CHECK-RES-DAG: store [[FIELD]] to {{.*}} : $*PubStruct
// CHECK-NONRES-DAG: store [[FIELD]] to [trivial] {{.*}} : $*PubStruct
// PubStruct.foovar.modify
// CHECK-RES-DAG: sil [serialized] [canonical] @$s3Lib9PubStructV6fooVarSivM : $@yield_once @convention(method) (@inout PubStruct) -> @yields @inout Int {
// CHECK-NONRES-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib9PubStructV6fooVarSivM : $@yield_once @convention(method) (@inout PubStruct) -> @yields @inout Int {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct_element_addr %0 : $*PubStruct, #PubStruct.fooVar
// CHECK-COMMON-DAG: yield [[FIELD]]
public var fooVar: Int
public init(_ arg: Int) {
// CHECK-RES-DAG: sil [serialized] [canonical] @$s3Lib9PubStructVyACSicfC : $@convention(method) (Int, @thin PubStruct.Type) -> @out PubStruct {
// CHECK-NONRES-DAG: sil [serialized] [canonical] @$s3Lib9PubStructVyACSicfC : $@convention(method) (Int, @thin PubStruct.Type) -> PubStruct {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct $PubStruct
// CHECK-RES-DAG: store [[FIELD]] to %0 : $*PubStruct
// CHECK-NONRES-DAG: return [[FIELD]] : $PubStruct
fooVar = arg
}
public func f() {
// CHECK-RES-DAG: sil [serialized] [canonical] @$s3Lib9PubStructV1fyyF : $@convention(method) (@in_guaranteed PubStruct) -> () {
// CHECK-NONRES-DAG: sil [serialized] [canonical] @$s3Lib9PubStructV1fyyF : $@convention(method) (PubStruct) -> () {
print(fooVar)
}
}
public func runPub(_ arg: PubStruct) {
// CHECK-RES-DAG: sil [serialized] [canonical] @$s3Lib6runPubyyAA0C6StructVF : $@convention(thin) (@in_guaranteed PubStruct) -> () {
// CHECK-NONRES-DAG: sil [serialized] [canonical] @$s3Lib6runPubyyAA0C6StructVF : $@convention(thin) (PubStruct) -> () {
print(arg)
}
@frozen
public struct FrPubStruct {
// CHECK-COMMON-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib11FrPubStructV6fooVarSivM : $@yield_once @convention(method) (@inout FrPubStruct) -> @yields @inout Int {
// FrPubStruct.fooVar.getter
// CHECK-COMMON-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib11FrPubStructV6fooVarSivg : $@convention(method) (FrPubStruct) -> Int {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct_extract %0 : $FrPubStruct, #FrPubStruct.fooVar
// CHECK-COMMON-DAG: return [[FIELD]] : $Int
// FrPubStruct.fooVar.setter
// CHECK-COMMON-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib11FrPubStructV6fooVarSivs : $@convention(method) (Int, @inout FrPubStruct) -> () {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct $FrPubStruct
// CHECK-COMMON-DAG: store [[FIELD]] to [trivial] {{.*}} : $*FrPubStruct
// FrPubStruct.fooVar.modify
// CHECK-COMMON-DAG: sil [transparent] [serialized] [canonical] [ossa] @$s3Lib11FrPubStructV6fooVarSivM : $@yield_once @convention(method) (@inout FrPubStruct) -> @yields @inout Int {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct_element_addr %0 : $*FrPubStruct, #FrPubStruct.fooVar
// CHECK-COMMON-DAG: yield [[FIELD]]
public var fooVar: Int
public init(_ arg: Int) {
// CHECK-COMMON-DAG: sil [serialized] [canonical] @$s3Lib11FrPubStructVyACSicfC : $@convention(method) (Int, @thin FrPubStruct.Type) -> FrPubStruct {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct $FrPubStruct
// CHECK-COMMON-DAG: return [[FIELD]] : $FrPubStruct
fooVar = arg
}
public func f() {
@@ -222,25 +252,44 @@ public func runFrPub(_ arg: FrPubStruct) {
}
package struct PkgStruct {
// fooVar.getter
// PkgStruct.fooVar.getter
// CHECK-RES-DAG: sil package [serialized] [canonical] @$s3Lib9PkgStructV6fooVarSivg : $@convention(method) (@in_guaranteed PkgStruct) -> Int {
// CHECK-NONRES-DAG: sil package [transparent] [serialized] [canonical] [ossa] @$s3Lib9PkgStructV6fooVarSivg : $@convention(method) (PkgStruct) -> Int {
// fooVar.modify
// CHECK-NONRES-DAG: sil package [transparent] [serialized] [canonical] [ossa] @$s3Lib9PkgStructV6fooVarSivM : $@yield_once @convention(method) (@inout PkgStruct) -> @yields @inout Int {
// fooVar.setter
// CHECK-RES-DAG: [[FIELD:%.*]] = struct_element_addr %0 : $*PkgStruct, #PkgStruct.fooVar
// CHECK-RES-DAG: load [[FIELD]] : $*Int
// CHECK-NONRES-DAG = struct_extract %0 : $PkgStruct, #PkgStruct.fooVar
// PkgStruct.fooVar.setter
// CHECK-RES-DAG: sil package [serialized] [canonical] @$s3Lib9PkgStructV6fooVarSivs : $@convention(method) (Int, @inout PkgStruct) -> () {
// CHECK-NONRES-DAG: sil package [transparent] [serialized] [canonical] [ossa] @$s3Lib9PkgStructV6fooVarSivs : $@convention(method) (Int, @inout PkgStruct) -> () {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct $PkgStruct
// CHECK-RES-DAG: store [[FIELD]] to {{.*}} : $*PkgStruct
// CHECK-NONRES-DAG: store [[FIELD]] to [trivial] {{.*}} : $*PkgStruct
// PkgStruct.fooVar.modify
// CHECK-RES-DAG: sil package [serialized] [canonical] @$s3Lib9PkgStructV6fooVarSivM : $@yield_once @convention(method) (@inout PkgStruct) -> @yields @inout Int {
// CHECK-NONRES-DAG: sil package [transparent] [serialized] [canonical] [ossa] @$s3Lib9PkgStructV6fooVarSivM : $@yield_once @convention(method) (@inout PkgStruct) -> @yields @inout Int {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct_element_addr %0 : $*PkgStruct, #PkgStruct.fooVar
// CHECK-COMMON-DAG: yield [[FIELD]]
package var fooVar: Int
package init(_ arg: Int) {
// CHECK-RES-DAG: sil package [serialized] [canonical] @$s3Lib9PkgStructVyACSicfC : $@convention(method) (Int, @thin PkgStruct.Type) -> @out PkgStruct {
// CHECK-NONRES-DAG: sil package [serialized] [canonical] @$s3Lib9PkgStructVyACSicfC : $@convention(method) (Int, @thin PkgStruct.Type) -> PkgStruct {
// CHECK-COMMON-DAG: [[FIELD:%.*]] = struct $PkgStruct
// CHECK-RES-DAG: store [[FIELD]] to %0 : $*PkgStruct
// CHECK-NONRES-DAG: return [[FIELD]] : $PkgStruct
fooVar = arg
}
package func f() {
// CHECK-RES-DAG: sil package [serialized] [canonical] @$s3Lib9PkgStructV1fyyF : $@convention(method) (@in_guaranteed PkgStruct) -> () {
// CHECK-NONRES-DAG: sil package [serialized] [canonical] @$s3Lib9PkgStructV1fyyF : $@convention(method) (PkgStruct) -> () {
print(fooVar)
}
}
package func runPkg(_ arg: PkgStruct) {
// CHECK-RES-DAG: sil package [serialized] [canonical] @$s3Lib6runPkgyyAA0C6StructVF : $@convention(thin) (@in_guaranteed PkgStruct) -> () {
// CHECK-NONRES-DAG: sil package [serialized] [canonical] @$s3Lib6runPkgyyAA0C6StructVF : $@convention(thin) (PkgStruct) -> () {
print(arg)
}