[llvm] 509fa8e - [SCEV] recognize logical and/or pattern

Juneyoung Lee via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 31 11:38:10 PST 2020


Author: Juneyoung Lee
Date: 2021-01-01T04:37:57+09:00
New Revision: 509fa8e02e25a610574c0fc2cceea1d350c35a66

URL: https://github.com/llvm/llvm-project/commit/509fa8e02e25a610574c0fc2cceea1d350c35a66
DIFF: https://github.com/llvm/llvm-project/commit/509fa8e02e25a610574c0fc2cceea1d350c35a66.diff

LOG: [SCEV] recognize logical and/or pattern

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

Added: 
    llvm/test/Analysis/ScalarEvolution/exit-count-select.ll
    llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll

Modified: 
    llvm/include/llvm/Analysis/ScalarEvolution.h
    llvm/lib/Analysis/ScalarEvolution.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index a7a24f086fbe..b3f199de2cfa 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -1676,10 +1676,7 @@ class ScalarEvolution {
   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

diff  --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index b0ead7349ba5..3c284007cc2d 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -135,6 +135,7 @@
 #include <vector>
 
 using namespace llvm;
+using namespace PatternMatch;
 
 #define DEBUG_TYPE "scalar-evolution"
 
@@ -7578,47 +7579,64 @@ ScalarEvolution::computeExitLimitFromCondFromBinOp(
     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())

diff  --git a/llvm/test/Analysis/ScalarEvolution/exit-count-select.ll b/llvm/test/Analysis/ScalarEvolution/exit-count-select.ll
new file mode 100644
index 000000000000..f683dd9a0e39
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/exit-count-select.ll
@@ -0,0 +1,312 @@
+; 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
+}

diff  --git a/llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll
new file mode 100644
index 000000000000..b62055e6ba6f
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll
@@ -0,0 +1,366 @@
+; 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
+}


        


More information about the llvm-commits mailing list