This patch makes SCEV recognize 'select A, B, false' and 'select A, true, B'.
This is a performance improvement that will be helpful after unsound select -> and/or transformation is removed, as discussed in D93065.
SCEV's answers for the select form should be a bit more conservative than the equivalent `and A, B` / `or A, B`.
Take this example: https://alive2.llvm.org/ce/z/NsP9ue .
To check whether it is valid for SCEV's computeExitLimit to return min(n, m) as ExactNotTaken value, I put llvm.assume at tgt.
It fails because the exit limit becomes poison if n is zero and m is poison. This is problematic if e.g. the exit value of i is replaced with min(n, m).
If either n or m is constant, we can revive the analysis again. I added relevant tests and put alive2 links there.
If and is used instead, this is okay: https://alive2.llvm.org/ce/z/K9rbJk . Hence the existing analysis is sound.
Reviewed By: nikic
Differential Revision: https://reviews.llvm.org/D93882
computeExitLimitFromCondFromBinOp(ExitLimitCacheTy &Cache, const Loop *L,
Value *ExitCond, bool ExitIfTrue,
bool ControlsExit, bool AllowPredicates);
- ExitLimit computeExitLimitFromCondFromBinOpHelper(
- ExitLimitCacheTy &Cache, const Loop *L, BinaryOperator *BO,
- bool EitherMayExit, bool ExitIfTrue, bool ControlsExit,
- bool AllowPredicates, const Constant *NeutralElement);
+
/// Compute the number of times the backedge of the specified loop will
/// execute if its exit condition were a conditional branch of the ICmpInst
/// ExitCond and ExitIfTrue. If AllowPredicates is set, this call will try
#include <vector>
using namespace llvm;
+using namespace PatternMatch;
#define DEBUG_TYPE "scalar-evolution"
ExitLimitCacheTy &Cache, const Loop *L, Value *ExitCond, bool ExitIfTrue,
bool ControlsExit, bool AllowPredicates) {
// Check if the controlling expression for this loop is an And or Or.
- if (auto *BO = dyn_cast<BinaryOperator>(ExitCond)) {
- if (BO->getOpcode() == Instruction::And)
- return computeExitLimitFromCondFromBinOpHelper(
- Cache, L, BO, !ExitIfTrue, ExitIfTrue, ControlsExit, AllowPredicates,
- ConstantInt::get(BO->getType(), 1));
- if (BO->getOpcode() == Instruction::Or)
- return computeExitLimitFromCondFromBinOpHelper(
- Cache, L, BO, ExitIfTrue, ExitIfTrue, ControlsExit, AllowPredicates,
- ConstantInt::get(BO->getType(), 0));
- }
- return None;
-}
+ Value *Op0, *Op1;
+ bool IsAnd = false;
+ if (match(ExitCond, m_LogicalAnd(m_Value(Op0), m_Value(Op1))))
+ IsAnd = true;
+ else if (match(ExitCond, m_LogicalOr(m_Value(Op0), m_Value(Op1))))
+ IsAnd = false;
+ else
+ return None;
+
+ // EitherMayExit is true in these two cases:
+ // br (and Op0 Op1), loop, exit
+ // br (or Op0 Op1), exit, loop
+ bool EitherMayExit = IsAnd ^ ExitIfTrue;
+ ExitLimit EL0 = computeExitLimitFromCondCached(Cache, L, Op0, ExitIfTrue,
+ ControlsExit && !EitherMayExit,
+ AllowPredicates);
+ ExitLimit EL1 = computeExitLimitFromCondCached(Cache, L, Op1, ExitIfTrue,
+ ControlsExit && !EitherMayExit,
+ AllowPredicates);
+
+ // Be robust against unsimplified IR for the form "op i1 X, NeutralElement"
+ const Constant *NeutralElement = ConstantInt::get(ExitCond->getType(), IsAnd);
+ if (isa<ConstantInt>(Op1))
+ return Op1 == NeutralElement ? EL0 : EL1;
+ if (isa<ConstantInt>(Op0))
+ return Op0 == NeutralElement ? EL1 : EL0;
-ScalarEvolution::ExitLimit
-ScalarEvolution::computeExitLimitFromCondFromBinOpHelper(
- ExitLimitCacheTy &Cache, const Loop *L, BinaryOperator *BO,
- bool EitherMayExit, bool ExitIfTrue, bool ControlsExit,
- bool AllowPredicates, const Constant *NeutralElement) {
- ExitLimit EL0 = computeExitLimitFromCondCached(
- Cache, L, BO->getOperand(0), ExitIfTrue, ControlsExit && !EitherMayExit,
- AllowPredicates);
- ExitLimit EL1 = computeExitLimitFromCondCached(
- Cache, L, BO->getOperand(1), ExitIfTrue, ControlsExit && !EitherMayExit,
- AllowPredicates);
- // Be robust against unsimplified IR for the form "op i1 X,
- // NeutralElement"
- if (ConstantInt *CI = dyn_cast<ConstantInt>(BO->getOperand(1)))
- return CI == NeutralElement ? EL0 : EL1;
- if (ConstantInt *CI = dyn_cast<ConstantInt>(BO->getOperand(0)))
- return CI == NeutralElement ? EL1 : EL0;
const SCEV *BECount = getCouldNotCompute();
const SCEV *MaxBECount = getCouldNotCompute();
if (EitherMayExit) {
// Both conditions must be same for the loop to continue executing.
// Choose the less conservative count.
- if (EL0.ExactNotTaken == getCouldNotCompute() ||
- EL1.ExactNotTaken == getCouldNotCompute())
- BECount = getCouldNotCompute();
- else
+ // If ExitCond is a short-circuit form (select), using
+ // umin(EL0.ExactNotTaken, EL1.ExactNotTaken) is unsafe in general.
+ // To see the detailed examples, please see
+ // test/Analysis/ScalarEvolution/exit-count-select.ll
+ bool PoisonSafe = isa<BinaryOperator>(ExitCond);
+ if (!PoisonSafe)
+ // Even if ExitCond is select, we can safely derive BECount using both
+ // EL0 and EL1 in these cases:
+ // (1) EL0.ExactNotTaken is non-zero
+ // (2) EL1.ExactNotTaken is non-poison
+ // (3) EL0.ExactNotTaken is zero (BECount should be simply zero and
+ // it cannot be umin(0, ..))
+ // The PoisonSafe assignment below is simplified and the assertion after
+ // BECount calculation fully guarantees the condition (3).
+ PoisonSafe = isa<SCEVConstant>(EL0.ExactNotTaken) ||
+ isa<SCEVConstant>(EL1.ExactNotTaken);
+ if (EL0.ExactNotTaken != getCouldNotCompute() &&
+ EL1.ExactNotTaken != getCouldNotCompute() && PoisonSafe) {
BECount =
getUMinFromMismatchedTypes(EL0.ExactNotTaken, EL1.ExactNotTaken);
+
+ // If EL0.ExactNotTaken was zero and ExitCond was a short-circuit form,
+ // it should have been simplified to zero (see the condition (3) above)
+ assert(!isa<BinaryOperator>(ExitCond) || !EL0.ExactNotTaken->isZero() ||
+ BECount->isZero());
+ }
if (EL0.MaxNotTaken == getCouldNotCompute())
MaxBECount = EL1.MaxNotTaken;
else if (EL1.MaxNotTaken == getCouldNotCompute())
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
+; RUN: opt -analyze -enable-new-pm=0 -scalar-evolution %s | FileCheck %s
+; RUN: opt -disable-output "-passes=print<scalar-evolution>" %s 2>&1 | FileCheck %s
+
+
+; exact-not-taken cannot be umin(n, m) because it is possible for (n, m) to be (0, poison)
+; https://alive2.llvm.org/ce/z/NsP9ue
+define void @logical_and(i32 %n, i32 %m) {
+; CHECK-LABEL: 'logical_and'
+; CHECK-NEXT: Classifying expressions for: @logical_and
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_and
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp ult i32 %i, %n
+ %cond_i2 = icmp ult i32 %i, %m
+ %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+ br i1 %cond, label %loop, label %exit
+exit:
+ ret void
+}
+
+; If m is constant, exact-not-taken is umin(n, m)
+; https://alive2.llvm.org/ce/z/ZTNXgY
+define void @logical_and_m_const(i32 %n) {
+; CHECK-LABEL: 'logical_and_m_const'
+; CHECK-NEXT: Classifying expressions for: @logical_and_m_const
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: [0,3) S: [0,3) Exits: (2 umin %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,4) S: [1,4) Exits: (1 + (2 umin %n))<nuw><nsw> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_and_m_const
+; CHECK-NEXT: Loop %loop: backedge-taken count is (2 umin %n)
+; CHECK-NEXT: Loop %loop: max backedge-taken count is 2
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (2 umin %n)
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp ult i32 %i, %n
+ %cond_i2 = icmp ult i32 %i, 2
+ %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+ br i1 %cond, label %loop, label %exit
+exit:
+ ret void
+}
+
+; exact-not-taken is umin(2, m) because m participates in the exit branch condition.
+; https://alive2.llvm.org/ce/z/rCVMmp
+define void @logical_and_nonzero(i32 %m) {
+; CHECK-LABEL: 'logical_and_nonzero'
+; CHECK-NEXT: Classifying expressions for: @logical_and_nonzero
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: [0,3) S: [0,3) Exits: (2 umin %m) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,4) S: [1,4) Exits: (1 + (2 umin %m))<nuw><nsw> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_and_nonzero
+; CHECK-NEXT: Loop %loop: backedge-taken count is (2 umin %m)
+; CHECK-NEXT: Loop %loop: max backedge-taken count is 2
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (2 umin %m)
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp ult i32 %i, 2
+ %cond_i2 = icmp ult i32 %i, %m
+ %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+ br i1 %cond, label %loop, label %exit
+exit:
+ ret void
+}
+
+; exact-not-taken cannot be umin(0, m) because m never participates in the exit branch condition.
+; https://alive2.llvm.org/ce/z/rlaN4a
+; Instead, it should be just 0.
+define void @logical_and_zero(i32 %m) {
+; CHECK-LABEL: 'logical_and_zero'
+; CHECK-NEXT: Classifying expressions for: @logical_and_zero
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: [0,1) S: [0,1) Exits: 0 LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,2) S: [1,2) Exits: 1 LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_and_zero
+; CHECK-NEXT: Loop %loop: backedge-taken count is 0
+; CHECK-NEXT: Loop %loop: max backedge-taken count is 0
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is 0
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp ult i32 %i, 0
+ %cond_i2 = icmp ult i32 %i, %m
+ %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+ br i1 %cond, label %loop, label %exit
+exit:
+ ret void
+}
+
+; exact-not-taken is umax(n, m) because both conditions (cond_i, cond_i2) participate in branching,
+; preventing them from being poison.
+; https://alive2.llvm.org/ce/z/8_p-zu
+; Currently SCEV is conservative in this case and simply returns unknown.
+define void @logical_and_inversed(i32 %n, i32 %m) {
+; CHECK-LABEL: 'logical_and_inversed'
+; CHECK-NEXT: Classifying expressions for: @logical_and_inversed
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_and_inversed
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp uge i32 %i, %n
+ %cond_i2 = icmp uge i32 %i, %m
+ %cond = select i1 %cond_i, i1 %cond_i2, i1 false
+ br i1 %cond, label %exit, label %loop
+exit:
+ ret void
+}
+
+; exact-not-taken cannot be umin(n, m) because it is possible for (n, m) to be (0, poison)
+; https://alive2.llvm.org/ce/z/ApRitq
+define void @logical_or(i32 %n, i32 %m) {
+; CHECK-LABEL: 'logical_or'
+; CHECK-NEXT: Classifying expressions for: @logical_or
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_or
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp uge i32 %i, %n
+ %cond_i2 = icmp uge i32 %i, %m
+ %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+ br i1 %cond, label %exit, label %loop
+exit:
+ ret void
+}
+
+; If m is constant, exact-not-taken is umin(n, m)
+; https://alive2.llvm.org/ce/z/RQmJiq
+define void @logical_or_m_const(i32 %n) {
+; CHECK-LABEL: 'logical_or_m_const'
+; CHECK-NEXT: Classifying expressions for: @logical_or_m_const
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: [0,3) S: [0,3) Exits: (2 umin %n) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,4) S: [1,4) Exits: (1 + (2 umin %n))<nuw><nsw> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_or_m_const
+; CHECK-NEXT: Loop %loop: backedge-taken count is (2 umin %n)
+; CHECK-NEXT: Loop %loop: max backedge-taken count is 2
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (2 umin %n)
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp uge i32 %i, %n
+ %cond_i2 = icmp uge i32 %i, 2
+ %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+ br i1 %cond, label %exit, label %loop
+exit:
+ ret void
+}
+
+; exact-not-taken is umin(2, m) because m participates in exit branch condition.
+; https://alive2.llvm.org/ce/z/zcHS_d
+define void @logical_or_nonzero(i32 %m) {
+; CHECK-LABEL: 'logical_or_nonzero'
+; CHECK-NEXT: Classifying expressions for: @logical_or_nonzero
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: [0,3) S: [0,3) Exits: (2 umin %m) LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,4) S: [1,4) Exits: (1 + (2 umin %m))<nuw><nsw> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_or_nonzero
+; CHECK-NEXT: Loop %loop: backedge-taken count is (2 umin %m)
+; CHECK-NEXT: Loop %loop: max backedge-taken count is 2
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (2 umin %m)
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp uge i32 %i, 2
+ %cond_i2 = icmp uge i32 %i, %m
+ %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+ br i1 %cond, label %exit, label %loop
+exit:
+ ret void
+}
+
+; exact-not-taken cannot be umin(0, m) because m does not participate in exit branch condition.
+; https://alive2.llvm.org/ce/z/-dUmmc
+; Instead, exact-not-taken should be just 0.
+define void @logical_or_zero(i32 %m) {
+; CHECK-LABEL: 'logical_or_zero'
+; CHECK-NEXT: Classifying expressions for: @logical_or_zero
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: [0,1) S: [0,1) Exits: 0 LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,2) S: [1,2) Exits: 1 LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_or_zero
+; CHECK-NEXT: Loop %loop: backedge-taken count is 0
+; CHECK-NEXT: Loop %loop: max backedge-taken count is 0
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is 0
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp uge i32 %i, 0
+ %cond_i2 = icmp uge i32 %i, %m
+ %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+ br i1 %cond, label %exit, label %loop
+exit:
+ ret void
+}
+
+; exact-not-taken is umax(n, m) because both conditions (cond_i, cond_i2) participate in branching,
+; preventing them from being poison.
+; https://alive2.llvm.org/ce/z/VaCu9C
+; Currently SCEV is conservative in this case and simply returns unknown.
+define void @logical_or_inversed(i32 %n, i32 %m) {
+; CHECK-LABEL: 'logical_or_inversed'
+; CHECK-NEXT: Classifying expressions for: @logical_or_inversed
+; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+; CHECK-NEXT: --> {0,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %i.next = add i32 %i, 1
+; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Computable }
+; CHECK-NEXT: %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+; CHECK-NEXT: --> %cond U: full-set S: full-set Exits: <<Unknown>> LoopDispositions: { %loop: Variant }
+; CHECK-NEXT: Determining loop execution counts for: @logical_or_inversed
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+loop:
+ %i = phi i32 [0, %entry], [%i.next, %loop]
+ %i.next = add i32 %i, 1
+ %cond_i = icmp ult i32 %i, %n
+ %cond_i2 = icmp ult i32 %i, %m
+ %cond = select i1 %cond_i, i1 true, i1 %cond_i2
+ br i1 %cond, label %loop, label %exit
+exit:
+ ret void
+}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
+; RUN: opt < %s -analyze -enable-new-pm=0 -scalar-evolution -scalar-evolution-classify-expressions=0 2>&1 | FileCheck %s
+; RUN: opt < %s -disable-output "-passes=print<scalar-evolution>" -scalar-evolution-classify-expressions=0 2>&1 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @unsimplified_and1(i32 %n) {
+; CHECK-LABEL: 'unsimplified_and1'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_and1
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %and = select i1 %becond, i1 true, i1 false
+ br i1 %and, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @unsimplified_and2(i32 %n) {
+; CHECK-LABEL: 'unsimplified_and2'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_and2
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %and = select i1 true, i1 %becond, i1 false
+ br i1 %and, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @unsimplified_and3(i32 %n) {
+; CHECK-LABEL: 'unsimplified_and3'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_and3
+; CHECK-NEXT: Loop %loop: backedge-taken count is false
+; CHECK-NEXT: Loop %loop: max backedge-taken count is false
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is false
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %and = select i1 false, i1 %becond, i1 false
+ br i1 %and, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @unsimplified_and4(i32 %n) {
+; CHECK-LABEL: 'unsimplified_and4'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_and4
+; CHECK-NEXT: Loop %loop: backedge-taken count is false
+; CHECK-NEXT: Loop %loop: max backedge-taken count is false
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is false
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %and = select i1 %becond, i1 false, i1 false
+ br i1 %and, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @unsimplified_or1(i32 %n) {
+; CHECK-LABEL: 'unsimplified_or1'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_or1
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %or = select i1 %becond, i1 true, i1 true
+ br i1 %or, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @unsimplified_or2(i32 %n) {
+; CHECK-LABEL: 'unsimplified_or2'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_or2
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %or = select i1 true, i1 true, i1 %becond
+ br i1 %or, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @unsimplified_or3(i32 %n) {
+; CHECK-LABEL: 'unsimplified_or3'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_or3
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %or = select i1 false, i1 true, i1 %becond
+ br i1 %or, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @unsimplified_or4(i32 %n) {
+; CHECK-LABEL: 'unsimplified_or4'
+; CHECK-NEXT: Determining loop execution counts for: @unsimplified_or4
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ule i32 %iv.inc, %n
+ %or = select i1 %becond, i1 true, i1 false
+ br i1 %or, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+define void @reversed_and1(i32 %n) {
+; CHECK-LABEL: 'reversed_and1'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_and1
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %and = select i1 %becond, i1 true, i1 false
+ br i1 %and, label %leave, label %loop
+
+leave:
+ ret void
+}
+
+define void @reversed_and2(i32 %n) {
+; CHECK-LABEL: 'reversed_and2'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_and2
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %and = select i1 true, i1 %becond, i1 false
+ br i1 %and, label %leave, label %loop
+
+leave:
+ ret void
+}
+
+define void @reversed_and3(i32 %n) {
+; CHECK-LABEL: 'reversed_and3'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_and3
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %and = select i1 false, i1 %becond, i1 false
+ br i1 %and, label %leave, label %loop
+
+leave:
+ ret void
+}
+
+define void @reversed_and4(i32 %n) {
+; CHECK-LABEL: 'reversed_and4'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_and4
+; CHECK-NEXT: Loop %loop: Unpredictable backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable max backedge-taken count.
+; CHECK-NEXT: Loop %loop: Unpredictable predicated backedge-taken count.
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %and = select i1 %becond, i1 false, i1 false
+ br i1 %and, label %leave, label %loop
+
+leave:
+ ret void
+}
+
+define void @reversed_or1(i32 %n) {
+; CHECK-LABEL: 'reversed_or1'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_or1
+; CHECK-NEXT: Loop %loop: backedge-taken count is false
+; CHECK-NEXT: Loop %loop: max backedge-taken count is false
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is false
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %or = select i1 %becond, i1 true, i1 true
+ br i1 %or, label %leave, label %loop
+
+leave:
+ ret void
+}
+
+define void @reversed_or2(i32 %n) {
+; CHECK-LABEL: 'reversed_or2'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_or2
+; CHECK-NEXT: Loop %loop: backedge-taken count is false
+; CHECK-NEXT: Loop %loop: max backedge-taken count is false
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is false
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %or = select i1 true, i1 true, i1 %becond
+ br i1 %or, label %leave, label %loop
+
+leave:
+ ret void
+}
+
+define void @reversed_or3(i32 %n) {
+; CHECK-LABEL: 'reversed_or3'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_or3
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %or = select i1 false, i1 true, i1 %becond
+ br i1 %or, label %leave, label %loop
+
+leave:
+ ret void
+}
+
+define void @reversed_or4(i32 %n) {
+; CHECK-LABEL: 'reversed_or4'
+; CHECK-NEXT: Determining loop execution counts for: @reversed_or4
+; CHECK-NEXT: Loop %loop: backedge-taken count is %n
+; CHECK-NEXT: Loop %loop: max backedge-taken count is -1
+; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %n
+; CHECK-NEXT: Predicates:
+; CHECK: Loop %loop: Trip multiple is 1
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.inc, %loop ]
+ %iv.inc = add nsw i32 %iv, 1
+ %becond = icmp ugt i32 %iv.inc, %n
+ %or = select i1 %becond, i1 true, i1 false
+ br i1 %or, label %leave, label %loop
+
+leave:
+ ret void
+}