Skip to content

Commit 9f49fe7

Browse files
committed
[concurrency] NFC refactor out some code from LowerHopToActor before changing the pass.
Specifically, I am refactoring out the code that converts actor/Optional<any Actor> to an executor in preparation for adding code to LowerHopToExecutor that handles Builtin.ImplicitIsolationActor. The only actual functional change is that I made getExecutorForOptionalActor support being invoked when generating code (i.e. when its SILBuilder has an insertion point at the end of the block). It previously assumed that it would always have a real SILInstruction as an insertion point. The changes can be seen in the places where we now check if the insertion point equals the end of a block. Its very minor and due to conditional control flow doesn't have any actual impact given the manner that the code today is generated. This came up in a subsequent commit when I reuse this code to generate a helper function for converting Builtin.ImplicitIsolationActor to Builtin.Executor.
1 parent 99462a6 commit 9f49fe7

File tree

1 file changed

+125
-108
lines changed

1 file changed

+125
-108
lines changed

lib/SILOptimizer/Mandatory/LowerHopToActor.cpp

Lines changed: 125 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ namespace {
5151
/// inserts the derivations, turning those hops into hops to executors.
5252
/// IRGen expects hops to be to executors before it runs.
5353
class LowerHopToActor {
54-
SILFunction *F;
5554
DominanceInfo *Dominance;
5655

5756
/// A map from an actor value to the dominating instruction that
@@ -194,6 +193,118 @@ static AccessorDecl *getUnownedExecutorGetter(ASTContext &ctx,
194193
return nullptr;
195194
}
196195

196+
/// Emit the instructions to derive an executor value from an actor value.
197+
static SILValue getExecutorForActor(SILBuilder &B, SILLocation loc,
198+
SILValue actor) {
199+
auto *F = actor->getFunction();
200+
auto &ctx = F->getASTContext();
201+
auto executorType = SILType::getPrimitiveObjectType(ctx.TheExecutorType);
202+
203+
// If the actor type is a default actor, go ahead and devirtualize here.
204+
auto module = F->getModule().getSwiftModule();
205+
CanType actorType = actor->getType().getASTType();
206+
207+
// Determine if the actor is a "default actor" in which case we'll build a
208+
// default actor executor ref inline, rather than calling out to the
209+
// user-provided executor function.
210+
if (isDefaultActorType(actorType, module, F->getResilienceExpansion())) {
211+
auto builtinName = ctx.getIdentifier(
212+
getBuiltinName(BuiltinValueKind::BuildDefaultActorExecutorRef));
213+
auto builtinDecl = cast<FuncDecl>(getBuiltinValueDecl(ctx, builtinName));
214+
auto subs = SubstitutionMap::get(builtinDecl->getGenericSignature(),
215+
{actorType}, LookUpConformanceInModule());
216+
return B.createBuiltin(loc, builtinName, executorType, subs, {actor});
217+
}
218+
219+
// Otherwise, go through (Distributed)Actor.unownedExecutor.
220+
auto actorKind = actorType->isDistributedActor()
221+
? KnownProtocolKind::DistributedActor
222+
: KnownProtocolKind::Actor;
223+
auto actorProtocol = ctx.getProtocol(actorKind);
224+
auto req = getUnownedExecutorGetter(ctx, actorProtocol);
225+
assert(req && "Concurrency library broken");
226+
SILDeclRef fn(req, SILDeclRef::Kind::Func);
227+
228+
// Open an existential actor type.
229+
if (actorType->isExistentialType()) {
230+
actorType = ExistentialArchetypeType::get(actorType)->getCanonicalType();
231+
SILType loweredActorType = F->getLoweredType(actorType);
232+
actor = B.createOpenExistentialRef(loc, actor, loweredActorType);
233+
}
234+
235+
auto actorConf = lookupConformance(actorType, actorProtocol);
236+
assert(actorConf && "hop_to_executor with actor that doesn't conform to "
237+
"Actor or DistributedActor");
238+
239+
auto subs = SubstitutionMap::get(req->getGenericSignature(), {actorType},
240+
{actorConf});
241+
auto fnType = F->getModule().Types.getConstantFunctionType(*F, fn);
242+
243+
auto witness = B.createWitnessMethod(loc, actorType, actorConf, fn,
244+
SILType::getPrimitiveObjectType(fnType));
245+
auto witnessCall = B.createApply(loc, witness, subs, {actor});
246+
247+
// The protocol requirement returns an UnownedSerialExecutor; extract
248+
// the Builtin.Executor from it.
249+
auto executorDecl = ctx.getUnownedSerialExecutorDecl();
250+
auto executorProps = executorDecl->getStoredProperties();
251+
assert(executorProps.size() == 1);
252+
return B.createStructExtract(loc, witnessCall, executorProps[0]);
253+
}
254+
255+
static SILValue getExecutorForOptionalActor(SILBuilder &B, SILLocation loc,
256+
SILValue actor) {
257+
auto &ctx = B.getASTContext();
258+
auto executorType = SILType::getPrimitiveObjectType(ctx.TheExecutorType);
259+
auto optionalExecutorType = SILType::getOptionalType(executorType);
260+
261+
// Unwrap the optional and call 'unownedExecutor'.
262+
auto *someDecl = ctx.getOptionalSomeDecl();
263+
auto *curBB = B.getInsertionBB();
264+
auto *contBB = B.getInsertionPoint() == curBB->end()
265+
? B.getFunction().createBasicBlockAfter(curBB)
266+
: curBB->split(B.getInsertionPoint());
267+
auto *someBB = B.getFunction().createBasicBlockAfter(curBB);
268+
auto *noneBB = B.getFunction().createBasicBlockAfter(someBB);
269+
270+
// unmarked executor
271+
SILValue result = contBB->createPhiArgument(optionalExecutorType,
272+
actor->getOwnershipKind());
273+
274+
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 1> caseBBs;
275+
caseBBs.push_back(std::make_pair(someDecl, someBB));
276+
B.setInsertionPoint(curBB);
277+
auto *switchEnum = B.createSwitchEnum(loc, actor, noneBB, caseBBs);
278+
279+
SILValue unwrappedActor;
280+
if (B.hasOwnership()) {
281+
unwrappedActor = switchEnum->createOptionalSomeResult();
282+
B.setInsertionPoint(someBB);
283+
} else {
284+
B.setInsertionPoint(someBB);
285+
unwrappedActor = B.createUncheckedEnumData(loc, actor, someDecl);
286+
}
287+
288+
// Call 'unownedExecutor' in the some block and wrap the result into
289+
// an optional.
290+
SILValue unwrappedExecutor = getExecutorForActor(B, loc, unwrappedActor);
291+
SILValue someValue =
292+
B.createOptionalSome(loc, unwrappedExecutor, optionalExecutorType);
293+
B.createBranch(loc, contBB, {someValue});
294+
295+
// In the none case, create a nil executor value, which represents
296+
// the generic executor.
297+
B.setInsertionPoint(noneBB);
298+
SILValue noneValue = B.createOptionalNone(loc, optionalExecutorType);
299+
B.createBranch(loc, contBB, {noneValue});
300+
if (contBB->begin() == contBB->end()) {
301+
B.setInsertionPoint(contBB);
302+
} else {
303+
B.setInsertionPoint(contBB->begin());
304+
}
305+
return result;
306+
}
307+
197308
SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
198309
SILLocation loc, SILValue actor,
199310
bool makeOptional) {
@@ -204,125 +315,31 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
204315
// If the operand is already a BuiltinExecutorType, just wrap it
205316
// in an optional.
206317
if (makeOptional && actor->getType().is<BuiltinExecutorType>()) {
207-
return B.createOptionalSome(
208-
loc, actor,
209-
SILType::getOptionalType(actor->getType()));
318+
return B.createOptionalSome(loc, actor,
319+
SILType::getOptionalType(actor->getType()));
210320
}
211321

212-
auto &ctx = F->getASTContext();
213-
auto executorType = SILType::getPrimitiveObjectType(ctx.TheExecutorType);
214-
auto optionalExecutorType = SILType::getOptionalType(executorType);
215-
216-
/// Emit the instructions to derive an executor value from an actor value.
217-
auto getExecutorFor = [&](SILValue actor) -> SILValue {
218-
// If the actor type is a default actor, go ahead and devirtualize here.
219-
auto module = F->getModule().getSwiftModule();
220-
CanType actorType = actor->getType().getASTType();
221-
222-
// Determine if the actor is a "default actor" in which case we'll build a default
223-
// actor executor ref inline, rather than calling out to the user-provided executor function.
224-
if (isDefaultActorType(actorType, module, F->getResilienceExpansion())) {
225-
auto builtinName = ctx.getIdentifier(
226-
getBuiltinName(BuiltinValueKind::BuildDefaultActorExecutorRef));
227-
auto builtinDecl = cast<FuncDecl>(getBuiltinValueDecl(ctx, builtinName));
228-
auto subs = SubstitutionMap::get(builtinDecl->getGenericSignature(),
229-
{actorType},
230-
LookUpConformanceInModule());
231-
return B.createBuiltin(loc, builtinName, executorType, subs, {actor});
232-
}
233-
234-
// Otherwise, go through (Distributed)Actor.unownedExecutor.
235-
auto actorKind = actorType->isDistributedActor() ?
236-
KnownProtocolKind::DistributedActor :
237-
KnownProtocolKind::Actor;
238-
auto actorProtocol = ctx.getProtocol(actorKind);
239-
auto req = getUnownedExecutorGetter(ctx, actorProtocol);
240-
assert(req && "Concurrency library broken");
241-
SILDeclRef fn(req, SILDeclRef::Kind::Func);
242-
243-
// Open an existential actor type.
244-
if (actorType->isExistentialType()) {
245-
actorType = ExistentialArchetypeType::get(actorType)->getCanonicalType();
246-
SILType loweredActorType = F->getLoweredType(actorType);
247-
actor = B.createOpenExistentialRef(loc, actor, loweredActorType);
248-
}
249-
250-
auto actorConf = lookupConformance(actorType, actorProtocol);
251-
assert(actorConf &&
252-
"hop_to_executor with actor that doesn't conform to Actor or DistributedActor");
253-
254-
auto subs = SubstitutionMap::get(req->getGenericSignature(),
255-
{actorType}, {actorConf});
256-
auto fnType = F->getModule().Types.getConstantFunctionType(*F, fn);
257-
258-
auto witness =
259-
B.createWitnessMethod(loc, actorType, actorConf, fn,
260-
SILType::getPrimitiveObjectType(fnType));
261-
auto witnessCall = B.createApply(loc, witness, subs, {actor});
262-
263-
// The protocol requirement returns an UnownedSerialExecutor; extract
264-
// the Builtin.Executor from it.
265-
auto executorDecl = ctx.getUnownedSerialExecutorDecl();
266-
auto executorProps = executorDecl->getStoredProperties();
267-
assert(executorProps.size() == 1);
268-
return B.createStructExtract(loc, witnessCall, executorProps[0]);
269-
};
270-
271322
bool needEndBorrow = false;
272323
SILValue unmarkedExecutor;
273324
if (auto wrappedActor = actorType->getOptionalObjectType()) {
274325
assert(makeOptional);
275-
276-
if (B.hasOwnership() && actor->getOwnershipKind() != OwnershipKind::Guaranteed) {
326+
if (B.hasOwnership() &&
327+
actor->getOwnershipKind() != OwnershipKind::Guaranteed) {
277328
actor = B.createBeginBorrow(loc, actor);
278329
needEndBorrow = true;
279330
}
280331

281-
// Unwrap the optional and call 'unownedExecutor'.
282-
auto *someDecl = B.getASTContext().getOptionalSomeDecl();
283-
auto *curBB = B.getInsertionPoint()->getParent();
284-
auto *contBB = curBB->split(B.getInsertionPoint());
285-
auto *someBB = B.getFunction().createBasicBlockAfter(curBB);
286-
auto *noneBB = B.getFunction().createBasicBlockAfter(someBB);
287-
288-
unmarkedExecutor = contBB->createPhiArgument(
289-
optionalExecutorType, actor->getOwnershipKind());
290-
291-
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 1> caseBBs;
292-
caseBBs.push_back(std::make_pair(someDecl, someBB));
293-
B.setInsertionPoint(curBB);
294-
auto *switchEnum = B.createSwitchEnum(loc, actor, noneBB, caseBBs);
295-
296-
SILValue unwrappedActor;
297-
if (B.hasOwnership()) {
298-
unwrappedActor = switchEnum->createOptionalSomeResult();
299-
B.setInsertionPoint(someBB);
300-
} else {
301-
B.setInsertionPoint(someBB);
302-
unwrappedActor = B.createUncheckedEnumData(loc, actor, someDecl);
303-
}
304-
305-
// Call 'unownedExecutor' in the some block and wrap the result into
306-
// an optional.
307-
SILValue unwrappedExecutor = getExecutorFor(unwrappedActor);
308-
SILValue someValue =
309-
B.createOptionalSome(loc, unwrappedExecutor, optionalExecutorType);
310-
B.createBranch(loc, contBB, {someValue});
311-
312-
// In the none case, create a nil executor value, which represents
313-
// the generic executor.
314-
B.setInsertionPoint(noneBB);
315-
SILValue noneValue = B.createOptionalNone(loc, optionalExecutorType);
316-
B.createBranch(loc, contBB, {noneValue});
317-
B.setInsertionPoint(contBB->begin());
332+
unmarkedExecutor = getExecutorForOptionalActor(B, loc, actor);
318333
} else {
319-
unmarkedExecutor = getExecutorFor(actor);
334+
unmarkedExecutor = getExecutorForActor(B, loc, actor);
335+
}
320336

321-
// Inject the result into an optional if requested.
322-
if (makeOptional) {
323-
unmarkedExecutor = B.createOptionalSome(loc, unmarkedExecutor,
324-
SILType::getOptionalType(unmarkedExecutor->getType()));
325-
}
337+
// Inject the result into an optional if requested and if our executor is not
338+
// yet optional.
339+
if (makeOptional && !unmarkedExecutor->getType().getOptionalObjectType()) {
340+
unmarkedExecutor = B.createOptionalSome(
341+
loc, unmarkedExecutor,
342+
SILType::getOptionalType(unmarkedExecutor->getType()));
326343
}
327344

328345
// Mark the dependence of the resulting value on the actor value to

0 commit comments

Comments
 (0)