Skip to content

[concurrency] SILGen: emit hop_to_executor instructions #34540

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 2 commits into from
Nov 4, 2020
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
3 changes: 3 additions & 0 deletions include/swift/AST/ActorIsolation.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ class ActorIsolation {
}
};

/// Determine how the given value declaration is isolated.
ActorIsolation getActorIsolation(ValueDecl *value);

void simple_display(llvm::raw_ostream &out, const ActorIsolation &state);

} // end namespace swift
Expand Down
2 changes: 1 addition & 1 deletion lib/SIL/IR/OperandOwnership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ INTERIOR_POINTER_PROJECTION(RefTailAddr)
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialValue)
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialBoxValue)
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, OpenExistentialBox)
CONSTANT_OWNERSHIP_INST(Guaranteed, MustBeLive, HopToExecutor)
CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, AutoreleaseValue)
CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocBox)
CONSTANT_OWNERSHIP_INST(Owned, MustBeInvalidated, DeallocExistentialBox)
Expand All @@ -193,7 +194,6 @@ CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndAccess)
CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndApply)
CONSTANT_OWNERSHIP_INST(None, MustBeLive, EndUnpairedAccess)
CONSTANT_OWNERSHIP_INST(None, MustBeLive, GetAsyncContinuationAddr)
CONSTANT_OWNERSHIP_INST(None, MustBeLive, HopToExecutor)
CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexAddr)
CONSTANT_OWNERSHIP_INST(None, MustBeLive, IndexRawPointer)
CONSTANT_OWNERSHIP_INST(None, MustBeLive, InitBlockStorageHeader)
Expand Down
3 changes: 3 additions & 0 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4366,6 +4366,9 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
return rawDirectResults[0];
}();

if (substFnType->isAsync())
emitHopToCurrentExecutor(loc);

// Pop the argument scope.
argScope.pop();

Expand Down
10 changes: 10 additions & 0 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
/// The metatype argument to an allocating constructor, if we're emitting one.
SILValue AllocatorMetatype;

/// If set, the current function is an async function which is isolated to
/// this actor.
/// If set, hop_to_executor instructions must be inserted at the begin of the
/// function and after all suspension points.
SILValue actor;

/// True if 'return' without an operand or falling off the end of the current
/// function is valid.
bool allowsVoidReturn() const { return ReturnDest.getBlock()->args_empty(); }
Expand Down Expand Up @@ -751,6 +757,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
CanAnyFunctionType outputSubstType,
bool baseLessVisibleThanDerived);

/// If the current function is actor isolated, insert a hop_to_executor
/// instruction.
void emitHopToCurrentExecutor(SILLocation loc);

//===--------------------------------------------------------------------===//
// Control flow
//===--------------------------------------------------------------------===//
Expand Down
9 changes: 9 additions & 0 deletions lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4480,6 +4480,15 @@ SILGenFunction::emitVTableThunk(SILDeclRef base,
F.verify();
}

//===----------------------------------------------------------------------===//
// Concurrency
//===----------------------------------------------------------------------===//

void SILGenFunction::emitHopToCurrentExecutor(SILLocation loc) {
if (actor)
B.createHopToExecutor(loc, actor);
}

//===----------------------------------------------------------------------===//
// Protocol witnesses
//===----------------------------------------------------------------------===//
Expand Down
43 changes: 43 additions & 0 deletions lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "Initialization.h"
#include "ManagedValue.h"
#include "Scope.h"
#include "ArgumentSource.h"
#include "swift/SIL/SILArgument.h"
#include "swift/AST/CanTypeVisitor.h"
#include "swift/AST/GenericEnvironment.h"
Expand Down Expand Up @@ -449,6 +450,48 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
}
}
}

if (auto *funcDecl =
dyn_cast_or_null<AbstractFunctionDecl>(FunctionDC->getAsDecl())) {
auto actorIsolation = getActorIsolation(funcDecl);
switch (actorIsolation.getKind()) {
case ActorIsolation::Unspecified:
case ActorIsolation::Independent:
case ActorIsolation::IndependentUnsafe:
break;
case ActorIsolation::ActorInstance: {
assert(selfParam && "no self parameter for ActorInstance isolation");
ManagedValue selfArg = ManagedValue::forUnmanaged(F.getSelfArgument());
ManagedValue borrowedSelf = selfArg.borrow(*this, F.getLocation());
actor = borrowedSelf.getValue();
break;
}
case ActorIsolation::GlobalActor: {
CanType actorType = CanType(actorIsolation.getGlobalActor());
NominalTypeDecl *nominal = actorType->getNominalOrBoundGenericNominal();
VarDecl *sharedInstanceDecl = nominal->getGlobalActorInstance();
assert(sharedInstanceDecl && "no shared actor field in global actor");
SubstitutionMap subs =
actorType->getContextSubstitutionMap(SGM.SwiftModule, nominal);
SILLocation loc = F.getLocation();
Type instanceType =
actorType->getTypeOfMember(SGM.SwiftModule, sharedInstanceDecl);

ManagedValue actorMetaType =
ManagedValue::forUnmanaged(B.createMetatype(loc,
SILType::getPrimitiveObjectType(
CanMetatypeType::get(actorType, MetatypeRepresentation::Thin))));

RValue actorInstanceRV = emitRValueForStorageLoad(loc, actorMetaType,
actorType, /*isSuper*/ false, sharedInstanceDecl, PreparedArguments(),
subs, AccessSemantics::Ordinary, instanceType, SGFContext());
ManagedValue actorInstance = std::move(actorInstanceRV).getScalarValue();
actor = actorInstance.borrow(*this, loc).getValue();
break;
}
}
}
emitHopToCurrentExecutor(F.getLocation());
}

static void emitIndirectResultParameters(SILGenFunction &SGF, Type resultType,
Expand Down
3 changes: 3 additions & 0 deletions lib/SILGen/SILGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,9 @@ SILGenFunction::getTryApplyErrorDest(SILLocation loc,
assert(B.hasValidInsertionPoint() && B.insertingAtEndOfBlock());
SILGenSavedInsertionPoint savedIP(*this, destBB, FunctionSection::Postmatter);

if (fnTy->isAsync())
emitHopToCurrentExecutor(loc);

// If we're suppressing error paths, just wrap it up as unreachable
// and return.
if (suppressErrorPath) {
Expand Down
3 changes: 0 additions & 3 deletions lib/Sema/TypeCheckConcurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ void addAsyncNotes(FuncDecl *func);
/// Check actor isolation rules.
void checkActorIsolation(const Expr *expr, const DeclContext *dc);

/// Determine how the given value declaration is isolated.
ActorIsolation getActorIsolation(ValueDecl *value);

/// The isolation restriction in effect for a given declaration that is
/// referenced from source.
class ActorIsolationRestriction {
Expand Down
88 changes: 88 additions & 0 deletions test/SILGen/hop_to_executor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// RUN: %target-swift-frontend -emit-silgen %s -module-name test -swift-version 5 -enable-experimental-concurrency | %FileCheck %s
// REQUIRES: concurrency


actor class MyActor {

private var p: Int

// CHECK-LABEL: sil hidden [ossa] @$s4test7MyActorC6calleeyySiYF : $@convention(method) @async (Int, @guaranteed MyActor) -> () {
// CHECK-NOT: hop_to_executor
// CHECK: } // end sil function '$s4test7MyActorC6calleeyySiYF'
@actorIndependent
func callee(_ x: Int) async {
print(x)
}

// CHECK-LABEL: sil hidden [ossa] @$s4test7MyActorC14throwingCalleeyySiYKF : $@convention(method) @async (Int, @guaranteed MyActor) -> @error Error {
// CHECK-NOT: hop_to_executor
// CHECK: } // end sil function '$s4test7MyActorC14throwingCalleeyySiYKF'
@actorIndependent
func throwingCallee(_ x: Int) async throws {
print(x)
}

// CHECK-LABEL: sil hidden [ossa] @$s4test7MyActorC0A13AsyncFunctionyyYKF : $@convention(method) @async (@guaranteed MyActor) -> @error Error {
// CHECK: hop_to_executor %0 : $MyActor
// CHECK: = apply {{.*}} : $@convention(method) @async (Int, @guaranteed MyActor) -> ()
// CHECK-NEXT: hop_to_executor %0 : $MyActor
// CHECK: try_apply {{.*}}, normal bb1, error bb2
// CHECK: bb1({{.*}}):
// CHECK-NEXT: hop_to_executor %0 : $MyActor
// CHECK: bb2({{.*}}):
// CHECK-NEXT: hop_to_executor %0 : $MyActor
// CHECK: } // end sil function '$s4test7MyActorC0A13AsyncFunctionyyYKF'
func testAsyncFunction() async throws {
await callee(p)
try await throwingCallee(p)
}

// CHECK-LABEL: sil hidden [ossa] @$s4test7MyActorC0A22ConsumingAsyncFunctionyyYF : $@convention(method) @async (@owned MyActor) -> () {
// CHECK: [[BORROWED_SELF:%[0-9]+]] = begin_borrow %0 : $MyActor
// CHECK: hop_to_executor [[BORROWED_SELF]] : $MyActor
// CHECK: = apply {{.*}} : $@convention(method) @async (Int, @guaranteed MyActor) -> ()
// CHECK-NEXT: hop_to_executor [[BORROWED_SELF]] : $MyActor
// CHECK: } // end sil function '$s4test7MyActorC0A22ConsumingAsyncFunctionyyYF'
__consuming func testConsumingAsyncFunction() async {
await callee(p)
}

init() {
p = 27
}
}

@globalActor
struct GlobalActor {
static var shared: MyActor = MyActor()
}

// CHECK-LABEL: sil hidden [ossa] @$s4test0A11GlobalActoryyYF : $@convention(thin) () -> () {
// CHECK: [[F:%[0-9]+]] = function_ref @$s4test11GlobalActorV6sharedAA02MyC0Cvau : $@convention(thin) () -> Builtin.RawPointer
// CHECK: [[P:%[0-9]+]] = apply [[F]]() : $@convention(thin) () -> Builtin.RawPointer
// CHECK: [[A:%[0-9]+]] = pointer_to_address %2 : $Builtin.RawPointer to [strict] $*MyActor
// CHECK: [[ACC:%[0-9]+]] = begin_access [read] [dynamic] [[A]] : $*MyActor
// CHECK: [[L:%[0-9]+]] = load [copy] [[ACC]] : $*MyActor
// CHECK: [[B:%[0-9]+]] = begin_borrow [[L]] : $MyActor
// CHECK: hop_to_executor [[B]] : $MyActor
// CHECK: } // end sil function '$s4test0A11GlobalActoryyYF'
@GlobalActor
func testGlobalActor() async {
}

@globalActor
struct GenericGlobalActorWithGetter<T> {
static var shared: MyActor { return MyActor() }
}

// CHECK-LABEL: sil hidden [ossa] @$s4test0A28GenericGlobalActorWithGetteryyYF : $@convention(thin) () -> () {
// CHECK: [[MT:%[0-9]+]] = metatype $@thin GenericGlobalActorWithGetter<Int>.Type
// CHECK: [[F:%[0-9]+]] = function_ref @$s4test28GenericGlobalActorWithGetterV6sharedAA02MyD0CvgZ : $@convention(method) <τ_0_0> (@thin GenericGlobalActorWithGetter<τ_0_0>.Type) -> @owned MyActor
// CHECK: [[A:%[0-9]+]] = apply [[F]]<Int>([[MT]]) : $@convention(method) <τ_0_0> (@thin GenericGlobalActorWithGetter<τ_0_0>.Type) -> @owned MyActor
// CHECK: [[B:%[0-9]+]] = begin_borrow [[A]] : $MyActor
// CHECK: hop_to_executor [[B]] : $MyActor
// CHECK: } // end sil function '$s4test0A28GenericGlobalActorWithGetteryyYF'
@GenericGlobalActorWithGetter<Int>
func testGenericGlobalActorWithGetter() async {
}