Files
swift-mirror/test/SILOptimizer/capture_promotion.sil
Roman Levenstein 8ad61d5cd6 Use function signatures for SILDeclRefs in witness_tables, vtables and witness_method instructions.
Textual SIL was sometimes ambiguous when SILDeclRefs were used, because the textual representation of SILDeclRefs was the same for functions that have the same name, but different signatures.
2017-01-27 12:16:14 -08:00

309 lines
14 KiB
Plaintext

// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -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
struct Int {
var value : Builtin.Int64
}
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 $<τ_0_0> { var τ_0_0 } <Foo>
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
%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 $<τ_0_0> { var τ_0_0 } <Baz>
%6a = project_box %6 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
%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 $<τ_0_0> { var τ_0_0 } <Int>
%11a = project_box %11 : $<τ_0_0> { var τ_0_0 } <Int>, 0
%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 <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> Int
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Foo>
strong_retain %6 : $<τ_0_0> { var τ_0_0 } <Baz>
strong_retain %11 : $<τ_0_0> { var τ_0_0 } <Int>
%21 = partial_apply %17(%1, %6, %11) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> Int
strong_release %11 : $<τ_0_0> { var τ_0_0 } <Int>
strong_release %6 : $<τ_0_0> { var τ_0_0 } <Baz>
strong_release %1 : $<τ_0_0> { var τ_0_0 } <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 $<τ_0_0> { var τ_0_0 } <Foo>
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
%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 $<τ_0_0> { var τ_0_0 } <Baz>
%6a = project_box %6 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
%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 $<τ_0_0> { var τ_0_0 } <Int>
%11a = project_box %11 : $<τ_0_0> { var τ_0_0 } <Int>, 0
%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 @_TTSf2n_i_i_i__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 <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> @out Int
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Foo>
strong_retain %6 : $<τ_0_0> { var τ_0_0 } <Baz>
strong_retain %11 : $<τ_0_0> { var τ_0_0 } <Int>
%21 = partial_apply %17(%1, %6, %11) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> @out Int
strong_release %11 : $<τ_0_0> { var τ_0_0 } <Int>
strong_release %6 : $<τ_0_0> { var τ_0_0 } <Baz>
strong_release %1 : $<τ_0_0> { var τ_0_0 } <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 <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Foo>, %2 : $<τ_0_0> { var τ_0_0 } <Baz>, %4 : $<τ_0_0> { var τ_0_0 } <Int>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
%3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
%5 = project_box %4 : $<τ_0_0> { var τ_0_0 } <Int>, 0
%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 : $<τ_0_0> { var τ_0_0 } <Int>
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Baz>
strong_release %0 : $<τ_0_0> { var τ_0_0 } <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 $<τ_0_0> { var τ_0_0 } <Foo>
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
%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) (<τ_0_0> { var τ_0_0 } <Foo>) -> Int
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Foo>
// CHECK: partial_apply {{%.*}}({{%.*}})
%21 = partial_apply %17(%1) : $@convention(thin) (<τ_0_0> { var τ_0_0 } <Foo>) -> Int
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
return %21 : $@callee_owned () -> Int
}
sil @mutate_foo : $@convention(thin) (@inout Foo) -> ()
sil private @closure1 : $@convention(thin) (<τ_0_0> { var τ_0_0 } <Foo>) -> Int {
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Foo>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
%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 : $<τ_0_0> { var τ_0_0 } <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 $<τ_0_0> { var τ_0_0 } <Int>
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <Int>, 0
copy_addr %0 to [initialization] %2a : $*Int
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
%4a = project_box %4 : $<τ_0_0> { var τ_0_0 } <Int>, 0
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 <τ_0_0> { var τ_0_0 } <Int>, Int) -> ()
%7 = function_ref @closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <Int>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
strong_retain %4 : $<τ_0_0> { var τ_0_0 } <Int>
strong_retain %2 : $<τ_0_0> { var τ_0_0 } <Int>
// CHECK: partial_apply [[PROMOTED]]<{{[^>]+}}>(
%10 = partial_apply %7<T>(%4, %2) : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <Int>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
%11 = apply %6(%10) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
copy_addr %4a to %1 : $*Int
strong_release %4 : $<τ_0_0> { var τ_0_0 } <Int>
copy_addr %2a to %0 : $*Int
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Int>
%16 = tuple ()
return %16 : $()
}
// CHECK: sil @_TTSf2n_i__closureWithGenericSignature : $@convention(thin) <{{[^>]+}}> (@owned <τ_0_0> { var τ_0_0 } <Int>, Int) -> ()
sil @closureWithGenericSignature : $@convention(thin) <T> (@owned <τ_0_0> { var τ_0_0 } <Int>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> () {
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Int>, %2 : $<τ_0_0> { var τ_0_0 } <Int>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int>, 0
%3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Int>, 0
%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 : $<τ_0_0> { var τ_0_0 } <Int>
strong_release %0 : $<τ_0_0> { var τ_0_0 } <Int>
%11 = tuple ()
return %11 : $()
}
sil [transparent] [fragile] @_TFsoi1pFTSiSi_Si : $@convention(thin) (Int, Int) -> Int
sil private @closure_indirect_result : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> @out Int {
bb0(%0: $*Int, %1 : $<τ_0_0> { var τ_0_0 } <Foo>, %2 : $<τ_0_0> { var τ_0_0 } <Baz>, %4 : $<τ_0_0> { var τ_0_0 } <Int>):
%17 = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
%3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
%5 = project_box %4 : $<τ_0_0> { var τ_0_0 } <Int>, 0
%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 : $<τ_0_0> { var τ_0_0 } <Int>
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Baz>
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
store %15 to %0 : $*Int
%16 = tuple()
return %16 : $()
}