1 ; RUN: opt -S -early-cse < %s | FileCheck %s 2 ; RUN: opt -S -basicaa -early-cse-memssa < %s | FileCheck %s 3 4 declare void @clobber_and_use(i32) 5 6 define void @f_0(i32* %ptr) { 7 ; CHECK-LABEL: @f_0( 8 ; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0 9 ; CHECK: call void @clobber_and_use(i32 %val0) 10 ; CHECK: call void @clobber_and_use(i32 %val0) 11 ; CHECK: call void @clobber_and_use(i32 %val0) 12 ; CHECK: ret void 13 14 %val0 = load i32, i32* %ptr, !invariant.load !{} 15 call void @clobber_and_use(i32 %val0) 16 %val1 = load i32, i32* %ptr, !invariant.load !{} 17 call void @clobber_and_use(i32 %val1) 18 %val2 = load i32, i32* %ptr, !invariant.load !{} 19 call void @clobber_and_use(i32 %val2) 20 ret void 21 } 22 23 define void @f_1(i32* %ptr) { 24 ; We can forward invariant loads to non-invariant loads. 25 26 ; CHECK-LABEL: @f_1( 27 ; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0 28 ; CHECK: call void @clobber_and_use(i32 %val0) 29 ; CHECK: call void @clobber_and_use(i32 %val0) 30 31 %val0 = load i32, i32* %ptr, !invariant.load !{} 32 call void @clobber_and_use(i32 %val0) 33 %val1 = load i32, i32* %ptr 34 call void @clobber_and_use(i32 %val1) 35 ret void 36 } 37 38 define void @f_2(i32* %ptr) { 39 ; We can forward a non-invariant load into an invariant load. 40 41 ; CHECK-LABEL: @f_2( 42 ; CHECK: %val0 = load i32, i32* %ptr 43 ; CHECK: call void @clobber_and_use(i32 %val0) 44 ; CHECK: call void @clobber_and_use(i32 %val0) 45 46 %val0 = load i32, i32* %ptr 47 call void @clobber_and_use(i32 %val0) 48 %val1 = load i32, i32* %ptr, !invariant.load !{} 49 call void @clobber_and_use(i32 %val1) 50 ret void 51 } 52 53 define void @f_3(i1 %cond, i32* %ptr) { 54 ; CHECK-LABEL: @f_3( 55 %val0 = load i32, i32* %ptr, !invariant.load !{} 56 call void @clobber_and_use(i32 %val0) 57 br i1 %cond, label %left, label %right 58 59 ; CHECK: %val0 = load i32, i32* %ptr, !invariant.load !0 60 ; CHECK: left: 61 ; CHECK-NEXT: call void @clobber_and_use(i32 %val0) 62 63 left: 64 %val1 = load i32, i32* %ptr 65 call void @clobber_and_use(i32 %val1) 66 ret void 67 68 right: 69 ret void 70 } 71 72 define void @f_4(i1 %cond, i32* %ptr) { 73 ; Negative test -- can't forward %val0 to %va1 because that'll break 74 ; def-dominates-use. 75 76 ; CHECK-LABEL: @f_4( 77 br i1 %cond, label %left, label %merge 78 79 left: 80 ; CHECK: left: 81 ; CHECK-NEXT: %val0 = load i32, i32* %ptr, !invariant.load ! 82 ; CHECK-NEXT: call void @clobber_and_use(i32 %val0) 83 84 %val0 = load i32, i32* %ptr, !invariant.load !{} 85 call void @clobber_and_use(i32 %val0) 86 br label %merge 87 88 merge: 89 ; CHECK: merge: 90 ; CHECK-NEXT: %val1 = load i32, i32* %ptr 91 ; CHECK-NEXT: call void @clobber_and_use(i32 %val1) 92 93 %val1 = load i32, i32* %ptr 94 call void @clobber_and_use(i32 %val1) 95 ret void 96 } 97 98 ; By assumption, the call can't change contents of p 99 ; LangRef is a bit unclear about whether the store is reachable, so 100 ; for the moment we chose to be conservative and just assume it's valid 101 ; to restore the same unchanging value. 102 define void @test_dse1(i32* %p) { 103 ; CHECK-LABEL: @test_dse1 104 ; CHECK-NOT: store 105 %v1 = load i32, i32* %p, !invariant.load !{} 106 call void @clobber_and_use(i32 %v1) 107 store i32 %v1, i32* %p 108 ret void 109 } 110 111 ; By assumption, v1 must equal v2 (TODO) 112 define void @test_false_negative_dse2(i32* %p, i32 %v2) { 113 ; CHECK-LABEL: @test_false_negative_dse2 114 ; CHECK: store 115 %v1 = load i32, i32* %p, !invariant.load !{} 116 call void @clobber_and_use(i32 %v1) 117 store i32 %v2, i32* %p 118 ret void 119 } 120 121 ; If we remove the load, we still start an invariant scope since 122 ; it lets us remove later loads not explicitly marked invariant 123 define void @test_scope_start_without_load(i32* %p) { 124 ; CHECK-LABEL: @test_scope_start_without_load 125 ; CHECK: %v1 = load i32, i32* %p 126 ; CHECK: %add = add i32 %v1, %v1 127 ; CHECK: call void @clobber_and_use(i32 %add) 128 ; CHECK: call void @clobber_and_use(i32 %v1) 129 ; CHECK: ret void 130 %v1 = load i32, i32* %p 131 %v2 = load i32, i32* %p, !invariant.load !{} 132 %add = add i32 %v1, %v2 133 call void @clobber_and_use(i32 %add) 134 %v3 = load i32, i32* %p 135 call void @clobber_and_use(i32 %v3) 136 ret void 137 } 138 139 ; If we already have an invariant scope, don't want to start a new one 140 ; with a potentially greater generation. This hides the earlier invariant 141 ; load 142 define void @test_scope_restart(i32* %p) { 143 ; CHECK-LABEL: @test_scope_restart 144 ; CHECK: %v1 = load i32, i32* %p 145 ; CHECK: call void @clobber_and_use(i32 %v1) 146 ; CHECK: %add = add i32 %v1, %v1 147 ; CHECK: call void @clobber_and_use(i32 %add) 148 ; CHECK: call void @clobber_and_use(i32 %v1) 149 ; CHECK: ret void 150 %v1 = load i32, i32* %p, !invariant.load !{} 151 call void @clobber_and_use(i32 %v1) 152 %v2 = load i32, i32* %p, !invariant.load !{} 153 %add = add i32 %v1, %v2 154 call void @clobber_and_use(i32 %add) 155 %v3 = load i32, i32* %p 156 call void @clobber_and_use(i32 %v3) 157 ret void 158 } 159