1; RUN: opt -S -early-cse < %s | FileCheck %s 2 3declare void @llvm.experimental.guard(i1,...) 4 5define 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 20define 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 35define 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 44define 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 63define 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 83define 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 103entry: 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 110bb0: 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 115left: 116 %cond3 = icmp ult i32 %val, 200 117 call void(i1,...) @llvm.experimental.guard(i1 %cond3) [ "deopt"() ] 118 ret i32 0 119 120right: 121 ret i32 20 122} 123 124define 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 145entry: 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 152bb0: 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 157left: 158 %cond3 = icmp ult i32 %val, 200 159 call void(i1,...) @llvm.experimental.guard(i1 %cond3) [ "deopt"() ] 160 br label %right 161 162right: 163 br label %left 164} 165 166define 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