https://gcc.gnu.org/g:22d833315ea8ed0069be1a5d920532e9771613d2

commit r17-3159-g22d833315ea8ed0069be1a5d920532e9771613d2
Author: Kyrylo Tkachov <[email protected]>
Date:   Fri Jul 31 05:13:39 2026 +0200

    match.pd: turn a product of two quotients into one division
    
    (A / B) * (C / D) is (A * C) / (B * D), which replaces one of the two
    divisions with a multiply.  A division costs several multiplies on every
    target.
    
      double f (double a, double b, double c)
      { return (a / b) * (1.0 / c); }
    
    AArch64 -Ofast before:
    
            fdiv    d0, d0, d1
            fdiv    d0, d0, d2
    
    After:
    
            fmul    d1, d1, d2
            fdiv    d0, d0, d1
    
    The rule needs infinities and NaNs excluded.  It forms two products, and 
each
    is a new place for the exponent to leave the range.  It also needs
    non-trapping math because it removes one division.  A function option test
    restores trapping math and verifies that both divisions remain.
    
    Both quotients must be singly used.  This excludes a shared reciprocal and
    the X * X form of one reciprocal square root, where the rewrite can add a
    division after its products fold.  Complex values also require signed zeros
    to be ignored because reassociation can change the sign of an imaginary 
zero.
    
    Bootstrapped and tested on aarch64-none-linux-gnu.
    
    gcc/ChangeLog:
    
            * match.pd ((A / B) * (C / D)): New simplification.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/tree-ssa/recip-mult-div-1.c: New test.
            * gcc.dg/tree-ssa/recip-mult-div-2.c: New test.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/match.pd                                     | 29 ++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-1.c | 43 ++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-2.c | 15 +++++++++
 3 files changed, 87 insertions(+)

diff --git a/gcc/match.pd b/gcc/match.pd
index 1ea46fd17264..c2f411001a03 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -811,6 +811,35 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (rdiv (rdiv:s @0 @1) @2)
   (rdiv @0 (mult @1 @2)))
 
+ /* Convert (A/B) * (C/D) to (A*C) / (B*D).  Two divisions become one
+    multiply and one division, and a division costs several multiplies on
+    every target.
+
+    The two products are new places for the exponent to leave the range, so
+    the rule needs more than the rounding licence: with B and D both large
+    B * D is an infinity and the quotient becomes inf / inf, and with both
+    small it is a zero and the quotient becomes 0 / 0.  Either way a finite
+    result turns into a NaN, so the rule is restricted to the case where
+    infinities and NaNs are excluded, as the cancellation rules above are.
+
+    Both quotients have to be dead outside the product, otherwise a division
+    would be added rather than removed, and a shared reciprocal is better
+    left alone for the multiplications to reuse.  That is a hard requirement
+    rather than a :s marker, because :s only forbids emitting new statements
+    and both products can fold away to nothing, as they do for X * X where X
+    is one reciprocal square root.  Requiring two singly used quotients also
+    excludes that case, since a value feeding both operands of the product
+    has two uses.  */
+ (simplify
+  (mult (rdiv@4 @0 @1) (rdiv@5 @2 @3))
+  (if (flag_associative_math
+       && !HONOR_NANS (type) && !HONOR_INFINITIES (type)
+       && !flag_trapping_math
+       && (TREE_CODE (type) != COMPLEX_TYPE
+          || !HONOR_SIGNED_ZEROS (type))
+       && single_use (@4) && single_use (@5))
+   (rdiv (mult @0 @2) (mult @1 @3))))
+
  /* Canonicalize x / (C1 * y) to (x * C2) / y.  */
  (simplify
   (rdiv @0 (mult:s @1 REAL_CST@2))
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-1.c
new file mode 100644
index 000000000000..56d2d9a7feb3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-1.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -freciprocal-math -ffinite-math-only 
-fdump-tree-optimized" } */
+/* { dg-additional-options "-fassociative-math -fno-signed-zeros 
-fno-trapping-math" } */
+
+/* (A / B) * (C / D) is (A * C) / (B * D): one of the two divisions becomes
+   a multiply.  The rule also needs infinities and NaNs excluded, because the
+   two products it forms can leave the range of the type.  */
+
+double f1 (double a, double b, double c)
+{
+  return (a / b) * (1.0 / c);
+}
+
+double f2 (double a, double b, double c, double d)
+{
+  return (a / b) * (c / d);
+}
+
+float f3 (float a, float b, float c, float d)
+{
+  return (a / b) * (c / d);
+}
+
+/* Trapping math must preserve both divisions.  */
+__attribute__((optimize ("trapping-math")))
+double trapping (double a, double b, double c, double d)
+{
+  return (a / b) * (c / d);
+}
+
+/* Must not fold: the reciprocal is shared, so the multiplications should
+   reuse it rather than pay for a second division.  */
+double keep (double a, double b, double c, double *r)
+{
+  double t = 1.0 / c;
+  r[0] = (a / b) * t;
+  r[1] = t;
+  return t;
+}
+
+/* Must not fold without -ffinite-math-only: see recip-mult-div-2.c.  */
+
+/* { dg-final { scan-tree-dump-times " / " 7 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-2.c
new file mode 100644
index 000000000000..e866d217ee7a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-mult-div-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -freciprocal-math -fdump-tree-optimized" } */
+
+/* (A / B) * (C / D) -> (A * C) / (B * D) forms two products that can leave
+   the range of the type.  With B and D both large B * D is an infinity and
+   the quotient becomes inf / inf; with both small it is a zero and the
+   quotient becomes 0 / 0.  Either turns a finite result into a NaN, so
+   -freciprocal-math on its own must not enable the rule.  */
+
+double f (double a, double b, double c, double d)
+{
+  return (a / b) * (c / d);
+}
+
+/* { dg-final { scan-tree-dump-times " / " 2 "optimized" } } */

Reply via email to