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" } } */
