// RUN: %target-sil-opt -enable-sil-verify-all %s -optimize-hop-to-executor | %FileCheck %s // REQUIRES: concurrency 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 () -> () // 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 : $() }