Skip to content

Run non-actor-isolated async functions on the generic executor #40910

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1974,12 +1974,16 @@ class SourceFileScope {

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

// emitAsyncMainThreadStart will handle creating argc argv
// for the async case
if (!sf->isAsyncContext()) {
auto prologueLoc = RegularLocation::getModuleLocation();
prologueLoc.markAsPrologue();
if (sf->isAsyncContext()) {
// emitAsyncMainThreadStart will create argc and argv.
// Just set the main actor as the expected executor; we should
// already be running on it.
sgm.TopLevelSGF->ExpectedExecutor =
sgm.TopLevelSGF->emitMainExecutor(prologueLoc);
} else {
// Create the argc and argv arguments.
auto prologueLoc = RegularLocation::getModuleLocation();
prologueLoc.markAsPrologue();
auto entry = sgm.TopLevelSGF->B.getInsertionBB();
auto context = sgm.TopLevelSGF->getTypeExpansionContext();
auto paramTypeIter = sgm.TopLevelSGF->F.getConventions()
Expand Down
2 changes: 1 addition & 1 deletion lib/SILGen/SILGenBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1589,7 +1589,7 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
// A hop is only needed in the thunk if it is global-actor isolated.
// Native, instance-isolated async methods will hop in the prologue.
if (isolation && isolation->isGlobalActor()) {
emitHopToTargetActor(loc, *isolation, None);
emitPrologGlobalActorHop(loc, isolation->getGlobalActor());
}
}

Expand Down
16 changes: 10 additions & 6 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,13 +672,17 @@ static void emitDefaultActorInitialization(
void SILGenFunction::emitConstructorPrologActorHop(
SILLocation loc,
Optional<ActorIsolation> maybeIso) {
if (!maybeIso)
return;

if (auto executor = emitExecutor(loc, *maybeIso, None)) {
ExpectedExecutor = *executor;
B.createHopToExecutor(loc.asAutoGenerated(), *executor, /*mandatory*/ false);
loc = loc.asAutoGenerated();
if (maybeIso) {
if (auto executor = emitExecutor(loc, *maybeIso, None)) {
ExpectedExecutor = *executor;
}
}

if (!ExpectedExecutor)
ExpectedExecutor = emitGenericExecutor(loc);

B.createHopToExecutor(loc, ExpectedExecutor, /*mandatory*/ false);
}

// MARK: class constructor
Expand Down
30 changes: 1 addition & 29 deletions lib/SILGen/SILGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,36 +923,8 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) {
jobResult = wrapCallArgs(jobResult, swiftJobRunFuncDecl, 0);

ModuleDecl * moduleDecl = entryPoint.getModuleContext();
// Get main executor
FuncDecl *getMainExecutorFuncDecl = SGM.getGetMainExecutor();
if (!getMainExecutorFuncDecl) {
// If it doesn't exist due to an SDK-compiler mismatch, we can conjure one
// up instead of crashing:
// @available(SwiftStdlib 5.1, *)
// @_silgen_name("swift_task_getMainExecutor")
// internal func _getMainExecutor() -> Builtin.Executor

ParameterList *emptyParams = ParameterList::createEmpty(ctx);
getMainExecutorFuncDecl = FuncDecl::createImplicit(
ctx, StaticSpellingKind::None,
DeclName(
ctx,
DeclBaseName(ctx.getIdentifier("_getMainExecutor")),
/*Arguments*/ emptyParams),
{}, /*async*/ false, /*throws*/ false, {}, emptyParams,
ctx.TheExecutorType,
moduleDecl);
getMainExecutorFuncDecl->getAttrs().add(
new (ctx)
SILGenNameAttr("swift_task_getMainExecutor", /*implicit*/ true));
}

SILFunction *getMainExeutorSILFunc = SGM.getFunction(
SILDeclRef(getMainExecutorFuncDecl, SILDeclRef::Kind::Func),
NotForDefinition);
SILValue getMainExeutorFunc =
B.createFunctionRefFor(moduleLoc, getMainExeutorSILFunc);
SILValue mainExecutor = B.createApply(moduleLoc, getMainExeutorFunc, {}, {});
SILValue mainExecutor = emitMainExecutor(moduleLoc);
mainExecutor = wrapCallArgs(mainExecutor, swiftJobRunFuncDecl, 1);

// Run first part synchronously
Expand Down
11 changes: 11 additions & 0 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -874,11 +874,22 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
void emitConstructorPrologActorHop(SILLocation loc,
Optional<ActorIsolation> actorIso);

/// Set the given global actor as the isolation for this function
/// (generally a thunk) and hop to it.
void emitPrologGlobalActorHop(SILLocation loc, Type globalActor);

/// Emit the executor for the given actor isolation.
Optional<SILValue> emitExecutor(SILLocation loc,
ActorIsolation isolation,
Optional<ManagedValue> maybeSelf);

/// Emit the executor value that corresponds to the generic (concurrent)
/// executor.
SILValue emitGenericExecutor(SILLocation loc);

/// Emit the executor value that corresponds to the main actor.
SILValue emitMainExecutor(SILLocation loc);

/// Emit a precondition check to ensure that the function is executing in
/// the expected isolation context.
void emitPreconditionCheckExpectedExecutor(
Expand Down
8 changes: 2 additions & 6 deletions lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2944,11 +2944,10 @@ static void buildThunkBody(SILGenFunction &SGF, SILLocation loc,

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

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

// If we hopped to the target's executor, then we need to hop back.
prevExecutor.emit(SGF, loc);

scope.pop();
SGF.B.createReturn(loc, outerResult);
}
Expand Down
72 changes: 58 additions & 14 deletions lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
return false;
};

// Initialize ExpectedExecutor if the function is an actor-isolated
// Initialize ExpectedExecutor if the function is an async
// function or closure.
bool wantDataRaceChecks = getOptions().EnableActorDataRaceChecks &&
!F.isAsync() &&
Expand Down Expand Up @@ -642,6 +642,13 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
}
}
}

// In async functions, the generic executor is our expected executor
// if we don't have any sort of isolation.
if (!ExpectedExecutor && F.isAsync()) {
ExpectedExecutor = emitGenericExecutor(
RegularLocation::getAutoGeneratedLocation(F.getLocation()));
}

// Jump to the expected executor.
if (ExpectedExecutor) {
Expand All @@ -661,6 +668,54 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
}
}

SILValue SILGenFunction::emitMainExecutor(SILLocation loc) {
// Get main executor
FuncDecl *getMainExecutorFuncDecl = SGM.getGetMainExecutor();
if (!getMainExecutorFuncDecl) {
// If it doesn't exist due to an SDK-compiler mismatch, we can conjure one
// up instead of crashing:
// @available(SwiftStdlib 5.1, *)
// @_silgen_name("swift_task_getMainExecutor")
// internal func _getMainExecutor() -> Builtin.Executor
auto &ctx = getASTContext();

ParameterList *emptyParams = ParameterList::createEmpty(ctx);
getMainExecutorFuncDecl = FuncDecl::createImplicit(
ctx, StaticSpellingKind::None,
DeclName(
ctx,
DeclBaseName(ctx.getIdentifier("_getMainExecutor")),
/*Arguments*/ emptyParams),
{}, /*async*/ false, /*throws*/ false, {}, emptyParams,
ctx.TheExecutorType,
getModule().getSwiftModule());
getMainExecutorFuncDecl->getAttrs().add(
new (ctx)
SILGenNameAttr("swift_task_getMainExecutor", /*implicit*/ true));
}

auto fn = SGM.getFunction(
SILDeclRef(getMainExecutorFuncDecl, SILDeclRef::Kind::Func),
NotForDefinition);
SILValue fnRef = B.createFunctionRefFor(loc, fn);
return B.createApply(loc, fnRef, {}, {});
}

SILValue SILGenFunction::emitGenericExecutor(SILLocation loc) {
// The generic executor is encoded as the nil value of
// Optional<Builtin.SerialExecutor>.
auto ty = SILType::getOptionalType(
SILType::getPrimitiveObjectType(
getASTContext().TheExecutorType));
return B.createOptionalNone(loc, ty);
}

void SILGenFunction::emitPrologGlobalActorHop(SILLocation loc,
Type globalActor) {
ExpectedExecutor = emitLoadGlobalActorExecutor(globalActor);
B.createHopToExecutor(loc, ExpectedExecutor, /*mandatory*/ false);
}

SILValue SILGenFunction::emitLoadGlobalActorExecutor(Type globalActor) {
CanType actorType = CanType(globalActor);
NominalTypeDecl *nominal = actorType->getNominalOrBoundGenericNominal();
Expand Down Expand Up @@ -800,19 +855,8 @@ void ExecutorBreadcrumb::emit(SILGenFunction &SGF, SILLocation loc) {
}

SILValue SILGenFunction::emitGetCurrentExecutor(SILLocation loc) {
// If this is an actor method, then the actor is the only executor we should
// be running on (if we aren't setting up for a cross-actor call).
if (ExpectedExecutor)
return ExpectedExecutor;

// Otherwise, we'll have to ask the current task what executor it's running
// on.
auto &ctx = getASTContext();
return B.createBuiltin(
loc,
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::GetCurrentExecutor)),
getLoweredType(OptionalType::get(ctx.TheExecutorType)),
SubstitutionMap(), { });
assert(ExpectedExecutor && "prolog failed to set up expected executor?");
return ExpectedExecutor;
}

static void emitIndirectResultParameters(SILGenFunction &SGF,
Expand Down
2 changes: 0 additions & 2 deletions test/Concurrency/Runtime/class_resilience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime

// XFAIL: windows

import StdlibUnittest
import resilient_class

Expand Down
4 changes: 0 additions & 4 deletions test/Concurrency/Runtime/protocol_resilience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime

// XFAIL: windows
// UNSUPPORTED: linux
// UNSUPPORTED: openbsd

import StdlibUnittest
import resilient_protocol

Expand Down
14 changes: 10 additions & 4 deletions test/Concurrency/cross_module_let_sil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import OtherActors

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

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

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

// CHECK: [[SOME]]({{%[0-9]+}} : @owned $OtherModuleActor):
// CHECK: [[REF:%[0-9]+]] = ref_element_addr {{%[0-9]+}} : $OtherModuleActor, #OtherModuleActor.d
// CHECK: [[OLD:%[0-9]+]] = builtin "getCurrentExecutor"() : $Optional<Builtin.Executor>
// CHECK: hop_to_executor {{%[0-9]+}} : $OtherModuleActor
// CHECK-NEXT: load [copy] [[REF]]
// CHECK: hop_to_executor [[OLD]] : $Optional<Builtin.Executor>
// CHECK: hop_to_executor [[GENERIC_EXEC]] : $Optional<Builtin.Executor>
// CHECK: } // end sil function '$s4test6check4y11OtherActors17SomeSendableClassCSgAC0C11ModuleActorCSgYaF'
func check4(_ actor: OtherModuleActor?) async -> SomeSendableClass? {
return await actor?.d
Expand Down
3 changes: 0 additions & 3 deletions test/Concurrency/throwing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
// REQUIRES: concurrency_runtime
// UNSUPPORTED: back_deployment_runtime

// SR-15252
// XFAIL: OS=windows-msvc

import _Concurrency
import StdlibUnittest

Expand Down
2 changes: 1 addition & 1 deletion test/DebugInfo/async-lifetime-extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Test that lifetime extension preserves a dbg.declare for "n" in the resume
// funclet.

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