diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1323f30cb14a..7590ddd24441 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2003-07-31 Roger Sayle + + * fold-const.c (fold ): Optimize both x*pow(x,c) and + pow(x,c)*x as pow(x,c+1) for constant values c. Optimize x*x + as pow(x,2.0) when the latter will be expanded back into x*x. + (fold ): Optimize pow(x,c)/x as pow(x,c-1). + * builtins.c (expand_builtin_pow): Ignore flag_errno_math as + pow can never set errno when used with an integer exponent. + Always use expand_powi when exponent is -1, 0, 1 or 2. + (fold_builtin): Don't rewrite pow(x,2.0) as x*x nor pow(x,-2.0) + as 1.0/(x*x). This avoids unbounded recursion as we now prefer + the pow forms of these expressions. + 2003-07-31 Geoffrey Keating * Makefile.in (libexecdir): New. diff --git a/gcc/builtins.c b/gcc/builtins.c index a8c1d47277f7..535b84cb09d7 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -2170,10 +2170,7 @@ expand_builtin_pow (tree exp, rtx target, rtx subtarget) arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - if (flag_unsafe_math_optimizations - && ! flag_errno_math - && ! optimize_size - && TREE_CODE (arg1) == REAL_CST + if (TREE_CODE (arg1) == REAL_CST && ! TREE_CONSTANT_OVERFLOW (arg1)) { REAL_VALUE_TYPE cint; @@ -2183,13 +2180,21 @@ expand_builtin_pow (tree exp, rtx target, rtx subtarget) c = TREE_REAL_CST (arg1); n = real_to_integer (&c); real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); - if (real_identical (&c, &cint) - && powi_cost (n) <= POWI_MAX_MULTS) + if (real_identical (&c, &cint)) { - enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); - rtx op = expand_expr (arg0, subtarget, VOIDmode, 0); - op = force_reg (mode, op); - return expand_powi (op, mode, n); + /* If the exponent is -1, 0, 1 or 2, then expand_powi is exact. + Otherwise, check the number of multiplications required. + Note that pow never sets errno for an integer exponent. */ + if ((n >= -1 && n <= 2) + || (flag_unsafe_math_optimizations + && ! optimize_size + && powi_cost (n) <= POWI_MAX_MULTS)) + { + enum machine_mode mode = TYPE_MODE (TREE_TYPE (exp)); + rtx op = expand_expr (arg0, subtarget, VOIDmode, 0); + op = force_reg (mode, op); + return expand_powi (op, mode, n); + } } } return expand_builtin_mathfn_2 (exp, target, NULL_RTX); @@ -6245,28 +6250,6 @@ fold_builtin (tree exp) build_real (type, dconst1), arg0)); - /* Optimize pow(x,2.0) = x*x. */ - if (REAL_VALUES_EQUAL (c, dconst2) - && (*lang_hooks.decls.global_bindings_p) () == 0 - && ! CONTAINS_PLACEHOLDER_P (arg0)) - { - arg0 = save_expr (arg0); - return fold (build (MULT_EXPR, type, arg0, arg0)); - } - - /* Optimize pow(x,-2.0) = 1.0/(x*x). */ - if (flag_unsafe_math_optimizations - && REAL_VALUES_EQUAL (c, dconstm2) - && (*lang_hooks.decls.global_bindings_p) () == 0 - && ! CONTAINS_PLACEHOLDER_P (arg0)) - { - arg0 = save_expr (arg0); - return fold (build (RDIV_EXPR, type, - build_real (type, dconst1), - fold (build (MULT_EXPR, type, - arg0, arg0)))); - } - /* Optimize pow(x,0.5) = sqrt(x). */ if (flag_unsafe_math_optimizations && REAL_VALUES_EQUAL (c, dconsthalf)) diff --git a/gcc/fold-const.c b/gcc/fold-const.c index aea392f8ab4e..c4faf075d0d7 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -6071,6 +6071,80 @@ fold (tree expr) return build_function_call_expr (sinfn, TREE_OPERAND (arg0, 1)); } + + /* Optimize x*pow(x,c) as pow(x,c+1). */ + if (fcode1 == BUILT_IN_POW + || fcode1 == BUILT_IN_POWF + || fcode1 == BUILT_IN_POWL) + { + tree arg10 = TREE_VALUE (TREE_OPERAND (arg1, 1)); + tree arg11 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg1, + 1))); + if (TREE_CODE (arg11) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg11) + && operand_equal_p (arg0, arg10, 0)) + { + tree powfn = TREE_OPERAND (TREE_OPERAND (arg1, 0), 0); + REAL_VALUE_TYPE c; + tree arg, arglist; + + c = TREE_REAL_CST (arg11); + real_arithmetic (&c, PLUS_EXPR, &c, &dconst1); + arg = build_real (type, c); + arglist = build_tree_list (NULL_TREE, arg); + arglist = tree_cons (NULL_TREE, arg0, arglist); + return build_function_call_expr (powfn, arglist); + } + } + + /* Optimize pow(x,c)*x as pow(x,c+1). */ + if (fcode0 == BUILT_IN_POW + || fcode0 == BUILT_IN_POWF + || fcode0 == BUILT_IN_POWL) + { + tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1)); + tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, + 1))); + if (TREE_CODE (arg01) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg01) + && operand_equal_p (arg1, arg00, 0)) + { + tree powfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0); + REAL_VALUE_TYPE c; + tree arg, arglist; + + c = TREE_REAL_CST (arg01); + real_arithmetic (&c, PLUS_EXPR, &c, &dconst1); + arg = build_real (type, c); + arglist = build_tree_list (NULL_TREE, arg); + arglist = tree_cons (NULL_TREE, arg1, arglist); + return build_function_call_expr (powfn, arglist); + } + } + + /* Optimize x*x as pow(x,2.0), which is expanded as x*x. */ + if (! optimize_size + && operand_equal_p (arg0, arg1, 0)) + { + tree powfn; + + if (type == double_type_node) + powfn = implicit_built_in_decls[BUILT_IN_POW]; + else if (type == float_type_node) + powfn = implicit_built_in_decls[BUILT_IN_POWF]; + else if (type == long_double_type_node) + powfn = implicit_built_in_decls[BUILT_IN_POWL]; + else + powfn = NULL_TREE; + + if (powfn) + { + tree arg = build_real (type, dconst2); + tree arglist = build_tree_list (NULL_TREE, arg); + arglist = tree_cons (NULL_TREE, arg0, arglist); + return build_function_call_expr (powfn, arglist); + } + } } } goto associate; @@ -6328,6 +6402,30 @@ fold (tree expr) tmp)); } } + + /* Optimize pow(x,c)/x as pow(x,c-1). */ + if (fcode0 == BUILT_IN_POW + || fcode0 == BUILT_IN_POWF + || fcode0 == BUILT_IN_POWL) + { + tree arg00 = TREE_VALUE (TREE_OPERAND (arg0, 1)); + tree arg01 = TREE_VALUE (TREE_CHAIN (TREE_OPERAND (arg0, 1))); + if (TREE_CODE (arg01) == REAL_CST + && ! TREE_CONSTANT_OVERFLOW (arg01) + && operand_equal_p (arg1, arg00, 0)) + { + tree powfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0); + REAL_VALUE_TYPE c; + tree arg, arglist; + + c = TREE_REAL_CST (arg01); + real_arithmetic (&c, MINUS_EXPR, &c, &dconst1); + arg = build_real (type, c); + arglist = build_tree_list (NULL_TREE, arg); + arglist = tree_cons (NULL_TREE, arg1, arglist); + return build_function_call_expr (powfn, arglist); + } + } } goto binary; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9299dde54e8a..26e44a3bfbe9 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2003-07-31 Roger Sayle + + * gcc.dg/builtins-27.c: New test case. + 2003-07-31 Jakub Jelinek * gcc.dg/tls/opt-7.c: New test. diff --git a/gcc/testsuite/gcc.dg/builtins-27.c b/gcc/testsuite/gcc.dg/builtins-27.c new file mode 100644 index 000000000000..69d8f994481c --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtins-27.c @@ -0,0 +1,47 @@ +/* Copyright (C) 2003 Free Software Foundation. + + Check that constant folding of built-in math functions doesn't + break anything and produces the expected results. + + Written by Roger Sayle, 29th July 2003. */ + +/* { dg-do link } */ +/* { dg-options "-O2 -ffast-math" } */ + +extern void link_error(void); + +extern double pow(double,double); + +void test(double x) +{ + if (pow(x,2.0) != x*x) + link_error (); + + if (x*pow(x,2.0) != pow(x,3.0)) + link_error (); + + if (pow(x,2.0)*x != pow(x,3.0)) + link_error (); + + if (pow(x,3.0) != x*x*x) + link_error (); + + if (pow(x,2.0)*x != x*x*x) + link_error (); + + if (x*pow(x,2.0) != x*x*x) + link_error (); + + if (pow(x,3.0)/x != pow(x,2.0)) + link_error (); + + if (pow(x,3.0)/x != x*x) + link_error (); +} + +int main() +{ + test (2.0); + return 0; +} +