diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 316ead1a477..65451c705ed 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2006-11-27 Richard Guenther + + PR middle-end/25620 + * builtins.c (expand_builtin_pow): Optimize non integer valued + constant exponents using sqrt or cbrt if possible. Always fall back + to expanding via optabs. + 2006-11-27 Ira Rosen PR tree-optimization/22372 diff --git a/gcc/builtins.c b/gcc/builtins.c index b2964ded296..d610198a637 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -2601,8 +2601,13 @@ expand_powi (rtx x, enum machine_mode mode, HOST_WIDE_INT n) static rtx expand_builtin_pow (tree exp, rtx target, rtx subtarget) { + tree arg0, arg1, fn, narg0, narglist; tree arglist = TREE_OPERAND (exp, 1); - tree arg0, arg1; + tree type = TREE_TYPE (exp); + REAL_VALUE_TYPE cint, c, c2; + HOST_WIDE_INT n; + rtx op, op2; + enum machine_mode mode = TYPE_MODE (type); if (! validate_arglist (arglist, REAL_TYPE, REAL_TYPE, VOID_TYPE)) return 0; @@ -2610,36 +2615,108 @@ expand_builtin_pow (tree exp, rtx target, rtx subtarget) arg0 = TREE_VALUE (arglist); arg1 = TREE_VALUE (TREE_CHAIN (arglist)); - if (TREE_CODE (arg1) == REAL_CST - && ! TREE_CONSTANT_OVERFLOW (arg1)) - { - REAL_VALUE_TYPE cint; - REAL_VALUE_TYPE c; - HOST_WIDE_INT n; + if (TREE_CODE (arg1) != REAL_CST + || TREE_CONSTANT_OVERFLOW (arg1)) + return expand_builtin_mathfn_2 (exp, target, 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)) + /* Handle constant exponents. */ + + /* For integer valued exponents we can expand to an optimal multiplication + sequence using expand_powi. */ + 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) + && ((n >= -1 && n <= 2) + || (flag_unsafe_math_optimizations + && !optimize_size + && powi_cost (n) <= POWI_MAX_MULTS))) + { + op = expand_expr (arg0, subtarget, VOIDmode, 0); + if (n != 1) { - /* 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)) + op = force_reg (mode, op); + op = expand_powi (op, mode, n); + } + return op; + } + + narg0 = builtin_save_expr (arg0); + narglist = build_tree_list (NULL_TREE, narg0); + + /* If the exponent is not integer valued, check if it is half of an integer. + In this case we can expand to sqrt (x) * x**(n/2). */ + fn = mathfn_built_in (type, BUILT_IN_SQRT); + if (fn != NULL_TREE) + { + real_arithmetic (&c2, MULT_EXPR, &c, &dconst2); + n = real_to_integer (&c2); + real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); + if (real_identical (&c2, &cint) + && ((flag_unsafe_math_optimizations + && !optimize_size + && powi_cost (n/2) <= POWI_MAX_MULTS) + || n == 1)) + { + tree call_expr = build_function_call_expr (fn, narglist); + op = expand_builtin (call_expr, NULL_RTX, subtarget, mode, 0); + if (n != 1) { - 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); + op2 = expand_expr (narg0, subtarget, VOIDmode, 0); + op2 = force_reg (mode, op2); + op2 = expand_powi (op2, mode, abs (n / 2)); + op = expand_simple_binop (mode, MULT, op, op2, NULL_RTX, + 0, OPTAB_LIB_WIDEN); + /* If the original exponent was negative, reciprocate the + result. */ + if (n < 0) + op = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), + op, NULL_RTX, 0, OPTAB_LIB_WIDEN); } + return op; } } - if (! flag_unsafe_math_optimizations) - return NULL_RTX; + /* Try if the exponent is a third of an integer. In this case + we can expand to x**(n/3) * cbrt(x)**(n%3). */ + fn = mathfn_built_in (type, BUILT_IN_CBRT); + if (fn != NULL_TREE) + { + real_arithmetic (&c2, MULT_EXPR, &c, &dconst3); + real_round (&c2, mode, &c2); + n = real_to_integer (&c2); + real_from_integer (&cint, VOIDmode, n, n < 0 ? -1 : 0, 0); + real_arithmetic (&c2, RDIV_EXPR, &cint, &dconst3); + real_convert (&c2, mode, &c2); + if (real_identical (&c2, &c) + && ((!optimize_size + && flag_unsafe_math_optimizations + && powi_cost (n/3) <= POWI_MAX_MULTS) + || n == 1)) + { + tree call_expr = build_function_call_expr (fn, narglist); + op = expand_builtin (call_expr, NULL_RTX, subtarget, mode, 0); + if (abs (n) % 3 == 2) + op = expand_simple_binop (mode, MULT, op, op, op, + 0, OPTAB_LIB_WIDEN); + if (n != 1) + { + op2 = expand_expr (narg0, subtarget, VOIDmode, 0); + op2 = force_reg (mode, op2); + op2 = expand_powi (op2, mode, abs (n / 3)); + op = expand_simple_binop (mode, MULT, op, op2, NULL_RTX, + 0, OPTAB_LIB_WIDEN); + /* If the original exponent was negative, reciprocate the + result. */ + if (n < 0) + op = expand_binop (mode, sdiv_optab, CONST1_RTX (mode), + op, NULL_RTX, 0, OPTAB_LIB_WIDEN); + } + return op; + } + } + + /* Fall back to optab expansion. */ return expand_builtin_mathfn_2 (exp, target, subtarget); } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f5cd43d3965..14e8f12da2e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2006-11-27 Richard Guenther + + PR middle-end/25620 + * gcc.target/i386/pow-1.c: New testcase. + * gcc.dg/builtins-58.c: Likewise. + 2006-11-26 Mark Mitchell PR c++/29886 diff --git a/gcc/testsuite/gcc.dg/builtins-58.c b/gcc/testsuite/gcc.dg/builtins-58.c new file mode 100644 index 00000000000..cf5a9c2f3cd --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtins-58.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "-O -ffast-math -std=c99" } */ + +#include "builtins-config.h" + +#ifdef HAVE_C99_RUNTIME +double test1 (double x) +{ + return __builtin_pow (x, 1./3.); +} + +double test2 (double x) +{ + return __builtin_pow (x, 4./3.); +} + +double test3a (double x) +{ + return __builtin_pow (x, 5./3.); +} + +double test3b (double x) +{ + return __builtin_pow (x, -5./3.); +} + +double test4 (double x) +{ + return __builtin_pow (x, 7./3.); +} +#endif + +/* { dg-final { scan-assembler-not "pow" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pow-1.c b/gcc/testsuite/gcc.target/i386/pow-1.c new file mode 100644 index 00000000000..3bb3fe0a906 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pow-1.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-O -ffast-math" } */ + +double test1 (double x) +{ + return __builtin_pow (x, 1./2.); +} + +double test2 (double x) +{ + return __builtin_pow (x, 3./2.); +} + +double test3 (double x) +{ + return __builtin_pow (x, 5./2.); +} + +double test4 (double x) +{ + return __builtin_pow (x, -5./2.); +} + +/* { dg-final { scan-assembler-not "call" } } */