Files
swift-mirror/test/SILOptimizer/lower_hop_to_actor.sil
Erik Eckstein 5489627310 LowerHopToActor: insert a borrow scope for an Optional<Actor>.none value
An optional-none value has "none" ownership, but still a borrow scope is needed.
Fixes a SIL verifier crash.
rdar://153066034
2025-06-11 13:35:21 +02:00

324 lines
14 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -lower-hop-to-actor | %FileCheck %s
// REQUIRES: concurrency
sil_stage canonical
import Builtin
import Swift
import _Concurrency
actor MyActor {
@_hasStorage private var p: Int { get set }
}
actor CustomActor {
nonisolated var unownedExecutor: UnownedSerialExecutor { get }
}
// CHECK-LABEL: sil [ossa] @simple :
sil [ossa] @simple : $@async (@guaranteed MyActor) -> () {
bb0(%0 : @guaranteed $MyActor):
// CHECK: bb0(%0 : @guaranteed $MyActor):
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
hop_to_executor %0 : $MyActor
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @pair :
sil [ossa] @pair : $@async (@guaranteed MyActor, @guaranteed MyActor) -> () {
bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor):
// CHECK: bb0(%0 : @guaranteed $MyActor, %1 : @guaranteed $MyActor):
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%1 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %1 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
hop_to_executor %0 : $MyActor
hop_to_executor %1 : $MyActor
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @immediate_dominance :
sil [ossa] @immediate_dominance : $@async (@guaranteed MyActor) -> () {
bb0(%0 : @guaranteed $MyActor):
// CHECK: bb0(%0 : @guaranteed $MyActor):
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
hop_to_executor %0 : $MyActor
hop_to_executor %0 : $MyActor
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @multi_block :
sil [ossa] @multi_block : $@async (@guaranteed MyActor, Builtin.Int1) -> () {
bb0(%0 : @guaranteed $MyActor, %1 : $Builtin.Int1):
// CHECK: bb0(%0 : @guaranteed $MyActor, %1 : $Builtin.Int1):
// CHECK-NEXT: cond_br %1, bb1, bb2
// CHECK: bb1:
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
cond_br %1, bb1, bb2
bb1:
hop_to_executor %0 : $MyActor
br bb3
bb2:
hop_to_executor %0 : $MyActor
br bb3
bb3:
%r = tuple ()
return %r : $()
}
// Test that we apply the dominance-based optimization correctly even
// opposite to program order.
// CHECK-LABEL: sil [ossa] @reverse :
sil [ossa] @reverse : $@async (@guaranteed MyActor, Builtin.Int1) -> () {
bb0(%0 : @guaranteed $MyActor, %1 : $Builtin.Int1):
// CHECK: bb0(%0 : @guaranteed $MyActor, %1 : $Builtin.Int1):
// CHECK-NEXT: br bb5
// CHECK: bb1:
// CHECK-NEXT: hop_to_executor [[DEPEND:%.*]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb6
// CHECK: bb2:
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb1
// CHECK: bb3:
// CHECK-NEXT: hop_to_executor [[DEPEND:%.*]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb6
// CHECK: bb4:
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb3
// CHECK: bb5:
// CHECK-NEXT: cond_br %1, bb2, bb4
// CHECK: bb6:
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
br bb5
bb1:
hop_to_executor %0 : $MyActor
br bb6
bb2:
hop_to_executor %0 : $MyActor
br bb1
bb3:
hop_to_executor %0 : $MyActor
br bb6
bb4:
hop_to_executor %0 : $MyActor
br bb3
bb5:
cond_br %1, bb2, bb4
bb6:
%r = tuple ()
return %r : $()
}
// Test that we merge blocks appropriately in dominance optimizations.
// CHECK-LABEL: sil [ossa] @reverse_merge :
sil [ossa] @reverse_merge : $@async (@guaranteed MyActor, Builtin.Int1) -> () {
bb0(%0 : @guaranteed $MyActor, %1 : $Builtin.Int1):
// CHECK: bb0(%0 : @guaranteed $MyActor, %1 : $Builtin.Int1):
// CHECK-NEXT: br bb5
// CHECK: bb1:
// CHECK-NEXT: hop_to_executor [[DEPEND:%.*]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb6
// CHECK: bb2:
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb1
// CHECK: bb3:
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb6
// CHECK: bb4:
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: br bb3
// CHECK: bb5:
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>(%0 : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $MyActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: cond_br %1, bb2, bb4
// CHECK: bb6:
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
br bb5
bb1:
hop_to_executor %0 : $MyActor
br bb6
bb2:
hop_to_executor %0 : $MyActor
br bb1
bb3:
hop_to_executor %0 : $MyActor
br bb6
bb4:
hop_to_executor %0 : $MyActor
br bb3
bb5:
hop_to_executor %0 : $MyActor
cond_br %1, bb2, bb4
bb6:
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @simple_custom :
sil [ossa] @simple_custom : $@async (@guaranteed CustomActor) -> () {
bb0(%0 : @guaranteed $CustomActor):
// CHECK: bb0(%0 : @guaranteed $CustomActor):
// CHECK-NEXT: [[WITNESS_METHOD:%.*]] = witness_method $CustomActor, #Actor.unownedExecutor!getter
// CHECK-NEXT: [[WRAPPED_EXECUTOR:%.*]] = apply [[WITNESS_METHOD]]<CustomActor>(%0)
// CHECK-NEXT: [[EXECUTOR:%.*]] = struct_extract [[WRAPPED_EXECUTOR]] : $UnownedSerialExecutor, #UnownedSerialExecutor.executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[SOME]] : $Optional<Builtin.Executor> on %0 : $CustomActor
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
hop_to_executor %0 : $CustomActor
%r = tuple ()
return %r : $()
}
sil @get_raw_executor : $@convention(thin) () -> Builtin.Executor
// CHECK-LABEL: sil [ossa] @non_optional_executor :
sil [ossa] @non_optional_executor : $@async () -> () {
bb0:
// CHECK: bb0:
// CHECK: [[GET_RAW_EXECUTOR:%.*]] = function_ref @get_raw_executor
// CHECK-NEXT: [[EXECUTOR:%.*]] = apply [[GET_RAW_EXECUTOR]]()
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: hop_to_executor [[SOME]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
%0 = function_ref @get_raw_executor : $@convention(thin) () -> Builtin.Executor
%1 = apply %0() : $@convention(thin) () -> Builtin.Executor
hop_to_executor %1 : $Builtin.Executor
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @simple_optional_actor :
sil [ossa] @simple_optional_actor : $@async (@guaranteed Optional<MyActor>) -> () {
bb0(%0 : @guaranteed $Optional<MyActor>):
// CHECK: bb0(%0 : @guaranteed $Optional<MyActor>):
// CHECK-NEXT: switch_enum %0 : $Optional<MyActor>, case #Optional.some!enumelt: bb1, default bb2
// CHECK: bb1([[ACTOR:%.*]] : @guaranteed $MyActor):
// CHECK-NEXT: [[EXECUTOR:%.*]] = builtin "buildDefaultActorExecutorRef"<MyActor>([[ACTOR]] : $MyActor) : $Builtin.Executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: br bb3([[SOME]] : $Optional<Builtin.Executor>)
// CHECK: bb2:
// CHECK-NEXT: [[NONE:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
// CHECK-NEXT: br bb3([[NONE]] : $Optional<Builtin.Executor>)
// CHECK: bb3([[OPT_EXEC:%.*]] : $Optional<Builtin.Executor>):
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[OPT_EXEC]] : $Optional<Builtin.Executor> on %0 : $Optional<MyActor>
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
hop_to_executor %0 : $Optional<MyActor>
hop_to_executor %0 : $Optional<MyActor>
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @optional_existential_actor :
sil [ossa] @optional_existential_actor : $@async (@guaranteed Optional<any Actor>) -> () {
bb0(%0 : @guaranteed $Optional<any Actor>):
// CHECK: bb0(%0 : @guaranteed $Optional<any Actor>):
// CHECK-NEXT: switch_enum %0 : $Optional<any Actor>, case #Optional.some!enumelt: bb1, default bb2
// CHECK: bb1([[ACTOR:%.*]] : @guaranteed $any Actor):
// CHECK-NEXT: [[OPENED:%.*]] = open_existential_ref [[ACTOR]] : $any Actor to $@opened("[[ID:.*]]", any Actor) Self
// CHECK-NEXT: [[WITNESS:%.*]] = witness_method $@opened("[[ID]]", any Actor) Self, #Actor.unownedExecutor!getter : <Self where Self : Actor> (Self) -> () -> UnownedSerialExecutor
// CHECK-NEXT: [[SERIAL_EXEC:%.*]] = apply [[WITNESS]]<@opened("[[ID]]", any Actor) Self>([[OPENED]])
// CHECK-NEXT: [[EXECUTOR:%.*]] = struct_extract [[SERIAL_EXEC]] : $UnownedSerialExecutor, #UnownedSerialExecutor.executor
// CHECK-NEXT: [[SOME:%.*]] = enum $Optional<Builtin.Executor>, #Optional.some!enumelt, [[EXECUTOR]] : $Builtin.Executor
// CHECK-NEXT: br bb3([[SOME]] : $Optional<Builtin.Executor>)
// CHECK: bb2:
// CHECK-NEXT: [[NONE:%.*]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
// CHECK-NEXT: br bb3([[NONE]] : $Optional<Builtin.Executor>)
// CHECK: bb3([[OPT_EXEC:%.*]] : $Optional<Builtin.Executor>):
// CHECK-NEXT: [[DEPEND:%.*]] = mark_dependence [[OPT_EXEC]] : $Optional<Builtin.Executor> on %0 : $Optional<any Actor>
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: hop_to_executor [[DEPEND]] : $Optional<Builtin.Executor>
// CHECK-NEXT: [[RET:%.*]] = tuple ()
// CHECK-NEXT: return [[RET]] : $()
hop_to_executor %0 : $Optional<any Actor>
hop_to_executor %0 : $Optional<any Actor>
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @optional_owned_actor :
// CHECK: %1 = copy_value %0
// CHECK-NEXT: %2 = begin_borrow %1
// CHECK-NEXT: switch_enum %2
// CHECK: mark_dependence
// CHECK-NEXT: end_borrow %2
// CHECKL: } // end sil function '@optional_owned_actor'
sil [ossa] @optional_owned_actor : $@convention(thin) @async (@guaranteed Optional<any Actor>) -> () {
bb0(%0 : @guaranteed $Optional<any Actor>):
%1 = copy_value %0
hop_to_executor %1
destroy_value %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @optional_none :
// CHECK: %0 = enum
// CHECK-NEXT: %1 = begin_borrow %0
// CHECK-NEXT: switch_enum %1
// CHECK: mark_dependence
// CHECK-NEXT: end_borrow %1
// CHECKL: } // end sil function '@optional_none'
sil [ossa] @optional_none : $@convention(thin) @async () -> () {
bb0:
%0 = enum $Optional<any Actor>, #Optional.none!enumelt
hop_to_executor %0
%r = tuple ()
return %r
}