[llvm] handle (X + A) % Op1 for small X, A (PR #140369)

via llvm-commits llvm-commits at lists.llvm.org
Sat May 17 05:38:30 PDT 2025


https://github.com/DesWurstes updated https://github.com/llvm/llvm-project/pull/140369

>From 73d88b3bca231b80a4dac468d0aa3edae45f5f43 Mon Sep 17 00:00:00 2001
From: DesWurstes <mcccs at gmx.com>
Date: Sat, 17 May 2025 11:49:26 +0100
Subject: [PATCH 1/2] [test] precommit some tests for (X+A)%Op1

---
 .../InstCombine/urem-via-cmp-select.ll        | 194 ++++++++++++++++++
 1 file changed, 194 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll b/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
index 02be67a2ca250..4d1252d7dd987 100644
--- a/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
@@ -19,6 +19,26 @@ define i8 @urem_assume(i8 %x, i8 %n) {
   ret i8 %out
 }
 
+; https://alive2.llvm.org/ce/z/NXHJJD
+define i8 @urem_assume_a(i8 %x, i8 %n, i8 %a) {
+; CHECK-LABEL: @urem_assume_a(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[X_FR:%.*]], [[N:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[CMP_A:%.*]] = icmp ult i8 [[A:%.*]], [[N]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP_A]])
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[X_FR]], [[A]]
+; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+;
+  %cmp = icmp ult i8 %x, %n
+  tail call void @llvm.assume(i1 %cmp)
+  %cmp_a = icmp ult i8 %a, %n
+  tail call void @llvm.assume(i1 %cmp_a)
+  %add = add nuw i8 %x, %a
+  %out = urem i8 %add, %n
+  ret i8 %out
+}
+
 ; https://alive2.llvm.org/ce/z/MGgtYN
 define i8 @urem_assume_without_nuw(i8 %x, i8 %n) {
 ; CHECK-LABEL: @urem_assume_without_nuw(
@@ -53,6 +73,48 @@ define i8 @urem_assume_eq(i8 %x, i8 %n) {
   ret i8 %out
 }
 
+; Negative test: The assume is false
+define i8 @urem_assume_eq_x(i8 %x, i8 %n, i8 %a) {
+; CHECK-LABEL: @urem_assume_eq_x(
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X:%.*]], [[N:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[CMP_A:%.*]] = icmp ult i8 [[A:%.*]], [[N]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP_A]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
+; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+;
+  %cmp = icmp eq i8 %x, %n
+  tail call void @llvm.assume(i1 %cmp)
+  %cmp_a = icmp ult i8 %a, %n
+  tail call void @llvm.assume(i1 %cmp_a)
+  %add = add i8 %x, 1
+  %out = urem i8 %add, %n
+  ret i8 %out
+}
+
+; Negative test: The assume is false
+define i8 @urem_assume_eq_a(i8 %x, i8 %n, i8 %a) {
+; CHECK-LABEL: @urem_assume_eq_a(
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[X_FR]], [[N:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[CMP_A:%.*]] = icmp eq i8 [[A:%.*]], [[N]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP_A]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X_FR]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[ADD]], [[N]]
+; CHECK-NEXT:    [[OUT:%.*]] = select i1 [[TMP1]], i8 0, i8 [[ADD]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+;
+  %cmp = icmp ult i8 %x, %n
+  tail call void @llvm.assume(i1 %cmp)
+  %cmp_a = icmp eq i8 %a, %n
+  tail call void @llvm.assume(i1 %cmp_a)
+  %add = add i8 %x, 1
+  %out = urem i8 %add, %n
+  ret i8 %out
+}
+
 ; Negative test: The assume is false
 define i8 @urem_assume_ne(i8 %x, i8 %n) {
 ; CHECK-LABEL: @urem_assume_ne(
@@ -71,6 +133,52 @@ start:
   ret i8 %out
 }
 
+; Negative test: The assume is false
+define i8 @urem_assume_ne_x(i8 %x, i8 %n, i8 %a) {
+; CHECK-LABEL: @urem_assume_ne_x(
+; CHECK-NEXT:  start:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X:%.*]], [[N:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[CMP_A:%.*]] = icmp ult i8 [[A:%.*]], [[N]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP_A]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
+; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+;
+start:
+  %cmp = icmp ne i8 %x, %n
+  tail call void @llvm.assume(i1 %cmp)
+  %cmp_a = icmp ult i8 %a, %n
+  tail call void @llvm.assume(i1 %cmp_a)
+  %add = add i8 %x, 1
+  %out = urem i8 %add, %n
+  ret i8 %out
+}
+
+; Negative test: The assume is false
+define i8 @urem_assume_ne_a(i8 %x, i8 %n, i8 %a) {
+; CHECK-LABEL: @urem_assume_ne_a(
+; CHECK-NEXT:  start:
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[X_FR]], [[N:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[CMP_A:%.*]] = icmp ne i8 [[A:%.*]], [[N]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP_A]])
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X_FR]], 1
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i8 [[ADD]], [[N]]
+; CHECK-NEXT:    [[OUT:%.*]] = select i1 [[TMP0]], i8 0, i8 [[ADD]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+;
+start:
+  %cmp = icmp ult i8 %x, %n
+  tail call void @llvm.assume(i1 %cmp)
+  %cmp_a = icmp ne i8 %a, %n
+  tail call void @llvm.assume(i1 %cmp_a)
+  %add = add i8 %x, 1
+  %out = urem i8 %add, %n
+  ret i8 %out
+}
+
 ; Negative test: The add constant is not 1
 define i8 @urem_assume_with_unexpected_const(i8 %x, i8 %n) {
 ; CHECK-LABEL: @urem_assume_with_unexpected_const(
@@ -103,6 +211,36 @@ define i8 @urem_without_assume(i8 %arg, i8 %arg2) {
   ret i8 %out
 }
 
+; Negative test: The add constant is not less than %arg2
+define i8 @urem_without_assume_a_var(i8 %arg, i8 %arg2, i8 %a) {
+; CHECK-LABEL: @urem_without_assume_a_var(
+; CHECK-NEXT:    [[X:%.*]] = urem i8 [[ARG:%.*]], [[ARG2:%.*]]
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], [[A:%.*]]
+; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[ARG2]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+;
+  %x = urem i8 %arg, %arg2
+  %add = add i8 %x, %a
+  %out = urem i8 %add, %arg2
+  ret i8 %out
+}
+
+; https://alive2.llvm.org/ce/z/tcdf4d
+define i8 @urem_without_assume_a(i8 %arg, i8 %arg2, i8 %a) {
+; CHECK-LABEL: @urem_without_assume_a(
+; CHECK-NEXT:    [[X_REM:%.*]] = urem i8 [[ARG:%.*]], [[ARG2:%.*]]
+; CHECK-NEXT:    [[A_REM:%.*]] = urem i8 [[A:%.*]], [[ARG2]]
+; CHECK-NEXT:    [[ADDD:%.*]] = add i8 [[X_REM]], [[A_REM]]
+; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADDD]], [[ARG2]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+;
+  %x_rem = urem i8 %arg, %arg2
+  %a_rem = urem i8 %a, %arg2
+  %add = add i8 %x_rem, %a_rem
+  %out = urem i8 %add, %arg2
+  ret i8 %out
+}
+
 ; https://alive2.llvm.org/ce/z/eHkgRa
 define i8 @urem_with_dominating_condition(i8 %x, i8 %n) {
 ; CHECK-LABEL: @urem_with_dominating_condition(
@@ -177,4 +315,60 @@ define noundef i8 @urem_with_opposite_condition(i8 %x, i8 %n) {
   ret i8 0
 }
 
+; Negative test
+define noundef i8 @urem_with_opposite_condition_x(i8 %x, i8 %n, i8 %a) {
+; CHECK-LABEL: @urem_with_opposite_condition_x(
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i8 [[X:%.*]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[DOTBB2:%.*]], label [[DOTBB0:%.*]]
+; CHECK:       .bb0:
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ult i8 [[A:%.*]], [[N]]
+; CHECK-NEXT:    br i1 [[COND2]], label [[DOTBB1:%.*]], label [[DOTBB2]]
+; CHECK:       .bb1:
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], [[A]]
+; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+; CHECK:       .bb2:
+; CHECK-NEXT:    ret i8 0
+;
+  %cond = icmp ult i8 %x, %n
+  br i1 %cond, label %.bb2, label %.bb0 ; Revert the condition
+.bb0:
+  %cond2 = icmp ult i8 %a, %n
+  br i1 %cond2, label %.bb1, label %.bb2
+.bb1:
+  %add = add i8 %x, %a
+  %out = urem i8 %add, %n
+  ret i8 %out
+.bb2:
+  ret i8 0
+}
+
+; Negative test
+define noundef i8 @urem_with_opposite_condition_a(i8 %x, i8 %n, i8 %a) {
+; CHECK-LABEL: @urem_with_opposite_condition_a(
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i8 [[X:%.*]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[DOTBB0:%.*]], label [[DOTBB2:%.*]]
+; CHECK:       .bb0:
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ult i8 [[A:%.*]], [[N]]
+; CHECK-NEXT:    br i1 [[COND2]], label [[DOTBB2]], label [[DOTBB1:%.*]]
+; CHECK:       .bb1:
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], [[A]]
+; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
+; CHECK-NEXT:    ret i8 [[OUT]]
+; CHECK:       .bb2:
+; CHECK-NEXT:    ret i8 0
+;
+  %cond = icmp ult i8 %x, %n
+  br i1 %cond, label %.bb0, label %.bb2
+.bb0:
+  %cond2 = icmp ult i8 %a, %n
+  br i1 %cond2, label %.bb2, label %.bb1 ; Revert the condition
+.bb1:
+  %add = add i8 %x, %a
+  %out = urem i8 %add, %n
+  ret i8 %out
+.bb2:
+  ret i8 0
+}
+
 declare void @llvm.assume(i1 noundef)

>From 76171ed243595a7e3c27ad694db2a96b9ee74687 Mon Sep 17 00:00:00 2001
From: DesWurstes <mcccs at gmx.com>
Date: Sat, 17 May 2025 11:50:17 +0100
Subject: [PATCH 2/2] [InstCombine] handle (X + A) % Op1 for small X, A

---
 .../InstCombine/InstCombineMulDivRem.cpp       | 18 ++++++++++++++++++
 .../InstCombine/urem-via-cmp-select.ll         | 10 ++++++++--
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index c7023eb79b04e..2e0d25ab46c15 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -2461,6 +2461,24 @@ Instruction *InstCombinerImpl::visitURem(BinaryOperator &I) {
     }
   }
 
+  // For "(X + A) % Op1" and if (X u< Op1 && A u< Op1)
+  // => (X + A) >= Op1 ? X + A - Op1 : X + A .
+  Value *A = nullptr;
+  if (match(Op0, m_Add(m_Value(X), m_Value(A)))) {
+    Value *Val_X =
+        simplifyICmpInst(ICmpInst::ICMP_ULT, X, Op1, SQ.getWithInstruction(&I));
+    Value *Val_A =
+        simplifyICmpInst(ICmpInst::ICMP_ULT, A, Op1, SQ.getWithInstruction(&I));
+    if (Val_X && match(Val_X, m_One()) && Val_A && match(Val_A, m_One())) {
+      Value *FrozenOp0 = Op0;
+      if (!isGuaranteedNotToBeUndef(Op0))
+        FrozenOp0 = Builder.CreateFreeze(Op0, Op0->getName() + ".frozen");
+      Value *Cmp = Builder.CreateICmpUGE(FrozenOp0, Op1);
+      Value *Sub = Builder.CreateSub(FrozenOp0, Op1);
+      return SelectInst::Create(Cmp, Sub, FrozenOp0);
+    }
+  }
+
   return nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll b/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
index 4d1252d7dd987..0f2567e6475da 100644
--- a/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
+++ b/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll
@@ -27,7 +27,10 @@ define i8 @urem_assume_a(i8 %x, i8 %n, i8 %a) {
 ; CHECK-NEXT:    [[CMP_A:%.*]] = icmp ult i8 [[A:%.*]], [[N]]
 ; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP_A]])
 ; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[X_FR]], [[A]]
-; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
+; CHECK-NEXT:    [[ADD_FROZEN:%.*]] = freeze i8 [[ADD]]
+; CHECK-NEXT:    [[DOTNOT:%.*]] = icmp ult i8 [[ADD_FROZEN]], [[N]]
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[DOTNOT]], i8 0, i8 [[N]]
+; CHECK-NEXT:    [[OUT:%.*]] = sub i8 [[ADD_FROZEN]], [[TMP1]]
 ; CHECK-NEXT:    ret i8 [[OUT]]
 ;
   %cmp = icmp ult i8 %x, %n
@@ -231,7 +234,10 @@ define i8 @urem_without_assume_a(i8 %arg, i8 %arg2, i8 %a) {
 ; CHECK-NEXT:    [[X_REM:%.*]] = urem i8 [[ARG:%.*]], [[ARG2:%.*]]
 ; CHECK-NEXT:    [[A_REM:%.*]] = urem i8 [[A:%.*]], [[ARG2]]
 ; CHECK-NEXT:    [[ADDD:%.*]] = add i8 [[X_REM]], [[A_REM]]
-; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADDD]], [[ARG2]]
+; CHECK-NEXT:    [[ADD_FROZEN:%.*]] = freeze i8 [[ADDD]]
+; CHECK-NEXT:    [[DOTNOT:%.*]] = icmp ult i8 [[ADD_FROZEN]], [[ARG2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[DOTNOT]], i8 0, i8 [[ARG2]]
+; CHECK-NEXT:    [[OUT:%.*]] = sub i8 [[ADD_FROZEN]], [[TMP1]]
 ; CHECK-NEXT:    ret i8 [[OUT]]
 ;
   %x_rem = urem i8 %arg, %arg2



More information about the llvm-commits mailing list