Files
clang-p2996/llvm/test/Transforms/GuardWidening/basic_widenable_condition_guards.ll
Serguei Katkov 0b5bb6923f [GuardWidening] Freeze the introduced use. Re-land.
Non-determenism is fixed.

Guard widening optimization is able to move the condition from one
guard to the previous one. As a result if the condition is poison
and orginal second guard is never executed but the first one does,
we introduce undefined behavior which was not observed in original
program.

To resolve the issue we must freeze the condition we are moving.
However optimization itself does not know how to work with freeze.
Additionally optimization is written in incremental way.
For example we have three guards
G1(base + 8 < L)
G2(base + 16 < L)
G3(base + 24 < L)

On the first step GW will combine G1 and G2 as
G1(base + 8 < L && freeze(base + 16 < L))
G2(true)
G3(base + 24 < L)

while combining G1 and G3 base appears to be different.

To keep optimization enabled after freezing the moving condition, the
freeze instruction is pushed as much as possible and later all uses
of freezed values are replaced with frozen version.

This is similar what instruction combining does but more aggressevely.
2023-03-30 10:59:01 +07:00

1127 lines
49 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -guard-widening-widen-branch-guards=true -passes=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: [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0:![0-9]+]]
; 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_GW_FR]], [[WIDENABLE_COND3]]
; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
; 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: [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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_GW_FR]], [[WIDENABLE_COND3]]
; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
; 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: [[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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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: [[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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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 [[PROF0]]
; 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: [[WIDE_CHK:%.*]] = icmp uge i32 [[A]], 11
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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 [[PROF0]]
; 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(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: [[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 [[PROF0]]
; CHECK: deopt:
; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ]
; CHECK-NEXT: ret void
; CHECK: guarded:
; CHECK-NEXT: store i1 [[UNKNOWN:%.*]], ptr [[B:%.*]], align 1
; CHECK-NEXT: [[COND_1:%.*]] = load i1, ptr [[A]], align 1
; 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 [[PROF0]]
; 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, ptr %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, ptr %b
%cond_1 = load i1, ptr %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, 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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; CHECK: deopt:
; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid() [ "deopt"() ]
; CHECK-NEXT: ret void
; CHECK: guarded:
; CHECK-NEXT: [[COND_2:%.*]] = load volatile i1, ptr [[COND_BUF]], align 1
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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, ptr %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, ptr %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: [[A_GW_FR:%.*]] = freeze i32 [[A:%.*]]
; 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 [[PROF0]]
; 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: [[COND_3:%.*]] = icmp ult i32 [[A_GW_FR]], 7
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_2:%.*]], [[COND_3]]
; CHECK-NEXT: [[WIDENABLE_COND3:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND4:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND3]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND4]], label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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: [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT: br label [[OUTER_HEADER:%.*]]
; CHECK: outer_header:
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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_GW_FR]], [[WIDENABLE_COND3]]
; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
; 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: [[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: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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 [[PROF0]]
; 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: [[WIDE_CHK:%.*]] = icmp ult i32 [[A]], 10
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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 [[PROF0]]
; 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: [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[EXIPLICIT_GUARD_COND:%.*]] = and i1 [[WIDENABLE_COND]], [[WIDE_CHK]]
; CHECK-NEXT: br i1 [[EXIPLICIT_GUARD_COND]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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_GW_FR]], [[WIDENABLE_COND3]]
; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
; 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: [[COND_0_GW_FR:%.*]] = freeze i1 [[COND_0:%.*]]
; CHECK-NEXT: [[WIDE_CHK:%.*]] = and i1 true, [[COND_0_GW_FR]]
; CHECK-NEXT: [[WIDENABLE_COND:%.*]] = call i1 @llvm.experimental.widenable.condition()
; CHECK-NEXT: [[TMP0:%.*]] = and i1 [[WIDE_CHK]], [[WIDENABLE_COND]]
; CHECK-NEXT: br i1 [[TMP0]], label [[GUARDED:%.*]], label [[DEOPT:%.*]], !prof [[PROF0]]
; 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_GW_FR]], [[WIDENABLE_COND3]]
; CHECK-NEXT: br i1 true, label [[GUARDED1:%.*]], label [[DEOPT2:%.*]], !prof [[PROF0]]
; 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}