mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
An argument-to-argument escape always involves a store, which makes an exclusive escape impossible.
387 lines
11 KiB
Plaintext
387 lines
11 KiB
Plaintext
// RUN: %target-sil-opt %s -compute-escape-effects | %FileCheck %s
|
|
|
|
// REQUIRES: swift_in_compiler
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
protocol ClassP : AnyObject {
|
|
func foo()
|
|
}
|
|
|
|
class X : ClassP {
|
|
func foo()
|
|
deinit
|
|
}
|
|
|
|
class Derived : X {
|
|
}
|
|
|
|
struct Str {
|
|
@_hasStorage var a: X
|
|
@_hasStorage var b: (X, X)
|
|
}
|
|
|
|
class Y {
|
|
@_hasStorage var s: Str
|
|
}
|
|
|
|
class Z {
|
|
@_hasStorage var y: Y
|
|
}
|
|
|
|
class DerivedZ : Z {
|
|
}
|
|
|
|
class LinkedNode {
|
|
@_hasStorage var next: LinkedNode;
|
|
@_hasStorage var x: X;
|
|
}
|
|
|
|
enum E {
|
|
case A(Z)
|
|
case B(Z)
|
|
}
|
|
|
|
struct PointerAndInt {
|
|
@_hasStorage var p: UnsafeMutablePointer<Int>
|
|
@_hasStorage var i: Int
|
|
}
|
|
|
|
sil_global @globalY : $Y
|
|
|
|
sil [ossa] @unknown_function : $@convention(thin) (@owned X) -> ()
|
|
sil [ossa] @unknown_function_y : $@convention(thin) (@owned Y) -> ()
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_basic
|
|
// CHECK-NEXT: [%1: escape v** -> %3.v**]
|
|
// CHECK-NEXT: [%2: noescape, escape c*.v** -> %r.v**]
|
|
// CHECK-NEXT: [%3: noescape]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_basic : $@convention(thin) (@owned X, @guaranteed Str, @guaranteed Z, @inout X) -> @owned Y {
|
|
bb0(%0 : @owned $X, %1 : @guaranteed $Str, %2 : @guaranteed $Z, %3 : $*X):
|
|
// %0 escapes to global
|
|
%4 = function_ref @unknown_function : $@convention(thin) (@owned X) -> ()
|
|
%5 = apply %4(%0) : $@convention(thin) (@owned X) -> ()
|
|
|
|
// %1 escapes to argument %3
|
|
%6 = struct_extract %1 : $Str, #Str.a
|
|
%7 = copy_value %6 : $X
|
|
store %7 to [assign] %3 : $*X
|
|
|
|
// content of %2 escapes to return
|
|
// %2 does not escape
|
|
%8 = ref_element_addr %2 : $Z, #Z.y
|
|
%9 = load [copy] %8 : $*Y
|
|
return %9 : $Y
|
|
}
|
|
|
|
// CHECK-LABEL: sil @simple_forwarding
|
|
// CHECK-NEXT: [%0: escape => %r, escape c*.v** => %r.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @simple_forwarding : $@convention(thin) (@owned Z) -> @owned Z {
|
|
bb0(%0 : $Z):
|
|
return %0 : $Z
|
|
}
|
|
|
|
// CHECK-LABEL: sil @forwarding_struct_element
|
|
// CHECK-NEXT: [%0: escape v** => %r.v**, escape v**.c*.v** => %r.v**.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @forwarding_struct_element : $@convention(thin) (@owned Str) -> @owned X {
|
|
bb0(%0 : $Str):
|
|
%1 = struct_extract %0 : $Str, #Str.a
|
|
return %1 : $X
|
|
}
|
|
|
|
// CHECK-LABEL: sil @forwarding_in_struct
|
|
// CHECK-NEXT: [%0: escape => %r.s1.0, escape c*.v** => %r.s1.0.c*.v**]
|
|
// CHECK-NEXT: [%1: escape => %r.s1.1, escape c*.v** => %r.s1.1.c*.v**]
|
|
// CHECK-NEXT: [%2: escape => %r.s0, escape c*.v** => %r.s0.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @forwarding_in_struct : $@convention(thin) (@owned X, @owned X, @owned X) -> @owned Str {
|
|
bb0(%0 : $X, %1 : $X, %2: $X):
|
|
%3 = tuple (%0 : $X, %1 : $X)
|
|
%4 = struct $Str(%2 : $X, %3 : $(X, X))
|
|
return %4 : $Str
|
|
}
|
|
|
|
// CHECK-LABEL: sil @forwarding_same_arg_in_struct
|
|
// CHECK-NEXT: [%0: escape -> %r.v**, escape c*.v** -> %r.v**.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @forwarding_same_arg_in_struct : $@convention(thin) (@owned X) -> @owned Str {
|
|
bb0(%0 : $X):
|
|
%1 = tuple (%0 : $X, %0 : $X)
|
|
%2 = struct $Str(%0 : $X, %1 : $(X, X))
|
|
return %2 : $Str
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_escaping_content
|
|
// CHECK-NEXT: [%0: noescape]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_escaping_content : $@convention(thin) (@guaranteed Z) -> () {
|
|
bb0(%0 : @guaranteed $Z):
|
|
%1 = ref_element_addr %0 : $Z, #Z.y
|
|
%2 = load [copy] %1 : $*Y
|
|
%3 = function_ref @unknown_function_y : $@convention(thin) (@owned Y) -> ()
|
|
%4 = apply %3(%2) : $@convention(thin) (@owned Y) -> ()
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @not_escaping_argument
|
|
// CHECK-NEXT: [%0: noescape! **]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @not_escaping_argument : $@convention(thin) (@guaranteed Z) -> () {
|
|
[%0: noescape! **]
|
|
bb0(%0 : $Z):
|
|
%1 = ref_element_addr %0 : $Z, #Z.y
|
|
%2 = global_addr @globalY : $*Y
|
|
// Even a store does not make the argument escaping.
|
|
copy_addr %2 to [init] %1 : $*Y
|
|
%4 = tuple ()
|
|
return %4 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @load_from_inout
|
|
// CHECK-NEXT: [%0: escape -> %r, escape c*.v** -> %r.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @load_from_inout : $@convention(thin) (@inout Y) -> @owned Y {
|
|
bb0(%0 : $*Y):
|
|
%2 = load %0 : $*Y
|
|
return %2 : $Y
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @followstore_implies_non_exclusive
|
|
// CHECK-NEXT: [%0: noescape, escape c*.v** -> %r.v**]
|
|
// CHECK-NEXT: [%1: noescape]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @followstore_implies_non_exclusive : $@convention(thin) (@guaranteed Z, @owned Y) -> @owned Y {
|
|
bb0(%0 : @guaranteed $Z, %1 : @owned $Y):
|
|
%2 = ref_element_addr %0 : $Z, #Z.y
|
|
%3 = load [copy] %2 : $*Y
|
|
destroy_value %1 : $Y
|
|
return %3 : $Y
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_not_exclusive_escaping_through_phi
|
|
// CHECK-NEXT: [%0: noescape, escape c*.v** -> %r.v**]
|
|
// CHECK-NEXT: [%1: escape -> %r]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_not_exclusive_escaping_through_phi : $@convention(thin) (@guaranteed Z, @owned Y) -> @owned Y {
|
|
bb0(%0 : @guaranteed $Z, %1 : @owned $Y):
|
|
cond_br undef, bb1, bb2
|
|
bb1:
|
|
%3 = ref_element_addr %0 : $Z, #Z.y
|
|
%4 = load [copy] %3 : $*Y
|
|
destroy_value %1 : $Y
|
|
br bb3(%4 : $Y)
|
|
bb2:
|
|
br bb3(%1 : $Y)
|
|
|
|
bb3(%7: @owned $Y):
|
|
return %7 : $Y
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_not_exclusive_through_store
|
|
// CHECK-NEXT: [%0: noescape, escape c*.v** -> %r.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_not_exclusive_through_store : $@convention(thin) (@guaranteed Z, @owned Y) -> @owned Y {
|
|
bb0(%0 : @guaranteed $Z, %1 : @owned $Y):
|
|
%2 = ref_element_addr %0 : $Z, #Z.y
|
|
%3 = load [copy] %2 : $*Y
|
|
store %1 to [assign] %2 : $*Y
|
|
return %3 : $Y
|
|
}
|
|
|
|
sil [ossa] @self_arg_callee : $@convention(thin) (@inout Str) -> () {
|
|
[%0: escape v** -> %0.v**]
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_self_arg_escape
|
|
// CHECK-NEXT: [%0: escape v** -> %0.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_self_arg_escape : $@convention(thin) (@inout Str) -> () {
|
|
bb0(%0 : $*Str):
|
|
%1 = function_ref @self_arg_callee : $@convention(thin) (@inout Str) -> ()
|
|
%2 = apply %1(%0) : $@convention(thin) (@inout Str) -> ()
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_self_arg_escape_with_copy
|
|
// CHECK-NEXT: [%0: escape v** -> %0.s1.0.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_self_arg_escape_with_copy : $@convention(thin) (@inout Str) -> () {
|
|
bb0(%0 : $*Str):
|
|
%1 = struct_element_addr %0 : $*Str, #Str.a
|
|
%2 = struct_element_addr %0 : $*Str, #Str.b
|
|
%3 = tuple_element_addr %2 : $*(X,X), 0
|
|
copy_addr %1 to %3 : $*X
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_self_arg_escape_with_bidirectional_copy
|
|
// CHECK-NEXT: [%0: escape v** -> %0.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_self_arg_escape_with_bidirectional_copy : $@convention(thin) (@inout Str) -> () {
|
|
bb0(%0 : $*Str):
|
|
%1 = struct_element_addr %0 : $*Str, #Str.a
|
|
%2 = struct_element_addr %0 : $*Str, #Str.b
|
|
%3 = tuple_element_addr %2 : $*(X,X), 0
|
|
copy_addr %1 to %3 : $*X
|
|
copy_addr %3 to %1 : $*X
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @test_content_to_arg_addr
|
|
// CHECK-NEXT: [%0: noescape **]
|
|
// CHECK-NEXT: [%1: noescape, escape c*.v** -> %0.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil [ossa] @test_content_to_arg_addr : $@convention(thin) (@guaranteed Y) -> @out Str {
|
|
bb0(%0 : $*Str, %1 : @guaranteed $Y):
|
|
%2 = ref_element_addr %1 : $Y, #Y.s
|
|
%3 = begin_access [read] [dynamic] %2 : $*Str
|
|
copy_addr %3 to [init] %0 : $*Str
|
|
end_access %3 : $*Str
|
|
%6 = tuple ()
|
|
return %6 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @escaping_pointer
|
|
// CHECK-NEXT: [%0: escape v** => %r.v**, escape v**.c*.v** => %r.v**.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @escaping_pointer : $@convention(thin) (PointerAndInt) -> UnsafeMutablePointer<Int> {
|
|
bb0(%0 : $PointerAndInt):
|
|
%1 = struct_extract %0 : $PointerAndInt, #PointerAndInt.p
|
|
return %1 : $UnsafeMutablePointer<Int>
|
|
}
|
|
|
|
// CHECK-LABEL: sil @not_escaping_int
|
|
// CHECK-NEXT: [%0: noescape **]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @not_escaping_int : $@convention(thin) (PointerAndInt) -> Int {
|
|
bb0(%0 : $PointerAndInt):
|
|
%1 = struct_extract %0 : $PointerAndInt, #PointerAndInt.i
|
|
return %1 : $Int
|
|
}
|
|
|
|
// CHECK-LABEL: sil @test_simple_recursion
|
|
// CHECK-NEXT: [%0: noescape **]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @test_simple_recursion : $@convention(thin) (@guaranteed Y) -> () {
|
|
bb0(%0 : $Y):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%2 = function_ref @test_simple_recursion : $@convention(thin) (@guaranteed Y) -> ()
|
|
%3 = apply %2(%0) : $@convention(thin) (@guaranteed Y) -> ()
|
|
br bb3
|
|
|
|
bb2:
|
|
br bb3
|
|
|
|
bb3:
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @test_recursion_with_escaping_param : $@convention(thin) (@guaranteed Y, @guaranteed Y) -> Y {
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @test_recursion_with_escaping_param : $@convention(thin) (@guaranteed Y, @guaranteed Y) -> Y {
|
|
bb0(%0 : $Y, %1 : $Y):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%2 = function_ref @test_recursion_with_escaping_param : $@convention(thin) (@guaranteed Y, @guaranteed Y) -> Y
|
|
%3 = apply %2(%1, %0) : $@convention(thin) (@guaranteed Y, @guaranteed Y) -> Y
|
|
br bb3(%3 : $Y)
|
|
|
|
bb2:
|
|
br bb3(%1 : $Y)
|
|
|
|
bb3(%5 : $Y):
|
|
return %5 : $Y
|
|
}
|
|
|
|
// CHECK-LABEL: sil @inout_class_argument
|
|
// CHECK-NEXT: [%0: noescape **]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @inout_class_argument : $@convention(thin) (@inout X) -> () {
|
|
bb0(%0 : $*X):
|
|
%1 = alloc_ref $X
|
|
store %1 to %0: $*X
|
|
%3 = tuple()
|
|
return %3 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @load_and_store_to_inout
|
|
// CHECK-NEXT: [%0: escape -> %r, escape c*.v** -> %r.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @load_and_store_to_inout : $@convention(thin) (@inout Z, @guaranteed Y) -> Z {
|
|
bb0(%0 : $*Z, %1 : $Y):
|
|
%2 = load %0 : $*Z
|
|
%3 = ref_element_addr %2 : $Z, #Z.y
|
|
store %1 to %3 : $*Y
|
|
return %2 : $Z
|
|
}
|
|
|
|
// CHECK-LABEL: sil @store_to_content
|
|
// CHECK-NEXT: [%0: escape => %r, escape c*.v** -> %r.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @store_to_content : $@convention(thin) (@owned Z, @guaranteed Y) -> @owned Z {
|
|
bb0(%0 : $Z, %1 : $Y):
|
|
%2 = ref_element_addr %0 : $Z, #Z.y
|
|
store %1 to %2 : $*Y
|
|
return %0 : $Z
|
|
}
|
|
|
|
// CHECK-LABEL: sil @non_returning_function
|
|
// CHECK-NEXT: [%1: noescape **]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @non_returning_function : $@convention(thin) (Str, @inout Str) -> () {
|
|
bb0(%0 : $Str, %1 : $*Str):
|
|
store %0 to %1 : $*Str
|
|
unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: sil @followStore_introducing1
|
|
// CHECK-NEXT: [%0: noescape]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @followStore_introducing1 : $@convention(thin) (@guaranteed Z) -> @owned Y {
|
|
bb0(%0 : $Z):
|
|
%1 = ref_element_addr %0 : $Z, #Z.y
|
|
%2 = load %1 : $*Y
|
|
store %2 to %1 : $*Y
|
|
%4 = load %1 : $*Y
|
|
return %4 : $Y
|
|
}
|
|
|
|
// CHECK-LABEL: sil @followStore_introducing2
|
|
// CHECK-NEXT: [%0: noescape **]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @followStore_introducing2 : $@convention(thin) (@inout Z, @guaranteed Y) -> () {
|
|
bb0(%0 : $*Z, %1 : $Y):
|
|
%2 = load %0 : $*Z
|
|
%3 = ref_element_addr %2 : $Z, #Z.y
|
|
store %1 to %3 : $*Y
|
|
%5 = tuple ()
|
|
return %5 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @store_to_class_field
|
|
// CHECK-NEXT: [%0: escape v** -> %r.c0.v**, escape v**.c*.v** -> %r.c0.v**.c*.v**]
|
|
// CHECK-NEXT: {{^[^[]}}
|
|
sil @store_to_class_field : $@convention(thin) (@in_guaranteed Str) -> @owned Y {
|
|
bb0(%0 : $*Str):
|
|
%1 = alloc_ref $Y
|
|
%2 = ref_element_addr %1 : $Y, #Y.s
|
|
copy_addr %0 to [init] %2 : $*Str
|
|
return %1 : $Y
|
|
}
|
|
|