From 38364a4c2ccf2dc1b3ab23ceca0bdfb4cca6d78d Mon Sep 17 00:00:00 2001 From: Juneyoung Lee Date: Fri, 1 Jan 2021 04:33:18 +0900 Subject: [PATCH] [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 --- include/llvm/Analysis/ScalarEvolution.h | 5 +- lib/Analysis/ScalarEvolution.cpp | 84 ++-- .../ScalarEvolution/exit-count-select.ll | 312 +++++++++++++++ .../trip-count-andor-selectform.ll | 366 ++++++++++++++++++ 4 files changed, 730 insertions(+), 37 deletions(-) create mode 100644 test/Analysis/ScalarEvolution/exit-count-select.ll create mode 100644 test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll diff --git a/include/llvm/Analysis/ScalarEvolution.h b/include/llvm/Analysis/ScalarEvolution.h index a7a24f086fb..b3f199de2cf 100644 --- a/include/llvm/Analysis/ScalarEvolution.h +++ b/include/llvm/Analysis/ScalarEvolution.h @@ -1676,10 +1676,7 @@ private: 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/lib/Analysis/ScalarEvolution.cpp b/lib/Analysis/ScalarEvolution.cpp index b0ead7349ba..3c284007cc2 100644 --- a/lib/Analysis/ScalarEvolution.cpp +++ b/lib/Analysis/ScalarEvolution.cpp @@ -135,6 +135,7 @@ #include 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(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(Op1)) + return Op1 == NeutralElement ? EL0 : EL1; + if (isa(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(BO->getOperand(1))) - return CI == NeutralElement ? EL0 : EL1; - if (ConstantInt *CI = dyn_cast(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(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(EL0.ExactNotTaken) || + isa(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(ExitCond) || !EL0.ExactNotTaken->isZero() || + BECount->isZero()); + } if (EL0.MaxNotTaken == getCouldNotCompute()) MaxBECount = EL1.MaxNotTaken; else if (EL1.MaxNotTaken == getCouldNotCompute()) diff --git a/test/Analysis/ScalarEvolution/exit-count-select.ll b/test/Analysis/ScalarEvolution/exit-count-select.ll new file mode 100644 index 00000000000..f683dd9a0e3 --- /dev/null +++ b/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" %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: <> LoopDispositions: { %loop: Computable } +; CHECK-NEXT: %i.next = add i32 %i, 1 +; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <> 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: <> 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)) 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: <> 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)) 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: <> 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: <> 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: <> LoopDispositions: { %loop: Computable } +; CHECK-NEXT: %i.next = add i32 %i, 1 +; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <> 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: <> 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: <> LoopDispositions: { %loop: Computable } +; CHECK-NEXT: %i.next = add i32 %i, 1 +; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <> 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: <> 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)) 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: <> 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)) 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: <> 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: <> 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: <> LoopDispositions: { %loop: Computable } +; CHECK-NEXT: %i.next = add i32 %i, 1 +; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: <> 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: <> 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/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll b/test/Analysis/ScalarEvolution/trip-count-andor-selectform.ll new file mode 100644 index 00000000000..b62055e6ba6 --- /dev/null +++ b/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-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 +}