[gcc r17-3352] match.pd: fold (t * u) % u to zero when the product cannot overflow

Richard Biener via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:ce6a2e9349065db52a7e25808aefc97ddaef9cba

commit r17-3352-gce6a2e9349065db52a7e25808aefc97ddaef9cba
Author: Dominic P <[email protected]>
Date:   Thu Aug 13 20:46:26 2026 +0100

    match.pd: fold (t * u) % u to zero when the product cannot overflow
    
    A product t * u is an exact multiple of u, so t * u % u is zero under
    every rounding convention whenever the multiplication does not wrap.
    For signed types the overflow is undefined so this always holds; for
    others use value ranges to prove the multiply is overflow-free, exactly
    as the neighbouring (t * u) / u -> t already does.  This is the
    remainder counterpart of that fold, for all four modulo codes: the
    C family only produces TRUNC_MOD_EXPR, but Fortran's MODULO and Ada's
    mod produce the floor and ceiling forms, which fold equally.
    
    Assisted-by: Claude Opus 4.8 (Anthropic)
    
    gcc/ChangeLog:
    
            * match.pd ((t * u) % u -> 0): New simplification.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/fold-mod-mult-1.c: New test.
    
    Signed-off-by: Dominic P <[email protected]>

Diff:
---
 gcc/match.pd                           | 19 +++++++++++++++++++
 gcc/testsuite/gcc.dg/fold-mod-mult-1.c | 25 +++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index 665e8eede7f2..0c399a11f8de 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1141,6 +1141,25 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 #endif
    ))))
 
+/* Simplify (t * u) % u -> 0.  The product is an exact multiple of u, so
+   the remainder is zero under every rounding convention as long as the
+   multiplication does not overflow.  Mirrors (t * u) / u -> t above.  */
+(for mod (trunc_mod floor_mod ceil_mod round_mod)
+ (simplify
+  (mod (mult:c@2 @0 @1) @1)
+  (if (ANY_INTEGRAL_TYPE_P (type))
+   (if (TYPE_OVERFLOW_UNDEFINED (type) && !TYPE_OVERFLOW_SANITIZED (type))
+    { build_zero_cst (type); }
+#if GIMPLE
+    (with {int_range_max vr0, vr1;}
+     (if (INTEGRAL_TYPE_P (type)
+	  && gimple_match_range_of_expr (vr0, @0, @2)
+	  && gimple_match_range_of_expr (vr1, @1, @2)
+	  && range_op_handler (MULT_EXPR).overflow_free_p (vr0, vr1))
+      { build_zero_cst (type); }))
+#endif
+   ))))
+
 #if GIMPLE
 (for div (trunc_div exact_div)
  /* Simplify (X + M*N) / N -> X / N + M.  */
diff --git a/gcc/testsuite/gcc.dg/fold-mod-mult-1.c b/gcc/testsuite/gcc.dg/fold-mod-mult-1.c
new file mode 100644
index 000000000000..9315e94822c4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fold-mod-mult-1.c
@@ -0,0 +1,25 @@
+/* (t * u) % u is zero whenever the product does not overflow: for signed
+   types the overflow is undefined so it always folds; for unsigned types it
+   folds when value ranges prove the multiply cannot wrap.  */
+/* { dg-do compile } */
+/* { dg-require-effective-target int32plus } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+f_signed (int a, int b)
+{
+  return (a * b) % b;
+}
+
+unsigned
+f_ranged (unsigned a, unsigned b)
+{
+  a &= 0xffff;
+  b &= 0xffff;
+  if (b == 0)
+    return 7;
+  return (a * b) % b;		/* a*b <= 0xfffe0001, cannot wrap */
+}
+
+/* Both remainders fold away; no modulo survives.  */
+/* { dg-final { scan-tree-dump-not " % " "optimized" } } */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.