Skip to content

Commit cfa07b6

Browse files
authored
Merge pull request swiftlang#36477 from jckarter/executor-hop-back
Concurrency: Hop back to the previous executor after actor calls.
2 parents b452919 + b9ee090 commit cfa07b6

20 files changed

+246
-60
lines changed

include/swift/AST/Builtins.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,13 @@ BUILTIN_MISC_OPERATION(DestroyDefaultActor, "destroyDefaultActor", "", Special)
736736
BUILTIN_MISC_OPERATION(Id, Name, Attrs, Overload)
737737
#endif
738738

739+
// getCurrentExecutor: () async -> Builtin.Word
740+
//
741+
// Retrieve the ExecutorRef on which the current asynchronous
742+
// function is executing.
743+
// Does not retain an actor executor.
744+
BUILTIN_MISC_OPERATION_WITH_SILGEN(GetCurrentExecutor, "getCurrentExecutor", "n", Special)
745+
739746
// getCurrentAsyncTask: () -> Builtin.NativeObject
740747
//
741748
// Retrieve the pointer to the task in which the current asynchronous

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,14 @@ FUNCTION(TaskSwitchFunc,
15811581
ARGS(SwiftContextPtrTy, Int8PtrTy, SwiftExecutorPtrTy),
15821582
ATTRS(NoUnwind))
15831583

1584+
// ExecutorRef swift_task_getCurrentExecutor();
1585+
FUNCTION(TaskGetCurrentExecutor,
1586+
swift_task_getCurrentExecutor, SwiftCC,
1587+
ConcurrencyAvailability,
1588+
RETURNS(IntPtrTy),
1589+
ARGS(),
1590+
ATTRS(NoUnwind, ArgMemOnly))
1591+
15841592
// void swift_defaultActor_initialize(DefaultActor *actor);
15851593
FUNCTION(DefaultActorInitialize,
15861594
swift_defaultActor_initialize, SwiftCC,

lib/AST/Builtins.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,13 @@ static ValueDecl *getGetCurrentAsyncTask(ASTContext &ctx, Identifier id) {
13421342
return getBuiltinFunction(id, { }, ctx.TheNativeObjectType);
13431343
}
13441344

1345+
static ValueDecl *getGetCurrentExecutor(ASTContext &ctx, Identifier id) {
1346+
BuiltinFunctionBuilder builder(ctx);
1347+
builder.setResult(makeConcrete(BuiltinIntegerType::getWordType(ctx)));
1348+
builder.setAsync();
1349+
return builder.build(id);
1350+
}
1351+
13451352
static ValueDecl *getCancelAsyncTask(ASTContext &ctx, Identifier id) {
13461353
return getBuiltinFunction(
13471354
id, { ctx.TheNativeObjectType }, ctx.TheEmptyTupleType);
@@ -2558,6 +2565,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
25582565
case BuiltinValueKind::GetCurrentAsyncTask:
25592566
return getGetCurrentAsyncTask(Context, Id);
25602567

2568+
case BuiltinValueKind::GetCurrentExecutor:
2569+
return getGetCurrentExecutor(Context, Id);
2570+
25612571
case BuiltinValueKind::CancelAsyncTask:
25622572
return getCancelAsyncTask(Context, Id);
25632573

lib/IRGen/GenBuiltin.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
219219
return;
220220
}
221221

222+
// getCurrentActor has no arguments.
223+
if (Builtin.ID == BuiltinValueKind::GetCurrentExecutor) {
224+
auto *call = IGF.Builder.CreateCall(IGF.IGM.getTaskGetCurrentExecutorFn(),
225+
{});
226+
call->setDoesNotThrow();
227+
call->setCallingConv(IGF.IGM.SwiftCC);
228+
out.add(call);
229+
return;
230+
}
231+
222232
// Everything else cares about the (rvalue) argument.
223233

224234
if (Builtin.ID == BuiltinValueKind::CancelAsyncTask) {

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ BUILTIN_OPERAND_OWNERSHIP(TrivialUse, AutoDiffCreateLinearMapContext)
798798
"Builtin should never be visited! E.x.: It may not have arguments"); \
799799
}
800800
SHOULD_NEVER_VISIT_BUILTIN(GetCurrentAsyncTask)
801+
SHOULD_NEVER_VISIT_BUILTIN(GetCurrentExecutor)
801802
#undef SHOULD_NEVER_VISIT_BUILTIN
802803

803804
// Builtins that should be lowered to SIL instructions so we should never see

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, DestroyDefaultActor)
548548
CONSTANT_OWNERSHIP_BUILTIN(Owned, AutoDiffCreateLinearMapContext)
549549
CONSTANT_OWNERSHIP_BUILTIN(None, AutoDiffProjectTopLevelSubcontext)
550550
CONSTANT_OWNERSHIP_BUILTIN(None, AutoDiffAllocateSubcontext)
551+
CONSTANT_OWNERSHIP_BUILTIN(None, GetCurrentExecutor)
551552

552553
#undef CONSTANT_OWNERSHIP_BUILTIN
553554

lib/SILGen/ExecutorBreadcrumb.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===--- ExecutorBreadcrumb.h - executor hop tracking for SILGen ----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
namespace swift {
14+
namespace Lowering {
15+
16+
/// Represents the information necessary to return to a caller's own
17+
/// active executor after making a hop to an actor for actor-isolated calls.
18+
class ExecutorBreadcrumb {
19+
SILValue Executor;
20+
21+
public:
22+
// An empty breadcrumb, indicating no hop back is necessary.
23+
ExecutorBreadcrumb() : Executor() {}
24+
25+
// A breadcrumb representing the need to hop back to the executor
26+
// represented by the given value.
27+
explicit ExecutorBreadcrumb(SILValue executor)
28+
: Executor(executor) {}
29+
30+
// Emits the hop back sequence, if any, necessary to get back to
31+
// the executor represented by this breadcrumb.
32+
void emit(SILGenFunction &SGF, SILLocation loc);
33+
};
34+
35+
} // namespace Lowering
36+
} // namespace swift

lib/SILGen/SILGenApply.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "ArgumentSource.h"
1515
#include "Callee.h"
1616
#include "Conversion.h"
17+
#include "ExecutorBreadcrumb.h"
1718
#include "FormalEvaluation.h"
1819
#include "Initialization.h"
1920
#include "LValue.h"
@@ -1707,7 +1708,8 @@ static void emitRawApply(SILGenFunction &SGF,
17071708
CanSILFunctionType substFnType,
17081709
ApplyOptions options,
17091710
ArrayRef<SILValue> indirectResultAddrs,
1710-
SmallVectorImpl<SILValue> &rawResults) {
1711+
SmallVectorImpl<SILValue> &rawResults,
1712+
ExecutorBreadcrumb prevExecutor) {
17111713
SILFunctionConventions substFnConv(substFnType, SGF.SGM.M);
17121714
// Get the callee value.
17131715
bool isConsumed = substFnType->isCalleeConsumed();
@@ -1783,7 +1785,7 @@ static void emitRawApply(SILGenFunction &SGF,
17831785
rawResults.push_back(result);
17841786

17851787
SILBasicBlock *errorBB =
1786-
SGF.getTryApplyErrorDest(loc, substFnType,
1788+
SGF.getTryApplyErrorDest(loc, substFnType, prevExecutor,
17871789
substFnType->getErrorResult(),
17881790
options.contains(ApplyFlags::DoesNotThrow));
17891791

@@ -3815,7 +3817,7 @@ SILGenFunction::emitBeginApply(SILLocation loc, ManagedValue fn,
38153817
// Emit the call.
38163818
SmallVector<SILValue, 4> rawResults;
38173819
emitRawApply(*this, loc, fn, subs, args, substFnType, options,
3818-
/*indirect results*/ {}, rawResults);
3820+
/*indirect results*/ {}, rawResults, ExecutorBreadcrumb());
38193821

38203822
auto token = rawResults.pop_back_val();
38213823
auto yieldValues = llvm::makeArrayRef(rawResults);
@@ -4385,6 +4387,8 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
43854387
subs.getGenericSignature().getCanonicalSignature());
43864388
}
43874389

4390+
ExecutorBreadcrumb breadcrumb;
4391+
43884392
// The presence of `implicitlyAsyncApply` indicates that the callee is a
43894393
// synchronous function isolated to an actor other than our own.
43904394
// Such functions require the caller to hop to the callee's executor
@@ -4399,24 +4403,28 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
43994403
if (args.size() > 0)
44004404
actorSelf = args.back();
44014405

4402-
auto didHop = emitHopToTargetActor(loc, getActorIsolation(funcDecl),
4403-
actorSelf);
4404-
assert(didHop);
4406+
breadcrumb = emitHopToTargetActor(loc, getActorIsolation(funcDecl),
4407+
actorSelf);
44054408
}
4409+
} else if (actor && substFnType->isAsync()) {
4410+
// Otherwise, if we're in an actor method ourselves, and we're calling into
4411+
// any sort of async function, we'll want to make sure to hop back to our
4412+
// own executor afterward, since the callee could have made arbitrary hops
4413+
// out of our isolation domain.
4414+
breadcrumb = ExecutorBreadcrumb(actor);
44064415
}
44074416

44084417
SILValue rawDirectResult;
44094418
{
44104419
SmallVector<SILValue, 1> rawDirectResults;
44114420
emitRawApply(*this, loc, fn, subs, args, substFnType, options,
4412-
indirectResultAddrs, rawDirectResults);
4421+
indirectResultAddrs, rawDirectResults, breadcrumb);
44134422
assert(rawDirectResults.size() == 1);
44144423
rawDirectResult = rawDirectResults[0];
44154424
}
44164425

44174426
// hop back to the current executor
4418-
if (substFnType->isAsync() || implicitlyAsyncApply.hasValue())
4419-
emitHopToCurrentExecutor(loc);
4427+
breadcrumb.emit(*this, loc);
44204428

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

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,13 @@ static ManagedValue emitBuiltinCancelAsyncTask(
13761376
return SGF.emitCancelAsyncTask(loc, args[0].borrow(SGF, loc).forward(SGF));
13771377
}
13781378

1379+
// Emit SIL for the named builtin: getCurrentExecutor.
1380+
static ManagedValue emitBuiltinGetCurrentExecutor(
1381+
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1382+
PreparedArguments &&preparedArgs, SGFContext C) {
1383+
return ManagedValue::forUnmanaged(SGF.emitGetCurrentExecutor(loc));
1384+
}
1385+
13791386
// Helper to lower a function argument to be usable as the entry point of a
13801387
// new async task
13811388
static ManagedValue

lib/SILGen/SILGenFunction.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class ResultPlan;
4646
using ResultPlanPtr = std::unique_ptr<ResultPlan>;
4747
class ArgumentScope;
4848
class Scope;
49+
class ExecutorBreadcrumb;
4950

5051
struct LValueOptions {
5152
bool IsNonAccessing = false;
@@ -760,11 +761,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
760761
CanAnyFunctionType inputSubstType,
761762
CanAnyFunctionType outputSubstType,
762763
bool baseLessVisibleThanDerived);
763-
764-
/// If the current function is actor isolated, insert a hop_to_executor
765-
/// instruction.
766-
void emitHopToCurrentExecutor(SILLocation loc);
767-
764+
768765
//===--------------------------------------------------------------------===//
769766
// Control flow
770767
//===--------------------------------------------------------------------===//
@@ -836,11 +833,17 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
836833
/// Generates code to obtain the executor for the given actor isolation,
837834
/// as-needed, and emits a \c hop_to_executor to that executor.
838835
///
839-
/// \returns a non-null pointer if a \c hop_to_executor was emitted.
840-
HopToExecutorInst* emitHopToTargetActor(SILLocation loc,
836+
/// \returns an \c ExecutorBreadcrumb that saves the information necessary to hop
837+
/// back to what was previously the current executor after the actor-isolated
838+
/// region ends. Invoke \c emit on the breadcrumb to
839+
/// restore the previously-active executor.
840+
ExecutorBreadcrumb emitHopToTargetActor(SILLocation loc,
841841
Optional<ActorIsolation> actorIso,
842842
Optional<ManagedValue> actorSelf);
843843

844+
/// Gets a reference to the current executor for the task.
845+
SILValue emitGetCurrentExecutor(SILLocation loc);
846+
844847
/// Generates code to obtain the executor given the actor's decl.
845848
/// \returns a SILValue representing the executor.
846849
SILValue emitLoadActorExecutor(VarDecl *actorDecl);
@@ -1632,6 +1635,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
16321635

16331636
SILBasicBlock *getTryApplyErrorDest(SILLocation loc,
16341637
CanSILFunctionType fnTy,
1638+
ExecutorBreadcrumb prevExecutor,
16351639
SILResultInfo exnResult,
16361640
bool isSuppressed);
16371641

0 commit comments

Comments
 (0)