[llvm] [ConstraintElim] Add facts for SREM. (PR #213453)

via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 1 08:01:37 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

<details>
<summary>Changes</summary>

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/hS3itY

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/213453.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+25-2) 
- (modified) llvm/test/Transforms/ConstraintElimination/srem.ll (+7-14) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index efe1920486df6..422283849a863 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,25 @@ 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_SGT, 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/srem.ll b/llvm/test/Transforms/ConstraintElimination/srem.ll
index 3a53a8ffba1f8..dbdfeb9ca84d7 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)

``````````

</details>


https://github.com/llvm/llvm-project/pull/213453


More information about the llvm-commits mailing list