Skip to content

Commit 6c1a2e2

Browse files
committed
[SILGen] Emit hop-to-executor for calls to global-actor-qualified functions.
1 parent 7560298 commit 6c1a2e2

File tree

4 files changed

+58
-21
lines changed

4 files changed

+58
-21
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3651,15 +3651,15 @@ class CallEmission {
36513651

36523652
Callee callee;
36533653
FormalEvaluationScope initialWritebackScope;
3654-
bool implicitlyAsync;
3654+
Optional<ActorIsolation> implicitAsyncIsolation;
36553655

36563656
public:
36573657
/// Create an emission for a call of the given callee.
36583658
CallEmission(SILGenFunction &SGF, Callee &&callee,
36593659
FormalEvaluationScope &&writebackScope)
36603660
: SGF(SGF), callee(std::move(callee)),
36613661
initialWritebackScope(std::move(writebackScope)),
3662-
implicitlyAsync(false) {}
3662+
implicitAsyncIsolation(None) {}
36633663

36643664
/// A factory method for decomposing the apply expr \p e into a call
36653665
/// emission.
@@ -3699,7 +3699,9 @@ class CallEmission {
36993699
/// Sets a flag that indicates whether this call be treated as being
37003700
/// implicitly async, i.e., it requires a hop_to_executor prior to
37013701
/// invoking the sync callee, etc.
3702-
void setImplicitlyAsync(bool flag) { implicitlyAsync = flag; }
3702+
void setImplicitlyAsync(Optional<ActorIsolation> implicitAsyncIsolation) {
3703+
this->implicitAsyncIsolation = implicitAsyncIsolation;
3704+
}
37033705

37043706
CleanupHandle applyCoroutine(SmallVectorImpl<ManagedValue> &yields);
37053707

@@ -3920,15 +3922,11 @@ RValue CallEmission::applyNormalCall(SGFContext C) {
39203922

39213923
auto mv = callee.getFnValue(SGF, borrowedSelf);
39223924

3923-
Optional<ValueDecl*> calleeDeclInfo;
3924-
if (implicitlyAsync)
3925-
calleeDeclInfo = callee.getDecl();
3926-
39273925
// Emit the uncurried call.
39283926
return SGF.emitApply(
39293927
std::move(resultPlan), std::move(argScope), uncurriedLoc.getValue(), mv,
39303928
callee.getSubstitutions(), uncurriedArgs, calleeTypeInfo, options,
3931-
uncurriedContext, calleeDeclInfo);
3929+
uncurriedContext, implicitAsyncIsolation);
39323930
}
39333931

39343932
static void emitPseudoFunctionArguments(SILGenFunction &SGF,
@@ -4243,7 +4241,28 @@ CallEmission CallEmission::forApplyExpr(SILGenFunction &SGF, ApplyExpr *e) {
42434241
apply.callSite->isNoThrows(),
42444242
apply.callSite->isNoAsync());
42454243

4246-
emission.setImplicitlyAsync(apply.callSite->implicitlyAsync());
4244+
// For an implicitly-async call, determine the actor isolation.
4245+
if (apply.callSite->implicitlyAsync()) {
4246+
Optional<ActorIsolation> isolation;
4247+
4248+
// Check for global-actor isolation on the function type.
4249+
if (auto fnType = apply.callSite->getFn()->getType()
4250+
->castTo<FunctionType>()) {
4251+
if (Type globalActor = fnType->getGlobalActor()) {
4252+
isolation = ActorIsolation::forGlobalActor(globalActor, false);
4253+
}
4254+
}
4255+
4256+
// If there was no global-actor isolation on the function type, find
4257+
// the callee declaration and retrieve the isolation from it.
4258+
if (!isolation) {
4259+
if (auto decl = emission.callee.getDecl())
4260+
isolation = getActorIsolation(decl);
4261+
}
4262+
4263+
assert(isolation && "Implicitly asynchronous call without isolation");
4264+
emission.setImplicitlyAsync(isolation);
4265+
}
42474266
}
42484267

42494268
return emission;
@@ -4302,7 +4321,7 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
43024321
ArrayRef<ManagedValue> args,
43034322
const CalleeTypeInfo &calleeTypeInfo,
43044323
ApplyOptions options, SGFContext evalContext,
4305-
Optional<ValueDecl *> implicitlyAsyncApply) {
4324+
Optional<ActorIsolation> implicitAsyncIsolation) {
43064325
auto substFnType = calleeTypeInfo.substFnType;
43074326
auto substResultType = calleeTypeInfo.substResultType;
43084327

@@ -4390,22 +4409,27 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
43904409

43914410
ExecutorBreadcrumb breadcrumb;
43924411

4393-
// The presence of `implicitlyAsyncApply` indicates that the callee is a
4412+
// The presence of `implicitAsyncIsolation` indicates that the callee is a
43944413
// synchronous function isolated to an actor other than our own.
43954414
// Such functions require the caller to hop to the callee's executor
43964415
// prior to invoking the callee.
4397-
if (implicitlyAsyncApply.hasValue()) {
4416+
if (implicitAsyncIsolation) {
43984417
assert(F.isAsync() && "cannot hop_to_executor in a non-async func!");
43994418

4400-
auto calleeVD = implicitlyAsyncApply.getValue();
4401-
if (auto *funcDecl = dyn_cast_or_null<AbstractFunctionDecl>(calleeVD)) {
4402-
Optional<ManagedValue> actorSelf;
4419+
switch (*implicitAsyncIsolation) {
4420+
case ActorIsolation::ActorInstance:
4421+
breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation,
4422+
args.back());
4423+
break;
44034424

4404-
if (args.size() > 0)
4405-
actorSelf = args.back();
4425+
case ActorIsolation::GlobalActor:
4426+
case ActorIsolation::GlobalActorUnsafe:
4427+
breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation, None);
4428+
break;
44064429

4407-
breadcrumb = emitHopToTargetActor(loc, getActorIsolation(funcDecl),
4408-
actorSelf);
4430+
case ActorIsolation::Independent:
4431+
case ActorIsolation::Unspecified:
4432+
llvm_unreachable("Not actor-isolated");
44094433
}
44104434
} else if (ExpectedExecutor && substFnType->isAsync()) {
44114435
// Otherwise, if we're in an actor method ourselves, and we're calling into

lib/SILGen/SILGenFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
15841584
ArrayRef<ManagedValue> args,
15851585
const CalleeTypeInfo &calleeTypeInfo, ApplyOptions options,
15861586
SGFContext evalContext,
1587-
Optional<ValueDecl *> implicitlyAsyncApply);
1587+
Optional<ActorIsolation> implicitAsyncIsolation);
15881588

15891589
RValue emitApplyOfDefaultArgGenerator(SILLocation loc,
15901590
ConcreteDeclRef defaultArgsOwner,

lib/Sema/TypeCheckEffects.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,11 @@ class ApplyClassifier {
908908
assert(param->getInterfaceType()
909909
->lookThroughAllOptionalTypes()
910910
->castTo<AnyFunctionType>()
911-
->hasEffect(kind));
911+
->hasEffect(kind) ||
912+
!param->getInterfaceType()
913+
->lookThroughAllOptionalTypes()
914+
->castTo<AnyFunctionType>()
915+
->getGlobalActor().isNull());
912916

913917
// If we're currently doing rethrows-checking on the body of the
914918
// function which declares the parameter, it's rethrowing-only.

test/SILGen/hop_to_executor.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,12 @@ func unspecifiedAsyncFunc() async {
236236
func anotherUnspecifiedAsyncFunc(_ red : RedActorImpl) async {
237237
await red.hello(12);
238238
}
239+
240+
// CHECK-LABEL: sil hidden [ossa] @$s4test0A20GlobalActorFuncValueyyyyXEYaF
241+
// CHECK: function_ref @$s4test8RedActorV6sharedAA0bC4ImplCvgZ
242+
// CHECK: hop_to_executor [[RED:%[0-9]+]] : $RedActorImpl
243+
// CHECK-NEXT: apply
244+
// CHECK-NEXT: hop_to_executor [[PREV:%[0-9]+]] : $Builtin.Executor
245+
func testGlobalActorFuncValue(_ fn: @RedActor () -> Void) async {
246+
await fn()
247+
}

0 commit comments

Comments
 (0)