mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
When rewriting a store, insert the copy_addr after it rather than before it. Previously, after the copy_addr is created, ``` alloc_stack %target_addr $Ty, var, name "something" ... copy_addr [take] %source_addr to [init] %target_addr store %instance to [init] %addr ``` when deleting the store, debug info salvaging may result in a debug_value instruction being created before the store, ``` alloc_stack %target_addr $Ty, var, name "something" ... copy_addr [take] %source_addr to [init] %target_addr debug_value %source_addr : $*Ty, var, name "something" store %instance to [init] %addr ``` using the %source_addr. If the created copy_addr is a [take], this results in the debug_value being a use-after-consume in the final SIL: ``` alloc_stack %target_addr $Ty, var, name "something" ... copy_addr [take] %source_addr to [init] %target_addr debug_value %source_addr : $*Ty, var, name "something" ``` Instead, now, the copy_addr is created after: ``` alloc_stack %target_addr $Ty, var, name "something" ... store %instance to [init] %addr copy_addr [take] %source_addr to [init] %target_addr ``` So when the debug_value instruction is created before the store during debug info salvaging ``` alloc_stack %target_addr $Ty, var, name "something" ... debug_value %source_addr : $*Ty, var, name "something" store %instance to [init] %addr copy_addr [take] %source_addr to [init] %target_addr ``` it is also before the newly added copy_addr. The result is that when the store is deleted, we have valid SIL: ``` alloc_stack %target_addr $Ty, var, name "something" ... debug_value %source_addr : $*Ty, var, name "something" copy_addr [take] %source_addr to [init] %target_addr
539 lines
21 KiB
Plaintext
539 lines
21 KiB
Plaintext
// RUN: %target-sil-opt -address-lowering -enable-sil-opaque-values -emit-sorted-sil -module-name Swift -sil-verify-all %s | %FileCheck %s
|
|
//
|
|
// Test the PhiStorageOptimizer within the AddressLowering pass.
|
|
|
|
import Builtin
|
|
|
|
sil_stage raw
|
|
|
|
typealias AnyObject = Builtin.AnyObject
|
|
typealias Int = Builtin.Int64
|
|
typealias Bool = Builtin.Int1
|
|
|
|
enum Optional<T> {
|
|
case none
|
|
case some(T)
|
|
}
|
|
|
|
struct S {}
|
|
|
|
struct SRef<T> {
|
|
@_hasStorage var object: AnyObject { get set }
|
|
@_hasStorage var element: T { get set }
|
|
init(object: AnyObject, element: T)
|
|
}
|
|
|
|
enum InnerEnum<T> {
|
|
case payload(T, AnyObject)
|
|
}
|
|
enum OuterEnum<T> {
|
|
case inner(InnerEnum<T>, AnyObject)
|
|
}
|
|
|
|
struct InnerStruct<T> {
|
|
var t: T
|
|
var object: AnyObject
|
|
}
|
|
struct OuterStruct<T> {
|
|
var inner: InnerStruct<T>
|
|
var object: AnyObject
|
|
}
|
|
|
|
sil [ossa] @getOut : $@convention(thin) <T> () -> @out T
|
|
|
|
// Test BBArgs allocation.
|
|
|
|
// No projection from incoming values. No interference.
|
|
// CHECK-LABEL: sil [ossa] @f010_testBBArgSelect : $@convention(thin) <T> () -> @out T {
|
|
// CHECK: bb0(%0 : $*T):
|
|
// CHECK: [[F:%.*]] = function_ref @getOut : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: [[GET0:%.*]] = apply [[F]]<T>(%0) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: [[GET1:%.*]] = apply [[F]]<T>(%0) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
// CHECK: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK: %{{.*}} = tuple ()
|
|
// CHECK: return %{{.*}} : $()
|
|
// CHECK-LABEL: } // end sil function 'f010_testBBArgSelect'
|
|
sil [ossa] @f010_testBBArgSelect : $@convention(thin) <T> () -> @out T {
|
|
bb0:
|
|
%get = function_ref @getOut : $@convention(thin) <τ_0_0>() -> @out τ_0_0
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%get0 = apply %get<T>() : $@convention(thin) <τ_0_0>() -> @out τ_0_0
|
|
br bb3(%get0 : $T)
|
|
|
|
bb2:
|
|
%get1 = apply %get<T>() : $@convention(thin) <τ_0_0>() -> @out τ_0_0
|
|
br bb3(%get1 : $T)
|
|
|
|
// %15
|
|
bb3(%15 : @owned $T):
|
|
return %15 : $T
|
|
}
|
|
|
|
// One projection from incoming values. One interference.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @f020_testBBArgProjectOne : $@convention(thin) <T> () -> @out T {
|
|
// CHECK: bb0(%0 : $*T):
|
|
// CHECK: [[ALLOC:%.*]] = alloc_stack $T
|
|
// CHECK: apply %{{.*}}<T>(%0) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
// CHECK: apply %{{.*}}<T>([[ALLOC]]) : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: destroy_addr %0 : $*T
|
|
// CHECK: copy_addr [take] %1 to [init] %0 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: destroy_addr %1 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK: dealloc_stack [[ALLOC]] : $*T
|
|
// CHECK: return %{{.*}} : $()
|
|
// CHECK-LABEL: } // end sil function 'f020_testBBArgProjectOne'
|
|
sil [ossa] @f020_testBBArgProjectOne : $@convention(thin) <T> () -> @out T {
|
|
bb0:
|
|
%get = function_ref @getOut : $@convention(thin) <τ_0_0>() -> @out τ_0_0
|
|
%get0 = apply %get<T>() : $@convention(thin) <τ_0_0>() -> @out τ_0_0
|
|
%get1 = apply %get<T>() : $@convention(thin) <τ_0_0>() -> @out τ_0_0
|
|
cond_br undef, bb2, bb1
|
|
|
|
bb1:
|
|
destroy_value %get0 : $T
|
|
br bb3(%get1 : $T)
|
|
|
|
bb2:
|
|
destroy_value %get1 : $T
|
|
br bb3(%get0 : $T)
|
|
|
|
bb3(%arg : @owned $T):
|
|
return %arg : $T
|
|
}
|
|
|
|
// Projection from incoming values. No interference.
|
|
// CHECK-LABEL: sil [ossa] @f030_testBBArgProjectIn : $@convention(thin) <T> (@in T, @in T) -> @out T {
|
|
// CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T):
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: destroy_addr %2 : $*T
|
|
// CHECK: copy_addr [take] %1 to [init] %0 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: destroy_addr %1 : $*T
|
|
// CHECK: copy_addr [take] %2 to [init] %0 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK-LABEL: } // end sil function 'f030_testBBArgProjectIn'
|
|
sil [ossa] @f030_testBBArgProjectIn : $@convention(thin) <T> (@in T, @in T) -> @out T {
|
|
bb0(%0 : @owned $T, %1 : @owned $T):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
destroy_value %0 : $T
|
|
br bb3(%1 : $T)
|
|
|
|
bb2:
|
|
destroy_value %1 : $T
|
|
br bb3(%0 : $T)
|
|
|
|
bb3(%arg : @owned $T):
|
|
return %arg : $T
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @f040_testInSwapOut : $@convention(thin) <T> (@in T, @in T) -> (@out T, @out T) {
|
|
// CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T, %3 : $*T):
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: copy_addr [take] %2 to [init] %1 : $*T
|
|
// CHECK: copy_addr [take] %3 to [init] %0 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: copy_addr [take] %2 to [init] %0 : $*T
|
|
// CHECK: copy_addr [take] %3 to [init] %1 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK-LABEL: } // end sil function 'f040_testInSwapOut'
|
|
sil [ossa] @f040_testInSwapOut : $@convention(thin) <T> (@in T, @in T) -> (@out T, @out T) {
|
|
bb0(%0 : @owned $T, %1 : @owned $T):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3(%0 : $T, %1 : $T)
|
|
|
|
bb2:
|
|
br bb3(%1 : $T, %0 : $T)
|
|
|
|
bb3(%arg0 : @owned $T, %arg1 : @owned $T):
|
|
%result = tuple (%arg0 : $T, %arg1 : $T)
|
|
return %result : $(T, T)
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @f050_testCombine : $@convention(thin) <T> (@in T, @in T) -> (@out T, @out T) {
|
|
// CHECK: bb0(%0 : $*T, %1 : $*T, %2 : $*T, %3 : $*T):
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: copy_addr [take] %2 to [init] %0 : $*T
|
|
// CHECK: copy_addr [take] %3 to [init] %1 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: copy_addr %2 to [init] %1 : $*T
|
|
// CHECK: destroy_addr %3 : $*T
|
|
// CHECK: copy_addr [take] %2 to [init] %0 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK-LABEL: } // end sil function 'f050_testCombine'
|
|
sil [ossa] @f050_testCombine : $@convention(thin) <T> (@in T, @in T) -> (@out T, @out T) {
|
|
bb0(%0 : @owned $T, %1 : @owned $T):
|
|
cond_br undef, bb2, bb1
|
|
|
|
bb1:
|
|
br bb3(%0 : $T, %1 : $T)
|
|
|
|
bb2:
|
|
%copy = copy_value %0 : $T
|
|
destroy_value %1 : $T
|
|
br bb3(%0 : $T, %copy : $T)
|
|
|
|
bb3(%arg0 : @owned $T, %arg1 : @owned $T):
|
|
%result = tuple (%arg0 : $T, %arg1 : $T)
|
|
return %result : $(T, T)
|
|
}
|
|
|
|
// Test cyclic anti-dependence on phi copies.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @f060_testInoutSwap : $@convention(thin) <T> (@inout T, @inout T) -> () {
|
|
// CHECK: bb0(%0 : $*T, %1 : $*T):
|
|
// CHECK: [[ALLOC0:%.*]] = alloc_stack $T
|
|
// CHECK: [[ALLOC1:%.*]] = alloc_stack $T
|
|
// CHECK: copy_addr [take] %0 to [init] [[ALLOC1]] : $*T
|
|
// CHECK: copy_addr [take] %1 to [init] [[ALLOC0]] : $*T
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: [[TMP:%.*]] = alloc_stack $T
|
|
// CHECK: copy_addr [take] [[ALLOC0]] to [init] [[TMP]] : $*T
|
|
// CHECK: copy_addr [take] [[ALLOC1]] to [init] [[ALLOC0]] : $*T
|
|
// CHECK: copy_addr [take] [[TMP]] to [init] [[ALLOC1]] : $*T
|
|
// CHECK: dealloc_stack [[TMP]] : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK: copy_addr [take] [[ALLOC0]] to [init] %0 : $*T
|
|
// CHECK: copy_addr [take] [[ALLOC1]] to [init] %1 : $*T
|
|
// CHECK: dealloc_stack [[ALLOC1]] : $*T
|
|
// CHECK: dealloc_stack [[ALLOC0]] : $*T
|
|
// CHECK-LABEL: } // end sil function 'f060_testInoutSwap'
|
|
sil [ossa] @f060_testInoutSwap : $@convention(thin) <T> (@inout T, @inout T) -> () {
|
|
bb0(%0 : $*T, %1 : $*T):
|
|
%2 = load [take] %0 : $*T
|
|
%3 = load [take] %1 : $*T
|
|
cond_br undef, bb2, bb1
|
|
|
|
bb1:
|
|
br bb3(%2 : $T, %3 : $T)
|
|
|
|
bb2:
|
|
br bb3(%3 : $T, %2 : $T)
|
|
|
|
bb3(%phi0 : @owned $T, %phi1 : @owned $T):
|
|
store %phi0 to [init] %0 : $*T
|
|
store %phi1 to [init] %1 : $*T
|
|
%99 = tuple ()
|
|
return %99 : $()
|
|
}
|
|
|
|
// Test phi copies that project into their use.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @f070_testInoutFieldSwap : $@convention(thin) <T> (@inout SRef<T>, @inout SRef<T>) -> () {
|
|
// CHECK: bb0(%0 : $*SRef<T>, %1 : $*SRef<T>):
|
|
// CHECK: [[ALLOCA:%.*]] = alloc_stack $SRef<T>
|
|
// CHECK: [[ALLOCB:%.*]] = alloc_stack $SRef<T>
|
|
// CHECK: [[ALLOCSA:%.*]] = alloc_stack $SRef<T>
|
|
// CHECK: [[ALLOCSB:%.*]] = alloc_stack $SRef<T>
|
|
// CHECK: copy_addr [take] %0 to [init] [[ALLOCA]] : $*SRef<T>
|
|
// CHECK: copy_addr [take] %1 to [init] [[ALLOCB]] : $*SRef<T>
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: [[A1OADR:%.*]] = struct_element_addr [[ALLOCA]] : $*SRef<T>, #SRef.object
|
|
// CHECK: [[A1O:%.*]] = load [take] [[A1OADR]] : $*AnyObject
|
|
// CHECK: [[A1EADR:%.*]] = struct_element_addr [[ALLOCA]] : $*SRef<T>, #SRef.element
|
|
// CHECK: [[B1OADR:%.*]] = struct_element_addr [[ALLOCB]] : $*SRef<T>, #SRef.object
|
|
// CHECK: [[B1O:%.*]] = load [take] [[B1OADR]] : $*AnyObject
|
|
// CHECK: [[B1EADR:%.*]] = struct_element_addr [[ALLOCB]] : $*SRef<T>, #SRef.element
|
|
// CHECK: destroy_value [[B1O]] : $AnyObject
|
|
// CHECK: [[CP1:%.*]] = copy_value [[A1O]] : $AnyObject
|
|
// CHECK: [[SA1EADR:%.*]] = struct_element_addr [[ALLOCSA]] : $*SRef<T>, #SRef.element
|
|
// CHECK: copy_addr [take] [[A1EADR]] to [init] [[SA1EADR]] : $*T
|
|
// CHECK: [[SB1EADR:%.*]] = struct_element_addr [[ALLOCSB]] : $*SRef<T>, #SRef.element
|
|
// CHECK: copy_addr [take] [[B1EADR]] to [init] [[SB1EADR]] : $*T
|
|
// CHECK: br bb3([[A1O]] : $AnyObject, [[CP1]] : $AnyObject)
|
|
// CHECK: bb2:
|
|
// CHECK: [[A2OADR:%.*]] = struct_element_addr [[ALLOCA]] : $*SRef<T>, #SRef.object
|
|
// CHECK: [[A2O:%.*]] = load [take] [[A2OADR]] : $*AnyObject
|
|
// CHECK: [[A2EADR:%.*]] = struct_element_addr [[ALLOCA]] : $*SRef<T>, #SRef.element
|
|
// CHECK: [[B2OADR:%.*]] = struct_element_addr [[ALLOCB]] : $*SRef<T>, #SRef.object
|
|
// CHECK: [[B2O:%.*]] = load [take] [[B2OADR]] : $*AnyObject
|
|
// CHECK: [[B2EADR:%.*]] = struct_element_addr [[ALLOCB]] : $*SRef<T>, #SRef.element
|
|
// CHECK: destroy_value [[B2O]] : $AnyObject
|
|
// CHECK: [[CP2:%.*]] = copy_value [[A2O]] : $AnyObject
|
|
// CHECK: [[SB2EADR:%.*]] = struct_element_addr [[ALLOCSB]] : $*SRef<T>, #SRef.element
|
|
// CHECK: copy_addr [take] [[A2EADR]] to [init] [[SB2EADR]] : $*T
|
|
// CHECK: [[SA2EADR:%.*]] = struct_element_addr [[ALLOCSA]] : $*SRef<T>, #SRef.element
|
|
// CHECK: copy_addr [take] [[B2EADR]] to [init] [[SA2EADR]] : $*T
|
|
// CHECK: br bb3([[A2O]] : $AnyObject, [[CP2]] : $AnyObject)
|
|
// CHECK: bb3([[PHI0:%.*]] : @owned $AnyObject, [[PHI1:%.*]] : @owned $AnyObject):
|
|
// CHECK: [[SA3EADR:%.*]] = struct_element_addr [[ALLOCSA]] : $*SRef<T>, #SRef.object
|
|
// CHECK: store [[PHI0]] to [init] [[SA3EADR]] : $*AnyObject
|
|
// CHECK: [[SA3EADR:%.*]] = struct_element_addr [[ALLOCSB]] : $*SRef<T>, #SRef.object
|
|
// CHECK: store [[PHI1]] to [init] [[SA3EADR]] : $*AnyObject
|
|
// CHECK: copy_addr [take] [[ALLOCSA]] to [init] %0 : $*SRef<T>
|
|
// CHECK: copy_addr [take] [[ALLOCSB]] to [init] %1 : $*SRef<T>
|
|
// CHECK-LABEL: } // end sil function 'f070_testInoutFieldSwap'
|
|
sil [ossa] @f070_testInoutFieldSwap : $@convention(thin) <T> (@inout SRef<T>, @inout SRef<T>) -> () {
|
|
bb0(%0 : $*SRef<T>, %1 : $*SRef<T>):
|
|
%la = load [take] %0 : $*SRef<T>
|
|
%lb = load [take] %1 : $*SRef<T>
|
|
cond_br undef, bb2, bb1
|
|
|
|
bb1:
|
|
(%da1o, %da1e) = destructure_struct %la : $SRef<T>
|
|
(%db1o, %db1e) = destructure_struct %lb : $SRef<T>
|
|
destroy_value %db1o : $AnyObject
|
|
%ca1o = copy_value %da1o : $AnyObject
|
|
br bb3(%da1o : $AnyObject, %ca1o : $AnyObject, %da1e : $T, %db1e : $T)
|
|
|
|
bb2:
|
|
(%da2o, %da2e) = destructure_struct %la : $SRef<T>
|
|
(%db2o, %db2e) = destructure_struct %lb : $SRef<T>
|
|
destroy_value %db2o : $AnyObject
|
|
%ca2o = copy_value %da2o : $AnyObject
|
|
br bb3(%da2o : $AnyObject, %ca2o : $AnyObject, %db2e : $T, %da2e : $T)
|
|
|
|
bb3(%phio0 : @owned $AnyObject, %phio1 : @owned $AnyObject, %phie0 : @owned $T, %phie1 : @owned $T):
|
|
%sa = struct $SRef<T> (%phio0 : $AnyObject, %phie0 : $T)
|
|
%sb = struct $SRef<T> (%phio1 : $AnyObject, %phie1 : $T)
|
|
store %sa to [init] %0 : $*SRef<T>
|
|
store %sb to [init] %1 : $*SRef<T>
|
|
%99 = tuple ()
|
|
return %99 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @f080_testNestedComposeEnumPhi : $@convention(thin) <T> (@in T, @in T, @owned AnyObject, @owned AnyObject) -> @out OuterEnum<T> {
|
|
// CHECK: bb0(%0 : $*OuterEnum<T>, %1 : $*T, %2 : $*T, %3 : @owned $AnyObject, %4 : @owned $AnyObject):
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: destroy_addr %2 : $*T
|
|
// CHECK: [[TUPLE1:%.*]] = init_enum_data_addr [[INNER1:%.*]] : $*InnerEnum<T>, #InnerEnum.payload!enumelt
|
|
// CHECK: [[TUPLE1_0:%.*]] = tuple_element_addr [[TUPLE1]] : $*(T, AnyObject), 0
|
|
// CHECK: copy_addr [take] %1 to [init] [[TUPLE1_0]] : $*T
|
|
// CHECK: [[TUPLE1_1:%.*]] = tuple_element_addr [[TUPLE1]] : $*(T, AnyObject), 1
|
|
// CHECK: store %3 to [init] [[TUPLE1_1]] : $*AnyObject
|
|
// CHECK: inject_enum_addr [[INNER1]] : $*InnerEnum<T>, #InnerEnum.payload!enumelt
|
|
// CHECK: copy_addr [take] [[INNER1]] to [init] [[PHI6:%.*]] : $*InnerEnum<T>
|
|
// CHECK: br bb6
|
|
// CHECK: bb2:
|
|
// CHECK: cond_br undef, bb4, bb3
|
|
// CHECK: bb3:
|
|
// CHECK: destroy_addr %1 : $*T
|
|
// CHECK: copy_addr [take] %2 to [init] [[PHI5:%.*]] : $*T
|
|
// CHECK: br bb5
|
|
// CHECK: bb4:
|
|
// CHECK: destroy_addr %2 : $*T
|
|
// CHECK: copy_addr [take] %1 to [init] [[PHI5]] : $*T
|
|
// CHECK: br bb5
|
|
// CHECK: bb5:
|
|
// CHECK: [[TUPLE5:%.*]] = init_enum_data_addr [[INNER5:%.*]] : $*InnerEnum<T>, #InnerEnum.payload!enumelt
|
|
// CHECK: [[TUPLE5_0:%.*]] = tuple_element_addr [[TUPLE5]] : $*(T, AnyObject), 0
|
|
// CHECK: copy_addr [take] [[PHI5]] to [init] [[TUPLE5_0]] : $*T
|
|
// CHECK: [[TUPLE5_1:%.*]] = tuple_element_addr [[TUPLE5]] : $*(T, AnyObject), 1
|
|
// CHECK: store %3 to [init] [[TUPLE5_1]] : $*AnyObject
|
|
// CHECK: inject_enum_addr [[INNER5]] : $*InnerEnum<T>, #InnerEnum.payload!enumelt
|
|
// CHECK: copy_addr [take] [[INNER5]] to [init] [[PHI6:%.*]] : $*InnerEnum<T>
|
|
// CHECK: br bb6
|
|
// CHECK: bb6:
|
|
// CHECK: [[TUPLE6:%.*]] = init_enum_data_addr %0 : $*OuterEnum<T>, #OuterEnum.inner!enumelt
|
|
// CHECK: [[TUPLE6_0:%.*]] = tuple_element_addr [[TUPLE6]] : $*(InnerEnum<T>, AnyObject), 0
|
|
// CHECK: copy_addr [take] [[PHI6]] to [init] [[TUPLE6_0]] : $*InnerEnum<T>
|
|
// CHECK: [[TUPLE6_1:%.*]] = tuple_element_addr [[TUPLE6]] : $*(InnerEnum<T>, AnyObject), 1
|
|
// CHECK: store %4 to [init] [[TUPLE6_1]] : $*AnyObject
|
|
// CHECK: inject_enum_addr %0 : $*OuterEnum<T>, #OuterEnum.inner!enumelt
|
|
// CHECK-LABEL: } // end sil function 'f080_testNestedComposeEnumPhi'
|
|
sil [ossa] @f080_testNestedComposeEnumPhi : $@convention(thin) <T> (@in T, @in T, @owned AnyObject, @owned AnyObject) -> @out OuterEnum<T> {
|
|
bb0(%0 : @owned $T, %1 : @owned $T, %2 : @owned $AnyObject, %3 : @owned $AnyObject):
|
|
cond_br undef, bb2, bb1
|
|
bb1:
|
|
destroy_value %1 : $T
|
|
%tuple1 = tuple (%0 : $T, %2 : $AnyObject)
|
|
%inner1 = enum $InnerEnum<T>, #InnerEnum.payload, %tuple1 : $(T, AnyObject)
|
|
br bb6(%inner1 : $InnerEnum<T>)
|
|
bb2:
|
|
cond_br undef, bb4, bb3
|
|
bb3:
|
|
destroy_value %0 : $T
|
|
br bb5(%1 : $T)
|
|
bb4:
|
|
destroy_value %1 : $T
|
|
br bb5(%0 : $T)
|
|
bb5(%phi5 : @owned $T):
|
|
%tuple5 = tuple (%phi5 : $T, %2 : $AnyObject)
|
|
%inner5 = enum $InnerEnum<T>, #InnerEnum.payload, %tuple5 : $(T, AnyObject)
|
|
br bb6(%inner5 : $InnerEnum<T>)
|
|
bb6(%phi6 : @owned $InnerEnum<T>):
|
|
%tuple6 = tuple (%phi6 : $InnerEnum<T>, %3 : $AnyObject)
|
|
%outer = enum $OuterEnum<T>, #OuterEnum.inner, %tuple6 : $(InnerEnum<T>, AnyObject)
|
|
return %outer : $OuterEnum<T>
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @f080_testNestedComposeStructWithPhi : $@convention(thin) <T> (@in T, @in T, @owned AnyObject, @owned AnyObject) -> @out OuterStruct<T> {
|
|
// CHECK: bb0(%0 : $*OuterStruct<T>, %1 : $*T, %2 : $*T, %3 : @owned $AnyObject, %4 : @owned $AnyObject):
|
|
// CHECK-NOT: alloc
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: destroy_addr %2 : $*T
|
|
// CHECK: [[INNER1:%.*]] = struct_element_addr %0 : $*OuterStruct<T>, #OuterStruct.inner
|
|
// CHECK: [[T2:%.*]] = struct_element_addr [[INNER1]] : $*InnerStruct<T>, #InnerStruct.t
|
|
// CHECK: copy_addr [take] %1 to [init] [[T2]] : $*T
|
|
// CHECK: [[O2:%.*]] = struct_element_addr [[INNER1]] : $*InnerStruct<T>, #InnerStruct.object
|
|
// CHECK: store %3 to [init] [[O2]] : $*AnyObject
|
|
// CHECK: br bb6
|
|
// CHECK: bb2:
|
|
// CHECK: cond_br undef, bb4, bb3
|
|
// CHECK: bb3:
|
|
// CHECK: destroy_addr %1 : $*T
|
|
// CHECK: [[INNER3:%.*]] = struct_element_addr %0 : $*OuterStruct<T>, #OuterStruct.inner
|
|
// CHECK: [[T3:%.*]] = struct_element_addr [[INNER3]] : $*InnerStruct<T>, #InnerStruct.t
|
|
// CHECK: copy_addr [take] %2 to [init] [[T3]] : $*T
|
|
// CHECK: br bb5
|
|
// CHECK: bb4:
|
|
// CHECK: destroy_addr %2 : $*T
|
|
// CHECK: [[INNER4:%.*]] = struct_element_addr %0 : $*OuterStruct<T>, #OuterStruct.inner
|
|
// CHECK: [[T4:%.*]] = struct_element_addr [[INNER4]] : $*InnerStruct<T>, #InnerStruct.t
|
|
// CHECK: copy_addr [take] %1 to [init] [[T4]] : $*T
|
|
// CHECK: br bb5
|
|
// CHECK: bb5:
|
|
// CHECK: [[INNER5:%.*]] = struct_element_addr %0 : $*OuterStruct<T>, #OuterStruct.inner
|
|
// CHECK: [[O5:%.*]] = struct_element_addr [[INNER5]] : $*InnerStruct<T>, #InnerStruct.object
|
|
// CHECK: store %3 to [init] [[O5]] : $*AnyObject
|
|
// CHECK: br bb6
|
|
// CHECK: bb6:
|
|
// CHECK: [[O6:%.*]] = struct_element_addr %0 : $*OuterStruct<T>, #OuterStruct.object
|
|
// CHECK: store %4 to [init] [[O6]] : $*AnyObject
|
|
// CHECK-NOT: dealloc
|
|
// CHECK-LABEL: } // end sil function 'f080_testNestedComposeStructWithPhi'
|
|
sil [ossa] @f080_testNestedComposeStructWithPhi : $@convention(thin) <T> (@in T, @in T, @owned AnyObject, @owned AnyObject) -> @out OuterStruct<T> {
|
|
bb0(%0 : @owned $T, %1 : @owned $T, %2 : @owned $AnyObject, %3 : @owned $AnyObject):
|
|
cond_br undef, bb2, bb1
|
|
bb1:
|
|
destroy_value %1 : $T
|
|
%inner2 = struct $InnerStruct<T> (%0 : $T, %2 : $AnyObject)
|
|
br bb6(%inner2 : $InnerStruct<T>)
|
|
bb2:
|
|
cond_br undef, bb4, bb3
|
|
bb3:
|
|
destroy_value %0 : $T
|
|
br bb5(%1 : $T)
|
|
bb4:
|
|
destroy_value %1 : $T
|
|
br bb5(%0 : $T)
|
|
bb5(%phi5 : @owned $T):
|
|
%inner5 = struct $InnerStruct<T> (%phi5 : $T, %2 : $AnyObject)
|
|
br bb6(%inner5 : $InnerStruct<T>)
|
|
bb6(%phi6 : @owned $InnerStruct<T>):
|
|
%outer = struct $OuterStruct<T> (%phi6 : $InnerStruct<T>, %3 : $AnyObject)
|
|
return %outer : $OuterStruct<T>
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @f090_payloadPhiOperand : $@convention(thin) <T> (@in Optional<T>, @in T) -> @out T {
|
|
// CHECK: bb0(%0 : $*T, %1 : $*Optional<T>, %2 : $*T):
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb1:
|
|
// CHECK: destroy_addr %2 : $*T
|
|
// CHECK: [[P:%.*]] = unchecked_take_enum_data_addr %1 : $*Optional<T>, #Optional.some!enumelt
|
|
// CHECK: copy_addr [take] [[P]] to [init] %0 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK: bb2:
|
|
// CHECK: destroy_addr %1 : $*Optional<T>
|
|
// CHECK: copy_addr [take] %2 to [init] %0 : $*T
|
|
// CHECK: br bb3
|
|
// CHECK-LABEL: } // end sil function 'f090_payloadPhiOperand'
|
|
sil [ossa] @f090_payloadPhiOperand : $@convention(thin) <T> (@in Optional<T>, @in T) -> @out T {
|
|
bb0(%0 : @owned $Optional<T>, %1 : @owned $T):
|
|
cond_br undef, bb2, bb1
|
|
bb1:
|
|
destroy_value %1 : $T
|
|
%payload = unchecked_enum_data %0 : $Optional<T>, #Optional.some!enumelt
|
|
br bb3(%payload : $T)
|
|
bb2:
|
|
destroy_value %0 : $Optional<T>
|
|
br bb3(%1 : $T)
|
|
bb3(%phi : @owned $T):
|
|
return %phi : $T
|
|
}
|
|
|
|
// Check that the debug_value instruction that is created when deleting a load
|
|
// gets rewritten and doesn't obstruct the deletion of the phi.
|
|
// CHECK-LABEL: sil [ossa] @f100_store_phi : {{.*}} {
|
|
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : $*Value):
|
|
// CHECK: [[TEMP:%[^,]+]] = alloc_stack $Value
|
|
// CHECK: [[VAR:%[^,]+]] = alloc_stack [lexical] $Value, var, name "value"
|
|
// CHECK: cond_br undef, bb2, bb1
|
|
// CHECK: bb3:
|
|
// CHECK: debug_value [[TEMP]] : $*Value, var, name "value"
|
|
// CHECK: copy_addr [take] [[TEMP]] to [init] [[VAR]]
|
|
// CHECK-LABEL: } // end sil function 'f100_store_phi'
|
|
sil [ossa] @f100_store_phi : $@convention(thin) <Value> (@in Value) -> () {
|
|
entry(%instance : @owned $Value):
|
|
%14 = alloc_stack [lexical] $Value, var, name "value"
|
|
cond_br undef, left, right
|
|
|
|
left:
|
|
br merge(%instance : $Value)
|
|
|
|
right:
|
|
br merge(%instance : $Value)
|
|
|
|
merge(%34 : @owned $Value):
|
|
store %34 to [init] %14 : $*Value
|
|
destroy_addr %14 : $*Value
|
|
dealloc_stack %14 : $*Value
|
|
%43 = tuple ()
|
|
return %43 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @f105_phi_into_tuple : {{.*}} {
|
|
// CHECK: [[STORAGE:%[^,]+]] = alloc_stack $(Self, Builtin.Int1)
|
|
// CHECK: [[SELF_ADDR:%[^,]+]] = tuple_element_addr [[STORAGE]]
|
|
// CHECK: apply {{%[^,]+}}<Self>([[SELF_ADDR]])
|
|
// CHECK-LABEL: } // end sil function 'f105_phi_into_tuple'
|
|
sil [ossa] @f105_phi_into_tuple : $@convention(thin) <Self> () -> () {
|
|
%getOut = function_ref @getOut : $@convention(thin) <T> () -> @out T
|
|
%self = apply %getOut<Self>() : $@convention(thin) <τ_0_0> () -> @out τ_0_0
|
|
br exit(%self : $Self)
|
|
|
|
exit(%self_2 : @owned $Self):
|
|
%tuple = tuple (%self_2 : $Self, undef : $Bool)
|
|
destroy_value %tuple : $(Self, Bool)
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|
|
|
|
// Check deleting the first of multiple block arguments.
|
|
// CHECK-LABEL: sil [ossa] @f110_nonfinal_argument : {{.*}} {
|
|
// CHECK: bb3({{%[^,]+}} : $S):
|
|
// CHECK-LABEL: } // end sil function 'f110_nonfinal_argument'
|
|
sil [ossa] @f110_nonfinal_argument : $@convention(thin) <T> (@in T, S) -> () {
|
|
entry(%instance : @owned $T, %s : $S):
|
|
cond_br undef, left, right
|
|
left:
|
|
br exit(%instance : $T, %s : $S)
|
|
right:
|
|
br exit(%instance : $T, %s : $S)
|
|
exit(%instance_2 : @owned $T, %s_2 : $S):
|
|
destroy_value %instance_2 : $T
|
|
%retval = tuple ()
|
|
return %retval : $()
|
|
}
|