Skip to content

Commit 60fa93f

Browse files
committed
emit hop_to_executor before & sometimes after implicitly-async calls
implicitly-async calls are calls to synchronous actor-isolated functions. Synchronous functions cannot perform hop_to_executor, so implicitly async calls have the convention that the caller is responsible for switching to the right executor prior to entering the actor-isolated callee. It follows naturally that the caller must then switch back to the appropriate executor after the implicitly-async call completed. Now, if the caller is not isolated to a _specific_ actor, then we are (currently) _not_ emitting a hop to go back to the caller's executor, because that caller's executor is unspecified (and currently not accessable in SIL). This behavior may change in the future; tracked in rdar://71905765
1 parent 233eccd commit 60fa93f

File tree

7 files changed

+234
-32
lines changed

7 files changed

+234
-32
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,10 @@ class Callee {
546546
return cast<EnumElementDecl>(Constant.getDecl());
547547
}
548548

549+
ValueDecl *getDecl() {
550+
return Constant.getDecl();
551+
}
552+
549553
CalleeTypeInfo createCalleeTypeInfo(SILGenFunction &SGF,
550554
Optional<SILDeclRef> constant,
551555
SILType formalFnType) const & {
@@ -3646,14 +3650,16 @@ class CallEmission {
36463650
Callee callee;
36473651
FormalEvaluationScope initialWritebackScope;
36483652
unsigned expectedSiteCount;
3653+
bool implicitlyAsync;
36493654

36503655
public:
36513656
/// Create an emission for a call of the given callee.
36523657
CallEmission(SILGenFunction &SGF, Callee &&callee,
36533658
FormalEvaluationScope &&writebackScope)
36543659
: SGF(SGF), callee(std::move(callee)),
36553660
initialWritebackScope(std::move(writebackScope)),
3656-
expectedSiteCount(callee.getParameterListCount()) {}
3661+
expectedSiteCount(callee.getParameterListCount()),
3662+
implicitlyAsync(false) {}
36573663

36583664
/// A factory method for decomposing the apply expr \p e into a call
36593665
/// emission.
@@ -3688,6 +3694,11 @@ class CallEmission {
36883694
return (callee.kind == Callee::Kind::EnumElement);
36893695
}
36903696

3697+
/// Sets a flag that indicates whether this call be treated as being
3698+
/// implicitly async, i.e., it requires a hop_to_executor prior to
3699+
/// invoking the sync callee, etc.
3700+
void setImplicitlyAsync(bool flag) { implicitlyAsync = flag; }
3701+
36913702
CleanupHandle applyCoroutine(SmallVectorImpl<ManagedValue> &yields);
36923703

36933704
RValue apply(SGFContext C = SGFContext()) {
@@ -3912,11 +3923,15 @@ RValue CallEmission::applyNormalCall(SGFContext C) {
39123923

39133924
auto mv = callee.getFnValue(SGF, borrowedSelf);
39143925

3926+
Optional<ValueDecl*> calleeDeclInfo;
3927+
if (implicitlyAsync)
3928+
calleeDeclInfo = callee.getDecl();
3929+
39153930
// Emit the uncurried call.
39163931
return SGF.emitApply(
39173932
std::move(resultPlan), std::move(argScope), uncurriedLoc.getValue(), mv,
39183933
callee.getSubstitutions(), uncurriedArgs, calleeTypeInfo, options,
3919-
uncurriedContext);
3934+
uncurriedContext, calleeDeclInfo);
39203935
}
39213936

39223937
static void emitPseudoFunctionArguments(SILGenFunction &SGF,
@@ -4224,6 +4239,8 @@ CallEmission CallEmission::forApplyExpr(SILGenFunction &SGF, ApplyExpr *e) {
42244239

42254240
emission.addCallSite(apply.callSite, std::move(preparedArgs),
42264241
apply.callSite->throws());
4242+
4243+
emission.setImplicitlyAsync(apply.callSite->implicitlyAsync());
42274244
}
42284245

42294246
return emission;
@@ -4266,6 +4283,31 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) {
42664283
return self.isFormalIndirect();
42674284
}
42684285

4286+
Optional<SILValue> SILGenFunction::EmitLoadActorExecutorForCallee(
4287+
SILGenFunction *SGF,
4288+
ValueDecl *calleeVD,
4289+
ArrayRef<ManagedValue> args) {
4290+
if (auto *funcDecl = dyn_cast_or_null<AbstractFunctionDecl>(calleeVD)) {
4291+
auto actorIso = getActorIsolation(funcDecl);
4292+
switch (actorIso.getKind()) {
4293+
case ActorIsolation::Unspecified:
4294+
case ActorIsolation::Independent:
4295+
case ActorIsolation::IndependentUnsafe:
4296+
break;
4297+
4298+
case ActorIsolation::ActorInstance: {
4299+
assert(args.size() > 0 && "no self argument for actor-instance call?");
4300+
auto calleeSelf = args.back();
4301+
return calleeSelf.borrow(*SGF, SGF->F.getLocation()).getValue();
4302+
}
4303+
4304+
case ActorIsolation::GlobalActor:
4305+
return SGF->emitLoadGlobalActorExecutor(actorIso.getGlobalActor());
4306+
}
4307+
}
4308+
return None;
4309+
}
4310+
42694311
//===----------------------------------------------------------------------===//
42704312
// Top Level Entrypoints
42714313
//===----------------------------------------------------------------------===//
@@ -4279,7 +4321,8 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
42794321
ManagedValue fn, SubstitutionMap subs,
42804322
ArrayRef<ManagedValue> args,
42814323
const CalleeTypeInfo &calleeTypeInfo,
4282-
ApplyOptions options, SGFContext evalContext) {
4324+
ApplyOptions options, SGFContext evalContext,
4325+
Optional<ValueDecl *> implicitlyAsyncApply) {
42834326
auto substFnType = calleeTypeInfo.substFnType;
42844327
auto substResultType = calleeTypeInfo.substResultType;
42854328

@@ -4365,15 +4408,31 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
43654408
subs.getGenericSignature().getCanonicalSignature());
43664409
}
43674410

4368-
auto rawDirectResult = [&] {
4411+
// The presence of `implicitlyAsyncApply` indicates that the callee is a
4412+
// synchronous function isolated to an actor other than our own.
4413+
// Such functions require the caller to hop to the callee's executor
4414+
// prior to invoking the callee.
4415+
if (implicitlyAsyncApply.hasValue()) {
4416+
assert(F.isAsync() && "cannot hop_to_executor in a non-async func!");
4417+
4418+
auto calleeVD = implicitlyAsyncApply.getValue();
4419+
auto maybeExecutor = EmitLoadActorExecutorForCallee(this, calleeVD, args);
4420+
4421+
assert(maybeExecutor.hasValue());
4422+
B.createHopToExecutor(loc, maybeExecutor.getValue());
4423+
}
4424+
4425+
SILValue rawDirectResult;
4426+
{
43694427
SmallVector<SILValue, 1> rawDirectResults;
43704428
emitRawApply(*this, loc, fn, subs, args, substFnType, options,
43714429
indirectResultAddrs, rawDirectResults);
43724430
assert(rawDirectResults.size() == 1);
4373-
return rawDirectResults[0];
4374-
}();
4431+
rawDirectResult = rawDirectResults[0];
4432+
}
43754433

4376-
if (substFnType->isAsync())
4434+
// hop back to the current executor
4435+
if (substFnType->isAsync() || implicitlyAsyncApply.hasValue())
43774436
emitHopToCurrentExecutor(loc);
43784437

43794438
// Pop the argument scope.
@@ -4482,7 +4541,7 @@ RValue SILGenFunction::emitMonomorphicApply(
44824541
*this, calleeTypeInfo, loc, evalContext);
44834542
ArgumentScope argScope(*this, loc);
44844543
return emitApply(std::move(resultPlan), std::move(argScope), loc, fn, {},
4485-
args, calleeTypeInfo, options, evalContext);
4544+
args, calleeTypeInfo, options, evalContext, None);
44864545
}
44874546

44884547
/// Emit either an 'apply' or a 'try_apply', with the error branch of
@@ -4768,7 +4827,7 @@ SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc,
47684827
ResultPlanBuilder::computeResultPlan(*this, calleeTypeInfo, loc, ctx);
47694828
ArgumentScope argScope(*this, loc);
47704829
return emitApply(std::move(resultPlan), std::move(argScope), loc, mv, subMap,
4771-
finalArgs, calleeTypeInfo, ApplyOptions::None, ctx);
4830+
finalArgs, calleeTypeInfo, ApplyOptions::None, ctx, None);
47724831
}
47734832

47744833
StringRef SILGenFunction::getMagicFunctionString() {

lib/SILGen/SILGenBridging.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ emitBridgeObjectiveCToNative(SILGenFunction &SGF,
243243
SGF.emitApply(std::move(resultPlan), std::move(argScope), loc,
244244
ManagedValue::forUnmanaged(witnessRef), subs,
245245
{objcValue, ManagedValue::forUnmanaged(metatypeValue)},
246-
calleeTypeInfo, ApplyOptions::None, context);
246+
calleeTypeInfo, ApplyOptions::None, context, None);
247247
return std::move(result).getAsSingleValue(SGF, loc);
248248
}
249249

@@ -1875,7 +1875,7 @@ void SILGenFunction::emitForeignToNativeThunk(SILDeclRef thunk) {
18751875
ManagedValue resultMV =
18761876
emitApply(std::move(resultPlan), std::move(argScope), fd,
18771877
ManagedValue::forUnmanaged(fn), subs, args,
1878-
calleeTypeInfo, ApplyOptions::None, context)
1878+
calleeTypeInfo, ApplyOptions::None, context, None)
18791879
.getAsSingleValue(*this, fd);
18801880

18811881
if (indirectResult) {

lib/SILGen/SILGenExpr.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,7 +2271,7 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc,
22712271
captures);
22722272

22732273
return emitApply(std::move(resultPtr), std::move(argScope), loc, fnRef,
2274-
subs, captures, calleeTypeInfo, ApplyOptions::None, C);
2274+
subs, captures, calleeTypeInfo, ApplyOptions::None, C, None);
22752275
}
22762276

22772277
RValue SILGenFunction::emitApplyOfStoredPropertyInitializer(
@@ -2294,7 +2294,7 @@ RValue SILGenFunction::emitApplyOfStoredPropertyInitializer(
22942294
ResultPlanBuilder::computeResultPlan(*this, calleeTypeInfo, loc, C);
22952295
ArgumentScope argScope(*this, loc);
22962296
return emitApply(std::move(resultPlan), std::move(argScope), loc, fnRef,
2297-
subs, {}, calleeTypeInfo, ApplyOptions::None, C);
2297+
subs, {}, calleeTypeInfo, ApplyOptions::None, C, None);
22982298
}
22992299

23002300
RValue RValueEmitter::visitDestructureTupleExpr(DestructureTupleExpr *E,
@@ -3201,7 +3201,7 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM,
32013201
loc, ManagedValue::forUnmanaged(equalsWitness),
32023202
equatableSub,
32033203
{lhsArg, rhsArg, metatyValue},
3204-
equalsInfo, ApplyOptions::None, SGFContext())
3204+
equalsInfo, ApplyOptions::None, SGFContext(), None)
32053205
.getUnmanagedSingleValue(subSGF, loc);
32063206
}
32073207

lib/SILGen/SILGenFunction.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,27 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
834834

835835
void mergeCleanupBlocks();
836836

837+
//===--------------------------------------------------------------------===//
838+
// Concurrency
839+
//===--------------------------------------------------------------------===//
840+
841+
/// Generates code into the given SGF that obtains the callee function's
842+
/// executor, if the function is actor-isolated.
843+
/// @returns a SILValue representing the executor, if an executor exists.
844+
static Optional<SILValue> EmitLoadActorExecutorForCallee(
845+
SILGenFunction *SGF,
846+
ValueDecl *calleeVD,
847+
ArrayRef<ManagedValue> args);
848+
849+
/// Generates code to obtain the executor given the actor's decl.
850+
/// @returns a SILValue representing the executor.
851+
SILValue emitLoadActorExecutor(VarDecl *actorDecl);
852+
853+
/// Generates the code to obtain the executor for the shared instance
854+
/// of the \p globalActor based on the type.
855+
/// @returns a SILValue representing the executor.
856+
SILValue emitLoadGlobalActorExecutor(Type globalActor);
857+
837858
//===--------------------------------------------------------------------===//
838859
// Memory management
839860
//===--------------------------------------------------------------------===//
@@ -852,10 +873,6 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
852873
Type resultType, DeclContext *DC,
853874
bool throws, SourceLoc throwsLoc);
854875

855-
/// Initializes 'actor' with the loaded shared instance of the \p globalActor
856-
/// type.
857-
void loadGlobalActor(Type globalActor);
858-
859876
/// Create SILArguments in the entry block that bind a single value
860877
/// of the given parameter suitably for being forwarded.
861878
void bindParameterForForwarding(ParamDecl *param,
@@ -1544,7 +1561,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
15441561
SILLocation loc, ManagedValue fn, SubstitutionMap subs,
15451562
ArrayRef<ManagedValue> args,
15461563
const CalleeTypeInfo &calleeTypeInfo, ApplyOptions options,
1547-
SGFContext evalContext);
1564+
SGFContext evalContext,
1565+
Optional<ValueDecl *> implicitlyAsyncApply);
15481566

15491567
RValue emitApplyOfDefaultArgGenerator(SILLocation loc,
15501568
ConcreteDeclRef defaultArgsOwner,

lib/SILGen/SILGenPoly.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4485,6 +4485,8 @@ SILGenFunction::emitVTableThunk(SILDeclRef base,
44854485
// Concurrency
44864486
//===----------------------------------------------------------------------===//
44874487

4488+
/// If the current function is associated with an actor, then this
4489+
/// function emits a hop_to_executor to that actor's executor at loc.
44884490
void SILGenFunction::emitHopToCurrentExecutor(SILLocation loc) {
44894491
if (actor)
44904492
B.createHopToExecutor(loc, actor);

lib/SILGen/SILGenProlog.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,6 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
456456

457457
// Initialize 'actor' if the function is an actor-isolated function or
458458
// closure.
459-
460459
if (auto *funcDecl =
461460
dyn_cast_or_null<AbstractFunctionDecl>(FunctionDC->getAsDecl())) {
462461
auto actorIsolation = getActorIsolation(funcDecl);
@@ -465,38 +464,45 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
465464
case ActorIsolation::Independent:
466465
case ActorIsolation::IndependentUnsafe:
467466
break;
467+
468468
case ActorIsolation::ActorInstance: {
469469
assert(selfParam && "no self parameter for ActorInstance isolation");
470470
ManagedValue selfArg = ManagedValue::forUnmanaged(F.getSelfArgument());
471471
actor = selfArg.borrow(*this, F.getLocation()).getValue();
472472
break;
473473
}
474+
474475
case ActorIsolation::GlobalActor:
475-
loadGlobalActor(actorIsolation.getGlobalActor());
476+
actor = emitLoadGlobalActorExecutor(actorIsolation.getGlobalActor());
476477
break;
477478
}
478479
} else if (auto *closureExpr = dyn_cast<AbstractClosureExpr>(FunctionDC)) {
479480
auto actorIsolation = closureExpr->getActorIsolation();
480481
switch (actorIsolation.getKind()) {
481482
case ClosureActorIsolation::Independent:
482483
break;
483-
case ClosureActorIsolation::ActorInstance: {
484-
VarDecl *actorDecl = actorIsolation.getActorInstance();
485-
RValue actorInstanceRV = emitRValueForDecl(F.getLocation(),
486-
actorDecl, actorDecl->getType(), AccessSemantics::Ordinary);
487-
ManagedValue actorInstance = std::move(actorInstanceRV).getScalarValue();
488-
actor = actorInstance.borrow(*this, F.getLocation()).getValue();
484+
485+
case ClosureActorIsolation::ActorInstance:
486+
actor = emitLoadActorExecutor(actorIsolation.getActorInstance());
489487
break;
490-
}
488+
491489
case ClosureActorIsolation::GlobalActor:
492-
loadGlobalActor(actorIsolation.getGlobalActor());
490+
actor = emitLoadGlobalActorExecutor(actorIsolation.getGlobalActor());
493491
break;
494492
}
495493
}
494+
496495
emitHopToCurrentExecutor(F.getLocation());
497496
}
498497

499-
void SILGenFunction::loadGlobalActor(Type globalActor) {
498+
SILValue SILGenFunction::emitLoadActorExecutor(VarDecl *actorDecl) {
499+
RValue actorInstanceRV = emitRValueForDecl(F.getLocation(),
500+
actorDecl, actorDecl->getType(), AccessSemantics::Ordinary);
501+
ManagedValue actorInstance = std::move(actorInstanceRV).getScalarValue();
502+
return actorInstance.borrow(*this, F.getLocation()).getValue();
503+
}
504+
505+
SILValue SILGenFunction::emitLoadGlobalActorExecutor(Type globalActor) {
500506
assert(F.isAsync());
501507
CanType actorType = CanType(globalActor);
502508
NominalTypeDecl *nominal = actorType->getNominalOrBoundGenericNominal();
@@ -517,7 +523,7 @@ void SILGenFunction::loadGlobalActor(Type globalActor) {
517523
actorType, /*isSuper*/ false, sharedInstanceDecl, PreparedArguments(),
518524
subs, AccessSemantics::Ordinary, instanceType, SGFContext());
519525
ManagedValue actorInstance = std::move(actorInstanceRV).getScalarValue();
520-
actor = actorInstance.borrow(*this, loc).getValue();
526+
return actorInstance.borrow(*this, loc).getValue();
521527
}
522528

523529
static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType,

0 commit comments

Comments
 (0)