| // 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 : $() |
| } |