Skip to content

Commit f6f0392

Browse files
committed
[SILGen] Only hop to the callee isolation domain once when evaluating isolated
default arguments. (cherry picked from commit 5ac839b)
1 parent 0fb9f4d commit f6f0392

File tree

3 files changed

+146
-21
lines changed

3 files changed

+146
-21
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2695,6 +2695,28 @@ class DelayedArgument {
26952695
return LV().Loc;
26962696
}
26972697

2698+
bool isDefaultArg() const {
2699+
return Kind == DefaultArgument;
2700+
}
2701+
2702+
SILLocation getDefaultArgLoc() const {
2703+
assert(isDefaultArg());
2704+
auto storage = Value.get<DefaultArgumentStorage>(Kind);
2705+
return storage.loc;
2706+
}
2707+
2708+
llvm::Optional<ActorIsolation> getIsolation() const {
2709+
if (!isDefaultArg())
2710+
return llvm::None;
2711+
2712+
auto storage = Value.get<DefaultArgumentStorage>(Kind);
2713+
if (!storage.implicitlyAsync)
2714+
return llvm::None;
2715+
2716+
auto callee = storage.defaultArgsOwner.getDecl();
2717+
return getActorIsolation(callee);
2718+
}
2719+
26982720
void emit(SILGenFunction &SGF, SmallVectorImpl<ManagedValue> &args,
26992721
size_t &argIndex) {
27002722
switch (Kind) {
@@ -2920,6 +2942,31 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29202942
MutableArrayRef<SmallVector<ManagedValue, 4>> args) {
29212943
assert(!delayedArgs.empty());
29222944

2945+
// If any of the delayed arguments are isolated default arguments,
2946+
// argument evaluation happens in the following order:
2947+
//
2948+
// 1. Left-to-right evalution of explicit r-value arguments
2949+
// 2. Left-to-right evaluation of formal access arguments
2950+
// 3. Hop to the callee's isolation domain
2951+
// 4. Left-to-right evaluation of default arguments
2952+
2953+
// So, if any delayed arguments are isolated, all default arguments
2954+
// are collected during the first pass over the delayed arguments,
2955+
// and emitted separately after a hop to the callee's isolation domain.
2956+
2957+
llvm::Optional<ActorIsolation> defaultArgIsolation;
2958+
for (auto &arg : delayedArgs) {
2959+
if (auto isolation = arg.getIsolation()) {
2960+
defaultArgIsolation = isolation;
2961+
break;
2962+
}
2963+
}
2964+
2965+
SmallVector<std::tuple<
2966+
/*delayedArgIt*/decltype(delayedArgs)::iterator,
2967+
/*siteArgsIt*/decltype(args)::iterator,
2968+
/*index*/size_t>, 2> isolatedArgs;
2969+
29232970
SmallVector<std::pair<SILValue, SILLocation>, 4> emittedInoutArgs;
29242971
auto delayedNext = delayedArgs.begin();
29252972

@@ -2928,7 +2975,8 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29282975
// wherever there's a delayed argument to insert.
29292976
//
29302977
// Note that this also begins the formal accesses in evaluation order.
2931-
for (auto &siteArgs : args) {
2978+
for (auto argsIt = args.begin(); argsIt != args.end(); ++argsIt) {
2979+
auto &siteArgs = *argsIt;
29322980
// NB: siteArgs.size() may change during iteration
29332981
for (size_t i = 0; i < siteArgs.size(); ) {
29342982
auto &siteArg = siteArgs[i];
@@ -2941,6 +2989,15 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29412989
assert(delayedNext != delayedArgs.end());
29422990
auto &delayedArg = *delayedNext;
29432991

2992+
if (defaultArgIsolation && delayedArg.isDefaultArg()) {
2993+
isolatedArgs.push_back(std::make_tuple(delayedNext, argsIt, i));
2994+
if (++delayedNext == delayedArgs.end()) {
2995+
goto done;
2996+
} else {
2997+
continue;
2998+
}
2999+
}
3000+
29443001
// Emit the delayed argument and replace it in the arguments array.
29453002
delayedArg.emit(SGF, siteArgs, i);
29463003

@@ -2961,6 +3018,45 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29613018

29623019
done:
29633020

3021+
if (defaultArgIsolation) {
3022+
assert(SGF.F.isAsync());
3023+
assert(!isolatedArgs.empty());
3024+
3025+
auto &firstArg = *std::get<0>(isolatedArgs[0]);
3026+
auto loc = firstArg.getDefaultArgLoc();
3027+
3028+
SILValue executor;
3029+
switch (*defaultArgIsolation) {
3030+
case ActorIsolation::GlobalActor:
3031+
case ActorIsolation::GlobalActorUnsafe:
3032+
executor = SGF.emitLoadGlobalActorExecutor(
3033+
defaultArgIsolation->getGlobalActor());
3034+
break;
3035+
3036+
case ActorIsolation::ActorInstance:
3037+
llvm_unreachable("default arg cannot be actor instance isolated");
3038+
3039+
case ActorIsolation::Unspecified:
3040+
case ActorIsolation::Nonisolated:
3041+
case ActorIsolation::NonisolatedUnsafe:
3042+
llvm_unreachable("Not isolated");
3043+
}
3044+
3045+
// Hop to the target isolation domain once to evaluate all
3046+
// default arguments.
3047+
SGF.emitHopToTargetExecutor(loc, executor);
3048+
3049+
size_t argsEmitted = 0;
3050+
for (auto &isolatedArg : isolatedArgs) {
3051+
auto &delayedArg = *std::get<0>(isolatedArg);
3052+
auto &siteArgs = *std::get<1>(isolatedArg);
3053+
auto argIndex = std::get<2>(isolatedArg) + argsEmitted;
3054+
auto origIndex = argIndex;
3055+
delayedArg.emit(SGF, siteArgs, argIndex);
3056+
argsEmitted += (argIndex - origIndex);
3057+
}
3058+
}
3059+
29643060
// Check to see if we have multiple inout arguments which obviously
29653061
// alias. Note that we could do this in a later SILDiagnostics pass
29663062
// as well: this would be stronger (more equivalences exposed) but

lib/SILGen/SILGenExpr.cpp

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2564,24 +2564,8 @@ SILGenFunction::emitApplyOfDefaultArgGenerator(SILLocation loc,
25642564
emitCaptures(loc, generator, CaptureEmission::ImmediateApplication,
25652565
captures);
25662566

2567-
// The default argument might require the callee's isolation. If so,
2568-
// make sure to emit an actor hop.
2569-
//
2570-
// FIXME: Instead of hopping back and forth for each individual isolated
2571-
// default argument, we should emit one hop for all default arguments if
2572-
// any of them are isolated, and immediately enter the function after.
2573-
llvm::Optional<ActorIsolation> implicitActorHopTarget = llvm::None;
2574-
if (implicitlyAsync) {
2575-
auto *param = getParameterAt(defaultArgsOwner.getDecl(), destIndex);
2576-
auto isolation = param->getInitializerIsolation();
2577-
if (isolation.isActorIsolated()) {
2578-
implicitActorHopTarget = isolation;
2579-
}
2580-
}
2581-
25822567
return emitApply(std::move(resultPtr), std::move(argScope), loc, fnRef, subs,
2583-
captures, calleeTypeInfo, ApplyOptions(), C,
2584-
implicitActorHopTarget);
2568+
captures, calleeTypeInfo, ApplyOptions(), C, llvm::None);
25852569
}
25862570

25872571
RValue SILGenFunction::emitApplyOfStoredPropertyInitializer(

test/Concurrency/isolated_default_argument_eval.swift

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,62 @@ func requiresMainActor() -> Int { 0 }
99
@MainActor
1010
func mainActorDefaultArg(value: Int = requiresMainActor()) {}
1111

12+
@MainActor
13+
func mainActorMultiDefaultArg(x: Int = requiresMainActor(),
14+
y: Int = 0,
15+
tuple: (Int, Int) = (requiresMainActor(), 2),
16+
z: Int = 0) {}
17+
1218
// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval15mainActorCalleryyF
1319
@MainActor func mainActorCaller() {
1420
mainActorDefaultArg()
21+
mainActorMultiDefaultArg()
1522
}
1623

1724
// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval22nonisolatedAsyncCalleryyYaF
1825
func nonisolatedAsyncCaller() async {
1926
// CHECK: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
20-
// CHECK: [[GETARG:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval19mainActorDefaultArg5valueySi_tFfA_
2127
// CHECK: hop_to_executor {{.*}} : $MainActor
22-
// CHECK-NEXT: apply [[GETARG]]()
23-
// CHECK-NEXT: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
28+
// CHECK: [[GET_VALUE:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval19mainActorDefaultArg5valueySi_tFfA_
29+
// CHECK-NEXT: apply [[GET_VALUE]]()
30+
// CHECK: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
2431
await mainActorDefaultArg()
32+
33+
// CHECK: hop_to_executor {{.*}} : $MainActor
34+
// CHECK: [[GET_X:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA_
35+
// CHECK-NEXT: apply [[GET_X]]()
36+
// CHECK-NOT: hop_to_executor
37+
// CHECK: [[GET_Y:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA0_
38+
// CHECK-NEXT: apply [[GET_Y]]()
39+
// CHECK-NOT: hop_to_executor
40+
// CHECK: [[GET_TUPLE:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA1_
41+
// CHECK-NEXT: apply [[GET_TUPLE]]()
42+
// CHECK-NOT: hop_to_executor
43+
// CHECK: [[GET_Z:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval24mainActorMultiDefaultArg1x1y5tuple1zySi_S2i_SitSitFfA2_
44+
// CHECK-NEXT: apply [[GET_Z]]()
45+
// CHECK: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
46+
await mainActorMultiDefaultArg()
47+
}
48+
49+
@MainActor
50+
func isolatedDefaultInoutMix(x: inout Int, y: Int, z: Int = requiresMainActor()) {}
51+
52+
var argValue: Int { 0 }
53+
54+
// CHECK-LABEL: sil hidden [ossa] @$s30isolated_default_argument_eval20passInoutWithDefaultyyYaF
55+
func passInoutWithDefault() async {
56+
// CHECK: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
57+
58+
var x = 0
59+
60+
// CHECK: [[GET_ARG_VALUE:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval8argValueSivg
61+
// CHECK-NEXT: [[ARG_VALUE:%[0-9]+]] = apply [[GET_ARG_VALUE]]()
62+
// CHECK-NEXT: [[INOUT_X:%[0-9]+]] = begin_access [modify]
63+
// CHECK: hop_to_executor {{.*}} : $MainActor
64+
// CHECK: [[GET_Z:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval0A15DefaultInoutMix1x1y1zySiz_S2itFfA1_
65+
// CHECK-NEXT: [[Z:%[0-9]+]] = apply [[GET_Z]]()
66+
// CHECK: [[FN:%[0-9]+]] = function_ref @$s30isolated_default_argument_eval0A15DefaultInoutMix1x1y1zySiz_S2itF : $@convention(thin) (@inout Int, Int, Int) -> ()
67+
// CHECK: apply [[FN]]([[INOUT_X]], [[ARG_VALUE]], [[Z]])
68+
// CHECK: hop_to_executor {{.*}} : $Optional<Builtin.Executor>
69+
await isolatedDefaultInoutMix(x: &x, y: argValue)
2570
}

0 commit comments

Comments
 (0)