mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
I am doing this separately from the actual change to eliminate the option to make it easier to review.
1463 lines
41 KiB
Plaintext
1463 lines
41 KiB
Plaintext
// RUN: %target-sil-opt %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 should be able to remove the local store into alloc_ref [stack] locations
|
|
// that is not read.
|
|
//
|
|
// CHECK-LABEL: trivial_local_dead_store_into_alloc_ref
|
|
// CHECK: bb0
|
|
// CHECK-NOT: {{ store}}
|
|
// CHECK: tuple
|
|
// CHECK: return
|
|
sil hidden @trivial_local_dead_store_into_alloc_ref_stack : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_ref [stack] $foo
|
|
%1 = integer_literal $Builtin.Int64, 1
|
|
%2 = struct $Int (%1 : $Builtin.Int64)
|
|
%3 = ref_element_addr %0 : $foo, #foo.a
|
|
store %2 to %3 : $*Int
|
|
%4 = tuple ()
|
|
dealloc_ref [stack] %0 : $foo
|
|
return %4 : $()
|
|
}
|
|
|
|
// We cannot remove the local store that is potentially read by the deinit of foo.
|
|
//
|
|
// CHECK-LABEL: sil hidden @blocking_read_on_local_store_into_alloc_ref_stack
|
|
// CHECK: bb0
|
|
// CHECK: alloc_ref
|
|
// CHECK: store
|
|
// CHECK: dealloc_ref
|
|
// CHECK: return
|
|
sil hidden @blocking_read_on_local_store_into_alloc_ref_stack : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_ref [stack] $foo
|
|
%1 = integer_literal $Builtin.Int64, 1
|
|
%2 = struct $Int (%1 : $Builtin.Int64)
|
|
%3 = ref_element_addr %0 : $foo, #foo.a
|
|
store %2 to %3 : $*Int
|
|
%4 = tuple ()
|
|
strong_release %0 : $foo
|
|
dealloc_ref [stack] %0 : $foo
|
|
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
|
|
store %0 to %1a : $*B
|
|
store %0 to %1a : $*B
|
|
%4 = tuple()
|
|
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: 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 : $()
|
|
}
|