@@ -119,6 +119,12 @@ class OptimizeHopToExecutor {
119
119
// / Search for hop_to_executor instructions and add their operands to \p actors.
120
120
void OptimizeHopToExecutor::collectActors (Actors &actors) {
121
121
int uniqueActorID = 0 ;
122
+
123
+ if (auto isolation = function->getActorIsolation ();
124
+ isolation && isolation->isCallerIsolationInheriting ()) {
125
+ actors[function->maybeGetIsolatedArgument ()] = uniqueActorID++;
126
+ }
127
+
122
128
for (SILBasicBlock &block : *function) {
123
129
for (SILInstruction &inst : block) {
124
130
if (auto *hop = dyn_cast<HopToExecutorInst>(&inst)) {
@@ -193,10 +199,7 @@ void OptimizeHopToExecutor::solveDataflowBackward() {
193
199
// / Returns true if \p inst is a suspension point or an async call.
194
200
static bool isSuspensionPoint (SILInstruction *inst) {
195
201
if (auto applySite = FullApplySite::isa (inst)) {
196
- // NOTE: For 6.2, we consider nonisolated(nonsending) to be a suspension
197
- // point, when it really isn't. We do this so that we have a truly
198
- // conservative change that does not change output.
199
- if (applySite.isAsync ())
202
+ if (applySite.isAsync () && !applySite.isCallerIsolationInheriting ())
200
203
return true ;
201
204
return false ;
202
205
}
@@ -213,8 +216,20 @@ bool OptimizeHopToExecutor::removeRedundantHopToExecutors(const Actors &actors)
213
216
214
217
// Initialize the dataflow.
215
218
for (BlockState &state : blockStates) {
216
- state.entry = (state.block == function->getEntryBlock () ?
217
- BlockState::Unknown : BlockState::NotSet);
219
+ state.entry = [&]() -> int {
220
+ if (state.block != function->getEntryBlock ()) {
221
+ return BlockState::NotSet;
222
+ }
223
+
224
+ if (auto isolation = function->getActorIsolation ();
225
+ isolation && isolation->isCallerIsolationInheriting ()) {
226
+ auto *fArg =
227
+ cast<SILFunctionArgument>(function->maybeGetIsolatedArgument ());
228
+ return actors.lookup (SILValue (fArg ));
229
+ }
230
+
231
+ return BlockState::Unknown;
232
+ }();
218
233
state.intra = BlockState::NotSet;
219
234
for (SILInstruction &inst : *state.block ) {
220
235
if (isSuspensionPoint (&inst)) {
@@ -316,44 +331,11 @@ void OptimizeHopToExecutor::updateNeedExecutor(int &needExecutor,
316
331
return ;
317
332
}
318
333
319
- // For 6.2 to be conservative, if we are calling a function with
320
- // caller_isolation_inheriting isolation, treat the callsite as if the
321
- // callsite is an instruction that needs an executor.
322
- //
323
- // DISCUSSION: The reason why we are doing this is that in 6.2, we are going
324
- // to continue treating caller isolation inheriting functions as a suspension
325
- // point for the purpose of eliminating redundant hop to executor to not make
326
- // this optimization more aggressive. Post 6.2, we will stop treating caller
327
- // isolation inheriting functions as suspension points, meaning this code can
328
- // be deleted.
329
- if (auto fas = FullApplySite::isa (inst);
330
- fas && fas.isAsync () && fas.isCallerIsolationInheriting ()) {
331
- needExecutor = BlockState::ExecutorNeeded;
332
- return ;
333
- }
334
-
335
- // For 6.2, if we are in a caller isolation inheriting function, we need to
336
- // treat its return as an executor needing function before
337
- // isSuspensionPoint.
338
- //
339
- // DISCUSSION: We need to do this here since for 6.2, a caller isolation
340
- // inheriting function is going to be considered a suspension point to be
341
- // conservative and make this optimization strictly more conservative. Post
342
- // 6.2, since caller isolation inheriting functions will no longer be
343
- // considered suspension points, we will be able to sink this code into needs
344
- // executor.
345
- if (isa<ReturnInst>(inst)) {
346
- if (auto isolation = inst->getFunction ()->getActorIsolation ();
347
- isolation && isolation->isCallerIsolationInheriting ()) {
348
- needExecutor = BlockState::ExecutorNeeded;
349
- return ;
350
- }
351
- }
352
-
353
334
if (isSuspensionPoint (inst)) {
354
335
needExecutor = BlockState::NoExecutorNeeded;
355
336
return ;
356
337
}
338
+
357
339
if (needsExecutor (inst))
358
340
needExecutor = BlockState::ExecutorNeeded;
359
341
}
@@ -403,6 +385,29 @@ bool OptimizeHopToExecutor::needsExecutor(SILInstruction *inst) {
403
385
if (isa<BeginBorrowInst>(inst) || isa<EndBorrowInst>(inst)) {
404
386
return false ;
405
387
}
388
+
389
+ // A call to a caller isolation inheriting function does not create dead
390
+ // executors since caller isolation inheriting functions do not hop in their
391
+ // prologue.
392
+ if (auto fas = FullApplySite::isa (inst);
393
+ fas && fas.isAsync () && fas.isCallerIsolationInheriting ()) {
394
+ return true ;
395
+ }
396
+
397
+ // Treat returns from a caller isolation inheriting function as requiring the
398
+ // liveness of hop to executors before it.
399
+ //
400
+ // DISCUSSION: We do this since callers of callee functions with isolation
401
+ // inheriting isolation are not required to have a hop after the return from
402
+ // the callee function... so we have no guarantee that there isn't code in the
403
+ // caller that needs this hop to executor to run on the correct actor.
404
+ if (isa<ReturnInst>(inst)) {
405
+ if (auto isolation = inst->getFunction ()->getActorIsolation ();
406
+ isolation && isolation->isCallerIsolationInheriting ()) {
407
+ return true ;
408
+ }
409
+ }
410
+
406
411
return inst->mayReadOrWriteMemory ();
407
412
}
408
413
0 commit comments