Simplify (t * (C + 1)) / C to t + t / C for signed and unsigned types
when the multiplication is known not to overflow. Unsigned division
by a power of two has already become a right shift when ranges are
available, so also simplify (t * (2^k + 1)) >> k to t + (t >> k) under
the same condition.
For exact_div only, also simplify (t * (C - 1)) / C to t - t / C. For
trunc_div the result is t - ceil (t / C), which differs unless C
divides t. exact_div cannot be produced from C source and the GIMPLE
front end cannot express it, so this case has no testcase.
Bootstrapped and regtested on x86_64-linux-gnu.
PR tree-optimization/126294
gcc/ChangeLog:
* match.pd: Simplify (t * (C + 1)) / C to t + t / C and, for
exact_div, (t * (C - 1)) / C to t - t / C, and
(t * (2^k + 1)) >> k to t + (t >> k), when the multiplication
does not overflow.
gcc/testsuite/ChangeLog:
* gcc.dg/tree-ssa/pr126294.c: New test.
Signed-off-by: Shivam Gupta <[email protected]>
---
gcc/match.pd | 48 ++++++++-
gcc/testsuite/gcc.dg/tree-ssa/pr126294.c | 129 +++++++++++++++++++++++
2 files changed, 176 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr126294.c
diff --git a/gcc/match.pd b/gcc/match.pd
index 1c0393d11..472551001 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -1141,8 +1141,31 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(if (gimple_match_range_of_expr (vr0, @0, @3)
&& gimple_match_range_of_expr (vr1, @1)
&& range_op_handler (MULT_EXPR).overflow_free_p (vr0, vr1))
- (mult @0 (div! @1 @2))))
+ (mult @0 (div! @1 @2))
)))
+ /* Otherwise simplify (t * (C + 1)) / C -> t + t / C, and for exact_div
+ only (t * (C - 1)) / C -> t - t / C. The latter is wrong for
+ trunc_div, where the result is t - ceil (t / C). */
+ (if (INTEGRAL_TYPE_P (type)
+ && (div == TRUNC_DIV_EXPR || div == EXACT_DIV_EXPR))
+ (with {
+ wide_int c1 = wi::to_wide (@1);
+ wide_int c2 = wi::to_wide (@2);
+ int_range_max vr0, vr1;
+ }
+ (if (wi::gts_p (c2, 0)
+ /* Avoid this transformation if c2 + 1 wraps, i.e. c2 is INT_MAX. */
+ && wi::gts_p (c1, 0)
+ && (c1 == c2 + 1
+ || (div == EXACT_DIV_EXPR && c1 == c2 - 1))
+ && gimple_match_range_of_expr (vr0, @0, @3)
+ && !vr0.varying_p ()
+ && gimple_match_range_of_expr (vr1, @1)
+ /* t * C1 doesn't overflow. */
+ && range_op_handler (MULT_EXPR).overflow_free_p (vr0, vr1))
+ (if (c1 == c2 + 1)
+ (plus @0 (div @0 @2))
+ (minus @0 (div @0 @2))))))))
#endif
/* Simplify (t * u) / (t * v) -> (u / v) if u is multiple of v. */
(simplify
@@ -1162,6 +1185,29 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
#endif
))))
+#if GIMPLE
+/* Simplify (t * (2^k + 1)) >> k -> t + (t >> k). Unsigned t / 2^k has
+ already become a shift when ranges are available, so the division
+ pattern above cannot see it. */
+(simplify
+ (rshift (mult@3 @0 INTEGER_CST@1) INTEGER_CST@2)
+ (if (INTEGRAL_TYPE_P (type)
+ /* Keep 2^k + 1 from wrapping, and reject bad shift counts. */
+ && wi::ltu_p (wi::to_wide (@2), TYPE_PRECISION (type) - 1))
+ (with {
+ wide_int d = wi::lshift (wi::one (TYPE_PRECISION (type)),
+ wi::to_wide (@2));
+ int_range_max vr0, vr1;
+ }
+ (if (wi::to_wide (@1) == d + 1
+ && gimple_match_range_of_expr (vr0, @0, @3)
+ && !vr0.varying_p ()
+ && gimple_match_range_of_expr (vr1, @1)
+ /* t * C1 doesn't overflow. */
+ && range_op_handler (MULT_EXPR).overflow_free_p (vr0, vr1))
+ (plus @0 (rshift @0 @2))))))
+#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. */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr126294.c
b/gcc/testsuite/gcc.dg/tree-ssa/pr126294.c
new file mode 100644
index 000000000..f39be6700
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr126294.c
@@ -0,0 +1,129 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-require-effective-target int32plus } */
+
+/* (t * (C + 1)) / C -> t + t / C */
+unsigned
+plus_1 (unsigned short b)
+{
+ unsigned t = b;
+ return t * 7 / 6;
+}
+
+int
+signed_plus_1 (short b)
+{
+ int t = b;
+ return t * 11 / 10;
+}
+
+
+/* Known negative range */
+int
+signed_plus_neg (short b)
+{
+ int t = b;
+ if (t >= 0)
+ return 0;
+ return t * 25 / 24;
+}
+
+/* (t * (C - 1)) / C with trunc_div: no transform */
+unsigned
+minus_1 (unsigned short b)
+{
+ unsigned t = b;
+ return t * 12 / 13;
+}
+
+int
+signed_minus_1 (short b)
+{
+ int t = b;
+ return t * 16 / 17;
+}
+
+/* Known non-negative range */
+int
+signed_minus_nonneg (short b)
+{
+ int t = b;
+ if (t < 0)
+ return 0;
+ return t * 14 / 15;
+}
+
+/* Unknown range */
+int
+no_range_signed (int t)
+{
+ return t * 19 / 18;
+}
+
+unsigned
+no_range_unsigned (unsigned t)
+{
+ return t * 20 / 19;
+}
+
+/* Too wide range */
+unsigned
+overflow_range (unsigned t)
+{
+ if (t > 0x7fffffff)
+ return 0;
+ return t * 23 / 22;
+}
+
+/* Non-adjacent constants */
+unsigned
+no_transform (unsigned short b)
+{
+ unsigned t = b;
+ return t * 5 / 3;
+}
+
+/* Original PR reproducer: rshift form. */
+unsigned
+pr126294 (unsigned short b)
+{
+ unsigned t = b;
+ return t * 3 / 2;
+}
+
+/* Signed power-of-two divisor */
+int
+signed_pow2 (short b)
+{
+ int t = b;
+ return t * 9 / 8;
+}
+
+/* t * 33 may overflow, no transformation. */
+unsigned
+no_range_shift (unsigned t)
+{
+ return t * 33 / 32;
+}
+
+/* Transformed */
+/* { dg-final { scan-tree-dump-not "\\* 7;" "optimized" } } */
+/* { dg-final { scan-tree-dump " / 6;" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "\\* 11;" "optimized" } } */
+/* { dg-final { scan-tree-dump " / 10;" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "\\* 25;" "optimized" } } */
+/* { dg-final { scan-tree-dump " / 24;" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "\\* 3;" "optimized" } } */
+/* { dg-final { scan-tree-dump " >> 1;" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "\\* 9;" "optimized" } } */
+/* { dg-final { scan-tree-dump " / 8;" "optimized" } } */
+
+/* Not transformed */
+/* { dg-final { scan-tree-dump "\\* 12;" "optimized" } } */
+/* { dg-final { scan-tree-dump "\\* 16;" "optimized" } } */
+/* { dg-final { scan-tree-dump "\\* 14;" "optimized" } } */
+/* { dg-final { scan-tree-dump "\\* 19;" "optimized" } } */
+/* { dg-final { scan-tree-dump "\\* 20;" "optimized" } } */
+/* { dg-final { scan-tree-dump "\\* 23;" "optimized" } } */
+/* { dg-final { scan-tree-dump "\\* 5;" "optimized" } } */
+/* { dg-final { scan-tree-dump "\\* 33;" "optimized" } } */
--
2.43.0