Index: llvm/include/llvm/Analysis/ScalarEvolution.h =================================================================== --- llvm/include/llvm/Analysis/ScalarEvolution.h +++ llvm/include/llvm/Analysis/ScalarEvolution.h @@ -1122,6 +1122,11 @@ const Instruction *CtxI, const SCEV *MaxIter); + std::optional + getLoopInvariantExitCondDuringFirstIterationsImpl( + ICmpInst::Predicate Pred, const SCEV *LHS, const SCEV *RHS, const Loop *L, + const Instruction *CtxI, const SCEV *MaxIter); + /// Simplify LHS and RHS in a comparison with predicate Pred. Return true /// iff any changes were made. If the operands are provably equal or /// unequal, LHS and RHS are set to the same value and Pred is set to either Index: llvm/lib/Analysis/ScalarEvolution.cpp =================================================================== --- llvm/lib/Analysis/ScalarEvolution.cpp +++ llvm/lib/Analysis/ScalarEvolution.cpp @@ -11046,6 +11046,26 @@ ScalarEvolution::getLoopInvariantExitCondDuringFirstIterations( ICmpInst::Predicate Pred, const SCEV *LHS, const SCEV *RHS, const Loop *L, const Instruction *CtxI, const SCEV *MaxIter) { + if (auto LIP = getLoopInvariantExitCondDuringFirstIterationsImpl( + Pred, LHS, RHS, L, CtxI, MaxIter)) + return LIP; + if (auto *UMin = dyn_cast(MaxIter)) + // Number of iterations expressed as UMIN isn't always great for expressing + // the value on the last iteration. If the straightforward approach didn't + // work, try the following trick: if the a predicate is invariant for X, it + // is also invariant for umin(X, ...). So try to find something that works + // among subexpressions of MaxIter expressed as umin. + for (auto *Op : UMin->operands()) + if (auto LIP = getLoopInvariantExitCondDuringFirstIterationsImpl( + Pred, LHS, RHS, L, CtxI, Op)) + return LIP; + return std::nullopt; +} + +std::optional +ScalarEvolution::getLoopInvariantExitCondDuringFirstIterationsImpl( + ICmpInst::Predicate Pred, const SCEV *LHS, const SCEV *RHS, const Loop *L, + const Instruction *CtxI, const SCEV *MaxIter) { // Try to prove the following set of facts: // - The predicate is monotonic in the iteration space. // - If the check does not fail on the 1st iteration: Index: llvm/test/Transforms/IndVarSimplify/X86/pr59615.ll =================================================================== --- llvm/test/Transforms/IndVarSimplify/X86/pr59615.ll +++ llvm/test/Transforms/IndVarSimplify/X86/pr59615.ll @@ -8,35 +8,30 @@ ; CHECK-LABEL: @test( ; CHECK-NEXT: bb: ; CHECK-NEXT: [[VAR:%.*]] = load atomic i32, ptr addrspace(1) poison unordered, align 8, !range [[RNG0:![0-9]+]], !invariant.load !1, !noundef !1 -; CHECK-NEXT: [[VAR1:%.*]] = add nsw i32 [[VAR]], -1 ; CHECK-NEXT: [[VAR2:%.*]] = icmp eq i32 [[VAR]], 0 ; CHECK-NEXT: br i1 [[VAR2]], label [[BB18:%.*]], label [[BB19:%.*]] ; CHECK: bb3: ; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[BB19]] ], [ [[INDVARS_IV_NEXT:%.*]], [[BB12:%.*]] ] -; CHECK-NEXT: [[TMP0:%.*]] = sub nsw i64 [[TMP3:%.*]], [[INDVARS_IV]] -; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[TMP0]] to i32 -; CHECK-NEXT: [[VAR6:%.*]] = icmp ult i32 [[TMP1]], [[VAR]] -; CHECK-NEXT: br i1 [[VAR6]], label [[BB8:%.*]], label [[BB7:%.*]] +; CHECK-NEXT: br i1 true, label [[BB8:%.*]], label [[BB7:%.*]] ; CHECK: bb7: ; CHECK-NEXT: ret void ; CHECK: bb8: ; CHECK-NEXT: [[VAR9:%.*]] = load atomic i32, ptr addrspace(1) poison unordered, align 8, !range [[RNG0]], !invariant.load !1, !noundef !1 -; CHECK-NEXT: [[TMP2:%.*]] = zext i32 [[VAR9]] to i64 -; CHECK-NEXT: [[VAR10:%.*]] = icmp ult i64 [[INDVARS_IV]], [[TMP2]] +; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[VAR9]] to i64 +; CHECK-NEXT: [[VAR10:%.*]] = icmp ult i64 [[INDVARS_IV]], [[TMP0]] ; CHECK-NEXT: br i1 [[VAR10]], label [[BB12]], label [[BB11:%.*]] ; CHECK: bb11: ; CHECK-NEXT: ret void ; CHECK: bb12: ; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1 -; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT:%.*]] +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT]], [[TMP1:%.*]] ; CHECK-NEXT: br i1 [[EXITCOND]], label [[BB3:%.*]], label [[BB17:%.*]] ; CHECK: bb17: ; CHECK-NEXT: unreachable ; CHECK: bb18: ; CHECK-NEXT: ret void ; CHECK: bb19: -; CHECK-NEXT: [[TMP3]] = sext i32 [[VAR1]] to i64 -; CHECK-NEXT: [[WIDE_TRIP_COUNT]] = zext i32 [[VAR]] to i64 +; CHECK-NEXT: [[TMP1]] = zext i32 [[VAR]] to i64 ; CHECK-NEXT: br label [[BB3]] ; bb: