Files
swift-mirror/test/SILOptimizer/redundant_load_elim.sil
Erik Eckstein 4d20423e00 Optimizer: re-implement the RedundantLoadElimination pass in Swift
The new implementation has several benefits compared to the old C++ implementation:

* It is significantly simpler. It optimizes each load separately instead of all at once with bit-field based dataflow.
* It's using alias analysis more accurately which enables more loads to be optimized
* It avoids inserting additional copies in OSSA

The algorithm is a data flow analysis which starts at the original load and searches for preceding stores or loads by following the control flow in backward direction.
The preceding stores and loads provide the "available values" with which the original load can be replaced.
2023-07-21 07:19:56 +02:00

1298 lines
48 KiB
Plaintext

// RUN: %target-sil-opt -enforce-exclusivity=none -enable-sil-verify-all %s -redundant-load-elimination | %FileCheck %s
// REQUIRES: swift_in_compiler
// Declare this SIL to be canonical because some tests break raw SIL
// conventions. e.g. address-type block args. -enforce-exclusivity=none is also
// required to allow address-type block args in canonical SIL.
sil_stage canonical
import Builtin
import Swift
///////////////////////
// Type Declarations //
///////////////////////
typealias I32 = Builtin.Int32
struct Int {
var value : Builtin.Int64
}
struct Int32 {
var value : Builtin.Int32
}
struct Int64 {
var value : Builtin.Int64
}
struct Bool {
var value : Builtin.Int1
}
class AX {
final var current: Int32
init()
}
struct A {
var i : Builtin.Int32
}
struct AA {
var a : A
var i : Builtin.Int32
}
class B {
var i : Builtin.Int32
init()
}
struct X {
var c : B
init()
}
struct Agg2 {
var t : (Builtin.Int64, Builtin.Int32)
}
struct Agg1 {
var a : Agg2
}
enum Optional<T> {
case none
case some(T)
}
class E : B { }
struct C {
var i : Builtin.Int16
}
struct D {
var p : Builtin.RawPointer
}
struct Wrapper {
var value : Builtin.Int32
}
class AB {
var value: Int
var value2: Int
init(value: Int)
deinit
}
enum XYZ {
case A
case B((Int32, Int32))
case C(Int32)
}
struct TwoField {
var a: Int
var b: Int
init(a: Int, b: Int)
init()
}
class C1 {}
class C2 {
var current: Int
init()
}
class C3 : C2 {
override init()
}
class NewRangeGenerator1 {
final var current: Int32
final let end: Int32
init(start: Int32, end: Int32)
}
final class NewHalfOpenRangeGenerator : NewRangeGenerator1 {
override init(start: Int32, end: Int32)
}
sil_global @total : $Int32
sil @use : $@convention(thin) (Builtin.Int32) -> ()
sil @use_Int : $@convention(thin) (Int) -> ()
sil @use_64 : $@convention(thin) (Builtin.Int64) -> ()
sil @use_2_64 : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> ()
sil @use_a : $@convention(thin) (A) -> ()
sil @use_twofield : $@convention(thin) (TwoField) -> ()
sil @escaped_a_ptr : $@convention(thin) () -> @out A
sil @escaped_a : $@convention(thin) () -> Builtin.RawPointer
sil @init_twofield : $@convention(thin) (@thin TwoField.Type) -> TwoField
// We have a bug in the old projection code which this test case exposes.
// Make sure its handled properly in the new projection.
//
// Make sure the store to the different fields does not affect the load
//
// CHECK-LABEL: sil hidden @load_forward_across_store_to_different_field
// CHECK: = load
// CHECK-NOT: = load
// CHECK: return
sil hidden @load_forward_across_store_to_different_field : $@convention(thin) (@owned AB) -> Int {
bb0(%0 : $AB):
%2 = ref_element_addr %0 : $AB, #AB.value // user: %3
%3 = load %2 : $*Int // user: %6
%222 = ref_element_addr %0 : $AB, #AB.value2 // user: %3
store %3 to %222 : $*Int
%4 = ref_element_addr %0 : $AB, #AB.value // user: %5
%5 = load %4 : $*Int // user: %7
%22 = function_ref @use_Int : $@convention(thin) (Int) -> ()
apply %22(%3) : $@convention(thin) (Int) -> ()
apply %22(%5) : $@convention(thin) (Int) -> ()
return %5 : $Int // id: %15
}
// CHECK-LABEL: sil hidden @load_forward_across_end_cow_mutation
// CHECK-NOT: = load
// CHECK: return %1
sil hidden @load_forward_across_end_cow_mutation : $@convention(thin) (@owned AB, Int) -> Int {
bb0(%0 : $AB, %1 : $Int):
%2 = ref_element_addr %0 : $AB, #AB.value
store %1 to %2 : $*Int
%4 = end_cow_mutation %0 : $AB
%5 = ref_element_addr %4 : $AB, #AB.value
%6 = load %5 : $*Int
return %6 : $Int
}
// CHECK-LABEL: sil hidden @redundant_load_across_fixlifetime_inst
// CHECK: = load
// CHECK-NOT: = load
// CHECK: return
sil hidden @redundant_load_across_fixlifetime_inst : $@convention(thin) (@owned AB) -> Int {
bb0(%0 : $AB):
%2 = ref_element_addr %0 : $AB, #AB.value // user: %3
%3 = load %2 : $*Int // user: %6
%4 = ref_element_addr %0 : $AB, #AB.value // user: %5
fix_lifetime %0 : $AB
%5 = load %4 : $*Int // user: %7
%22 = function_ref @use_Int : $@convention(thin) (Int) -> ()
apply %22(%3) : $@convention(thin) (Int) -> ()
apply %22(%5) : $@convention(thin) (Int) -> ()
return %5 : $Int // id: %15
}
// Check that we don't crash if the address is an unchecked_addr_cast.
// CHECK-LABEL: sil @test_unchecked_addr_cast
// CHECK: = load
// CHECK: return
sil @test_unchecked_addr_cast : $@convention(thin) (@inout A, A) -> A {
bb0(%0 : $*A, %1 : $A):
%2 = unchecked_addr_cast %0 : $*A to $*A
store %1 to %2 : $*A
%l1 = load %2 : $*A
return %l1 : $A
}
// Multi-BB version of the previous test.
// CHECK-LABEL: sil @test_forwarding_ignoring_unchecked_addr_cast2 : $@convention(thin) (@inout A, A, A) -> A {
// CHECK: bb1
// CHECK: = load
// CHECK: cond_br
sil @test_forwarding_ignoring_unchecked_addr_cast2 : $@convention(thin) (@inout A, A, A) -> A {
bb0(%0 : $*A, %1 : $A, %2: $A):
%3 = unchecked_addr_cast %0 : $*A to $*A
store %1 to %3 : $*A
br bb1
bb1:
%5 = load %3 : $*A
%6 = load %3 : $*A
store %2 to %3 : $*A
cond_br undef, bb1, bb2
bb2:
return %5 : $A
}
// CHECK-LABEL: sil @test_read_dependence_allows_forwarding_multi_bb_1 : $@convention(thin) (@inout A, A) -> A {
// CHECK: bb0
// CHECK: store
// CHECK: bb1
// CHECK: store
// CHECK-NOT: = load
// CHECK: cond_br
sil @test_read_dependence_allows_forwarding_multi_bb_1 : $@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
br bb1
bb1:
// This means that the first store is not dead.
%4 = load %3 : $*A
// But we still should be able to forward this load.
%5 = load %0 : $*A
// We need to dedup this store to trigger the self loop
// forwarding. Once we do the full optimistic data flow this will no
// longer be needed.
%6 = load %0 : $*A
store %1 to %0 : $*A
cond_br undef, bb1, bb2
bb2:
return %5 : $A
}
// DISABLE this test for now. it seems DCE is not getting rid of the load in bb8 after the RLE happens.
//
// Make sure the switch does not affect the forwarding of the load.
// switch_enum cannot have BBArgument, but the %17 = load %2 : $*Int32 is not produced in the
// switch basic block.
// DISABLE_CHECK-LABEL: load_elimination_disregard_switch_enum
// DISABLE_CHECK: bb8
// DISABLE_CHECK-NOT: = load
// DISABLE_CHECK: return
sil @load_elimination_disregard_switch_enum : $@convention(thin) (Int32, Int32, @inout Int32) -> Int32 {
// %0 // user: %4
// %1 // user: %4
// %2 // users: %17, %19
bb0(%0 : $Int32, %1 : $Int32, %2 : $*Int32):
cond_br undef, bb7, bb1 // id: %3
bb1: // Preds: bb0
%4 = tuple (%0 : $Int32, %1 : $Int32) // user: %5
%5 = enum $XYZ, #XYZ.B!enumelt, %4 : $(Int32, Int32) // user: %6
switch_enum %5 : $XYZ, case #XYZ.A!enumelt: bb2, case #XYZ.B!enumelt: bb4, case #XYZ.C!enumelt: bb6 // id: %6
bb2: // Preds: bb1
br bb3 // id: %7
bb3: // Preds: bb2
%8 = integer_literal $Builtin.Int32, 0 // user: %9
%9 = struct $Int32 (%8 : $Builtin.Int32)
br bb5 // id: %10
// %11 // user: %12
bb4(%11 : $(Int32, Int32)): // Preds: bb1
%12 = tuple_extract %11 : $(Int32, Int32), 0
br bb5 // id: %13
bb5: // Preds: bb4 bb5 bb6
br bb5 // id: %14
bb6(%15 : $Int32): // Preds: bb1
br bb5 // id: %16
bb7: // Preds: bb0
%17 = load %2 : $*Int32
br bb8 // id: %18
bb8: // Preds: bb3 bb7
%19 = load %2 : $*Int32 // user: %20
return %19 : $Int32 // id: %20
}
// The load should be eliminated here. but currently is not ... Look into why
//
// CHECK-LABEL: sil @load_store_forwarding_from_aggregate_to_field
sil @load_store_forwarding_from_aggregate_to_field : $@convention(thin) (Agg1) -> (Builtin.Int32) {
bb0(%0 : $Agg1):
%1 = alloc_stack $Agg1
store %0 to %1 : $*Agg1
%2 = struct_element_addr %1 : $*Agg1, #Agg1.a
%3 = struct_element_addr %2 : $*Agg2, #Agg2.t
%4 = tuple_element_addr %3 : $*(Builtin.Int64, Builtin.Int32), 1
%5 = load %4 : $*Builtin.Int32
dealloc_stack %1 : $*Agg1
return %5 : $Builtin.Int32
}
// CHECK-LABEL: sil @store_promotion
// CHECK: store
// CHECK-NEXT: strong_retain
// CHECK-NEXT: strong_retain
// CHECK: return
sil @store_promotion : $@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
%3 = load %1a : $*B
%4 = load %1a : $*B
strong_retain %3 : $B
strong_retain %4 : $B
%7 = tuple()
return %7 : $()
}
// CHECK-LABEL: sil @eliminate_duplicate_loads_over_noread_builtins
// CHECK: bb0
// CHECK-NEXT: [[LOAD_RESULT:%[0-9]+]] = load
// CHECK-NEXT: integer_literal
// CHECK-NEXT: builtin "sadd_with_overflow_Int64"([[LOAD_RESULT]] : ${{.*}}, [[LOAD_RESULT]]
// CHECK-NEXT: [[APPLY_RESULT:%[0-9]+]] = tuple_extract
// CHECK-NEXT: builtin "sadd_with_overflow_Int64"([[LOAD_RESULT]] : ${{.*}}, [[APPLY_RESULT]]
// CHECK-NEXT: tuple_extract
// CHECK-NEXT: return
sil @eliminate_duplicate_loads_over_noread_builtins : $@convention(thin) (@inout Builtin.Int64) -> (Builtin.Int64) {
bb0(%0 : $*Builtin.Int64):
%1 = load %0 : $*Builtin.Int64
%3 = integer_literal $Builtin.Int1, 0
%4 = builtin "sadd_with_overflow_Int64"(%1 : $Builtin.Int64, %1 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%5 = load %0 : $*Builtin.Int64
%6 = tuple_extract %4 : $(Builtin.Int64, Builtin.Int1), 0
%7 = builtin "sadd_with_overflow_Int64"(%5 : $Builtin.Int64, %6 : $Builtin.Int64, %3 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%8 = tuple_extract %7 : $(Builtin.Int64, Builtin.Int1), 0
return %8 : $Builtin.Int64
}
// CHECK-LABEL: sil @load_store_forwarding_over_noread_builtins
// CHECK: bb0
// CHECK-NEXT: = load
// CHECK-NEXT: integer_literal
// CHECK-NEXT: builtin
// CHECK-NEXT: tuple_extract
// CHECK-NEXT: store
// CHECK-NEXT: builtin
// CHECK-NEXT: tuple_extract
// CHECK-NEXT: builtin
// CHECK-NEXT: tuple_extract
// CHECK-NEXT: return
sil @load_store_forwarding_over_noread_builtins : $@convention(thin) (@inout Builtin.Int64, @inout Builtin.Int64) -> (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
%10 = load %1 : $*Builtin.Int64
%11 = builtin "sadd_with_overflow_Int64"(%10 : $Builtin.Int64, %9 : $Builtin.Int64, %4 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%12 = tuple_extract %11 : $(Builtin.Int64, Builtin.Int1), 0
return %12 : $Builtin.Int64
}
// CHECK-LABEL: sil @load_store_forwarding_over_dealloc_stack
// CHECK: bb0
// CHECK-NEXT: alloc_stack $Builtin.Int64
// CHECK-NEXT: alloc_stack $Builtin.Int64
// CHECK-NEXT: store
// CHECK-NEXT: alloc_stack $Builtin.Int64
// CHECK-NEXT: = load
// CHECK: dealloc_stack
// CHECK-NOT: = load
// CHECK: return
sil @load_store_forwarding_over_dealloc_stack : $@convention(thin) (Builtin.Int64) -> (Builtin.Int64) {
bb0(%0 : $Builtin.Int64):
%1 = alloc_stack $Builtin.Int64
%2 = alloc_stack $Builtin.Int64
store %0 to %1 : $*Builtin.Int64
%3 = alloc_stack $Builtin.Int64
%5 = load %2 : $*Builtin.Int64
%22 = function_ref @use_64 : $@convention(thin) (Builtin.Int64) -> ()
%23 = apply %22(%5) : $@convention(thin) (Builtin.Int64) -> ()
dealloc_stack %3 : $*Builtin.Int64
%4 = load %1 : $*Builtin.Int64
store %0 to %1 : $*Builtin.Int64
%6 = load %2 : $*Builtin.Int64
%222 = function_ref @use_2_64 : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> ()
%232 = apply %222(%4, %6) : $@convention(thin) (Builtin.Int64, Builtin.Int64) -> ()
dealloc_stack %2 : $*Builtin.Int64
dealloc_stack %1 : $*Builtin.Int64
return %4 : $Builtin.Int64
}
// CHECK-LABEL: sil @load_dedup_forwarding_from_aggregate_to_field
// CHECK: bb0(%0 : $*Agg1):
// CHECK: [[L:%.*]] = load %0
// CHECK: [[S1:%.*]] = struct_extract [[L]]
// CHECK: [[S2:%.*]] = struct_extract [[S1]]
// CHECK: [[T:%.*]] = tuple_extract [[S2]]
// CHECK-NOT: load
// CHECK: return [[T]]
// CHECK: } // end sil function 'load_dedup_forwarding_from_aggregate_to_field'
sil @load_dedup_forwarding_from_aggregate_to_field : $@convention(thin) (@inout Agg1) -> (Builtin.Int32) {
bb0(%0 : $*Agg1):
%1 = load %0 : $*Agg1
%2 = struct_element_addr %0 : $*Agg1, #Agg1.a
%3 = struct_element_addr %2 : $*Agg2, #Agg2.t
%4 = tuple_element_addr %3 : $*(Builtin.Int64, Builtin.Int32), 1
%5 = load %4 : $*Builtin.Int32
return %5 : $Builtin.Int32
}
// CHECK-LABEL: promote_partial_load
// CHECK: alloc_stack
// CHECK-NOT: = load
// CHECK: [[RESULT:%[0-9]+]] = struct_extract
// CHECK: return [[RESULT]]
sil @promote_partial_load : $@convention(thin) (Builtin.Int32) -> Builtin.Int32 {
bb0(%0 : $Builtin.Int32):
%1 = alloc_stack $Wrapper
%2 = struct $Wrapper (%0 : $Builtin.Int32)
store %2 to %1 : $*Wrapper
%3 = struct_element_addr %1 : $*Wrapper, #Wrapper.value
%4 = load %3 : $*Builtin.Int32
dealloc_stack %1 : $*Wrapper
return %4 : $Builtin.Int32
}
// TODO: HANDLE THIS, THIS IS SAME VALUE STORES.
//
// CHECK-LABEL: sil @store_loaded_value
sil @store_loaded_value : $@convention(thin) (@inout Agg2, @inout Agg1) -> () {
bb0(%0 : $*Agg2, %1 : $*Agg1):
%2 = load %1 : $*Agg1
%3 = load %0 : $*Agg2
store %2 to %1 : $*Agg1
store %3 to %0 : $*Agg2
%6 = tuple()
return %6 : $()
}
// Check load forwarding across strong_release in case the stored memory does
// not escape.
// CHECK-LABEL: sil @test_store_forwarding_strong_release
// CHECK: strong_release
// CHECK-NOT: [[BOX0:%.*]] = load
// CHECK: apply
sil @test_store_forwarding_strong_release : $@convention(thin) (B, X) -> () {
bb0(%0 : $B, %1 : $X):
%2 = alloc_stack $A // users: %3, %13
%3 = struct_element_addr %2 : $*A, #A.i // users: %5, %10
%4 = integer_literal $Builtin.Int32, 32 // user: %5
store %4 to %3 : $*Builtin.Int32 // id: %5
%6 = ref_to_unowned %0 : $B to $@sil_unowned B // user: %7
unowned_release %6 : $@sil_unowned B // id: %7
strong_release %0 : $B // id: %8
release_value %1 : $X // id: %9
%10 = load %3 : $*Builtin.Int32 // user: %12
// function_ref use
%11 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %12
%12 = apply %11(%10) : $@convention(thin) (Builtin.Int32) -> ()
dealloc_stack %2 : $*A // id: %13
%14 = tuple () // user: %15
return %14 : $()
}
// Check load forwarding across strong_release in case the loaded memory does
// not escape.
// CHECK-LABEL: sil @test_load_forwarding_strong_release
// CHECK: strong_release
// CHECK-NOT: [[BOX0:%.*]] = load
// CHECK: apply
sil @test_load_forwarding_strong_release : $@convention(thin) (B, X) -> () {
bb0(%0 : $B, %1 : $X):
%2 = alloc_stack $A // users: %3, %12
%3 = struct_element_addr %2 : $*A, #A.i // users: %4, %9
%4 = load %3 : $*Builtin.Int32
%5 = ref_to_unowned %0 : $B to $@sil_unowned B // user: %6
unowned_release %5 : $@sil_unowned B // id: %6
strong_release %0 : $B // id: %7
release_value %1 : $X // id: %8
%9 = load %3 : $*Builtin.Int32 // user: %11
// function_ref use
%10 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %11
%11 = apply %10(%9) : $@convention(thin) (Builtin.Int32) -> ()
dealloc_stack %2 : $*A // id: %12
%13 = tuple () // user: %14
return %13 : $() // id: %14
}
// Make sure we RLE the second load.
//
// CHECK-LABEL: test_simple_rle_in_class
// CHECK: = load
// CHECK-NOT: = load
// CHECK: cond_fail
sil hidden @test_simple_rle_in_class : $@convention(thin) (@owned AB) -> Int {
bb0(%0 : $AB):
%2 = ref_element_addr %0 : $AB, #AB.value // user: %3
%3 = load %2 : $*Int // user: %6
%4 = ref_element_addr %0 : $AB, #AB.value // user: %5
%5 = load %4 : $*Int // user: %7
%6 = struct_extract %3 : $Int, #Int.value // user: %9
%7 = struct_extract %5 : $Int, #Int.value // user: %9
%8 = integer_literal $Builtin.Int1, -1 // user: %9
%9 = builtin "sadd_with_overflow_Int64"(%6 : $Builtin.Int64, %7 : $Builtin.Int64, %8 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %10, %11
%10 = tuple_extract %9 : $(Builtin.Int64, Builtin.Int1), 0 // user: %13
%11 = tuple_extract %9 : $(Builtin.Int64, Builtin.Int1), 1 // user: %12
cond_fail %11 : $Builtin.Int1 // id: %12
%13 = struct $Int (%10 : $Builtin.Int64) // user: %15
strong_release %0 : $AB // id: %14
return %13 : $Int // id: %15
}
// Make sure we RLE the load in BB2.
//
// CHECK-LABEL: test_silargument_rle
// CHECK: bb2
// CHECK-NOT: = load
// CHECK: cond_br
sil @test_silargument_rle : $@convention(thin) () -> () {
bb0:
%0 = global_addr @total : $*Int32
%1 = integer_literal $Builtin.Int32, 0
%2 = struct $Int32 (%1 : $Builtin.Int32)
store %2 to %0 : $*Int32
%6 = alloc_ref $AX
%8 = ref_element_addr %6 : $AX, #AX.current
store %2 to %8 : $*Int32
// %10 = load %8 : $*Int32
cond_br undef, bb3, bb2
bb2:
%24 = integer_literal $Builtin.Int1, -1
%31 = struct_element_addr %0 : $*Int32, #Int32.value
%32 = load %31 : $*Builtin.Int32
%33 = builtin "sadd_with_overflow_Int32"(%32 : $Builtin.Int32, %1 : $Builtin.Int32, %24 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%34 = tuple_extract %33 : $(Builtin.Int32, Builtin.Int1), 0
%37 = struct $Int32 (%34 : $Builtin.Int32)
store %37 to %0 : $*Int32
cond_br undef, bb3, bb2
bb3:
strong_release %6 : $AX
%44 = tuple ()
return %44 : $()
}
// CHECK-LABEL: sil @load_to_load_forwarding_diamonds : $@convention(thin) (@inout Builtin.Int32) -> Builtin.Int32 {
// CHECK: = load
// CHECK-NOT: = load
// CHECK: return
sil @load_to_load_forwarding_diamonds : $@convention(thin) (@inout Builtin.Int32) -> Builtin.Int32 {
bb0(%0 : $*Builtin.Int32):
%1 = load %0 : $*Builtin.Int32
// Simple diamond.
cond_br undef, bb1, bb2
bb1:
br bb3
bb2:
br bb3
bb3:
// Triangle
cond_br undef, bb4, bb5
bb4:
br bb5
bb5:
%2 = load %0 : $*Builtin.Int32
return %2 : $Builtin.Int32
}
// CHECK-LABEL: sil @load_to_load_conflicting_branches_diamond : $@convention(thin) (@inout Builtin.Int32) -> () {
// CHECK: bb0(
// CHECK: = load
// CHECK: bb1:
// CHECK-NOT: = load
// CHECK: store
// CHECK-NOT: = load
// CHECK: bb2:
// CHECK: bb3([[A:%[0-9]+]] : $Builtin.Int32):
// CHECK-NOT: = load
// CHECK: apply %{{[0-9]+}}([[A]])
// CHECK-LABEL: } // end sil function 'load_to_load_conflicting_branches_diamond'
sil @load_to_load_conflicting_branches_diamond : $@convention(thin) (@inout Builtin.Int32) -> () {
// %0 // users: %1, %4, %9, %11, %16, %21
bb0(%0 : $*Builtin.Int32):
%1 = load %0 : $*Builtin.Int32 // user: %2
%2 = builtin "trunc_Int32_Int1"(%1 : $Builtin.Int32) : $Builtin.Int1
cond_br undef, bb1, bb2 // id: %3
bb1: // Preds: bb0
%4 = load %0 : $*Builtin.Int32 // users: %6, %8, %10
// function_ref use
%5 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %6
%6 = apply %5(%4) : $@convention(thin) (Builtin.Int32) -> ()
%7 = integer_literal $Builtin.Int32, 2 // user: %9
%8 = builtin "trunc_Int32_Int1"(%4 : $Builtin.Int32) : $Builtin.Int1
store %7 to %0 : $*Builtin.Int32 // id: %9
%10 = builtin "trunc_Int32_Int1"(%4 : $Builtin.Int32) : $Builtin.Int1
%11 = load %0 : $*Builtin.Int32 // users: %13, %14
// function_ref use
%12 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %13
%13 = apply %12(%11) : $@convention(thin) (Builtin.Int32) -> ()
%14 = builtin "trunc_Int32_Int1"(%11 : $Builtin.Int32) : $Builtin.Int1
br bb3 // id: %15
bb2: // Preds: bb0
%16 = load %0 : $*Builtin.Int32 // users: %18, %19
// function_ref use
%17 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %18
%18 = apply %17(%16) : $@convention(thin) (Builtin.Int32) -> ()
%19 = builtin "trunc_Int32_Int1"(%16 : $Builtin.Int32) : $Builtin.Int1
br bb3 // id: %20
bb3: // Preds: bb1 bb2
%21 = load %0 : $*Builtin.Int32 // user: %23
// function_ref use
%22 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%23 = apply %22(%21) : $@convention(thin) (Builtin.Int32) -> ()
%24 = tuple () // user: %25
return %24 : $() // id: %25
}
// Forward store %1 and store %2 such that load %3 becomes an identity trivial cast.
// Both loads from %0 will be eliminated.
// CHECK-LABEL: sil @test_read_dependence_allows_forwarding_multi_bb_2 : $@convention(thin) (@inout A, A, A) -> A {
// CHECK: bb1
// CHECK: = load
// CHECK-NOT: = load
// CHECK: bb2
sil @test_read_dependence_allows_forwarding_multi_bb_2 : $@convention(thin) (@inout A, A, A) -> A {
bb0(%0 : $*A, %1 : $A, %2 : $A):
store %1 to %0 : $*A
%3 = unchecked_addr_cast %0 : $*A to $*A
%4 = unchecked_addr_cast %3 : $*A to $*A
br bb1
bb1:
// This means that the first store is not dead.
%6 = load %3 : $*A
%7 = load %0 : $*A
%8 = load %0 : $*A
%22 = function_ref @use_a : $@convention(thin) (A) -> ()
%123 = apply %22(%6) : $@convention(thin) (A) -> ()
%223 = apply %22(%7) : $@convention(thin) (A) -> ()
%323 = apply %22(%8) : $@convention(thin) (A) -> ()
store %2 to %0 : $*A
cond_br undef, bb1, bb2
bb2:
return %7 : $A
}
// CHECK-LABEL: sil @load_to_load_loop
sil @load_to_load_loop : $@convention(thin) () -> () {
bb0:
%101 = alloc_stack $Int32
%102 = alloc_stack $Int32
%0 = struct_element_addr %101 : $*Int32, #Int32.value
%1 = struct_element_addr %102 : $*Int32, #Int32.value
%2 = load %0 : $*Builtin.Int32
%99 = load %1 : $*Builtin.Int32
%125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%126 = apply %125(%2) : $@convention(thin) (Builtin.Int32) -> ()
%127 = apply %125(%99) : $@convention(thin) (Builtin.Int32) -> ()
br bb1
bb1:
%4 = load %0 : $*Builtin.Int32
%5 = integer_literal $Builtin.Int32, 2
%1125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%1126 = apply %1125(%4) : $@convention(thin) (Builtin.Int32) -> ()
store %5 to %0 : $*Builtin.Int32
builtin "trunc_Int32_Int1"(%4 : $Builtin.Int32) : $Builtin.Int1
%6 = load %0 : $*Builtin.Int32
%11125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%11126 = apply %11125(%6) : $@convention(thin) (Builtin.Int32) -> ()
cond_br undef, bb1, bb2
bb2:
%7 = load %0 : $*Builtin.Int32
%111125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%111126 = apply %111125(%7) : $@convention(thin) (Builtin.Int32) -> ()
dealloc_stack %102 : $*Int32
dealloc_stack %101 : $*Int32
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: store_and_load_to_load_branches_diamond
// CHECK: bb3
// CHECK-NOT: = load
// CHECK: return
sil @store_and_load_to_load_branches_diamond : $@convention(thin) (@inout Builtin.Int32) -> () {
// %0 // users: %1, %4, %9, %11, %16, %21
bb0(%0 : $*Builtin.Int32):
cond_br undef, bb1, bb2 // id: %3
bb1: // Preds: bb0
%1 = load %0 : $*Builtin.Int32 // user: %2
br bb3 // id: %15
bb2: // Preds: bb0
%5 = integer_literal $Builtin.Int32, 2
store %5 to %0 : $*Builtin.Int32
br bb3 // id: %20
bb3: // Preds: bb1 bb2
%21 = load %0 : $*Builtin.Int32 // user: %23
// function_ref use
%22 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%23 = apply %22(%21) : $@convention(thin) (Builtin.Int32) -> ()
%24 = tuple () // user: %25
return %24 : $() // id: %25
}
// CHECK-LABEL: agg_and_field_store_branches_diamond
// CHECK: bb3
// CHECK-NOT: = load
// CHECK: return
sil hidden @agg_and_field_store_branches_diamond : $@convention(thin) (Bool) -> () {
bb0(%0 : $Bool):
%1 = alloc_stack $TwoField, var, name "x" // users: %6, %11, %16, %21, %24
%7 = struct_extract %0 : $Bool, #Bool.value // user: %8
cond_br %7, bb1, bb2 // id: %8
bb1: // Preds: bb0
%9 = integer_literal $Builtin.Int64, 10 // user: %10
%10 = struct $Int (%9 : $Builtin.Int64) // user: %12
%11 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %12
store %10 to %11 : $*Int // id: %12
%14 = integer_literal $Builtin.Int64, 20 // user: %15
%15 = struct $Int (%14 : $Builtin.Int64) // user: %17
%16 = struct_element_addr %1 : $*TwoField, #TwoField.b // user: %17
store %15 to %16 : $*Int // id: %17
br bb3 // id: %13
bb2: // Preds: bb0
%3 = function_ref @init_twofield : $@convention(thin) (@thin TwoField.Type) -> TwoField // user: %5
%4 = metatype $@thin TwoField.Type // user: %5
%5 = apply %3(%4) : $@convention(thin) (@thin TwoField.Type) -> TwoField // user: %6
store %5 to %1 : $*TwoField // id: %6
br bb3 // id: %18
bb3: // Preds: bb1 bb2
%99 = load %1 : $*TwoField // id: %6
%991 = function_ref @use_twofield : $@convention(thin) (TwoField) -> () // user: %5
%55 = apply %991(%99) : $@convention(thin) (TwoField) -> () // user: %6
%23 = tuple () // user: %25
dealloc_stack %1 : $*TwoField // id: %24
return %23 : $() // id: %25
}
// CHECK-LABEL: agg_and_field_store_with_the_same_value
// CHECK: bb2
// CHECK-NOT: = load
// CHECK: return
sil hidden @agg_and_field_store_with_the_same_value : $@convention(thin) (Bool) -> () {
bb0(%0 : $Bool):
%1 = alloc_stack $TwoField, var, name "x" // users: %6, %11, %16, %21, %24
%7 = struct_extract %0 : $Bool, #Bool.value // user: %8
br bb1
bb1: // Preds: bb0
%9 = integer_literal $Builtin.Int64, 10 // user: %10
%10 = struct $Int (%9 : $Builtin.Int64) // user: %12
%11 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %12
store %10 to %11 : $*Int // id: %12
%16 = struct_element_addr %1 : $*TwoField, #TwoField.b // user: %17
store %10 to %16 : $*Int // id: %17
br bb2 // id: %13
bb2: // Preds: bb1 bb2
%99 = load %1 : $*TwoField // id: %6
%991 = function_ref @use_twofield : $@convention(thin) (TwoField) -> () // user: %5
%55 = apply %991(%99) : $@convention(thin) (TwoField) -> () // user: %6
%23 = tuple () // user: %25
dealloc_stack %1 : $*TwoField // id: %24
return %23 : $() // id: %25
}
// Make sure we form a single SILArgument.
//
// CHECK-LABEL: silargument_agg_in_one_block
// CHECK: bb3([[A1:%.*]] : $Int, [[A2:%.*]] : $Int):
// CHECK-NOT: = load
// CHECK: return
sil hidden @silargument_agg_in_one_block : $@convention(thin) (Bool) -> () {
bb0(%0 : $Bool):
%1 = alloc_stack $TwoField, var, name "x" // users: %5, %7, %13, %15, %19
cond_br undef, bb1, bb2 // id: %2
bb1: // Preds: bb0
%3 = integer_literal $Builtin.Int64, 10 // user: %4
%4 = struct $Int (%3 : $Builtin.Int64) // users: %6, %8
%5 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %6
store %4 to %5 : $*Int // id: %6
%7 = struct_element_addr %1 : $*TwoField, #TwoField.b // user: %8
store %4 to %7 : $*Int // id: %8
br bb3 // id: %9
bb2: // Preds: bb0
%10 = integer_literal $Builtin.Int64, 10 // user: %11
%11 = struct $Int (%10 : $Builtin.Int64) // users: %12, %12
%12 = struct $TwoField (%11 : $Int, %11 : $Int) // user: %13
store %12 to %1 : $*TwoField // id: %13
br bb3 // id: %14
bb3: // Preds: bb1 bb2
%15 = load %1 : $*TwoField // user: %17
// function_ref use_twofield
%16 = function_ref @use_twofield : $@convention(thin) (TwoField) -> () // user: %17
%17 = apply %16(%15) : $@convention(thin) (TwoField) -> ()
%18 = tuple () // user: %20
dealloc_stack %1 : $*TwoField // id: %19
return %18 : $() // id: %20
}
// CHECK-LABEL: large_diamond_silargument_forwarding
// CHECK: bb9
// CHECK-NOT: = load
// CHECK: return
sil hidden @large_diamond_silargument_forwarding : $@convention(thin) (Bool) -> Int {
bb0(%0 : $Bool):
%1 = alloc_stack $TwoField, var, name "x" // users: %7, %10, %13, %16, %21, %23
%2 = integer_literal $Builtin.Int64, 10 // user: %3
%3 = struct $Int (%2 : $Builtin.Int64) // users: %8, %11, %14, %17
cond_br undef, bb1, bb2 // id: %4
bb1: // Preds: bb0
cond_br undef, bb3, bb4 // id: %5
bb2: // Preds: bb0
cond_br undef, bb5, bb6 // id: %6
bb3: // Preds: bb1
%7 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %8
store %3 to %7 : $*Int // id: %8
br bb7 // id: %9
bb4: // Preds: bb1
%10 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %11
store %3 to %10 : $*Int // id: %11
br bb7 // id: %12
bb5: // Preds: bb2
%13 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %14
store %3 to %13 : $*Int // id: %14
br bb8 // id: %15
bb6: // Preds: bb2
%16 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %17
store %3 to %16 : $*Int // id: %17
br bb8 // id: %18
bb7: // Preds: bb3 bb4
br bb9 // id: %19
bb8: // Preds: bb5 bb6
br bb9 // id: %20
bb9: // Preds: bb7 bb8
%21 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %22
%22 = load %21 : $*Int // user: %24
dealloc_stack %1 : $*TwoField // id: %23
return %22 : $Int // id: %24
}
// CHECK-LABEL: reuse_silargument_multiple_bb_forwarding
// CHECK: bb7:
// CHECK: br bb10(%3 : $Int)
// CHECK: bb8:
// CHECK: cond_br undef, bb9, bb10(%3 : $Int)
// CHECK-NOT: = load
// CHECK: bb10([[A:%[0-9]+]] : $Int):
// CHECK-NOT: = load
// CHECK: return [[A]]
// CHECK: } // end sil function 'reuse_silargument_multiple_bb_forwarding'
sil hidden @reuse_silargument_multiple_bb_forwarding : $@convention(thin) (Bool) -> Int {
bb0(%0 : $Bool):
%1 = alloc_stack $TwoField, var, name "x" // users: %7, %10, %13, %16, %21, %26, %28
%2 = integer_literal $Builtin.Int64, 10 // user: %3
%3 = struct $Int (%2 : $Builtin.Int64) // users: %8, %11, %14, %17
cond_br undef, bb1, bb2 // id: %4
bb1: // Preds: bb0
cond_br undef, bb3, bb4 // id: %5
bb2: // Preds: bb0
cond_br undef, bb5, bb6 // id: %6
bb3: // Preds: bb1
%7 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %8
store %3 to %7 : $*Int // id: %8
br bb7 // id: %9
bb4: // Preds: bb1
%10 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %11
store %3 to %10 : $*Int // id: %11
br bb7 // id: %12
bb5: // Preds: bb2
%13 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %14
store %3 to %13 : $*Int // id: %14
br bb8 // id: %15
bb6: // Preds: bb2
%16 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %17
store %3 to %16 : $*Int // id: %17
br bb8 // id: %18
bb7: // Preds: bb3 bb4
br bb10 // id: %19
bb8: // Preds: bb5 bb6
cond_br undef, bb9, bb10 // id: %20
bb9: // Preds: bb8
%21 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %22
%22 = load %21 : $*Int // user: %24
%23 = function_ref @use_Int : $@convention(thin) (Int) -> () // user: %24
%24 = apply %23(%22) : $@convention(thin) (Int) -> ()
br bb10 // id: %25
bb10: // Preds: bb7 bb8 bb9
%26 = struct_element_addr %1 : $*TwoField, #TwoField.a // user: %27
%27 = load %26 : $*Int // user: %29
dealloc_stack %1 : $*TwoField // id: %28
return %27 : $Int // id: %29
}
// CHECK-LABEL: sil @test_project_box
// CHECK: [[PB:%[0-9]*]] = project_box %0
// CHECK: [[LD:%[0-9]*]] = load [[PB]]
// CHECK: [[TP:%[0-9]*]] = tuple ([[LD]] : $Builtin.Int32, [[LD]] : $Builtin.Int32)
// CHECK: return [[TP]]
sil @test_project_box : $@convention(thin) (<τ_0_0> { var τ_0_0 } <Builtin.Int32>) -> (Builtin.Int32, Builtin.Int32) {
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Builtin.Int32>):
%2 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Builtin.Int32>, 0
%3 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Builtin.Int32>, 0
%4 = load %2 : $*Builtin.Int32
%5 = load %3 : $*Builtin.Int32
%r = tuple(%4 : $Builtin.Int32, %5 : $Builtin.Int32)
return %r : $(Builtin.Int32, Builtin.Int32)
}
// Make sure we can forward loads to class members from the same class through
// upcast.
//
// CHECK-LABEL: sil @load_forward_same_upcasted_base
// CHECK: bb0
// CHECK: = load
// CHECK-NOT: = load
// CHECK: return
sil @load_forward_same_upcasted_base : $@convention(thin)(C3) -> () {
bb0(%0 : $C3):
%1 = upcast %0 : $C3 to $C2
%2 = ref_element_addr %1 : $C2, #C2.current
%3 = load %2 : $*Int
%4 = upcast %0 : $C3 to $C2
%5 = ref_element_addr %4 : $C2, #C2.current
%6 = load %5 : $*Int
%7 = tuple ()
return %7 : $()
}
// Make sure we can forward loads to class members from the same class through
// downcast.
//
// CHECK-LABEL: sil @load_forward_same_downcasted_base
// CHECK: bb0
// CHECK: = load
// CHECK-NOT: = load
// CHECK: return
sil @load_forward_same_downcasted_base : $@convention(thin)(C1) -> () {
bb0(%0 : $C1):
%1 = unchecked_ref_cast %0 : $C1 to $C2
%2 = ref_element_addr %1 : $C2, #C2.current
%3 = load %2 : $*Int
%4 = unchecked_ref_cast %0 : $C1 to $C2
%5 = ref_element_addr %4 : $C2, #C2.current
%6 = load %5 : $*Int
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: sil @load_forwarding_self_cycle
// CHECK: bb0
// CHECK-NOT: = load
// CHECK: return
sil @load_forwarding_self_cycle : $@convention(thin) () -> () {
bb0:
%0 = global_addr @total : $*Int32
%1 = integer_literal $Builtin.Int32, 0
%2 = struct $Int32 (%1 : $Builtin.Int32)
store %2 to %0 : $*Int32
%4 = integer_literal $Builtin.Int32, 10
%5 = struct $Int32 (%4 : $Builtin.Int32)
%6 = alloc_ref $NewHalfOpenRangeGenerator
%7 = upcast %6 : $NewHalfOpenRangeGenerator to $NewRangeGenerator1
%8 = ref_element_addr %7 : $NewRangeGenerator1, #NewRangeGenerator1.current
store %2 to %8 : $*Int32
%10 = ref_element_addr %7 : $NewRangeGenerator1, #NewRangeGenerator1.end
store %5 to %10 : $*Int32
%12 = struct_element_addr %8 : $*Int32, #Int32.value
%13 = struct_element_addr %10 : $*Int32, #Int32.value
%15 = load %12 : $*Builtin.Int32
%16 = load %13 : $*Builtin.Int32
%17 = builtin "cmp_eq_Int32"(%15 : $Builtin.Int32, %16 : $Builtin.Int32) : $Builtin.Int1
cond_br %17, bb3, bb1
bb2(%20 : $Builtin.Int32):
%21 = load %12 : $*Builtin.Int32
%22 = integer_literal $Builtin.Int32, 1
%24 = integer_literal $Builtin.Int1, -1
%25 = builtin "sadd_with_overflow_Int32"(%20 : $Builtin.Int32, %22 : $Builtin.Int32, %24 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%26 = tuple_extract %25 : $(Builtin.Int32, Builtin.Int1), 0
%27 = tuple_extract %25 : $(Builtin.Int32, Builtin.Int1), 1
cond_fail %27 : $Builtin.Int1
%29 = struct $Int32 (%26 : $Builtin.Int32)
store %29 to %8 : $*Int32
%31 = struct_element_addr %0 : $*Int32, #Int32.value
%32 = load %31 : $*Builtin.Int32
%33 = builtin "sadd_with_overflow_Int32"(%32 : $Builtin.Int32, %21 : $Builtin.Int32, %24 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%34 = tuple_extract %33 : $(Builtin.Int32, Builtin.Int1), 0
%35 = tuple_extract %33 : $(Builtin.Int32, Builtin.Int1), 1
cond_fail %35 : $Builtin.Int1
%37 = struct $Int32 (%34 : $Builtin.Int32)
store %37 to %0 : $*Int32
%39 = load %12 : $*Builtin.Int32
%40 = load %13 : $*Builtin.Int32
%41 = builtin "cmp_eq_Int32"(%39 : $Builtin.Int32, %40 : $Builtin.Int32) : $Builtin.Int1
cond_br %41, bb3, bb2(%39 : $Builtin.Int32)
// bb1 is after bb2 to make sure the first predecessor of bb2 is not bb2 to
// expose the bug.
bb1:
br bb2(%15 : $Builtin.Int32)
bb3:
strong_release %7 : $NewRangeGenerator1
%44 = tuple ()
return %44 : $()
}
// Make sure the first load in bb1 is not eliminated as we have
// this unreachable block which will have a liveout of nil.
// we make this in the context of a loop, because we want to run an
// optimistic data flow.
//
// CHECK-LABEL: sil @load_to_load_loop_with_unreachable_block
// CHECK: bb1:
// CHECK: = load
// CHECK: cond_br
sil @load_to_load_loop_with_unreachable_block : $@convention(thin) () -> () {
bb0:
%101 = alloc_stack $Int32
%102 = alloc_stack $Int32
%0 = struct_element_addr %101 : $*Int32, #Int32.value
%1 = struct_element_addr %102 : $*Int32, #Int32.value
%2 = load %0 : $*Builtin.Int32
%99 = load %1 : $*Builtin.Int32
%125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%126 = apply %125(%2) : $@convention(thin) (Builtin.Int32) -> ()
%127 = apply %125(%99) : $@convention(thin) (Builtin.Int32) -> ()
br bb1
bb20:
%44 = load %0 : $*Builtin.Int32
br bb1
bb1:
%4 = load %0 : $*Builtin.Int32
%5 = integer_literal $Builtin.Int32, 2
%1125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%1126 = apply %1125(%4) : $@convention(thin) (Builtin.Int32) -> ()
store %5 to %0 : $*Builtin.Int32
builtin "trunc_Int32_Int1"(%4 : $Builtin.Int32) : $Builtin.Int1
%6 = load %0 : $*Builtin.Int32
%11125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%11126 = apply %11125(%6) : $@convention(thin) (Builtin.Int32) -> ()
cond_br undef, bb1, bb2
bb2:
%7 = load %0 : $*Builtin.Int32
%111125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23
%111126 = apply %111125(%7) : $@convention(thin) (Builtin.Int32) -> ()
dealloc_stack %102 : $*Int32
dealloc_stack %101 : $*Int32
%9999 = tuple()
return %9999 : $()
}
// CHECK-LABEL: sil hidden @redundant_load_over_intermediate_release_without_epilogue_release : $@convention(thin) (@owned AB) -> () {
// CHECK: [[AD:%.*]] = ref_element_addr
// CHECK: [[AD2:%.*]] = load [[AD]]
// CHECK: [[AD3:%.*]] = load [[AD]]
// CHECK: return
sil hidden @redundant_load_over_intermediate_release_without_epilogue_release : $@convention(thin) (@owned AB) -> () {
bb0(%0 : $AB):
%1 = ref_element_addr %0 : $AB, #AB.value
%2 = load %1 : $*Int
release_value %0 : $AB
%3 = load %1 : $*Int
retain_value %0 : $AB
%4 = tuple ()
return %4 : $()
}
// Make sure we have a deterministic forward ordering and also both loads are forwarded.
//
// CHECK-LABEL: sil @load_store_deterministic_forwarding
// CHECK: bb0
// CHECK-NEXT: [[VAL:%.*]] = integer_literal $Builtin.Int64, 0
// CHECK-NEXT: store
// CHECK-NEXT: store
// CHECK-NEXT: return [[VAL]] : $Builtin.Int64
sil @load_store_deterministic_forwarding : $@convention(thin) (@inout Builtin.Int64, @inout Builtin.Int64) -> (Builtin.Int64) {
bb0(%0 : $*Builtin.Int64, %1 : $*Builtin.Int64):
%2 = integer_literal $Builtin.Int64, 0
store %2 to %0 : $*Builtin.Int64
%3 = load %0 : $*Builtin.Int64
store %3 to %1: $*Builtin.Int64
%4 = load %1 : $*Builtin.Int64
return %4 : $Builtin.Int64
}
sil @redundant_load_mark_dependence : $@convention(thin) (@inout Builtin.Int64, @guaranteed Builtin.NativeObject) -> (Builtin.Int64, Builtin.Int64) {
bb0(%0 : $*Builtin.Int64, %1 : $Builtin.NativeObject):
%2 = mark_dependence %0 : $*Builtin.Int64 on %1 : $Builtin.NativeObject
%4 = load %2 : $*Builtin.Int64
%5 = load %2 : $*Builtin.Int64
%6 = tuple(%4 : $Builtin.Int64, %5 : $Builtin.Int64)
return %6 : $(Builtin.Int64, Builtin.Int64)
}
sil @dont_crash_on_index_addr_projection : $@convention(thin) (Builtin.RawPointer) -> (Int, Int, Int, Int) {
bb0(%0 : $Builtin.RawPointer):
// Negative (valid constant index)
%3 = integer_literal $Builtin.Word, 4294967295 // '0xffffffff'
%4 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Int
%5 = index_addr %4 : $*Int, %3 : $Builtin.Word
%6 = load %5 : $*Int
// TailIndex (invalid constant index)
%7 = integer_literal $Builtin.Word, 2147483647 // '0x7fffffff'
%8 = index_addr %4 : $*Int, %7 : $Builtin.Word
%9 = load %8 : $*Int
// UnknownOffset (valid index)
%10 = integer_literal $Builtin.Word, 3221225472 // '0xC0000000'
%11 = index_addr %4 : $*Int, %10 : $Builtin.Word
%12 = load %11 : $*Int
// Root (unused/invalid index))
%13 = integer_literal $Builtin.Word, 2147483648 // '0x80000000'
%14 = index_addr %4 : $*Int, %13 : $Builtin.Word
%15 = load %14 : $*Int
%99 = tuple (%6 : $Int, %9 : $Int, %12 : $Int, %15 : $Int)
return %99 : $(Int, Int, Int, Int)
}
sil @overwrite_int : $@convention(thin) (@inout Int, Int) -> ()
// Make sure that the store is forwarded to the load, ie. the load is
// eliminated. That's correct as the stored value can't be changed by the
// callee as it's passed with @in_guaranteed.
sil @test_rle_in_guaranteed_sink : $@convention(thin) (Int) -> ()
sil @test_rle_in_guaranteed_callee : $@convention(thin) (@in_guaranteed Int) -> ()
// CHECK-LABEL: sil @test_rle_in_guaranteed_entry
sil @test_rle_in_guaranteed_entry : $@convention(thin) (@in Int) -> () {
bb0(%0 : $*Int):
%value_raw = integer_literal $Builtin.Int64, 42
// CHECK: [[VAL:%.*]] = struct $Int
%value = struct $Int (%value_raw : $Builtin.Int64)
store %value to %0 : $*Int
%f_callee = function_ref @test_rle_in_guaranteed_callee : $@convention(thin) (@in_guaranteed Int) -> ()
%r1 = apply %f_callee(%0) : $@convention(thin) (@in_guaranteed Int) -> ()
// CHECK-NOT: load
%value_again = load %0 : $*Int
%f_sink = function_ref @test_rle_in_guaranteed_sink : $@convention(thin) (Int) -> ()
// CHECK: ([[VAL]])
%r2 = apply %f_sink(%value_again) : $@convention(thin) (Int) -> ()
%3 = tuple()
return %3 : $()
}
// Check that begin_access, end_access, strong_release, set_deallocating, and dealloc_ref don't prevent optimization.
// CHECK-LABEL: ignore_read_write
// CHECK: bb0
// CHECK-NOT: load
// CHECK-LABEL: end sil function 'ignore_read_write'
sil @ignore_read_write : $@convention(thin) () -> Int32 {
bb0:
%1 = alloc_ref [stack] $AX
%2 = integer_literal $Builtin.Int32, 0
%3 = struct $Int32 (%2 : $Builtin.Int32)
%4 = ref_element_addr %1 : $AX, #AX.current
store %3 to %4 : $*Int32
%5 = load %4 : $*Int32
set_deallocating %1 : $AX
dealloc_ref %1 : $AX
dealloc_stack_ref %1 : $AX
return %5 : $Int32
}
sil @read_emptytuple : $@convention(thin) (@in_guaranteed ()) -> ()
// CHECK-LABEL: sil @emptytuple_promotion :
// CHECK: store
// CHECK-LABEL: } // end sil function 'emptytuple_promotion'
sil @emptytuple_promotion : $@convention(thin) () -> () {
bb0:
%stk2 = alloc_stack $()
store undef to %stk2 : $*()
br bb1
bb1:
%ld = load %stk2 : $*()
dealloc_stack %stk2 : $*()
%r = tuple ()
return %r : $()
}
sil @call_closure : $@convention(method) (@noescape @callee_guaranteed () -> ()) -> ()
sil @closure : $@convention(thin) (@inout_aliasable Int64) -> ()
// CHECK-LABEL: sil @test_closure_capturing_address :
// CHECK: [[L:%[0-9]+]] = load
// CHECK: return [[L]]
// CHECK-LABEL: } // end sil function 'test_closure_capturing_address'
sil @test_closure_capturing_address : $@convention(thin) () -> Int64 {
bb0:
%0 = alloc_stack $Int64
%1 = integer_literal $Builtin.Int64, 0
%2 = struct $Int64 (%1 : $Builtin.Int64)
%3 = function_ref @closure : $@convention(thin) (@inout_aliasable Int64) -> ()
%4 = partial_apply [callee_guaranteed] [on_stack] %3(%0) : $@convention(thin) (@inout_aliasable Int64) -> ()
store %2 to %0 : $*Int64
%6 = function_ref @call_closure : $@convention(method) (@noescape @callee_guaranteed () -> ()) -> ()
%7 = apply %6(%4) : $@convention(method) (@noescape @callee_guaranteed () -> ()) -> ()
dealloc_stack %4 : $@noescape @callee_guaranteed () -> ()
%9 = load %0 : $*Int64
dealloc_stack %0 : $*Int64
return %9 : $Int64
}
sil @yieldTwoAddresses : $@yield_once @convention(thin) () -> (@yields @in_guaranteed String, @yields @in_guaranteed Int64)
// CHECK-LABEL: sil @dont_remove_load_from_different_yield_result :
// CHECK: [[L1:%[0-9]+]] = load
// CHECK: [[L2:%[0-9]+]] = load
// CHECK: tuple ([[L1]] : $String, [[L2]] : $Int64)
// CHECK-LABEL: } // end sil function 'dont_remove_load_from_different_yield_result'
sil @dont_remove_load_from_different_yield_result : $@convention(thin) () -> (String, Int64) {
bb0:
%0 = function_ref @yieldTwoAddresses : $@yield_once @convention(thin) () -> (@yields @in_guaranteed String, @yields @in_guaranteed Int64)
(%1, %2, %3) = begin_apply %0() : $@yield_once @convention(thin) () -> (@yields @in_guaranteed String, @yields @in_guaranteed Int64)
%4 = load %1 : $*String
%5 = load %2 : $*Int64
end_apply %3
%7 = tuple (%4 : $String, %5 : $Int64)
return %7 : $(String, Int64)
}