Index: llvm/include/llvm/Analysis/ScalarEvolution.h =================================================================== --- llvm/include/llvm/Analysis/ScalarEvolution.h +++ llvm/include/llvm/Analysis/ScalarEvolution.h @@ -1727,6 +1727,7 @@ ExitLimit computeExitLimit(const Loop *L, BasicBlock *ExitingBlock, bool AllowPredicates = false); +public: /// Compute the number of times the backedge of the specified loop will /// execute if its exit condition were a conditional branch of ExitCond. /// @@ -1741,6 +1742,7 @@ bool ExitIfTrue, bool ControlsExit, bool AllowPredicates = false); +private: /// Return a symbolic upper bound for the backedge taken count of the loop. /// This is more general than getConstantMaxBackedgeTakenCount as it returns /// an arbitrary expression as opposed to only constants. Index: llvm/lib/Transforms/Scalar/IndVarSimplify.cpp =================================================================== --- llvm/lib/Transforms/Scalar/IndVarSimplify.cpp +++ llvm/lib/Transforms/Scalar/IndVarSimplify.cpp @@ -1453,13 +1453,39 @@ // For AND aggregations, only consider true branch staying in loop. return false; + // If the number of conditions is reasonably small, and this block is the last + // block without SkipLastIter, we can try to apply SkipLastIter to some of its + // conditions if there is another condition that gives the very same exit + // count. + bool TryMoreOptimisticLastIter = false; + if (!SkipLastIter && Conditions.size() < 8 && + SE->getExitCount(L, ExitingBB, + ScalarEvolution::ExitCountKind::SymbolicMaximum) == + MaxIter) + TryMoreOptimisticLastIter = true; + bool Changed = false; - for (size_t i = 0; i < Conditions.size(); ++i) - if (auto NewCond = createReplacement(Conditions[i], L, ExitingBB, MaxIter, - SkipLastIter, SE, Rewriter)) { + for (size_t i = 0; i < Conditions.size(); ++i) { + bool OptimisticSkipLastIter = SkipLastIter; + if (TryMoreOptimisticLastIter) { + for (size_t j = 0; j < Conditions.size(); j++) + if (i != j) { + auto EL = SE->computeExitLimitFromCond(L, Conditions[j], ExitIfTrue, + /*ControlsExit*/ false); + if (EL.SymbolicMaxNotTaken == MaxIter) { + OptimisticSkipLastIter = true; + break; + } + } + } + + if (auto NewCond = + createReplacement(Conditions[i], L, ExitingBB, MaxIter, + OptimisticSkipLastIter, SE, Rewriter)) { Changed = true; Conditions[i] = *NewCond; } + } if (!Changed) return false; Index: llvm/test/Transforms/IndVarSimplify/turn-to-invariant.ll =================================================================== --- llvm/test/Transforms/IndVarSimplify/turn-to-invariant.ll +++ llvm/test/Transforms/IndVarSimplify/turn-to-invariant.ll @@ -526,21 +526,18 @@ ret i32 -2 } -; TODO: This test is equivalent to @test_simple_case, with only difference -; that both checks are merged together into one 'and' check. This -; should not prevent turning the range check into invariant. -; https://alive2.llvm.org/ce/z/G-2ERB +; https://alive2.llvm.org/ce/z/G-2ERB define i32 @test_and_conditions(i32 %start, i32 %len) { ; CHECK-LABEL: define {{[^@]+}}@test_and_conditions( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1 ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: -; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ] +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ] ; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0 -; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1 -; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp ult i32 [[IV_MINUS_1]], [[LEN:%.*]] -; CHECK-NEXT: [[BOTH_CHECKS:%.*]] = and i1 [[ZERO_CHECK]], [[RANGE_CHECK]] -; CHECK-NEXT: br i1 [[BOTH_CHECKS]], label [[BACKEDGE]], label [[FAILED:%.*]] +; CHECK-NEXT: [[BOTH_CHECKS2:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = and i1 [[BOTH_CHECKS2]], [[ZERO_CHECK]] +; CHECK-NEXT: br i1 [[TMP1]], label [[BACKEDGE]], label [[FAILED:%.*]] ; CHECK: backedge: ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1 ; CHECK-NEXT: [[LOOP_COND:%.*]] = call i1 @cond()