mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Once we move to a copy-on-write implementation of existential value buffers we can no longer consume or destroy values of an opened existential unless the buffer is uniquely owned. Therefore we need to track the allowed operation on opened values. Add qualifiers "mutable_access" and "immutable_access" to open_existential_addr instructions to indicate the allowed access to the opened value. Once we move to a copy-on-write implementation, an "open_existential_addr mutable_access" instruction will ensure unique ownership of the value buffer.
233 lines
5.4 KiB
Plaintext
233 lines
5.4 KiB
Plaintext
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -alloc-stack-hoisting | %FileCheck %s
|
|
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -alloc-stack-hoisting -sil-merge-stack-slots=false | %FileCheck %s
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
protocol P {
|
|
}
|
|
|
|
struct Generic<T> {
|
|
var x : T
|
|
}
|
|
|
|
struct FixedSize {
|
|
var x : Builtin.Int8
|
|
}
|
|
|
|
// CHECK-LABEL: sil @hoist_generic
|
|
// CHECK: bb0({{.*}}):
|
|
// CHECK: [[AS:%.*]] = alloc_stack $T
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK-NOT: dealloc_stack
|
|
// CHECK: bb2
|
|
// CHECK: bb3:
|
|
// CHECK: dealloc_stack [[AS]]
|
|
// CHECK: return
|
|
|
|
sil @hoist_generic : $@convention(thin) <T> (@in T, Builtin.Int1) -> () {
|
|
bb0(%0 : $*T, %1: $Builtin.Int1):
|
|
cond_br %1, bb1, bb2
|
|
bb1:
|
|
%2 = alloc_stack $T
|
|
copy_addr [take] %0 to [initialization] %2 : $*T
|
|
destroy_addr %2 : $*T
|
|
dealloc_stack %2 : $*T
|
|
br bb3
|
|
bb2:
|
|
destroy_addr %0 : $*T
|
|
br bb3
|
|
bb3:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
sil @throwing_fun : $@convention(thin) () -> @error Error
|
|
|
|
// CHECK-LABEL: sil @hoist_generic
|
|
// CHECK: bb0({{.*}}):
|
|
// CHECK: [[AS:%.*]] = alloc_stack $T
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK-NOT: dealloc_stack
|
|
// CHECK: bb2
|
|
// CHECK: bb3:
|
|
// CHECK: try_apply
|
|
// CHECK: bb4({{.*}}:
|
|
// CHECK: dealloc_stack [[AS]]
|
|
// CHECK: return
|
|
// CHECK: bb5({{.*}}):
|
|
// CHECK: dealloc_stack [[AS]]
|
|
// CHECK: throw
|
|
|
|
sil @hoist_generic_throwing : $@convention(thin) <T> (@in T, Builtin.Int1) -> @error Error {
|
|
bb0(%0 : $*T, %1: $Builtin.Int1):
|
|
cond_br %1, bb1, bb2
|
|
bb1:
|
|
%2 = alloc_stack $T
|
|
copy_addr [take] %0 to [initialization] %2 : $*T
|
|
destroy_addr %2 : $*T
|
|
dealloc_stack %2 : $*T
|
|
br bb3
|
|
bb2:
|
|
destroy_addr %0 : $*T
|
|
br bb3
|
|
bb3:
|
|
%3 = function_ref @throwing_fun : $@convention(thin) () -> @error Error
|
|
try_apply %3() : $@convention(thin) () -> @error Error, normal bb4, error bb5
|
|
|
|
bb4(%6: $()):
|
|
%4 = tuple ()
|
|
return %4 : $()
|
|
|
|
bb5(%5: $Error):
|
|
throw %5: $Error
|
|
}
|
|
|
|
// CHECK-LABEL: sil @hoist_generic
|
|
// CHECK: bb0({{.*}}):
|
|
// CHECK: [[AS:%.*]] = alloc_stack $Generic<T>
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK-NOT: dealloc_stack
|
|
// CHECK: bb2
|
|
// CHECK: bb3:
|
|
// CHECK: dealloc_stack [[AS]]
|
|
// CHECK: return
|
|
|
|
sil @hoist_generic_type : $@convention(thin) <T> (@in Generic<T>, Builtin.Int1) -> () {
|
|
bb0(%0 : $*Generic<T>, %1: $Builtin.Int1):
|
|
cond_br %1, bb1, bb2
|
|
bb1:
|
|
%2 = alloc_stack $Generic<T>
|
|
copy_addr [take] %0 to [initialization] %2 : $*Generic<T>
|
|
destroy_addr %2 : $*Generic<T>
|
|
dealloc_stack %2 : $*Generic<T>
|
|
br bb3
|
|
bb2:
|
|
destroy_addr %0 : $*Generic<T>
|
|
br bb3
|
|
bb3:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @hoist_generic_nesting
|
|
// CHECK: bb0({{.*}}):
|
|
// CHECK: [[AS:%.*]] = alloc_stack $T
|
|
// CHECK: [[AS2:%.*]] = alloc_stack $T
|
|
// CHECK: [[FIXED:%.*]] = alloc_stack $FixedSize
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK-NOT: dealloc_stack
|
|
// CHECK: bb2
|
|
// CHECK: bb3:
|
|
// CHECK dealloc_stack [[FIXED]]
|
|
// CHECK: dealloc_stack [[AS2]]
|
|
// CHECK: dealloc_stack [[AS]]
|
|
// CHECK: return
|
|
|
|
sil @hoist_generic_nesting : $@convention(thin) <T> (@in T, Builtin.Int1) -> () {
|
|
bb0(%0 : $*T, %1: $Builtin.Int1):
|
|
%2 = alloc_stack $FixedSize
|
|
cond_br %1, bb1, bb2
|
|
bb1:
|
|
%3 = alloc_stack $T
|
|
%4 = alloc_stack $T
|
|
copy_addr [take] %0 to [initialization] %3 : $*T
|
|
destroy_addr %3 : $*T
|
|
dealloc_stack %4: $*T
|
|
dealloc_stack %3 : $*T
|
|
br bb3
|
|
bb2:
|
|
destroy_addr %0 : $*T
|
|
br bb3
|
|
bb3:
|
|
dealloc_stack %2: $*FixedSize
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_hoist_opened_generic
|
|
// CHECK: bb0({{.*}}):
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK: bb1:
|
|
// CHECK: alloc_stack
|
|
// CHECK: bb2:
|
|
// CHECK: bb3:
|
|
// CHECK-NOT: dealloc_stack
|
|
// CHECK: return
|
|
|
|
sil @dont_hoist_opened_generic : $@convention(thin) (@in P, Builtin.Int1) -> () {
|
|
bb0(%0 : $*P, %1: $Builtin.Int1):
|
|
cond_br %1, bb1, bb2
|
|
bb1:
|
|
%2 = open_existential_addr mutable_access %0 : $*P to $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
|
|
%3 = alloc_stack $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
|
|
copy_addr [take] %2 to [initialization] %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
|
|
destroy_addr %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
|
|
dealloc_stack %3 : $*@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") P
|
|
br bb3
|
|
bb2:
|
|
destroy_addr %0 : $*P
|
|
br bb3
|
|
bb3:
|
|
%4 = tuple ()
|
|
return %4 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_hoist_protocol
|
|
// CHECK: bb0({{.*}}):
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK: bb1:
|
|
// CHECK: alloc_stack
|
|
// CHECK: bb2:
|
|
// CHECK: bb3:
|
|
// CHECK-NOT: dealloc_stack
|
|
// CHECK: return
|
|
|
|
sil @dont_hoist_protocol : $@convention(thin) (@in P, Builtin.Int1) -> () {
|
|
bb0(%0 : $*P, %1: $Builtin.Int1):
|
|
cond_br %1, bb1, bb2
|
|
bb1:
|
|
%2 = alloc_stack $P
|
|
copy_addr [take] %0 to [initialization] %2 : $*P
|
|
destroy_addr %2 : $*P
|
|
dealloc_stack %2 : $*P
|
|
br bb3
|
|
bb2:
|
|
destroy_addr %0 : $*P
|
|
br bb3
|
|
bb3:
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_crash_on_unreachable
|
|
// CHECK: bb0({{.*}}):
|
|
// CHECK: alloc_stack
|
|
// CHECK: bb1:
|
|
// CHECK-NOT: alloc_stack
|
|
// CHECK: bb2:
|
|
// CHECK: bb3:
|
|
// CHECK: unreachable
|
|
|
|
sil @dont_crash_on_unreachable : $@convention(thin) <T> (@in T, Builtin.Int1) -> Never {
|
|
bb0(%0 : $*T, %1: $Builtin.Int1):
|
|
cond_br %1, bb1, bb2
|
|
bb1:
|
|
%2 = alloc_stack $T
|
|
copy_addr [take] %0 to [initialization] %2 : $*T
|
|
destroy_addr %2 : $*T
|
|
br bb3
|
|
bb2:
|
|
destroy_addr %0 : $*T
|
|
br bb3
|
|
bb3:
|
|
%3 = builtin "int_trap"() : $()
|
|
unreachable
|
|
}
|