Index: include/llvm/Analysis/ScalarEvolution.h =================================================================== --- include/llvm/Analysis/ScalarEvolution.h +++ include/llvm/Analysis/ScalarEvolution.h @@ -529,6 +529,17 @@ const SCEV *FoundLHS, const SCEV *FoundRHS); + /// Test whether the condition described by Pred, LHS, and RHS is true + /// whenever the condition described by Pred, FoundLHS, and FoundRHS is + /// true. + /// + /// This routine tries to rule out integer overflow, and then tries to + /// reason about arithmetic properties of the predicates. + bool isImpliedCondOperandsViaNoOverflow(ICmpInst::Predicate Pred, + const SCEV *LHS, const SCEV *RHS, + const SCEV *FoundLHS, + const SCEV *FoundRHS); + /// If we know that the specified Phi is in the header of its containing /// loop, we know the loop executes a constant number of times, and the PHI /// node is just a recurrence involving constants, fold it. Index: lib/Analysis/ScalarEvolution.cpp =================================================================== --- lib/Analysis/ScalarEvolution.cpp +++ lib/Analysis/ScalarEvolution.cpp @@ -7291,6 +7291,103 @@ return false; } +// Return true if More == (Less + 1). +static bool IsOneMore(ScalarEvolution &SE, const SCEV *Less, const SCEV *More) { + // We avoid subtracting expressions here because this function is usually + // fairly deep in the call stack (i.e. is called many times). + // + + auto SplitBinaryAdd = [](const SCEV *Expr, const SCEV *&L, const SCEV *&R) { + const auto *AE = dyn_cast(Expr); + if (!AE || AE->getNumOperands() != 2) + return false; + + L = AE->getOperand(0); + R = AE->getOperand(1); + return true; + }; + + if (isa(Less) && isa(More)) { + const auto *LAR = cast(Less); + const auto *MAR = cast(More); + + if (LAR->getLoop() != MAR->getLoop()) + return false; + + // We look at affine expressions only; not for correctness but to keep + // getStepRecurrence cheap. + if (!LAR->isAffine() || !MAR->isAffine()) + return false; + + if (LAR->getStepRecurrence(SE) != MAR->getStepRecurrence(SE)) + return false; + + Less = LAR->getStart(); + More = MAR->getStart(); + + // fall through + } + + if (isa(Less) && isa(More)) + return (cast(More)->getValue()->getValue() - + cast(Less)->getValue()->getValue()) == 1; + + const SCEV *L, *R; + if (SplitBinaryAdd(Less, L, R)) + if (const auto *LC = dyn_cast(L)) + return LC->getValue()->getValue().isAllOnesValue() && R == More; + + if (SplitBinaryAdd(More, L, R)) + if (const auto *LC = dyn_cast(L)) + return LC->getValue()->isOne() && R == Less; + + return false; +} + +bool ScalarEvolution::isImpliedCondOperandsViaNoOverflow( + ICmpInst::Predicate Pred, const SCEV *LHS, const SCEV *RHS, + const SCEV *FoundLHS, const SCEV *FoundRHS) { + if (Pred != CmpInst::ICMP_SLT && Pred != CmpInst::ICMP_ULT) + return false; + + const auto *AddRecLHS = dyn_cast(LHS); + if (!AddRecLHS) + return false; + + const auto *AddRecFoundLHS = dyn_cast(FoundLHS); + if (!AddRecFoundLHS) + return false; + + // We'd like to let SCEV reason about control dependencies, so we constrain + // both the inequalities to be about add recurrences on the same loop. This + // way we can use isLoopEntryGuardedByCond later. + + const Loop *L = AddRecFoundLHS->getLoop(); + if (L != AddRecLHS->getLoop()) + return false; + + // If we can prove B + 1 does not signed / unsigned overflow, then we can + // prove + // + // A < B => (A + 1) < (B + 1) + // + // B + 1 does not signed / unsigned overflow iff B != INT_SMAX / B != -1. + // Alternatively, (B + 1) does not signed / unsigned overflow iff (B + 1) != + // INT_SMIN / (B + 1) != 0. + + if (!IsOneMore(*this, FoundLHS, LHS) || !IsOneMore(*this, FoundRHS, RHS)) + return false; + + if (Pred == CmpInst::ICMP_ULT) + return isLoopEntryGuardedByCond(L, CmpInst::ICMP_NE, RHS, + getZero(RHS->getType())); + + assert(Pred == CmpInst::ICMP_SLT && "Should be!"); + unsigned Width = cast(RHS->getType())->getBitWidth(); + return isLoopEntryGuardedByCond(L, CmpInst::ICMP_NE, RHS, + getConstant(APInt::getSignedMinValue(Width))); +} + /// isImpliedCondOperands - Test whether the condition described by Pred, /// LHS, and RHS is true whenever the condition described by Pred, FoundLHS, /// and FoundRHS is true. @@ -7301,6 +7398,9 @@ if (isImpliedCondOperandsViaRanges(Pred, LHS, RHS, FoundLHS, FoundRHS)) return true; + if (isImpliedCondOperandsViaNoOverflow(Pred, LHS, RHS, FoundLHS, FoundRHS)) + return true; + return isImpliedCondOperandsHelper(Pred, LHS, RHS, FoundLHS, FoundRHS) || // ~x < ~y --> x > y Index: test/Transforms/IndVarSimplify/eliminate-comparison.ll =================================================================== --- test/Transforms/IndVarSimplify/eliminate-comparison.ll +++ test/Transforms/IndVarSimplify/eliminate-comparison.ll @@ -209,3 +209,88 @@ unrolledend: ; preds = %forcond38 ret i32 0 } + +declare void @side_effect() + +define void @func_13(i32* %len.ptr) { +; CHECK-LABEL: @func_13( + entry: + %len = load i32, i32* %len.ptr, !range !0 + %len.sub.1 = add i32 %len, -1 + %len.is.zero = icmp eq i32 %len, 0 + br i1 %len.is.zero, label %leave, label %loop + + loop: +; CHECK: loop: + %iv = phi i32 [ 0, %entry ], [ %iv.inc, %be ] + call void @side_effect() + %iv.inc = add i32 %iv, 1 + %iv.cmp = icmp ult i32 %iv, %len + br i1 %iv.cmp, label %be, label %leave +; CHECK: br i1 true, label %be, label %leave + + be: + call void @side_effect() + %be.cond = icmp ult i32 %iv, %len.sub.1 + br i1 %be.cond, label %loop, label %leave + + leave: + ret void +} + +define void @func_14(i32* %len.ptr) { +; CHECK-LABEL: @func_14( + entry: + %len = load i32, i32* %len.ptr, !range !0 + %len.sub.1 = add i32 %len, -1 + %len.is.zero = icmp eq i32 %len, 0 + %len.is.int_min = icmp eq i32 %len, 2147483648 + %no.entry = or i1 %len.is.zero, %len.is.int_min + br i1 %no.entry, label %leave, label %loop + + loop: +; CHECK: loop: + %iv = phi i32 [ 0, %entry ], [ %iv.inc, %be ] + call void @side_effect() + %iv.inc = add i32 %iv, 1 + %iv.cmp = icmp slt i32 %iv, %len + br i1 %iv.cmp, label %be, label %leave +; CHECK: br i1 true, label %be, label %leave + + be: + call void @side_effect() + %be.cond = icmp slt i32 %iv, %len.sub.1 + br i1 %be.cond, label %loop, label %leave + + leave: + ret void +} + +define void @func_15(i32* %len.ptr) { +; CHECK-LABEL: @func_15( + entry: + %len = load i32, i32* %len.ptr, !range !0 + %len.add.1 = add i32 %len, 1 + %len.add.1.is.zero = icmp eq i32 %len.add.1, 0 + br i1 %len.add.1.is.zero, label %leave, label %loop + + loop: +; CHECK: loop: + %iv = phi i32 [ 0, %entry ], [ %iv.inc, %be ] + call void @side_effect() + %iv.inc = add i32 %iv, 1 + %iv.cmp = icmp ult i32 %iv, %len.add.1 + br i1 %iv.cmp, label %be, label %leave +; CHECK: br i1 true, label %be, label %leave + + be: + call void @side_effect() + %be.cond = icmp ult i32 %iv, %len + br i1 %be.cond, label %loop, label %leave + + leave: + ret void +} + +!0 = !{i32 0, i32 2147483647} +