[llvm] 6c83817 - [ConstraintElim] Add facts for SREM. (#213453)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 2 02:42:33 PDT 2026
Author: Florian Hahn
Date: 2026-08-02T09:42:28Z
New Revision: 6c838170ac5d612902dcf9858aed2f26a7fbd8e2
URL: https://github.com/llvm/llvm-project/commit/6c838170ac5d612902dcf9858aed2f26a7fbd8e2
DIFF: https://github.com/llvm/llvm-project/commit/6c838170ac5d612902dcf9858aed2f26a7fbd8e2.diff
LOG: [ConstraintElim] Add facts for SREM. (#213453)
Add facts for SRem, if operands are known to be non-negative.
Add signed bounds for `srem x, n`:
* `x s>= 0` => result s>= 0 and result s<= x
* `n s> 0` => result s<= n
Alive2 Proofs: https://alive2.llvm.org/ce/z/e-zoAP
Compile-time is in the noise
https://llvm-compile-time-tracker.com/compare.php?from=60f965b1f62c0c77bcdb2997ea9bb6603aa0d002&to=ebc652af5700d569884e27812d33735d604990a1&stat=instructions:u
InstCombine already has a similar fold, but with more limited reasoning.
It does not trigger any changes on
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/841
It simplifies a few times on other C/C++ workloads, including ffmpeg and
OpenColorIO.
Extracted end-to-end examples simplified with the change:
https://clang.godbolt.org/z/1co6rvjKs
This is part of an effort to improve ConstraintElimination support for
IR generated by the Swift compiler, where such patterns are more common
due to a number of signed runtime checks.
PR: https://github.com/llvm/llvm-project/pull/213453
Added:
Modified:
llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
llvm/test/Transforms/ConstraintElimination/ne-tightening.ll
llvm/test/Transforms/ConstraintElimination/srem.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 8b7018f6c8433..0705c97a9455f 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1287,14 +1287,18 @@ void State::addInfoFor(BasicBlock &BB) {
break;
}
- // Add facts from unsigned division, remainder and logical shift right.
+ // Add facts from unsigned division, remainder and logical shift right, and
+ // from signed 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
if (auto *BO = dyn_cast<BinaryOperator>(&I)) {
if ((BO->getOpcode() == Instruction::URem ||
BO->getOpcode() == Instruction::UDiv ||
- BO->getOpcode() == Instruction::LShr) &&
+ BO->getOpcode() == Instruction::LShr ||
+ BO->getOpcode() == Instruction::SRem) &&
isGuaranteedNotToBePoison(BO))
WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), BO));
}
@@ -2176,6 +2180,26 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
AddFact(CmpInst::ICMP_ULE, BO, BO->getOperand(0));
continue;
}
+ if (BO->getOpcode() == Instruction::SRem) {
+ Value *X = BO->getOperand(0);
+ Value *N = BO->getOperand(1);
+ Constant *Zero = Constant::getNullValue(BO->getType());
+ if (Info.doesHold(CmpInst::ICMP_SGE, X, Zero) ||
+ isKnownNonNegative(X, F.getDataLayout())) {
+ // srem x, n: result >= 0, if x >= 0 (result has the sign of x)
+ AddFact(CmpInst::ICMP_SGE, BO, Zero);
+ // srem x, n: result <= x, if x >= 0 (|result| <= |x| and both are
+ // non-negative)
+ AddFact(CmpInst::ICMP_SLE, BO, X);
+ }
+ if (Info.doesHold(CmpInst::ICMP_SGE, N, Zero) ||
+ isKnownPositive(N, F.getDataLayout())) {
+ // srem x, n: result <= n, if n >= 0 (|result| < n, so result <= n -
+ // 1
+ AddFact(CmpInst::ICMP_SLT, BO, N);
+ }
+ continue;
+ }
}
auto &DL = F.getDataLayout();
diff --git a/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll b/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll
index af2b5b2b9deec..2175b73d37d22 100644
--- a/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll
+++ b/llvm/test/Transforms/ConstraintElimination/ne-tightening.ll
@@ -288,8 +288,7 @@ define i1 @tightened_bound_enables_srem_fact(i64 noundef %x, i64 noundef %n) {
; CHECK-NEXT: [[B:%.*]] = icmp ne i64 [[N]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[B]])
; CHECK-NEXT: [[R:%.*]] = srem i64 [[X]], [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[R]], [[N]]
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%a = icmp sge i64 %n, 0
call void @llvm.assume(i1 %a)
diff --git a/llvm/test/Transforms/ConstraintElimination/srem.ll b/llvm/test/Transforms/ConstraintElimination/srem.ll
index 3a53a8ffba1f8..3540b0f5978dc 100644
--- a/llvm/test/Transforms/ConstraintElimination/srem.ll
+++ b/llvm/test/Transforms/ConstraintElimination/srem.ll
@@ -9,8 +9,7 @@ define i1 @srem_sge_zero(i32 noundef %x, i32 noundef %n) {
; CHECK-NEXT: [[CMP:%.*]] = icmp sge i32 [[X]], 0
; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp sge i32 [[R]], 0
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%cmp = icmp sge i32 %x, 0
call void @llvm.assume(i1 %cmp)
@@ -27,8 +26,7 @@ define i1 @srem_slt_divisor_bound(i32 noundef %x, i32 noundef %n, i32 noundef %m
; CHECK-NEXT: [[LE:%.*]] = icmp sle i32 [[N]], [[M]]
; CHECK-NEXT: call void @llvm.assume(i1 [[LE]])
; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], [[M]]
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%pos = icmp sgt i32 %n, 0
call void @llvm.assume(i1 %pos)
@@ -47,8 +45,7 @@ define i1 @srem_sle_dividend_bound(i32 noundef %x, i32 noundef %n, i32 noundef %
; CHECK-NEXT: [[LE:%.*]] = icmp sle i32 [[X]], [[LIMIT]]
; CHECK-NEXT: call void @llvm.assume(i1 [[LE]])
; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp sle i32 [[R]], [[LIMIT]]
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%nneg = icmp sge i32 %x, 0
call void @llvm.assume(i1 %nneg)
@@ -69,8 +66,7 @@ define i1 @srem_ult_divisor_bound_via_transfer(i32 noundef %x, i32 noundef %n, i
; CHECK-NEXT: [[LE:%.*]] = icmp sle i32 [[N]], [[M]]
; CHECK-NEXT: call void @llvm.assume(i1 [[LE]])
; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[R]], [[M]]
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%nneg = icmp sge i32 %x, 0
call void @llvm.assume(i1 %nneg)
@@ -87,8 +83,7 @@ define i1 @srem_const_dividend(i32 noundef %n) {
; CHECK-LABEL: define i1 @srem_const_dividend(
; CHECK-SAME: i32 noundef [[N:%.*]]) {
; CHECK-NEXT: [[R:%.*]] = srem i32 42, [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp sge i32 [[R]], 0
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%r = srem i32 42, %n
%c = icmp sge i32 %r, 0
@@ -99,8 +94,7 @@ define i1 @srem_const_divisor(i32 noundef %x) {
; CHECK-LABEL: define i1 @srem_const_divisor(
; CHECK-SAME: i32 noundef [[X:%.*]]) {
; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 10
-; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 10
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%r = srem i32 %x, 10
%c = icmp slt i32 %r, 10
@@ -115,8 +109,7 @@ define i1 @srem_i64(i64 noundef %x, i64 noundef %n, i64 noundef %m) {
; CHECK-NEXT: [[LE:%.*]] = icmp sle i64 [[N]], [[M]]
; CHECK-NEXT: call void @llvm.assume(i1 [[LE]])
; CHECK-NEXT: [[R:%.*]] = srem i64 [[X]], [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[R]], [[M]]
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%pos = icmp sgt i64 %n, 0
call void @llvm.assume(i1 %pos)
@@ -183,8 +176,7 @@ define i1 @neg_srem_divisor_may_be_zero(i32 noundef %x, i32 noundef %n, i32 noun
; CHECK-NEXT: [[LE:%.*]] = icmp sle i32 [[N]], [[M]]
; CHECK-NEXT: call void @llvm.assume(i1 [[LE]])
; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], [[N]]
-; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], [[M]]
-; CHECK-NEXT: ret i1 [[C]]
+; CHECK-NEXT: ret i1 true
;
%nneg = icmp sge i32 %n, 0
call void @llvm.assume(i1 %nneg)
More information about the llvm-commits
mailing list