[llvm] ab07cbe - [SCEV] Support sub in and negative constants willNotOverflow
Max Kazantsev via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 25 03:08:29 PDT 2023
Author: Max Kazantsev
Date: 2023-04-25T16:40:37+07:00
New Revision: ab07cbe4377a1c5aca3693fd01c347eea7ac6988
URL: https://github.com/llvm/llvm-project/commit/ab07cbe4377a1c5aca3693fd01c347eea7ac6988
DIFF: https://github.com/llvm/llvm-project/commit/ab07cbe4377a1c5aca3693fd01c347eea7ac6988.diff
LOG: [SCEV] Support sub in and negative constants willNotOverflow
This lifts two TODOs from this function, allowing us to prove
no-overflow whether it happens through max int (up) or through
min int (down) for both and and sub.
Differential Revision: https://reviews.llvm.org/D148618
Reviewed By: dmakogon
Added:
Modified:
llvm/lib/Analysis/ScalarEvolution.cpp
llvm/test/Transforms/IndVarSimplify/predicated_ranges.ll
Removed:
################################################################################
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index f12013f4ff620..6a3e91a0e3249 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -2340,25 +2340,42 @@ bool ScalarEvolution::willNotOverflow(Instruction::BinaryOps BinOp, bool Signed,
// Can we use context to prove the fact we need?
if (!CtxI)
return false;
- // We can prove that add(x, constant) doesn't wrap if isKnownPredicateAt can
- // guarantee that x <= max_int - constant at the given context.
- // TODO: Support other operations.
- if (BinOp != Instruction::Add)
+ // TODO: Support mul.
+ if (BinOp == Instruction::Mul)
return false;
auto *RHSC = dyn_cast<SCEVConstant>(RHS);
// TODO: Lift this limitation.
if (!RHSC)
return false;
APInt C = RHSC->getAPInt();
- // TODO: Also lift this limitation.
- if (Signed && C.isNegative())
- return false;
unsigned NumBits = C.getBitWidth();
- APInt Max =
- Signed ? APInt::getSignedMaxValue(NumBits) : APInt::getMaxValue(NumBits);
- APInt Limit = Max - C;
+ bool IsSub = (BinOp == Instruction::Sub);
+ bool IsNegativeConst = (Signed && C.isNegative());
+ // Compute the direction and magnitude by which we need to check overflow.
+ bool OverflowDown = IsSub ^ IsNegativeConst;
+ APInt Magnitude = C;
+ if (IsNegativeConst) {
+ if (C == APInt::getSignedMinValue(NumBits))
+ // TODO: SINT_MIN on inversion gives the same negative value, we don't
+ // want to deal with that.
+ return false;
+ Magnitude = -C;
+ }
+
ICmpInst::Predicate Pred = Signed ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE;
- return isKnownPredicateAt(Pred, LHS, getConstant(Limit), CtxI);
+ if (OverflowDown) {
+ // To avoid overflow down, we need to make sure that MIN + Magnitude <= LHS.
+ APInt Min = Signed ? APInt::getSignedMinValue(NumBits)
+ : APInt::getMinValue(NumBits);
+ APInt Limit = Min + Magnitude;
+ return isKnownPredicateAt(Pred, getConstant(Limit), LHS, CtxI);
+ } else {
+ // To avoid overflow up, we need to make sure that LHS <= MAX - Magnitude.
+ APInt Max = Signed ? APInt::getSignedMaxValue(NumBits)
+ : APInt::getMaxValue(NumBits);
+ APInt Limit = Max - Magnitude;
+ return isKnownPredicateAt(Pred, LHS, getConstant(Limit), CtxI);
+ }
}
std::optional<SCEV::NoWrapFlags>
diff --git a/llvm/test/Transforms/IndVarSimplify/predicated_ranges.ll b/llvm/test/Transforms/IndVarSimplify/predicated_ranges.ll
index 208a587171a96..d005804ff633e 100644
--- a/llvm/test/Transforms/IndVarSimplify/predicated_ranges.ll
+++ b/llvm/test/Transforms/IndVarSimplify/predicated_ranges.ll
@@ -19,7 +19,7 @@ define void @test_predicated_simple_unsigned(ptr %p, ptr %arr) {
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw nsw i32 [[IV]], 1
; CHECK-NEXT: br i1 true, label [[BACKEDGE]], label [[FAIL:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[P]], i32 [[IV]]
@@ -68,7 +68,7 @@ define void @test_predicated_simple_signed(ptr %p, ptr %arr) {
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw nsw i32 [[IV]], 1
; CHECK-NEXT: br i1 true, label [[BACKEDGE]], label [[FAIL:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[P]], i32 [[IV]]
@@ -477,7 +477,7 @@ define void @test_can_predicate_simple_unsigned(ptr %p, ptr %arr) {
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i32 [[IV]], 1
; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[BACKEDGE]], label [[FAIL:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[P]], i32 [[IV]]
@@ -528,7 +528,7 @@ define void @test_can_predicate_simple_unsigned_inverted(ptr %p, ptr %arr) {
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i32 [[IV]], 1
; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[FAIL:%.*]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[P]], i32 [[IV]]
@@ -578,7 +578,7 @@ define void @test_can_predicate_simple_signed(ptr %p, ptr %arr) {
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i32 [[IV]], 1
; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp slt i32 [[IV_NEXT]], [[LEN]]
; CHECK-NEXT: br i1 [[RANGE_CHECK]], label [[BACKEDGE]], label [[FAIL:%.*]]
; CHECK: backedge:
@@ -631,7 +631,7 @@ define void @test_can_predicate_trunc_unsigned(ptr %p, ptr %arr) {
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i64 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub nsw i64 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw nsw i64 [[IV]], 1
; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[BACKEDGE]], label [[FAIL:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[ARR:%.*]], i64 [[IV]]
@@ -685,7 +685,7 @@ define void @test_can_predicate_trunc_unsigned_inverted(ptr %p, ptr %arr) {
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i64 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub nsw i64 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw nsw i64 [[IV]], 1
; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[FAIL:%.*]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[ARR:%.*]], i64 [[IV]]
@@ -736,7 +736,7 @@ define void @test_can_predicate_trunc_unsigned_
diff erent_start_and_len(ptr %p, i
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i64 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i64 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i64 [[IV]], 1
; CHECK-NEXT: [[NARROW:%.*]] = trunc i64 [[IV_NEXT]] to i32
; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp ult i32 [[NARROW]], [[LEN]]
; CHECK-NEXT: br i1 [[RANGE_CHECK]], label [[BACKEDGE]], label [[FAIL:%.*]]
@@ -788,7 +788,7 @@ define void @test_can_predicate_trunc_unsigned_
diff erent_start_and_len_inverted(
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i64 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i64 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i64 [[IV]], 1
; CHECK-NEXT: [[NARROW:%.*]] = trunc i64 [[IV_NEXT]] to i32
; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp uge i32 [[NARROW]], [[LEN]]
; CHECK-NEXT: br i1 [[RANGE_CHECK]], label [[FAIL:%.*]], label [[BACKEDGE]]
@@ -842,7 +842,7 @@ define void @test_can_predicate_simple_unsigned_
diff erent_start(i32 %start, ptr
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i32 [[IV]], 1
; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[BACKEDGE]], label [[FAIL:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[P]], i32 [[IV]]
@@ -893,7 +893,7 @@ define void @test_can_predicate_simple_unsigned_inverted_
diff erent_start(i32 %st
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i32 [[IV]], 1
; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[FAIL:%.*]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[EL_PTR:%.*]] = getelementptr i32, ptr [[P]], i32 [[IV]]
@@ -943,7 +943,7 @@ define void @test_can_predicate_simple_signed_
diff erent_start(i32 %start, ptr %p
; CHECK-NEXT: [[ZERO_COND:%.*]] = icmp eq i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_COND]], label [[EXIT:%.*]], label [[RANGE_CHECK_BLOCK:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_NEXT]] = sub i32 [[IV]], 1
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw i32 [[IV]], 1
; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp slt i32 [[IV_NEXT]], [[LEN]]
; CHECK-NEXT: br i1 [[RANGE_CHECK]], label [[BACKEDGE]], label [[FAIL:%.*]]
; CHECK: backedge:
More information about the llvm-commits
mailing list