Skip to content

Commit 716f4b9

Browse files
committed
Hop to the generic executor in non-actor-isolated async functions.
Async functions are now expected to set ExpectedExecutor in their prologue (and, generally, immediately hop to it). I updated the prologue code for a bunch of function emission, most of which was uninteresting. Top-level code was not returning to the main executor, which is now fixed; fortunately, we weren't assuming that we were on the main executor yet. We had some code that only kicked in when an ExpectedExecutor wasn't set which made us capture the current executor before a hop and then return to it later. This code has been removed; there's no situation in which save-and-return is the semantically correct thing to do given the possibility of hop optimization. I suspect it could also have led to crashes if the current executor is being kept alive only because it's currently running code. If we ever add async functions that are supposed to inherit their caller's executor, we should have the caller pass the right executor down to it. This is the first half of SE-0338; the second, sendability enforcement, is much more complicated, and Doug has volunteered to do it. Fixes rdar://79284465, as well as some tests that were XFAILed on Windows.
1 parent ac875d0 commit 716f4b9

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

test/Concurrency/throwing.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
// REQUIRES: concurrency_runtime
77
// UNSUPPORTED: back_deployment_runtime
88

9-
// SR-15252
10-
// XFAIL: OS=windows-msvc
11-
129
import _Concurrency
1310
import StdlibUnittest
1411

test/DebugInfo/async-lifetime-extension.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// Test that lifetime extension preserves a dbg.declare for "n" in the resume
77
// funclet.
88

9-
// CHECK-LABEL: define {{.*}} void @"$s1a4fiboyS2iYaFTQ0_"
9+
// CHECK-LABEL: define {{.*}} void @"$s1a4fiboyS2iYaFTY0_"
1010
// CHECK-NEXT: entryresume.0:
1111
// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[R:[0-9]+]], {{.*}}!DIExpression(DW_OP
1212
// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[N:[0-9]+]], {{.*}}!DIExpression(DW_OP

0 commit comments

Comments
 (0)