1 ; RUN: opt -S -early-cse < %s | FileCheck %s 2 3 declare void @llvm.experimental.guard(i1,...) 4 5 define i32 @test0(i32* %ptr, i1 %cond) { 6 ; We can do store to load forwarding over a guard, since it does not 7 ; clobber memory 8 9 ; CHECK-LABEL: @test0( 10 ; CHECK-NEXT: store i32 40, i32* %ptr 11 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] 12 ; CHECK-NEXT: ret i32 40 13 14 store i32 40, i32* %ptr 15 call void(i1,...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] 16 %rval = load i32, i32* %ptr 17 ret i32 %rval 18 } 19 20 define i32 @test1(i32* %val, i1 %cond) { 21 ; We can CSE loads over a guard, since it does not clobber memory 22 23 ; CHECK-LABEL: @test1( 24 ; CHECK-NEXT: %val0 = load i32, i32* %val 25 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] 26 ; CHECK-NEXT: ret i32 0 27 28 %val0 = load i32, i32* %val 29 call void(i1,...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] 30 %val1 = load i32, i32* %val 31 %rval = sub i32 %val0, %val1 32 ret i32 %rval 33 } 34 35 define i32 @test2() { 36 ; Guards on "true" get removed 37 38 ; CHECK-LABEL: @test2( 39 ; CHECK-NEXT: ret i32 0 40 call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ] 41 ret i32 0 42 } 43 44 define i32 @test3(i32 %val) { 45 ; After a guard has executed the condition it was guarding is known to 46 ; be true. 47 48 ; CHECK-LABEL: @test3( 49 ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 50 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] 51 ; CHECK-NEXT: ret i32 -1 52 53 %cond0 = icmp slt i32 %val, 40 54 call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] 55 %cond1 = icmp slt i32 %val, 40 56 call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ] 57 58 %cond2 = icmp slt i32 %val, 40 59 %rval = sext i1 %cond2 to i32 60 ret i32 %rval 61 } 62 63 define i32 @test3.unhandled(i32 %val) { 64 ; After a guard has executed the condition it was guarding is known to 65 ; be true. 66 67 ; CHECK-LABEL: @test3.unhandled( 68 ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 69 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] 70 ; CHECK-NEXT: %cond1 = icmp sge i32 %val, 40 71 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ] 72 ; CHECK-NEXT: ret i32 0 73 74 ; Demonstrates a case we do not yet handle (it is legal to fold %cond2 75 ; to false) 76 %cond0 = icmp slt i32 %val, 40 77 call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] 78 %cond1 = icmp sge i32 %val, 40 79 call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ] 80 ret i32 0 81 } 82 83 define i32 @test4(i32 %val, i1 %c) { 84 ; Same as test3, but with some control flow involved. 85 86 ; CHECK-LABEL: @test4( 87 ; CHECK: entry: 88 ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 89 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond0 90 ; CHECK-NEXT: br label %bb0 91 92 ; CHECK: bb0: 93 ; CHECK-NEXT: %cond2 = icmp ult i32 %val, 200 94 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond2 95 ; CHECK-NEXT: br i1 %c, label %left, label %right 96 97 ; CHECK: left: 98 ; CHECK-NEXT: ret i32 0 99 100 ; CHECK: right: 101 ; CHECK-NEXT: ret i32 20 102 103 entry: 104 %cond0 = icmp slt i32 %val, 40 105 call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] 106 %cond1 = icmp slt i32 %val, 40 107 call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ] 108 br label %bb0 109 110 bb0: 111 %cond2 = icmp ult i32 %val, 200 112 call void(i1,...) @llvm.experimental.guard(i1 %cond2) [ "deopt"() ] 113 br i1 %c, label %left, label %right 114 115 left: 116 %cond3 = icmp ult i32 %val, 200 117 call void(i1,...) @llvm.experimental.guard(i1 %cond3) [ "deopt"() ] 118 ret i32 0 119 120 right: 121 ret i32 20 122 } 123 124 define i32 @test5(i32 %val, i1 %c) { 125 ; Same as test4, but the %left block has mutliple predecessors. 126 127 ; CHECK-LABEL: @test5( 128 129 ; CHECK: entry: 130 ; CHECK-NEXT: %cond0 = icmp slt i32 %val, 40 131 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond0 132 ; CHECK-NEXT: br label %bb0 133 134 ; CHECK: bb0: 135 ; CHECK-NEXT: %cond2 = icmp ult i32 %val, 200 136 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %cond2 137 ; CHECK-NEXT: br i1 %c, label %left, label %right 138 139 ; CHECK: left: 140 ; CHECK-NEXT: br label %right 141 142 ; CHECK: right: 143 ; CHECK-NEXT: br label %left 144 145 entry: 146 %cond0 = icmp slt i32 %val, 40 147 call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ] 148 %cond1 = icmp slt i32 %val, 40 149 call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ] 150 br label %bb0 151 152 bb0: 153 %cond2 = icmp ult i32 %val, 200 154 call void(i1,...) @llvm.experimental.guard(i1 %cond2) [ "deopt"() ] 155 br i1 %c, label %left, label %right 156 157 left: 158 %cond3 = icmp ult i32 %val, 200 159 call void(i1,...) @llvm.experimental.guard(i1 %cond3) [ "deopt"() ] 160 br label %right 161 162 right: 163 br label %left 164 } 165 166 define void @test6(i1 %c, i32* %ptr) { 167 ; Check that we do not DSE over calls to @llvm.experimental.guard. 168 ; Guard intrinsics do _read_ memory, so th call to guard below needs 169 ; to see the store of 500 to %ptr 170 171 ; CHECK-LABEL: @test6( 172 ; CHECK-NEXT: store i32 500, i32* %ptr 173 ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %c) [ "deopt"() ] 174 ; CHECK-NEXT: store i32 600, i32* %ptr 175 176 177 store i32 500, i32* %ptr 178 call void(i1,...) @llvm.experimental.guard(i1 %c) [ "deopt"() ] 179 store i32 600, i32* %ptr 180 ret void 181 } 182