| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt -S -passes=guard-widening < %s | FileCheck %s |
| ; RUN: opt -S -passes=guard-widening < %s | FileCheck %s |
| |
| declare void @llvm.experimental.guard(i1,...) |
| |
| ; Basic test case: we wide the first check to check both the |
| ; conditions. |
| define void @f_0(i1 %cond_0, i1 %cond_1) { |
| ; CHECK-LABEL: @f_0( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]] |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| } |
| |
| ; Same as @f_0, but with using a more general notion of postdominance. |
| define void @f_1(i1 %cond_0, i1 %cond_1) { |
| ; CHECK-LABEL: @f_1( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]] |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] |
| ; CHECK: left: |
| ; CHECK-NEXT: br label [[MERGE:%.*]] |
| ; CHECK: right: |
| ; CHECK-NEXT: br label [[MERGE]] |
| ; CHECK: merge: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %left, label %right |
| |
| left: |
| br label %merge |
| |
| right: |
| br label %merge |
| |
| merge: |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| } |
| |
| ; Like @f_1, but we have some code we need to hoist before we can |
| ; widen a dominanting check. |
| define void @f_2(i32 %a, i32 %b) { |
| ; CHECK-LABEL: @f_2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[B_GW_FR:%.*]] = freeze i32 [[B:%.*]] |
| ; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 |
| ; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B_GW_FR]], 10 |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] |
| ; CHECK: left: |
| ; CHECK-NEXT: br label [[MERGE:%.*]] |
| ; CHECK: right: |
| ; CHECK-NEXT: br label [[MERGE]] |
| ; CHECK: merge: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| %cond_0 = icmp ult i32 %a, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %left, label %right |
| |
| left: |
| br label %merge |
| |
| right: |
| br label %merge |
| |
| merge: |
| %cond_1 = icmp ult i32 %b, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| } |
| |
| ; Negative test: don't hoist stuff out of control flow |
| ; indiscriminately, since that can make us do more work than needed. |
| define void @f_3(i32 %a, i32 %b) { |
| ; CHECK-LABEL: @f_3( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] |
| ; CHECK: left: |
| ; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1]]) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; CHECK: right: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| %cond_0 = icmp ult i32 %a, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %left, label %right |
| |
| left: |
| |
| %cond_1 = icmp ult i32 %b, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| |
| right: |
| ret void |
| } |
| |
| ; But hoisting out of control flow is fine if it makes a loop computed |
| ; condition loop invariant. This behavior may require some tuning in |
| ; the future. |
| define void @f_4(i32 %a, i32 %b) { |
| ; CHECK-LABEL: @f_4( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[B_GW_FR:%.*]] = freeze i32 [[B:%.*]] |
| ; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 |
| ; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B_GW_FR]], 10 |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LOOP:%.*]], label [[LEAVE:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[LEAVE]] |
| ; CHECK: leave: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| %cond_0 = icmp ult i32 %a, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %loop, label %leave |
| |
| loop: |
| %cond_1 = icmp ult i32 %b, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| br i1 undef, label %loop, label %leave |
| |
| leave: |
| ret void |
| } |
| |
| ; Hoisting out of control flow is also fine if we can widen the |
| ; dominating check without doing any extra work. |
| define void @f_5(i32 %a) { |
| ; CHECK-LABEL: @f_5( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_0:%.*]] = icmp ugt i32 [[A:%.*]], 7 |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = icmp uge i32 [[A]], 11 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] |
| ; CHECK: left: |
| ; CHECK-NEXT: [[COND_1:%.*]] = icmp ugt i32 [[A]], 10 |
| ; CHECK-NEXT: ret void |
| ; CHECK: right: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| %cond_0 = icmp ugt i32 %a, 7 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %left, label %right |
| |
| left: |
| %cond_1 = icmp ugt i32 %a, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| |
| right: |
| ret void |
| } |
| |
| ; Negative test: the load from %a can be safely speculated to before |
| ; the first guard, but there is no guarantee that it will produce the |
| ; same value. |
| define void @f_6(ptr dereferenceable(32) %a, ptr %b, i1 %unknown) { |
| ; CHECK-LABEL: @f_6( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_0:%.*]] = load i1, ptr [[A:%.*]], align 1 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0]]) [ "deopt"() ] |
| ; CHECK-NEXT: store i1 [[UNKNOWN:%.*]], ptr [[B:%.*]], align 1 |
| ; CHECK-NEXT: [[COND_1:%.*]] = load i1, ptr [[A]], align 1 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1]]) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| %cond_0 = load i1, ptr %a |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| store i1 %unknown, ptr %b |
| %cond_1 = load i1, ptr %a |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| } |
| |
| ; All else equal, we try to widen the earliest guard we can. This |
| ; heuristic can use some tuning. |
| define void @f_7(i32 %a, ptr %cond_buf) { |
| ; CHECK-LABEL: @f_7( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[A_GW_FR:%.*]] = freeze i32 [[A:%.*]] |
| ; CHECK-NEXT: [[COND_1:%.*]] = load volatile i1, ptr [[COND_BUF:%.*]], align 1 |
| ; CHECK-NEXT: [[COND_3:%.*]] = icmp ult i32 [[A_GW_FR]], 7 |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_3]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: [[COND_2:%.*]] = load volatile i1, ptr [[COND_BUF]], align 1 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_2]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] |
| ; CHECK: left: |
| ; CHECK-NEXT: br label [[LEFT]] |
| ; CHECK: right: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| %cond_1 = load volatile i1, ptr %cond_buf |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| %cond_2 = load volatile i1, ptr %cond_buf |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ] |
| br i1 undef, label %left, label %right |
| |
| left: |
| %cond_3 = icmp ult i32 %a, 7 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_3) [ "deopt"() ] |
| br label %left |
| |
| right: |
| ret void |
| } |
| |
| ; In this case the earliest dominating guard is in a loop, and we |
| ; don't want to put extra work in there. This heuristic can use some |
| ; tuning. |
| define void @f_8(i32 %a, i1 %cond_1, i1 %cond_2) { |
| ; CHECK-LABEL: @f_8( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[A_GW_FR:%.*]] = freeze i32 [[A:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1:%.*]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[LEAVE:%.*]] |
| ; CHECK: leave: |
| ; CHECK-NEXT: [[COND_3:%.*]] = icmp ult i32 [[A_GW_FR]], 7 |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_2:%.*]], [[COND_3]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LOOP2:%.*]], label [[LEAVE2:%.*]] |
| ; CHECK: loop2: |
| ; CHECK-NEXT: br label [[LOOP2]] |
| ; CHECK: leave2: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| br i1 undef, label %loop, label %leave |
| |
| leave: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"() ] |
| br i1 undef, label %loop2, label %leave2 |
| |
| loop2: |
| %cond_3 = icmp ult i32 %a, 7 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_3) [ "deopt"() ] |
| br label %loop2 |
| |
| leave2: |
| ret void |
| } |
| |
| ; In cases like these where there isn't any "obviously profitable" |
| ; widening sites, we refuse to do anything. |
| define void @f_9(i32 %a, i1 %cond_0, i1 %cond_1) { |
| ; CHECK-LABEL: @f_9( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[FIRST_LOOP:%.*]] |
| ; CHECK: first_loop: |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[FIRST_LOOP]], label [[SECOND_LOOP:%.*]] |
| ; CHECK: second_loop: |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1:%.*]]) [ "deopt"() ] |
| ; CHECK-NEXT: br label [[SECOND_LOOP]] |
| ; |
| entry: |
| br label %first_loop |
| |
| first_loop: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %first_loop, label %second_loop |
| |
| second_loop: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| br label %second_loop |
| } |
| |
| ; Same situation as in @f_9: no "obviously profitable" widening sites, |
| ; so we refuse to do anything. |
| define void @f_10(i32 %a, i1 %cond_0, i1 %cond_1) { |
| ; CHECK-LABEL: @f_10( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[NO_LOOP:%.*]] |
| ; CHECK: no_loop: |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1:%.*]]) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %loop, label %no_loop |
| |
| no_loop: |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| } |
| |
| ; With guards in loops, we're okay hoisting out the guard into the |
| ; containing loop. |
| define void @f_11(i32 %a, i1 %cond_0, i1 %cond_1) { |
| ; CHECK-LABEL: @f_11( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]] |
| ; CHECK-NEXT: br label [[INNER:%.*]] |
| ; CHECK: inner: |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[INNER]], label [[OUTER:%.*]] |
| ; CHECK: outer: |
| ; CHECK-NEXT: br label [[INNER]] |
| ; |
| entry: |
| br label %inner |
| |
| inner: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %inner, label %outer |
| |
| outer: |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| br label %inner |
| } |
| |
| ; Checks that we are adequately guarded against exponential-time |
| ; behavior when hoisting code. |
| define void @f_12(i32 %a0) { |
| ; CHECK-LABEL: @f_12( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[A0_GW_FR:%.*]] = freeze i32 [[A0:%.*]] |
| ; CHECK-NEXT: [[A1:%.*]] = mul i32 [[A0_GW_FR]], [[A0_GW_FR]] |
| ; CHECK-NEXT: [[A2:%.*]] = mul i32 [[A1]], [[A1]] |
| ; CHECK-NEXT: [[A3:%.*]] = mul i32 [[A2]], [[A2]] |
| ; CHECK-NEXT: [[A4:%.*]] = mul i32 [[A3]], [[A3]] |
| ; CHECK-NEXT: [[A5:%.*]] = mul i32 [[A4]], [[A4]] |
| ; CHECK-NEXT: [[A6:%.*]] = mul i32 [[A5]], [[A5]] |
| ; CHECK-NEXT: [[A7:%.*]] = mul i32 [[A6]], [[A6]] |
| ; CHECK-NEXT: [[A8:%.*]] = mul i32 [[A7]], [[A7]] |
| ; CHECK-NEXT: [[A9:%.*]] = mul i32 [[A8]], [[A8]] |
| ; CHECK-NEXT: [[A10:%.*]] = mul i32 [[A9]], [[A9]] |
| ; CHECK-NEXT: [[A11:%.*]] = mul i32 [[A10]], [[A10]] |
| ; CHECK-NEXT: [[A12:%.*]] = mul i32 [[A11]], [[A11]] |
| ; CHECK-NEXT: [[A13:%.*]] = mul i32 [[A12]], [[A12]] |
| ; CHECK-NEXT: [[A14:%.*]] = mul i32 [[A13]], [[A13]] |
| ; CHECK-NEXT: [[A15:%.*]] = mul i32 [[A14]], [[A14]] |
| ; CHECK-NEXT: [[A16:%.*]] = mul i32 [[A15]], [[A15]] |
| ; CHECK-NEXT: [[A17:%.*]] = mul i32 [[A16]], [[A16]] |
| ; CHECK-NEXT: [[A18:%.*]] = mul i32 [[A17]], [[A17]] |
| ; CHECK-NEXT: [[A19:%.*]] = mul i32 [[A18]], [[A18]] |
| ; CHECK-NEXT: [[A20:%.*]] = mul i32 [[A19]], [[A19]] |
| ; CHECK-NEXT: [[A21:%.*]] = mul i32 [[A20]], [[A20]] |
| ; CHECK-NEXT: [[A22:%.*]] = mul i32 [[A21]], [[A21]] |
| ; CHECK-NEXT: [[A23:%.*]] = mul i32 [[A22]], [[A22]] |
| ; CHECK-NEXT: [[A24:%.*]] = mul i32 [[A23]], [[A23]] |
| ; CHECK-NEXT: [[A25:%.*]] = mul i32 [[A24]], [[A24]] |
| ; CHECK-NEXT: [[A26:%.*]] = mul i32 [[A25]], [[A25]] |
| ; CHECK-NEXT: [[A27:%.*]] = mul i32 [[A26]], [[A26]] |
| ; CHECK-NEXT: [[A28:%.*]] = mul i32 [[A27]], [[A27]] |
| ; CHECK-NEXT: [[A29:%.*]] = mul i32 [[A28]], [[A28]] |
| ; CHECK-NEXT: [[A30:%.*]] = mul i32 [[A29]], [[A29]] |
| ; CHECK-NEXT: [[COND:%.*]] = trunc i32 [[A30]] to i1 |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 true, [[COND]] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; |
| |
| ; Eliding the earlier 29 multiplications for brevity |
| |
| entry: |
| call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ] |
| %a1 = mul i32 %a0, %a0 |
| %a2 = mul i32 %a1, %a1 |
| %a3 = mul i32 %a2, %a2 |
| %a4 = mul i32 %a3, %a3 |
| %a5 = mul i32 %a4, %a4 |
| %a6 = mul i32 %a5, %a5 |
| %a7 = mul i32 %a6, %a6 |
| %a8 = mul i32 %a7, %a7 |
| %a9 = mul i32 %a8, %a8 |
| %a10 = mul i32 %a9, %a9 |
| %a11 = mul i32 %a10, %a10 |
| %a12 = mul i32 %a11, %a11 |
| %a13 = mul i32 %a12, %a12 |
| %a14 = mul i32 %a13, %a13 |
| %a15 = mul i32 %a14, %a14 |
| %a16 = mul i32 %a15, %a15 |
| %a17 = mul i32 %a16, %a16 |
| %a18 = mul i32 %a17, %a17 |
| %a19 = mul i32 %a18, %a18 |
| %a20 = mul i32 %a19, %a19 |
| %a21 = mul i32 %a20, %a20 |
| %a22 = mul i32 %a21, %a21 |
| %a23 = mul i32 %a22, %a22 |
| %a24 = mul i32 %a23, %a23 |
| %a25 = mul i32 %a24, %a24 |
| %a26 = mul i32 %a25, %a25 |
| %a27 = mul i32 %a26, %a26 |
| %a28 = mul i32 %a27, %a27 |
| %a29 = mul i32 %a28, %a28 |
| %a30 = mul i32 %a29, %a29 |
| %cond = trunc i32 %a30 to i1 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ] |
| ret void |
| } |
| |
| define void @f_13(i32 %a) { |
| ; CHECK-LABEL: @f_13( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 14 |
| ; CHECK-NEXT: [[WIDE_CHK:%.*]] = icmp ult i32 [[A]], 10 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] |
| ; CHECK: left: |
| ; CHECK-NEXT: [[COND_1:%.*]] = icmp slt i32 [[A]], 10 |
| ; CHECK-NEXT: ret void |
| ; CHECK: right: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| %cond_0 = icmp ult i32 %a, 14 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %left, label %right |
| |
| left: |
| %cond_1 = icmp slt i32 %a, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| |
| right: |
| ret void |
| } |
| |
| define void @f_14(i32 %a) { |
| ; CHECK-LABEL: @f_14( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 14 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0]]) [ "deopt"() ] |
| ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] |
| ; CHECK: left: |
| ; CHECK-NEXT: [[COND_1:%.*]] = icmp sgt i32 [[A]], 10 |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_1]]) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; CHECK: right: |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| %cond_0 = icmp ult i32 %a, 14 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| br i1 undef, label %left, label %right |
| |
| left: |
| |
| %cond_1 = icmp sgt i32 %a, 10 |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"() ] |
| ret void |
| |
| right: |
| ret void |
| } |
| |
| ; Make sure we do not widen guard by trivial true conditions into something. |
| define void @f_15(i1 %cond_0, i1 %cond_1) { |
| ; CHECK-LABEL: @f_15( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ] |
| ret void |
| } |
| |
| ; Make sure we do not widen guard by trivial false conditions into something. |
| define void @f_16(i1 %cond_0, i1 %cond_1) { |
| ; CHECK-LABEL: @f_16( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"() ] |
| ; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] |
| ; CHECK-NEXT: ret void |
| ; |
| entry: |
| |
| call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"() ] |
| call void(i1, ...) @llvm.experimental.guard(i1 false) [ "deopt"() ] |
| ret void |
| } |