; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -guard-widening-widen-branch-guards=true -guard-widening < %s | FileCheck %s ; RUN: opt -S -guard-widening-widen-branch-guards=true -passes=guard-widening < %s | FileCheck %s ; 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1:%.*]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1:%.*]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; 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: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry br i1 undef, label %left, label %right left: ; preds = %guarded br label %merge right: ; preds = %guarded br label %merge merge: ; preds = %right, %left %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %merge call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %merge 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: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10 ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; 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: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %cond_0 = icmp ult i32 %a, 10 %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry br i1 undef, label %left, label %right left: ; preds = %guarded br label %merge right: ; preds = %guarded br label %merge merge: ; preds = %right, %left %cond_1 = icmp ult i32 %b, 10 %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %merge call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %merge 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] ; CHECK: left: ; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10 ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; CHECK: right: ; CHECK-NEXT: ret void ; entry: %cond_0 = icmp ult i32 %a, 10 %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry br i1 undef, label %left, label %right left: ; preds = %guarded %cond_1 = icmp ult i32 %b, 10 %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %left call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %left ret void right: ; preds = %guarded 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: [[COND_0:%.*]] = icmp ult i32 [[A:%.*]], 10 ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[COND_1:%.*]] = icmp ult i32 [[B:%.*]], 10 ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0]], [[COND_1]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[LOOP:%.*]], label [[LEAVE:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[LEAVE]] ; CHECK: leave: ; CHECK-NEXT: ret void ; entry: %cond_0 = icmp ult i32 %a, 10 %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry br i1 undef, label %loop, label %leave loop: ; preds = %guarded1, %guarded %cond_1 = icmp ult i32 %b, 10 %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %loop call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %loop br i1 undef, label %loop, label %leave leave: ; preds = %guarded1, %guarded 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[WIDE_CHK:%.*]] = icmp uge i32 [[A]], 11 ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] ; CHECK: left: ; CHECK-NEXT: [[COND_1:%.*]] = icmp ugt i32 [[A]], 10 ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; CHECK: right: ; CHECK-NEXT: ret void ; entry: %cond_0 = icmp ugt i32 %a, 7 %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry br i1 undef, label %left, label %right left: ; preds = %guarded %cond_1 = icmp ugt i32 %a, 10 %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %left call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %left ret void right: ; preds = %guarded 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(i1* dereferenceable(32) %a, i1* %b, i1 %unknown) { ; CHECK-LABEL: @f_6( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[COND_0:%.*]] = load i1, i1* [[A:%.*]] ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: store i1 [[UNKNOWN:%.*]], i1* [[B:%.*]] ; CHECK-NEXT: [[COND_1:%.*]] = load i1, i1* [[A]] ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %cond_0 = load i1, i1* %a %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry store i1 %unknown, i1* %b %cond_1 = load i1, i1* %a %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded 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, i1* %cond_buf) { ; CHECK-LABEL: @f_7( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[COND_1:%.*]] = load volatile i1, i1* [[COND_BUF:%.*]] ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[COND_3:%.*]] = icmp ult i32 [[A:%.*]], 7 ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_1]], [[COND_3]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: [[COND_2:%.*]] = load volatile i1, i1* [[COND_BUF]] ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_2]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] ; CHECK: left: ; CHECK-NEXT: [[WIDENABLE_COND7:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND8:%.*]] = and i1 [[COND_3]], [[WIDENABLE_COND7]] ; CHECK-NEXT: br i1 true, label [[GUARDED5:%.*]], label [[DEOPT6:%.*]], !prof !0 ; CHECK: deopt6: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded5: ; CHECK-NEXT: br label [[LEFT]] ; CHECK: right: ; CHECK-NEXT: ret void ; entry: %cond_1 = load volatile i1, i1* %cond_buf %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_1, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry %cond_2 = load volatile i1, i1* %cond_buf %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_2, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded br i1 undef, label %left, label %right left: ; preds = %guarded5, %guarded1 %cond_3 = icmp ult i32 %a, 7 %widenable_cond7 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond8 = and i1 %cond_3, %widenable_cond7 br i1 %exiplicit_guard_cond8, label %guarded5, label %deopt6, !prof !0 deopt6: ; preds = %left call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded5: ; preds = %left br label %left right: ; preds = %guarded1 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: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_1:%.*]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[LEAVE:%.*]] ; CHECK: leave: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[COND_3:%.*]] = icmp ult i32 [[A:%.*]], 7 ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_2:%.*]], [[COND_3]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: br i1 undef, label [[LOOP2:%.*]], label [[LEAVE2:%.*]] ; CHECK: loop2: ; CHECK-NEXT: [[WIDENABLE_COND7:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND8:%.*]] = and i1 [[COND_3]], [[WIDENABLE_COND7]] ; CHECK-NEXT: br i1 true, label [[GUARDED5:%.*]], label [[DEOPT6:%.*]], !prof !0 ; CHECK: deopt6: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded5: ; CHECK-NEXT: br label [[LOOP2]] ; CHECK: leave2: ; CHECK-NEXT: ret void ; entry: br label %loop loop: ; preds = %guarded, %entry %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_1, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %loop call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %loop br i1 undef, label %loop, label %leave leave: ; preds = %guarded %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_2, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %leave call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %leave br i1 undef, label %loop2, label %leave2 loop2: ; preds = %guarded5, %guarded1 %cond_3 = icmp ult i32 %a, 7 %widenable_cond7 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond8 = and i1 %cond_3, %widenable_cond7 br i1 %exiplicit_guard_cond8, label %guarded5, label %deopt6, !prof !0 deopt6: ; preds = %loop2 call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded5: ; preds = %loop2 br label %loop2 leave2: ; preds = %guarded1 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[FIRST_LOOP]], label [[SECOND_LOOP:%.*]] ; CHECK: second_loop: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1:%.*]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: br label [[SECOND_LOOP]] ; entry: br label %first_loop first_loop: ; preds = %guarded, %entry %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %first_loop call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %first_loop br i1 undef, label %first_loop, label %second_loop second_loop: ; preds = %guarded1, %guarded %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %second_loop call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %second_loop 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[LOOP]], label [[NO_LOOP:%.*]] ; CHECK: no_loop: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1:%.*]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: br label %loop loop: ; preds = %guarded, %entry %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %loop call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %loop br i1 undef, label %loop, label %no_loop no_loop: ; preds = %guarded %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %no_loop call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %no_loop 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: br label [[OUTER_HEADER:%.*]] ; CHECK: outer_header: ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1:%.*]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br label [[INNER:%.*]] ; CHECK: inner: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: br i1 undef, label [[INNER]], label [[OUTER_LATCH:%.*]] ; CHECK: outer_latch: ; CHECK-NEXT: br i1 undef, label [[OUTER_HEADER]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: ret void ; entry: br label %outer_header outer_header: ; preds = %outer_latch, %entry %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %outer_header call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %outer_header br label %inner inner: ; preds = %guarded1, %guarded %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %inner call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %inner br i1 undef, label %inner, label %outer_latch outer_latch: ; preds = %guarded1 br i1 undef, label %outer_header, label %exit exit: ; preds = %outer_latch ret void } ; 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[A1:%.*]] = mul i32 [[A0:%.*]], [[A0]] ; 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: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 true, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry %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 %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[WIDE_CHK:%.*]] = icmp ult i32 [[A]], 10 ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] ; CHECK: left: ; CHECK-NEXT: [[COND_1:%.*]] = icmp slt i32 [[A]], 10 ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; CHECK: right: ; CHECK-NEXT: ret void ; entry: %cond_0 = icmp ult i32 %a, 14 %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry br i1 undef, label %left, label %right left: ; preds = %guarded %cond_1 = icmp slt i32 %a, 10 %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %left call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %left ret void right: ; preds = %guarded 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: br i1 undef, label [[LEFT:%.*]], label [[RIGHT:%.*]] ; CHECK: left: ; CHECK-NEXT: [[COND_1:%.*]] = icmp sgt i32 [[A]], 10 ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; CHECK: right: ; CHECK-NEXT: ret void ; entry: %cond_0 = icmp ult i32 %a, 14 %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry br i1 undef, label %left, label %right left: ; preds = %guarded %cond_1 = icmp sgt i32 %a, 10 %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %left call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %left ret void right: ; preds = %guarded 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 true, [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 true, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[COND_0:%.*]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 false, [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %cond_0, %widenable_cond br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 false, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded ret void } define void @swapped_wb(i1 %cond_0, i1 %cond_1) { ; CHECK-LABEL: @swapped_wb( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1:%.*]] ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDENABLE_COND]], [[WIDE_CHK]] ; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_1]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %widenable_cond = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond = and i1 %widenable_cond, %cond_0 br i1 %exiplicit_guard_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_1, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded ret void } define void @trivial_wb(i1 %cond_0) { ; CHECK-LABEL: @trivial_wb( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 true, [[COND_0:%.*]] ; CHECK-NEXT: [[TMP0:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]] ; CHECK-NEXT: br i1 [[TMP0]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof !0 ; CHECK: deopt: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded: ; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition() ; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[COND_0]], [[WIDENABLE_COND3]] ; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof !0 ; CHECK: deopt2: ; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ; CHECK-NEXT: ret void ; CHECK: guarded1: ; CHECK-NEXT: ret void ; entry: %widenable_cond = call i1 @llvm.experimental.widenable.condition() br i1 %widenable_cond, label %guarded, label %deopt, !prof !0 deopt: ; preds = %entry call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded: ; preds = %entry %widenable_cond3 = call i1 @llvm.experimental.widenable.condition() %exiplicit_guard_cond4 = and i1 %cond_0, %widenable_cond3 br i1 %exiplicit_guard_cond4, label %guarded1, label %deopt2, !prof !0 deopt2: ; preds = %guarded call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ] ret void guarded1: ; preds = %guarded ret void } declare void @llvm.experimental.deoptimize.isVoid(...) ; Function Attrs: inaccessiblememonly nounwind declare i1 @llvm.experimental.widenable.condition() #0 attributes #0 = { inaccessiblememonly nounwind } !0 = !{!"branch_weights", i32 1048576, i32 1}