Re: [PATCH] match: fold a remainder of a remainder by a multiple
Jeffrey Law <[email protected]> Wed, 5 Aug 2026 22:16:26 -0600
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
On 8/4/2026 3:50 AM, [email protected] wrote: > From: Kyrylo Tkachov <[email protected]> > > Truncating remainder keeps the sign of the dividend and its magnitude > modulo the divisor, so reducing X % C1 again modulo C2 gives the same > result as reducing X directly whenever C2 divides C1. Folding the pair > removes one division. > > int f (int x) { return (x % 12) % 4; } > > aarch64 -O2: > > before after > mov w1, 12 negs w1, w0 > sdiv w1, w0, w1 and w0, w0, 3 > add w1, w1, w1, lsl 1 and w1, w1, 3 > sub w0, w0, w1, lsl 2 csneg w0, w0, w1, mi > negs w1, w0 > and w0, w0, 3 > and w1, w1, 3 > csneg w0, w0, w1, mi > > Bootstrapped and tested on aarch64-none-linux-gnu. > Ok for trunk? > Thanks, > Kyrill > > gcc/ChangeLog: > > * match.pd ((X % C1) % C2): New simplification. > > gcc/testsuite/ChangeLog: > > * gcc.dg/tree-ssa/modmod-1.c: New test. > > Signed-off-by: Kyrylo Tkachov <[email protected]> > --- > gcc/match.pd | 12 ++++++++++++ > gcc/testsuite/gcc.dg/tree-ssa/modmod-1.c | 19 +++++++++++++++++++ > 2 files changed, 31 insertions(+) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/modmod-1.c > > diff --git a/gcc/match.pd b/gcc/match.pd > index 4fca75d6fb6..22202af2cc1 100644 > --- a/gcc/match.pd > +++ b/gcc/match.pd > @@ -975,6 +975,18 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > (with { tree utype = unsigned_type_for (TREE_TYPE (@0)); } > (cmp (mod (convert:utype @0) (convert:utype @2)) (convert:utype @1))))))) > > +/* (X % C1) % C2 is X % C2 when C2 divides C1. Truncating remainder keeps > + the sign of X and the magnitude modulo C1, so reducing modulo C2 gives > + the same result as reducing X directly. */ > +(simplify > + (trunc_mod (trunc_mod @0 INTEGER_CST@1) INTEGER_CST@2) > + (if (INTEGRAL_TYPE_P (type) > + && !TYPE_OVERFLOW_TRAPS (type) > + && !integer_zerop (@1) > + && !integer_zerop (@2) > + && wi::multiple_of_p (wi::to_widest (@1), wi::to_widest (@2), SIGNED)) Is SIGNED really correct for that argument to wi::multiple_of_p? I don't have a testcase where it matters. Just a generic question. > + (trunc_mod @0 @2))) > + > /* X % -C is the same as X % C. */ > (simplify > (trunc_mod @0 INTEGER_CST@1) > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modmod-1.c b/gcc/testsuite/gcc.dg/tree-ssa/modmod-1.c > new file mode 100644 > index 00000000000..6689a518ff3 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/modmod-1.c > @@ -0,0 +1,19 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > + > +/* (X % C1) % C2 is X % C2 when C2 divides C1. */ > + > +int f1 (int x) { return (x % 12) % 4; } > +int f2 (int x) { return (x % 100) % 25; } > +int f3 (int x) { return (x % -12) % 4; } > +int f4 (int x) { return (x % 12) % -4; } > +unsigned int f5 (unsigned int x) { return (x % 12) % 4; } For f5, do you want to verify it collapses to an & 3? I guess the lack of % 12 or %4 for it is probably sufficient since the other counts would get thrown off if we failed to optimize f5 down to &3. Generally it looks good. Just like to nail down that the SIGNED argument is really what we want. Jeff