[llvm] [InstCombine] Fold srem comparisons with the dividend into range checks (PR #215096)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 11 01:58:06 PDT 2026


https://github.com/jihwan38 updated https://github.com/llvm/llvm-project/pull/215096

>From 7dacaecf7a59fc728aedeb35d78c0ca3c5e727ff Mon Sep 17 00:00:00 2001
From: jihwan38 <sss203800 at gmail.com>
Date: Sun, 9 Aug 2026 10:04:01 +0900
Subject: [PATCH 1/3] [InstCombine] Add tests for srem compared with its
 dividend

Add baseline coverage for equality comparisons between a one-use signed
remainder and its dividend. Cover commuted operands, vector splats,
boundary constants, and cases that should not fold.
---
 llvm/test/Transforms/InstCombine/icmp-srem.ll | 124 ++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp-srem.ll b/llvm/test/Transforms/InstCombine/icmp-srem.ll
index e4838131713e8..0cacc596cdd13 100644
--- a/llvm/test/Transforms/InstCombine/icmp-srem.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-srem.ll
@@ -397,3 +397,127 @@ define i1 @icmp_ult_sremsmax_smax(i32 %x) {
   %c = icmp ult i32 %r, 2147483647
   ret i1 %c
 }
+
+define i1 @icmp_eq_srem_dividend(i64 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_dividend(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[REM]], [[X]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i64 %x, 250
+  %cmp = icmp eq i64 %rem, %x
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_dividend_srem(i64 %x) {
+; CHECK-LABEL: define i1 @icmp_ne_dividend_srem(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[X]], [[REM]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i64 %x, 250
+  %cmp = icmp ne i64 %x, %rem
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_srem_dividend_smax(i8 %x) {
+; CHECK-LABEL: define i1 @icmp_ne_srem_dividend_smax(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X]], 127
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[REM]], [[X]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i8 %x, 127
+  %cmp = icmp ne i8 %rem, %x
+  ret i1 %cmp
+}
+
+define <2 x i1> @icmp_eq_srem_dividend_vec(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_srem_dividend_vec(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem <2 x i8> [[X]], splat (i8 5)
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[REM]], [[X]]
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %rem = srem <2 x i8> %x, <i8 5, i8 5>
+  %cmp = icmp eq <2 x i8> %rem, %x
+  ret <2 x i1> %cmp
+}
+
+define <2 x i1> @icmp_eq_srem_dividend_vec_nonsplat(<2 x i8> %x) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_srem_dividend_vec_nonsplat(
+; CHECK-SAME: <2 x i8> [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem <2 x i8> [[X]], <i8 5, i8 7>
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[REM]], [[X]]
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %rem = srem <2 x i8> %x, <i8 5, i8 7>
+  %cmp = icmp eq <2 x i8> %rem, %x
+  ret <2 x i1> %cmp
+}
+
+define i64 @icmp_ne_srem_dividend_multi_use(i64 %x) {
+; CHECK-LABEL: define i64 @icmp_ne_srem_dividend_multi_use(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[REM]], [[X]]
+; CHECK-NEXT:    [[EXT:%.*]] = zext i1 [[CMP]] to i64
+; CHECK-NEXT:    [[RES:%.*]] = add nsw i64 [[REM]], [[EXT]]
+; CHECK-NEXT:    ret i64 [[RES]]
+;
+  %rem = srem i64 %x, 250
+  %cmp = icmp ne i64 %rem, %x
+  %ext = zext i1 %cmp to i64
+  %res = add i64 %rem, %ext
+  ret i64 %res
+}
+
+define i1 @icmp_eq_srem_dividend_negative_divisor_canonicalized(i64 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_dividend_negative_divisor_canonicalized(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[REM]], [[X]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i64 %x, -250
+  %cmp = icmp eq i64 %rem, %x
+  ret i1 %cmp
+}
+
+define i1 @icmp_eq_srem_dividend_signed_min_divisor(i8 %x) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_dividend_signed_min_divisor(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X]], -128
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[REM]], [[X]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i8 %x, -128
+  %cmp = icmp eq i8 %rem, %x
+  ret i1 %cmp
+}
+
+define i1 @icmp_ne_srem_dividend_variable_divisor(i64 %x, i64 %divisor) {
+; CHECK-LABEL: define i1 @icmp_ne_srem_dividend_variable_divisor(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[DIVISOR:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], [[DIVISOR]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[REM]], [[X]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i64 %x, %divisor
+  %cmp = icmp ne i64 %rem, %x
+  ret i1 %cmp
+}
+
+define i1 @icmp_eq_srem_different_dividend(i64 %x, i64 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_srem_different_dividend(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]]) {
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[REM]], [[Y]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i64 %x, 250
+  %cmp = icmp eq i64 %rem, %y
+  ret i1 %cmp
+}

>From e9d0f391438a65270451b1b61f29896a2b50abe9 Mon Sep 17 00:00:00 2001
From: jihwan38 <sss203800 at gmail.com>
Date: Sun, 9 Aug 2026 10:07:14 +0900
Subject: [PATCH 2/3] [InstCombine] Fold srem compared with its dividend

For a strictly positive constant divisor C, srem X, C is equal to X
exactly when X is in the signed interval [1-C, C). Replace one-use
eq/ne comparisons with an offset range test so the srem becomes dead.
---
 .../InstCombine/InstCombineCompares.cpp       | 14 +++++++++++++
 llvm/test/Transforms/InstCombine/icmp-srem.ll | 20 +++++++++----------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index cc79f67e9f32f..61e78c5f654f6 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7759,6 +7759,20 @@ Instruction *InstCombinerImpl::foldICmpCommutative(CmpPredicate Pred,
       return foldICmpAddOpConst(X, *C, Pred);
   }
 
+  {
+    const APInt *C;
+    // (X s% C) == X --> X in [1-C, C)
+    // (X s% C) != X --> X outside [1-C, C)
+    if (ICmpInst::isEquality(Pred) &&
+        match(Op0, m_OneUse(m_SRem(m_Specific(Op1), m_APInt(C)))) &&
+        C->isStrictlyPositive()) {
+      APInt Lo = -*C + 1;
+      return replaceInstUsesWith(
+          CxtI, insertRangeTest(Op1, Lo, *C, /*isSigned=*/true,
+                                /*Inside=*/Pred == ICmpInst::ICMP_EQ));
+    }
+  }
+
   // abs(X) >=  X --> true
   // abs(X) u<= X --> true
   // abs(X) <   X --> false
diff --git a/llvm/test/Transforms/InstCombine/icmp-srem.ll b/llvm/test/Transforms/InstCombine/icmp-srem.ll
index 0cacc596cdd13..a1f0cbecc82b4 100644
--- a/llvm/test/Transforms/InstCombine/icmp-srem.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-srem.ll
@@ -401,8 +401,8 @@ define i1 @icmp_ult_sremsmax_smax(i32 %x) {
 define i1 @icmp_eq_srem_dividend(i64 %x) {
 ; CHECK-LABEL: define i1 @icmp_eq_srem_dividend(
 ; CHECK-SAME: i64 [[X:%.*]]) {
-; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[REM]], [[X]]
+; CHECK-NEXT:    [[X_OFF:%.*]] = add i64 [[X]], 249
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[X_OFF]], 499
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i64 %x, 250
@@ -413,8 +413,8 @@ define i1 @icmp_eq_srem_dividend(i64 %x) {
 define i1 @icmp_ne_dividend_srem(i64 %x) {
 ; CHECK-LABEL: define i1 @icmp_ne_dividend_srem(
 ; CHECK-SAME: i64 [[X:%.*]]) {
-; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[X]], [[REM]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add i64 [[X]], -250
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[TMP1]], -499
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i64 %x, 250
@@ -425,8 +425,8 @@ define i1 @icmp_ne_dividend_srem(i64 %x) {
 define i1 @icmp_ne_srem_dividend_smax(i8 %x) {
 ; CHECK-LABEL: define i1 @icmp_ne_srem_dividend_smax(
 ; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X]], 127
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[REM]], [[X]]
+; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[X]], -127
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[TMP1]], 3
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i8 %x, 127
@@ -437,8 +437,8 @@ define i1 @icmp_ne_srem_dividend_smax(i8 %x) {
 define <2 x i1> @icmp_eq_srem_dividend_vec(<2 x i8> %x) {
 ; CHECK-LABEL: define <2 x i1> @icmp_eq_srem_dividend_vec(
 ; CHECK-SAME: <2 x i8> [[X:%.*]]) {
-; CHECK-NEXT:    [[REM:%.*]] = srem <2 x i8> [[X]], splat (i8 5)
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[REM]], [[X]]
+; CHECK-NEXT:    [[X_OFF:%.*]] = add <2 x i8> [[X]], splat (i8 4)
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult <2 x i8> [[X_OFF]], splat (i8 9)
 ; CHECK-NEXT:    ret <2 x i1> [[CMP]]
 ;
   %rem = srem <2 x i8> %x, <i8 5, i8 5>
@@ -477,8 +477,8 @@ define i64 @icmp_ne_srem_dividend_multi_use(i64 %x) {
 define i1 @icmp_eq_srem_dividend_negative_divisor_canonicalized(i64 %x) {
 ; CHECK-LABEL: define i1 @icmp_eq_srem_dividend_negative_divisor_canonicalized(
 ; CHECK-SAME: i64 [[X:%.*]]) {
-; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X]], 250
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[REM]], [[X]]
+; CHECK-NEXT:    [[X_OFF:%.*]] = add i64 [[X]], 249
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[X_OFF]], 499
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i64 %x, -250

>From 5b87ced9e5f65b6f63313ffcb95d85d633b4dfb9 Mon Sep 17 00:00:00 2001
From: jihwan38 <sss203800 at gmail.com>
Date: Tue, 11 Aug 2026 17:57:12 +0900
Subject: [PATCH 3/3] fixup! [InstCombine] Fold srem compared with its dividend

---
 llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 61e78c5f654f6..d50da84adf934 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7764,8 +7764,7 @@ Instruction *InstCombinerImpl::foldICmpCommutative(CmpPredicate Pred,
     // (X s% C) == X --> X in [1-C, C)
     // (X s% C) != X --> X outside [1-C, C)
     if (ICmpInst::isEquality(Pred) &&
-        match(Op0, m_OneUse(m_SRem(m_Specific(Op1), m_APInt(C)))) &&
-        C->isStrictlyPositive()) {
+        match(Op0, m_OneUse(m_SRem(m_Specific(Op1), m_StrictlyPositive(C))))) {
       APInt Lo = -*C + 1;
       return replaceInstUsesWith(
           CxtI, insertRangeTest(Op1, Lo, *C, /*isSigned=*/true,



More information about the llvm-commits mailing list