[llvm] d41ee5c - [ConstraintElim] Add facts for sdiv with a positive divisor. (#225535)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 18:49:40 PDT 2026


Author: Florian Hahn
Date: 2026-09-24T01:49:32Z
New Revision: d41ee5c7c900f8c80026d8f477729b4956db7c3c

URL: https://github.com/llvm/llvm-project/commit/d41ee5c7c900f8c80026d8f477729b4956db7c3c
DIFF: https://github.com/llvm/llvm-project/commit/d41ee5c7c900f8c80026d8f477729b4956db7c3c.diff

LOG: [ConstraintElim] Add facts for sdiv with a positive divisor. (#225535)

Add signed bounds for `sdiv x, n`:

  * `x s>= 0` and `n s> 0`  =>  result s>= 0 and result s<= x
    https://alive2.llvm.org/ce/z/Tvb3Nq
  * `x s>  0` and `n s> 1`  => result s= 0 and result s< x
   https://alive2.llvm.org/ce/z/sL-XyG

PR: https://github.com/llvm/llvm-project/pull/225535

Added: 
    llvm/test/Transforms/ConstraintElimination/sdiv.ll

Modified: 
    llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
    llvm/test/Transforms/ConstraintElimination/srem.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 44af930ff410d..0d1dae4e667c0 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -415,6 +415,10 @@ class ConstraintInfo {
   /// signed system implies it or because ValueTracking can prove it.
   bool isKnownNonNegative(Value *V);
 
+  /// Returns true if \p V is known to be positive, either because the signed
+  /// system implies it or because ValueTracking can prove it.
+  bool isKnownPositive(Value *V);
+
   void addFact(CmpInst::Predicate Pred, Value *A, Value *B, unsigned NumIn,
                unsigned NumOut, SmallVectorImpl<StackEntry> &DFSInStack);
 
@@ -1024,6 +1028,13 @@ bool ConstraintInfo::isKnownNonNegative(Value *V) {
          doesHold(CmpInst::ICMP_SGE, V, ConstantInt::get(V->getType(), 0));
 }
 
+bool ConstraintInfo::isKnownPositive(Value *V) {
+  if (auto *CI = dyn_cast<ConstantInt>(V))
+    return CI->getValue().isStrictlyPositive();
+  return ::isKnownPositive(V, DL) ||
+         doesHold(CmpInst::ICMP_SGT, V, ConstantInt::get(V->getType(), 0));
+}
+
 void ConstraintInfo::transferToOtherSystem(
     CmpInst::Predicate Pred, Value *A, Value *B, unsigned NumIn,
     unsigned NumOut, SmallVectorImpl<StackEntry> &DFSInStack) {
@@ -1595,17 +1606,20 @@ void State::addInfoFor(BasicBlock &BB) {
     }
 
     // Add facts from unsigned division, remainder and logical shift right, and
-    // from signed remainder.
+    // from signed division and remainder.
     //   urem x, n: result < n  and  result <= x
     //   udiv x, n: result <= x
     //   lshr x, n: result <= x
     //   srem x, n: result >= 0 and result <= x, if x >= 0
     //              result < n,                  if n > 0
+    //   sdiv x, n: result >= 0 and result <= x, if x >= 0 and n > 0
+    //              result >= 0 and result < x,  if x > 0 and n > 1
     if (auto *BO = dyn_cast<BinaryOperator>(&I)) {
       if ((BO->getOpcode() == Instruction::URem ||
            BO->getOpcode() == Instruction::UDiv ||
            BO->getOpcode() == Instruction::LShr ||
-           BO->getOpcode() == Instruction::SRem) &&
+           BO->getOpcode() == Instruction::SRem ||
+           BO->getOpcode() == Instruction::SDiv) &&
           isGuaranteedNotToBePoison(BO))
         WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), BO));
     }
@@ -2594,6 +2608,19 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
           }
           continue;
         }
+        if (BO->getOpcode() == Instruction::SDiv) {
+          Value *X = BO->getOperand(0);
+          Value *N = BO->getOperand(1);
+          if (!Info.isKnownNonNegative(X) || !Info.isKnownPositive(N))
+            continue;
+
+          bool IsStrict = Info.isKnownPositive(X) &&
+                          Info.doesHold(CmpInst::ICMP_SGT, N,
+                                        ConstantInt::get(N->getType(), 1));
+          AddFact(CmpInst::ICMP_SGE, BO, Constant::getNullValue(BO->getType()));
+          AddFact(IsStrict ? CmpInst::ICMP_SLT : CmpInst::ICMP_SLE, BO, X);
+          continue;
+        }
       }
 
       auto &DL = F.getDataLayout();

diff  --git a/llvm/test/Transforms/ConstraintElimination/sdiv.ll b/llvm/test/Transforms/ConstraintElimination/sdiv.ll
new file mode 100644
index 0000000000000..4507f8363ddf7
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/sdiv.ll
@@ -0,0 +1,492 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+declare void @llvm.assume(i1)
+
+define i1 @sdiv_sge_zero(i32 noundef %x) {
+; CHECK-LABEL: define i1 @sdiv_sge_zero(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 4
+; CHECK-NEXT:    ret i1 true
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %q = sdiv i32 %x, 4
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+
+define i1 @sdiv_sle_dividend(i32 noundef %x) {
+; CHECK-LABEL: define i1 @sdiv_sle_dividend(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    ret i1 true
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %q = sdiv i32 %x, 2
+  %c = icmp sle i32 %q, %x
+  ret i1 %c
+}
+
+define i1 @sdiv_slt_dividend(i32 noundef %x) {
+; CHECK-LABEL: define i1 @sdiv_slt_dividend(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    ret i1 true
+;
+  %pos = icmp sgt i32 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %q = sdiv i32 %x, 2
+  %c = icmp slt i32 %q, %x
+  ret i1 %c
+}
+
+define i1 @sdiv_slt_dividend_bound(i32 noundef %x, i32 noundef %limit) {
+; CHECK-LABEL: define i1 @sdiv_slt_dividend_bound(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[LIMIT:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[LE:%.*]] = icmp sle i32 [[X]], [[LIMIT]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[LE]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 3
+; CHECK-NEXT:    ret i1 true
+;
+  %pos = icmp sgt i32 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %le = icmp sle i32 %x, %limit
+  call void @llvm.assume(i1 %le)
+  %q = sdiv i32 %x, 3
+  %c = icmp slt i32 %q, %limit
+  ret i1 %c
+}
+
+define i1 @sdiv_positive_divisor(i32 noundef %x, i32 noundef %n) {
+; CHECK-LABEL: define i1 @sdiv_positive_divisor(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[N]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], [[N]]
+; CHECK-NEXT:    ret i1 true
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %pos = icmp sgt i32 %n, 0
+  call void @llvm.assume(i1 %pos)
+  %q = sdiv i32 %x, %n
+  %c = icmp sle i32 %q, %x
+  ret i1 %c
+}
+
+define i1 @sdiv_slt_dividend_divisor_gt_one(i32 noundef %x, i32 noundef %n) {
+; CHECK-LABEL: define i1 @sdiv_slt_dividend_divisor_gt_one(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[GT1:%.*]] = icmp sgt i32 [[N]], 1
+; CHECK-NEXT:    call void @llvm.assume(i1 [[GT1]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], [[N]]
+; CHECK-NEXT:    ret i1 true
+;
+  %pos = icmp sgt i32 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %gt1 = icmp sgt i32 %n, 1
+  call void @llvm.assume(i1 %gt1)
+  %q = sdiv i32 %x, %n
+  %c = icmp slt i32 %q, %x
+  ret i1 %c
+}
+
+define i1 @sdiv_ult_dividend_via_transfer(i32 noundef %x) {
+; CHECK-LABEL: define i1 @sdiv_ult_dividend_via_transfer(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 4
+; CHECK-NEXT:    ret i1 true
+;
+  %pos = icmp sgt i32 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %q = sdiv i32 %x, 4
+  %c = icmp ult i32 %q, %x
+  ret i1 %c
+}
+
+define i1 @sdiv_nneg_dividend_from_valuetracking(i32 noundef %y) {
+; CHECK-LABEL: define i1 @sdiv_nneg_dividend_from_valuetracking(
+; CHECK-SAME: i32 noundef [[Y:%.*]]) {
+; CHECK-NEXT:    [[X:%.*]] = and i32 [[Y]], 255
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    ret i1 true
+;
+  %x = and i32 %y, 255
+  %q = sdiv i32 %x, 2
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+
+define i1 @sdiv_const_dividend() {
+; CHECK-LABEL: define i1 @sdiv_const_dividend() {
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 42, 4
+; CHECK-NEXT:    ret i1 true
+;
+  %q = sdiv i32 42, 4
+  %c = icmp slt i32 %q, 42
+  ret i1 %c
+}
+
+define i1 @sdiv_smax_divisor(i64 noundef %x, i64 noundef %limit) {
+; CHECK-LABEL: define i1 @sdiv_smax_divisor(
+; CHECK-SAME: i64 noundef [[X:%.*]], i64 noundef [[LIMIT:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i64 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[LE:%.*]] = icmp sle i64 [[X]], [[LIMIT]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[LE]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i64 [[X]], 9223372036854775807
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[Q]], [[LIMIT]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %pos = icmp sgt i64 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %le = icmp sle i64 %x, %limit
+  call void @llvm.assume(i1 %le)
+  %q = sdiv i64 %x, 9223372036854775807
+  %c = icmp slt i64 %q, %limit
+  ret i1 %c
+}
+
+; The binary-search midpoint `low + (high - low) / 2` is in [low, high), if
+; low s< high. The lower bound needs `(high - low) / 2 s>= 0`, the upper bound
+; the strict `(high - low) / 2 s< high - low`.
+define i1 @sdiv_binary_search_midpoint_lower(i32 noundef %low, i32 noundef %high) {
+; CHECK-LABEL: define i1 @sdiv_binary_search_midpoint_lower(
+; CHECK-SAME: i32 noundef [[LOW:%.*]], i32 noundef [[HIGH:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[LOW]], [[HIGH]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[D:%.*]] = sub nsw i32 [[HIGH]], [[LOW]]
+; CHECK-NEXT:    [[HALF:%.*]] = sdiv i32 [[D]], 2
+; CHECK-NEXT:    [[MID:%.*]] = add nsw i32 [[LOW]], [[HALF]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT]], label %[[TRAP:.*]]
+; CHECK:       [[TRAP]]:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i1 true
+;
+entry:
+  %cmp = icmp slt i32 %low, %high
+  br i1 %cmp, label %then, label %exit
+
+then:
+  %d = sub nsw i32 %high, %low
+  %half = sdiv i32 %d, 2
+  %mid = add nsw i32 %low, %half
+  %c = icmp sge i32 %mid, %low
+  br i1 %c, label %exit, label %trap
+
+trap:
+  ret i1 false
+
+exit:
+  ret i1 true
+}
+
+define i1 @sdiv_binary_search_midpoint_upper(i32 noundef %low, i32 noundef %high) {
+; CHECK-LABEL: define i1 @sdiv_binary_search_midpoint_upper(
+; CHECK-SAME: i32 noundef [[LOW:%.*]], i32 noundef [[HIGH:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[LOW]], [[HIGH]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[EXIT:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[D:%.*]] = sub nsw i32 [[HIGH]], [[LOW]]
+; CHECK-NEXT:    [[HALF:%.*]] = sdiv i32 [[D]], 2
+; CHECK-NEXT:    [[MID:%.*]] = add nsw i32 [[LOW]], [[HALF]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT]], label %[[TRAP:.*]]
+; CHECK:       [[TRAP]]:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i1 true
+;
+entry:
+  %cmp = icmp slt i32 %low, %high
+  br i1 %cmp, label %then, label %exit
+
+then:
+  %d = sub nsw i32 %high, %low
+  %half = sdiv i32 %d, 2
+  %mid = add nsw i32 %low, %half
+  %c = icmp slt i32 %mid, %high
+  br i1 %c, label %exit, label %trap
+
+trap:
+  ret i1 false
+
+exit:
+  ret i1 true
+}
+
+; The bounds on the quotient also strengthen the no-wrap flags of its users.
+define i64 @sdiv_add_nuw_from_quotient_bounds(i64 noundef %x) {
+; CHECK-LABEL: define i64 @sdiv_add_nuw_from_quotient_bounds(
+; CHECK-SAME: i64 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i64 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i64 [[X]], 2
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i64 [[DIV]], [[X]]
+; CHECK-NEXT:    ret i64 [[ADD]]
+;
+  %nneg = icmp sge i64 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %div = sdiv i64 %x, 2
+  %add = add nsw i64 %div, %x
+  ret i64 %add
+}
+
+define i32 @sdiv_sub_nuw_from_quotient_bounds(i32 noundef %x) {
+; CHECK-LABEL: define i32 @sdiv_sub_nuw_from_quotient_bounds(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw nsw i32 [[X]], [[DIV]]
+; CHECK-NEXT:    ret i32 [[SUB]]
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %div = sdiv i32 %x, 2
+  %sub = sub nsw i32 %x, %div
+  ret i32 %sub
+}
+
+define <2 x i1> @sdiv_vector(<2 x i32> noundef %y) {
+; CHECK-LABEL: define <2 x i1> @sdiv_vector(
+; CHECK-SAME: <2 x i32> noundef [[Y:%.*]]) {
+; CHECK-NEXT:    [[X:%.*]] = and <2 x i32> [[Y]], splat (i32 255)
+; CHECK-NEXT:    [[Q:%.*]] = sdiv <2 x i32> [[X]], splat (i32 2)
+; CHECK-NEXT:    ret <2 x i1> splat (i1 true)
+;
+  %x = and <2 x i32> %y, splat (i32 255)
+  %q = sdiv <2 x i32> %x, splat (i32 2)
+  %c = icmp sge <2 x i32> %q, zeroinitializer
+  ret <2 x i1> %c
+}
+
+; Negative tests: folding any of the conditions below would be incorrect.
+
+; sdiv x, -2 with x == 2 is -1, which is not non-negative.
+define i1 @neg_sdiv_negative_divisor(i32 noundef %x) {
+; CHECK-LABEL: define i1 @neg_sdiv_negative_divisor(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], -2
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[Q]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %q = sdiv i32 %x, -2
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+
+; sdiv x, 1 is x, so the strict bound does not hold.
+define i1 @neg_sdiv_divisor_one(i32 noundef %x) {
+; CHECK-LABEL: define i1 @neg_sdiv_divisor_one(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 1
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[Q]], [[X]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %pos = icmp sgt i32 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %q = sdiv i32 %x, 1
+  %c = icmp slt i32 %q, %x
+  ret i1 %c
+}
+
+define i1 @neg_sdiv_dividend_sign_unknown(i32 noundef %x) {
+; CHECK-LABEL: define i1 @neg_sdiv_dividend_sign_unknown(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[Q]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %q = sdiv i32 %x, 2
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+
+; sdiv x, 2 with x == -4 is -2, which is not s<= -4.
+define i1 @neg_sdiv_negative_dividend(i32 noundef %x) {
+; CHECK-LABEL: define i1 @neg_sdiv_negative_dividend(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NEG:%.*]] = icmp slt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    [[C:%.*]] = icmp sle i32 [[Q]], [[X]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %neg = icmp slt i32 %x, 0
+  call void @llvm.assume(i1 %neg)
+  %q = sdiv i32 %x, 2
+  %c = icmp sle i32 %q, %x
+  ret i1 %c
+}
+
+; sdiv x, 2 with x == 0 is 0, which is not s< 0: the strict bound needs a
+; strictly positive dividend, not just a non-negative one.
+define i1 @neg_sdiv_strict_needs_positive_dividend(i32 noundef %x) {
+; CHECK-LABEL: define i1 @neg_sdiv_strict_needs_positive_dividend(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[Q]], [[X]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %q = sdiv i32 %x, 2
+  %c = icmp slt i32 %q, %x
+  ret i1 %c
+}
+
+; The divisor may be negative, and sdiv 2, -2 is -1, which is not non-negative.
+define i1 @neg_sdiv_divisor_sign_unknown(i32 noundef %x, i32 noundef %n) {
+; CHECK-LABEL: define i1 @neg_sdiv_divisor_sign_unknown(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], [[N]]
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[Q]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %q = sdiv i32 %x, %n
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+
+; The divisor may be 1, in which case the quotient is the dividend.
+define i1 @neg_sdiv_strict_needs_divisor_gt_one(i32 noundef %x, i32 noundef %n) {
+; CHECK-LABEL: define i1 @neg_sdiv_strict_needs_divisor_gt_one(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[NPOS:%.*]] = icmp sgt i32 [[N]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NPOS]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], [[N]]
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i32 [[Q]], [[X]]
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %pos = icmp sgt i32 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %npos = icmp sgt i32 %n, 0
+  call void @llvm.assume(i1 %npos)
+  %q = sdiv i32 %x, %n
+  %c = icmp slt i32 %q, %x
+  ret i1 %c
+}
+
+; A positive dividend and a divisor s> 1 do not imply a positive result
+define i1 @neg_sdiv_strict_quotient_may_be_zero(i32 noundef %x, i32 noundef %n) {
+; CHECK-LABEL: define i1 @neg_sdiv_strict_quotient_may_be_zero(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]]) {
+; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[POS]])
+; CHECK-NEXT:    [[GT1:%.*]] = icmp sgt i32 [[N]], 1
+; CHECK-NEXT:    call void @llvm.assume(i1 [[GT1]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], [[N]]
+; CHECK-NEXT:    [[C:%.*]] = icmp sgt i32 [[Q]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %pos = icmp sgt i32 %x, 0
+  call void @llvm.assume(i1 %pos)
+  %gt1 = icmp sgt i32 %n, 1
+  call void @llvm.assume(i1 %gt1)
+  %q = sdiv i32 %x, %n
+  %c = icmp sgt i32 %q, 0
+  ret i1 %c
+}
+
+; The second lane divides by -2, so the quotient is not non-negative there.
+define <2 x i1> @neg_sdiv_vector_negative_lane(<2 x i32> noundef %y) {
+; CHECK-LABEL: define <2 x i1> @neg_sdiv_vector_negative_lane(
+; CHECK-SAME: <2 x i32> noundef [[Y:%.*]]) {
+; CHECK-NEXT:    [[X:%.*]] = and <2 x i32> [[Y]], splat (i32 255)
+; CHECK-NEXT:    [[Q:%.*]] = sdiv <2 x i32> [[X]], <i32 2, i32 -2>
+; CHECK-NEXT:    [[C:%.*]] = icmp sge <2 x i32> [[Q]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[C]]
+;
+  %x = and <2 x i32> %y, splat (i32 255)
+  %q = sdiv <2 x i32> %x, <i32 2, i32 -2>
+  %c = icmp sge <2 x i32> %q, zeroinitializer
+  ret <2 x i1> %c
+}
+
+; Negative tests for cases that are not handled, where folding would be legal.
+
+; Division by zero is immediate UB.
+define i1 @neg_sdiv_divisor_zero(i32 noundef %x) {
+; CHECK-LABEL: define i1 @neg_sdiv_divisor_zero(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 0
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[Q]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %q = sdiv i32 %x, 0
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+
+; An `exact` division is poison unless the dividend is a multiple of the
+; divisor, so no fact is recorded for it.
+define i1 @neg_sdiv_exact(i32 noundef %x) {
+; CHECK-LABEL: define i1 @neg_sdiv_exact(
+; CHECK-SAME: i32 noundef [[X:%.*]]) {
+; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
+; CHECK-NEXT:    [[Q:%.*]] = sdiv exact i32 [[X]], 4
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[Q]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %nneg = icmp sge i32 %x, 0
+  call void @llvm.assume(i1 %nneg)
+  %q = sdiv exact i32 %x, 4
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+
+; Same as @sdiv_nneg_dividend_from_valuetracking, but the dividend may be
+; poison.
+define i1 @neg_sdiv_no_noundef(i32 %y) {
+; CHECK-LABEL: define i1 @neg_sdiv_no_noundef(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[X:%.*]] = and i32 [[Y]], 255
+; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], 2
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i32 [[Q]], 0
+; CHECK-NEXT:    ret i1 [[C]]
+;
+  %x = and i32 %y, 255
+  %q = sdiv i32 %x, 2
+  %c = icmp sge i32 %q, 0
+  ret i1 %c
+}
+

diff  --git a/llvm/test/Transforms/ConstraintElimination/srem.ll b/llvm/test/Transforms/ConstraintElimination/srem.ll
index 3540b0f5978dc..85ae19d32ee21 100644
--- a/llvm/test/Transforms/ConstraintElimination/srem.ll
+++ b/llvm/test/Transforms/ConstraintElimination/srem.ll
@@ -268,23 +268,3 @@ define i1 @neg_srem_wrong_direction(i32 noundef %x, i32 noundef %n, i32 noundef
   %c = icmp slt i32 %r, %m
   ret i1 %c
 }
-
-define i1 @neg_sdiv_not_handled(i32 noundef %x, i32 noundef %n, i32 noundef %limit) {
-; CHECK-LABEL: define i1 @neg_sdiv_not_handled(
-; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[LIMIT:%.*]]) {
-; CHECK-NEXT:    [[NNEG:%.*]] = icmp sge i32 [[X]], 0
-; CHECK-NEXT:    call void @llvm.assume(i1 [[NNEG]])
-; CHECK-NEXT:    [[LE:%.*]] = icmp sle i32 [[X]], [[LIMIT]]
-; CHECK-NEXT:    call void @llvm.assume(i1 [[LE]])
-; CHECK-NEXT:    [[Q:%.*]] = sdiv i32 [[X]], [[N]]
-; CHECK-NEXT:    [[C:%.*]] = icmp sle i32 [[Q]], [[LIMIT]]
-; CHECK-NEXT:    ret i1 [[C]]
-;
-  %nneg = icmp sge i32 %x, 0
-  call void @llvm.assume(i1 %nneg)
-  %le = icmp sle i32 %x, %limit
-  call void @llvm.assume(i1 %le)
-  %q = sdiv i32 %x, %n
-  %c = icmp sle i32 %q, %limit
-  ret i1 %c
-}


        


More information about the llvm-commits mailing list