https://gcc.gnu.org/g:a327f4cf83765f53e54c93523bbdb3cebec9d1fc

commit r17-5455-ga327f4cf83765f53e54c93523bbdb3cebec9d1fc
Author: Roger Sayle <[email protected]>
Date:   Wed Oct 21 13:26:48 2026 +0100

    fold-const: Constant fold pow(-Inf,0.5) at compile-time
    
    This patch adds support constant folding pow where one or both operands
    is +Inf or -Inf.  The current implementation errs on the side of caution
    only folding finite arguments.
    
    A few technical points of interest.  This change required implementing
    real_isoddinteger in real.cc to test if the argument is an odd integer.
    This implementation should work for decimal float, but is untested, so
    to err on the side of caution, fold_const_pow checks that the base of
    the floating point representation is 2 (i.e. binary).  This check can
    be removed if testing on real hardware produces the expected results.
    Given the efficient implementation of real_isoddinteger, it seemed
    appropriate to improve/update the existing implementation of
    real_isinteger to use the same idiom (avoiding creating, copying
    and modifying of floating point temporaries).
    
    2026-10-21  Roger Sayle  <[email protected]>
    
    gcc/ChangeLog
            * fold-const-call.cc (fold_const_pow): Add compile-time evaluation
            for more cases (of infinite arguments).
            * real.cc (real_isinteger): Improve (both) implementations.
            (real_isoddinteger): New function to test if REAL_VALUE_TYPE
            is an odd integer.
            * real.h (real_isoddinteger): Prototype here.
    
    gcc/testsuite/ChangeLog
            * gcc.dg/fold-pow-1.c: New test case.

Diff:
---
 gcc/fold-const-call.cc            |  85 +++++++++++++++++++++++++++++++
 gcc/real.cc                       | 104 ++++++++++++++++++++++++++++++++++++--
 gcc/real.h                        |   3 ++
 gcc/testsuite/gcc.dg/fold-pow-1.c |  43 ++++++++++++++++
 4 files changed, 230 insertions(+), 5 deletions(-)

diff --git a/gcc/fold-const-call.cc b/gcc/fold-const-call.cc
index c04e93ce2eec..a4bf526d0bf8 100644
--- a/gcc/fold-const-call.cc
+++ b/gcc/fold-const-call.cc
@@ -516,6 +516,91 @@ fold_const_pow (real_value *result, const real_value *arg0,
       return true;
     }
 
+  if (!flag_errno_math
+      && format->b == 2)
+    {
+      /* For any value of y (including NaN), if x is +1, 1.0 is returned. */
+      if (real_equal (arg0, &dconst1))
+       {
+         *result = dconst1;
+         return true;
+       }
+
+      /* For any value of x (including NaN), if y is +-0, 1.0 is returned.  */
+      if (arg1->cl == rvc_zero)
+       {
+         *result = dconst1;
+         return true;
+       }
+
+      /* If x is -1 and y is +-Inf, 1.0 shall be returned.  */
+      if (arg1->cl == rvc_inf
+         && real_equal (arg0, &dconstm1))
+       {
+         *result = dconst1;
+         return true;
+       }
+
+      /* If |x| < 1, and y is -Inf, +Inf shall be returned.  */
+      /* If |x| > 1, and y is -Inf, +0 shall be returned.  */
+      if (arg1->cl == rvc_inf && arg1->sign == 1
+         && arg0->cl == rvc_normal)
+       {
+         *result = REAL_EXP (arg0) <= 0 ? dconstinf : dconst0;
+         return true;
+       }
+
+      if (arg1->cl == rvc_inf && arg1->sign == 1
+         && arg0->cl == rvc_inf)
+       {
+         *result = dconst0;
+         return true;
+       }
+
+      /* If |x| < 1, and y is +Inf, +0 shall be returned.  */
+      /* If |x| > 1, and y is +Inf, +Inf shall be returned.  */
+      if (arg1->cl == rvc_inf && arg1->sign == 0
+         && arg0->cl == rvc_normal)
+       {
+         *result = REAL_EXP (arg0) <= 0 ? dconst0 : dconstinf;
+         return true;
+       }
+
+      if (arg1->cl == rvc_inf && arg1->sign == 0
+         && arg0->cl == rvc_inf)
+       {
+         *result = dconstinf;
+         return true;
+       }
+
+      /* For y an odd integer < 0, if x is -Inf, -0 shall be returned.  */
+      /* For y < 0 and not an odd integer, if x is -Inf, +0 is returned.  */
+      if (arg0->cl == rvc_inf && arg0->sign == 1
+         && arg1->cl == rvc_normal && arg1->sign == 1)
+       {
+         *result = real_isoddinteger (arg1, format) ? dconstm0 : dconst0;
+         return true;
+       }
+
+      /* For y an odd integer > 0, if x is -Inf, -Inf shall be returned.  */
+      /* For y > 0 and not an odd integer, if x is -Inf, +Inf is returned.  */
+      if (arg0->cl == rvc_inf && arg0->sign == 1
+         && arg1->cl == rvc_normal && arg1->sign == 0)
+       {
+         *result = real_isoddinteger (arg1, format) ? dconstninf : dconstinf;
+         return true;
+       }
+
+      /* For y < 0, if x is +Inf, +0 shall be returned.  */
+      /* For y > 0, if x is +Inf, +Inf shall be returned.  */
+      if (arg0->cl == rvc_inf && arg0->sign == 0
+         && arg1->cl == rvc_normal)
+       {
+         *result = arg1->sign ? dconst0 : dconstinf;
+         return true;
+       }
+    }
+
   return false;
 }
 
diff --git a/gcc/real.cc b/gcc/real.cc
index 1dadd2aa6cd5..d7a6556822aa 100644
--- a/gcc/real.cc
+++ b/gcc/real.cc
@@ -5327,15 +5327,55 @@ real_copysign (REAL_VALUE_TYPE *r, const 
REAL_VALUE_TYPE *x)
 }
 
 /* Check whether the real constant value given is an integer.
-   Returns false for signaling NaN.  */
+   Returns false for sNaN, qNaN, +Inf, -Inf and -0.0.  */
 
 bool
 real_isinteger (const REAL_VALUE_TYPE *c, format_helper fmt)
 {
-  REAL_VALUE_TYPE cint;
+  switch (c->cl)
+    {
+    case rvc_zero:
+      if (c->sign)
+       return false;
+      return true;
 
-  real_trunc (&cint, fmt, c);
-  return real_identical (c, &cint);
+    case rvc_inf:
+    case rvc_nan:
+      return false;
+
+    case rvc_normal:
+      break;
+    }
+
+  if (c->decimal)
+    {
+      REAL_VALUE_TYPE cint;
+      decimal_do_fix_trunc (&cint, c);
+      if (fmt)
+       decimal_real_convert (&cint, fmt, &cint);
+      return real_identical (c, &cint);
+    }
+
+  int e = REAL_EXP (c);
+  if (e <= 0)
+    return false;
+  if (e >= SIGNIFICAND_BITS)
+    return true;
+  unsigned int n = SIGNIFICAND_BITS - e;
+  int w = n / HOST_BITS_PER_LONG;
+  int i;
+  for (i = 0; i < w; ++i)
+    if (c->sig[i] != 0)
+      return false;
+
+  unsigned int t = n % HOST_BITS_PER_LONG;
+  if (t != 0)
+    {
+      unsigned long mask = ((unsigned long)1 << t) - 1;
+      if ((c->sig[w] & mask) != 0)
+       return false;
+    }
+  return true;
 }
 
 /* Check whether C is an integer that fits in a HOST_WIDE_INT,
@@ -5344,8 +5384,23 @@ real_isinteger (const REAL_VALUE_TYPE *c, format_helper 
fmt)
 bool
 real_isinteger (const REAL_VALUE_TYPE *c, HOST_WIDE_INT *int_out)
 {
-  REAL_VALUE_TYPE cint;
+  switch (c->cl)
+    {
+    case rvc_zero:
+      if (c->sign)
+       return false;
+      *int_out = 0;
+      return true;
+
+    case rvc_inf:
+    case rvc_nan:
+      return false;
 
+    case rvc_normal:
+      break;
+    }
+
+  REAL_VALUE_TYPE cint;
   HOST_WIDE_INT n = real_to_integer (c);
   real_from_integer (&cint, VOIDmode, n, SIGNED);
   if (real_identical (c, &cint))
@@ -5356,6 +5411,45 @@ real_isinteger (const REAL_VALUE_TYPE *c, HOST_WIDE_INT 
*int_out)
   return false;
 }
 
+/* Check whether C is an odd integer.  */
+
+bool
+real_isoddinteger (const REAL_VALUE_TYPE *c, format_helper fmt)
+{
+  if (c->cl != rvc_normal)
+    return false;
+
+  if (c->decimal)
+    {
+      /* Step 1. c is not an integer if c != trunc (c).  */
+      REAL_VALUE_TYPE cint;
+      decimal_do_fix_trunc (&cint, c);
+      if (fmt)
+       decimal_real_convert (&cint, fmt, &cint);
+      if (real_identical (c, &cint))
+       return false;
+
+      /* Step 2. t = c / 2.  */
+      cint.uexp--;
+
+      /* Step 3. c is an odd integer if t != trunc (t).  */
+      REAL_VALUE_TYPE hint;
+      decimal_do_fix_trunc (&hint, &cint);
+      if (fmt)
+       decimal_real_convert (&hint, fmt, &hint);
+      return !real_identical (&cint, &hint);
+    }
+
+  int e = REAL_EXP (c);
+  if (e <= 0 && e > SIGNIFICAND_BITS)
+    return false;
+  unsigned int n = SIGNIFICAND_BITS - e;
+  int w = n / HOST_BITS_PER_LONG;
+  unsigned int t = n % HOST_BITS_PER_LONG;
+  unsigned long mask = (unsigned long)1 << t;
+  return (c->sig[w] & mask) != 0;
+}
+
 /* Calculate nextafter (X, Y) or nexttoward (X, Y).  Return true if
    underflow or overflow needs to be raised.  */
 
diff --git a/gcc/real.h b/gcc/real.h
index 380dd72b04dd..f7be5df17db3 100644
--- a/gcc/real.h
+++ b/gcc/real.h
@@ -543,6 +543,9 @@ extern void real_copysign (REAL_VALUE_TYPE *, const 
REAL_VALUE_TYPE *);
 extern bool real_isinteger (const REAL_VALUE_TYPE *, format_helper);
 extern bool real_isinteger (const REAL_VALUE_TYPE *, HOST_WIDE_INT *);
 
+/* Check whether the real constant value given is an odd integer.  */
+extern bool real_isoddinteger (const REAL_VALUE_TYPE *, format_helper);
+
 /* Calculate nextafter (X, Y) in format FMT.  */
 extern bool real_nextafter (REAL_VALUE_TYPE *, format_helper,
                            const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);
diff --git a/gcc/testsuite/gcc.dg/fold-pow-1.c 
b/gcc/testsuite/gcc.dg/fold-pow-1.c
new file mode 100644
index 000000000000..e3ac4a435f24
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/fold-pow-1.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-math-errno -fdump-tree-optimized" } */
+
+double pow_one_inf() { return __builtin_pow(1.0,__builtin_inf()); }
+double pow_one_ninf() { return __builtin_pow(1.0,-__builtin_inf()); }
+double pow_one_nan() { return __builtin_pow(1.0,__builtin_nan("")); }
+
+double pow_inf_zero() { return __builtin_pow(__builtin_inf(),0.0); }
+double pow_ninf_zero() { return __builtin_pow(-__builtin_inf(),0.0); }
+double pow_nan_zero() { return __builtin_pow(__builtin_nan(""),0.0); }
+double pow_inf_mzero() { return __builtin_pow(__builtin_inf(),-0.0); }
+double pow_ninf_mzero() { return __builtin_pow(-__builtin_inf(),-0.0); }
+double pow_nan_mzero() { return __builtin_pow(__builtin_nan(""),-0.0); }
+
+double pow_mone_inf() { return __builtin_pow(-1.0,__builtin_inf()); }
+double pow_mone_ninf() { return __builtin_pow(-1.0,__builtin_inf()); }
+
+double pow_half_ninf() { return __builtin_pow(0.5,-__builtin_inf()); }
+double pow_mhalf_ninf() { return __builtin_pow(-0.5,-__builtin_inf()); }
+double pow_two_ninf() { return __builtin_pow(2.0,-__builtin_inf()); }
+double pow_mtwo_ninf() { return __builtin_pow(-2.0,-__builtin_inf()); }
+double pow_inf_ninf() { return 
__builtin_pow(__builtin_inf(),-__builtin_inf()); }
+double pow_ninf_ninf() { return 
__builtin_pow(-__builtin_inf(),-__builtin_inf()); }
+
+double pow_half_inf() { return __builtin_pow(0.5,__builtin_inf()); }
+double pow_mhalf_inf() { return __builtin_pow(-0.5,__builtin_inf()); }
+double pow_two_inf() { return __builtin_pow(2.0,__builtin_inf()); }
+double pow_mtwo_inf() { return __builtin_pow(-2.0,__builtin_inf()); }
+double pow_inf_inf() { return __builtin_pow(__builtin_inf(),__builtin_inf()); }
+double pow_ninf_inf() { return 
__builtin_pow(-__builtin_inf(),__builtin_inf()); }
+
+double pow_ninf_mthree() { return __builtin_pow(-__builtin_inf(),-3.0); }
+double pow_ninf_mtwo() { return __builtin_pow(-__builtin_inf(),-2.0); }
+
+double pow_ninf_three() { return __builtin_pow(-__builtin_inf(),3.0); }
+double pow_ninf_two() { return __builtin_pow(-__builtin_inf(),2.0); }
+
+double pow_inf_mtwo() { return __builtin_pow(__builtin_inf(),-2.0); }
+double pow_inf_mone() { return __builtin_pow(__builtin_inf(),-1.0); }
+double pow_inf_half() { return __builtin_pow(__builtin_inf(),0.5); }
+double pow_inf_two() { return __builtin_pow(__builtin_inf(),2.0); }
+
+/* { dg-final { scan-tree-dump-not "__builtin_pow" "optimized" } } */

Reply via email to