blob: 80c768104f323d9a09bff69368cdfff49fa34184 [file] [log] [blame]
// 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
}