Files
swift-mirror/test/SILOptimizer/escape_analysis.sil
Arnold Schwaighofer 876cea81ae SIL: Add an allowed access kind to the opened value of an open_existential_addr instruction
Once we move to a copy-on-write implementation of existential value buffers we
can no longer consume or destroy values of an opened existential unless the
buffer is uniquely owned.

Therefore we need to track the allowed operation on opened values.

Add qualifiers "mutable_access" and "immutable_access" to open_existential_addr
instructions to indicate the allowed access to the opened value.

Once we move to a copy-on-write implementation, an "open_existential_addr
mutable_access" instruction will ensure unique ownership of the value buffer.
2017-02-15 14:23:12 -08:00

1468 lines
52 KiB
Plaintext

// RUN: %target-sil-opt -new-mangling-for-tests -assume-parsing-unqualified-ownership-sil %s -escapes-dump -o /dev/null | %FileCheck %s
// REQUIRES: asserts
sil_stage canonical
import Builtin
import Swift
import SwiftShims
protocol ClassP : class {
func foo()
}
class X : ClassP {
func foo()
deinit
}
class Derived : X {
}
class Y {
@sil_stored var x: X
init(newx: X)
}
class Z {
@sil_stored var x: X
init(newx: X)
deinit
}
protocol P {
func foo()
}
struct Pointer : P {
let y : Y
func foo()
}
enum PointerEnum {
case B(Pointer, Pointer)
}
enum PointerEnum2 {
case A
case B(Pointer)
case C
}
class LinkedNode {
@sil_stored var next: LinkedNode;
init(_ n: LinkedNode)
}
struct MyError : Error {
}
final class ErrorClass : Error {
}
struct SomeData {
let x : X
}
struct FourFields {
let a : SomeData
let b : Pointer
let c : Builtin.RawPointer
let d : Y
}
// Sanity check with a simple function.
// CHECK-LABEL: CG of test_simple
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %2
// CHECK-NEXT: Arg %1 Esc: G, Succ:
// CHECK-NEXT: Val %2 Esc: A, Succ: (%2.1)
// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2)
// CHECK-NEXT: Con %2.2 Esc: G, Succ: %1
// CHECK-NEXT: End
sil @test_simple : $@convention(thin) (@inout Y, @owned X) -> () {
bb0(%0 : $*Y, %1 : $X):
%2 = alloc_ref $Y
%3 = ref_element_addr %2 : $Y, #Y.x
store %1 to %3 : $*X
%5 = load %0 : $*Y
store %2 to %0 : $*Y
strong_release %5 : $Y
%7 = tuple ()
return %7 : $()
}
// Test if a deferring edge is created for a block argument.
// CHECK-LABEL: CG of deferringEdge
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%3.1)
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: Val %3 Esc: %3, Succ: (%3.1), %0
// CHECK-NEXT: Con %3.1 Esc: A, Succ: (%3.2)
// CHECK-NEXT: Con %3.2 Esc: A, Succ: %1
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @deferringEdge : $@convention(thin) (@owned LinkedNode, @owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode, %1 : $LinkedNode):
br bb1(%0 : $LinkedNode)
bb1(%3 : $LinkedNode):
%4 = ref_element_addr %3 : $LinkedNode, #LinkedNode.next
store %1 to %4 : $*LinkedNode
return %0 : $LinkedNode
}
// Test a local object just escaping via return.
// CHECK-LABEL: CG of escapes_via_return
// CHECK-NEXT: Val %0 Esc: R, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @escapes_via_return : $@convention(thin) () -> @owned X {
bb0:
%0 = alloc_ref $X
return %0 : $X
}
// A linear chain of assignments is collapsed to a single node.
// CHECK-LABEL: CG of test_linked_list
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Val %1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%11.1)
// CHECK-NEXT: Val %4 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Val %7 Esc: %11, Succ: (%1.1)
// CHECK-NEXT: Val %11 Esc: %11, Succ: (%1.1), %7, %11.1
// CHECK-NEXT: Con %11.1 Esc: A, Succ: (%1.1), %0, %1, %4
// CHECK-NEXT: Ret Esc: R, Succ: %11.1
// CHECK-NEXT: End
sil @test_linked_list : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
%1 = alloc_ref $LinkedNode
%2 = ref_element_addr %1 : $LinkedNode, #LinkedNode.next
store %0 to %2 : $*LinkedNode
%4 = alloc_ref $LinkedNode
%5 = ref_element_addr %4 : $LinkedNode, #LinkedNode.next
store %1 to %5 : $*LinkedNode
%7 = alloc_ref $LinkedNode
%8 = ref_element_addr %7 : $LinkedNode, #LinkedNode.next
store %4 to %8 : $*LinkedNode
br bb1(%7 : $LinkedNode)
// This "x = x.next" loop let's the chain collapse to a single node.
bb1(%11 : $LinkedNode):
%12 = ref_element_addr %11 : $LinkedNode, #LinkedNode.next
%13 = load %12 : $*LinkedNode
cond_br undef, bb1(%13 : $LinkedNode), bb2
bb2:
return %13 : $LinkedNode
}
// The same example as above but distributed over two functions.
// CHECK-LABEL: CG of create_chain
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%7.1)
// CHECK-NEXT: Val %1 Esc: A, Succ: (%7.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ: %0, %1, %4
// CHECK-NEXT: Val %4 Esc: A, Succ: (%7.1)
// CHECK-NEXT: Val %7 Esc: %11, Succ: (%7.1)
// CHECK-NEXT: Con %7.1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Val %11 Esc: R, Succ: (%7.1), %1.1
// CHECK-NEXT: Ret Esc: R, Succ: %11
// CHECK-NEXT: End
sil @create_chain : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
%1 = alloc_ref $LinkedNode
%2 = ref_element_addr %1 : $LinkedNode, #LinkedNode.next
store %0 to %2 : $*LinkedNode
%4 = alloc_ref $LinkedNode
%5 = ref_element_addr %4 : $LinkedNode, #LinkedNode.next
store %1 to %5 : $*LinkedNode
%7 = alloc_ref $LinkedNode
%8 = ref_element_addr %7 : $LinkedNode, #LinkedNode.next
store %4 to %8 : $*LinkedNode
%10 = function_ref @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%11 = apply %10(%7) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
return %11 : $LinkedNode
}
// CHECK-LABEL: CG of loadNext
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%2.1)
// CHECK-NEXT: Val %2 Esc: %2, Succ: (%2.1), %0, %2.2
// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2)
// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%2.1)
// CHECK-NEXT: Ret Esc: R, Succ: %2.2
// CHECK-NEXT: End
sil @loadNext : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
br bb1(%0 : $LinkedNode)
bb1(%2 : $LinkedNode):
%3 = ref_element_addr %2 : $LinkedNode, #LinkedNode.next
%4 = load %3 : $*LinkedNode
cond_br undef, bb1(%4 : $LinkedNode), bb2
bb2:
return %4 : $LinkedNode
}
// Content nodes in the callee are duplicated in the caller.
// CHECK-LABEL: CG of call_load_next3
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4)
// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5)
// CHECK-NEXT: Con %0.5 Esc: A, Succ: (%0.6)
// CHECK-NEXT: Con %0.6 Esc: A, Succ:
// CHECK-NEXT: Val %2 Esc: R, Succ: %0.6
// CHECK-NEXT: Ret Esc: R, Succ: %2
// CHECK-NEXT: End
sil @call_load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
%1 = function_ref @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%2 = apply %1(%0) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
return %2 : $LinkedNode
}
// CHECK-LABEL: CG of load_next3
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4)
// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.5)
// CHECK-NEXT: Con %0.5 Esc: A, Succ: (%0.6)
// CHECK-NEXT: Con %0.6 Esc: A, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0.6
// CHECK-NEXT: End
sil @load_next3 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
%1 = ref_element_addr %0 : $LinkedNode, #LinkedNode.next
%2 = load %1 : $*LinkedNode
%3 = ref_element_addr %2 : $LinkedNode, #LinkedNode.next
%4 = load %3 : $*LinkedNode
%5 = ref_element_addr %4 : $LinkedNode, #LinkedNode.next
%6 = load %5 : $*LinkedNode
return %6 : $LinkedNode
}
sil_global @global_pointer : $Pointer
sil_global @global_x : $X
// The argument escapes because it is stored to a global variable in the callee.
// CHECK-LABEL: CG of call_store_pointer
// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0.2
// CHECK-NEXT: End
sil @call_store_pointer : $@convention(thin) (@owned Pointer) -> @owned X {
bb0(%0 : $Pointer):
debug_value %0 : $Pointer
%2 = function_ref @store_pointer : $@convention(thin) (@owned Pointer) -> ()
%3 = struct_extract %0 : $Pointer, #Pointer.y
%5 = apply %2(%0) : $@convention(thin) (@owned Pointer) -> ()
%6 = ref_element_addr %3 : $Y, #Y.x
%7 = load %6 : $*X
return %7 : $X
}
// CHECK-LABEL: CG of store_pointer
// CHECK-NEXT: Arg %0 Esc: G, Succ:
// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0
// CHECK-NEXT: End
sil @store_pointer : $@convention(thin) (@owned Pointer) -> () {
bb0(%0 : $Pointer):
%1 = global_addr @global_pointer : $*Pointer
store %0 to %1 : $*Pointer
%3 = tuple ()
return %3 : $()
}
// The argument does not escape because only the content is stored to a
// global variable in the callee.
// CHECK-LABEL: CG of store_content
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ:
// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0.2
// CHECK-NEXT: End
sil @store_content : $@convention(thin) (@owned Pointer) -> () {
bb0(%0 : $Pointer):
%1 = global_addr @global_x : $*X
%3 = struct_extract %0 : $Pointer, #Pointer.y
%4 = ref_element_addr %3 : $Y, #Y.x
%5 = load %4 : $*X
store %5 to %1 : $*X
%11 = tuple ()
return %11 : $()
}
// CHECK-LABEL: CG of call_store_content
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0.2
// CHECK-NEXT: End
sil @call_store_content : $@convention(thin) (@owned Pointer) -> @owned X {
bb0(%0 : $Pointer):
%2 = function_ref @store_content : $@convention(thin) (@owned Pointer) -> ()
%3 = struct_extract %0 : $Pointer, #Pointer.y
%5 = apply %2(%0) : $@convention(thin) (@owned Pointer) -> ()
%6 = ref_element_addr %3 : $Y, #Y.x
%7 = load %6 : $*X
return %7 : $X
}
// CHECK-LABEL: CG of copy_addr_content
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1
// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ:
// CHECK-NEXT: End
sil @copy_addr_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 {
bb0(%0: $*Int32, %1: $*Int32):
copy_addr %1 to [initialization] %0 : $*Int32
%2 = tuple()
return %2 : $()
}
// CHECK-LABEL: CG of copy_addr_take_content
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1
// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ:
// CHECK-NEXT: End
sil @copy_addr_take_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 {
bb0(%0: $*Int32, %1: $*Int32):
copy_addr [take] %1 to [initialization] %0 : $*Int32
%2 = tuple()
return %2 : $()
}
// CHECK-LABEL: CG of copy_addr_noinit_content
// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: G, Succ:
// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ:
// CHECK-NEXT: End
sil @copy_addr_noinit_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32 {
bb0(%0: $*Int32, %1: $*Int32):
copy_addr [take] %1 to %0 : $*Int32
%2 = tuple()
return %2 : $()
}
// CHECK-LABEL: CG of call_copy_addr_content
// CHECK-NEXT: Val %0 Esc: %3, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: %3, Succ: %1.1
// CHECK-NEXT: Val %1 Esc: %3, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: %3, Succ:
// CHECK-NEXT: End
sil @call_copy_addr_content : $@convention(thin) () -> () {
%0 = alloc_stack $Int32
%1 = alloc_stack $Int32
%2 = function_ref @copy_addr_content : $@convention(thin) (@in_guaranteed Int32) -> @out Int32
%3 = apply %2(%0, %1) : $@convention(thin) (@in_guaranteed Int32) -> @out Int32
%4 = tuple()
%6 = dealloc_stack %1 : $*Int32
%5 = dealloc_stack %0 : $*Int32
return %4 : $()
}
// Test partial_apply. The partial_apply and the boxes do not escape.
// The X parameter does escape because it is stored in Y and the release
// of Y's box _could_ capture Y.x in Y's deinit.
// CHECK-LABEL: CG of test_partial_apply
// CHECK-NEXT: Arg %1 Esc: G, Succ:
// CHECK-NEXT: Arg %2 Esc: A, Succ: (%6.3)
// CHECK-NEXT: Val %3 Esc: %14,%15,%17, Succ: (%6.1)
// CHECK-NEXT: Val %6 Esc: %14,%15,%16, Succ: (%6.1)
// CHECK-NEXT: Con %6.1 Esc: %14,%15,%16,%17, Succ: (%6.2)
// CHECK-NEXT: Con %6.2 Esc: %14,%15,%16,%17, Succ: %2
// CHECK-NEXT: Con %6.3 Esc: G, Succ:
// CHECK-NEXT: Val %12 Esc: %14,%15, Succ: %3, %6
// CHECK-NEXT: End
sil @test_partial_apply : $@convention(thin) (Int64, @owned X, @owned Y) -> Int64 {
bb0(%0 : $Int64, %1 : $X, %2 : $Y):
%3 = alloc_box $<τ_0_0> { var τ_0_0 } <Int64>
%4 = project_box %3 : $<τ_0_0> { var τ_0_0 } <Int64>, 0
store %0 to %4 : $*Int64
%6 = alloc_box $<τ_0_0> { var τ_0_0 } <Y>
%7 = project_box %6 : $<τ_0_0> { var τ_0_0 } <Y>, 0
store %2 to %7 : $*Y
%9 = function_ref @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } <Int64>, @owned <τ_0_0> { var τ_0_0 } <Y>) -> Int64
strong_retain %3 : $<τ_0_0> { var τ_0_0 } <Int64>
strong_retain %6 : $<τ_0_0> { var τ_0_0 } <Y>
%12 = partial_apply %9(%3, %6) : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } <Int64>, @owned <τ_0_0> { var τ_0_0 } <Y>) -> Int64
strong_retain %12 : $@callee_owned (@owned X) -> Int64
%14 = apply %12(%1) : $@callee_owned (@owned X) -> Int64
strong_release %12 : $@callee_owned (@owned X) -> Int64
strong_release %6 : $<τ_0_0> { var τ_0_0 } <Y>
strong_release %3 : $<τ_0_0> { var τ_0_0 } <Int64>
return %14 : $Int64
}
// CHECK-LABEL: CG of closure1
// CHECK-NEXT: Arg %0 Esc: G, Succ:
// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2)
// CHECK-NEXT: Con %1.2 Esc: A, Succ: (%1.3)
// CHECK-NEXT: Con %1.3 Esc: G, Succ:
// CHECK-NEXT: Arg %2 Esc: A, Succ: (%2.1)
// CHECK-NEXT: Con %2.1 Esc: A, Succ: (%2.2)
// CHECK-NEXT: Con %2.2 Esc: A, Succ: (%2.3)
// CHECK-NEXT: Con %2.3 Esc: G, Succ:
// CHECK-NEXT: Val %7 Esc: %8, Succ: %2
// CHECK-NEXT: End
sil @closure1 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } <Int64>, @owned <τ_0_0> { var τ_0_0 } <Y>) -> Int64 {
bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } <Int64>, %2 : $<τ_0_0> { var τ_0_0 } <Y>):
%3 = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int64>, 0
%4 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Y>, 0
%5 = load %3 : $*Int64
%6 = function_ref @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } <Y>) -> ()
%7 = partial_apply %6(%2) : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } <Y>) -> ()
%8 = apply %7(%0) : $@callee_owned (@owned X) -> ()
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Int64>
return %5 : $Int64
}
// CHECK-LABEL: CG of closure2
// CHECK-NEXT: Arg %0 Esc: G, Succ:
// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2)
// CHECK-NEXT: Con %1.2 Esc: A, Succ: (%1.3)
// CHECK-NEXT: Con %1.3 Esc: G, Succ: (%1.4)
// CHECK-NEXT: Con %1.4 Esc: G, Succ: %0
// CHECK-NEXT: End
sil @closure2 : $@convention(thin) (@owned X, @owned <τ_0_0> { var τ_0_0 } <Y>) -> () {
bb0(%0 : $X, %1 : $<τ_0_0> { var τ_0_0 } <Y>):
%2 = project_box %1 : $<τ_0_0> { var τ_0_0 } <Y>, 0
%3 = load %2 : $*Y
%4 = ref_element_addr %3 : $Y, #Y.x
store %0 to %4 : $*X
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Y>
%7 = tuple ()
return %7 : $()
}
// Test partial_apply. The box escapes in the callee.
// CHECK-LABEL: CG of test_escaped_box
// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ: (%1.2)
// CHECK-NEXT: Con %1.2 Esc: G, Succ: (%1.3)
// CHECK-NEXT: Con %1.3 Esc: G, Succ:
// CHECK-NEXT: Val %6 Esc: G, Succ: %1
// CHECK-NEXT: End
sil @test_escaped_box : $@convention(thin) (Int64) -> Int64 {
bb0(%0 : $Int64):
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int64>
%2 = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int64>, 0
store %0 to %2 : $*Int64
%4 = function_ref @let_box_escape : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> Int64
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Int64>
%6 = partial_apply %4(%1) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> Int64
strong_retain %6 : $@callee_owned () -> Int64
%8 = apply %6() : $@callee_owned () -> Int64
strong_release %6 : $@callee_owned () -> Int64
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Int64>
return %8 : $Int64
}
// CHECK-LABEL: CG of let_box_escape
// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: G, Succ:
// CHECK-NEXT: End
sil @let_box_escape : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> Int64 {
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Int64>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int64>, 0
%2 = load %1 : $*Int64
%3 = function_ref @takebox : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> ()
%4 = apply %3(%0) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> ()
strong_release %0 : $<τ_0_0> { var τ_0_0 } <Int64>
return %2 : $Int64
}
sil @takebox : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> ()
// The partial_apply itself escapes and therefore also the box escapes.
// CHECK-LABEL: CG of test_escaped_partial_apply
// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ:
// CHECK-NEXT: Val %6 Esc: G, Succ: %1
// CHECK-NEXT: End
sil @test_escaped_partial_apply : $@convention(thin) (Int64) -> () {
bb0(%0 : $Int64):
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Int64>
%2 = project_box %1 : $<τ_0_0> { var τ_0_0 } <Int64>, 0
store %0 to %2 : $*Int64
%4 = function_ref @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> Int64
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Int64>
%6 = partial_apply %4(%1) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> Int64
strong_retain %6 : $@callee_owned () -> Int64
%7 = function_ref @take_partial_apply : $@convention(thin) (@owned @callee_owned () -> Int64) -> ()
%8 = apply %7(%6) : $@convention(thin) (@owned @callee_owned () -> Int64) -> ()
%9 = tuple()
return %9 : $()
}
// CHECK-LABEL: CG of closure3
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: G, Succ:
// CHECK-NEXT: End
sil @closure3 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Int64>) -> Int64 {
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Int64>):
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int64>, 0
%2 = load %1 : $*Int64
strong_release %0 : $<τ_0_0> { var τ_0_0 } <Int64>
return %2 : $Int64
}
sil @take_partial_apply : $@convention(thin) (@owned @callee_owned () -> Int64) -> ()
// Test if the global escape state is propagated through recursive functions.
sil_global @global_ln : $LinkedNode
// CHECK-LABEL: CG of load_next_recursive
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ:
// CHECK-NEXT: Val %4 Esc: G, Succ: %0.2
// CHECK-NEXT: Ret Esc: R, Succ: %4
// CHECK-NEXT: End
sil @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
%1 = ref_element_addr %0 : $LinkedNode, #LinkedNode.next
%2 = load %1 : $*LinkedNode
%3 = function_ref @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%4 = apply %3(%2) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
return %4 : $LinkedNode
}
// CHECK-LABEL: CG of let_escape
// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: G, Succ:
// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ: %0
// CHECK-NEXT: Val %4 Esc: G, Succ: %0
// CHECK-NEXT: Ret Esc: R, Succ: %4
// CHECK-NEXT: End
sil @let_escape : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
%1 = global_addr @global_ln : $*LinkedNode
store %0 to %1 : $*LinkedNode
%3 = function_ref @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%4 = apply %3(%0) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
return %4 : $LinkedNode
}
// CHECK-LABEL: CG of return_same
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: G, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ: (%0.1)
// CHECK-NEXT: Val %3 Esc: G, Succ: (%0.1), %0.2
// CHECK-NEXT: Val %5 Esc: R, Succ: %0, %3
// CHECK-NEXT: Ret Esc: R, Succ: %5
// CHECK-NEXT: End
sil @return_same : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
cond_br undef, bb1, bb2(%0 : $LinkedNode)
bb1:
%2 = function_ref @load_next_recursive : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%3 = apply %2(%0) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
br bb2(%3 : $LinkedNode)
bb2(%5 : $LinkedNode):
return %5 : $LinkedNode
}
// Another recursion test.
// CHECK-LABEL: CG of loadNext2
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4)
// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%4.1)
// CHECK-NEXT: Val %4 Esc: R, Succ: %0.4
// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%4.2)
// CHECK-NEXT: Con %4.2 Esc: A, Succ: (%4.1)
// CHECK-NEXT: Ret Esc: R, Succ: %4
// CHECK-NEXT: End
sil @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
%1 = ref_element_addr %0 : $LinkedNode, #LinkedNode.next
%2 = load %1 : $*LinkedNode
%3 = function_ref @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%4 = apply %3(%2) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
return %4 : $LinkedNode
}
// CHECK-LABEL: CG of returnNext2
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: A, Succ: (%0.4)
// CHECK-NEXT: Con %0.4 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Val %3 Esc: R, Succ: (%0.3), %0.4
// CHECK-NEXT: Val %8 Esc: R, Succ: %0.2, %3
// CHECK-NEXT: Ret Esc: R, Succ: %8
// CHECK-NEXT: End
sil @returnNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
cond_br undef, bb1, bb2
bb1:
%2 = function_ref @loadNext2 : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%3 = apply %2(%0) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
br bb3(%3 : $LinkedNode)
bb2:
%5 = ref_element_addr %0 : $LinkedNode, #LinkedNode.next
%6 = load %5 : $*LinkedNode
br bb3(%6 : $LinkedNode)
bb3(%8 : $LinkedNode):
return %8 : $LinkedNode
}
// A single-cycle recursion test.
// CHECK-LABEL: CG of single_cycle_recursion
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Val %5 Esc: R, Succ: (%0.2), %0.1
// CHECK-NEXT: Val %7 Esc: R, Succ: %0, %5
// CHECK-NEXT: Ret Esc: R, Succ: %7
// CHECK-NEXT: End
sil @single_cycle_recursion : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode {
bb0(%0 : $LinkedNode):
cond_br undef, bb1, bb2(%0 : $LinkedNode)
bb1:
%1 = ref_element_addr %0 : $LinkedNode, #LinkedNode.next
%2 = load %1 : $*LinkedNode
%3 = function_ref @single_cycle_recursion : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
%4 = apply %3(%2) : $@convention(thin) (@owned LinkedNode) -> @owned LinkedNode
br bb2(%4 : $LinkedNode)
bb2(%5 : $LinkedNode):
return %5 : $LinkedNode
}
// Test if a try_apply is represented correctly in the connection graph.
// CHECK-LABEL: CG of call_throwing_func
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Val %3 Esc: R, Succ: %0
// CHECK-NEXT: Ret Esc: R, Succ: %3
// CHECK-NEXT: End
sil @call_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) {
bb0(%0 : $X):
%1 = function_ref @throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error)
try_apply %1(%0) : $@convention(thin) (@owned X) -> (@owned X, @error Error), normal bb1, error bb2
bb1(%3 : $X):
return %3 : $X
bb2(%5 : $Error):
throw %5 : $Error
}
// CHECK-LABEL: CG of throwing_func
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Val %3 Esc: G, Succ: (%3.1)
// CHECK-NEXT: Con %3.1 Esc: G, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) {
bb0(%0 : $X):
cond_br undef, bb1, bb2
bb1:
%2 = alloc_existential_box $Error, $MyError
%3 = project_existential_box $MyError in %2 : $Error
%4 = struct $MyError ()
store %4 to %3 : $*MyError
throw %2 : $Error
bb2:
return %0 : $X
}
// Test if a try_apply to an unknown function is handled correctly.
// CHECK-LABEL: CG of call_unknown_throwing_func
// CHECK-NEXT: Arg %0 Esc: G, Succ:
// CHECK-NEXT: Con %0.1 Esc: G, Succ:
// CHECK-NEXT: Val %3 Esc: G, Succ:
// CHECK-NEXT: Con %3.1 Esc: G, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %3
// CHECK-NEXT: End
sil @call_unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error) {
bb0(%0 : $X):
%1 = function_ref @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error)
try_apply %1(%0) : $@convention(thin) (@owned X) -> (@owned X, @error Error), normal bb1, error bb2
bb1(%3 : $X):
return %3 : $X
bb2(%5 : $Error):
throw %5 : $Error
}
sil @unknown_throwing_func : $@convention(thin) (@owned X) -> (@owned X, @error Error)
// Test that the deinit of a box itself does not capture anything.
// CHECK-LABEL: CG of test_release_of_partial_apply_with_box
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%1.3)
// CHECK-NEXT: Val %1 Esc: %6, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: %6, Succ: (%1.2)
// CHECK-NEXT: Con %1.2 Esc: %6, Succ: %0
// CHECK-NEXT: Con %1.3 Esc: G, Succ:
// CHECK-NEXT: Val %5 Esc: %6, Succ: %1
// CHECK-NEXT: End
sil @test_release_of_partial_apply_with_box : $@convention(thin) (@owned Y) -> () {
bb0(%0 : $Y):
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Y>
%2 = project_box %1 : $<τ_0_0> { var τ_0_0 } <Y>, 0
store %0 to %2 : $*Y
%3 = function_ref @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Y>) -> ()
%4 = partial_apply %3(%1) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Y>) -> ()
strong_release %4 : $@callee_owned () -> ()
%6 = tuple ()
return %6 : $()
}
sil @take_y_box : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Y>) -> ()
// Test is an unknown value is merged correctly into the caller graph.
// CHECK-LABEL: CG of store_to_unknown_reference
// CHECK-NEXT: Arg %0 Esc: G, Succ:
// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1)
// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2)
// CHECK-NEXT: Con %2.2 Esc: G, Succ: %0
// CHECK-NEXT: End
sil @store_to_unknown_reference : $@convention(thin) (@owned X) -> () {
bb0(%0 : $X):
%1 = function_ref @get_reference : $@convention(thin) () -> @owned Y
%2 = apply %1() : $@convention(thin) () -> @owned Y
%3 = ref_element_addr %2 : $Y, #Y.x
store %0 to %3 : $*X
%5 = tuple ()
return %5 : $()
}
// CHECK-LABEL: CG of get_reference
// CHECK-NEXT: Val %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %1
// CHECK-NEXT: End
sil @get_reference : $@convention(thin) () -> @owned Y {
bb0:
%0 = function_ref @unknown_get_reference : $@convention(thin) () -> @owned Y
%1 = apply %0() : $@convention(thin) () -> @owned Y
return %1 : $Y
}
sil @unknown_get_reference : $@convention(thin) () -> @owned Y
// A test for a specific case of callee->caller graph merging.
sil @unknown_set_y : $@convention(thin) () -> @out Y
// CHECK-LABEL: CG of get_y
// CHECK-NEXT: Val %0 Esc: G, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: G, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0.1
// CHECK-NEXT: End
sil @get_y : $@convention(thin) () -> @owned Y {
bb0:
%0 = alloc_stack $Y
%f = function_ref @unknown_set_y : $@convention(thin) () -> @out Y
%a = apply %f(%0) : $@convention(thin) () -> @out Y
%r = load %0 : $*Y
%5 = dealloc_stack %0 : $*Y
return %r : $Y
}
// CHECK-LABEL: CG of create_and_store_x
// CHECK-NEXT: Val %0 Esc: G, Succ:
// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1)
// CHECK-NEXT: Con %2.1 Esc: G, Succ: (%2.2)
// CHECK-NEXT: Con %2.2 Esc: G, Succ: %0
// CHECK-NEXT: End
sil @create_and_store_x : $@convention(thin) () -> () {
bb0:
%0 = alloc_ref $X
%f = function_ref @get_y : $@convention(thin) () -> @owned Y
%y = apply %f() : $@convention(thin) () -> @owned Y
%x = ref_element_addr %y : $Y, #Y.x
store %0 to %x : $*X
%r = tuple()
return %r : $()
}
// Test types which are considered as pointers.
// CHECK-LABEL: CG of pointer_types
// CHECK-NEXT: Arg %0 Esc: A, Succ: %1
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: Val %7 Esc: R, Succ: %0
// CHECK-NEXT: Ret Esc: R, Succ: %7
// CHECK-NEXT: End
sil @pointer_types : $@convention(thin) (@owned Y, @owned Y) -> @owned Y {
bb0(%0 : $Y, %1 : $Y):
%2 = struct $Pointer (%0 : $Y)
%3 = struct $Pointer (%1 : $Y)
%4 = tuple (%2 : $Pointer, %3 : $Pointer)
%5 = enum $PointerEnum, #PointerEnum.B!enumelt.1, %4 : $(Pointer, Pointer)
switch_enum %5 : $PointerEnum, case #PointerEnum.B!enumelt.1: bb1
bb1(%7 : $(Pointer, Pointer)):
%8 = tuple_extract %7 : $(Pointer, Pointer), 1
%9 = struct_extract %8 : $Pointer, #Pointer.y
return %9 : $Y
}
// CHECK-LABEL: CG of defer_edge_cycle
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.1
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: A, Succ:
// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%0.2), %0.1
// CHECK-NEXT: End
sil @defer_edge_cycle : $@convention(thin) (@inout Y, @inout Y) -> () {
entry(%0 : $*Y, %1 : $*Y):
%l1 = load %0 : $*Y
store %l1 to %1 : $*Y
%l2 = load %1 : $*Y
store %l2 to %0 : $*Y
%x = ref_element_addr %l1 : $Y, #Y.x
%l3 = load %x : $*X
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: CG of take_c_func
// CHECK-NEXT: Arg %0 Esc: G, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: G, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @take_c_func : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> () {
bb0(%0 : $@convention(c) () -> ()):
%3 = apply %0() : $@convention(c) () -> ()
return %0 : $@convention(c) () -> ()
}
// CHECK-LABEL: CG of c_func
// CHECK-NEXT: End
sil @c_func : $@convention(c) () -> () {
bb0:
%0 = tuple ()
return %0 : $()
}
// CHECK-LABEL: CG of pass_c_func
// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1)
// CHECK-NEXT: Con %2.1 Esc: G, Succ:
// CHECK-NEXT: End
sil @pass_c_func : $@convention(thin) () -> () {
bb0:
%0 = function_ref @take_c_func : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> ()
%1 = function_ref @c_func : $@convention(c) () -> ()
%2 = apply %0(%1) : $@convention(thin) (@convention(c) () -> ()) -> @convention(c) () -> ()
%3 = tuple ()
return %3 : $()
}
// CHECK-LABEL: CG of test_select_enum
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: Arg %2 Esc: A, Succ:
// CHECK-NEXT: Arg %3 Esc: A, Succ:
// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3
// CHECK-NEXT: Ret Esc: R, Succ: %4
// CHECK-NEXT: End
sil @test_select_enum : $@convention(thin) (PointerEnum2, @owned X, @owned X, @owned X) -> @owned X {
bb0(%0 : $PointerEnum2, %1 : $X, %2 : $X, %3 : $X):
%4 = select_enum %0 : $PointerEnum2, case #PointerEnum2.A!enumelt: %1, case #PointerEnum2.B!enumelt: %2, default %3 : $X
return %4 : $X
}
// CHECK-LABEL: CG of test_select_enum_addr
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: Arg %2 Esc: A, Succ:
// CHECK-NEXT: Arg %3 Esc: A, Succ:
// CHECK-NEXT: Val %4 Esc: R, Succ: %1, %2, %3
// CHECK-NEXT: Ret Esc: R, Succ: %4
// CHECK-NEXT: End
sil @test_select_enum_addr : $@convention(thin) (@in PointerEnum2, @owned X, @owned X, @owned X) -> @owned X {
bb0(%0 : $*PointerEnum2, %1 : $X, %2 : $X, %3 : $X):
%4 = select_enum_addr %0 : $*PointerEnum2, case #PointerEnum2.A!enumelt: %1, case #PointerEnum2.B!enumelt: %2, default %3 : $X
return %4 : $X
}
// CHECK-LABEL: CG of test_select_value
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: Arg %2 Esc: A, Succ:
// CHECK-NEXT: Arg %3 Esc: A, Succ:
// CHECK-NEXT: Val %6 Esc: R, Succ: %1, %2, %3
// CHECK-NEXT: Ret Esc: R, Succ: %6
// CHECK-NEXT: End
sil @test_select_value : $@convention(thin) (Builtin.Int64, @owned X, @owned X, @owned X) -> @owned X {
bb0(%0 : $Builtin.Int64, %1 : $X, %2 : $X, %3 : $X):
%4 = integer_literal $Builtin.Int64, 0
%5 = integer_literal $Builtin.Int64, 1
%6 = select_value %0 : $Builtin.Int64, case %4: %1, case %5: %2, default %3 : $X
return %6 : $X
}
// CHECK-LABEL: CG of test_existential_addr
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: , Succ: (%1.2)
// CHECK-NEXT: Con %1.2 Esc: , Succ: %0
// CHECK-NEXT: End
sil @test_existential_addr : $@convention(thin) (@owned Pointer) -> () {
bb0(%0 : $Pointer):
%1 = alloc_stack $P
%2 = init_existential_addr %1 : $*P, $Pointer
store %0 to %2 : $*Pointer
%4 = open_existential_addr immutable_access %1 : $*P to $*@opened("C62B1408-97C8-11E5-8D7C-685B35C48C83") P
%5 = witness_method $@opened("C62B1408-97C8-11E5-8D7C-685B35C48C83") P, #P.foo!1, %4 : $*@opened("C62B1408-97C8-11E5-8D7C-685B35C48C83") P : $@convention(witness_method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
dealloc_stack %1 : $*P
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: CG of test_existential_ref
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @test_existential_ref : $@convention(thin) (@owned X) -> () {
bb0(%0 : $X):
%1 = init_existential_ref %0 : $X : $X, $ClassP
%2 = open_existential_ref %1 : $ClassP to $@opened("EC6B6A86-9887-11E5-926E-685B35C48C83") ClassP
%3 = witness_method $@opened("EC6B6A86-9887-11E5-926E-685B35C48C83") ClassP, #ClassP.foo!1, %2 : $@opened("EC6B6A86-9887-11E5-926E-685B35C48C83") ClassP : $@convention(witness_method) <τ_0_0 where τ_0_0 : ClassP> (@guaranteed τ_0_0) -> ()
%7 = tuple ()
return %7 : $()
}
// We don't handle existential boxes currently.
// Check that we don't crash on this.
// CHECK-LABEL: CG of test_unknown_store
// CHECK-NEXT: Arg %0 Esc: G, Succ:
// CHECK-NEXT: Val %2 Esc: G, Succ: (%2.1)
// CHECK-NEXT: Con %2.1 Esc: G, Succ: %0
// CHECK-NEXT: End
sil @test_unknown_store : $@convention(thin) (@owned ErrorClass) -> () {
bb0(%0 : $ErrorClass):
%2 = alloc_existential_box $Error, $ErrorClass
%3 = project_existential_box $ErrorClass in %2 : $Error
store %0 to %3 : $*ErrorClass
%5 = tuple ()
return %5 : $()
}
// CHECK-LABEL: CG of test_raw_pointer_to_ref
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @test_raw_pointer_to_ref : $@convention(thin) (@owned X) -> @owned X {
bb0(%0 : $X):
%1 = ref_to_raw_pointer %0 : $X to $Builtin.RawPointer
%2 = raw_pointer_to_ref %1 : $Builtin.RawPointer to $X
return %2 : $X
}
// CHECK-LABEL: CG of test_bridge_object_to_ref
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @test_bridge_object_to_ref : $@convention(thin) (@owned X, Builtin.Word) -> @owned X {
bb0(%0 : $X, %1 : $Builtin.Word):
%2 = ref_to_bridge_object %0 : $X, %1 : $Builtin.Word
%3 = bridge_object_to_ref %2 : $Builtin.BridgeObject to $X
return %3 : $X
}
// CHECK-LABEL: CG of test_address_to_pointer
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: End
sil @test_address_to_pointer : $@convention(thin) (@owned X) -> @out X {
bb0(%0 : $*X, %1 : $X):
%2 = address_to_pointer %0 : $*X to $Builtin.RawPointer
%3 = pointer_to_address %2 : $Builtin.RawPointer to [strict] $*X
store %1 to %3 : $*X
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: CG of test_casts
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @test_casts : $@convention(thin) (@owned AnyObject) -> @owned X {
bb0(%0 : $AnyObject):
%1 = unchecked_ref_cast %0 : $AnyObject to $Derived
%2 = upcast %1 : $Derived to $X
return %2 : $X
}
// CHECK-LABEL: CG of test_unchecked_ref_cast_addr
// CHECK-NEXT: Arg %0 Esc: A, Succ: %1
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: End
sil @test_unchecked_ref_cast_addr : $@convention(thin) <T, U> (@in T, @thick U.Type) -> @out U {
bb0(%0 : $*U, %1 : $*T, %2 : $@thick U.Type):
unchecked_ref_cast_addr T in %1 : $*T to U in %0 : $*U
%5 = tuple ()
return %5 : $()
}
// CHECK-LABEL: CG of test_pin
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Ret Esc: R, Succ: %0
// CHECK-NEXT: End
sil @test_pin : $@convention(thin) (Builtin.NativeObject) -> Optional<Builtin.NativeObject> {
bb0(%0 : $Builtin.NativeObject):
%1 = strong_pin %0 : $Builtin.NativeObject
return %1 : $Optional<Builtin.NativeObject>
}
// CHECK-LABEL: CG of test_unpin
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ:
// CHECK-NEXT: End
sil @test_unpin : $@convention(thin) (Optional<Builtin.NativeObject>) -> () {
bb0(%0 : $Optional<Builtin.NativeObject>):
strong_unpin %0 : $Optional<Builtin.NativeObject>
%2 = tuple ()
return %2 : $()
}
sil_global @global_y : $SomeData
// CHECK-LABEL: CG of test_node_merge_during_struct_inst
// CHECK-NEXT: Arg %0 Esc: G, Succ: (%4.1)
// CHECK-NEXT: Val %1 Esc: G, Succ: (%4.1)
// CHECK-NEXT: Val %4 Esc: G, Succ: (%4.1)
// CHECK-NEXT: Con %4.1 Esc: G, Succ: (%4.1), %0, %1, %4
// CHECK-NEXT: End
sil @test_node_merge_during_struct_inst : $@convention(thin) (Y) -> () {
bb0(%0 : $Y):
%1 = global_addr @global_y : $*SomeData
%2 = address_to_pointer %1 : $*SomeData to $Builtin.RawPointer
%3 = raw_pointer_to_ref %2 : $Builtin.RawPointer to $Y
%4 = alloc_stack $Pointer
%5 = struct_element_addr %4 : $*Pointer, #Pointer.y
store %3 to %5 : $*Y
%7 = load %1 : $*SomeData
%8 = load %4 : $*Pointer
%9 = address_to_pointer %4 : $*Pointer to $Builtin.RawPointer
// Analyzing this instruction causes a node merge.
// Check that we don't crash on this.
%l = struct $FourFields(%7 : $SomeData, %8 : $Pointer, %9 : $Builtin.RawPointer, %0 : $Y)
dealloc_stack %4 : $*Pointer
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_is_native_no_typecheck
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @arraysemantics_is_native_no_typecheck : $@convention(thin) (Array<X>) -> () {
bb0(%0 : $Array<X>):
%f = function_ref @is_native_type_checked : $@convention(method) (@guaranteed Array<X>) -> Bool
%a = apply %f(%0) : $@convention(method) (@guaranteed Array<X>) -> Bool
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_check_subscript
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @arraysemantics_check_subscript : $@convention(thin) (Array<X>) -> () {
bb0(%0 : $Array<X>):
%il = integer_literal $Builtin.Int32, 0
%i = struct $Int32(%il : $Builtin.Int32)
%bl = integer_literal $Builtin.Int1, 0
%b = struct $Bool (%bl : $Builtin.Int1)
%f = function_ref @check_subscript : $@convention(method) (Int32, Bool, @guaranteed Array<X>) -> ()
%a = apply %f(%i, %b, %0) : $@convention(method) (Int32, Bool, @guaranteed Array<X>) -> ()
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_check_index
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @arraysemantics_check_index : $@convention(thin) (Array<X>) -> () {
bb0(%0 : $Array<X>):
%il = integer_literal $Builtin.Int32, 0
%i = struct $Int32(%il : $Builtin.Int32)
%f = function_ref @check_index : $@convention(method) (Int32, @guaranteed Array<X>) -> ()
%a = apply %f(%i, %0) : $@convention(method) (Int32, @guaranteed Array<X>) -> ()
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_get_element
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1.2
// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%1.2)
// CHECK-NEXT: Con %1.2 Esc: A, Succ:
// CHECK-NEXT: End
sil @arraysemantics_get_element : $@convention(thin) (Array<X>) -> @out X {
bb0(%io : $*X, %1 : $Array<X>):
%il = integer_literal $Builtin.Int32, 0
%i = struct $Int32(%il : $Builtin.Int32)
%bl = integer_literal $Builtin.Int1, 0
%b = struct $Bool (%bl : $Builtin.Int1)
%f = function_ref @get_element : $@convention(method) (Int32, Bool, Bool, @guaranteed Array<X>) -> @out X
%a = apply %f(%io, %i, %b, %b, %1) : $@convention(method) (Int32, Bool, Bool, @guaranteed Array<X>) -> @out X
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_make_mutable
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @arraysemantics_make_mutable : $@convention(thin) (@inout Array<X>) -> () {
bb0(%0 : $*Array<X>):
%f = function_ref @make_mutable : $@convention(method) (@inout Array<X>) -> ()
%a = apply %f(%0) : $@convention(method) (@inout Array<X>) -> ()
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_get_element_address
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ:
// CHECK-NEXT: Val %4 Esc: , Succ: %0.1
// CHECK-NEXT: End
sil @arraysemantics_get_element_address : $@convention(thin) (Array<X>) -> () {
bb0(%0 : $Array<X>):
%il = integer_literal $Builtin.Int32, 0
%i = struct $Int32(%il : $Builtin.Int32)
%f = function_ref @get_element_address : $@convention(method) (Int32, @guaranteed Array<X>) -> UnsafeMutablePointer<X>
%a = apply %f(%i, %0) : $@convention(method) (Int32, @guaranteed Array<X>) -> UnsafeMutablePointer<X>
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_get_count
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @arraysemantics_get_count : $@convention(thin) (Array<X>) -> () {
bb0(%0 : $Array<X>):
%f = function_ref @get_count : $@convention(method) (@guaranteed Array<X>) -> Int32
%a = apply %f(%0) : $@convention(method) (@guaranteed Array<X>) -> Int32
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_get_capacity
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @arraysemantics_get_capacity : $@convention(thin) (Array<X>) -> () {
bb0(%0 : $Array<X>):
%f = function_ref @get_capacity : $@convention(method) (@guaranteed Array<X>) -> Int32
%a = apply %f(%0) : $@convention(method) (@guaranteed Array<X>) -> Int32
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_withUnsafeMutableBufferPointer
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: A, Succ: (%0.3)
// CHECK-NEXT: Con %0.3 Esc: G, Succ:
// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ:
// CHECK-NEXT: Val %3 Esc: %4, Succ:
// CHECK-NEXT: End
sil @arraysemantics_withUnsafeMutableBufferPointer : $@convention(thin) (@inout Array<X>, @owned @callee_owned (@inout X) -> (@out (), @error ErrorType)) -> () {
bb(%0 : $*Array<X>, %1 : $@callee_owned (@inout X) -> (@out (), @error ErrorType)):
%2 = function_ref @withUnsafeMutableBufferPointer : $@convention(method) (@owned @callee_owned (@inout X) -> (@out (), @error ErrorType), @inout Array<X>) -> (@out (), @error ErrorType)
%3 = alloc_stack $()
%4 = apply [nothrow] %2(%3, %1, %0) : $@convention(method) (@owned @callee_owned (@inout X) -> (@out (), @error ErrorType), @inout Array<X>) -> (@out (), @error ErrorType)
dealloc_stack %3 : $*()
%r = tuple()
return %r : $()
}
// CHECK-LABEL: CG of arraysemantics_createUninitialized
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Val %2 Esc: R, Succ: (%4.2)
// CHECK-NEXT: Val %4 Esc: R, Succ: (%4.1)
// CHECK-NEXT: Con %4.1 Esc: R, Succ: %2
// CHECK-NEXT: Con %4.2 Esc: R, Succ: %0
// CHECK-NEXT: Ret Esc: R, Succ: %4
// CHECK-NEXT: End
sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array<X> {
bb0(%0 : $X):
%1 = function_ref @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject
%2 = apply %1() : $@convention(thin) () -> @owned AnyObject
%3 = function_ref @createUninitialized : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
%4 = apply %3(%2) : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
%5 = tuple_extract %4 : $(Array<X>, UnsafeMutablePointer<X>), 0
%6 = tuple_extract %4 : $(Array<X>, UnsafeMutablePointer<X>), 1
%7 = struct_extract %6 : $UnsafeMutablePointer<X>, #UnsafeMutablePointer._rawValue
%8 = pointer_to_address %7 : $Builtin.RawPointer to $*X
store %0 to %8 : $*X
return %5 : $Array<X>
}
sil [_semantics "array.withUnsafeMutableBufferPointer"] @withUnsafeMutableBufferPointer : $@convention(method) (@owned @callee_owned (@inout X) -> (@out (), @error ErrorType), @inout Array<X>) -> (@out (), @error ErrorType)
sil [_semantics "array.props.isNativeTypeChecked"] @is_native_type_checked : $@convention(method) (@guaranteed Array<X>) -> Bool
sil [_semantics "array.check_subscript"] @check_subscript : $@convention(method) (Int32, Bool, @guaranteed Array<X>) -> ()
sil [_semantics "array.check_index"] @check_index : $@convention(method) (Int32, @guaranteed Array<X>) -> ()
sil [_semantics "array.get_element"] @get_element : $@convention(method) (Int32, Bool, Bool, @guaranteed Array<X>) -> @out X
sil [_semantics "array.make_mutable"] @make_mutable : $@convention(method) (@inout Array<X>) -> ()
sil [_semantics "array.get_element_address"] @get_element_address : $@convention(method) (Int32, @guaranteed Array<X>) -> UnsafeMutablePointer<X>
sil [_semantics "array.get_count"] @get_count : $@convention(method) (@guaranteed Array<X>) -> Int32
sil [_semantics "array.get_capacity"] @get_capacity : $@convention(method) (@guaranteed Array<X>) -> Int32
sil [_semantics "pair_no_escaping_closure"] @unsafeWithNotEscapedSelfPointerPair : $@convention(method) (@owned X, @owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
sil [_semantics "self_no_escaping_closure"] @unsafeWithNotEscapedSelfPointer: $@convention(method) (@owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
// A simplified version of swift_bufferAllocate
sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject
// CHECK-LABEL: CG of semantics_pair_no_escaping_closure
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Arg %1 Esc: A, Succ:
// CHECK-NEXT: Arg %2 Esc: G, Succ: (%2.1)
// CHECK-NEXT: Con %2.1 Esc: G, Succ:
// CHECK-NEXT: Val %4 Esc: %5, Succ:
// CHECK-NEXT: End
sil @semantics_pair_no_escaping_closure : $@convention(thin) (@owned X, @guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error ErrorType)) -> () {
bb(%0 : $X, %1 : $X, %2: $@callee_owned (X, X) -> (@out X, @error ErrorType)):
%3 = function_ref @unsafeWithNotEscapedSelfPointerPair : $@convention(method) (@owned X, @owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
%4 = alloc_stack $X
%6 = apply [nothrow] %3(%4, %0, %2, %1) : $@convention(method) (@owned X, @owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
dealloc_stack %4 : $*X
%7 = tuple()
return %7 : $()
}
// CHECK-LABEL: CG of semantics_self_no_escaping_closure
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Arg %1 Esc: G, Succ: (%1.1)
// CHECK-NEXT: Con %1.1 Esc: G, Succ:
// CHECK-NEXT: Val %3 Esc: %4, Succ:
// CHECK-NEXT: End
sil @semantics_self_no_escaping_closure : $@convention(thin) (@guaranteed X, @owned @callee_owned (X, X) -> (@out X, @error ErrorType)) -> () {
bb(%0 : $X, %1: $@callee_owned (X, X) -> (@out X, @error ErrorType)):
%2 = function_ref @unsafeWithNotEscapedSelfPointer : $@convention(method) (@owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
%3 = alloc_stack $X
%6 = apply [nothrow] %2(%3, %1, %0) : $@convention(method) (@owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
dealloc_stack %3 : $*X
%7 = tuple()
return %7 : $()
}
// CHECK-LABEL: CG of check_dealloc_ref
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @check_dealloc_ref : $@convention(thin) (@owned X) -> () {
bb0(%0 : $X):
dealloc_ref %0 : $X
%1 = tuple ()
return %1 : $()
}
// CHECK-LABEL: CG of check_set_deallocating
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: End
sil @check_set_deallocating : $@convention(thin) (@owned X) -> () {
bb0(%0 : $X):
set_deallocating %0 : $X
%1 = tuple ()
return %1 : $()
}
// Escape analysis should analyze the implicit destructor call which may
// happen due to strong_release and figure out that the object will not
// globally escape.
// CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_strong_release
// CHECK-NEXT: Val %0 Esc: %1, Succ:
// CHECK-NEXT: End
sil @check_non_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () {
bb0:
%0 = alloc_ref [stack] $X
strong_release %0 : $X
dealloc_ref [stack] %0 : $X
%3 = tuple ()
return %3 : $()
}
// Escape analysis should analyze the implicit destructor call which may
// happen due to strong_release and figure out that the object will not
// globally escape.
// CHECK-LABEL: CG of check_non_escaping_implicit_destructor_invocation_via_release_value
// CHECK-NEXT: Val %0 Esc: %1, Succ:
// CHECK-NEXT: End
sil @check_non_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () {
bb0:
%0 = alloc_ref [stack] $X
release_value %0 : $X
dealloc_ref [stack] %0 : $X
%3 = tuple ()
return %3 : $()
}
// Escape analysis should analyze the implicit destructor call which may
// happen due to strong_release and figure out that the object will
// globally escape.
// CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_strong_release
// CHECK-NEXT: Val %0 Esc: %1, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: %1, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ:
// CHECK-NEXT: End
sil @check_escaping_implicit_destructor_invocation_via_strong_release : $@convention(thin) () -> () {
bb0:
%0 = alloc_ref [stack] $Z
strong_release %0 : $Z
dealloc_ref [stack] %0 : $Z
%3 = tuple ()
return %3 : $()
}
// Escape analysis should analyze the implicit destructor call which may
// happen due to strong_release and figure out that the object will
// globally escape.
// CHECK-LABEL: CG of check_escaping_implicit_destructor_invocation_via_release_value
// CHECK-NEXT: Val %0 Esc: %1, Succ: (%0.1)
// CHECK-NEXT: Con %0.1 Esc: %1, Succ: (%0.2)
// CHECK-NEXT: Con %0.2 Esc: G, Succ:
// CHECK-NEXT: End
sil @check_escaping_implicit_destructor_invocation_via_release_value : $@convention(thin) () -> () {
bb0:
%0 = alloc_ref [stack] $Z
release_value %0 : $Z
dealloc_ref [stack] %0 : $Z
%3 = tuple ()
return %3 : $()
}
// Check that escape analysis can look through bb arguments and figure
// out that all objects are local. By analyzing a destructor implicitly
// invoked by strong_release it would also determine that these objects
// do not escape from the function.
// CHECK-LABEL: CG of check_is_local_object_through_bb_args
// CHECK-LABEL: Val %2 Esc: %6,%7, Succ:
// CHECK-LABEL: Val %4 Esc: %6,%7, Succ:
// CHECK-LABEL: Val %6 Esc: %6,%7, Succ: %2, %4
// CHECK-LABEL: End
sil @check_is_local_object_through_bb_args: $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : $Builtin.Int1):
cond_br %0, bb1, bb2
bb1:
%2 = alloc_ref $X
br bb3(%2 : $X)
bb2:
%4 = alloc_ref $X
br bb3(%4 : $X)
bb3(%6: $X):
strong_release %6 : $X
%8 = tuple ()
return %8 : $()
}
// X.deinit
// CHECK-LABEL: CG of _T04main1XCfD
// CHECK: Arg %0 Esc: A, Succ:
// CHECKL: End
sil @_T04main1XCfD: $@convention(method) (@owned X) -> () {
bb0(%0 : $X):
%1 = tuple ()
return %1 : $()
}
// Z.deinit
// CHECK-LABEL: CG of _T04main1ZCfD
// CHECK: Arg %0 Esc: A, Succ: (%0.1)
// CHECK: Con %0.1 Esc: A, Succ: (%0.2)
// CHECK: Con %0.2 Esc: G, Succ:
// CHECK: Val %3 Esc: G, Succ: (%3.1)
// CHECK: Con %3.1 Esc: G, Succ: %0.2
// CHECKL: End
sil @_T04main1ZCfD: $@convention(method) (@owned Z) -> () {
bb0(%0 : $Z):
%1 = ref_element_addr %0 : $Z, #Z.x
%2 = load %1 : $*X
%3 = global_addr @global_x : $*X
store %2 to %3 : $*X
%5 = tuple ()
return %5 : $()
}
sil_vtable X {
#X.deinit!deallocator: _T04main1XCfD
}
sil_vtable Z {
#Z.deinit!deallocator: _T04main1ZCfD
}