This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Perform constant folding of math builtins
- From: Roger Sayle <roger at eyesopen dot com>
- To: <gcc-patches at gcc dot gnu dot org>
- Date: Sun, 25 Aug 2002 20:17:55 -0600 (MDT)
- Subject: [PATCH] Perform constant folding of math builtins
The following patch performs constant folding and simplification
transformations on the libm math functions recognized as GCC
builtins. I appreciate we're probably unofficially in stage 3
for GCC 3.3, but whilst the WWW pages still indicate stage 2,
I thought I'd test the gradual transition policy.
The following patch implements the following optimizations:
sqrt(0.0) = 0.0
sqrt(1.0) = 1.0
log(1.0) = 0.0
exp(0.0) = 1.0
fabs(sqrt(x)) = sqrt(x)
fabs(exp(x)) = exp(x)
x/exp(y) = x*exp(-y)
exp(log(x)) = x
log(exp(x)) = x
sqrt(x)*sqrt(y) = sqrt(x*y)
exp(x)*exp(y) = exp(x+y)
sqrt(exp(x)) = exp(x/2.0)
log(sqrt(x)) = log(x)/2.0
The results of these transformations are then re-folded, so that
exp(x/2.0) becomes exp(x*0.5) and exp(x)/exp(y) becomes exp(x-y).
These tranformations also apply to the float and long double forms
of the libm builtins, e.g. sqrtf and sqrtl respectively. Most are
guarded by flag_unsafe_math_optimizations so only apply when
compiling with "-ffast-math".
One major benefit of these optimizations is that they include the
classic whetstone optimization. On i686-pc-cygwin, loop N8 of
whetstone improves 10% with "-O2 -ffast-math", resulting in an
overall improvement of 2.5% (910 MWIPS vs. 889 MWIPS).
This patch has been tested with "make bootstrap" and "make -k check"
on i686-pc-linux-gnu, all languages except Ada and treelang, with
no new regressions. I've included two new test cases that (i)
exercise the new code paths and (ii) check that the precise
constant folding optimizations are being applied. I've also examined
the -S assembly output to make sure these optimizations are all being
performed, and checked the behavior on some trial values to ensure the
results remain the same.
Ok for mainline? My apologies for the late submission.
2002-08-25 Roger Sayle <roger@eyesopen.com>
* builtins.c (build_function_call_expr): Remove prototype, export
as non-static and add a comment above function definition.
(builtin_mathfn_p): New function to check for math builtins.
(fold_builtin): Optimize sqrt(0.0) as 0.0, sqrt(1.0) as 1.0,
exp(0.0) as 1.0, and log(1.0) as 0.0. Optimize exp(log(x)) and
log(exp(x)) as x. Optimize sqrt(exp(x)) as exp(x/2.0) and
log(sqrt(x)) as log(x)/2.0.
* tree.h: Prototype build_function_call_expr and builtin_mathfn_p
in new "builtins.c" section. Place the build_range_type prototype
with the other prototypes from "tree.c".
* fold-const.c (fold) [ABS_EXPR]: Fold fabs(sqrt(x)) as sqrt(x)
and fabs(exp(x)) as exp(x). [MULT_EXPR]: Fold sqrt(x)*sqrt(y)
as sqrt(x*y) and exp(x)*exp(y) as exp(x+y). [RDIV_EXPR]: Fold
x/exp(y) as x*exp(-y).
* gcc.dg/builtins-2.c: New test case.
* gcc.dg/builtins-3.c: New test case.
Index: builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.159
diff -c -3 -p -r1.159 builtins.c
*** builtins.c 5 Aug 2002 18:46:32 -0000 1.159
--- builtins.c 25 Aug 2002 02:26:18 -0000
*************** static tree stabilize_va_list PARAMS ((
*** 148,154 ****
static rtx expand_builtin_expect PARAMS ((tree, rtx));
static tree fold_builtin_constant_p PARAMS ((tree));
static tree fold_builtin_classify_type PARAMS ((tree));
- static tree build_function_call_expr PARAMS ((tree, tree));
static int validate_arglist PARAMS ((tree, ...));
/* Return the alignment in bits of EXP, a pointer valued expression.
--- 148,153 ----
*************** expand_builtin (exp, target, subtarget,
*** 4077,4082 ****
--- 4076,4112 ----
return expand_call (exp, target, ignore);
}
+ /* Determine whether a tree node represents a call to a built-in
+ math function. If the tree T is a call to a built-in function
+ taking a single real argument, then the return value is the
+ DECL_FUNCTION_CODE of the call, e.g. BUILT_IN_SQRT. Otherwise
+ the return value is END_BUILTINS. */
+
+ enum built_in_function
+ builtin_mathfn_p (t)
+ tree t;
+ {
+ tree fndecl, arglist;
+
+ if (TREE_CODE (t) != CALL_EXPR
+ || TREE_CODE (TREE_OPERAND (t, 0)) != ADDR_EXPR)
+ return END_BUILTINS;
+
+ fndecl = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+ if (TREE_CODE (fndecl) != FUNCTION_DECL
+ || ! DECL_BUILT_IN (fndecl)
+ || DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
+ return END_BUILTINS;
+
+ arglist = TREE_OPERAND (t, 1);
+ if (! arglist
+ || TREE_CODE (TREE_TYPE (TREE_VALUE (arglist))) != REAL_TYPE
+ || TREE_CHAIN (arglist))
+ return END_BUILTINS;
+
+ return DECL_FUNCTION_CODE (fndecl);
+ }
+
/* Fold a call to __builtin_constant_p, if we know it will evaluate to a
constant. ARGLIST is the argument list of the call. */
*************** fold_builtin (exp)
*** 4162,4167 ****
--- 4192,4282 ----
}
break;
+ case BUILT_IN_SQRT:
+ case BUILT_IN_SQRTF:
+ case BUILT_IN_SQRTL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ enum built_in_function fcode;
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize sqrt(0.0) = 0.0 and sqrt(1.0) = 1.0. */
+ if (real_zerop (arg) || real_onep (arg))
+ return arg;
+
+ /* Optimize sqrt(exp(x)) = exp(x/2.0). */
+ fcode = builtin_mathfn_p (arg);
+ if (flag_unsafe_math_optimizations
+ && (fcode == BUILT_IN_EXP
+ || fcode == BUILT_IN_EXPF
+ || fcode == BUILT_IN_EXPL))
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
+ arg = build (RDIV_EXPR, TREE_TYPE (arg),
+ TREE_VALUE (TREE_OPERAND (arg, 1)),
+ build_real (TREE_TYPE (arg), dconst2));
+ arglist = build_tree_list (NULL_TREE, arg);
+ return build_function_call_expr (expfn, arglist);
+ }
+ }
+ break;
+
+ case BUILT_IN_EXP:
+ case BUILT_IN_EXPF:
+ case BUILT_IN_EXPL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ enum built_in_function fcode;
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize exp(0.0) = 1.0. */
+ if (real_zerop (arg))
+ return build_real (TREE_TYPE (arg), dconst1);
+
+ /* Optimize exp(log(x)) = x. */
+ fcode = builtin_mathfn_p (arg);
+ if (flag_unsafe_math_optimizations
+ && (fcode == BUILT_IN_LOG
+ || fcode == BUILT_IN_LOGF
+ || fcode == BUILT_IN_LOGL))
+ return TREE_VALUE (TREE_OPERAND (arg, 1));
+ }
+ break;
+
+ case BUILT_IN_LOG:
+ case BUILT_IN_LOGF:
+ case BUILT_IN_LOGL:
+ if (validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+ {
+ enum built_in_function fcode;
+ tree arg = TREE_VALUE (arglist);
+
+ /* Optimize log(1.0) = 0.0. */
+ if (real_onep (arg))
+ return build_real (TREE_TYPE (arg), dconst0);
+
+ /* Optimize log(exp(x)) = x. */
+ fcode = builtin_mathfn_p (arg);
+ if (flag_unsafe_math_optimizations
+ && (fcode == BUILT_IN_EXP
+ || fcode == BUILT_IN_EXPF
+ || fcode == BUILT_IN_EXPL))
+ return TREE_VALUE (TREE_OPERAND (arg, 1));
+
+ /* Optimize log(sqrt(x)) = log(x)/2.0. */
+ if (flag_unsafe_math_optimizations
+ && (fcode == BUILT_IN_SQRT
+ || fcode == BUILT_IN_SQRTF
+ || fcode == BUILT_IN_SQRTL))
+ {
+ tree logfn = build_function_call_expr (fndecl,
+ TREE_OPERAND (arg, 1));
+ return fold (build (RDIV_EXPR, TREE_TYPE (arg), logfn,
+ build_real (TREE_TYPE (arg), dconst2)));
+ }
+ }
+ break;
+
default:
break;
}
*************** fold_builtin (exp)
*** 4169,4175 ****
return 0;
}
! static tree
build_function_call_expr (fn, arglist)
tree fn, arglist;
{
--- 4284,4292 ----
return 0;
}
! /* Conveniently construct a function call expression. */
!
! tree
build_function_call_expr (fn, arglist)
tree fn, arglist;
{
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.349
diff -c -3 -p -r1.349 tree.h
*** tree.h 14 Aug 2002 02:15:40 -0000 1.349
--- tree.h 25 Aug 2002 02:26:19 -0000
*************** extern void rrotate_double PARAMS ((unsi
*** 2845,2853 ****
extern int operand_equal_p PARAMS ((tree, tree, int));
extern tree invert_truthvalue PARAMS ((tree));
! extern tree fold_builtin PARAMS ((tree));
!
! extern tree build_range_type PARAMS ((tree, tree, tree));
/* In alias.c */
extern void record_component_aliases PARAMS ((tree));
--- 2845,2854 ----
extern int operand_equal_p PARAMS ((tree, tree, int));
extern tree invert_truthvalue PARAMS ((tree));
! /* In builtins.c */
! extern tree fold_builtin PARAMS ((tree));
! extern enum built_in_function builtin_mathfn_p PARAMS ((tree));
! extern tree build_function_call_expr PARAMS ((tree, tree));
/* In alias.c */
extern void record_component_aliases PARAMS ((tree));
*************** extern void gcc_obstack_init PARAMS ((s
*** 2894,2899 ****
--- 2895,2901 ----
extern void init_ttree PARAMS ((void));
extern void build_common_tree_nodes PARAMS ((int));
extern void build_common_tree_nodes_2 PARAMS ((int));
+ extern tree build_range_type PARAMS ((tree, tree, tree));
/* In function.c */
extern void setjmp_protect_args PARAMS ((void));
Index: fold-const.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/fold-const.c,v
retrieving revision 1.219
diff -c -3 -p -r1.219 fold-const.c
*** fold-const.c 22 Aug 2002 00:42:39 -0000 1.219
--- fold-const.c 25 Aug 2002 02:26:20 -0000
*************** fold (expr)
*** 5061,5066 ****
--- 5061,5078 ----
}
else if (TREE_CODE (arg0) == ABS_EXPR || TREE_CODE (arg0) == NEGATE_EXPR)
return build1 (ABS_EXPR, type, TREE_OPERAND (arg0, 0));
+ else
+ {
+ /* fabs(sqrt(x)) = sqrt(x) and fabs(exp(x)) = exp(x). */
+ enum built_in_function fcode = builtin_mathfn_p (arg0);
+ if (fcode == BUILT_IN_SQRT
+ || fcode == BUILT_IN_SQRTF
+ || fcode == BUILT_IN_SQRTL
+ || fcode == BUILT_IN_EXP
+ || fcode == BUILT_IN_EXPF
+ || fcode == BUILT_IN_EXPL)
+ t = arg0;
+ }
return t;
case CONJ_EXPR:
*************** fold (expr)
*** 5501,5506 ****
--- 5513,5550 ----
tree arg = save_expr (arg0);
return build (PLUS_EXPR, type, arg, arg);
}
+
+ if (flag_unsafe_math_optimizations)
+ {
+ enum built_in_function fcode0 = builtin_mathfn_p (arg0);
+ enum built_in_function fcode1 = builtin_mathfn_p (arg1);
+
+ /* Optimize sqrt(x)*sqrt(y) as sqrt(x*y). */
+ if ((fcode0 == BUILT_IN_SQRT && fcode1 == BUILT_IN_SQRT)
+ || (fcode0 == BUILT_IN_SQRTF && fcode1 == BUILT_IN_SQRTF)
+ || (fcode0 == BUILT_IN_SQRTL && fcode1 == BUILT_IN_SQRTL))
+ {
+ tree sqrtfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
+ tree arg = build (MULT_EXPR, type,
+ TREE_VALUE (TREE_OPERAND (arg0, 1)),
+ TREE_VALUE (TREE_OPERAND (arg1, 1)));
+ tree arglist = build_tree_list (NULL_TREE, arg);
+ return fold (build_function_call_expr (sqrtfn, arglist));
+ }
+
+ /* Optimize exp(x)*exp(y) as exp(x+y). */
+ if ((fcode0 == BUILT_IN_EXP && fcode1 == BUILT_IN_EXP)
+ || (fcode0 == BUILT_IN_EXPF && fcode1 == BUILT_IN_EXPF)
+ || (fcode0 == BUILT_IN_EXPL && fcode1 == BUILT_IN_EXPL))
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg0, 0), 0);
+ tree arg = build (PLUS_EXPR, type,
+ TREE_VALUE (TREE_OPERAND (arg0, 1)),
+ TREE_VALUE (TREE_OPERAND (arg1, 1)));
+ tree arglist = build_tree_list (NULL_TREE, arg);
+ return fold (build_function_call_expr (expfn, arglist));
+ }
+ }
}
goto associate;
*************** fold (expr)
*** 5668,5673 ****
--- 5712,5734 ----
build (RDIV_EXPR, type, arg0,
TREE_OPERAND (arg1, 0)),
TREE_OPERAND (arg1, 1)));
+ }
+
+ /* Optimize x/exp(y) into x*exp(-y). */
+ if (flag_unsafe_math_optimizations)
+ {
+ enum built_in_function fcode = builtin_mathfn_p (arg1);
+ if (fcode == BUILT_IN_EXP
+ || fcode == BUILT_IN_EXPF
+ || fcode == BUILT_IN_EXPL)
+ {
+ tree expfn = TREE_OPERAND (TREE_OPERAND (arg1, 0), 0);
+ tree arg = build1 (NEGATE_EXPR, type,
+ TREE_VALUE (TREE_OPERAND (arg1, 1)));
+ tree arglist = build_tree_list (NULL_TREE, arg);
+ arg1 = build_function_call_expr (expfn, arglist);
+ return fold (build (MULT_EXPR, type, arg0, arg1));
+ }
}
goto binary;
*** /dev/null Thu Aug 30 14:30:55 2001
--- gcc.dg/builtins-2.c Sat Aug 24 20:39:07 2002
***************
*** 0 ****
--- 1,146 ----
+ /* Copyright (C) 2002 Free Software Foundation.
+
+ Verify that built-in math function constant folding doesn't
+ cause any problems for the compiler.
+
+ Written by Roger Sayle, 16th August 2002. */
+
+ /* { dg-do compile } */
+ /* { dg-options "-O2 -ffast-math" } */
+
+ double test1(double x)
+ {
+ return log(exp(x));
+ }
+
+ double test2(double x)
+ {
+ return exp(log(x));
+ }
+
+ double test3(double x)
+ {
+ return sqrt(exp(x));
+ }
+
+ double test4(double x)
+ {
+ return log(sqrt(x));
+ }
+
+ double test5(double x, double y)
+ {
+ return sqrt(x)*sqrt(y);
+ }
+
+ double test6(double x, double y)
+ {
+ return exp(x)*exp(y);
+ }
+
+ double test7(double x, double y)
+ {
+ return x/exp(y);
+ }
+
+ double test8(double x)
+ {
+ return fabs(sqrt(x));
+ }
+
+ double test9(double x)
+ {
+ return fabs(exp(x));
+ }
+
+ float test1f(float x)
+ {
+ return logf(expf(x));
+ }
+
+ float test2f(float x)
+ {
+ return expf(logf(x));
+ }
+
+ float test3f(float x)
+ {
+ return sqrtf(expf(x));
+ }
+
+ float test4f(float x)
+ {
+ return logf(sqrtf(x));
+ }
+
+ float test5f(float x, float y)
+ {
+ return sqrtf(x)*sqrtf(y);
+ }
+
+ float test6f(float x, float y)
+ {
+ return expf(x)*expf(y);
+ }
+
+ float test7f(float x, float y)
+ {
+ return x/expf(y);
+ }
+
+ float test8f(float x)
+ {
+ return fabsf(sqrtf(x));
+ }
+
+ float test9f(float x)
+ {
+ return fabsf(expf(x));
+ }
+
+ long double test1l(long double x)
+ {
+ return logl(expl(x));
+ }
+
+ long double test2l(long double x)
+ {
+ return expl(logl(x));
+ }
+
+ long double test3l(long double x)
+ {
+ return sqrtl(expl(x));
+ }
+
+ long double test4l(long double x)
+ {
+ return logl(sqrtl(x));
+ }
+
+ long double test5l(long double x, long double y)
+ {
+ return sqrtl(x)*sqrtl(y);
+ }
+
+ long double test6l(long double x, long double y)
+ {
+ return expl(x)*expl(y);
+ }
+
+ long double test7l(long double x, long double y)
+ {
+ return x/expl(y);
+ }
+
+ long double test8l(long double x)
+ {
+ return fabsl(sqrtl(x));
+ }
+
+ long double test9l(long double x)
+ {
+ return fabsl(expl(x));
+ }
+
+
*** /dev/null Thu Aug 30 14:30:55 2001
--- gcc.dg/builtins-3.c Sat Aug 24 20:39:07 2002
***************
*** 0 ****
--- 1,55 ----
+ /* Copyright (C) 2002 Free Software Foundation.
+
+ Verify that built-in math function constant folding of constant
+ arguments is correctly performed by the by the compiler.
+
+ Written by Roger Sayle, 16th August 2002. */
+
+ /* { dg-do link } */
+ /* { dg-options "-O2 -ffast-math" } */
+
+ extern void link_error(void);
+
+ int main()
+ {
+ if (sqrt (0.0) != 0.0)
+ link_error ();
+
+ if (sqrt (1.0) != 1.0)
+ link_error ();
+
+ if (exp (0.0) != 1.0)
+ link_error ();
+
+ if (log (1.0) != 0.0)
+ link_error ();
+
+
+ if (sqrtf (0.0f) != 0.0f)
+ link_error ();
+
+ if (sqrtf (1.0f) != 1.0f)
+ link_error ();
+
+ if (expf (0.0f) != 1.0f)
+ link_error ();
+
+ if (logf (1.0f) != 0.0f)
+ link_error ();
+
+
+ if (sqrtl (0.0l) != 0.0l)
+ link_error ();
+
+ if (sqrtl (1.0l) != 1.0l)
+ link_error ();
+
+ if (expl (0.0l) != 1.0l)
+ link_error ();
+
+ if (logl (1.0l) != 0.0l)
+ link_error ();
+
+ return 0;
+ }
+
Roger
--
Roger Sayle, E-mail: roger@eyesopen.com
OpenEye Scientific Software, WWW: http://www.eyesopen.com/
Suite 1107, 3600 Cerrillos Road, Tel: (+1) 505-473-7385
Santa Fe, New Mexico, 87507. Fax: (+1) 505-473-0833