@@ -119,6 +119,12 @@ class OptimizeHopToExecutor {
119119// / Search for hop_to_executor instructions and add their operands to \p actors.
120120void OptimizeHopToExecutor::collectActors (Actors &actors) {
121121 int uniqueActorID = 0 ;
122+
123+ if (auto isolation = function->getActorIsolation ();
124+ isolation && isolation->isCallerIsolationInheriting ()) {
125+ actors[function->maybeGetIsolatedArgument ()] = uniqueActorID++;
126+ }
127+
122128 for (SILBasicBlock &block : *function) {
123129 for (SILInstruction &inst : block) {
124130 if (auto *hop = dyn_cast<HopToExecutorInst>(&inst)) {
@@ -193,10 +199,7 @@ void OptimizeHopToExecutor::solveDataflowBackward() {
193199// / Returns true if \p inst is a suspension point or an async call.
194200static bool isSuspensionPoint (SILInstruction *inst) {
195201 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 ())
200203 return true ;
201204 return false ;
202205 }
@@ -213,8 +216,20 @@ bool OptimizeHopToExecutor::removeRedundantHopToExecutors(const Actors &actors)
213216
214217 // Initialize the dataflow.
215218 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+ }();
218233 state.intra = BlockState::NotSet;
219234 for (SILInstruction &inst : *state.block ) {
220235 if (isSuspensionPoint (&inst)) {
@@ -316,44 +331,11 @@ void OptimizeHopToExecutor::updateNeedExecutor(int &needExecutor,
316331 return ;
317332 }
318333
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-
353334 if (isSuspensionPoint (inst)) {
354335 needExecutor = BlockState::NoExecutorNeeded;
355336 return ;
356337 }
338+
357339 if (needsExecutor (inst))
358340 needExecutor = BlockState::ExecutorNeeded;
359341}
@@ -403,6 +385,29 @@ bool OptimizeHopToExecutor::needsExecutor(SILInstruction *inst) {
403385 if (isa<BeginBorrowInst>(inst) || isa<EndBorrowInst>(inst)) {
404386 return false ;
405387 }
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+
406411 return inst->mayReadOrWriteMemory ();
407412}
408413
0 commit comments