| // RUN: %target-sil-opt -enable-sil-verify-all %s -module-name Swift -redundant-load-elim -dce | FileCheck %s |
| |
| import Builtin |
| |
| typealias I32 = Builtin.Int32 |
| |
| /////////////////////// |
| // Type Declarations // |
| /////////////////////// |
| |
| 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 |
| 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() |
| } |
| |
| |
| sil @use : $@convention(thin) (Builtin.Int32) -> () |
| 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 |
| |
| // Check that we don't crash if the address is an unchecked_addr_cast. |
| // CHECK-LABEL: sil @test_unchecked_addr_cast |
| // CHECK-NOT: 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-NOT: 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 can not 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.1, %4 : $(Int32, Int32) // user: %6 |
| switch_enum %5 : $XYZ, case #XYZ.A!enumelt: bb2, case #XYZ.B!enumelt.1: bb4, case #XYZ.C!enumelt.1: 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#1 : $*Agg1 |
| %2 = struct_element_addr %1#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#0 : $*@local_storage Agg1 |
| return %5 : $Builtin.Int32 |
| } |
| |
| sil_global @total : $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 $B |
| %2 = store %0 to %1#1 : $*B |
| %3 = load %1#1 : $*B |
| %4 = load %1#1 : $*B |
| %5 = strong_retain %3 : $B |
| %6 = strong_retain %4 : $B |
| %7 = tuple() |
| %8 = 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#1 : $*Builtin.Int64 |
| %3 = alloc_stack $Builtin.Int64 |
| %5 = load %2#1 : $*Builtin.Int64 |
| %22 = function_ref @use_64 : $@convention(thin) (Builtin.Int64) -> () |
| %23 = apply %22(%5) : $@convention(thin) (Builtin.Int64) -> () |
| dealloc_stack %3#0 : $*@local_storage Builtin.Int64 |
| %4 = load %1#1 : $*Builtin.Int64 |
| store %0 to %1#1 : $*Builtin.Int64 |
| %6 = load %2#1 : $*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#0 : $*@local_storage Builtin.Int64 |
| dealloc_stack %1#0 : $*@local_storage Builtin.Int64 |
| return %4 : $Builtin.Int64 |
| } |
| |
| // CHECK-LABEL: sil @load_dedup_forwarding_from_aggregate_to_field |
| // CHECK: bb0([[INPUT_PTR:%[0-9]+]] |
| // CHECK-NEXT: load [[INPUT_PTR]] |
| // CHECK-NEXT: struct_extract |
| // CHECK-NEXT: struct_extract |
| // CHECK-NEXT: tuple_extract |
| // CHECK-NEXT: return |
| 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#1 : $*Wrapper |
| %3 = struct_element_addr %1#1 : $*Wrapper, #Wrapper.value |
| %4 = load %3 : $*Builtin.Int32 |
| dealloc_stack %1#0 : $*@local_storage 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 |
| %4 = store %2 to %1 : $*Agg1 |
| %5 = store %3 to %0 : $*Agg2 |
| %6 = tuple() |
| %7 = return %6 : $() |
| } |
| |
| // *NOTE* This does not handle raw pointer since raw pointer is only layout compatible with heap references. |
| // CHECK-LABEL: sil @store_to_load_forward_unchecked_addr_cast_struct : $@convention(thin) (Optional<A>) -> () { |
| // CHECK: bb0([[INPUT:%[0-9]+]] |
| // CHECK-NOT: load |
| // CHECK: return |
| sil @store_to_load_forward_unchecked_addr_cast_struct : $@convention(thin) (Optional<A>) -> () { |
| bb0(%0 : $Optional<A>): |
| %1 = alloc_stack $Optional<A> |
| store %0 to %1#1 : $*Optional<A> |
| %2 = unchecked_addr_cast %1#1 : $*Optional<A> to $*Builtin.Int32 |
| %3 = load %2 : $*Builtin.Int32 |
| %4 = unchecked_addr_cast %1#1 : $*Optional<A> to $*A |
| %5 = load %4 : $*A |
| %6 = unchecked_addr_cast %1#1 : $*Optional<A> to $*Builtin.RawPointer |
| %7 = load %6 : $*Builtin.RawPointer |
| %8 = unchecked_addr_cast %1#1 : $*Optional<A> to $*Builtin.NativeObject |
| %9 = load %8 : $*Builtin.NativeObject |
| %10 = unchecked_addr_cast %1#1 : $*Optional<A> to $*Optional<Builtin.Int32> |
| %11 = load %10 : $*Optional<Builtin.Int32> |
| %12 = unchecked_addr_cast %1#1 : $*Optional<A> to $*Optional<Builtin.RawPointer> |
| %13 = load %12 : $*Optional<Builtin.RawPointer> |
| %14 = unchecked_addr_cast %1#1 : $*Optional<A> to $*Optional<Builtin.NativeObject> |
| %15 = load %14 : $*Optional<Builtin.NativeObject> |
| dealloc_stack %1#0 : $*@local_storage Optional<A> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // 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#1 : $*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#0 : $*@local_storage 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#1 : $*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#0 : $*@local_storage 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: |
| // CHECK: load |
| 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 |
| } |
| |
| // CHECK-LABEL: load_to_load_irreducible_loop |
| // CHECK: bb0 |
| // CHECK: load |
| // CHECK: bb1 |
| // CHECK-NOT: load |
| // CHECK: store |
| // CHECK: bb2 |
| // CHECK-NOT: load |
| // CHECK: bb3 |
| // CHECK-NOT: load |
| // CHECK: return |
| sil @load_to_load_irreducible_loop : $@convention(thin) () -> () { |
| bb0: |
| %0 = alloc_stack $Int32 |
| %99 = struct_element_addr %0#1 : $*Int32, #Int32.value |
| %1 = load %99 : $*Builtin.Int32 |
| builtin "trunc_Int32_Int1"(%1 : $Builtin.Int32) : $Builtin.Int1 |
| cond_br undef, bb1, bb2 |
| |
| bb1: |
| %3 = load %99 : $*Builtin.Int32 |
| %4 = integer_literal $Builtin.Int32, 2 |
| %22 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23 |
| %23 = apply %22(%3) : $@convention(thin) (Builtin.Int32) -> () |
| store %4 to %99 : $*Builtin.Int32 |
| builtin "trunc_Int32_Int1"(%3 : $Builtin.Int32) : $Builtin.Int1 |
| %5 = load %99 : $*Builtin.Int32 |
| %24 = apply %22(%5) : $@convention(thin) (Builtin.Int32) -> () |
| builtin "trunc_Int32_Int1"(%5 : $Builtin.Int32) : $Builtin.Int1 |
| cond_br undef, bb2, bb3 |
| |
| bb2: |
| %6 = load %99 : $*Builtin.Int32 |
| %25 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23 |
| %26 = apply %25(%6) : $@convention(thin) (Builtin.Int32) -> () |
| builtin "trunc_Int32_Int1"(%6 : $Builtin.Int32) : $Builtin.Int1 |
| cond_br undef, bb1, bb3 |
| |
| bb3: |
| %7 = load %99 : $*Builtin.Int32 |
| %125 = function_ref @use : $@convention(thin) (Builtin.Int32) -> () // user: %23 |
| %126 = apply %125(%7) : $@convention(thin) (Builtin.Int32) -> () |
| builtin "trunc_Int32_Int1"(%7 : $Builtin.Int32) : $Builtin.Int1 |
| dealloc_stack %0#0 : $*@local_storage Int32 |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // 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#1 : $*Int32, #Int32.value |
| %1 = struct_element_addr %102#1 : $*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#0 : $*@local_storage Int32 |
| dealloc_stack %101#0 : $*@local_storage 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#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#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#1 : $*TwoField // id: %6 |
| br bb3 // id: %18 |
| |
| bb3: // Preds: bb1 bb2 |
| %99 = load %1#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#0 : $*@local_storage 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#1 : $*TwoField, #TwoField.a // user: %12 |
| store %10 to %11 : $*Int // id: %12 |
| %16 = struct_element_addr %1#1 : $*TwoField, #TwoField.b // user: %17 |
| store %10 to %16 : $*Int // id: %17 |
| br bb2 // id: %13 |
| |
| bb2: // Preds: bb1 bb2 |
| %99 = load %1#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#0 : $*@local_storage TwoField // id: %24 |
| return %23 : $() // id: %25 |
| } |
| |
| |