[llvm] Fold (a % b) lt/ge (b-1) where b is a power of 2 (PR #72504)

via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 16 03:50:59 PST 2023


https://github.com/elhewaty created https://github.com/llvm/llvm-project/pull/72504

- [InstCombine] Add test coverage for (a % b) lt/ge (b-1) where b is a power of 2 (NFC).
- [InstCombine] Fold (a % b) lt/ge (b-1) where b is a power of 2.
Alive2: https://alive2.llvm.org/ce/z/LtfqTv
Fixes: https://github.com/llvm/llvm-project/issues/71280


>From c81904e8448d8e74c0737c20154914edb273bd54 Mon Sep 17 00:00:00 2001
From: Mohamed Atef <mohamedatef1698 at gmail.com>
Date: Thu, 16 Nov 2023 12:48:35 +0200
Subject: [PATCH 1/2] [InstCombine] Add test coverage for (a % b) lt/ge (b-1)
 where b is a power of 2 (NFC).

---
 llvm/test/Transforms/InstCombine/icmp.ll | 133 +++++++++++++++++++++++
 1 file changed, 133 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp.ll b/llvm/test/Transforms/InstCombine/icmp.ll
index 78ac730cf026ed9..394a71d865a77c6 100644
--- a/llvm/test/Transforms/InstCombine/icmp.ll
+++ b/llvm/test/Transforms/InstCombine/icmp.ll
@@ -10,6 +10,139 @@ declare void @use_i8(i8)
 declare void @use_i32(i32)
 declare void @use_i64(i64)
 
+; tests for (x % y) >=/ < (y - 1)
+define i1 @srem_sge_test1(i64 %x) {
+; CHECK-LABEL: @srem_sge_test1(
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X:%.*]], 8589934592
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[REM]], 8589934590
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i64 %x, 8589934592
+  %cmp = icmp sge i64 %rem, 8589934591
+  ret i1 %cmp
+}
+
+define i1 @srem_slt_test1(i64 %x) {
+; CHECK-LABEL: @srem_slt_test1(
+; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X:%.*]], 8589934592
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[REM]], 8589934591
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i64 %x, 8589934592
+  %cmp = icmp slt i64 %rem, 8589934591
+  ret i1 %cmp
+}
+
+define i1 @srem_sge_test2(i32 %x) {
+; CHECK-LABEL: @srem_sge_test2(
+; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[X:%.*]], 1024
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[REM]], 1022
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i32 %x, 1024
+  %cmp = icmp sge i32 %rem, 1023
+  ret i1 %cmp
+}
+
+define i1 @srem_slt_test2(i32 %x) {
+; CHECK-LABEL: @srem_slt_test2(
+; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[X:%.*]], 256
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[REM]], 255
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i32 %x, 256
+  %cmp = icmp slt i32 %rem, 255
+  ret i1 %cmp
+}
+
+define i1 @srem_sge_test3(i16 %x) {
+; CHECK-LABEL: @srem_sge_test3(
+; CHECK-NEXT:    [[REM:%.*]] = srem i16 [[X:%.*]], 8192
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[REM]], 8190
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i16 %x, 8192
+  %cmp = icmp sge i16 %rem, 8191
+  ret i1 %cmp
+}
+
+define i1 @srem_slt_test3(i16 %x) {
+; CHECK-LABEL: @srem_slt_test3(
+; CHECK-NEXT:    [[REM:%.*]] = srem i16 [[X:%.*]], 8192
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i16 [[REM]], 8191
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i16 %x, 8192
+  %cmp = icmp slt i16 %rem, 8191
+  ret i1 %cmp
+}
+
+define i1 @srem_sge_test4(i8 %x) {
+; CHECK-LABEL: @srem_sge_test4(
+; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X:%.*]], 64
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[REM]], 62
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i8 %x, 64
+  %cmp = icmp sge i8 %rem, 63
+  ret i1 %cmp
+}
+
+define i1 @srem_slt_test4(i8 %x) {
+; CHECK-LABEL: @srem_slt_test4(
+; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X:%.*]], 64
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[REM]], 63
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i8 %x, 64
+  %cmp = icmp slt i8 %rem, 63
+  ret i1 %cmp
+}
+
+; tests for (y - 1) >/<= (x % y)
+define i1 @srem_sgt_test1(i32 %x) {
+; CHECK-LABEL: @srem_sgt_test1(
+; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[X:%.*]], 1048576
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[REM]], 1048575
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i32 %x, 1048576
+  %cmp = icmp sgt i32 1048575, %rem
+  ret i1 %cmp
+}
+
+define i1 @srem_sle_test1(i16 %x) {
+; CHECK-LABEL: @srem_sle_test1(
+; CHECK-NEXT:    [[REM:%.*]] = srem i16 [[X:%.*]], 8192
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[REM]], 8190
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i16 %x, 8192
+  %cmp = icmp sle i16 8191, %rem
+  ret i1 %cmp
+}
+
+; negative tests
+define i1 @srem_sgt_test(i32 %x) {
+; CHECK-LABEL: @srem_sgt_test(
+; CHECK-NEXT:    ret i1 false
+;
+  %rem = srem i32 %x, 32
+  %cmp = icmp sgt i32 %rem, 31
+  ret i1 %cmp
+}
+
+define i1 @srem_another_negative_test(i32 %x) {
+; CHECK-LABEL: @srem_another_negative_test(
+; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[X:%.*]], 8
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[REM]], 5
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rem = srem i32 %x, 8
+  %cmp = icmp sge i32 %rem, 6
+  ret i1 %cmp
+}
+
 define i32 @test1(i32 %X) {
 ; CHECK-LABEL: @test1(
 ; CHECK-NEXT:    [[X_LOBIT:%.*]] = lshr i32 [[X:%.*]], 31

>From fb9a3d60f72fd1c69e0e578ddbd050bfbf1b2b94 Mon Sep 17 00:00:00 2001
From: Mohamed Atef <mohamedatef1698 at gmail.com>
Date: Thu, 16 Nov 2023 13:43:22 +0200
Subject: [PATCH 2/2] [InstCombine] Fold (a % b) lt/ge (b-1) where b is a power
 of 2.

---
 .../InstCombine/InstCombineCompares.cpp       | 29 ++++++++++++++
 llvm/test/Transforms/InstCombine/icmp.ll      | 40 +++++++++----------
 2 files changed, 49 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 9bc84c7dd6e1539..96ca914388a9df3 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6837,6 +6837,35 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     Changed = true;
   }
 
+  {
+    Value *X;
+    const APInt *C, *CC;
+    ICmpInst::Predicate Pred = I.getPredicate();
+    if (match(Op1, m_SRem(m_Value(X), m_Power2(C))) &&
+        match(Op0, m_APInt(CC))) {
+      std::swap(Op0, Op1);
+      Pred = I.getSwappedPredicate();
+    }
+
+    if (match(Op0, m_SRem(m_Value(X), m_Power2(C))) &&
+        match(Op1, m_APInt(CC)) && *CC == *C - 1) {
+      int BW = C->getBitWidth();
+      int Log2 = C->exactLogBase2();
+      long AndWith = -(1ll << (BW - 1)) + (1ll << Log2) - 1;
+      auto *And = Builder.CreateAnd(X, AndWith);
+      // icmp sge (X % C), (C - 1)
+      //   --> icmp eq (X & -(pow(2, BW - 1) - pow(2, log(C)) + 1)), (C - 1)
+      if (Pred == ICmpInst::ICMP_SLT)
+        return new ICmpInst(ICmpInst::ICMP_NE, And,
+                            ConstantInt::get(And->getType(), *CC));
+      // icmp sge (X % C), (C - 1)
+      //   --> icmp eq (X & -(pow(2, BW - 1) - pow(2, log(C)) + 1)), (C - 1)
+      if (Pred == ICmpInst::ICMP_SGE)
+        return new ICmpInst(ICmpInst::ICMP_EQ, And,
+                            ConstantInt::get(And->getType(), *CC));
+    }
+  }
+
   if (Value *V = simplifyICmpInst(I.getPredicate(), Op0, Op1, Q))
     return replaceInstUsesWith(I, V);
 
diff --git a/llvm/test/Transforms/InstCombine/icmp.ll b/llvm/test/Transforms/InstCombine/icmp.ll
index 394a71d865a77c6..d7ef8e56f11f858 100644
--- a/llvm/test/Transforms/InstCombine/icmp.ll
+++ b/llvm/test/Transforms/InstCombine/icmp.ll
@@ -13,8 +13,8 @@ declare void @use_i64(i64)
 ; tests for (x % y) >=/ < (y - 1)
 define i1 @srem_sge_test1(i64 %x) {
 ; CHECK-LABEL: @srem_sge_test1(
-; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X:%.*]], 8589934592
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[REM]], 8589934590
+; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[X:%.*]], -9223372028264841217
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[TMP1]], 8589934591
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i64 %x, 8589934592
@@ -24,8 +24,8 @@ define i1 @srem_sge_test1(i64 %x) {
 
 define i1 @srem_slt_test1(i64 %x) {
 ; CHECK-LABEL: @srem_slt_test1(
-; CHECK-NEXT:    [[REM:%.*]] = srem i64 [[X:%.*]], 8589934592
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[REM]], 8589934591
+; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[X:%.*]], -9223372028264841217
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i64 [[TMP1]], 8589934591
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i64 %x, 8589934592
@@ -35,8 +35,8 @@ define i1 @srem_slt_test1(i64 %x) {
 
 define i1 @srem_sge_test2(i32 %x) {
 ; CHECK-LABEL: @srem_sge_test2(
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[X:%.*]], 1024
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[REM]], 1022
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[X:%.*]], -2147482625
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[TMP1]], 1023
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i32 %x, 1024
@@ -46,8 +46,8 @@ define i1 @srem_sge_test2(i32 %x) {
 
 define i1 @srem_slt_test2(i32 %x) {
 ; CHECK-LABEL: @srem_slt_test2(
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[X:%.*]], 256
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[REM]], 255
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[X:%.*]], -2147483393
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[TMP1]], 255
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i32 %x, 256
@@ -57,8 +57,8 @@ define i1 @srem_slt_test2(i32 %x) {
 
 define i1 @srem_sge_test3(i16 %x) {
 ; CHECK-LABEL: @srem_sge_test3(
-; CHECK-NEXT:    [[REM:%.*]] = srem i16 [[X:%.*]], 8192
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[REM]], 8190
+; CHECK-NEXT:    [[TMP1:%.*]] = and i16 [[X:%.*]], -24577
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i16 [[TMP1]], 8191
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i16 %x, 8192
@@ -68,8 +68,8 @@ define i1 @srem_sge_test3(i16 %x) {
 
 define i1 @srem_slt_test3(i16 %x) {
 ; CHECK-LABEL: @srem_slt_test3(
-; CHECK-NEXT:    [[REM:%.*]] = srem i16 [[X:%.*]], 8192
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i16 [[REM]], 8191
+; CHECK-NEXT:    [[TMP1:%.*]] = and i16 [[X:%.*]], -24577
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i16 [[TMP1]], 8191
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i16 %x, 8192
@@ -79,8 +79,8 @@ define i1 @srem_slt_test3(i16 %x) {
 
 define i1 @srem_sge_test4(i8 %x) {
 ; CHECK-LABEL: @srem_sge_test4(
-; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X:%.*]], 64
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[REM]], 62
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X:%.*]], -65
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[TMP1]], 63
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i8 %x, 64
@@ -90,8 +90,8 @@ define i1 @srem_sge_test4(i8 %x) {
 
 define i1 @srem_slt_test4(i8 %x) {
 ; CHECK-LABEL: @srem_slt_test4(
-; CHECK-NEXT:    [[REM:%.*]] = srem i8 [[X:%.*]], 64
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[REM]], 63
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X:%.*]], -65
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[TMP1]], 63
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i8 %x, 64
@@ -102,8 +102,8 @@ define i1 @srem_slt_test4(i8 %x) {
 ; tests for (y - 1) >/<= (x % y)
 define i1 @srem_sgt_test1(i32 %x) {
 ; CHECK-LABEL: @srem_sgt_test1(
-; CHECK-NEXT:    [[REM:%.*]] = srem i32 [[X:%.*]], 1048576
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[REM]], 1048575
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[X:%.*]], -2146435073
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i32 [[TMP1]], 1048575
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i32 %x, 1048576
@@ -113,8 +113,8 @@ define i1 @srem_sgt_test1(i32 %x) {
 
 define i1 @srem_sle_test1(i16 %x) {
 ; CHECK-LABEL: @srem_sle_test1(
-; CHECK-NEXT:    [[REM:%.*]] = srem i16 [[X:%.*]], 8192
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i16 [[REM]], 8190
+; CHECK-NEXT:    [[TMP1:%.*]] = and i16 [[X:%.*]], -24577
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i16 [[TMP1]], 8191
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %rem = srem i16 %x, 8192



More information about the llvm-commits mailing list