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