[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