| // RUN: %target-sil-opt %s -dead-store-elim -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 B { |
| var i : Builtin.Int32 |
| init() |
| } |
| |
| enum Example { |
| case A(Int64, Int64) |
| case B(Int64) |
| case C |
| case D |
| } |
| |
| 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 @escaped_a : $@convention(thin) () -> Builtin.RawPointer |
| |
| // We should be able to remove the local store that is not read. |
| // |
| // DISABLECHECK-LABEL: trivial_local_dead_store |
| // DISABLECHECK: bb0 |
| // DISABLECHECK-NOT: store |
| // DISABLECHECK: return |
| sil hidden @trivial_local_dead_store : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $Int, var, name "a" // users: %3, %5 |
| %1 = integer_literal $Builtin.Int64, 1 // user: %2 |
| %2 = struct $Int (%1 : $Builtin.Int64) // user: %3 |
| store %2 to %0#1 : $*Int // id: %3 |
| %4 = tuple () // user: %6 |
| dealloc_stack %0#0 : $*@local_storage Int // id: %5 |
| return %4 : $() // id: %6 |
| } |
| |
| // We can not remove the local store that is read. |
| // |
| // DISABLECHECK-LABEL: blocking_read_on_local_store |
| // DISABLECHECK: bb0 |
| // DISABLECHECK: store |
| // DISABLECHECK: return |
| sil hidden @blocking_read_on_local_store : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $Int, var, name "a" // users: %3, %5 |
| %1 = integer_literal $Builtin.Int64, 1 // user: %2 |
| %2 = struct $Int (%1 : $Builtin.Int64) // user: %3 |
| store %2 to %0#1 : $*Int // id: %3 |
| %4 = tuple () // user: %6 |
| %99 = load %0#1 : $*Int |
| dealloc_stack %0#0 : $*@local_storage Int // id: %5 |
| return %4 : $() // id: %6 |
| } |
| |
| // CHECK-LABEL: sil @store_after_store |
| // CHECK: alloc_box |
| // CHECK: store |
| // CHECK-NOT: store |
| // CHECK: return |
| sil @store_after_store : $@convention(thin) (@owned B) -> () { |
| bb0(%0 : $B): |
| %1 = alloc_box $B |
| %2 = store %0 to %1#1 : $*B |
| %3 = store %0 to %1#1 : $*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 can not 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" // users: %3, %5 |
| %1 = integer_literal $Builtin.Int64, 1 // user: %2 |
| %2 = struct $Int (%1 : $Builtin.Int64) // user: %3 |
| store %2 to %0#1 : $*Int // id: %3 |
| debug_value_addr %0#1 : $*Int |
| %4 = tuple () // user: %6 |
| dealloc_stack %0#0 : $*@local_storage Int // id: %5 |
| return %4 : $() // id: %6 |
| } |
| |
| // 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 $*Builtin.Int32 |
| %5 = apply %2() : $@convention(thin) () -> Builtin.RawPointer |
| %6 = pointer_to_address %5 : $Builtin.RawPointer to $*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 |
| // cant due to deficiency in alias analysis. |
| // |
| // CHECK-LABEL: DeadStoreWithAliasingBasesSimpleClass |
| // CHECK: bb0([[RET0:%.+]] : $Bool): |
| // CHECK: store |
| // CHECK: store |
| // CHECK: store |
| sil hidden @DeadStoreWithAliasingBasesSimpleClass : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_ref $foo // users: %3, %6 |
| %2 = alloc_stack $foo // users: %3, %8, %15 |
| store %1 to %2#1 : $*foo // id: %3 |
| %8 = load %2#1 : $*foo // user: %11 |
| %4 = integer_literal $Builtin.Int64, 12 // user: %5 |
| %5 = struct $Int (%4 : $Builtin.Int64) // user: %7 |
| %6 = ref_element_addr %1 : $foo, #foo.a // user: %7 |
| store %5 to %6 : $*Int // id: %7 |
| %9 = ref_element_addr %8 : $foo, #foo.a // user: %12 |
| store %5 to %9 : $*Int // id: %12 |
| %14 = tuple () // user: %16 |
| dealloc_stack %2#0 : $*@local_storage foo // id: %15 |
| return %14 : $() // id: %16 |
| } |
| |
| // Remove dead stores in if-else block on a simple struct as there are stores |
| // in the joint block. |
| // |
| // CHECK-LABEL: diamond_control_flow_dead_store |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: br |
| // CHECK: bb2: |
| // CHECK-NOT: store |
| // CHECK: br |
| sil hidden @diamond_control_flow_dead_store : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 // users: %6, %11, %16, %21, %24 |
| // function_ref S1_init |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %5 |
| %4 = metatype $@thin S1.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 // user: %6 |
| store %5 to %2#1 : $*S1 // id: %6 |
| %7 = struct_extract %0 : $Bool, #Bool.value // user: %8 |
| cond_br %7, bb1, bb2 // id: %8 |
| |
| bb1: // Preds: bb0 |
| %9 = integer_literal $Builtin.Int64, 0 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %12 |
| %11 = struct_element_addr %2#1 : $*S1, #S1.a // user: %12 |
| store %10 to %11 : $*Int // id: %12 |
| br bb3 // id: %13 |
| |
| bb2: // Preds: bb0 |
| %14 = integer_literal $Builtin.Int64, 1 // user: %15 |
| %15 = struct $Int (%14 : $Builtin.Int64) // user: %17 |
| %16 = struct_element_addr %2#1 : $*S1, #S1.a // user: %17 |
| store %15 to %16 : $*Int // id: %17 |
| br bb3 // id: %18 |
| |
| bb3: // Preds: bb1 bb2 |
| %19 = integer_literal $Builtin.Int64, 2 // user: %20 |
| %20 = struct $Int (%19 : $Builtin.Int64) // user: %22 |
| %21 = struct_element_addr %2#1 : $*S1, #S1.a // user: %22 |
| store %20 to %21 : $*Int // id: %22 |
| %23 = tuple () // user: %25 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %24 |
| return %23 : $() // id: %25 |
| } |
| |
| // Remove a dead store in the split block as there are stores in the if-else |
| // blocks. |
| // |
| // CHECK-LABEL: DeadStoreInSplitSimpleStruct |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: cond_br |
| sil hidden @DeadStoreInSplitSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 // users: %6, %11, %16, %21, %25 |
| // function_ref S1_init |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %5 |
| %4 = metatype $@thin S1.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 // user: %6 |
| store %5 to %2#1 : $*S1 // id: %6 |
| br bb1 // id: %7 |
| |
| bb1: // Preds: bb0 |
| %8 = struct_extract %0 : $Bool, #Bool.value // user: %13 |
| %9 = integer_literal $Builtin.Int64, 0 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %12 |
| %11 = struct_element_addr %2#1 : $*S1, #S1.a // user: %12 |
| store %10 to %11 : $*Int // id: %12 |
| cond_br %8, bb2, bb3 // id: %13 |
| |
| bb2: // Preds: bb1 |
| %14 = integer_literal $Builtin.Int64, 0 // user: %15 |
| %15 = struct $Int (%14 : $Builtin.Int64) // user: %17 |
| %16 = struct_element_addr %2#1 : $*S1, #S1.a // user: %17 |
| store %15 to %16 : $*Int // id: %17 |
| br bb4 // id: %18 |
| |
| bb3: // Preds: bb1 |
| %19 = integer_literal $Builtin.Int64, 1 // user: %20 |
| %20 = struct $Int (%19 : $Builtin.Int64) // user: %22 |
| %21 = struct_element_addr %2#1 : $*S1, #S1.a // user: %22 |
| store %20 to %21 : $*Int // id: %22 |
| br bb4 // id: %23 |
| |
| bb4: // Preds: bb2 bb3 |
| %24 = tuple () // user: %26 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %25 |
| return %24 : $() // id: %26 |
| } |
| |
| // Remove dead stores in split and else block. |
| // |
| // CHECK-LABEL: DeadStoreInSplitElseBlockSimpleStruct |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: cond_br |
| sil hidden @DeadStoreInSplitElseBlockSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 // users: %6, %11, %17, %22, %25 |
| // function_ref S1_init |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %5 |
| %4 = metatype $@thin S1.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 // user: %6 |
| store %5 to %2#1 : $*S1 // id: %6 |
| br bb1 // id: %7 |
| |
| bb1: // Preds: bb0 |
| %8 = struct_extract %0 : $Bool, #Bool.value // user: %13 |
| %9 = integer_literal $Builtin.Int64, 0 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %12 |
| %11 = struct_element_addr %2#1 : $*S1, #S1.a // user: %12 |
| store %10 to %11 : $*Int // id: %12 |
| cond_br %8, bb2, bb3 // id: %13 |
| |
| bb2: // Preds: bb1 |
| br bb4 // id: %14 |
| |
| bb3: // Preds: bb1 |
| %15 = integer_literal $Builtin.Int64, 1 // user: %16 |
| %16 = struct $Int (%15 : $Builtin.Int64) // user: %18 |
| %17 = struct_element_addr %2#1 : $*S1, #S1.a // user: %18 |
| store %16 to %17 : $*Int // id: %18 |
| br bb4 // id: %19 |
| |
| bb4: // Preds: bb2 bb3 |
| %20 = integer_literal $Builtin.Int64, 0 // user: %21 |
| %21 = struct $Int (%20 : $Builtin.Int64) // user: %23 |
| %22 = struct_element_addr %2#1 : $*S1, #S1.a // user: %23 |
| store %21 to %22 : $*Int // id: %23 |
| %24 = tuple () // user: %26 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %25 |
| return %24 : $() // id: %26 |
| } |
| |
| // Remove dead stores in else block, store is only partially dead |
| // for split block. |
| // |
| // CHECK-LABEL: DeadStoreInElsePartialDeadStoreInSplitSimpleStruct |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: cond_br |
| sil hidden @DeadStoreInElsePartialDeadStoreInSplitSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 // users: %6, %11, %18, %23, %26 |
| // function_ref S1_init |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %5 |
| %4 = metatype $@thin S1.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 // user: %6 |
| store %5 to %2#1 : $*S1 // id: %6 |
| br bb1 // id: %7 |
| |
| bb1: // Preds: bb0 |
| %8 = struct_extract %0 : $Bool, #Bool.value // user: %14 |
| %9 = integer_literal $Builtin.Int64, 0 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %13 |
| %11 = struct_element_addr %2#1 : $*S1, #S1.a // users: %12, %13 |
| %12 = load %11 : $*Int |
| store %10 to %11 : $*Int // id: %13 |
| cond_br %8, bb2, bb3 // id: %14 |
| |
| bb2: // Preds: bb1 |
| br bb4 // id: %15 |
| |
| bb3: // Preds: bb1 |
| %16 = integer_literal $Builtin.Int64, 1 // user: %17 |
| %17 = struct $Int (%16 : $Builtin.Int64) // user: %19 |
| %18 = struct_element_addr %2#1 : $*S1, #S1.a // user: %19 |
| store %17 to %18 : $*Int // id: %19 |
| br bb4 // id: %20 |
| |
| bb4: // Preds: bb2 bb3 |
| %21 = integer_literal $Builtin.Int64, 0 // user: %22 |
| %22 = struct $Int (%21 : $Builtin.Int64) // user: %24 |
| %23 = struct_element_addr %2#1 : $*S1, #S1.a // user: %24 |
| store %22 to %23 : $*Int // id: %24 |
| %25 = tuple () // user: %27 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %26 |
| return %25 : $() // id: %27 |
| } |
| |
| // 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: DeadStoreInUnreachablePredecessorSimpleStruct |
| // CHECK: bb0 |
| // CHECK-NOT: store |
| // CHECK: br |
| // CHECK: bb1 |
| // CHECK-NOT: store |
| // CHECK: br |
| sil hidden @DeadStoreInUnreachablePredecessorSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 // users: %6, %11, %16, %21, %25 |
| // function_ref S1_init |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %5 |
| %4 = metatype $@thin S1.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 // user: %6 |
| store %5 to %2#1 : $*S1 // id: %6 |
| br bb1 // id: %7 |
| |
| bb1: // Preds: bb0 |
| %8 = struct_extract %0 : $Bool, #Bool.value |
| %9 = integer_literal $Builtin.Int64, 0 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %12 |
| %11 = struct_element_addr %2#1 : $*S1, #S1.a // user: %12 |
| store %10 to %11 : $*Int // id: %12 |
| br bb3 // id: %13 |
| |
| bb2: |
| %14 = integer_literal $Builtin.Int64, 0 // user: %15 |
| %15 = struct $Int (%14 : $Builtin.Int64) // user: %17 |
| %16 = struct_element_addr %2#1 : $*S1, #S1.a // user: %17 |
| store %15 to %16 : $*Int // id: %17 |
| br bb3 // id: %18 |
| |
| bb3: // Preds: bb1 bb2 |
| %19 = integer_literal $Builtin.Int64, 1 // user: %20 |
| %20 = struct $Int (%19 : $Builtin.Int64) // user: %22 |
| %21 = struct_element_addr %2#1 : $*S1, #S1.a // user: %22 |
| store %20 to %21 : $*Int // id: %22 |
| br bb4 // id: %23 |
| |
| bb4: // Preds: bb3 |
| %24 = tuple () // user: %26 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %25 |
| return %24 : $() // id: %26 |
| } |
| |
| // CHECK-LABEL: DeadStoreInIfElseBlockComplexStruct |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: br |
| // CHECK: bb2: |
| // CHECK-NOT: store |
| // CHECK: br |
| sil hidden @DeadStoreInIfElseBlockComplexStruct : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $S2 // users: %5, %10, %16, %22, %26 |
| // function_ref S2_init |
| %2 = function_ref @S2_init : $@convention(thin) (@thin S2.Type) -> S2 // user: %4 |
| %3 = metatype $@thin S2.Type // user: %4 |
| %4 = apply %2(%3) : $@convention(thin) (@thin S2.Type) -> S2 // user: %5 |
| store %4 to %1#1 : $*S2 // id: %5 |
| %6 = struct_extract %0 : $Bool, #Bool.value // user: %7 |
| cond_br %6, bb1, bb2 // id: %7 |
| |
| bb1: // Preds: bb0 |
| %8 = integer_literal $Builtin.Int64, 0 // user: %9 |
| %9 = struct $Int (%8 : $Builtin.Int64) // user: %12 |
| %10 = struct_element_addr %1#1 : $*S2, #S2.x // user: %11 |
| %11 = struct_element_addr %10 : $*S1, #S1.a // user: %12 |
| store %9 to %11 : $*Int // id: %12 |
| br bb3 // id: %13 |
| |
| bb2: // Preds: bb0 |
| %14 = integer_literal $Builtin.Int64, 1 // user: %15 |
| %15 = struct $Int (%14 : $Builtin.Int64) // user: %18 |
| %16 = struct_element_addr %1#1 : $*S2, #S2.x // user: %17 |
| %17 = struct_element_addr %16 : $*S1, #S1.a // user: %18 |
| store %15 to %17 : $*Int // id: %18 |
| br bb3 // id: %19 |
| |
| bb3: // Preds: bb1 bb2 |
| %20 = integer_literal $Builtin.Int64, 2 // user: %21 |
| %21 = struct $Int (%20 : $Builtin.Int64) // user: %24 |
| %22 = struct_element_addr %1#1 : $*S2, #S2.x // user: %23 |
| %23 = struct_element_addr %22 : $*S1, #S1.a // user: %24 |
| store %21 to %23 : $*Int // id: %24 |
| %25 = tuple () // user: %27 |
| dealloc_stack %1#0 : $*@local_storage S2 // id: %26 |
| return %25 : $() // id: %27 |
| } |
| |
| // Remove dead stores in split and else block on a complex struct. |
| // |
| // CHECK-LABEL: DeadStoreInSplitElseBlockComplexStruct |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: br |
| // CHECK: bb3: |
| // CHECK-NOT: store |
| // CHECK: br |
| sil hidden @DeadStoreInSplitElseBlockComplexStruct : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $S2 // users: %5, %9, %17, %23, %27 |
| // function_ref S2_init |
| %2 = function_ref @S2_init : $@convention(thin) (@thin S2.Type) -> S2 // user: %4 |
| %3 = metatype $@thin S2.Type // user: %4 |
| %4 = apply %2(%3) : $@convention(thin) (@thin S2.Type) -> S2 // user: %5 |
| store %4 to %1#1 : $*S2 // id: %5 |
| br bb1 // id: %6 |
| |
| bb1: // Preds: bb0 |
| %7 = integer_literal $Builtin.Int64, 0 // user: %8 |
| %8 = struct $Int (%7 : $Builtin.Int64) // user: %11 |
| %9 = struct_element_addr %1#1 : $*S2, #S2.x // user: %10 |
| %10 = struct_element_addr %9 : $*S1, #S1.a // user: %11 |
| store %8 to %10 : $*Int // id: %11 |
| %12 = struct_extract %0 : $Bool, #Bool.value // user: %13 |
| cond_br %12, bb2, bb3 // id: %13 |
| |
| bb2: // Preds: bb1 |
| br bb4 // id: %14 |
| |
| bb3: // Preds: bb1 |
| %15 = integer_literal $Builtin.Int64, 1 // user: %16 |
| %16 = struct $Int (%15 : $Builtin.Int64) // user: %19 |
| %17 = struct_element_addr %1#1 : $*S2, #S2.x // user: %18 |
| %18 = struct_element_addr %17 : $*S1, #S1.a // user: %19 |
| store %16 to %18 : $*Int // id: %19 |
| br bb4 // id: %20 |
| |
| bb4: // Preds: bb2 bb3 |
| %21 = integer_literal $Builtin.Int64, 2 // user: %22 |
| %22 = struct $Int (%21 : $Builtin.Int64) // user: %25 |
| %23 = struct_element_addr %1#1 : $*S2, #S2.x // user: %24 |
| %24 = struct_element_addr %23 : $*S1, #S1.a // user: %25 |
| store %22 to %24 : $*Int // id: %25 |
| %26 = tuple () // user: %28 |
| dealloc_stack %1#0 : $*@local_storage S2 // id: %27 |
| return %26 : $() // id: %28 |
| } |
| |
| // Remove dead store in 1 loop block, as the store in exit block kills it. |
| // |
| // CHECK-LABEL: DeadStoreSingleLoopBlockSimpleStruct |
| // CHECK: bb2: |
| // CHECK: store |
| // CHECK: br |
| sil hidden @DeadStoreSingleLoopBlockSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1, var, name "x" // users: %8, %13, %18, %23, %26 |
| %5 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %7 |
| %6 = metatype $@thin S1.Type // user: %7 |
| %7 = apply %5(%6) : $@convention(thin) (@thin S1.Type) -> S1 // user: %8 |
| store %7 to %2#1 : $*S1 // id: %8 |
| br bb1 |
| |
| bb1: // Preds: bb0 |
| %11 = integer_literal $Builtin.Int64, 0 // user: %12 |
| %12 = struct $Int (%11 : $Builtin.Int64) // user: %14 |
| %13 = struct_element_addr %2#1 : $*S1, #S1.a // user: %14 |
| %14 = load %13 : $*Int |
| br bb2 // id: %15 |
| |
| bb2: // Preds: bb0 |
| %16 = integer_literal $Builtin.Int64, 1 // user: %17 |
| %17 = struct $Int (%16 : $Builtin.Int64) // user: %19 |
| %18 = struct_element_addr %2#1 : $*S1, #S1.a // user: %19 |
| store %17 to %18 : $*Int // id: %19 |
| %9 = struct_extract %0 : $Bool, #Bool.value // user: %10 |
| cond_br %9, bb1, bb3 // id: %10 |
| |
| bb3: // Preds: bb1 bb2 |
| %21 = integer_literal $Builtin.Int64, 2 // user: %22 |
| %22 = struct $Int (%21 : $Builtin.Int64) // user: %24 |
| %23 = struct_element_addr %2#1 : $*S1, #S1.a // user: %24 |
| store %22 to %23 : $*Int // id: %24 |
| %25 = tuple () // user: %27 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %26 |
| return %25 : $() // id: %27 |
| } |
| |
| // Remove dead stores in loop blocks, as the store in exit block kills them. |
| // |
| // CHECK-LABEL: DeadStoreInMultiLoopBlocksSimpleStruct |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: br |
| // CHECK: bb2: |
| // CHECK-NOT: store |
| // CHECK: br |
| sil hidden @DeadStoreInMultiLoopBlocksSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1, var, name "x" // users: %8, %13, %18, %23, %26 |
| %5 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %7 |
| %6 = metatype $@thin S1.Type // user: %7 |
| %7 = apply %5(%6) : $@convention(thin) (@thin S1.Type) -> S1 // user: %8 |
| store %7 to %2#1 : $*S1 // id: %8 |
| br bb1 |
| |
| bb1: // Preds: bb0 |
| %11 = integer_literal $Builtin.Int64, 0 // user: %12 |
| %12 = struct $Int (%11 : $Builtin.Int64) // user: %14 |
| %13 = struct_element_addr %2#1 : $*S1, #S1.a // user: %14 |
| store %12 to %13 : $*Int // id: %14 |
| br bb2 // id: %15 |
| |
| bb2: // Preds: bb0 |
| %16 = integer_literal $Builtin.Int64, 1 // user: %17 |
| %17 = struct $Int (%16 : $Builtin.Int64) // user: %19 |
| %18 = struct_element_addr %2#1 : $*S1, #S1.a // user: %19 |
| store %17 to %18 : $*Int // id: %19 |
| %9 = struct_extract %0 : $Bool, #Bool.value // user: %10 |
| cond_br %9, bb1, bb3 // id: %10 |
| |
| bb3: // Preds: bb1 bb2 |
| %21 = integer_literal $Builtin.Int64, 2 // user: %22 |
| %22 = struct $Int (%21 : $Builtin.Int64) // user: %24 |
| %23 = struct_element_addr %2#1 : $*S1, #S1.a // user: %24 |
| store %22 to %23 : $*Int // id: %24 |
| %25 = tuple () // user: %27 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %26 |
| return %25 : $() // id: %27 |
| } |
| |
| // Remove dead store in the tuple data structure. |
| // |
| // CHECK-LABEL: DeadStoreInSimpleTuple |
| // CHECK: bb0: |
| // CHECK: store |
| // CHECK: store |
| // CHECK-NOT: store |
| // CHECK: load |
| sil hidden @DeadStoreInSimpleTuple : $@convention(thin) () -> Int { |
| bb0: |
| %0 = alloc_stack $(a: Int, b: Int), var, name "x" // users: %1, %2, %11, %15, %20 |
| %1 = tuple_element_addr %0#1 : $*(a: Int, b: Int), 0 // user: %5 |
| %2 = tuple_element_addr %0#1 : $*(a: Int, b: Int), 1 // user: %8 |
| %3 = integer_literal $Builtin.Int64, 2 // user: %4 |
| %4 = struct $Int (%3 : $Builtin.Int64) // user: %5 |
| store %4 to %1 : $*Int // id: %5 |
| %6 = integer_literal $Builtin.Int64, 2 // user: %7 |
| %7 = struct $Int (%6 : $Builtin.Int64) // user: %8 |
| store %7 to %2 : $*Int // id: %8 |
| %9 = integer_literal $Builtin.Int64, 10 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %12 |
| %11 = tuple_element_addr %0#1 : $*(a: Int, b: Int), 0 // user: %12 |
| store %10 to %11 : $*Int // id: %12 |
| %13 = integer_literal $Builtin.Int64, 12 // user: %14 |
| %14 = struct $Int (%13 : $Builtin.Int64) // user: %16 |
| %15 = tuple_element_addr %0#1 : $*(a: Int, b: Int), 1 // user: %16 |
| store %14 to %15 : $*Int // id: %16 |
| %22 = load %15 : $*Int |
| %23 = load %11 : $*Int |
| %24 = load %2 : $*Int |
| %25 = load %1 : $*Int |
| %17 = integer_literal $Builtin.Int64, 22 // user: %19 |
| %18 = tuple () |
| %19 = struct $Int (%17 : $Builtin.Int64) // user: %21 |
| dealloc_stack %0#0 : $*@local_storage (a: Int, b: Int) // id: %20 |
| return %19 : $Int // id: %21 |
| } |
| |
| // Remove dead stores in if else blocks for simple class. |
| // |
| // CHECK-LABEL: DeadStoreInIfElseSimpleClass |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: br |
| // CHECK: bb2: |
| // CHECK-NOT: store |
| // CHECK: br |
| sil hidden @DeadStoreInIfElseSimpleClass : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $foo // users: %8, %36 |
| %3 = alloc_ref $foo // users: %6, %8, %11, %14, %17, %19, %22, %25, %27, %30, %33, %34 |
| %4 = integer_literal $Builtin.Int64, 10 // user: %5 |
| %5 = struct $Int (%4 : $Builtin.Int64) // user: %7 |
| %6 = ref_element_addr %3 : $foo, #foo.a // user: %7 |
| store %5 to %6 : $*Int // id: %7 |
| store %3 to %1#1 : $*foo // id: %8 |
| %9 = struct_extract %0 : $Bool, #Bool.value // user: %10 |
| cond_br %9, bb1, bb2 // id: %10 |
| |
| bb1: // Preds: bb0 |
| %12 = integer_literal $Builtin.Int64, 11 // user: %13 |
| %13 = struct $Int (%12 : $Builtin.Int64) // user: %15 |
| %14 = ref_element_addr %3 : $foo, #foo.a // user: %15 |
| store %13 to %14 : $*Int // id: %15 |
| %16 = tuple () |
| br bb3 // id: %18 |
| |
| bb2: // Preds: bb0 |
| %20 = integer_literal $Builtin.Int64, 12 // user: %21 |
| %21 = struct $Int (%20 : $Builtin.Int64) // user: %23 |
| %22 = ref_element_addr %3 : $foo, #foo.a // user: %23 |
| store %21 to %22 : $*Int // id: %23 |
| %24 = tuple () |
| br bb3 // id: %26 |
| |
| bb3: // Preds: bb1 bb2 |
| strong_retain %3 : $foo // id: %27 |
| %28 = integer_literal $Builtin.Int64, 13 // user: %29 |
| %29 = struct $Int (%28 : $Builtin.Int64) // user: %31 |
| %30 = ref_element_addr %3 : $foo, #foo.a // user: %31 |
| store %29 to %30 : $*Int // id: %31 |
| %32 = tuple () |
| strong_release %3 : $foo // id: %33 |
| strong_release %3 : $foo // id: %34 |
| %35 = tuple () // user: %37 |
| dealloc_stack %1#0 : $*@local_storage foo // id: %36 |
| return %35 : $() // id: %37 |
| } |
| |
| // Remove dead store in split block for simple class. |
| // |
| // CHECK-LABEL: DeadStoreInSplitSimpleClass |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: cond_br |
| sil hidden @DeadStoreInSplitSimpleClass : $@convention(thin) (Bool) -> () { |
| bb0(%0 : $Bool): |
| %1 = alloc_stack $foo // users: %3, %25 |
| %2 = alloc_ref $foo // users: %3, %7, %13, %19, %23 |
| store %2 to %1#1 : $*foo // id: %3 |
| br bb1 // id: %4 |
| |
| bb1: // Preds: bb0 |
| %5 = integer_literal $Builtin.Int64, 10 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // user: %8 |
| %7 = ref_element_addr %2 : $foo, #foo.a // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = struct_extract %0 : $Bool, #Bool.value // user: %10 |
| cond_br %9, bb2, bb3 // id: %10 |
| |
| bb2: // Preds: bb1 |
| %11 = integer_literal $Builtin.Int64, 11 // user: %12 |
| %12 = struct $Int (%11 : $Builtin.Int64) // user: %14 |
| %13 = ref_element_addr %2 : $foo, #foo.a // user: %14 |
| store %12 to %13 : $*Int // id: %14 |
| %15 = tuple () |
| br bb4 // id: %16 |
| |
| bb3: // Preds: bb1 |
| %17 = integer_literal $Builtin.Int64, 12 // user: %18 |
| %18 = struct $Int (%17 : $Builtin.Int64) // user: %20 |
| %19 = ref_element_addr %2 : $foo, #foo.a // user: %20 |
| store %18 to %19 : $*Int // id: %20 |
| %21 = tuple () |
| br bb4 // id: %22 |
| |
| bb4: // Preds: bb2 bb3 |
| strong_release %2 : $foo // id: %23 |
| %24 = tuple () // user: %26 |
| dealloc_stack %1#0 : $*@local_storage foo // id: %25 |
| return %24 : $() // id: %26 |
| } |
| |
| // Can not 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 // users: %3, %20 |
| %2 = alloc_ref $foo // users: %3, %7, %14, %18 |
| store %2 to %1#1 : $*foo // id: %3 |
| br bb1 // id: %4 |
| |
| bb1: // Preds: bb0 |
| %5 = integer_literal $Builtin.Int64, 10 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // user: %8 |
| %7 = ref_element_addr %2 : $foo, #foo.a // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = struct_extract %0 : $Bool, #Bool.value // user: %10 |
| cond_br %9, bb2, bb3 // id: %10 |
| |
| bb2: // Preds: bb1 |
| br bb4 // id: %11 |
| |
| bb3: // Preds: bb1 |
| %12 = integer_literal $Builtin.Int64, 12 // user: %13 |
| %13 = struct $Int (%12 : $Builtin.Int64) // user: %15 |
| %14 = ref_element_addr %2 : $foo, #foo.a // user: %15 |
| store %13 to %14 : $*Int // id: %15 |
| %16 = tuple () |
| br bb4 // id: %17 |
| |
| bb4: // Preds: bb2 bb3 |
| strong_release %2 : $foo // id: %18 |
| %19 = tuple () // user: %21 |
| dealloc_stack %1#0 : $*@local_storage foo // id: %20 |
| return %19 : $() // id: %21 |
| } |
| |
| // Can not 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 // users: %8, %36 |
| %3 = alloc_ref $foo // users: %6, %8, %11, %14, %17, %19, %22, %25, %27, %30, %33, %34 |
| store %3 to %1#1 : $*foo // id: %8 |
| br bb1 |
| |
| bb1: |
| %4 = integer_literal $Builtin.Int64, 10 // user: %5 |
| %5 = struct $Int (%4 : $Builtin.Int64) // user: %7 |
| %6 = ref_element_addr %3 : $foo, #foo.a // user: %7 |
| store %5 to %6 : $*Int // id: %7 |
| %9 = struct_extract %0 : $Bool, #Bool.value // user: %10 |
| cond_br %9, bb2, bb3 // id: %10 |
| |
| bb2: // Preds: bb0 |
| %32 = function_ref @foo_user : $@convention(thin) (@guaranteed foo) -> ()// user: %4 |
| %33 = apply %32(%3) : $@convention(thin) (@guaranteed foo) -> () // users: %6, %15 |
| %120 = integer_literal $Builtin.Int64, 12 // user: %21 |
| %121 = struct $Int (%120 : $Builtin.Int64) // user: %23 |
| store %121 to %6 : $*Int // id: %23 |
| %124 = tuple () |
| br bb4 // id: %18 |
| |
| bb3: // Preds: bb0 |
| %20 = integer_literal $Builtin.Int64, 12 // user: %21 |
| %21 = struct $Int (%20 : $Builtin.Int64) // user: %23 |
| store %21 to %6 : $*Int // id: %23 |
| %24 = tuple () |
| br bb4 // id: %26 |
| |
| bb4: // Preds: bb1 bb2 |
| strong_release %3 : $foo // id: %34 |
| %35 = tuple () // user: %37 |
| dealloc_stack %1#0 : $*@local_storage foo // id: %36 |
| return %35 : $() // id: %37 |
| } |
| |
| // 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: DeadStoreSameBlockAcrossFunctionCallSimpleStruct |
| // CHECK: bb1: |
| // CHECK-NOT: store |
| // CHECK: function_ref |
| sil hidden @DeadStoreSameBlockAcrossFunctionCallSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 // users: %6, %9, %18 |
| // function_ref S1_init |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %5 |
| %4 = metatype $@thin S1.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 // user: %6 |
| store %5 to %2#1 : $*S1 // id: %6 |
| %7 = integer_literal $Builtin.Int64, 0 // user: %8 |
| %8 = struct $Int (%7 : $Builtin.Int64) // users: %12, %15 |
| %9 = struct_element_addr %2#1 : $*S1, #S1.a // users: %12, %15 |
| %10 = alloc_ref $foo // user: %14 |
| br bb1 // id: %11 |
| |
| bb1: // Preds: bb0 |
| store %8 to %9 : $*Int // id: %12 |
| %13 = function_ref @foo_user : $@convention(thin) (@guaranteed foo) -> () // user: %14 |
| %14 = apply %13(%10) : $@convention(thin) (@guaranteed foo) -> () |
| store %8 to %9 : $*Int // id: %15 |
| br bb2 // id: %16 |
| |
| bb2: // Preds: bb1 |
| %17 = tuple () // user: %19 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %18 |
| return %17 : $() // id: %19 |
| } |
| |
| |
| // store to stack allocated memory can not alias with incoming argument. |
| // |
| // CHECK-LABEL: DeadStoreInoutAndStackAlias |
| // CHECK: bb0 |
| // CHECK : struct_element_addr |
| // CHECK-NOT: store |
| // CHECK: load |
| sil hidden @DeadStoreInoutAndStackAlias : $@convention(thin) (@inout A) -> () { |
| bb0(%0 : $*A): |
| %1 = alloc_stack $A // users: %4, %8 |
| %2 = integer_literal $Builtin.Int32, 0 // users: %5, %7 |
| %3 = struct_element_addr %0 : $*A, #A.i // user: %6 |
| %4 = struct_element_addr %1#1 : $*A, #A.i // users: %5, %7 |
| store %2 to %4 : $*Builtin.Int32 // id: %5 |
| %6 = load %3 : $*Builtin.Int32 |
| store %2 to %4 : $*Builtin.Int32 // id: %7 |
| dealloc_stack %1#0 : $*@local_storage A // id: %8 |
| %9 = tuple () // user: %10 |
| return %9 : $() // id: %10 |
| } |
| |
| // test dead store for enums. there should be only 1 store left. |
| // |
| // CHECK-LABEL: DeadStoreEnumSameCase |
| // CHECK: store |
| // CHECK-NOT: store |
| // CHECK: return |
| sil hidden @DeadStoreEnumSameCase : $@convention(thin) (@inout Example) -> Int64 { |
| bb0(%0 : $*Example): |
| %1 = integer_literal $Builtin.Int64, 64 // user: %2 |
| %2 = struct $Int64 (%1 : $Builtin.Int64) // user: %5 |
| %3 = integer_literal $Builtin.Int64, 64 // user: %4 |
| %4 = struct $Int64 (%3 : $Builtin.Int64) // user: %5 |
| %5 = tuple (%2 : $Int64, %4 : $Int64) // user: %6 |
| %6 = enum $Example, #Example.A!enumelt.1, %5 : $(Int64, Int64) // users: %7, %15 |
| store %6 to %0 : $*Example // id: %7 |
| %8 = integer_literal $Builtin.Int64, 64 // user: %9 |
| %9 = struct $Int64 (%8 : $Builtin.Int64) // user: %12 |
| %10 = integer_literal $Builtin.Int64, 64 // user: %11 |
| %11 = struct $Int64 (%10 : $Builtin.Int64) // user: %12 |
| %12 = tuple (%9 : $Int64, %11 : $Int64) // user: %13 |
| %13 = enum $Example, #Example.A!enumelt.1, %12 : $(Int64, Int64) // users: %14, %18 |
| store %13 to %0 : $*Example // id: %14 |
| release_value %6 : $Example // id: %15 |
| %16 = integer_literal $Builtin.Int64, 0 // user: %17 |
| %17 = struct $Int64 (%16 : $Builtin.Int64) // user: %20 |
| release_value %13 : $Example // id: %18 |
| return %17 : $Int64 // id: %20 |
| } |
| |
| // The store in bb0 as the its only partially dead, i.e. |
| // the structure S3 has 2 fields. |
| // |
| // CHECK-LABEL: PartialDeadStoreSimpleStruct |
| // CHECK: bb0 |
| // CHECK-NEXT: [[RET0:%.+]] = alloc_stack |
| // CHECK: [[RET1:%.+]] = function_ref @S3_init |
| // CHECK-NEXT: [[RET2:%.+]] = metatype $@thin S3.Type |
| // CHECK-NEXT: [[RET3:%.+]] = apply [[RET1:%.+]]([[RET2:%.+]]) |
| // CHECK-NOT: store [[RET3:%.+]] to [[RET0:%.+]]#1 : $*S3 |
| sil hidden @PartialDeadStoreSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S3 // users: %6, %11, %16, %20 |
| // function_ref S3_init |
| %3 = function_ref @S3_init : $@convention(thin) (@thin S3.Type) -> S3 // user: %5 |
| %4 = metatype $@thin S3.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S3.Type) -> S3 // user: %6 |
| store %5 to %2#1 : $*S3 // id: %6 |
| %7 = struct_extract %0 : $Bool, #Bool.value // user: %8 |
| cond_br %7, bb1, bb2 // id: %8 |
| |
| bb1: // Preds: bb0 |
| %9 = integer_literal $Builtin.Int64, 0 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %12 |
| %11 = struct_element_addr %2#1 : $*S3, #S3.a // user: %12 |
| store %10 to %11 : $*Int // id: %12 |
| br bb3 // id: %13 |
| |
| bb2: // Preds: bb0 |
| %14 = integer_literal $Builtin.Int64, 1 // user: %15 |
| %15 = struct $Int (%14 : $Builtin.Int64) // user: %17 |
| %16 = struct_element_addr %2#1 : $*S3, #S3.a // user: %17 |
| store %15 to %16 : $*Int // id: %17 |
| br bb3 // id: %18 |
| |
| bb3: // Preds: bb1 bb2 |
| %19 = tuple () // user: %21 |
| dealloc_stack %2#0 : $*@local_storage S3 // id: %20 |
| return %19 : $() // id: %21 |
| } |
| |
| /// Make sure we can coalesce the 2 live stores to the 2 fields in S4. |
| /// |
| /// CHECK-LABEL : PartialDeadStoreStructInStruct |
| /// CHECK: [[RET0:%.+]] = struct_extract |
| /// CHECK: [[RET1:%.+]] = struct_element_addr |
| /// CHECK: store [[RET0:%.+]] to [[RET1:%.+]] : $*S4 |
| sil hidden @PartialDeadStoreStructInStruct : $@convention(thin) (@inout S5) -> () { |
| bb0(%0 : $*S5): |
| %1 = function_ref @S5_init : $@convention(thin) (@thin S5.Type) -> S5 // user: %3 |
| %2 = metatype $@thin S5.Type // user: %3 |
| %3 = apply %1(%2) : $@convention(thin) (@thin S5.Type) -> S5 // user: %4 |
| store %3 to %0 : $*S5 // id: %4 |
| %5 = integer_literal $Builtin.Int64, 11 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // user: %8 |
| %7 = struct_element_addr %0 : $*S5, #S5.y // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = tuple () // user: %11 |
| return %9 : $() // id: %11 |
| } |
| |
| /// Make sure we do not coalesce the 2 live stores to the 2 fields in S6. |
| /// |
| /// CHECK-LABEL : DiscontiguosPartialDeadStoreInSimpleStruct |
| /// CHECK: store |
| /// CHECK: store |
| /// CHECK: store |
| sil hidden @DiscontiguosPartialDeadStoreInSimpleStruct : $@convention(thin) (@inout S6) -> () { |
| bb0(%0 : $*S6): |
| %1 = function_ref @S6_init : $@convention(thin) (@thin S6.Type) -> S6 // user: %3 |
| %2 = metatype $@thin S6.Type // user: %3 |
| %3 = apply %1(%2) : $@convention(thin) (@thin S6.Type) -> S6 // user: %4 |
| store %3 to %0 : $*S6 // id: %4 |
| %5 = integer_literal $Builtin.Int64, 10 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // user: %8 |
| %7 = struct_element_addr %0 : $*S6, #S6.y // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = tuple () // user: %11 |
| return %9 : $() // id: %11 |
| } |
| |
| /// Make sure we do not generate too many stores from a large store that |
| /// is partially dead. |
| /// |
| /// CHECK-LABEL : DiscontiguosPartialDeadStoreInSimpleLargeStruct |
| /// CHECK: store |
| /// CHECK-NOT: store |
| /// CHECK: store |
| sil hidden @DiscontiguosPartialDeadStoreInSimpleLargeStruct : $@convention(thin) (@inout S7) -> () { |
| bb0(%0 : $*S7): |
| %1 = function_ref @S7_init : $@convention(thin) (@thin S7.Type) -> S7 // user: %3 |
| %2 = metatype $@thin S7.Type // user: %3 |
| %3 = apply %1(%2) : $@convention(thin) (@thin S7.Type) -> S7 // user: %4 |
| store %3 to %0 : $*S7 // id: %4 |
| %5 = integer_literal $Builtin.Int64, 10 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // user: %8 |
| %7 = struct_element_addr %0 : $*S7, #S7.y // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = tuple () // user: %11 |
| return %9 : $() // id: %11 |
| } |
| |
| /// 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 : PartialDeadStoreBailOutProperPropagation |
| /// CHECK: bb0 |
| /// CHECK-NOT: store |
| /// CHECK: function_ref |
| sil hidden @PartialDeadStoreBailOutProperPropagation : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $S7, var, name "a" // users: %4, %7, %10 |
| %55 = integer_literal $Builtin.Int64, 10 // user: %6 |
| %56 = struct $Int (%55 : $Builtin.Int64) // user: %8 |
| %57 = struct_element_addr %0#1 : $*S7, #S7.a // user: %8 |
| store %56 to %57 : $*Int // id: %8 |
| %1 = function_ref @S7_init : $@convention(thin) (@thin S7.Type) -> S7 // user: %3 |
| %2 = metatype $@thin S7.Type // user: %3 |
| %3 = apply %1(%2) : $@convention(thin) (@thin S7.Type) -> S7 // user: %4 |
| store %3 to %0#1 : $*S7 // id: %4 |
| %5 = integer_literal $Builtin.Int64, 10 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // user: %8 |
| %7 = struct_element_addr %0#1 : $*S7, #S7.y // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = tuple () // user: %11 |
| dealloc_stack %0#0 : $*@local_storage S7 // id: %10 |
| return %9 : $() // id: %11 |
| } |
| |
| /// 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: PartialDeadStoreInSimpleSmallStruct |
| /// CHECK: apply |
| /// CHECK-NEXT: struct_extract |
| sil hidden @PartialDeadStoreInSimpleSmallStruct : $@convention(thin) (@inout S3) -> () { |
| bb0(%0 : $*S3): |
| %1 = function_ref @S3_init : $@convention(thin) (@thin S3.Type) -> S3 // user: %3 |
| %2 = metatype $@thin S3.Type // user: %3 |
| %3 = apply %1(%2) : $@convention(thin) (@thin S3.Type) -> S3 // user: %4 |
| store %3 to %0 : $*S3 // id: %4 |
| %5 = integer_literal $Builtin.Int64, 10 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // user: %8 |
| %7 = struct_element_addr %0 : $*S3, #S3.a // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = tuple () // user: %11 |
| return %9 : $() // id: %11 |
| } |
| |
| /// 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: SelfLoopClassTestTypeExpansion |
| /// CHECK: [[RET0:%.+]] = struct_extract |
| /// CHECK: [[RET1:%.+]] = struct_element_addr |
| /// CHECK-NEXT: store [[RET0:%.+]] to [[RET1:%.+]] : $*SelfLoop |
| sil hidden @SelfLoopClassTestTypeExpansion : $@convention(thin) (@inout S8) -> () { |
| bb0(%0 : $*S8): |
| %1 = function_ref @S8_init : $@convention(thin) (@thin S8.Type) -> @owned S8 // user: %3 |
| %2 = metatype $@thin S8.Type // user: %3 |
| %3 = apply %1(%2) : $@convention(thin) (@thin S8.Type) -> @owned S8 // users: %4, %9 |
| store %3 to %0 : $*S8 // id: %4 |
| %5 = integer_literal $Builtin.Int64, 12 // user: %6 |
| %6 = struct $Int (%5 : $Builtin.Int64) // users: %8, %10 |
| %7 = struct_element_addr %0 : $*S8, #S8.k // user: %8 |
| store %6 to %7 : $*Int // id: %8 |
| %9 = struct_extract %3 : $S8, #S8.i // user: %10 |
| %10 = struct $S8 (%9 : $SelfLoop, %6 : $Int) // user: %11 |
| release_value %10 : $S8 // id: %11 |
| %12 = tuple () // user: %14 |
| return %12 : $() // id: %14 |
| } |
| |
| /// Make sure we do not remove the first store as there is no way to prove |
| /// %0 and %1 can not alias here. |
| /// |
| /// CHECK-LABEL: NoDeadStoreWithInterferingLoad |
| /// CHECK: store |
| /// CHECK: load |
| /// CHECK: store |
| sil hidden @NoDeadStoreWithInterferingLoad : $@convention(thin) (@owned foo, @owned foo) -> () { |
| bb0(%0 : $foo, %1 : $foo): |
| debug_value %0 : $foo, let, name "x" // id: %2 |
| debug_value %1 : $foo, let, name "a" // id: %3 |
| %4 = integer_literal $Builtin.Int64, 10 // user: %5 |
| %5 = struct $Int (%4 : $Builtin.Int64) // user: %7 |
| %6 = ref_element_addr %0 : $foo, #foo.a // user: %7 |
| store %5 to %6 : $*Int // id: %7 |
| %8 = tuple () |
| %9 = integer_literal $Builtin.Int64, 12 // user: %10 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %12 |
| %11 = ref_element_addr %1 : $foo, #foo.a // user: %12 |
| %99 = load %11 : $*Int // id: %12 |
| %13 = tuple () |
| %14 = integer_literal $Builtin.Int64, 10 // user: %15 |
| %15 = struct $Int (%14 : $Builtin.Int64) // user: %17 |
| %16 = ref_element_addr %0 : $foo, #foo.a // user: %17 |
| store %15 to %16 : $*Int // id: %17 |
| %18 = tuple () |
| strong_release %1 : $foo // id: %19 |
| strong_release %0 : $foo // id: %20 |
| %21 = tuple () // user: %22 |
| return %21 : $() // id: %22 |
| } |
| |
| // 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: DeadStoreAcrossLoopSimpleStruct |
| // CHECK: bb0 |
| // CHECK-NOT: store |
| // CHECK: br |
| sil hidden @DeadStoreAcrossLoopSimpleStruct : $@convention(thin) (Bool, Int) -> () { |
| bb0(%0 : $Bool, %1 : $Int): |
| %2 = alloc_stack $S1 // users: %6, %9, %16, %19 |
| %3 = function_ref @S1_init : $@convention(thin) (@thin S1.Type) -> S1 // user: %5 |
| %4 = metatype $@thin S1.Type // user: %5 |
| %5 = apply %3(%4) : $@convention(thin) (@thin S1.Type) -> S1 // user: %6 |
| store %5 to %2#1 : $*S1 // id: %6 |
| %7 = integer_literal $Builtin.Int64, 2 // user: %8 |
| %8 = struct $Int (%7 : $Builtin.Int64) // user: %10 |
| %9 = struct_element_addr %2#1 : $*S1, #S1.a // user: %10 |
| store %8 to %9 : $*Int // id: %10 |
| br bb1 // id: %11 |
| |
| bb1: // Preds: bb0 bb1 |
| %12 = struct_extract %0 : $Bool, #Bool.value // user: %13 |
| cond_br %12, bb1, bb2 // id: %13 |
| |
| bb2: // Preds: bb1 |
| %14 = integer_literal $Builtin.Int64, 2 // user: %15 |
| %15 = struct $Int (%14 : $Builtin.Int64) // user: %17 |
| %16 = struct_element_addr %2#1 : $*S1, #S1.a // user: %17 |
| store %15 to %16 : $*Int // id: %17 |
| %18 = tuple () // user: %20 |
| dealloc_stack %2#0 : $*@local_storage S1 // id: %19 |
| return %18 : $() // id: %20 |
| } |
| |
| |
| // 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 // user: %5 |
| %5 = struct $Int (%4 : $Builtin.Int64) // user: %7 |
| %6 = ref_element_addr %0 : $foo, #foo.a // user: %17 |
| store %5 to %6 : $*Int |
| br bb1 |
| |
| bb1: |
| %7 = ref_element_addr %1 : $foo, #foo.a // user: %17 |
| %8 = load %7 : $*Int |
| br bb2 |
| |
| bb2: |
| %9 = integer_literal $Builtin.Int64, 10 // user: %5 |
| %10 = struct $Int (%9 : $Builtin.Int64) // user: %7 |
| %11 = ref_element_addr %0 : $foo, #foo.a // user: %17 |
| store %5 to %6 : $*Int |
| %18 = tuple () // user: %19 |
| return %18 : $() // id: %19 |
| } |
| |
| /// 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<protocol<>.Type>) -> Int { |
| bb0(%0 : $Optional<protocol<>.Type>): |
| %2 = alloc_stack $Optional<protocol<>.Type> // users: %3, %17, %19, %26 |
| store %0 to %2#1 : $*Optional<protocol<>.Type> // id: %3 |
| %17 = unchecked_addr_cast %2#1 : $*Optional<protocol<>.Type> to $*Int // user: %18 |
| %18 = load %17 : $*Int // user: %27 |
| dealloc_stack %2#0 : $*@local_storage Optional<protocol<>.Type> // id: %26 |
| return %18 : $Int // id: %27 |
| } |
| |
| /// Make sure we DO get rid of the store as a dead store, i.e. no read to a store |
| /// to a local variable. |
| /// |
| /// DISABLECHECK-LABEL: local_dead_store |
| /// DISABLECHECK: load |
| /// DISABLECHECK-NOT: store |
| /// DISABLECHECK: return |
| sil @local_dead_store : $@convention(thin) (Optional<protocol<>.Type>) -> Int { |
| bb0(%0 : $Optional<protocol<>.Type>): |
| %2 = alloc_stack $Optional<protocol<>.Type> // users: %3, %17, %19, %26 |
| %17 = unchecked_addr_cast %2#1 : $*Optional<protocol<>.Type> to $*Int // user: %18 |
| %18 = load %17 : $*Int // user: %27 |
| store %0 to %2#1 : $*Optional<protocol<>.Type> // id: %3 |
| dealloc_stack %2#0 : $*@local_storage Optional<protocol<>.Type> // id: %26 |
| return %18 : $Int // id: %27 |
| } |