Skip to content

Commit 746457f

Browse files
committed
[SILOptimizer] TypeWrappers: Implement self.$storage initialization injection
Inject a call to $Storage.init(...) which is then used to initialize type wrapper property `$storage` via `self.$storage = <TypeWrapper>(memberwise: <storage>)` call at each point where `_storage` becomes fully initialized.
1 parent d1ad9e3 commit 746457f

File tree

3 files changed

+510
-1
lines changed

3 files changed

+510
-1
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,13 @@ namespace {
459459
/// corresponds to `self`.
460460
void injectActorHops();
461461

462+
/// Injects `self.$storage = .init(memberwise: $Storage(...))`
463+
/// assignment instructions into the function after each point
464+
/// where `_storage` becomes fully initialized via `assign_by_wrapper`.
465+
/// This is only necessary only for user-defined initializers of a
466+
/// type wrapped types.
467+
void injectTypeWrapperStorageInitalization();
468+
462469
void emitSelfConsumedDiagnostic(SILInstruction *Inst);
463470

464471
LiveOutBlockState &getBlockInfo(SILBasicBlock *BB) {
@@ -1070,6 +1077,284 @@ void LifetimeChecker::injectActorHops() {
10701077
injectExecutorHopAfter(point);
10711078
}
10721079

1080+
void LifetimeChecker::injectTypeWrapperStorageInitalization() {
1081+
auto *storageVar = TheMemory.getAsTypeWrapperLocalStorageVar();
1082+
if (!storageVar)
1083+
return;
1084+
1085+
SmallVector<SILInstruction *> points;
1086+
findFullInitializationPoints(points);
1087+
1088+
// `_storage` has not been initialized at all.
1089+
if (points.empty())
1090+
return;
1091+
1092+
auto *ctor = cast<ConstructorDecl>(storageVar->getDeclContext()->getAsDecl());
1093+
auto *parentType = ctor->getDeclContext()->getSelfNominalTypeDecl();
1094+
auto *storageDecl = parentType->getTypeWrapperStorageDecl();
1095+
1096+
for (auto *point : points) {
1097+
SILBuilderWithScope::insertAfter(point, [&](SILBuilder &b) {
1098+
SILLocation loc = SILLocation::invalid().asAutoGenerated();
1099+
1100+
SmallVector<SILValue, 4> allocations;
1101+
1102+
auto allocStack = [&](SILType type) -> SILValue {
1103+
auto value = b.createAllocStack(loc, type);
1104+
allocations.push_back(value);
1105+
return value;
1106+
};
1107+
1108+
auto createInitRef = [&](ConstructorDecl *ctor) -> SILValue {
1109+
auto *init = F.getModule().lookUpFunction(SILDeclRef(ctor));
1110+
assert(init);
1111+
return b.createFunctionRef(loc, init);
1112+
};
1113+
1114+
// The instance type of $Storage declaration.
1115+
auto storageType = F.getLoweredType(
1116+
ctor->mapTypeIntoContext(storageDecl->getDeclaredInterfaceType()));
1117+
1118+
// Argument value to use in call to <TypeWrapper>.init(memberwise:)
1119+
SILValue storageObj = allocStack(storageType);
1120+
1121+
// let storageObj = $Storage(<destructured _storage tuple>)
1122+
{
1123+
SILValue localStorageVar = TheMemory.getUninitializedValue();
1124+
1125+
SmallVector<SILValue> storageInitArgs;
1126+
1127+
SILValue storageInitRef;
1128+
if (auto *memberwiseInit = storageDecl->getMemberwiseInitializer()) {
1129+
storageInitRef = createInitRef(memberwiseInit);
1130+
} else {
1131+
assert(storageDecl->hasDefaultInitializer());
1132+
storageInitRef = createInitRef(storageDecl->getDefaultInitializer());
1133+
}
1134+
1135+
CanSILFunctionType storageInitTy =
1136+
storageInitRef->getType().castTo<SILFunctionType>();
1137+
SILFunctionConventions convention(storageInitTy, F.getModule());
1138+
1139+
// If `.init` produces result indirectly, let's load it
1140+
// into the prepared buffer.
1141+
if (convention.hasIndirectSILResults()) {
1142+
assert(convention.getNumIndirectSILResults() == 1);
1143+
storageInitArgs.push_back(storageObj);
1144+
}
1145+
1146+
// Prepare arguments for $Storage.init(...) call. `_storage`
1147+
// tuple needs to be flattened and its elements have to be
1148+
// either copied or loaded based on a particular argument convention.
1149+
{
1150+
auto localStorageRef =
1151+
b.createBeginAccess(loc, localStorageVar, SILAccessKind::Read,
1152+
SILAccessEnforcement::Unsafe,
1153+
/*noNestedConflict=*/false,
1154+
/*fromBuiltin=*/false);
1155+
1156+
// There could be only one indirect result her - $Storage,
1157+
// which is also verified above, actual arguments start
1158+
// at 1 in such case.
1159+
unsigned argIdx = convention.getNumIndirectSILResults();
1160+
1161+
std::function<void(SILValue, SmallVectorImpl<SILValue> &)>
1162+
prepareArguments =
1163+
[&](SILValue val, SmallVectorImpl<SILValue> &results) {
1164+
// Destructure the tuple into individual elements recursively.
1165+
if (auto tupleType = val->getType().getAs<TupleType>()) {
1166+
for (unsigned i = 0, n = tupleType->getNumElements();
1167+
i != n; ++i) {
1168+
SILValue elt = b.createTupleElementAddr(loc, val, i);
1169+
prepareArguments(elt, results);
1170+
}
1171+
return;
1172+
}
1173+
1174+
switch (convention.getSILArgumentConvention(argIdx)) {
1175+
case SILArgumentConvention::Indirect_In:
1176+
case SILArgumentConvention::Indirect_In_Constant: {
1177+
// The only way to load opaque type is to allocate a temporary
1178+
// variable on the stack for it and initialize from the element
1179+
// address.
1180+
SILValue arg = allocStack(val->getType());
1181+
b.createCopyAddr(loc, val, arg, IsNotTake, IsInitialization);
1182+
results.push_back(arg);
1183+
break;
1184+
}
1185+
1186+
case SILArgumentConvention::Indirect_In_Guaranteed:
1187+
// The argument is +0, so we can use the address of the
1188+
// element as an argument.
1189+
results.push_back(val);
1190+
break;
1191+
1192+
case SILArgumentConvention::Direct_Owned: {
1193+
// Copy the value out at +1.
1194+
results.push_back(b.createTrivialLoadOr(
1195+
loc, val, LoadOwnershipQualifier::Copy));
1196+
break;
1197+
}
1198+
1199+
case SILArgumentConvention::Direct_Unowned:
1200+
case SILArgumentConvention::Direct_Guaranteed: {
1201+
results.push_back(b.createTrivialLoadOr(
1202+
loc, val, LoadOwnershipQualifier::Take));
1203+
break;
1204+
}
1205+
1206+
case SILArgumentConvention::Indirect_Out:
1207+
llvm_unreachable("indirect result is handled separately");
1208+
1209+
case SILArgumentConvention::Indirect_Inout:
1210+
case SILArgumentConvention::Indirect_InoutAliasable:
1211+
llvm_unreachable("inout arguments are not supported by $Storage.");
1212+
}
1213+
1214+
++argIdx;
1215+
};
1216+
1217+
prepareArguments(localStorageRef, storageInitArgs);
1218+
1219+
b.createEndAccess(loc, localStorageRef, /*abort=*/false);
1220+
}
1221+
1222+
// append $Storage.Type as the last argument to $Storage.init()
1223+
{
1224+
auto storageMetatypeType = F.getLoweredType(
1225+
ctor->mapTypeIntoContext(storageDecl->getInterfaceType()));
1226+
storageInitArgs.push_back(b.createMetatype(loc, storageMetatypeType));
1227+
}
1228+
1229+
SubstitutionMap storageInitSubs;
1230+
// Generic signature of $Storage is appropriate because
1231+
// the call is to either memberwise or default initializer
1232+
// which cannot introduce any additional generic parameters.
1233+
if (auto genericSig = storageDecl->getGenericSignature()) {
1234+
storageInitSubs = SubstitutionMap::get(
1235+
genericSig,
1236+
[&](SubstitutableType *type) {
1237+
return ctor->mapTypeIntoContext(type);
1238+
},
1239+
LookUpConformanceInModule(
1240+
ctor->getDeclContext()->getParentModule()));
1241+
}
1242+
1243+
// <storage> = $Storage.init(<flattened `_storage` elements>)
1244+
auto storageInitResult = b.createApply(
1245+
loc, storageInitRef, storageInitSubs, storageInitArgs);
1246+
1247+
// If result was indirect it would already be loaded into
1248+
// \c storageObj by the call itself.
1249+
if (!convention.hasIndirectSILResults())
1250+
b.createTrivialStoreOr(loc, storageInitResult, storageObj,
1251+
StoreOwnershipQualifier::Init);
1252+
}
1253+
1254+
// self.$storage = <TypeWrapper>(memberwise: storageObj))
1255+
{
1256+
bool isClass = isa<ClassDecl>(parentType);
1257+
1258+
auto typeWrapper = parentType->getTypeWrapper();
1259+
auto *typeWrapperInit = typeWrapper->getTypeWrapperInitializer();
1260+
SILValue typeWrapperInitRef = createInitRef(typeWrapperInit);
1261+
1262+
auto *self = TheMemory.findUninitializedSelfValue();
1263+
1264+
SILValue selfRef;
1265+
if (isClass) {
1266+
selfRef = b.emitBeginBorrowOperation(loc, self);
1267+
} else {
1268+
selfRef = b.createBeginAccess(loc, self, SILAccessKind::Modify,
1269+
SILAccessEnforcement::Static,
1270+
/*noNestedConflict=*/false,
1271+
/*fromBuiltin=*/false);
1272+
}
1273+
1274+
CanSILFunctionType wrapperInitTy =
1275+
typeWrapperInitRef->getType().castTo<SILFunctionType>();
1276+
SILFunctionConventions convention(wrapperInitTy, F.getModule());
1277+
1278+
// Argument is always passed indirectly because it's the
1279+
// generic parameter `S`.
1280+
1281+
// Reference to self.$storage
1282+
SILValue storagePropRef;
1283+
if (isClass) {
1284+
storagePropRef = b.createRefElementAddr(
1285+
loc, selfRef, parentType->getTypeWrapperProperty());
1286+
} else {
1287+
assert(isa<StructDecl>(parentType));
1288+
storagePropRef = b.createStructElementAddr(
1289+
loc, selfRef, parentType->getTypeWrapperProperty());
1290+
}
1291+
1292+
auto typeWrapperType =
1293+
b.createMetatype(loc, F.getLoweredType(MetatypeType::get(
1294+
storagePropRef->getType().getASTType())));
1295+
1296+
SmallVector<SILValue, 2> wrapperInitArgs;
1297+
1298+
Optional<SILValue> localWrapperObj;
1299+
1300+
if (convention.hasIndirectSILResults()) {
1301+
assert(convention.getNumIndirectSILResults() == 1);
1302+
localWrapperObj = allocStack(storagePropRef->getType());
1303+
wrapperInitArgs.push_back(*localWrapperObj);
1304+
}
1305+
1306+
wrapperInitArgs.push_back(storageObj);
1307+
wrapperInitArgs.push_back(typeWrapperType);
1308+
1309+
// <wrapper-var> = <TypeWrapper>.init(memberwise: tmpStorage)
1310+
auto wrapperInitResult = b.createApply(
1311+
loc, typeWrapperInitRef,
1312+
SubstitutionMap::get(typeWrapperInit->getGenericSignature(),
1313+
/*substitutions=*/{storageType.getASTType()},
1314+
/*conformances=*/{}),
1315+
wrapperInitArgs);
1316+
1317+
// self.$storage is a property access so it has to has to be wrapped
1318+
// in begin/end access instructions.
1319+
{
1320+
auto storagePropAccess =
1321+
b.createBeginAccess(loc, storagePropRef, SILAccessKind::Modify,
1322+
SILAccessEnforcement::Dynamic,
1323+
/*noNestedConflict=*/false,
1324+
/*fromBuiltin=*/false);
1325+
1326+
// self.$storage = <wrapper-var>
1327+
{
1328+
// If the result of `init` is indirect, let's just
1329+
// initialize the property with it.
1330+
if (localWrapperObj) {
1331+
b.createCopyAddr(loc, *localWrapperObj, storagePropAccess, IsTake,
1332+
IsInitialization);
1333+
} else {
1334+
b.createAssign(loc, wrapperInitResult, storagePropAccess,
1335+
AssignOwnershipQualifier::Init);
1336+
}
1337+
}
1338+
1339+
b.createEndAccess(loc, storagePropAccess, /*abort=*/false);
1340+
}
1341+
1342+
if (isClass) {
1343+
b.emitEndBorrowOperation(loc, selfRef);
1344+
} else {
1345+
b.createEndAccess(loc, selfRef, /*aborted=*/false);
1346+
}
1347+
1348+
// Dealloc all stack allocated data.
1349+
for (auto alloca = allocations.rbegin(); alloca != allocations.rend();
1350+
++alloca) {
1351+
b.createDeallocStack(loc, *alloca);
1352+
}
1353+
}
1354+
});
1355+
}
1356+
}
1357+
10731358
void LifetimeChecker::doIt() {
10741359
// With any escapes tallied up, we can work through all the uses, checking
10751360
// for definitive initialization, promoting loads, rewriting assigns, and
@@ -1160,6 +1445,10 @@ void LifetimeChecker::doIt() {
11601445
// Insert hop_to_executor instructions for actor initializers, if needed.
11611446
injectActorHops();
11621447

1448+
// Insert `self.$storage` initialization for user-defined initializers
1449+
// declared in a type wrapped type.
1450+
injectTypeWrapperStorageInitalization();
1451+
11631452
// If the memory object had any non-trivial stores that are init or assign
11641453
// based on the control flow path reaching them, then insert dynamic control
11651454
// logic and CFG diamonds to handle this.

0 commit comments

Comments
 (0)