| // RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil %s -dead-store-elim -max-partial-store-count=2 -enable-sil-verify-all | %FileCheck %s |
| |
| sil_stage canonical |
| |
| import Swift |
| import Builtin |
| |
| /////////////////////// |
| // Type Declarations // |
| /////////////////////// |
| |
| struct Int { |
| var value : Builtin.Int64 |
| } |
| |
| struct Int64 { |
| var value : Builtin.Int64 |
| } |
| |
| struct Bool { |
| var value : Builtin.Int1 |
| } |
| |
| struct A { |
| var i : Builtin.Int32 |
| } |
| |
| struct AA { |
| var a : A |
| var i : Builtin.Int32 |
| } |
| |
| struct S1 { |
| var a: Int |
| init(a: Int, b: Int) |
| init() |
| } |
| |
| struct S2 { |
| var x: S1 |
| var a: Int |
| var b: Int |
| init(x: S1, a: Int, b: Int) |
| init() |
| } |
| |
| struct S3 { |
| var a: Int |
| var b: Int |
| init(a: Int, b: Int) |
| init() |
| } |
| |
| struct S4 { |
| var a: Int |
| var b: Int |
| init(a: Int, b: Int) |
| init() |
| } |
| |
| struct S5 { |
| var x: S4 |
| var y: Int |
| init(x: S4, y: Int) |
| init() |
| } |
| |
| struct S6 { |
| var x: Int |
| var y: Int |
| var z: Int |
| init(x: Int, y: Int, z: Int) |
| init() |
| } |
| |
| struct S7 { |
| var x: Int |
| var y: Int |
| var z: Int |
| var a: Int |
| var b: Int |
| var c: Int |
| init(x: Int, y: Int, z: Int, a: Int, b: Int, c: Int) |
| init() |
| } |
| |
| |
| class SelfLoop { |
| var a: SelfLoop |
| init() |
| deinit |
| } |
| |
| struct S8 { |
| var i: SelfLoop |
| var k: Int |
| init(i: SelfLoop, k: Int) |
| init() |
| } |
| |
| class foo { |
| var a: Int |
| deinit |
| init() |
| } |
| |
| class AB { |
| var value: Int |
| init(value: Int) |
| deinit |
| } |
| |
| |
| class B { |
| var i : Builtin.Int32 |
| init() |
| } |
| |
| enum Example { |
| case A(Int64, Int64) |
| case B(Int64) |
| case C |
| case D |
| } |
| |
| struct S9 { |
| var x : S3 |
| init(x : S3) |
| init() |
| } |
| |
| sil @foo_user : $@convention(thin) (@guaranteed foo) -> () |
| sil @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| sil @S2_init : $@convention(thin) (@thin S2.Type) -> S2 |
| sil @S3_init : $@convention(thin) (@thin S3.Type) -> S3 |
| sil @S5_init : $@convention(thin) (@thin S5.Type) -> S5 |
| sil @S6_init : $@convention(thin) (@thin S6.Type) -> S6 |
| sil @S7_init : $@convention(thin) (@thin S7.Type) -> S7 |
| sil @S8_init : $@convention(thin) (@thin S8.Type) -> @owned S8 |
| sil @S9_init : $@convention(thin) (@thin S9.Type) -> S9 |
| sil @escaped_a : $@convention(thin) () -> Builtin.RawPointer |
| |
| // CHECK-LABEL: sil hidden @dead_store_across_fixlifetime_inst |
| // CHECK: bb0 |
| // CHECK-NOT: {{ store}} |
| // CHECK: {{ store}} |
| sil hidden @dead_store_across_fixlifetime_inst : $@convention(thin) (@owned AB, Int) -> () { |
| bb0(%0 : $AB, %1 : $Int): |
| %2 = ref_element_addr %0 : $AB, #AB.value |
| store %1 to %2 : $*Int |
| %4 = ref_element_addr %0 : $AB, #AB.value |
| fix_lifetime %0 : $AB |
| store %1 to %2 : $*Int |
| %18 = tuple () |
| return %18 : $() |
| } |
| |
| // We should be able to remove the local store that is not read. |
| // |
| // CHECK-LABEL: trivial_local_dead_store |
| // CHECK: bb0 |
| // CHECK-NOT: {{ store}} |
| // CHECK: return |
| sil hidden @trivial_local_dead_store : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $Int, var, name "a" |
| %1 = integer_literal $Builtin.Int64, 1 |
| %2 = struct $Int (%1 : $Builtin.Int64) |
| store %2 to %0 : $*Int |
| %4 = tuple () |
| dealloc_stack %0 : $*Int |
| return %4 : $() |
| } |
| |
| // We cannot remove the local store that is read. |
| // |
| // CHECK-LABEL: blocking_read_on_local_store |
| // CHECK: bb0 |
| // CHECK: {{ store}} |
| // CHECK: return |
| sil hidden @blocking_read_on_local_store : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $Int, var, name "a" |
| %1 = integer_literal $Builtin.Int64, 1 |
| %2 = struct $Int (%1 : $Builtin.Int64) |
| store %2 to %0 : $*Int |
| %4 = tuple () |
| %99 = load %0 : $*Int |
| dealloc_stack %0 : $*Int |
| return %4 : $() |
| } |
| |
| // CHECK-LABEL: sil @store_after_store |
| // CHECK: bb0 |
| // CHECK: {{ store}} |
| // CHECK-NOT: {{ store}} |
| // CHECK: return |
| sil @store_after_store : $@convention(thin) (@owned B) -> () { |
| bb0(%0 : $B): |
| %1 = alloc_box $<Ï„_0_0> { var Ï„_0_0 } <B> |
| %1a = project_box %1 : $<Ï„_0_0> { var Ï„_0_0 } <B>, 0 |
| %2 = store %0 to %1a : $*B |
| %3 = store %0 to %1a : $*B |
| %4 = tuple() |
| %5 = return %4 : $() |
| } |
| |
| // CHECK-LABEL: sil @dead_store_elimination_over_noread_builtins |
| // CHECK: bb0 |
| // CHECK-NEXT: load |
| // CHECK-NEXT: integer_literal |
| // CHECK-NEXT: builtin |
| // CHECK-NEXT: tuple_extract |
| // CHECK-NEXT: {{ store}} |
| // CHECK-NEXT: tuple |
| // CHECK-NEXT: return |
| sil @dead_store_elimination_over_noread_builtins : $@convention(thin) (@inout Builtin.Int64, @inout Builtin.Int64) -> () { |
| bb0(%0 : $*Builtin.Int64, %1 : $*Builtin.Int64): |
| %2 = load %0 : $*Builtin.Int64 |
| %4 = integer_literal $Builtin.Int1, 0 |
| %5 = builtin "sadd_with_overflow_Int64"(%2 : $Builtin.Int64, %2 : $Builtin.Int64, %4 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) |
| %6 = tuple_extract %5 : $(Builtin.Int64, Builtin.Int1), 0 |
| store %6 to %1 : $*Builtin.Int64 |
| %8 = builtin "smul_with_overflow_Int64"(%2 : $Builtin.Int64, %2 : $Builtin.Int64, %4 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) |
| %9 = tuple_extract %8 : $(Builtin.Int64, Builtin.Int1), 0 |
| store %9 to %1 : $*Builtin.Int64 |
| %10 = tuple() |
| return %10 : $() |
| } |
| |
| // CHECK-LABEL: sil @post_dominating_dead_store : $@convention(thin) (@inout Builtin.Int32) -> () { |
| // CHECK: {{ store}} |
| // CHECK-NOT: {{ store}} |
| // CHECK: return |
| sil @post_dominating_dead_store : $@convention(thin) (@inout Builtin.Int32) -> () { |
| bb0(%0 : $*Builtin.Int32): |
| %1 = integer_literal $Builtin.Int32, 0 |
| store %1 to %0 : $*Builtin.Int32 |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| store %1 to %0 : $*Builtin.Int32 |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @post_dominating_dead_store_partial : $@convention(thin) (@inout Builtin.Int32) -> () { |
| // CHECK: bb0( |
| // CHECK-NOT: {{ store}} |
| // CHECK: bb1: |
| // CHECK_NOT: store |
| // CHECK: bb2: |
| // CHECK: bb3: |
| // CHECK: {{ store}} |
| // CHECK: return |
| sil @post_dominating_dead_store_partial : $@convention(thin) (@inout Builtin.Int32) -> () { |
| bb0(%0 : $*Builtin.Int32): |
| %1 = integer_literal $Builtin.Int32, 0 |
| %2 = integer_literal $Builtin.Int32, 1 |
| %3 = integer_literal $Builtin.Int32, 2 |
| store %1 to %0 : $*Builtin.Int32 |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| store %2 to %0 : $*Builtin.Int32 |
| br bb3 |
| |
| bb2: |
| br bb3 |
| |
| bb3: |
| store %3 to %0 : $*Builtin.Int32 |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We can't eliminate any stores here. |
| // CHECK-LABEL: sil @post_dominating_dead_store_fail : $@convention(thin) (@inout Builtin.Int32) -> () { |
| // CHECK: {{ store}} |
| // CHECK: {{ store}} |
| // CHECK-NOT: {{ store}} |
| // CHECK: return |
| sil @post_dominating_dead_store_fail : $@convention(thin) (@inout Builtin.Int32) -> () { |
| bb0(%0 : $*Builtin.Int32): |
| %1 = integer_literal $Builtin.Int32, 0 |
| %2 = integer_literal $Builtin.Int32, 1 |
| store %1 to %0 : $*Builtin.Int32 |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| store %2 to %0 : $*Builtin.Int32 |
| br bb2 |
| |
| bb2: |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We cannot remove the local store as the debug_value_addr could |
| // be turned to a debug_value and thus act as a read on the memory |
| // location.. |
| // |
| // CHECK-LABEL: blocking_debug_value_addr_on_dead_store |
| // CHECK: bb0 |
| // CHECK: {{ store}} |
| // CHECK: debug_value_addr |
| sil hidden @blocking_debug_value_addr_on_dead_store : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $Int, var, name "a" |
| %1 = integer_literal $Builtin.Int64, 1 |
| %2 = struct $Int (%1 : $Builtin.Int64) |
| store %2 to %0 : $*Int |
| debug_value_addr %0 : $*Int |
| %4 = tuple () |
| dealloc_stack %0 : $*Int |
| return %4 : $() |
| } |
| |
| // CHECK-LABEL: sil @test_read_dependence_allows_forwarding : $@convention(thin) (@inout A, A) -> A { |
| // CHECK: bb0 |
| // CHECK-NEXT: {{ store}} |
| // CHECK-NEXT: unchecked_addr_cast |
| // CHECK-NEXT: unchecked_addr_cast |
| // CHECK-NEXT: load |
| // CHECK-NEXT: {{ store}} |
| // CHECK-NEXT: return |
| sil @test_read_dependence_allows_forwarding : $@convention(thin) (@inout A, A) -> A { |
| bb0(%0 : $*A, %1 : $A): |
| store %1 to %0 : $*A |
| %2 = unchecked_addr_cast %0 : $*A to $*A |
| %3 = unchecked_addr_cast %2 : $*A to $*A |
| // This means that the first store is not dead. |
| %4 = load %3 : $*A |
| store %1 to %0 : $*A |
| return %4 : $A |
| } |
| |
| // Make sure that we do not eliminate a partially dead store as if it |
| // was a full dead store. |
| // |
| // CHECK-LABEL: sil @partially_dead_store1 : $@convention(thin) (@inout AA, AA, A) -> () { |
| // CHECK: {{ store}} |
| // CHECK: {{ store}} |
| sil @partially_dead_store1 : $@convention(thin) (@inout AA, AA, A) -> () { |
| bb0(%0 : $*AA, %1 : $AA, %2 : $A): |
| store %1 to %0 : $*AA |
| %3 = unchecked_addr_cast %0 : $*AA to $*A |
| store %2 to %3 : $*A |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Make sure that we do not eliminate a partially dead store as if it |
| // was a full dead store. |
| // |
| // CHECK-LABEL: sil @partially_dead_store2 : $@convention(thin) (@inout AA, AA, A) -> () { |
| // CHECK: {{ store}} |
| // CHECK: {{ store}} |
| sil @partially_dead_store2 : $@convention(thin) (@inout AA, AA, A) -> () { |
| bb0(%0 : $*AA, %1 : $AA, %2 : $A): |
| store %1 to %0 : $*AA |
| %3 = struct_element_addr %0 : $*AA, #AA.a |
| store %2 to %3 : $*A |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Make sure that we properly eliminate the stores in bb1, bb2 from StoreMap. |
| // |
| // CHECK-LABEL: sil @store_map_failed_dead_object_elim_invalidation : $@convention(thin) () -> () { |
| // CHECK: bb1: |
| // CHECK: {{ store}} |
| // CHECK: bb2: |
| // CHECK: {{ store}} |
| // CHECK: bb3: |
| // CHECK: {{ store}} |
| sil @store_map_failed_dead_object_elim_invalidation : $@convention(thin) () -> () { |
| bb0: |
| %0 = integer_literal $Builtin.Int32, 32 |
| %1 = integer_literal $Builtin.Int32, 64 |
| %2 = function_ref @escaped_a : $@convention(thin) () -> Builtin.RawPointer |
| %3 = apply %2() : $@convention(thin) () -> Builtin.RawPointer |
| %4 = pointer_to_address %3 : $Builtin.RawPointer to [strict] $*Builtin.Int32 |
| %5 = apply %2() : $@convention(thin) () -> Builtin.RawPointer |
| %6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*Builtin.Int32 |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| store %0 to %4 : $*Builtin.Int32 |
| br bb3 |
| |
| bb2: |
| store %1 to %4 : $*Builtin.Int32 |
| br bb3 |
| |
| bb3: |
| store %0 to %6 : $*Builtin.Int32 |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We should be able to remove the store in bb0, but we currently |
| // can't due to deficiency in alias analysis. |
| // |
| // CHECK-LABEL: dead_store_with_aliasing_base_in_simple_class |
| // CHECK: bb0([[RET0:%.+]] : $Bool): |
| // CHECK: {{ store}} |
| // CHECK: {{ store}} |
| sil hidden @dead_store_with_aliasing_base_in_simple_class : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_ref $foo |
| %2 = alloc_stack $foo |
| store %1 to %2 : $*foo |
| %8 = load %2 : $*foo |
| %4 = integer_literal $Builtin.Int64, 12 |
| %5 = struct $Int (%4 : $Builtin.Int64) |
| %6 = ref_element_addr %1 : $foo, #foo.a |
| store %5 to %6 : $*Int |
| %9 = ref_element_addr %8 : $foo, #foo.a |
| store %5 to %9 : $*Int |
| %14 = tuple () |
| dealloc_stack %2 : $*foo |
| return %14 : $() |
| } |
| |
| // Remove dead stores in if-else block on a simple struct as there are stores |
| // in the joint block. |
| // |
| // CHECK-LABEL: dead_store_diamond_control_flow |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: bb2: |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| sil hidden @dead_store_diamond_control_flow : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %4 = metatype $@thin S1.Type |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %5 to %2 : $*S1 |
| %7 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %7, bb1, bb2 |
| |
| bb1: |
| %9 = integer_literal $Builtin.Int64, 0 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = struct_element_addr %2 : $*S1, #S1.a |
| store %10 to %11 : $*Int |
| br bb3 |
| |
| bb2: |
| %14 = integer_literal $Builtin.Int64, 1 |
| %15 = struct $Int (%14 : $Builtin.Int64) |
| %16 = struct_element_addr %2 : $*S1, #S1.a |
| store %15 to %16 : $*Int |
| br bb3 |
| |
| bb3: |
| %19 = integer_literal $Builtin.Int64, 2 |
| %20 = struct $Int (%19 : $Builtin.Int64) |
| %21 = struct_element_addr %2 : $*S1, #S1.a |
| store %20 to %21 : $*Int |
| %23 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %23 : $() |
| } |
| |
| // Remove a dead store in the split block as there are stores in the if-else |
| // blocks. |
| // |
| // CHECK-LABEL: dead_store_in_split_block_simple_struct |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: cond_br |
| sil hidden @dead_store_in_split_block_simple_struct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %4 = metatype $@thin S1.Type |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %5 to %2 : $*S1 |
| br bb1 |
| |
| bb1: |
| %8 = struct_extract %0 : $Bool, #Bool.value |
| %9 = integer_literal $Builtin.Int64, 0 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = struct_element_addr %2 : $*S1, #S1.a |
| store %10 to %11 : $*Int |
| cond_br %8, bb2, bb3 |
| |
| bb2: |
| %14 = integer_literal $Builtin.Int64, 0 |
| %15 = struct $Int (%14 : $Builtin.Int64) |
| %16 = struct_element_addr %2 : $*S1, #S1.a |
| store %15 to %16 : $*Int |
| br bb4 |
| |
| bb3: |
| %19 = integer_literal $Builtin.Int64, 1 |
| %20 = struct $Int (%19 : $Builtin.Int64) |
| %21 = struct_element_addr %2 : $*S1, #S1.a |
| store %20 to %21 : $*Int |
| br bb4 |
| |
| bb4: |
| %24 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %24 : $() |
| } |
| |
| // Remove dead stores in split and else block. |
| // |
| // CHECK-LABEL: dead_store_split_else_block_simple_struct |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: cond_br |
| sil hidden @dead_store_split_else_block_simple_struct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %4 = metatype $@thin S1.Type |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %5 to %2 : $*S1 |
| br bb1 |
| |
| bb1: |
| %8 = struct_extract %0 : $Bool, #Bool.value |
| %9 = integer_literal $Builtin.Int64, 0 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = struct_element_addr %2 : $*S1, #S1.a |
| store %10 to %11 : $*Int |
| cond_br %8, bb2, bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| %15 = integer_literal $Builtin.Int64, 1 |
| %16 = struct $Int (%15 : $Builtin.Int64) |
| %17 = struct_element_addr %2 : $*S1, #S1.a |
| store %16 to %17 : $*Int |
| br bb4 |
| |
| bb4: |
| %20 = integer_literal $Builtin.Int64, 0 |
| %21 = struct $Int (%20 : $Builtin.Int64) |
| %22 = struct_element_addr %2 : $*S1, #S1.a |
| store %21 to %22 : $*Int |
| %24 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %24 : $() |
| } |
| |
| // Remove dead stores in else block, store is only partially dead |
| // for split block. |
| // |
| // CHECK-LABEL: partial_dead_store_in_split_block_simple_struct |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: cond_br |
| sil hidden @partial_dead_store_in_split_block_simple_struct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %4 = metatype $@thin S1.Type |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %5 to %2 : $*S1 |
| br bb1 |
| |
| bb1: |
| %8 = struct_extract %0 : $Bool, #Bool.value |
| %9 = integer_literal $Builtin.Int64, 0 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = struct_element_addr %2 : $*S1, #S1.a |
| %12 = load %11 : $*Int |
| store %10 to %11 : $*Int |
| cond_br %8, bb2, bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| %16 = integer_literal $Builtin.Int64, 1 |
| %17 = struct $Int (%16 : $Builtin.Int64) |
| %18 = struct_element_addr %2 : $*S1, #S1.a |
| store %17 to %18 : $*Int |
| br bb4 |
| |
| bb4: |
| %21 = integer_literal $Builtin.Int64, 0 |
| %22 = struct $Int (%21 : $Builtin.Int64) |
| %23 = struct_element_addr %2 : $*S1, #S1.a |
| store %22 to %23 : $*Int |
| %25 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %25 : $() |
| } |
| |
| // Remove a dead store in the split block as there are stores in the if-else |
| // blocks. |
| // |
| // Store in bb1 is still alive as post-order does not iterate over unreachable |
| // block. |
| // |
| // CHECK-LABEL: dead_store_unreachable_predecessor_simple_struct |
| // CHECK: bb0 |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| // CHECK: bb1 |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| sil hidden @dead_store_unreachable_predecessor_simple_struct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %4 = metatype $@thin S1.Type |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %5 to %2 : $*S1 |
| br bb1 |
| |
| bb1: |
| %8 = struct_extract %0 : $Bool, #Bool.value |
| %9 = integer_literal $Builtin.Int64, 0 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = struct_element_addr %2 : $*S1, #S1.a |
| store %10 to %11 : $*Int |
| br bb3 |
| |
| bb2: |
| %14 = integer_literal $Builtin.Int64, 0 |
| %15 = struct $Int (%14 : $Builtin.Int64) |
| %16 = struct_element_addr %2 : $*S1, #S1.a |
| store %15 to %16 : $*Int |
| br bb3 |
| |
| bb3: |
| %19 = integer_literal $Builtin.Int64, 1 |
| %20 = struct $Int (%19 : $Builtin.Int64) |
| %21 = struct_element_addr %2 : $*S1, #S1.a |
| store %20 to %21 : $*Int |
| br bb4 |
| |
| bb4: |
| %24 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %24 : $() |
| } |
| |
| // CHECK-LABEL: dead_store_split_block_complex_struct |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| // CHECK: bb2: |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| sil hidden @dead_store_split_block_complex_struct : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $S2 |
| %2 = function_ref @S2_init : $@convention(thin) (@thin S2.Type) -> S2 |
| %3 = metatype $@thin S2.Type |
| %4 = apply %2(%3) : $@convention(thin) (@thin S2.Type) -> S2 |
| store %4 to %1 : $*S2 |
| %6 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %6, bb1, bb2 |
| |
| bb1: |
| %8 = integer_literal $Builtin.Int64, 0 |
| %9 = struct $Int (%8 : $Builtin.Int64) |
| %10 = struct_element_addr %1 : $*S2, #S2.x |
| %11 = struct_element_addr %10 : $*S1, #S1.a |
| store %9 to %11 : $*Int |
| br bb3 |
| |
| bb2: |
| %14 = integer_literal $Builtin.Int64, 1 |
| %15 = struct $Int (%14 : $Builtin.Int64) |
| %16 = struct_element_addr %1 : $*S2, #S2.x |
| %17 = struct_element_addr %16 : $*S1, #S1.a |
| store %15 to %17 : $*Int |
| br bb3 |
| |
| bb3: |
| %20 = integer_literal $Builtin.Int64, 2 |
| %21 = struct $Int (%20 : $Builtin.Int64) |
| %22 = struct_element_addr %1 : $*S2, #S2.x |
| %23 = struct_element_addr %22 : $*S1, #S1.a |
| store %21 to %23 : $*Int |
| %25 = tuple () |
| dealloc_stack %1 : $*S2 |
| return %25 : $() |
| } |
| |
| // Remove dead stores in split and else block on a complex struct. |
| // |
| // CHECK-LABEL: dead_store_split_else_block_complex_struct |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| // CHECK: bb3: |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| sil hidden @dead_store_split_else_block_complex_struct : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $S2 |
| %2 = function_ref @S2_init : $@convention(thin) (@thin S2.Type) -> S2 |
| %3 = metatype $@thin S2.Type |
| %4 = apply %2(%3) : $@convention(thin) (@thin S2.Type) -> S2 |
| store %4 to %1 : $*S2 |
| br bb1 |
| |
| bb1: |
| %7 = integer_literal $Builtin.Int64, 0 |
| %8 = struct $Int (%7 : $Builtin.Int64) |
| %9 = struct_element_addr %1 : $*S2, #S2.x |
| %10 = struct_element_addr %9 : $*S1, #S1.a |
| store %8 to %10 : $*Int |
| %12 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %12, bb2, bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| %15 = integer_literal $Builtin.Int64, 1 |
| %16 = struct $Int (%15 : $Builtin.Int64) |
| %17 = struct_element_addr %1 : $*S2, #S2.x |
| %18 = struct_element_addr %17 : $*S1, #S1.a |
| store %16 to %18 : $*Int |
| br bb4 |
| |
| bb4: |
| %21 = integer_literal $Builtin.Int64, 2 |
| %22 = struct $Int (%21 : $Builtin.Int64) |
| %23 = struct_element_addr %1 : $*S2, #S2.x |
| %24 = struct_element_addr %23 : $*S1, #S1.a |
| store %22 to %24 : $*Int |
| %26 = tuple () |
| dealloc_stack %1 : $*S2 |
| return %26 : $() |
| } |
| |
| // Remove dead store in 1 loop block, as the store in exit block kills it. |
| // |
| // CHECK-LABEL: dead_store_single_loop_block_simple_struct |
| // CHECK: bb2: |
| // CHECK: {{ store}} |
| // CHECK: br |
| sil hidden @dead_store_single_loop_block_simple_struct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1, var, name "x" |
| %5 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %6 = metatype $@thin S1.Type |
| %7 = apply %5(%6) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %7 to %2 : $*S1 |
| br bb1 |
| |
| bb1: |
| %11 = integer_literal $Builtin.Int64, 0 |
| %12 = struct $Int (%11 : $Builtin.Int64) |
| %13 = struct_element_addr %2 : $*S1, #S1.a |
| %14 = load %13 : $*Int |
| br bb2 |
| |
| bb2: |
| %16 = integer_literal $Builtin.Int64, 1 |
| %17 = struct $Int (%16 : $Builtin.Int64) |
| %18 = struct_element_addr %2 : $*S1, #S1.a |
| store %17 to %18 : $*Int |
| %9 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %9, bb1, bb3 |
| |
| bb3: |
| %21 = integer_literal $Builtin.Int64, 2 |
| %22 = struct $Int (%21 : $Builtin.Int64) |
| %23 = struct_element_addr %2 : $*S1, #S1.a |
| store %22 to %23 : $*Int |
| %25 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %25 : $() |
| } |
| |
| // Remove dead stores in loop blocks, as the store in exit block kills them. |
| // |
| // CHECK-LABEL: dead_store_multi_loop_blocks_simple_struct |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| // CHECK: bb2: |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| sil hidden @dead_store_multi_loop_blocks_simple_struct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1, var, name "x" |
| %5 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %6 = metatype $@thin S1.Type |
| %7 = apply %5(%6) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %7 to %2 : $*S1 |
| br bb1 |
| |
| bb1: |
| %11 = integer_literal $Builtin.Int64, 0 |
| %12 = struct $Int (%11 : $Builtin.Int64) |
| %13 = struct_element_addr %2 : $*S1, #S1.a |
| store %12 to %13 : $*Int |
| br bb2 |
| |
| bb2: |
| %16 = integer_literal $Builtin.Int64, 1 |
| %17 = struct $Int (%16 : $Builtin.Int64) |
| %18 = struct_element_addr %2 : $*S1, #S1.a |
| store %17 to %18 : $*Int |
| %9 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %9, bb1, bb3 |
| |
| bb3: |
| %21 = integer_literal $Builtin.Int64, 2 |
| %22 = struct $Int (%21 : $Builtin.Int64) |
| %23 = struct_element_addr %2 : $*S1, #S1.a |
| store %22 to %23 : $*Int |
| %25 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %25 : $() |
| } |
| |
| // Remove dead store in the tuple data structure. |
| // |
| // CHECK-LABEL: dead_store_simple_tuple |
| // CHECK: bb0: |
| // CHECK: {{ store}} |
| // CHECK: {{ store}} |
| // CHECK-NOT: {{ store}} |
| // CHECK: load |
| sil hidden @dead_store_simple_tuple : $@convention(thin) () -> Int { |
| bb0: |
| %0 = alloc_stack $(a: Int, b: Int), var, name "x" |
| %1 = tuple_element_addr %0 : $*(a: Int, b: Int), 0 |
| %2 = tuple_element_addr %0 : $*(a: Int, b: Int), 1 |
| %3 = integer_literal $Builtin.Int64, 2 |
| %4 = struct $Int (%3 : $Builtin.Int64) |
| store %4 to %1 : $*Int |
| %6 = integer_literal $Builtin.Int64, 2 |
| %7 = struct $Int (%6 : $Builtin.Int64) |
| store %7 to %2 : $*Int |
| %9 = integer_literal $Builtin.Int64, 10 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = tuple_element_addr %0 : $*(a: Int, b: Int), 0 |
| store %10 to %11 : $*Int |
| %13 = integer_literal $Builtin.Int64, 12 |
| %14 = struct $Int (%13 : $Builtin.Int64) |
| %15 = tuple_element_addr %0 : $*(a: Int, b: Int), 1 |
| store %14 to %15 : $*Int |
| %22 = load %15 : $*Int |
| %23 = load %11 : $*Int |
| %24 = load %2 : $*Int |
| %25 = load %1 : $*Int |
| %17 = integer_literal $Builtin.Int64, 22 |
| %18 = tuple () |
| %19 = struct $Int (%17 : $Builtin.Int64) |
| dealloc_stack %0 : $*(a: Int, b: Int) |
| return %19 : $Int |
| } |
| |
| // Cannot remove partially dead store in split block for simple class. |
| // |
| // CHECK-LABEL: partial_dead_store_simple_class |
| // CHECK: bb1: |
| // CHECK: {{ store}} |
| // CHECK: cond_br |
| sil hidden @partial_dead_store_simple_class : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $foo |
| %2 = alloc_ref $foo |
| store %2 to %1 : $*foo |
| br bb1 |
| |
| bb1: |
| %5 = integer_literal $Builtin.Int64, 10 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = ref_element_addr %2 : $foo, #foo.a |
| store %6 to %7 : $*Int |
| %9 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %9, bb2, bb3 |
| |
| bb2: |
| br bb4 |
| |
| bb3: |
| %12 = integer_literal $Builtin.Int64, 12 |
| %13 = struct $Int (%12 : $Builtin.Int64) |
| %14 = ref_element_addr %2 : $foo, #foo.a |
| store %13 to %14 : $*Int |
| %16 = tuple () |
| br bb4 |
| |
| bb4: |
| strong_release %2 : $foo |
| %19 = tuple () |
| dealloc_stack %1 : $*foo |
| return %19 : $() |
| } |
| |
| // Cannot remove partially dead store in split block for simple class. |
| // |
| // CHECK-LABEL: partial_dead_store_with_function_call |
| // CHECK: bb1: |
| // CHECK: {{ store}} |
| // CHECK: cond_br |
| sil hidden @partial_dead_store_with_function_call : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $foo |
| %3 = alloc_ref $foo |
| store %3 to %1 : $*foo |
| br bb1 |
| |
| bb1: |
| %4 = integer_literal $Builtin.Int64, 10 |
| %5 = struct $Int (%4 : $Builtin.Int64) |
| %6 = ref_element_addr %3 : $foo, #foo.a |
| store %5 to %6 : $*Int |
| %9 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %9, bb2, bb3 |
| |
| bb2: |
| %32 = function_ref @foo_user : $@convention(thin) (@guaranteed foo) -> () |
| %33 = apply %32(%3) : $@convention(thin) (@guaranteed foo) -> () |
| %120 = integer_literal $Builtin.Int64, 12 |
| %121 = struct $Int (%120 : $Builtin.Int64) |
| store %121 to %6 : $*Int |
| %124 = tuple () |
| br bb4 |
| |
| bb3: |
| %20 = integer_literal $Builtin.Int64, 12 |
| %21 = struct $Int (%20 : $Builtin.Int64) |
| store %21 to %6 : $*Int |
| %24 = tuple () |
| br bb4 |
| |
| bb4: |
| strong_release %3 : $foo |
| %35 = tuple () |
| dealloc_stack %1 : $*foo |
| return %35 : $() |
| } |
| |
| // Remove dead store in same basic block, test for alias analysis/side effect. |
| // Currently, %14 = apply %13(%10) is marked as having side effect on the |
| // store %8 to %9 : $*Int // id: %15 |
| // |
| // CHECK-LABEL: dead_store_across_function_call |
| // CHECK: bb1: |
| // CHECK-NOT: {{ store}} |
| // CHECK: function_ref |
| sil hidden @dead_store_across_function_call : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %4 = metatype $@thin S1.Type |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %5 to %2 : $*S1 |
| %7 = integer_literal $Builtin.Int64, 0 |
| %8 = struct $Int (%7 : $Builtin.Int64) |
| %9 = struct_element_addr %2 : $*S1, #S1.a |
| %10 = alloc_ref $foo |
| br bb1 |
| |
| bb1: |
| store %8 to %9 : $*Int |
| %13 = function_ref @foo_user : $@convention(thin) (@guaranteed foo) -> () |
| %14 = apply %13(%10) : $@convention(thin) (@guaranteed foo) -> () |
| store %8 to %9 : $*Int |
| br bb2 |
| |
| bb2: |
| %17 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %17 : $() |
| } |
| |
| |
| // store to stack allocated memory cannot alias with incoming argument. |
| // |
| // CHECK-LABEL: dead_store_inout_stack_alias |
| // CHECK: bb0 |
| // CHECK : struct_element_addr |
| // CHECK-NOT: {{ store}} |
| // CHECK: load |
| sil hidden @dead_store_inout_stack_alias : $@convention(thin) (@inout A) -> () { |
| bb0(%0 : $*A): |
| %1 = alloc_stack $A |
| %2 = integer_literal $Builtin.Int32, 0 |
| %3 = struct_element_addr %0 : $*A, #A.i |
| %4 = struct_element_addr %1 : $*A, #A.i |
| store %2 to %4 : $*Builtin.Int32 |
| %6 = load %3 : $*Builtin.Int32 |
| store %2 to %4 : $*Builtin.Int32 |
| dealloc_stack %1 : $*A |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| // test dead store for enums. there should be only 1 store left. |
| // |
| // CHECK-LABEL: dead_store_enum_same_case |
| // CHECK: {{ store}} |
| // CHECK-NOT: {{ store}} |
| // CHECK: return |
| sil hidden @dead_store_enum_same_case : $@convention(thin) (@inout Example) -> Int64 { |
| bb0(%0 : $*Example): |
| %1 = integer_literal $Builtin.Int64, 64 |
| %2 = struct $Int64 (%1 : $Builtin.Int64) |
| %3 = integer_literal $Builtin.Int64, 64 |
| %4 = struct $Int64 (%3 : $Builtin.Int64) |
| %5 = tuple (%2 : $Int64, %4 : $Int64) |
| %6 = enum $Example, #Example.A!enumelt.1, %5 : $(Int64, Int64) |
| store %6 to %0 : $*Example |
| %8 = integer_literal $Builtin.Int64, 64 |
| %9 = struct $Int64 (%8 : $Builtin.Int64) |
| %10 = integer_literal $Builtin.Int64, 64 |
| %11 = struct $Int64 (%10 : $Builtin.Int64) |
| %12 = tuple (%9 : $Int64, %11 : $Int64) |
| %13 = enum $Example, #Example.A!enumelt.1, %12 : $(Int64, Int64) |
| store %13 to %0 : $*Example |
| release_value %6 : $Example |
| %16 = integer_literal $Builtin.Int64, 0 |
| %17 = struct $Int64 (%16 : $Builtin.Int64) |
| release_value %13 : $Example |
| return %17 : $Int64 |
| } |
| |
| /// Make sure we can coalesce the 2 live stores to the 2 fields in S4. |
| /// |
| /// CHECK-LABEL : partial_dead_store_struct_in_struct |
| /// CHECK: [[RET0:%.+]] = struct_extract |
| /// CHECK: [[RET1:%.+]] = struct_element_addr |
| /// CHECK: {{ store}} [[RET0:%.+]] to [[RET1:%.+]] : $*S4 |
| sil hidden @partial_dead_store_struct_in_struct : $@convention(thin) (@inout S5) -> () { |
| bb0(%0 : $*S5): |
| %1 = function_ref @S5_init : $@convention(thin) (@thin S5.Type) -> S5 |
| %2 = metatype $@thin S5.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S5.Type) -> S5 |
| store %3 to %0 : $*S5 |
| %5 = integer_literal $Builtin.Int64, 11 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S5, #S5.y |
| store %6 to %7 : $*Int |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| /// Make sure we do not coalesce the 2 live stores to the 2 fields in S6. |
| /// |
| /// CHECK-LABEL : sil hidden @discontiguous_partial_dead_store_simple_struct |
| /// CHECK: store |
| /// CHECK: store |
| sil hidden @discontiguous_partial_dead_store_simple_struct : $@convention(thin) (@inout S6) -> () { |
| bb0(%0 : $*S6): |
| %1 = function_ref @S6_init : $@convention(thin) (@thin S6.Type) -> S6 |
| %2 = metatype $@thin S6.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S6.Type) -> S6 |
| store %3 to %0 : $*S6 |
| %5 = integer_literal $Builtin.Int64, 10 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S6, #S6.y |
| store %6 to %7 : $*Int |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| /// Make sure we generate the store to field x first. |
| /// |
| /// CHECK-LABEL : sil hidden @discontiguous_partial_dead_store_lives_insert_deterministically |
| /// CHECK: bb0([[A:%.*]] : $*S6): |
| /// CHECK: [[IN:%.*]] = apply |
| /// CHECK: [[EXT1:%.*]] = struct_extract [[IN]] : $S6, #S6.x |
| /// CHECK: [[EXT2:%.*]] = struct_element_addr [[A]] : $*S6, #S6.x |
| /// CHECK: store [[EXT1]] to [[EXT2]] : $*Int |
| sil hidden @discontiguous_partial_dead_store_lives_insert_deterministically : $@convention(thin) (@inout S6) -> () { |
| bb0(%0 : $*S6): |
| %1 = function_ref @S6_init : $@convention(thin) (@thin S6.Type) -> S6 |
| %2 = metatype $@thin S6.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S6.Type) -> S6 |
| store %3 to %0 : $*S6 |
| %5 = integer_literal $Builtin.Int64, 10 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S6, #S6.y |
| store %6 to %7 : $*Int |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| /// Make sure we do not generate too many stores from a large store that |
| /// is partially dead. |
| /// |
| /// CHECK-LABEL: sil hidden @discontiguous_partial_dead_store_simple_large_struct |
| /// CHECK: store |
| /// CHECK: store |
| sil hidden @discontiguous_partial_dead_store_simple_large_struct : $@convention(thin) (@inout S7) -> () { |
| bb0(%0 : $*S7): |
| %1 = function_ref @S7_init : $@convention(thin) (@thin S7.Type) -> S7 |
| %2 = metatype $@thin S7.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S7.Type) -> S7 |
| store %3 to %0 : $*S7 |
| %5 = integer_literal $Builtin.Int64, 10 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S7, #S7.y |
| store %6 to %7 : $*Int |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| /// We are not performing partial dead store for store %3 to %0#1 : $*S7, |
| /// i.e. too many stores generated, but make sure we track the store correctly |
| /// so that we can get rid of store %56 to %57 : $*Int. |
| /// |
| /// CHECK-LABEL : partial_dead_store_bail_out_proper_propagation |
| /// CHECK: bb0 |
| /// CHECK-NOT: {{ store}} |
| /// CHECK: function_ref |
| sil hidden @partial_dead_store_bail_out_proper_propagation : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $S7, var, name "a" |
| %55 = integer_literal $Builtin.Int64, 10 |
| %56 = struct $Int (%55 : $Builtin.Int64) |
| %57 = struct_element_addr %0 : $*S7, #S7.a |
| store %56 to %57 : $*Int |
| %1 = function_ref @S7_init : $@convention(thin) (@thin S7.Type) -> S7 |
| %2 = metatype $@thin S7.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S7.Type) -> S7 |
| store %3 to %0 : $*S7 |
| %5 = integer_literal $Builtin.Int64, 10 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S7, #S7.y |
| store %6 to %7 : $*Int |
| %9 = tuple () |
| dealloc_stack %0 : $*S7 |
| return %9 : $() |
| } |
| |
| /// Make sure we do perform partial dead store for the first store. |
| /// we have a case which partial dead store miscompiles a program, |
| /// in that case the bitvector is not tracked correctly and we end |
| /// up deleting the entire larger store, store %3 to %0#1 : $*S3 in |
| /// this case. |
| /// |
| /// CHECK-LABEL: partial_dead_store_simple_struct |
| /// CHECK: apply |
| /// CHECK-NEXT: struct_extract |
| sil hidden @partial_dead_store_simple_struct : $@convention(thin) (@inout S3) -> () { |
| bb0(%0 : $*S3): |
| %1 = function_ref @S3_init : $@convention(thin) (@thin S3.Type) -> S3 |
| %2 = metatype $@thin S3.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S3.Type) -> S3 |
| store %3 to %0 : $*S3 |
| %5 = integer_literal $Builtin.Int64, 10 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S3, #S3.a |
| store %6 to %7 : $*Int |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| /// Make sure we do not hang in this test case. Test for Location::expand. |
| /// expand should stop on class type. Also need to get rid of the partial |
| /// dead store. |
| /// |
| /// CHECK-LABEL: self_loop_class_type_expansion |
| /// CHECK: [[RET0:%.+]] = struct_extract |
| /// CHECK: [[RET1:%.+]] = struct_element_addr |
| /// CHECK-NEXT: store [[RET0:%.+]] to [[RET1:%.+]] : $*SelfLoop |
| sil hidden @self_loop_class_type_expansion : $@convention(thin) (@inout S8) -> () { |
| bb0(%0 : $*S8): |
| %1 = function_ref @S8_init : $@convention(thin) (@thin S8.Type) -> @owned S8 |
| %2 = metatype $@thin S8.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S8.Type) -> @owned S8 |
| store %3 to %0 : $*S8 |
| %5 = integer_literal $Builtin.Int64, 12 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S8, #S8.k |
| store %6 to %7 : $*Int |
| %9 = struct_extract %3 : $S8, #S8.i |
| %10 = struct $S8 (%9 : $SelfLoop, %6 : $Int) |
| release_value %10 : $S8 |
| %12 = tuple () |
| return %12 : $() |
| } |
| |
| /// Make sure we do not remove the first store as there is no way to prove |
| /// %0 and %1 cannot alias here. |
| /// |
| /// CHECK-LABEL: no_dead_store_with_interfering_load |
| /// CHECK: {{ store}} |
| /// CHECK: load |
| /// CHECK: {{ store}} |
| sil hidden @no_dead_store_with_interfering_load : $@convention(thin) (@owned foo, @owned foo) -> () { |
| bb0(%0 : $foo, %1 : $foo): |
| %4 = integer_literal $Builtin.Int64, 10 |
| %5 = struct $Int (%4 : $Builtin.Int64) |
| %6 = ref_element_addr %0 : $foo, #foo.a |
| store %5 to %6 : $*Int |
| %9 = integer_literal $Builtin.Int64, 12 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = ref_element_addr %1 : $foo, #foo.a |
| %99 = load %11 : $*Int |
| %14 = integer_literal $Builtin.Int64, 10 |
| %15 = struct $Int (%14 : $Builtin.Int64) |
| %16 = ref_element_addr %0 : $foo, #foo.a |
| store %15 to %16 : $*Int |
| strong_release %1 : $foo |
| strong_release %0 : $foo |
| %21 = tuple () |
| return %21 : $() |
| } |
| |
| // Remove the dead stores in the entry block as the store in bb2 |
| // kills them. This test how the optimistic data flow works. |
| // |
| // CHECK-LABEL: dead_store_across_loop_simple_struct |
| // CHECK: bb0 |
| // CHECK-NOT: {{ store}} |
| // CHECK: br |
| sil hidden @dead_store_across_loop_simple_struct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 |
| %4 = metatype $@thin S1.Type |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 |
| store %5 to %2 : $*S1 |
| %7 = integer_literal $Builtin.Int64, 2 |
| %8 = struct $Int (%7 : $Builtin.Int64) |
| %9 = struct_element_addr %2 : $*S1, #S1.a |
| store %8 to %9 : $*Int |
| br bb1 |
| |
| bb1: |
| %12 = struct_extract %0 : $Bool, #Bool.value |
| cond_br %12, bb1, bb2 |
| |
| bb2: |
| %14 = integer_literal $Builtin.Int64, 2 |
| %15 = struct $Int (%14 : $Builtin.Int64) |
| %16 = struct_element_addr %2 : $*S1, #S1.a |
| store %15 to %16 : $*Int |
| %18 = tuple () |
| dealloc_stack %2 : $*S1 |
| return %18 : $() |
| } |
| |
| // Make sure the load in bb1 prevents the store in bb0 to be eliminated. we have a bug |
| // in constructing kill set when it is not conservative enough. i.e. this test case |
| // makes sure the killset for bb1 includes the kill for the store in bb2. |
| // |
| // CHECK-LABEL: conservative_kill_set_class |
| // CHECK: bb0 |
| // CHECK: {{ store}} |
| // CHECK: br bb1 |
| sil hidden @conservative_kill_set_class : $@convention(thin) (@owned foo, @owned foo) -> () { |
| bb0(%0 : $foo, %1 : $foo): |
| %4 = integer_literal $Builtin.Int64, 10 |
| %5 = struct $Int (%4 : $Builtin.Int64) |
| %6 = ref_element_addr %0 : $foo, #foo.a |
| store %5 to %6 : $*Int |
| br bb1 |
| |
| bb1: |
| %7 = ref_element_addr %1 : $foo, #foo.a |
| %8 = load %7 : $*Int |
| br bb2 |
| |
| bb2: |
| %9 = integer_literal $Builtin.Int64, 10 |
| %10 = struct $Int (%9 : $Builtin.Int64) |
| %11 = ref_element_addr %0 : $foo, #foo.a |
| store %5 to %6 : $*Int |
| %18 = tuple () |
| return %18 : $() |
| } |
| |
| /// Make sure we DO NOT get rid of the first store as a dead store. |
| /// |
| /// CHECK-LABEL: tbaa_no_alias_no_dead_store |
| /// CHECK: {{ store}} |
| /// CHECK: load |
| sil @tbaa_no_alias_no_dead_store : $@convention(thin) (Optional<@thick Any.Type>) -> Int { |
| bb0(%0 : $Optional<@thick Any.Type>): |
| %2 = alloc_stack $Optional<@thick Any.Type> |
| store %0 to %2 : $*Optional<@thick Any.Type> |
| %17 = unchecked_addr_cast %2 : $*Optional<@thick Any.Type> to $*Int |
| %18 = load %17 : $*Int |
| dealloc_stack %2 : $*Optional<@thick Any.Type> |
| return %18 : $Int |
| } |
| |
| /// Make sure we DO get rid of the store as a dead store, i.e. no read to a store |
| /// to a local variable. |
| /// |
| /// CHECK-LABEL: local_dead_store |
| /// CHECK: load |
| /// CHECK-NOT: {{ store}} |
| /// CHECK: return |
| sil @local_dead_store : $@convention(thin) (Optional<@thick Any.Type>) -> Int { |
| bb0(%0 : $Optional<@thick Any.Type>): |
| %2 = alloc_stack $Optional<@thick Any.Type> |
| %17 = unchecked_addr_cast %2 : $*Optional<@thick Any.Type> to $*Int |
| %18 = load %17 : $*Int |
| store %0 to %2 : $*Optional<@thick Any.Type> |
| dealloc_stack %2 : $*Optional<@thick Any.Type> |
| return %18 : $Int |
| } |
| |
| /// Make sure we do perform partial dead store for the first store. |
| /// we have a case which partial dead store miscompiles a program, |
| /// in that case the bitvector is not tracked correctly and we end |
| /// up deleting the entire larger store, store %3 to %0#1 : $*S3 in |
| /// this case. |
| /// |
| /// CHECK-LABEL: partial_dead_store_in_nested_struct |
| /// CHECK: apply |
| /// CHECK-NEXT: struct_extract |
| sil hidden @partial_dead_store_in_nested_struct : $@convention(thin) (@inout S9) -> () { |
| bb0(%0 : $*S9): |
| %1 = function_ref @S9_init : $@convention(thin) (@thin S9.Type) -> S9 |
| %2 = metatype $@thin S9.Type |
| %3 = apply %1(%2) : $@convention(thin) (@thin S9.Type) -> S9 |
| store %3 to %0 : $*S9 |
| %5 = integer_literal $Builtin.Int64, 10 |
| %6 = struct $Int (%5 : $Builtin.Int64) |
| %7 = struct_element_addr %0 : $*S9, #S9.x |
| %8 = struct_element_addr %7 : $*S3, #S3.a |
| store %6 to %8 : $*Int |
| %9 = tuple () |
| return %9 : $() |
| } |
| |
| // CHECK-LABEL: dont_remove_store_to_stack_used_as_partial_apply_indirect_parameter |
| // CHECK: [[S:%[0-9]+]] = alloc_stack |
| // CHECK-NEXT: store %0 to [[S]] |
| sil @dont_remove_store_to_stack_used_as_partial_apply_indirect_parameter : $@convention(thin) (Int) -> @owned @callee_owned (Bool) -> Int { |
| bb0(%0 : $Int): |
| %s = alloc_stack $Int |
| store %0 to %s : $*Int |
| %f = function_ref @callee : $@convention(thin) (@in Int) -> @owned @callee_owned (Bool) -> Int |
| %a = apply %f(%s) : $@convention(thin) (@in Int) -> @owned @callee_owned (Bool) -> Int |
| dealloc_stack %s : $*Int |
| return %a : $@callee_owned (Bool) -> Int |
| } |
| |
| sil @callee : $@convention(thin) (@in Int) -> @owned @callee_owned (Bool) -> Int { |
| bb0(%0 : $*Int): |
| %2 = function_ref @closure : $@convention(thin) (Bool, @in Int) -> Int |
| %3 = partial_apply %2(%0) : $@convention(thin) (Bool, @in Int) -> Int |
| return %3 : $@callee_owned (Bool) -> Int |
| } |
| |
| sil @closure : $@convention(thin) (Bool, @in Int) -> Int |
| |
| // Make sure we invalidate the store in bb1 when we process the SILargument. If not |
| // we could remove the store in bb1 which is incorrect. |
| // |
| // CHECK-LABEL: invalidate_base_for_silargument |
| // CHECK: bb1([[A0:%.+]] : $foo): |
| // CHECK: {{ store}} |
| // CHECK: bb2: |
| // CHECK: {{ store}} |
| sil hidden @invalidate_base_for_silargument : $@convention(thin) () -> () { |
| bb0: |
| %1 = alloc_ref $foo |
| %4 = integer_literal $Builtin.Int64, 12 |
| %5 = struct $Int (%4 : $Builtin.Int64) |
| br bb1(%1 : $foo) |
| |
| bb1(%2 : $foo): |
| %6 = ref_element_addr %2 : $foo, #foo.a |
| store %5 to %6 : $*Int |
| %9 = alloc_ref $foo |
| cond_br undef, bb1(%9: $foo), bb2 |
| |
| bb2: |
| %7 = ref_element_addr %2 : $foo, #foo.a |
| store %5 to %7 : $*Int |
| %14 = tuple () |
| return %14 : $() |
| } |
| |
| // CHECK-LABEL: test_is_unique |
| // CHECK: %1 = alloc_stack |
| // CHECK: store %0 to %1 |
| // CHECK: is_unique |
| // CHECK: return |
| sil @test_is_unique : $@convention(thin) (@guaranteed AB) -> Builtin.Int1 { |
| bb0(%0 : $AB): // Preds: bb6 bb5 bb4 bb3 |
| %1 = alloc_stack $AB |
| store %0 to %1 : $*AB |
| %2 = is_unique %1 : $*AB |
| dealloc_stack %1 : $*AB |
| return %2 : $Builtin.Int1 |
| } |
| |
| // CHECK-LABEL: test_is_unique_or_pinned |
| // CHECK: %1 = alloc_stack |
| // CHECK: store %0 to %1 |
| // CHECK: is_unique_or_pinned |
| // CHECK: return |
| sil @test_is_unique_or_pinned : $@convention(thin) (@guaranteed AB) -> Builtin.Int1 { |
| bb0(%0 : $AB): // Preds: bb6 bb5 bb4 bb3 |
| %1 = alloc_stack $AB |
| store %0 to %1 : $*AB |
| %2 = is_unique_or_pinned %1 : $*AB |
| dealloc_stack %1 : $*AB |
| return %2 : $Builtin.Int1 |
| } |
| |
| // CHECK-LABEL: dont_remove_store |
| // CHECK: load |
| // CHECK: store |
| // CHECK: value_metatype |
| // CHECK: return |
| sil @dont_remove_store : $@convention(thin) (@in_guaranteed foo) -> () { |
| bb0(%0 : $*foo): |
| %1 = alloc_stack $foo |
| %2 = load %0 : $*foo |
| store %2 to %1 : $*foo |
| %3 = value_metatype $@thick foo.Type, %1 : $*foo |
| dealloc_stack %1 : $*foo |
| %20 = tuple() |
| return %20 : $() |
| } |