blob: 4674eceead552a23e6d7cfd29ed31e8bc04f7a9d [file] [log] [blame]
// RUN: %target-sil-opt -enable-sil-verify-all %s -capture-promotion | FileCheck %s
// Check to make sure that the process of promoting closure captures results in
// a correctly cloned and modified closure function body. This test
// intentionally only includes one promotable closure so that there is minimal
// ordering dependence in the checked output.
sil_stage raw
import Builtin
import Swift
class Foo {
func foo() -> Int
}
class Bar {
}
struct Baz {
var bar: Bar
var x: Int
}
sil @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
sil @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
sil @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
sil @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
// CHECK-LABEL: sil @test_capture_promotion
sil @test_capture_promotion : $@convention(thin) () -> @owned @callee_owned () -> Int {
bb0:
%0 = tuple ()
%1 = alloc_box $Foo
%1a = project_box %1 : $@box Foo
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
%3 = metatype $@thick Foo.Type
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
store %4 to %1a : $*Foo
%6 = alloc_box $Baz
%6a = project_box %6 : $@box Baz
%7 = function_ref @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
%8 = metatype $@thin Baz.Type
%9 = apply %7(%8) : $@convention(thin) (@thin Baz.Type) -> @owned Baz
store %9 to %6a : $*Baz
%11 = alloc_box $Int
%11a = project_box %11 : $@box Int
%12 = function_ref @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
%13 = metatype $@thin Int.Type
%14 = integer_literal $Builtin.Word, 3
%15 = apply %12(%14, %13) : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
store %15 to %11a : $*Int
// CHECK-NOT: function_ref @closure0 :
// CHECK: [[CLOSURE_PROMOTE:%.*]] = function_ref @_TTSf2i_i_i__closure0
// CHECK-NOT: function_ref @closure0 :
// The three strong retains are removed
// The Foo variable is loaded from and retained, because it is a reference type
// CHECK-NEXT: [[LOADFOO:%.*]] = load {{.*}} : $*Foo
// CHECK-NEXT: strong_retain [[LOADFOO]] : $Foo
// The Baz variable is loaded and retain_value'd, because it is a non-trivial
// aggregate type
// CHECK-NEXT: [[LOADBAZ:%.*]] = load {{.*}} : $*Baz
// CHECK-NEXT: retain_value [[LOADBAZ:%.*]] : $Baz
// The Int variable is loaded only, because it is trivial
// CHECK-NEXT: [[LOADINT:%.*]] = load {{.*}} : $*Int
// The partial apply has one value argument for each pair of arguments that was
// previously used to capture and pass the variable by reference
// CHECK-NEXT: {{.*}} = partial_apply [[CLOSURE_PROMOTE]]([[LOADFOO]], [[LOADBAZ]], [[LOADINT]])
%17 = function_ref @closure0 : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> Int
strong_retain %1 : $@box Foo
strong_retain %6 : $@box Baz
strong_retain %11 : $@box Int
%21 = partial_apply %17(%1, %6, %11) : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> Int
strong_release %11 : $@box Int
strong_release %6 : $@box Baz
strong_release %1 : $@box Foo
return %21 : $@callee_owned () -> Int
}
// CHECK-LABEL: sil @test_capture_promotion_indirect
sil @test_capture_promotion_indirect : $@convention(thin) () -> @owned @callee_owned () -> @out Int {
bb0:
%0 = tuple ()
%1 = alloc_box $Foo
%1a = project_box %1 : $@box Foo
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
%3 = metatype $@thick Foo.Type
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
store %4 to %1a : $*Foo
%6 = alloc_box $Baz
%6a = project_box %6 : $@box Baz
%7 = function_ref @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
%8 = metatype $@thin Baz.Type
%9 = apply %7(%8) : $@convention(thin) (@thin Baz.Type) -> @owned Baz
store %9 to %6a : $*Baz
%11 = alloc_box $Int
%11a = project_box %11 : $@box Int
%12 = function_ref @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
%13 = metatype $@thin Int.Type
%14 = integer_literal $Builtin.Word, 3
%15 = apply %12(%14, %13) : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
store %15 to %11a : $*Int
// CHECK-NOT: function_ref @closure_indirect_result :
// CHECK: [[CLOSURE_PROMOTE:%.*]] = function_ref @_TTSf2i_i_i_n__closure_indirect_result
// CHECK-NOT: function_ref @closure_indirect_result :
// The three strong retains are removed
// The Foo variable is loaded from and retained, because it is a reference type
// CHECK-NEXT: [[LOADFOO:%.*]] = load {{.*}} : $*Foo
// CHECK-NEXT: strong_retain [[LOADFOO]] : $Foo
// The Baz variable is loaded and retain_value'd, because it is a non-trivial
// aggregate type
// CHECK-NEXT: [[LOADBAZ:%.*]] = load {{.*}} : $*Baz
// CHECK-NEXT: retain_value [[LOADBAZ:%.*]] : $Baz
// The Int variable is loaded only, because it is trivial
// CHECK-NEXT: [[LOADINT:%.*]] = load {{.*}} : $*Int
// The partial apply has one value argument for each pair of arguments that was
// previously used to capture and pass the variable by reference
// CHECK-NEXT: {{.*}} = partial_apply [[CLOSURE_PROMOTE]]([[LOADFOO]], [[LOADBAZ]], [[LOADINT]])
%17 = function_ref @closure_indirect_result : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> @out Int
strong_retain %1 : $@box Foo
strong_retain %6 : $@box Baz
strong_retain %11 : $@box Int
%21 = partial_apply %17(%1, %6, %11) : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> @out Int
strong_release %11 : $@box Int
strong_release %6 : $@box Baz
strong_release %1 : $@box Foo
return %21 : $@callee_owned () -> @out Int
}
// CHECK-LABEL: sil private @_TTSf2i_i_i__closure0 : $@convention(thin) (@owned Foo, @owned Baz, Int) -> Int
// CHECK: [[DUMMY_FUNC:%.*]] = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
// The load of %1 is removed, and its uses replaced with the Foo argument
// CHECK-NEXT: strong_retain {{.*}} : $Foo
// CHECK-NEXT: [[METHOD_FOO:%.*]] = class_method {{.*}} : $Foo, #Foo.foo!1 : (Foo) -> () -> Int , $@convention(method) (@guaranteed Foo) -> Int
// CHECK-NEXT: [[APPLY_FOO:%.*]] = apply [[METHOD_FOO]]({{.*}}) : $@convention(method) (@guaranteed Foo) -> Int
// The struct_element_addr of %3 followed by a load is replaced by a struct_extract of the Baz argument
// CHECK-NEXT: [[EXTRACT_BAZ_X:%.*]] = struct_extract {{.*}} : $Baz, #Baz.x
// CHECK-NEXT: [[RETVAL:%.*]] = apply [[DUMMY_FUNC]]([[APPLY_FOO]], [[EXTRACT_BAZ_X]], {{.*}}) : $@convention(thin) (Int, Int, Int) -> Int
// The release of %4 is removed because the Int type is trivial
// The release of %2 is replaced by a release_value of the Baz argument, since
// it is a non-trivial aggregate
// CHECK-NEXT: release_value {{.*}} : $Baz
// The release of %0 is replaced by a strong_release of the Foo argument, since
// it is a reference type
// CHECK-NEXT: strong_release {{.*}} : $Foo
// CHECK-NEXT: return [[RETVAL]] : $Int
sil private @closure0 : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> Int {
bb0(%0 : $@box Foo, %2 : $@box Baz, %4 : $@box Int):
%1 = project_box %0 : $@box Foo
%3 = project_box %2 : $@box Baz
%5 = project_box %4 : $@box Int
%6 = tuple ()
// function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int
%7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
%8 = load %1 : $*Foo
strong_retain %8 : $Foo
%10 = class_method %8 : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
%11 = apply %10(%8) : $@convention(method) (@guaranteed Foo) -> Int
%12 = struct_element_addr %3 : $*Baz, #Baz.x
%13 = load %12 : $*Int
%14 = load %5 : $*Int
%15 = apply %7(%11, %13, %14) : $@convention(thin) (Int, Int, Int) -> Int
strong_release %4 : $@box Int
strong_release %2 : $@box Baz
strong_release %0 : $@box Foo
return %15 : $Int
}
// The closure in this function is not promotable because it mutates its argument
// CHECK-LABEL: sil @test_unpromotable
sil @test_unpromotable : $@convention(thin) () -> @owned @callee_owned () -> Int {
bb0:
%0 = tuple ()
%1 = alloc_box $Foo
%1a = project_box %1 : $@box Foo
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
%3 = metatype $@thick Foo.Type
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
store %4 to %1a : $*Foo
%17 = function_ref @closure1 : $@convention(thin) (@box Foo) -> Int
strong_retain %1 : $@box Foo
// CHECK: partial_apply {{%.*}}({{%.*}})
%21 = partial_apply %17(%1) : $@convention(thin) (@box Foo) -> Int
strong_release %1 : $@box Foo
return %21 : $@callee_owned () -> Int
}
sil @mutate_foo : $@convention(thin) (@inout Foo) -> ()
sil private @closure1 : $@convention(thin) (@box Foo) -> Int {
bb0(%0 : $@box Foo):
%1 = project_box %0 : $@box Foo
%6 = tuple ()
// function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int
%7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
%8 = load %1 : $*Foo
strong_retain %8 : $Foo
%10 = class_method %8 : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
%11 = apply %10(%8) : $@convention(method) (@guaranteed Foo) -> Int
%12 = function_ref @mutate_foo : $@convention(thin) (@inout Foo) -> ()
%13 = apply %12(%1) : $@convention(thin) (@inout Foo) -> ()
strong_release %0 : $@box Foo
return %11 : $Int
}
sil @apply : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
// CHECK-LABEL: sil @captureWithinGeneric
sil @captureWithinGeneric : $@convention(thin) <T> (@inout Int, @inout Int) -> () {
// CHECK: bb0
bb0(%0 : $*Int, %1 : $*Int):
%2 = alloc_box $Int
%2a = project_box %2 : $@box Int
copy_addr %0 to [initialization] %2a : $*Int
%4 = alloc_box $Int
%4a = project_box %4 : $@box Int
copy_addr %1 to [initialization] %4a : $*Int
%6 = function_ref @apply : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
// CHECK: [[PROMOTED:%[0-9a-zA-Z]+]] = function_ref @_TTSf2n_i__closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned @box Int, Int) -> ()
%7 = function_ref @closureWithGenericSignature : $@convention(thin) _0_0> (@owned @box Int, @owned @box Int) -> ()
strong_retain %4 : $@box Int
strong_retain %2 : $@box Int
// CHECK: partial_apply [[PROMOTED]]<{{[^>]+}}>(
%10 = partial_apply %7<T>(%4, %2) : $@convention(thin) _0_0> (@owned @box Int, @owned @box Int) -> ()
%11 = apply %6(%10) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
copy_addr %4a to %1 : $*Int
strong_release %4 : $@box Int
copy_addr %2a to %0 : $*Int
strong_release %2 : $@box Int
%16 = tuple ()
return %16 : $()
}
// CHECK: sil @_TTSf2n_i__closureWithGenericSignature : $@convention(thin) <{{[^>]+}}> (@owned @box Int, Int) -> ()
sil @closureWithGenericSignature : $@convention(thin) <T> (@owned @box Int, @owned @box Int) -> () {
bb0(%0 : $@box Int, %2 : $@box Int):
%1 = project_box %0 : $@box Int
%3 = project_box %2 : $@box Int
%4 = function_ref @_TFsoi1pFTSiSi_Si : $@convention(thin) (Int, Int) -> Int
%5 = load %3 : $*Int
%6 = load %3 : $*Int
%7 = apply %4(%5, %6) : $@convention(thin) (Int, Int) -> Int
assign %7 to %1 : $*Int
strong_release %2 : $@box Int
strong_release %0 : $@box Int
%11 = tuple ()
return %11 : $()
}
sil [transparent] [fragile] @_TFsoi1pFTSiSi_Si : $@convention(thin) (Int, Int) -> Int
sil private @closure_indirect_result : $@convention(thin) (@owned @box Foo, @owned @box Baz, @owned @box Int) -> @out Int {
bb0(%0: $*Int, %1 : $@box Foo, %2 : $@box Baz, %4 : $@box Int):
%17 = project_box %1 : $@box Foo
%3 = project_box %2 : $@box Baz
%5 = project_box %4 : $@box Int
%6 = tuple ()
// function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int
%7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
%8 = load %17 : $*Foo
strong_retain %8 : $Foo
%10 = class_method %8 : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
%11 = apply %10(%8) : $@convention(method) (@guaranteed Foo) -> Int
%12 = struct_element_addr %3 : $*Baz, #Baz.x
%13 = load %12 : $*Int
%14 = load %5 : $*Int
%15 = apply %7(%11, %13, %14) : $@convention(thin) (Int, Int, Int) -> Int
strong_release %4 : $@box Int
strong_release %2 : $@box Baz
strong_release %1 : $@box Foo
store %15 to %0 : $*Int
%16 = tuple()
return %16 : $()
}