https://gcc.gnu.org/g:34e1afc66c3239bf5d5dbe8005f8930d84fa20bf

commit r17-5395-g34e1afc66c3239bf5d5dbe8005f8930d84fa20bf
Author: Richard Biener <[email protected]>
Date:   Tue Oct 20 09:48:15 2026 +0200

    tree-optimization/128006 - only decrement constant integer pow exponents
    
    When undistributing a factor X out of a multiplication chain,
    zero_one_operation uses stmt_is_power_of_op to find a pow/powi call
    with base X and then removes one factor of X via decrement_power.
    stmt_is_power_of_op only checked the base, but decrement_power assumes
    a constant integer exponent.  For
    
      (x * y) * __builtin_pow (x, 2.5) + z * x
    
    with -ffast-math the exponent 2.5 was truncated to 2 and decremented
    to 1, replacing pow (x, 2.5) with x and miscompiling the expression to
    x * (x * x * y + z).  A non-constant exponent ICEd in decrement_power.
    
    Make stmt_is_power_of_op require a constant integer exponent so that
    the factor is removed from elsewhere in the chain instead.
    
            PR tree-optimization/128006
            * tree-ssa-reassoc.cc (stmt_is_power_of_op): Require a
            constant integer exponent.
    
            * gcc.dg/tree-ssa/reassoc-52.c: New test.
            * gcc.dg/tree-ssa/reassoc-53.c: New test.

Diff:
---
 gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c | 19 +++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/reassoc-53.c | 27 +++++++++++++++++++++++++++
 gcc/tree-ssa-reassoc.cc                    | 18 ++++++++++++++++--
 3 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c 
b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c
new file mode 100644
index 000000000000..d5b9b17a84e0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -ffast-math" } */
+
+/* Undistribution must not treat pow (x, 2.5) as x * pow (x, 1.5)
+   by truncating its exponent.  */
+
+__attribute__((noipa)) double
+f (double x, double y, double z)
+{
+  return (x * y) * __builtin_pow (x, 2.5) + z * x;
+}
+
+int
+main (void)
+{
+  if (f (4.0, 1.0, 0.0) != 128.0)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-53.c 
b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-53.c
new file mode 100644
index 000000000000..818f8583521c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-53.c
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -ffast-math" } */
+
+/* Undistribution must not try to decrement a non-constant exponent
+   of pow or powi.  */
+
+__attribute__((noipa)) double
+f (double x, double y, double z, double n)
+{
+  return (x * y) * __builtin_pow (x, n) + z * x;
+}
+
+__attribute__((noipa)) double
+g (double x, double y, double z, int n)
+{
+  return (x * y) * __builtin_powi (x, n) + z * x;
+}
+
+int
+main (void)
+{
+  if (f (4.0, 1.0, 0.0, 3.0) != 256.0)
+    __builtin_abort ();
+  if (g (4.0, 1.0, 0.0, 3) != 256.0)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/tree-ssa-reassoc.cc b/gcc/tree-ssa-reassoc.cc
index 737d30ba0a4c..ca342ba3d8c8 100644
--- a/gcc/tree-ssa-reassoc.cc
+++ b/gcc/tree-ssa-reassoc.cc
@@ -1190,7 +1190,7 @@ oecount_cmp (const void *p1, const void *p2)
 }
 
 /* Return TRUE iff STMT represents a builtin call that raises OP
-   to some exponent.  */
+   to some constant integer exponent, as required by decrement_power.  */
 
 static bool
 stmt_is_power_of_op (gimple *stmt, tree op)
@@ -1201,8 +1201,22 @@ stmt_is_power_of_op (gimple *stmt, tree op)
   switch (gimple_call_combined_fn (stmt))
     {
     CASE_CFN_POW:
+      {
+       tree arg1 = gimple_call_arg (stmt, 1);
+       if (TREE_CODE (arg1) != REAL_CST)
+         return false;
+       REAL_VALUE_TYPE c = TREE_REAL_CST (arg1);
+       REAL_VALUE_TYPE cint;
+       real_from_integer (&cint, VOIDmode, real_to_integer (&c), SIGNED);
+       if (!real_identical (&c, &cint))
+         return false;
+       return operand_equal_p (gimple_call_arg (stmt, 0), op, 0);
+      }
+
     CASE_CFN_POWI:
-      return (operand_equal_p (gimple_call_arg (stmt, 0), op, 0));
+      if (!tree_fits_shwi_p (gimple_call_arg (stmt, 1)))
+       return false;
+      return operand_equal_p (gimple_call_arg (stmt, 0), op, 0);
 
     default:
       return false;

Reply via email to