Files
swift-mirror/test/SILOptimizer/address_lowering_phi.sil
Erik Eckstein 18063707b5 Optimizer: enable complete OSSA lifetimes throughout the pass pipeline
This new OSSA invariant simplifies many optimizations because they don't have to take care of the corner case of incomplete lifetimes in dead-end blocks.

The implementation basically consists of these changes:
* add the lifetime completion utility
* add a flag in SILFunction which tells optimization that they need to run the lifetime completion utility
* let all optimizations complete lifetimes if necessary
* enable the ownership verifier to check complete lifetimes
2026-01-22 17:41:48 +01:00

1160 lines
44 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -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
protocol Error {}
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 Box<T> {
var t: T
}
struct InnerStruct<T> {
var t: T
var object: AnyObject
}
struct OuterStruct<T> {
var inner: InnerStruct<T>
var object: AnyObject
}
struct Pair<T> {
var x: T
var y: T
}
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
}
// Double use projection without interference.
// CHECK-LABEL: sil [ossa] @f011_testBBArgSelect_useProjection : $@convention(thin) <T> () -> () {
// CHECK: [[STACK:%[^,]+]] = alloc_stack $Optional<T>
// CHECK: cond_br undef, [[RIGHT:bb[0-9]+]], [[LEFT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: [[LEFT_ADDR:%[^,]+]] = init_enum_data_addr [[STACK]]
// CHECK: apply {{%[^,]+}}<T>([[LEFT_ADDR]])
// CHECK: inject_enum_addr [[STACK]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: [[RIGHT_ADDR:%[^,]+]] = init_enum_data_addr [[STACK]]
// CHECK: apply {{%[^,]+}}<T>([[RIGHT_ADDR]])
// CHECK: inject_enum_addr [[STACK]]
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK: destroy_addr [[STACK]]
// CHECK: dealloc_stack [[STACK]]
// CHECK-LABEL: } // end sil function 'f011_testBBArgSelect_useProjection'
sil [ossa] @f011_testBBArgSelect_useProjection : $@convention(thin) <T> () -> () {
entry:
%get = function_ref @getOut : $@convention(thin) <τ_0_0>() -> @out τ_0_0
cond_br undef, left, right
left:
%left = apply %get<T>() : $@convention(thin) <T> () -> (@out T)
%forward = enum $Optional<T>, #Optional.some, %left : $T
br exit(%forward : $Optional<T>)
right:
%right = apply %get<T>() : $@convention(thin) <T> () -> (@out T)
%backward = enum $Optional<T>, #Optional.some, %right : $T
br exit(%backward : $Optional<T>)
exit(%box : @owned $Optional<T>):
destroy_value %box : $Optional<T>
%retval = tuple ()
return %retval : $()
}
// Double use projection + interference.
// CHECK-LABEL: sil [ossa] @f012_testBBArgSelect_useProjection_oneInterference : $@convention(thin) <T> () -> () {
// CHECK: [[LEFT_OUTER:%[^,]+]] = alloc_stack $Optional<T>
// CHECK: [[LEFT_ADDR:%[^,]+]] = init_enum_data_addr [[LEFT_OUTER]]
// CHECK: apply {{%[^,]+}}<T>([[LEFT_ADDR]])
// CHECK: [[PHI_ADDR:%[^,]+]] = alloc_stack $Optional<T>
// CHECK: [[RIGHT_ADDR:%[^,]+]] = init_enum_data_addr [[PHI_ADDR]]
// CHECK: apply {{%[^,]+}}<T>([[RIGHT_ADDR]])
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: destroy_addr [[LEFT_ADDR]]
// CHECK: inject_enum_addr [[PHI_ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: destroy_addr [[RIGHT_ADDR]]
// CHECK: inject_enum_addr [[LEFT_OUTER]]
// CHECK: copy_addr [take] [[LEFT_OUTER]] to [init] [[PHI_ADDR]]
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK: destroy_addr [[PHI_ADDR]]
// CHECK: dealloc_stack [[PHI_ADDR]]
// CHECK: dealloc_stack [[LEFT_OUTER]]
// CHECK-LABEL: } // end sil function 'f012_testBBArgSelect_useProjection_oneInterference'
sil [ossa] @f012_testBBArgSelect_useProjection_oneInterference : $@convention(thin) <T> () -> () {
entry:
%get = function_ref @getOut : $@convention(thin) <τ_0_0>() -> @out τ_0_0
%left = apply %get<T>() : $@convention(thin) <T> () -> (@out T)
%right = apply %get<T>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, left, right
left:
destroy_value %right : $T
%forward = enum $Optional<T>, #Optional.some, %left : $T
br exit(%forward : $Optional<T>)
right:
destroy_value %left : $T
%backward = enum $Optional<T>, #Optional.some, %right : $T
br exit(%backward : $Optional<T>)
exit(%box : @owned $Optional<T>):
destroy_value %box : $Optional<T>
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @f013_testBBArgSelect_useProjections_interference : {{.*}} {
// CHECK: [[S2_ADDR:%[^,]+]] = alloc_stack $Box<T>
// CHECK: [[PHI_ADDR:%[^,]+]] = alloc_stack $Box<T>
// CHECK: [[T_ADDR:%[^,]+]] = struct_element_addr [[PHI_ADDR]] : $*Box<T>, #Box.t
// CHECK: apply undef<T>([[T_ADDR]])
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: br [[MERGE:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: [[S2_FIELD_ADDR:%[^,]+]] = struct_element_addr [[S2_ADDR]] : $*Box<T>, #Box.t
// CHECK: copy_addr [take] [[T_ADDR]] to [init] [[S2_FIELD_ADDR]]
// CHECK: copy_addr [take] [[S2_ADDR]] to [init] [[PHI_ADDR]]
// CHECK: br [[MERGE]]
// CHECK: [[MERGE]]:
// CHECK: apply undef<Box<T>>([[PHI_ADDR]])
// CHECK: destroy_addr [[PHI_ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK: dealloc_stack [[PHI_ADDR]]
// CHECK: dealloc_stack [[S2_ADDR]]
// CHECK-LABEL: } // end sil function 'f013_testBBArgSelect_useProjections_interference'
sil [ossa] @f013_testBBArgSelect_useProjections_interference : $@convention(thin) <T> () -> () {
entry:
%t = apply undef<T>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, left, right
left:
%s2 = struct $Box<T>(%t : $T)
br merge(%s2 : $Box<T>)
right:
%s = struct $Box<T>(%t : $T)
br merge(%s : $Box<T>)
merge(%tb : @owned $Box<T>):
apply undef<Box<T>>(%tb) : $@convention(thin) <T> (@in_guaranteed T) -> ()
destroy_value %tb : $Box<T>
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// 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: [[ALLOC1:%.*]] = alloc_stack $T
// CHECK: copy_addr [take] %0 to [init] [[ALLOC1]] : $*T
// CHECK: [[ALLOC0:%.*]] = alloc_stack $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 [[ALLOC0]] : $*T
// CHECK: dealloc_stack [[ALLOC1]] : $*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: [[B1OADR:%.*]] = struct_element_addr [[ALLOCB]] : $*SRef<T>, #SRef.object
// CHECK: [[B1O:%.*]] = load [take] [[B1OADR]] : $*AnyObject
// CHECK: destroy_value [[B1O]] : $AnyObject
// CHECK: [[CP1:%.*]] = copy_value [[A1O]] : $AnyObject
// CHECK: [[SA1EADR:%.*]] = struct_element_addr [[ALLOCSA]] : $*SRef<T>, #SRef.element
// CHECK: [[A1EADR:%.*]] = struct_element_addr [[ALLOCA]] : $*SRef<T>, #SRef.element
// CHECK: copy_addr [take] [[A1EADR]] to [init] [[SA1EADR]] : $*T
// CHECK: [[SB1EADR:%.*]] = struct_element_addr [[ALLOCSB]] : $*SRef<T>, #SRef.element
// CHECK: [[B1EADR:%.*]] = struct_element_addr [[ALLOCB]] : $*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: [[B2OADR:%.*]] = struct_element_addr [[ALLOCB]] : $*SRef<T>, #SRef.object
// CHECK: [[B2O:%.*]] = load [take] [[B2OADR]] : $*AnyObject
// CHECK: destroy_value [[B2O]] : $AnyObject
// CHECK: [[CP2:%.*]] = copy_value [[A2O]] : $AnyObject
// CHECK: [[SB2EADR:%.*]] = struct_element_addr [[ALLOCSB]] : $*SRef<T>, #SRef.element
// CHECK: [[A2EADR:%.*]] = struct_element_addr [[ALLOCA]] : $*SRef<T>, #SRef.element
// CHECK: copy_addr [take] [[A2EADR]] to [init] [[SB2EADR]] : $*T
// CHECK: [[SA2EADR:%.*]] = struct_element_addr [[ALLOCSA]] : $*SRef<T>, #SRef.element
// CHECK: [[B2EADR:%.*]] = struct_element_addr [[ALLOCB]] : $*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: [[SB3EADR:%.*]] = struct_element_addr [[ALLOCSB]] : $*SRef<T>, #SRef.object
// CHECK: store [[PHI1]] to [init] [[SB3EADR]] : $*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: 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: 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] [[INNER1]] 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]] : {{.*}}, 0
// 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 : $()
}
// Check behavior of non-coalesceable single incoming phi.
// CHECK-LABEL: sil [ossa] @f120_single_phi_argument_forwarded_function_argument : {{.*}} {
// CHECK: bb0([[T:%[^,]+]] :
// CHECK: [[STORAGE:%[^,]+]] = alloc_stack
// CHECK: copy_addr [take] [[T]] to [init] [[STORAGE]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK: destroy_addr [[STORAGE]]
// CHECK: dealloc_stack [[STORAGE]]
// CHECK-LABEL: } // end sil function 'f120_single_phi_argument_forwarded_function_argument'
sil [ossa]@f120_single_phi_argument_forwarded_function_argument : $@convention(thin) <T> (@in T) -> () {
entry(%t : @owned $T):
br exit(%t : $T)
exit(%t2 : @owned $T):
destroy_value %t2 : $T
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @f121_single_phi_argument_forwarded_function_argument_returned : {{.*}} {
// CHECK: bb0([[OUT:%[^,]+]] :
// CHECK-SAME: [[IN:%[^,]+]] :
// CHECK: copy_addr [take] [[IN]] to [init] [[OUT]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'f121_single_phi_argument_forwarded_function_argument_returned'
sil [ossa]@f121_single_phi_argument_forwarded_function_argument_returned : $@convention(thin) <T> (@in T) -> @out T {
entry(%t : @owned $T):
br exit(%t : $T)
exit(%t2 : @owned $T):
return %t2 : $T
}
// CHECK-LABEL: sil [ossa] @f122_single_phi_argument_destructure : $@convention(thin) <T> () -> () {
// CHECK: [[PHI_ADDR:%[^,]+]] = alloc_stack $T
// CHECK: [[BOX_ADDR:%[^,]+]] = alloc_stack $Box<T>
// CHECK: apply undef<T>([[BOX_ADDR]])
// CHECK: [[T_ADDR:%[^,]+]] = struct_element_addr [[BOX_ADDR]] : $*Box<T>, #Box.t
// CHECK: copy_addr [take] [[T_ADDR]] to [init] [[PHI_ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK: destroy_addr [[PHI_ADDR]]
// CHECK: dealloc_stack [[BOX_ADDR]]
// CHECK: dealloc_stack [[PHI_ADDR]]
// CHECK-LABEL: } // end sil function 'f122_single_phi_argument_destructure'
sil [ossa]@f122_single_phi_argument_destructure : $@convention(thin) <T> () -> () {
entry:
%box = apply undef<T>() : $@convention(thin) <T> () -> (@out Box<T>)
%t = destructure_struct %box : $Box<T>
br exit(%t : $T)
exit(%t2 : @owned $T):
destroy_value %t2 : $T
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @f130_single_phi_use_of_phi {{.*}} {
// CHECK: [[ADDR:%[^,]+]] = alloc_stack $Box<T>
// CHECK: [[T_ADDR:%[^,]+]] = struct_element_addr [[ADDR]] : $*Box<T>, #Box.t
// CHECK: apply undef<T>([[T_ADDR]])
// CHECK: br [[FAKE_MERGE1:bb[0-9]+]]
// CHECK: [[FAKE_MERGE1]]:
// CHECK: [[T_ADDR_2:%[^,]+]] = struct_element_addr [[ADDR]] : $*Box<T>, #Box.t
// CHECK: apply undef<T>([[T_ADDR_2]])
// CHECK: br [[FAKE_MERGE2:bb[0-9]+]]
// CHECK: [[FAKE_MERGE2]]:
// CHECK: apply undef<Box<T>>([[ADDR]])
// CHECK: destroy_addr [[ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK: dealloc_stack [[ADDR]]
// CHECK-LABEL: } // end sil function 'f130_single_phi_use_of_phi'
sil [ossa] @f130_single_phi_use_of_phi : $@convention(thin) <T> () -> () {
entry:
%t = apply undef<T>() : $@convention(thin) <T> () -> (@out T)
br fakeMerge1(%t : $T)
fakeMerge1(%tp : @owned $T):
apply undef<T>(%tp) : $@convention(thin) <T> (@in_guaranteed T) -> ()
%box = struct $Box<T>(%tp : $T)
br fakeMerge2(%box : $Box<T>)
fakeMerge2(%boxp : @owned $Box<T>):
apply undef<Box<T>>(%boxp) : $@convention(thin) <T> (@in_guaranteed T) -> ()
destroy_value %boxp : $Box<T>
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @f131_single_phi_use_of_phi_interference : {{.*}} {
// CHECK: [[BOXP_ADDR:%[^,]+]] = alloc_stack $Box<T>
// CHECK: [[T_ADDR:%[^,]+]] = struct_element_addr [[BOXP_ADDR]] : $*Box<T>, #Box.t
// CHECK: apply undef<T>([[T_ADDR]])
// CHECK: br [[FAKE_MERGE:bb[0-9]+]]
// CHECK: [[FAKE_MERGE]]:
// CHECK: [[T_ADDR_2:%[^,]+]] = struct_element_addr [[BOXP_ADDR]] : $*Box<T>, #Box.t
// CHECK: apply undef<T>([[T_ADDR_2]])
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: [[BOX_2_ADDR:%[^,]+]] = alloc_stack $Box<T>
// CHECK: apply undef<Box<T>>([[BOX_2_ADDR]])
// CHECK: destroy_addr [[BOXP_ADDR]]
// CHECK: copy_addr [take] [[BOX_2_ADDR]] to [init] [[BOXP_ADDR]]
// CHECK: dealloc_stack [[BOX_2_ADDR]]
// CHECK: br [[MERGE:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: br [[MERGE]]
// CHECK: [[MERGE]]:
// CHECK: apply undef<Box<T>>([[BOXP_ADDR]])
// CHECK: destroy_addr [[BOXP_ADDR]]
// CHECK: dealloc_stack [[BOXP_ADDR]]
// CHECK-LABEL: } // end sil function 'f131_single_phi_use_of_phi_interference'
sil [ossa] @f131_single_phi_use_of_phi_interference : $@convention(thin) <T> () -> () {
entry:
%t = apply undef<T>() : $@convention(thin) <T> () -> (@out T)
br fakeMerge1(%t : $T)
fakeMerge1(%tp : @owned $T):
apply undef<T>(%tp) : $@convention(thin) <T> (@in_guaranteed T) -> ()
%box = struct $Box<T>(%tp : $T)
cond_br undef, left, right
right:
%box2 = apply undef<Box<T>>() : $@convention(thin) <T> () -> (@out T)
destroy_value %box : $Box<T>
br merge2(%box2 : $Box<T>)
left:
br merge2(%box : $Box<T>)
merge2(%boxp : @owned $Box<T>):
apply undef<Box<T>>(%boxp) : $@convention(thin) <T> (@in_guaranteed T) -> ()
destroy_value %boxp : $Box<T>
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @f140_interfering_use_projection : {{.*}} {
// CHECK: [[S2_ADDR:%[^,]+]] = alloc_stack $Pair<T>
// CHECK: [[T2_ADDR:%[^,]+]] = struct_element_addr [[S2_ADDR]] : $*Pair<T>, #Pair.y
// CHECK: apply undef<T>([[T2_ADDR]])
// CHECK: [[PHI_ADDR:%[^,]+]] = alloc_stack $Pair<T>
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: apply undef<Pair<T>>([[PHI_ADDR]])
// CHECK: destroy_addr [[T2_ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: [[T1_ADDR:%[^,]+]] = struct_element_addr [[S2_ADDR]] : $*Pair<T>, #Pair.x
// CHECK: apply undef<T>([[T1_ADDR]])
// CHECK: copy_addr [take] [[S2_ADDR]] to [init] [[PHI_ADDR]]
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK: destroy_addr [[PHI_ADDR]]
// CHECK: dealloc_stack [[PHI_ADDR]]
// CHECK: dealloc_stack [[S2_ADDR]]
// CHECK-LABEL: } // end sil function 'f140_interfering_use_projection'
sil [ossa] @f140_interfering_use_projection : $@convention(thin) <T> () -> () {
entry:
%t2 = apply undef<T>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, left, right
left:
%t1 = apply undef<T>() : $@convention(thin) <T> () -> (@out T)
%s2 = struct $Pair<T>(%t1 : $T, %t2 : $T)
br merge(%s2 : $Pair<T>)
right:
%s1 = apply undef<Pair<T>>() : $@convention(thin) <T> () -> (@out T)
destroy_value %t2 : $T
br merge(%s1 : $Pair<T>)
merge(%sp : @owned $Pair<T>):
destroy_value %sp : $Pair<T>
%retval = tuple ()
return %retval : $()
}
// Incoming value is the root of a chain of projections:
// storage <- use <- use <- def
// ^^^ ^^^ ^^^
// %s projects out of %s2's storage
// ^^^ ^^^
// %box projects out of %s's storage
// ^^^
// %t projects out of %box's storage
// There is no interference (and only one incoming phi.)
// CHECK-LABEL: sil [ossa] @f150_incoming_use_use_def_no_interference : {{.*}} {
// CHECK: [[PHI_ADDR:%[^,]+]] = alloc_stack $Box<Box<Box<T>>>
// CHECK: [[S_ADDR:%[^,]+]] = struct_element_addr [[PHI_ADDR]] : $*Box<Box<Box<T>>>, #Box.t
// CHECK: [[BOX_ADDR:%[^,]+]] = struct_element_addr [[S_ADDR]] : $*Box<Box<T>>, #Box.t
// CHECK: apply undef<Box<T>>([[BOX_ADDR]])
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: br [[FAKE_MERGE:bb[0-9]+]]
// CHECK: [[FAKE_MERGE]]:
// CHECK: apply undef<Box<Box<Box<T>>>>([[PHI_ADDR]])
// CHECK: destroy_addr [[PHI_ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: [[T_ADDR:%[^,]+]] = struct_element_addr [[BOX_ADDR]] : $*Box<T>, #Box.t
// CHECK: destroy_addr [[T_ADDR]]
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK: dealloc_stack [[PHI_ADDR]]
// CHECK-LABEL: } // end sil function 'f150_incoming_use_use_def_no_interference'
sil [ossa] @f150_incoming_use_use_def_no_interference : $@convention(thin) <T> () -> () {
entry:
%box = apply undef<Box<T>>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, left, right
left:
%t = destructure_struct %box : $Box<T>
destroy_value %t : $T
br exit
right:
%s = struct $Box<Box<T>>(%box : $Box<T>)
%s2 = struct $Box<Box<Box<T>>>(%s : $Box<Box<T>>)
br fakeMerge(%s2 : $Box<Box<Box<T>>>)
fakeMerge(%tb : @owned $Box<Box<Box<T>>>):
apply undef<Box<Box<Box<T>>>>(%tb) : $@convention(thin) <T> (@in_guaranteed T) -> ()
destroy_value %tb : $Box<Box<Box<T>>>
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// Incoming value is the root of a chain of projections:
// storage <- use <- use <- def
// ^^^ ^^^ ^^^
// %s projects out of %s2's storage
// ^^^ ^^^
// %box projects out of %s's storage
// ^^^
// %t projects out of %box's storage
// There is interference of the _def_ projection (which can only be detected by
// adding its uses to liveness:
// %t is destroyed in ^left2
// %s3 is defined in ^left2
// TODO: When doing per-instruction rather than per-block liveness, this test
// will no longer have interference. At that point, move the
// destroy_value %t below the subsequent apply to reintroduce interference
// and duplicate this test.
// CHECK-LABEL: sil [ossa] @f160_incoming_use_use_def_interference : {{.*}} {
// CHECK: [[PHI_ADDR:%[^,]+]] = alloc_stack $Box<Box<Box<T>>>
// CHECK: [[S_ADDR:%[^,]+]] = struct_element_addr [[PHI_ADDR]] : $*Box<Box<Box<T>>>, #Box.t
// CHECK: [[BOX_ADDR:%[^,]+]] = struct_element_addr [[S_ADDR]] : $*Box<Box<T>>, #Box.t
// CHECK: apply undef<Box<T>>([[BOX_ADDR]])
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: br [[MERGE:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: br [[LEFT2:bb[0-9]+]]
// CHECK: [[LEFT2]]:
// CHECK: [[S3_ADDR:%[^,]+]] = alloc_stack $Box<Box<Box<T>>>
// CHECK: [[T_ADDR:%[^,]+]] = struct_element_addr [[BOX_ADDR]] : $*Box<T>, #Box.t
// CHECK: destroy_addr [[T_ADDR]]
// CHECK: apply undef<Box<Box<Box<T>>>>([[S3_ADDR]])
// CHECK: copy_addr [take] [[S3_ADDR]] to [init] [[PHI_ADDR]] : $*Box<Box<Box<T>>>
// CHECK: dealloc_stack [[S3_ADDR]]
// CHECK: br [[MERGE:bb[0-9]+]]
// CHECK: [[MERGE]]:
// CHECK: apply undef<Box<Box<Box<T>>>>([[PHI_ADDR]])
// CHECK: destroy_addr [[PHI_ADDR]]
// CHECK: dealloc_stack [[PHI_ADDR]]
// CHECK-LABEL: } // end sil function 'f160_incoming_use_use_def_interference'
sil [ossa] @f160_incoming_use_use_def_interference : $@convention(thin) <T> () -> () {
entry:
%box = apply undef<Box<T>>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, left, right
left:
%t = destructure_struct %box : $Box<T>
br left2
left2:
destroy_value %t : $T
%s3 = apply undef<Box<Box<Box<T>>>>() : $@convention(thin) <T> () -> (@out T)
br merge(%s3 : $Box<Box<Box<T>>>)
right:
%s = struct $Box<Box<T>>(%box : $Box<T>)
%s2 = struct $Box<Box<Box<T>>>(%s : $Box<Box<T>>)
br merge(%s2 : $Box<Box<Box<T>>>)
merge(%tb : @owned $Box<Box<Box<T>>>):
apply undef<Box<Box<Box<T>>>>(%tb) : $@convention(thin) <T> (@in_guaranteed T) -> ()
destroy_value %tb : $Box<Box<Box<T>>>
%retval = tuple ()
return %retval : $()
}
// Incoming phi.
// TODO: Coalesce in this case.
// CHECK-LABEL: sil [ossa] @f170_incoming_phi_no_interference : {{.*}} {
// CHECK: [[T_ADDR:%[^,]+]] = alloc_stack $T
// CHECK: apply undef<T>([[T_ADDR]])
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: br [[FAKE_MERGE_1:bb[0-9]+]]
// CHECK: [[FAKE_MERGE_1]]:
// CHECK: [[TP_ADDR:%[^,]+]] = alloc_stack $T
// CHECK: apply undef<T>([[T_ADDR]])
// CHECK: copy_addr [take] [[T_ADDR]] to [init] [[TP_ADDR]]
// CHECK: br [[FAKE_MERGE_2:bb[0-9]+]]
// CHECK: [[FAKE_MERGE_2]]:
// CHECK: apply undef<T>([[TP_ADDR]])
// CHECK: destroy_addr [[TP_ADDR]]
// CHECK: dealloc_stack [[TP_ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: destroy_addr [[T_ADDR]]
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK: dealloc_stack [[T_ADDR]]
// CHECK-LABEL: } // end sil function 'f170_incoming_phi_no_interference'
sil [ossa] @f170_incoming_phi_no_interference : $@convention(thin) <T> () -> () {
entry:
%t = apply undef<T>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, left, right
left:
destroy_value %t : $T
br exit
right:
br fakeMerge1(%t : $T)
fakeMerge1(%tp : @owned $T):
apply undef<T>(%tp) : $@convention(thin) <T> (@in_guaranteed T) -> ()
br fakeMerge2(%tp : $T)
fakeMerge2(%tpp : @owned $T):
apply undef<T>(%tpp) : $@convention(thin) <T> (@in_guaranteed T) -> ()
destroy_value %tpp : $T
br exit
exit:
%retval = tuple ()
return %retval : $()
}
// CHECK-LABEL: sil [ossa] @f180_incoming_terminator_results : {{.*}} {
// CHECK: bb0(%0 :
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: try_apply undef<T>(%0)
// CHECK-SAME: normal [[RIGHT_SUCCESS:bb[0-9]+]], error [[RIGHT_FAILURE:bb[0-9]+]]
// CHECK: [[RIGHT_FAILURE]]({{%[^,]+}} : @owned $any Error):
// CHECK: unreachable
// CHECK: [[RIGHT_SUCCESS]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[LEFT]]:
// CHECK: try_apply undef<T>(%0)
// CHECK-SAME: normal [[LEFT_SUCCESS:bb[0-9]+]], error [[LEFT_FAILURE:bb[0-9]+]]
// CHECK: [[LEFT_FAILURE]]({{%[^,]+}} : @owned $any Error):
// CHECK: unreachable
// CHECK: [[LEFT_SUCCESS]]
// CHECK: br [[EXIT]]
// CHECK: [[EXIT]]:
// CHECK-LABEL: } // end sil function 'f180_incoming_terminator_results'
sil [ossa] @f180_incoming_terminator_results : $@convention(thin) <T> () -> (@out T) {
entry:
cond_br undef, left, right
left:
try_apply undef<T>() : $@convention(thin) <T> () -> (@out T, @error any Error), normal left_success, error left_failure
left_success(%lt : @owned $T):
br exit(%lt : $T)
left_failure(%lerror : @owned $any Error):
destroy_value [dead_end] %lerror
unreachable
right:
try_apply undef<T>() : $@convention(thin) <T> () -> (@out T, @error any Error), normal right_success, error right_failure
right_success(%rt : @owned $T):
br exit(%rt : $T)
exit(%tp : @owned $T):
return %tp : $T
right_failure(%rerror : @owned $any Error):
destroy_value [dead_end] %rerror
unreachable
}
// Verify that LCA for `%agg` is correctly determined to be `parent` so that the
// backwards walk when determining the liveness contribution from `%v` stops at
// `%trouble2`, that there is only a single `alloc_stack`.
// CHECK-LABEL: sil [ossa] @f190_vexing_incoming_def_projection : {{.*}} {
// CHECK: [[P_ADDR:%[^,]+]] = alloc_stack $Box<Box<T>>
// CHECK-NOT: alloc_stack
// CHECK: apply undef<Box<Box<T>>>([[P_ADDR]])
// CHECK: cond_br undef, [[OKAY:bb[0-9]+]], [[TROUBLE:bb[0-9]+]]
// CHECK: [[TROUBLE]]:
// CHECK: destroy_addr [[P_ADDR]]
// CHECK: br [[TROUBLE2:bb[0-9]+]]
// CHECK: [[TROUBLE2]]:
// CHECK: [[V_ADDR:%[^,]+]] = struct_element_addr [[P_ADDR]]
// CHECK: apply undef<Box<T>>([[V_ADDR]])
// CHECK: cond_br undef, [[PARENT:bb[0-9]+]], [[OTHER:bb[0-9]+]]
// CHECK: [[OTHER]]:
// CHECK: destroy_addr [[V_ADDR]]
// CHECK: br [[OTHER2:bb[0-9]+]]
// CHECK: [[OTHER2]]:
// CHECK: apply undef<Box<Box<T>>>([[P_ADDR]])
// CHECK: br [[MERGE:bb[0-9]+]]
// CHECK: [[PARENT]]:
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]], [[RIGHT:bb[0-9]+]]
// CHECK: [[RIGHT]]:
// CHECK: br [[MERGE]]
// CHECK: [[LEFT]]:
// CHECK: [[T_ADDR:%[^,]+]] = struct_element_addr [[V_ADDR]]
// CHECK: destroy_addr [[T_ADDR]]
// CHECK: br [[LEFT2:bb[0-9]+]]
// CHECK: [[LEFT2]]:
// CHECK: apply undef<Box<Box<T>>>([[P_ADDR]])
// CHECK: br [[MERGE]]
// CHECK: [[OKAY]]:
// CHECK: br [[MERGE]]
// CHECK: [[MERGE]]:
// CHECK: destroy_addr [[P_ADDR]]
// CHECK: br [[EXIT:bb[0-9]+]]
// CHECK: [[EXIT]]:
// CHECK: dealloc_stack [[P_ADDR]]
// CHECK-LABEL: } // end sil function 'f190_vexing_incoming_def_projection'
sil [ossa] @f190_vexing_incoming_def_projection : $@convention(thin) <T> () -> () {
entry:
%agg4 = apply undef<Box<Box<T>>>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, okay, trouble
okay:
br merge(%agg4 : $Box<Box<T>>)
trouble:
destroy_value %agg4 : $Box<Box<T>>
br trouble2
trouble2:
%v = apply undef<Box<T>>() : $@convention(thin) <T> () -> (@out T)
cond_br undef, parent, other
parent:
cond_br undef, left, right
left:
%t = destructure_struct %v : $Box<T>
destroy_value %t : $T
br left2
left2:
%agg2 = apply undef<Box<Box<T>>>() : $@convention(thin) <T> () -> (@out T)
br merge(%agg2 : $Box<Box<T>>)
right:
%agg = struct $Box<Box<T>>(%v : $Box<T>)
br merge(%agg : $Box<Box<T>>)
other:
destroy_value %v : $Box<T>
br other2
other2:
%agg3 = apply undef<Box<Box<T>>>() : $@convention(thin) <T> () -> (@out T)
br merge(%agg3 : $Box<Box<T>>)
merge(%p : @owned $Box<Box<T>>):
destroy_value %p : $Box<Box<T>>
br exit
exit:
%retval = tuple ()
return %retval : $()
}