// RUN: %target-sil-opt -sil-print-function-isolation-info -enable-sil-verify-all %s -optimize-hop-to-executor | %FileCheck %s // REQUIRES: concurrency // REQUIRES: asserts sil_stage canonical import Builtin import Swift actor MyActor { @_hasStorage private var p: Int { get set } } sil [ossa] @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () sil [ossa] @anotherAsyncFunction : $@convention(thin) @async () -> () sil [ossa] @syncFunction : $@convention(thin) () -> () // CHECK-LABEL: sil [ossa] @simpleRedundantActor : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NEXT: hop_to_executor %0 // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'simpleRedundantActor' sil [ossa] @simpleRedundantActor : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () hop_to_executor %0 : $MyActor apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @redundantActorAndControlFlow : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NEXT: hop_to_executor %0 // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'redundantActorAndControlFlow' sil [ossa] @redundantActorAndControlFlow : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () cond_br undef, bb1, bb2 bb1: hop_to_executor %0 : $MyActor br bb3 bb2: br bb3 bb3: hop_to_executor %0 : $MyActor apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @notRedundantActor : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb1: // CHECK-NEXT: hop_to_executor %0 // CHECK: bb3: // CHECK-NEXT: hop_to_executor %0 // CHECK: } // end sil function 'notRedundantActor' sil [ossa] @notRedundantActor : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () cond_br undef, bb1, bb2 bb1: hop_to_executor %0 : $MyActor apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () br bb3 bb2: br bb3 bb3: hop_to_executor %0 : $MyActor apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @mismatchingActor : $@convention(method) @async (@guaranteed MyActor, @guaranteed MyActor) -> () { // CHECK: hop_to_executor %0 // CHECK: apply // CHECK: hop_to_executor %1 // CHECK: apply // CHECK: } // end sil function 'mismatchingActor' sil [ossa] @mismatchingActor : $@convention(method) @async (@guaranteed MyActor, @guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor): %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () hop_to_executor %0 : $MyActor apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () hop_to_executor %1 : $MyActor apply %f(%1) : $@convention(method) (@guaranteed MyActor) -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @simpleDeadHopElimination : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'simpleDeadHopElimination' sil [ossa] @simpleDeadHopElimination : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @doubleDeadHopElimination : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NEXT: hop_to_executor %0 // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'doubleDeadHopElimination' sil [ossa] @doubleDeadHopElimination : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor hop_to_executor %0 : $MyActor %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () hop_to_executor %0 : $MyActor apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @deadHopAndControlFlow : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'deadHopAndControlFlow' sil [ossa] @deadHopAndControlFlow : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor cond_br undef, bb1, bb2 bb1: br bb3 bb2: br bb3 bb3: %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply %f() : $@convention(thin) @async () -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @partialAliveHop : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NEXT: hop_to_executor %0 // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'partialAliveHop' sil [ossa] @partialAliveHop : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor cond_br undef, bb1, bb2 bb1: %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () br bb3 bb2: br bb3 bb3: %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @handleUnreachable1 : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'handleUnreachable1' sil [ossa] @handleUnreachable1 : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor cond_br undef, bb1, bb2 bb1: unreachable bb2: %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @handleUnreachable2 : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'handleUnreachable2' sil [ossa] @handleUnreachable2 : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): cond_br undef, bb1, bb2 bb1: hop_to_executor %0 : $MyActor unreachable bb2: %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @handleUnreachableBlock : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NEXT: hop_to_executor // CHECK: bb2: // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'handleUnreachableBlock' sil [ossa] @handleUnreachableBlock : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () br bb2 bb1: br bb2 bb2: hop_to_executor %0 : $MyActor %r = tuple () return %r : $() } // This pattern is definitely optimizable, but it's *not* supposed to // be optimized by simply removing the hop before the builtin. // CHECK-LABEL: sil [ossa] @handleGetCurrentExecutor : $@convention(method) @async (@guaranteed MyActor, @guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor): // CHECK-NEXT: // function_ref // CHECK-NEXT: function_ref // CHECK-NEXT: apply // CHECK-NEXT: hop_to_executor // CHECK-NEXT: builtin // CHECK-NEXT: hop_to_executor // CHECK-NEXT: // function_ref // CHECK-NEXT: function_ref // CHECK-NEXT: apply // CHECK-NEXT: tuple // CHECK-NEXT: return // CHECK: } // end sil function 'handleGetCurrentExecutor' sil [ossa] @handleGetCurrentExecutor : $@convention(method) @async (@guaranteed MyActor, @guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor %async_f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply %async_f() : $@convention(thin) @async () -> () hop_to_executor %0 : $MyActor %curExec = builtin "getCurrentExecutor"() : $Optional hop_to_executor %1 : $MyActor %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () apply %f(%1) : $@convention(method) (@guaranteed MyActor) -> () hop_to_executor %0 : $MyActor %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @redundantMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: bb0(%0 : @guaranteed $MyActor): // CHECK-NEXT: hop_to_executor %0 // CHECK: apply // CHECK: hop_to_executor [mandatory] %0 // CHECK: } // end sil function 'redundantMandatoryHop' sil [ossa] @redundantMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor %f = function_ref @requiredToRunOnActor : $@convention(method) (@guaranteed MyActor) -> () apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () hop_to_executor [mandatory] %0 : $MyActor apply %f(%0) : $@convention(method) (@guaranteed MyActor) -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @simpleMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: hop_to_executor [mandatory] // CHECK: } // end sil function 'simpleMandatoryHop' sil [ossa] @simpleMandatoryHop : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor [mandatory] %0 : $MyActor %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @simpleMandatoryHopAndSuspend : $@convention(method) @async (@guaranteed MyActor) -> () { // CHECK: hop_to_executor [mandatory] // CHECK: apply // CHECK: } // end sil function 'simpleMandatoryHopAndSuspend' sil [ossa] @simpleMandatoryHopAndSuspend : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor [mandatory] %0 : $MyActor %async_f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply %async_f() : $@convention(thin) @async () -> () %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @handleBeginBorrow : {{.*}} { // CHECK-NOT: hop_to_executor // CHECK-LABEL: } // end sil function 'handleBeginBorrow' sil [ossa] @handleBeginBorrow : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 : $MyActor %b = begin_borrow %0 : $MyActor end_borrow %b : $MyActor %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @handleEndBorrow : {{.*}} { // CHECK-NOT: hop_to_executor // CHECK-LABEL: } // end sil function 'handleEndBorrow' sil [ossa] @handleEndBorrow : $@convention(method) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): %b = begin_borrow %0 : $MyActor hop_to_executor %0 : $MyActor end_borrow %b : $MyActor %r = tuple () return %r : $() } // CHECK-LABEL: sil [ossa] @simpleDCEAsync : $@convention(thin) @async (@guaranteed MyActor) -> () { // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'simpleDCEAsync' sil [ossa] @simpleDCEAsync : $@convention(thin) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply %f() : $@convention(thin) @async () -> () %9999 = tuple () return %9999 : $() } // CHECK-LABEL: sil [ossa] @simpleCallerIsolationInheritingStopsDCE : $@convention(thin) @async (@guaranteed MyActor) -> () { // CHECK: hop_to_executor // CHECK: } // end sil function 'simpleCallerIsolationInheritingStopsDCE' sil [ossa] @simpleCallerIsolationInheritingStopsDCE : $@convention(thin) @async (@guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () %9999 = tuple () return %9999 : $() } // We should eliminate none of the hop_to_executor here since // caller_isolation_inheriting @async apply sites do not cross isolation // boundaries. // // CHECK-LABEL: sil [ossa] @simpleCallerIsolationInheritingStopsDCE2 : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { // CHECK: hop_to_executor // CHECK: hop_to_executor // CHECK: hop_to_executor // CHECK: } // end sil function 'simpleCallerIsolationInheritingStopsDCE2' sil [ossa] @simpleCallerIsolationInheritingStopsDCE2 : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): hop_to_executor %0 %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () hop_to_executor %1 %f2 = function_ref @syncFunction : $@convention(thin) () -> () apply %f2() : $@convention(thin) () -> () apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () hop_to_executor %2 apply %f2() : $@convention(thin) () -> () apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () %9999 = tuple () return %9999 : $() } // CHECK-LABEL: sil [ossa] @simpleWithoutCallerIsolationInheritingHaveDCE : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { // CHECK: bb0([[ARG1:%.*]] : @guaranteed $MyActor, [[ARG2:%.*]] : @guaranteed $MyActor, [[ARG3:%.*]] : @guaranteed $MyActor): // CHECK-NEXT: // function_ref anotherAsyncFunction // CHECK-NEXT: [[FUNC:%.*]] = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () // CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () // CHECK-NEXT: hop_to_executor [[ARG2]] // CHECK-NEXT: // function_ref syncFunction // CHECK-NEXT: [[FUNC2:%.*]] = function_ref @syncFunction : $@convention(thin) () -> () // CHECK-NEXT: apply [[FUNC2]]() : $@convention(thin) () -> () // CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () // CHECK-NEXT: hop_to_executor [[ARG3]] // CHECK-NEXT: apply [[FUNC2]]() : $@convention(thin) () -> () // CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () // CHECK-NEXT: apply [[FUNC]]() : $@convention(thin) @async () -> () // CHECK: } // end sil function 'simpleWithoutCallerIsolationInheritingHaveDCE' sil [ossa] @simpleWithoutCallerIsolationInheritingHaveDCE : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): hop_to_executor %0 %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply %f() : $@convention(thin) @async () -> () hop_to_executor %1 %f2 = function_ref @syncFunction : $@convention(thin) () -> () apply %f2() : $@convention(thin) () -> () apply %f() : $@convention(thin) @async () -> () hop_to_executor %2 apply %f2() : $@convention(thin) () -> () apply %f() : $@convention(thin) @async () -> () hop_to_executor %2 apply %f() : $@convention(thin) @async () -> () %9999 = tuple () return %9999 : $() } // We do not allow for these to be propagated yet through caller isolation // inheriting, so we should have all of the hop_to_executor. // // CHECK-LABEL: sil [ossa] @callerIsolationInheritingStopsAllowsPropagating : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { // CHECK: bb0( // CHECK: hop_to_executor // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'callerIsolationInheritingStopsAllowsPropagating' sil [ossa] @callerIsolationInheritingStopsAllowsPropagating : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): hop_to_executor %0 %f = function_ref @anotherAsyncFunction : $@convention(thin) @async () -> () apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () hop_to_executor %0 %f2 = function_ref @syncFunction : $@convention(thin) () -> () apply %f2() : $@convention(thin) () -> () apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () hop_to_executor %0 apply %f2() : $@convention(thin) () -> () apply [callee_isolation=caller_isolation_inheriting] [caller_isolation=caller_isolation_inheriting] %f() : $@convention(thin) @async () -> () %9999 = tuple () return %9999 : $() } // Since we are caller isolation inheriting, make sure that we leave around the // hop_to_executor due to ehre elase. We do eliminate one of the hop_to_executor // though. // // CHECK: sil [isolation "caller_isolation_inheriting"] [ossa] @callerIsolationInheritingStopsReturnDeadEnd : $@convention(thin) @async (@sil_isolated @guaranteed MyActor) -> () { // CHECK: bb0( // CHECK-NEXT: tuple // CHECK-NEXT: return // CHECK: } // end sil function 'callerIsolationInheritingStopsReturnDeadEnd' sil [isolation "caller_isolation_inheriting"] [ossa] @callerIsolationInheritingStopsReturnDeadEnd : $@convention(thin) @async (@sil_isolated @guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor): hop_to_executor %0 hop_to_executor %0 %9999 = tuple () return %9999 : $() } // CHECK-LABEL: sil [ossa] @noIsolationStillRemoves : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { // CHECK-NOT: hop_to_executor // CHECK: } // end sil function 'noIsolationStillRemoves' sil [ossa] @noIsolationStillRemoves : $@convention(thin) @async (@guaranteed MyActor, @guaranteed MyActor, @guaranteed MyActor) -> () { bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor, %2 : @guaranteed $MyActor): hop_to_executor %0 %9999 = tuple () return %9999 : $() }