blob: c1427377063e123bcbbac076d6b0b20f30813c81 [file] [log] [blame]
// RUN: %target-sil-opt -destroy-hoisting %s | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
import SwiftShims
class X {
}
enum TwoCases {
case A(X)
case B
}
struct S {
var x: X
}
struct Outer {
var s: S
var ox: X
}
struct Mixed {
var x: X
var i: Int
}
public struct S2 {
let s: S
}
public enum E {
case A
case B
}
sil @unknown : $@convention(thin) () -> ()
sil @use_S : $@convention(thin) (@in_guaranteed S) -> ()
// CHECK-LABEL: sil [ossa] @test_simple
// CHECK: bb0(%0 : $*S):
// CHECK-NEXT: destroy_addr %0
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @test_simple : $@convention(thin) (@in S) -> () {
bb0(%0 : $*S):
br bb1
bb1:
destroy_addr %0 : $*S
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @dont_move_over_pure_insts
// CHECK: bb0(%0 : $*S):
// CHECK-NEXT: tuple
// CHECK-NEXT: destroy_addr %0
// CHECK-NEXT: return
sil [ossa] @dont_move_over_pure_insts : $@convention(thin) (@in S) -> () {
bb0(%0 : $*S):
%r = tuple ()
destroy_addr %0 : $*S
return %r : $()
}
// CHECK-LABEL: sil [ossa] @combine_load
// CHECK: bb0(%0 : $*S):
// CHECK-NEXT: load [take] %0
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: return
sil [ossa] @combine_load : $@convention(thin) (@in S) -> @owned S {
bb0(%0 : $*S):
%v = load [copy] %0 : $*S
br bb1
bb1:
destroy_addr %0 : $*S
return %v : $S
}
// CHECK-LABEL: sil [ossa] @combine_copy_addr
// CHECK: bb0(%0 : $*S, %1 : $*S):
// CHECK-NEXT: copy_addr [take] %1 to [initialization] %0
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @combine_copy_addr : $@convention(thin) (@in S) -> @out S {
bb0(%0 : $*S, %1 : $*S):
copy_addr %1 to [initialization] %0 : $*S
br bb1
bb1:
destroy_addr %1 : $*S
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @tail_merging
// CHECK: bb1:
// CHECK: apply
// CHECK-NEXT: br bb3
// CHECK: bb2:
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: destroy_addr %0
// CHECK-NEXT: br bb4
// CHECK: bb4:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @tail_merging : $@convention(thin) (@in S) -> () {
bb0(%0 : $*S):
cond_br undef, bb1, bb2
bb1:
%f = function_ref @use_S : $@convention(thin) (@in_guaranteed S) -> ()
%a = apply %f(%0) : $@convention(thin) (@in_guaranteed S) -> ()
br bb3
bb2:
br bb3
bb3:
br bb4
bb4:
destroy_addr %0 : $*S
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @store_splitting
// CHECK: destroy_addr %0
// CHECK-NEXT: store %1 to [init] %0
// CHECK: return
sil [ossa] @store_splitting : $@convention(thin) (@inout S, @owned S, @guaranteed TwoCases) -> () {
bb0(%0 : $*S, %1 : @owned $S, %2 : @guaranteed $TwoCases):
switch_enum %2 : $TwoCases, case #TwoCases.A!enumelt: bb1, case #TwoCases.B!enumelt: bb2
bb1(%4 : @guaranteed $X):
%5 = enum $TwoCases, #TwoCases.B!enumelt
store %1 to [assign] %0 : $*S
br bb3
bb2:
destroy_value %1 : $S
br bb3
bb3:
%r = tuple ()
return %r : $()
}
// Check several things: critical edge splitting, handling of sub-locations, complex control flow
// CHECK-LABEL: sil [ossa] @test_complex
// CHECK: bb0(%0 : $*Outer, %1 : $*Outer):
// CHECK: [[OOX:%[0-9]+]] = struct_element_addr %0 : $*Outer, #Outer.ox
// CHECK-NEXT: destroy_addr [[OOX]]
// CHECK: [[OS:%[0-9]+]] = struct_element_addr %0 : $*Outer, #Outer.s
// CHECK: cond_br undef, bb1, bb2
// CHECK: bb1:
// CHECK-NEXT: apply
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK-NEXT: destroy_addr [[OS]]
// CHECK-NEXT: br bb4
// CHECK: bb4:
// CHECK: apply
// CHECK: cond_br undef, bb5, bb6
// CHECK: bb5:
// CHECK-NEXT: br bb4
// CHECK: bb6:
// CHECK-NEXT: destroy_addr %1
// CHECK: br bb7
// CHECK: bb7:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil [ossa] @test_complex : $@convention(thin) (@in Outer, @in Outer) -> () {
bb0(%0 : $*Outer, %1 : $*Outer):
%f = function_ref @use_S : $@convention(thin) (@in_guaranteed S) -> ()
%2 = struct_element_addr %0 : $*Outer, #Outer.s
cond_br undef, bb1, bb2
bb1:
%a1 = apply %f(%2) : $@convention(thin) (@in_guaranteed S) -> ()
br bb3
bb2:
br bb3
bb3:
br bb4
bb4:
%4 = struct_element_addr %1 : $*Outer, #Outer.s
%a2 = apply %f(%4) : $@convention(thin) (@in_guaranteed S) -> ()
cond_br undef, bb5, bb6
bb5:
br bb4
bb6:
destroy_addr %2 : $*S
%3 = struct_element_addr %0 : $*Outer, #Outer.ox
destroy_addr %3 : $*X
br bb7
bb7:
destroy_addr %1 : $*Outer
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @test_mixed
// CHECK: load
// CHECK: destroy_addr %0
// CHECK: br bb1
// CHECK: return
sil [ossa] @test_mixed : $@convention(thin) (@in Mixed) -> Int {
bb0(%0 : $*Mixed):
%2 = struct_element_addr %0 : $*Mixed, #Mixed.i
%v = load [trivial] %2 : $*Int
br bb1
bb1:
destroy_addr %0 : $*Mixed
return %v : $Int
}
sil @coro : $@yield_once @convention(thin) (@in S) -> @yields @inout Int
// CHECK-LABEL: sil [ossa] @test_begin_apply
// CHECK: end_apply
// CHECK-NEXT: destroy_addr %0
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK: return
sil [ossa] @test_begin_apply : $@convention(thin) (@in S, Int) -> () {
bb0(%0 : $*S, %1 : $Int):
%f = function_ref @coro : $@yield_once @convention(thin) (@in S) -> @yields @inout Int
(%i, %t) = begin_apply %f(%0) : $@yield_once @convention(thin) (@in S) -> @yields @inout Int
store %1 to [trivial] %i : $*Int
end_apply %t
br bb1
bb1:
destroy_addr %0 : $*S
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @test_abort_apply
// CHECK: abort_apply
// CHECK-NEXT: destroy_addr %0
// CHECK-NEXT: br bb1
// CHECK: bb1:
// CHECK: return
sil [ossa] @test_abort_apply : $@convention(thin) (@in S, Int) -> () {
bb0(%0 : $*S, %1 : $Int):
%f = function_ref @coro : $@yield_once @convention(thin) (@in S) -> @yields @inout Int
(%i, %t) = begin_apply %f(%0) : $@yield_once @convention(thin) (@in S) -> @yields @inout Int
abort_apply %t
br bb1
bb1:
destroy_addr %0 : $*S
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @test_simple_infinite_loop
// CHECK-NOT: destroy_addr
// CHECK: } // end sil function 'test_simple_infinite_loop'
sil [ossa] @test_simple_infinite_loop : $@convention(thin) (@in_guaranteed S) -> () {
bb0(%0 : $*S):
br bb1
bb1:
br bb1
}
// CHECK-LABEL: sil [ossa] @test_infinite_loop
// CHECK-NOT: destroy_addr
// CHECK: bb4:
// CHECK-NEXT: destroy_addr %1
// CHECK-NOT: destroy_addr
// CHECK: } // end sil function 'test_infinite_loop'
sil [ossa] @test_infinite_loop : $@convention(thin) (@in_guaranteed S, @in S) -> () {
bb0(%0 : $*S, %1 : $*S):
cond_br undef, bb1, bb4
bb1:
br bb2
bb2:
br bb3
bb3:
br bb2
bb4:
br bb5
bb5:
destroy_addr %1 : $*S
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @test_debug_value
// CHECK: bb0({{.*}}):
// CHECK-NEXT: struct_element_addr
// CHECK-NEXT: destroy_addr
// CHECK-NEXT: br bb1
// CHECK: } // end sil function 'test_debug_value'
sil [ossa] @test_debug_value : $@convention(method) (@inout S2, @owned X, @owned S, @inout E) -> () {
bb0(%0 : $*S2, %1 : @owned $X, %2 : @owned $S, %3 : $*E):
debug_value_addr %0 : $*S2, var, name "self", argno 1
br bb1
bb1:
%60 = struct_element_addr %0 : $*S2, #S2.s
destroy_addr %60 : $*S
store %2 to [init] %60 : $*S
%71 = struct_element_addr %60 : $*S, #S.x
store %1 to [assign] %71 : $*X
%75 = tuple ()
return %75 : $()
}