Skip to content

Commit d22255e

Browse files
authored
Merge pull request #83855 from gottesmm/pr-723e8720ba1310d5199281d6ef14fd5ab808667c
[concurrency] NFC refactor out some code from LowerHopToActor before changing the pass.
2 parents 8b9ebcc + 21915ae commit d22255e

File tree

1 file changed

+127
-114
lines changed

1 file changed

+127
-114
lines changed

lib/SILOptimizer/Mandatory/LowerHopToActor.cpp

Lines changed: 127 additions & 114 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
@@ -72,11 +71,7 @@ class LowerHopToActor {
7271
SILValue actor, bool makeOptional);
7372

7473
public:
75-
LowerHopToActor(SILFunction *f,
76-
DominanceInfo *dominance)
77-
: F(f),
78-
Dominance(dominance)
79-
{ }
74+
LowerHopToActor(DominanceInfo *dominance) : Dominance(dominance) {}
8075

8176
/// The entry point to the transformation.
8277
bool run();
@@ -194,6 +189,118 @@ static AccessorDecl *getUnownedExecutorGetter(ASTContext &ctx,
194189
return nullptr;
195190
}
196191

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

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-
271318
bool needEndBorrow = false;
272319
SILValue unmarkedExecutor;
273320
if (auto wrappedActor = actorType->getOptionalObjectType()) {
274321
assert(makeOptional);
275-
276-
if (B.hasOwnership() && actor->getOwnershipKind() != OwnershipKind::Guaranteed) {
322+
if (B.hasOwnership() &&
323+
actor->getOwnershipKind() != OwnershipKind::Guaranteed) {
277324
actor = B.createBeginBorrow(loc, actor);
278325
needEndBorrow = true;
279326
}
280327

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());
328+
unmarkedExecutor = getExecutorForOptionalActor(B, loc, actor);
318329
} else {
319-
unmarkedExecutor = getExecutorFor(actor);
330+
unmarkedExecutor = getExecutorForActor(B, loc, actor);
331+
}
320332

321-
// Inject the result into an optional if requested.
322-
if (makeOptional) {
323-
unmarkedExecutor = B.createOptionalSome(loc, unmarkedExecutor,
324-
SILType::getOptionalType(unmarkedExecutor->getType()));
325-
}
333+
// Inject the result into an optional if requested and if our executor is not
334+
// yet optional.
335+
if (makeOptional && !unmarkedExecutor->getType().getOptionalObjectType()) {
336+
unmarkedExecutor = B.createOptionalSome(
337+
loc, unmarkedExecutor,
338+
SILType::getOptionalType(unmarkedExecutor->getType()));
326339
}
327340

328341
// Mark the dependence of the resulting value on the actor value to
@@ -342,7 +355,7 @@ class LowerHopToActorPass : public SILFunctionTransform {
342355
void run() override {
343356
auto fn = getFunction();
344357
auto domTree = getAnalysis<DominanceAnalysis>()->get(fn);
345-
LowerHopToActor pass(getFunction(), domTree);
358+
LowerHopToActor pass(domTree);
346359
if (pass.run())
347360
invalidateAnalysis(SILAnalysis::InvalidationKind::BranchesAndInstructions);
348361
}

0 commit comments

Comments
 (0)