Skip to content

Commit 9eb9df8

Browse files
authored
Merge pull request swiftlang#40910 from rjmccall/hop-to-generic-executor
Run non-actor-isolated async functions on the generic executor
2 parents 96231e2 + 716f4b9 commit 9eb9df8

19 files changed

+273
-153
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,12 +1974,16 @@ class SourceFileScope {
19741974

19751975
sgm.TopLevelSGF->prepareEpilog(None, true, moduleCleanupLoc);
19761976

1977-
// emitAsyncMainThreadStart will handle creating argc argv
1978-
// for the async case
1979-
if (!sf->isAsyncContext()) {
1977+
auto prologueLoc = RegularLocation::getModuleLocation();
1978+
prologueLoc.markAsPrologue();
1979+
if (sf->isAsyncContext()) {
1980+
// emitAsyncMainThreadStart will create argc and argv.
1981+
// Just set the main actor as the expected executor; we should
1982+
// already be running on it.
1983+
sgm.TopLevelSGF->ExpectedExecutor =
1984+
sgm.TopLevelSGF->emitMainExecutor(prologueLoc);
1985+
} else {
19801986
// Create the argc and argv arguments.
1981-
auto prologueLoc = RegularLocation::getModuleLocation();
1982-
prologueLoc.markAsPrologue();
19831987
auto entry = sgm.TopLevelSGF->B.getInsertionBB();
19841988
auto context = sgm.TopLevelSGF->getTypeExpansionContext();
19851989
auto paramTypeIter = sgm.TopLevelSGF->F.getConventions()

lib/SILGen/SILGenBridging.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1589,7 +1589,7 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
15891589
// A hop is only needed in the thunk if it is global-actor isolated.
15901590
// Native, instance-isolated async methods will hop in the prologue.
15911591
if (isolation && isolation->isGlobalActor()) {
1592-
emitHopToTargetActor(loc, *isolation, None);
1592+
emitPrologGlobalActorHop(loc, isolation->getGlobalActor());
15931593
}
15941594
}
15951595

lib/SILGen/SILGenConstructor.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -672,13 +672,17 @@ static void emitDefaultActorInitialization(
672672
void SILGenFunction::emitConstructorPrologActorHop(
673673
SILLocation loc,
674674
Optional<ActorIsolation> maybeIso) {
675-
if (!maybeIso)
676-
return;
677-
678-
if (auto executor = emitExecutor(loc, *maybeIso, None)) {
679-
ExpectedExecutor = *executor;
680-
B.createHopToExecutor(loc.asAutoGenerated(), *executor, /*mandatory*/ false);
675+
loc = loc.asAutoGenerated();
676+
if (maybeIso) {
677+
if (auto executor = emitExecutor(loc, *maybeIso, None)) {
678+
ExpectedExecutor = *executor;
679+
}
681680
}
681+
682+
if (!ExpectedExecutor)
683+
ExpectedExecutor = emitGenericExecutor(loc);
684+
685+
B.createHopToExecutor(loc, ExpectedExecutor, /*mandatory*/ false);
682686
}
683687

684688
// MARK: class constructor

lib/SILGen/SILGenFunction.cpp

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -923,36 +923,8 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) {
923923
jobResult = wrapCallArgs(jobResult, swiftJobRunFuncDecl, 0);
924924

925925
ModuleDecl * moduleDecl = entryPoint.getModuleContext();
926-
// Get main executor
927-
FuncDecl *getMainExecutorFuncDecl = SGM.getGetMainExecutor();
928-
if (!getMainExecutorFuncDecl) {
929-
// If it doesn't exist due to an SDK-compiler mismatch, we can conjure one
930-
// up instead of crashing:
931-
// @available(SwiftStdlib 5.1, *)
932-
// @_silgen_name("swift_task_getMainExecutor")
933-
// internal func _getMainExecutor() -> Builtin.Executor
934-
935-
ParameterList *emptyParams = ParameterList::createEmpty(ctx);
936-
getMainExecutorFuncDecl = FuncDecl::createImplicit(
937-
ctx, StaticSpellingKind::None,
938-
DeclName(
939-
ctx,
940-
DeclBaseName(ctx.getIdentifier("_getMainExecutor")),
941-
/*Arguments*/ emptyParams),
942-
{}, /*async*/ false, /*throws*/ false, {}, emptyParams,
943-
ctx.TheExecutorType,
944-
moduleDecl);
945-
getMainExecutorFuncDecl->getAttrs().add(
946-
new (ctx)
947-
SILGenNameAttr("swift_task_getMainExecutor", /*implicit*/ true));
948-
}
949926

950-
SILFunction *getMainExeutorSILFunc = SGM.getFunction(
951-
SILDeclRef(getMainExecutorFuncDecl, SILDeclRef::Kind::Func),
952-
NotForDefinition);
953-
SILValue getMainExeutorFunc =
954-
B.createFunctionRefFor(moduleLoc, getMainExeutorSILFunc);
955-
SILValue mainExecutor = B.createApply(moduleLoc, getMainExeutorFunc, {}, {});
927+
SILValue mainExecutor = emitMainExecutor(moduleLoc);
956928
mainExecutor = wrapCallArgs(mainExecutor, swiftJobRunFuncDecl, 1);
957929

958930
// Run first part synchronously

lib/SILGen/SILGenFunction.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,11 +874,22 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
874874
void emitConstructorPrologActorHop(SILLocation loc,
875875
Optional<ActorIsolation> actorIso);
876876

877+
/// Set the given global actor as the isolation for this function
878+
/// (generally a thunk) and hop to it.
879+
void emitPrologGlobalActorHop(SILLocation loc, Type globalActor);
880+
877881
/// Emit the executor for the given actor isolation.
878882
Optional<SILValue> emitExecutor(SILLocation loc,
879883
ActorIsolation isolation,
880884
Optional<ManagedValue> maybeSelf);
881885

886+
/// Emit the executor value that corresponds to the generic (concurrent)
887+
/// executor.
888+
SILValue emitGenericExecutor(SILLocation loc);
889+
890+
/// Emit the executor value that corresponds to the main actor.
891+
SILValue emitMainExecutor(SILLocation loc);
892+
882893
/// Emit a precondition check to ensure that the function is executing in
883894
/// the expected isolation context.
884895
void emitPreconditionCheckExpectedExecutor(

lib/SILGen/SILGenPoly.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2944,11 +2944,10 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
29442944

29452945
// If the input is synchronous and global-actor-qualified, and the
29462946
// output is asynchronous, hop to the executor expected by the input.
2947-
ExecutorBreadcrumb prevExecutor;
2947+
// Treat this thunk as if it were isolated to that global actor.
29482948
if (outputSubstType->isAsync() && !inputSubstType->isAsync()) {
29492949
if (Type globalActor = inputSubstType->getGlobalActor()) {
2950-
prevExecutor = SGF.emitHopToTargetActor(
2951-
loc, ActorIsolation::forGlobalActor(globalActor, false), None);
2950+
SGF.emitPrologGlobalActorHop(loc, globalActor);
29522951
}
29532952
}
29542953

@@ -2995,9 +2994,6 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,
29952994
// Reabstract the result.
29962995
SILValue outerResult = resultPlanner.execute(innerResult);
29972996

2998-
// If we hopped to the target's executor, then we need to hop back.
2999-
prevExecutor.emit(SGF, loc);
3000-
30012997
scope.pop();
30022998
SGF.B.createReturn(loc, outerResult);
30032999
}

lib/SILGen/SILGenProlog.cpp

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
552552
return false;
553553
};
554554

555-
// Initialize ExpectedExecutor if the function is an actor-isolated
555+
// Initialize ExpectedExecutor if the function is an async
556556
// function or closure.
557557
bool wantDataRaceChecks = getOptions().EnableActorDataRaceChecks &&
558558
!F.isAsync() &&
@@ -642,6 +642,13 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
642642
}
643643
}
644644
}
645+
646+
// In async functions, the generic executor is our expected executor
647+
// if we don't have any sort of isolation.
648+
if (!ExpectedExecutor && F.isAsync()) {
649+
ExpectedExecutor = emitGenericExecutor(
650+
RegularLocation::getAutoGeneratedLocation(F.getLocation()));
651+
}
645652

646653
// Jump to the expected executor.
647654
if (ExpectedExecutor) {
@@ -661,6 +668,54 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
661668
}
662669
}
663670

671+
SILValue SILGenFunction::emitMainExecutor(SILLocation loc) {
672+
// Get main executor
673+
FuncDecl *getMainExecutorFuncDecl = SGM.getGetMainExecutor();
674+
if (!getMainExecutorFuncDecl) {
675+
// If it doesn't exist due to an SDK-compiler mismatch, we can conjure one
676+
// up instead of crashing:
677+
// @available(SwiftStdlib 5.1, *)
678+
// @_silgen_name("swift_task_getMainExecutor")
679+
// internal func _getMainExecutor() -> Builtin.Executor
680+
auto &ctx = getASTContext();
681+
682+
ParameterList *emptyParams = ParameterList::createEmpty(ctx);
683+
getMainExecutorFuncDecl = FuncDecl::createImplicit(
684+
ctx, StaticSpellingKind::None,
685+
DeclName(
686+
ctx,
687+
DeclBaseName(ctx.getIdentifier("_getMainExecutor")),
688+
/*Arguments*/ emptyParams),
689+
{}, /*async*/ false, /*throws*/ false, {}, emptyParams,
690+
ctx.TheExecutorType,
691+
getModule().getSwiftModule());
692+
getMainExecutorFuncDecl->getAttrs().add(
693+
new (ctx)
694+
SILGenNameAttr("swift_task_getMainExecutor", /*implicit*/ true));
695+
}
696+
697+
auto fn = SGM.getFunction(
698+
SILDeclRef(getMainExecutorFuncDecl, SILDeclRef::Kind::Func),
699+
NotForDefinition);
700+
SILValue fnRef = B.createFunctionRefFor(loc, fn);
701+
return B.createApply(loc, fnRef, {}, {});
702+
}
703+
704+
SILValue SILGenFunction::emitGenericExecutor(SILLocation loc) {
705+
// The generic executor is encoded as the nil value of
706+
// Optional<Builtin.SerialExecutor>.
707+
auto ty = SILType::getOptionalType(
708+
SILType::getPrimitiveObjectType(
709+
getASTContext().TheExecutorType));
710+
return B.createOptionalNone(loc, ty);
711+
}
712+
713+
void SILGenFunction::emitPrologGlobalActorHop(SILLocation loc,
714+
Type globalActor) {
715+
ExpectedExecutor = emitLoadGlobalActorExecutor(globalActor);
716+
B.createHopToExecutor(loc, ExpectedExecutor, /*mandatory*/ false);
717+
}
718+
664719
SILValue SILGenFunction::emitLoadGlobalActorExecutor(Type globalActor) {
665720
CanType actorType = CanType(globalActor);
666721
NominalTypeDecl *nominal = actorType->getNominalOrBoundGenericNominal();
@@ -800,19 +855,8 @@ void ExecutorBreadcrumb::emit(SILGenFunction &SGF, SILLocation loc) {
800855
}
801856

802857
SILValue SILGenFunction::emitGetCurrentExecutor(SILLocation loc) {
803-
// If this is an actor method, then the actor is the only executor we should
804-
// be running on (if we aren't setting up for a cross-actor call).
805-
if (ExpectedExecutor)
806-
return ExpectedExecutor;
807-
808-
// Otherwise, we'll have to ask the current task what executor it's running
809-
// on.
810-
auto &ctx = getASTContext();
811-
return B.createBuiltin(
812-
loc,
813-
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::GetCurrentExecutor)),
814-
getLoweredType(OptionalType::get(ctx.TheExecutorType)),
815-
SubstitutionMap(), { });
858+
assert(ExpectedExecutor && "prolog failed to set up expected executor?");
859+
return ExpectedExecutor;
816860
}
817861

818862
static void emitIndirectResultParameters(SILGenFunction &SGF,

test/Concurrency/Runtime/class_resilience.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
// REQUIRES: concurrency_runtime
1616
// UNSUPPORTED: back_deployment_runtime
1717

18-
// XFAIL: windows
19-
2018
import StdlibUnittest
2119
import resilient_class
2220

test/Concurrency/Runtime/protocol_resilience.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@
1515
// REQUIRES: concurrency_runtime
1616
// UNSUPPORTED: back_deployment_runtime
1717

18-
// XFAIL: windows
19-
// UNSUPPORTED: linux
20-
// UNSUPPORTED: openbsd
21-
2218
import StdlibUnittest
2319
import resilient_protocol
2420

test/Concurrency/cross_module_let_sil.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import OtherActors
77

88
// CHECK-LABEL: sil hidden [ossa] @$s4test6check1ySi11OtherActors0C11ModuleActorCYaF : $@convention(thin) @async (@guaranteed OtherModuleActor) -> Int {
99
// CHECK: bb0([[SELF:%[0-9]+]] : @guaranteed $OtherModuleActor):
10+
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
11+
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
1012
// CHECK: [[REF:%[0-9]+]] = ref_element_addr [[SELF]] : $OtherModuleActor, #OtherModuleActor.a
11-
// CHECK: [[OLD:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional<Builtin.Executor>
1213
// CHECK: hop_to_executor [[SELF]] : $OtherModuleActor
1314
// CHECK-NEXT: load [trivial] [[REF]]
14-
// CHECK-NEXT: hop_to_executor [[OLD]] : $Optional<Builtin.Executor>
15+
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
1516
// CHECK: } // end sil function '$s4test6check1ySi11OtherActors0C11ModuleActorCYaF'
1617
func check1(_ actor: OtherModuleActor) async -> Int {
1718
return await actor.a
@@ -21,20 +22,25 @@ func check2(_ actor: isolated OtherModuleActor) -> Int {
2122
return actor.a
2223
}
2324

25+
// CHECK-LABEL: sil hidden [ossa] @$s4test6check3ySi11OtherActors0C11ModuleActorCYaF : $@convention(thin) @async (@guaranteed OtherModuleActor) -> Int {
26+
// CHECK: bb0([[SELF:%[0-9]+]] : @guaranteed $OtherModuleActor):
27+
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
28+
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
2429
func check3(_ actor: OtherModuleActor) async -> Int {
2530
return actor.b
2631
}
2732

2833
// CHECK-LABEL: sil hidden [ossa] @$s4test6check4y11OtherActors17SomeSendableClassCSgAC0C11ModuleActorCSgYaF : $@convention(thin) @async (@guaranteed Optional<OtherModuleActor>) -> @owned Optional<SomeSendableClass> {
2934
// CHECK: bb0({{%[0-9]+}} : @guaranteed $Optional<OtherModuleActor>):
35+
// CHECK: [[GENERIC_EXEC:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none
36+
// CHECK-NEXT: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
3037
// CHECK: switch_enum {{%[0-9]+}} : $Optional<OtherModuleActor>, case #Optional.some!enumelt: [[SOME:bb[0-9]+]], case #Optional.none!enumelt: {{bb[0-9]+}}
3138

3239
// CHECK: [[SOME]]({{%[0-9]+}} : @owned $OtherModuleActor):
3340
// CHECK: [[REF:%[0-9]+]] = ref_element_addr {{%[0-9]+}} : $OtherModuleActor, #OtherModuleActor.d
34-
// CHECK: [[OLD:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional<Builtin.Executor>
3541
// CHECK: hop_to_executor {{%[0-9]+}} : $OtherModuleActor
3642
// CHECK-NEXT: load [copy] [[REF]]
37-
// CHECK: hop_to_executor [[OLD]] : $Optional<Builtin.Executor>
43+
// CHECK: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
3844
// CHECK: } // end sil function '$s4test6check4y11OtherActors17SomeSendableClassCSgAC0C11ModuleActorCSgYaF'
3945
func check4(_ actor: OtherModuleActor?) async -> SomeSendableClass? {
4046
return await actor?.d

0 commit comments

Comments
 (0)