blob: 26943f8c5442b9f6e2e73b6214dca0097a461f46 [file] [log] [blame]
// RUN: %target-sil-opt -enable-sil-verify-all %s -mem2reg | %FileCheck %s
import Builtin
import Swift
//////////////////
// Declarations //
//////////////////
class Klass {}
struct SmallCodesizeStruct {
var cls1 : Klass
var cls2 : Klass
}
struct LargeCodesizeStruct {
var s1: SmallCodesizeStruct
var s2: SmallCodesizeStruct
var s3: SmallCodesizeStruct
var s4: SmallCodesizeStruct
var s5: SmallCodesizeStruct
}
///////////
// Tests //
///////////
// CHECK-LABEL: sil [ossa] @store_only_allocas :
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'store_only_allocas'
// simple.foo0 (c : Swift.Int64) -> ()
sil [ossa] @store_only_allocas : $@convention(thin) (Int64) -> () {
bb0(%0 : $Int64):
%1 = alloc_stack $Int64, var, name "c"
store %0 to [trivial] %1 : $*Int64
// function_ref Swift.print (val : Swift.Int64) -> ()
%3 = function_ref @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> ()
%4 = apply %3(%0) : $@convention(thin) (Int64) -> ()
dealloc_stack %1 : $*Int64
%6 = tuple ()
return %6 : $()
}
// Swift.print (val : Swift.Int64) -> ()
sil [ossa] @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> ()
// CHECK-LABEL: sil [ossa] @multiple_store_vals :
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'multiple_store_vals'
// simple.foo1 (c : Swift.Int64) -> Swift.Int64
sil [ossa] @multiple_store_vals : $@convention(thin) (Int64) -> Int64 {
bb0(%0 : $Int64):
%1 = alloc_stack $Int64, var, name "c"
store %0 to [trivial] %1 : $*Int64
%3 = alloc_stack $Int64, var, name "x"
%4 = integer_literal $Builtin.Int64, 2
%5 = struct $Int64 (%4 : $Builtin.Int64)
store %5 to [trivial] %3 : $*Int64
%7 = integer_literal $Builtin.Int64, 5
%8 = integer_literal $Builtin.Int1, 0
%9 = struct $Int64 (%7 : $Builtin.Int64)
cond_fail %8 : $Builtin.Int1
store %9 to [trivial] %3 : $*Int64
store %9 to [trivial] %3 : $*Int64
dealloc_stack %3 : $*Int64
dealloc_stack %1 : $*Int64
return %9 : $Int64
}
// CHECK-LABEL: sil [ossa] @multiple_store_vals2 :
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'multiple_store_vals2'
// simple.foo2 (c : Swift.Int64) -> Swift.Int64
sil [ossa] @multiple_store_vals2 : $@convention(thin) (Int64) -> Int64 {
bb0(%0 : $Int64):
%1 = alloc_stack $Int64, var, name "c"
store %0 to [trivial] %1 : $*Int64
%3 = alloc_box $_0_0> { var τ_0_0 } <Int64>, var, name "x"
%3a = project_box %3 : $_0_0> { var τ_0_0 } <Int64>, 0
%4 = integer_literal $Builtin.Int64, 2
%5 = struct $Int64 (%4 : $Builtin.Int64)
store %5 to [trivial] %3a : $*Int64
%8 = struct_extract %0 : $Int64, #Int64._value
%9 = builtin "cmp_sgt_Int64"(%8 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1
cond_br %9, bb1, bb2
bb1:
destroy_value %3 : $_0_0> { var τ_0_0 } <Int64>
br bb3(%5 : $Int64)
bb2:
%13 = integer_literal $Builtin.Int64, 5
%14 = struct $Int64 (%13 : $Builtin.Int64)
cond_fail %9 : $Builtin.Int1
destroy_value %3 : $_0_0> { var τ_0_0 } <Int64>
br bb3(%14 : $Int64)
bb3(%18 : $Int64):
dealloc_stack %1 : $*Int64
return %18 : $Int64
}
// CHECK-LABEL: sil [ossa] @with_loads :
// CHECK: bb3([[RET:%[0-9]+]] : $Int64):
// CHECK-LABEL: } // end sil function 'with_loads'
// simple.foo2 (c : Swift.Int64) -> Swift.Int64
sil [ossa] @with_loads : $@convention(thin) (Int64) -> Int64 {
bb0(%0 : $Int64):
%1 = alloc_stack $Int64, var, name "c"
store %0 to [trivial] %1 : $*Int64
%3 = alloc_box $_0_0> { var τ_0_0 } <Int64>, var, name "x"
%3a = project_box %3 : $_0_0> { var τ_0_0 } <Int64>, 0
%4 = integer_literal $Builtin.Int64, 2
%5 = struct $Int64 (%4 : $Builtin.Int64)
store %5 to [trivial] %3a : $*Int64
%8 = struct_extract %0 : $Int64, #Int64._value
%9 = builtin "cmp_sgt_Int64"(%8 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1
cond_br %9, bb1, bb2
bb1:
destroy_value %3 : $_0_0> { var τ_0_0 } <Int64>
br bb3(%5 : $Int64)
bb2:
%13 = integer_literal $Builtin.Int64, 5
%14 = struct $Int64 (%13 : $Builtin.Int64)
cond_fail %9 : $Builtin.Int1
destroy_value %3 : $_0_0> { var τ_0_0 } <Int64>
br bb3(%14 : $Int64)
bb3(%18 : $Int64):
dealloc_stack %1 : $*Int64
%20 = load [trivial] %1 : $*Int64
return %18 : $Int64
}
// CHECK-LABEL: sil [ossa] @basic_block_with_loads_and_stores :
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'basic_block_with_loads_and_stores'
// test.foo3 (c : Swift.Int64) -> ()
sil [ossa] @basic_block_with_loads_and_stores : $@convention(thin) (Int64) -> () {
bb0(%0 : $Int64):
%1 = alloc_stack $Int64, var, name "c"
store %0 to [trivial] %1 : $*Int64
%3 = alloc_stack $Int64, var, name "x"
%4 = integer_literal $Builtin.Int64, 3
%5 = struct $Int64 (%4 : $Builtin.Int64)
store %5 to [trivial] %3 : $*Int64
%7 = integer_literal $Builtin.Int64, 3
%9 = struct_extract %0 : $Int64, #Int64._value
%10 = builtin "cmp_sgt_Int64"(%9 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
%12 = integer_literal $Builtin.Int64, 2
%13 = struct $Int64 (%12 : $Builtin.Int64)
store %13 to [trivial] %3 : $*Int64
// function_ref Swift.print (val : Swift.Int64) -> ()
%16 = function_ref @_Ts5printFT3valSi_T_ : $@convention(thin) (Int64) -> ()
%17 = load [trivial] %3 : $*Int64
%18 = apply %16(%17) : $@convention(thin) (Int64) -> ()
dealloc_stack %3 : $*Int64
dealloc_stack %1 : $*Int64
%21 = tuple ()
return %21 : $()
}
// CHECK-LABEL: sil [ossa] @load_uninitialized_empty :
// CHECK-NOT: load
// CHECK-LABEL: } // end sil function 'load_uninitialized_empty'
sil [ossa] @load_uninitialized_empty : $@convention(thin) (@inout ()) -> () {
bb0(%0 : $*()):
%1 = alloc_stack $()
%2 = load [trivial] %1 : $*()
store %2 to [trivial] %0 : $*()
dealloc_stack %1 : $*()
%3 = tuple ()
return %3 : $()
}
// CHECK-LABEL: sil [ossa] @mem2reg_debug_value_addr :
// CHECK-NOT: alloc_stack
// CHECK-NOT: debug_value_addr
// CHECK: debug_value %0
// CHECK-LABEL: } // end sil function 'mem2reg_debug_value_addr'
sil [ossa] @mem2reg_debug_value_addr : $@convention(thin) (Int) -> Int {
bb0(%0 : $Int):
%1 = alloc_stack $Int
store %0 to [trivial] %1 : $*Int
debug_value_addr %1 : $*Int
%2 = load [trivial] %1 : $*Int
dealloc_stack %1 : $*Int
return %2 : $Int
}
// CHECK-LABEL: sil [ossa] @mem2reg_struct_addr :
// CHECK-NOT: alloc_stack
// CHECK: struct_extract
// CHECK-LABEL: } // end sil function 'mem2reg_struct_addr'
sil [ossa] @mem2reg_struct_addr : $@convention(thin) (Int64) -> Builtin.Int64 {
bb0(%0 : $Int64):
%1 = alloc_stack $Int64
store %0 to [trivial] %1 : $*Int64
%2 = struct_element_addr %1 : $*Int64, #Int64._value
%3 = load [trivial] %2 : $*Builtin.Int64
dealloc_stack %1 : $*Int64
return %3 : $Builtin.Int64
}
// CHECK-LABEL: sil [ossa] @mem2reg_tuple_addr :
// CHECK-NOT: alloc_stack
// CHECK: tuple_extract {{.*}}, 0
// CHECK-LABEL: } // end sil function 'mem2reg_tuple_addr'
sil [ossa] @mem2reg_tuple_addr : $@convention(thin) (Int64) -> Int64 {
bb0(%0 : $Int64):
%1 = alloc_stack $(Int64, Int64)
%2 = tuple (%0 : $Int64, %0 : $Int64)
store %2 to [trivial] %1 : $*(Int64, Int64)
%4 = tuple_element_addr %1 : $*(Int64, Int64), 0
%5 = load [trivial] %4 : $*Int64
dealloc_stack %1 : $*(Int64, Int64)
return %5 : $Int64
}
// CHECK-LABEL: sil [ossa] @struct_extract_if_then_else :
// CHECK-NOT: alloc_stack
sil [ossa] @struct_extract_if_then_else : $@convention(thin) (Int64) -> Int64 {
bb0(%0 : $Int64):
%1 = alloc_stack $Int64
store %0 to [trivial] %1 : $*Int64
%3 = integer_literal $Builtin.Int64, 2
%4 = struct_extract %0 : $Int64, #Int64._value
%5 = builtin "cmp_sgt_Int64"(%4 : $Builtin.Int64, %3 : $Builtin.Int64) : $Builtin.Int1
%6 = struct_element_addr %1 : $*Int64, #Int64._value
cond_br %5, bb1, bb2
// CHECK: bb1:
// CHECK: struct_extract %0
bb1:
%8 = load [trivial] %6 : $*Builtin.Int64
%9 = integer_literal $Builtin.Int64, 1
%10 = integer_literal $Builtin.Int1, 0
%11 = builtin "sadd_with_overflow_Int64"(%8 : $Builtin.Int64, %9 : $Builtin.Int64, %10 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%12 = tuple_extract %11 : $(Builtin.Int64, Builtin.Int1), 0
br bb3(%12 : $Builtin.Int64)
// CHECK: bb2:
// CHECK: struct_extract %0
bb2:
%14 = load [trivial] %6 : $*Builtin.Int64
%15 = integer_literal $Builtin.Int64, 2
%16 = integer_literal $Builtin.Int1, 0
%17 = builtin "sadd_with_overflow_Int64"(%14 : $Builtin.Int64, %15 : $Builtin.Int64, %16 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%18 = tuple_extract %17 : $(Builtin.Int64, Builtin.Int1), 0
br bb3(%18 : $Builtin.Int64)
// CHECK-NOT: dealloc_stack
bb3(%20 : $Builtin.Int64):
dealloc_stack %1 : $*Int64
%22 = struct $Int64 (%20 : $Builtin.Int64)
return %22 : $Int64
}
// CHECK-LABEL: } // end sil function 'struct_extract_if_then_else'
sil [ossa] @first : $@convention(thin) () -> Int
sil [ossa] @second : $@convention(thin) () -> Int
// CHECK: sil [ossa] @promote_function_refs :
sil [ossa] @promote_function_refs : $@convention(thin) (Bool) -> Int {
// CHECK: bb0
bb0(%0 : $Bool):
// CHECK-NOT: [[STACK:%.*]] = alloc_stack
%1 = alloc_stack $@callee_owned () -> Int
debug_value %0 : $Bool
%3 = struct_extract %0 : $Bool, #Bool._value
cond_br %3, bb1, bb2
// CHECK: bb1
bb1:
// CHECK: [[FIRSTREF:%.*]] = function_ref @first
%5 = function_ref @first : $@convention(thin) () -> Int
// CHECK: [[FIRSTTHICK:%.*]] = thin_to_thick_function [[FIRSTREF]]
%6 = thin_to_thick_function %5 : $@convention(thin) () -> Int to $@callee_owned () -> Int
// CHECK-NOT: store
store %6 to [init] %1 : $*@callee_owned () -> Int
// CHECK: br bb3([[FIRSTTHICK]] : $@callee_owned () -> Int
br bb3
// CHECK: bb2
bb2:
// CHECK: [[SECONDREF:%.*]] = function_ref @second
%9 = function_ref @second : $@convention(thin) () -> Int
// CHECK: [[SECONDTHICK:%.*]] = thin_to_thick_function [[SECONDREF]]
%10 = thin_to_thick_function %9 : $@convention(thin) () -> Int to $@callee_owned () -> Int
// CHECK-NOT: store
store %10 to [init] %1 : $*@callee_owned () -> Int
// CHECK: br bb3([[SECONDTHICK]] : $@callee_owned () -> Int)
br bb3
// CHECK: bb3([[ARG:%.*]] : @owned $@callee_owned () -> Int):
bb3:
// CHECK-NOT: load [[STACK]]
%13 = load [copy] %1 : $*@callee_owned () -> Int
// CHECK: [[COPY:%.*]] = copy_value [[ARG]]
// CHECK: [[RESULT:%.*]] = apply [[COPY]]
%15 = apply %13() : $@callee_owned () -> Int
br bb4
// NOTE: This block and the branch above exist to ensure that we
// test what happens when %1 hasn't already been loaded in this
// block.
// CHECK: bb4
bb4:
// CHECK-NOT: destroy_addr [[STACK]]
// CHECK: destroy_value [[ARG]]
destroy_addr %1 : $*@callee_owned () -> Int
// CHECK-NOT: dealloc_stack [[STACK]]
dealloc_stack %1 : $*@callee_owned () -> Int
return %15 : $Int
}
// CHECK-LABEL: } // end sil function 'promote_function_refs'
// Test cases where the only use is a debug_value_addr
// CHECK-LABEL: sil [ossa] @no_real_uses :
sil [ossa] @no_real_uses : $@convention(thin) () -> () {
// CHECK: bb0
bb0:
// CHECK-NOT: alloc_stack
%0 = alloc_stack $Builtin.Int32
// CHECK-NOT: debug_value_addr
debug_value_addr %0 : $*Builtin.Int32, let, name "x", argno 1
// CHECK-NOT: dealloc_stack
dealloc_stack %0 : $*Builtin.Int32
%1 = tuple ()
return %1 : $()
}
// CHECK-LABEL: } // end sil function 'no_real_uses'
// CHECK-LABEL: sil [ossa] @keep_release :
// CHECK: destroy_value %0
// CHECK-LABEL: } // end sil function 'keep_release'
sil [ossa] @keep_release : $@convention(thin) (@owned AnyObject) -> () {
bb0(%0 : @owned $AnyObject):
%1 = alloc_stack $AnyObject
store %0 to [init] %1 : $*AnyObject
destroy_addr %1 : $*AnyObject
dealloc_stack %1 : $*AnyObject
%7 = tuple ()
return %7 : $()
}
// Test cases where there are dead address instructions.
// CHECK-LABEL: sil [ossa] @dead_use :
// CHECK-NOT: alloc_stack
// CHECK-LABEL: } // end sil function 'dead_use'
sil [ossa] @dead_use : $@convention(thin) () -> () {
%0 = alloc_stack $Int64
%1 = struct_element_addr %0 : $*Int64, #Int64._value
dealloc_stack %0 : $*Int64
%2 = alloc_stack $(Int64, Int64)
%3 = tuple_element_addr %2 : $*(Int64, Int64), 0
dealloc_stack %2 : $*(Int64, Int64)
%4 = tuple ()
return %4 : $()
}
// CHECK-LABEL: sil [ossa] @dont_crash_on_dead_arg_use :
// CHECK: bb0{{.*}}:
// CHECK: tuple ()
// CHECK-LABEL: } // end sil function 'dont_crash_on_dead_arg_use'
sil [ossa] @dont_crash_on_dead_arg_use : $@convention(thin) (@inout Int64) -> () {
bb0(%0 : $*Int64):
%2 = alloc_stack $Int64
%1 = struct_element_addr %0 : $*Int64, #Int64._value
%3 = struct_element_addr %2 : $*Int64, #Int64._value
dealloc_stack %2 : $*Int64
%4 = tuple ()
return %4 : $()
}
// Make sure that we do expand destroy_addr appropriately for code-size
// trade-offs.
// CHECK-LABEL: sil [ossa] @large_struct_test :
// CHECK: bb0([[ARG0:%.*]] : @owned $LargeCodesizeStruct):
// CHECK: destroy_value [[ARG0]]
// CHECK: } // end sil function 'large_struct_test'
sil [ossa] @large_struct_test : $@convention(thin) (@owned LargeCodesizeStruct) -> () {
bb0(%0 : @owned $LargeCodesizeStruct):
%1 = alloc_stack $LargeCodesizeStruct
store %0 to [init] %1 : $*LargeCodesizeStruct
destroy_addr %1 : $*LargeCodesizeStruct
dealloc_stack %1 : $*LargeCodesizeStruct
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: sil [ossa] @small_struct_test :
// CHECK: bb0([[ARG0:%.*]] : @owned $SmallCodesizeStruct):
// CHECK: ([[ELEM1:%[0-9]+]], [[ELEM2:%[0-9]+]]) = destructure_struct [[ARG0]]
// CHECK: destroy_value [[ELEM1]]
// CHECK: destroy_value [[ELEM2]]
// CHECK: } // end sil function 'small_struct_test'
sil [ossa] @small_struct_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () {
bb0(%0 : @owned $SmallCodesizeStruct):
%1 = alloc_stack $SmallCodesizeStruct
store %0 to [init] %1 : $*SmallCodesizeStruct
destroy_addr %1 : $*SmallCodesizeStruct
dealloc_stack %1 : $*SmallCodesizeStruct
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: sil [ossa] @small_struct_multi_test :
// CHECK-NOT: alloc_stack
// CHECK: [[COPY:%.*]] = copy_value %0
// CHECK-NEXT: destructure_struct %0
// CHECK-NEXT: destroy_value
// CHECK-NEXT: destroy_value
// CHECK-NEXT: begin_borrow [[COPY]]
// CHECK-NEXT: debug_value
// CHECK-NEXT: end_borrow
// CHECK-NEXT: destroy_value [[COPY]]
// CHECK: bb2:
// CHECK-NEXT: destructure_struct %0
// CHECK-NEXT: destroy_value
// CHECK-NEXT: destroy_value
// CHECK-LABEL: } // end sil function 'small_struct_multi_test'
sil [ossa] @small_struct_multi_test : $@convention(thin) (@owned SmallCodesizeStruct) -> () {
bb0(%0 : @owned $SmallCodesizeStruct):
%1 = alloc_stack $SmallCodesizeStruct
store %0 to [init] %1 : $*SmallCodesizeStruct
cond_br undef, bb1, bb2
bb1:
%3 = load [copy] %1 : $*SmallCodesizeStruct
destroy_addr %1 : $*SmallCodesizeStruct
dealloc_stack %1 : $*SmallCodesizeStruct
%4 = begin_borrow %3 : $SmallCodesizeStruct
debug_value %4 : $SmallCodesizeStruct
end_borrow %4 : $SmallCodesizeStruct
destroy_value %3 : $SmallCodesizeStruct
br bb3
bb2:
destroy_addr %1 : $*SmallCodesizeStruct
dealloc_stack %1 : $*SmallCodesizeStruct
br bb3
bb3:
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: sil [ossa] @dead_address_projections :
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'dead_address_projections'
sil [ossa] @dead_address_projections : $@convention(thin) (((), ())) -> ((), ()) {
bb0(%0 : $((), ())):
%1 = alloc_stack $((), ())
%200 = tuple_element_addr %1 : $*((), ()), 0
%300 = tuple_element_addr %1 : $*((), ()), 1
cond_br undef, bb1, bb2
bb1:
store %0 to [trivial] %1 : $*((), ())
%16 = load [trivial] %1 : $*((), ())
dealloc_stack %1 : $*((), ())
br bb3(%16 : $((), ()))
bb2:
dealloc_stack %1 : $*((), ())
br bb3(%0 : $((), ()))
bb3(%20 : $((), ())):
return %20 : $((), ())
}
// CHECK-LABEL: sil [ossa] @load_tuple_of_void :
// CHECK-NOT: alloc_stack
// CHECK: return undef : $((), ())
// CHECK: } // end sil function 'load_tuple_of_void'
sil [ossa] @load_tuple_of_void : $@convention(thin) () -> ((), ()) {
bb0:
%1 = alloc_stack $((), ())
%16 = load [trivial] %1 : $*((), ())
dealloc_stack %1 : $*((), ())
return %16 : $((), ())
}