This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH]: PR middle-end/29335 use MPFR for cabs, and fold complex sign ops
- From: "Kaveh R. GHAZI" <ghazi at caip dot rutgers dot edu>
- To: gcc-patches at gcc dot gnu dot org
- Date: Tue, 23 Jan 2007 02:51:12 -0500 (EST)
- Subject: [PATCH]: PR middle-end/29335 use MPFR for cabs, and fold complex sign ops
This patch enhances sign folding for complex math functions, similar to
what we already do for the regular ones. I also used MPFR for cabs with
constant args. (Hopefully all of these will be less controversial than
the csin folding...)
Anyway, the details of the changes are listed below:
1. When cabs has a constant arg, it currently uses the real.c routines to
calculate the result at compile-time using a simplistic sqrt(r*r+i*i).
While this is more accurate than the same calculation in the host float
precision, my understanding is that MPFR goes to extra lengths to ensure
exact results in its hypot routine. By getting exact results from MPFR,
it also allows me to remove the check on -funsafe-math-optimizations.
One nit is that the MPFR infrastructure I wrote is setup *not* to do the
transformation if the argument (or return value) is Inf or NaN. This is to
avoid having to special-case each c99 function as to how they handle these
values and their signs as well as possibly generating runtime exceptions
and/or errors. I'd like to keep it that way.
The current cabs hand-calculation allows Inf/NaN only if -fno-traping-math
is used (in addition to -funsafe-math-optimizations as noted above). My
change stops GCC from compile-time calculating cabs of Inf/NaN under those
circumstances in return for greater accuracy for all other values as well
as not requiring the -f*math* flags. Hopefully this won't be considered a
big deal.
2. I changed folding of cabs, which was hand detecting negate or conj
argument, to instead use fold_strip_sign_ops. This handles many more
cases and is also recursive.
3. I treat ccos and ccosh as "even" functions, just like we already do
for cos/cosh/fabs and cabs. This enables them to strip sign ops from
their argument.
4. I treat casin, casinh, catan, catanh, csin, csinh, ctan and ctanh as
'odd' functions. This allows various sign stripping opportunities.
5. I add a few more EXPRs to fold_strip_sign_ops to handle various cases
of complex functions and/or complex arguments.
6. Finally of course, I add a bunch of wierd testcases to exercise all
these new transformations.
Arguably, #1 is conceptually separate from the rest. I can split it out
if necessary. Hopefully not. :-)
Bootstrapped on sparc-sun-solaris2.10 (with --disable-libgcj due to
PR30513) and the libstdc++ testsuite is useless because of the libiconv
problems. With those caveats, I had no regressions and the new cases all
pass.
Okay for mainline?
Thanks,
--Kaveh
2007-01-21 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* builtins.c (fold_builtin_cabs): Use fold_strip_sign_ops
instead of manually checking for EXPRs.
(fold_builtin_1): Treat ccos and ccosh as 'even' functions.
* fold-const.c (negate_mathfn_p): Treat casin, casinh, catan,
catanh, csin, csinh, ctan and ctanh as 'odd' functions.
(fold_strip_sign_ops): Handle CONJ_EXPR, REALPART_EXPR,
IMAGPART_EXPR and COMPLEX_EXPR.
PR middle-end/29335
* builtins.c (fold_builtin_cabs): Use MPFR to evaluate a
constant argument to cabs and do it without checking for
-funsafe-math-optimizations.
testsuite:
* gcc.dg/builtins-54.c: Add more cases.
* gcc.dg/torture/builtin-symmetric-1.c: Likewise.
diff -rup orig/egcc-SVN20070120/gcc/builtins.c egcc-SVN20070120/gcc/builtins.c
--- orig/egcc-SVN20070120/gcc/builtins.c 2007-01-19 20:02:40.000000000 -0500
+++ egcc-SVN20070120/gcc/builtins.c 2007-01-22 19:50:28.764379415 -0500
@@ -7016,7 +7016,7 @@ fold_fixed_mathfn (tree fndecl, tree arg
static tree
fold_builtin_cabs (tree arglist, tree type, tree fndecl)
{
- tree arg;
+ tree arg, narg, res;
if (!arglist || TREE_CHAIN (arglist))
return NULL_TREE;
@@ -7026,27 +7026,12 @@ fold_builtin_cabs (tree arglist, tree ty
|| TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != REAL_TYPE)
return NULL_TREE;
- /* Evaluate cabs of a constant at compile-time. */
- if (flag_unsafe_math_optimizations
- && TREE_CODE (arg) == COMPLEX_CST
- && TREE_CODE (TREE_REALPART (arg)) == REAL_CST
- && TREE_CODE (TREE_IMAGPART (arg)) == REAL_CST
- && !TREE_OVERFLOW (TREE_REALPART (arg))
- && !TREE_OVERFLOW (TREE_IMAGPART (arg)))
- {
- REAL_VALUE_TYPE r, i;
-
- r = TREE_REAL_CST (TREE_REALPART (arg));
- i = TREE_REAL_CST (TREE_IMAGPART (arg));
-
- real_arithmetic (&r, MULT_EXPR, &r, &r);
- real_arithmetic (&i, MULT_EXPR, &i, &i);
- real_arithmetic (&r, PLUS_EXPR, &r, &i);
- if (real_sqrt (&r, TYPE_MODE (type), &r)
- || ! flag_trapping_math)
- return build_real (type, r);
- }
-
+ /* Calculate the result when the argument is a constant. */
+ if (TREE_CODE (arg) == COMPLEX_CST
+ && (res = do_mpfr_arg2 (TREE_REALPART (arg), TREE_IMAGPART (arg),
+ type, mpfr_hypot)))
+ return res;
+
/* If either part is zero, cabs is fabs of the other. */
if (TREE_CODE (arg) == COMPLEX_EXPR
&& real_zerop (TREE_OPERAND (arg, 0)))
@@ -7056,12 +7041,9 @@ fold_builtin_cabs (tree arglist, tree ty
return fold_build1 (ABS_EXPR, type, TREE_OPERAND (arg, 0));
/* Optimize cabs(-z) and cabs(conj(z)) as cabs(z). */
- if (TREE_CODE (arg) == NEGATE_EXPR
- || TREE_CODE (arg) == CONJ_EXPR)
- {
- tree arglist = build_tree_list (NULL_TREE, TREE_OPERAND (arg, 0));
- return build_function_call_expr (fndecl, arglist);
- }
+ if ((narg = fold_strip_sign_ops (arg)))
+ return build_function_call_expr (fndecl,
+ build_tree_list (NULL_TREE, narg));
/* Don't do this when optimizing for size. */
if (flag_unsafe_math_optimizations
@@ -9331,6 +9313,17 @@ fold_builtin_1 (tree fndecl, tree arglis
TREE_VALUE (arglist)));
break;
+ CASE_FLT_FN (BUILT_IN_CCOS):
+ CASE_FLT_FN (BUILT_IN_CCOSH):
+ if (validate_arglist (arglist, COMPLEX_TYPE, VOID_TYPE))
+ {
+ tree narg = fold_strip_sign_ops (TREE_VALUE (arglist));
+ if (narg)
+ return build_function_call_expr (fndecl,
+ build_tree_list (NULL_TREE, narg));
+ }
+ break;
+
CASE_FLT_FN (BUILT_IN_CABS):
return fold_builtin_cabs (arglist, type, fndecl);
diff -rup orig/egcc-SVN20070120/gcc/fold-const.c egcc-SVN20070120/gcc/fold-const.c
--- orig/egcc-SVN20070120/gcc/fold-const.c 2007-01-18 20:02:47.000000000 -0500
+++ egcc-SVN20070120/gcc/fold-const.c 2007-01-22 20:48:51.534542972 -0500
@@ -913,7 +913,15 @@ negate_mathfn_p (enum built_in_function
CASE_FLT_FN (BUILT_IN_ASINH):
CASE_FLT_FN (BUILT_IN_ATAN):
CASE_FLT_FN (BUILT_IN_ATANH):
+ CASE_FLT_FN (BUILT_IN_CASIN):
+ CASE_FLT_FN (BUILT_IN_CASINH):
+ CASE_FLT_FN (BUILT_IN_CATAN):
+ CASE_FLT_FN (BUILT_IN_CATANH):
CASE_FLT_FN (BUILT_IN_CBRT):
+ CASE_FLT_FN (BUILT_IN_CSIN):
+ CASE_FLT_FN (BUILT_IN_CSINH):
+ CASE_FLT_FN (BUILT_IN_CTAN):
+ CASE_FLT_FN (BUILT_IN_CTANH):
CASE_FLT_FN (BUILT_IN_ERF):
CASE_FLT_FN (BUILT_IN_LLROUND):
CASE_FLT_FN (BUILT_IN_LROUND):
@@ -13640,13 +13648,23 @@ fold_strip_sign_ops (tree exp)
{
case ABS_EXPR:
case NEGATE_EXPR:
+ case CONJ_EXPR:
arg0 = fold_strip_sign_ops (TREE_OPERAND (exp, 0));
return arg0 ? arg0 : TREE_OPERAND (exp, 0);
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ arg0 = fold_strip_sign_ops (TREE_OPERAND (exp, 0));
+ if (arg0)
+ return fold_build1 (TREE_CODE (exp), TREE_TYPE (exp), arg0);
+ break;
+
case MULT_EXPR:
case RDIV_EXPR:
if (HONOR_SIGN_DEPENDENT_ROUNDING (TYPE_MODE (TREE_TYPE (exp))))
return NULL_TREE;
+ /* Fall thru */
+ case COMPLEX_EXPR:
arg0 = fold_strip_sign_ops (TREE_OPERAND (exp, 0));
arg1 = fold_strip_sign_ops (TREE_OPERAND (exp, 1));
if (arg0 != NULL_TREE || arg1 != NULL_TREE)
diff -rup orig/egcc-SVN20070120/gcc/testsuite/gcc.dg/builtins-54.c egcc-SVN20070120/gcc/testsuite/gcc.dg/builtins-54.c
--- orig/egcc-SVN20070120/gcc/testsuite/gcc.dg/builtins-54.c 2006-05-31 20:00:17.000000000 -0400
+++ egcc-SVN20070120/gcc/testsuite/gcc.dg/builtins-54.c 2007-01-22 21:39:17.506416355 -0500
@@ -1,44 +1,251 @@
/* { dg-do link } */
/* { dg-options "-O2 -ffast-math" } */
+double fabs(double);
+float fabsf(float);
+long double fabsl(long double);
+double cos(double);
+float cosf(float);
+long double cosl(long double);
+double tan(double);
+float tanf(float);
+long double tanl(long double);
double cabs(__complex__ double);
float cabsf(__complex__ float);
long double cabsl(__complex__ long double);
+__complex__ double conj(__complex__ double);
+__complex__ float conjf(__complex__ float);
+__complex__ long double conjl(__complex__ long double);
+__complex__ double ccos(__complex__ double);
+__complex__ float ccosf(__complex__ float);
+__complex__ long double ccosl(__complex__ long double);
+__complex__ double ctan(__complex__ double);
+__complex__ float ctanf(__complex__ float);
+__complex__ long double ctanl(__complex__ long double);
void link_error (void);
-void test(__complex__ double x)
+void test(__complex__ double x, __complex__ double y, int i)
{
if (cabs(x) != cabs(-x))
link_error();
if (cabs(x) != cabs(~x))
link_error();
+
+ if (cabs(x) != cabs(conj(x)))
+ link_error();
+
+ if (fabs(__imag__ x) != cabs(__imag__ -conj(x)))
+ link_error();
+
+ if (fabs(x) != cabs(__real__ ~ x))
+ link_error();
+
+ if (cabs(x) != fabs(cabs(x)))
+ link_error();
+
+ if (fabs(x) != cabs(fabs(x)))
+ link_error();
+
+ if (fabs(x) != fabs(conj(x)))
+ link_error();
+
+ if (fabs(x) != fabs(- x))
+ link_error();
+
+ if (fabs(__imag__ x) != cabs(fabs(__imag__ - x)))
+ link_error();
+
+ if (ccos(x) != ccos(conj(x)))
+ link_error();
+
+ if (ccos(__real__ x) != ccos(__real__ - x))
+ link_error();
+
+ if (ccos(ctan(x)) != ccos(ctan(-conj(x))))
+ link_error();
+
+ if (ctan(x-y) != -ctan(y-x))
+ link_error();
+
+ if (ccos(x/y) != ccos(-x/y))
+ link_error();
+
+ if (ccos(x/y) != ccos(x/-y))
+ link_error();
+
+ if (ccos(x/y) != ccos(-x/-y))
+ link_error();
+
+ if (ccos(x*y) != ccos(-x*y))
+ link_error();
+
+ if (ccos(x*y) != ccos(x*-y))
+ link_error();
+
+ if (ccos(x*y) != ccos(-x*-y))
+ link_error();
+
+ if (ccos(ctan(x/y)) != ccos(-conj(ctan(x/-y))))
+ link_error();
+
+ if (fabs(__imag__ ccos(tan(__real__ x / __imag__ y))) != fabs(__imag__ ~ccos(-conj(-tan(__real__ x / - __imag__ y)))))
+ link_error();
+
+ if (cabs(i ? x : y) != cabs(i ? -x : ~y))
+ link_error();
}
-void testf(__complex__ float x)
+void testf(__complex__ float x, __complex__ float y, int i)
{
if (cabsf(x) != cabsf(-x))
link_error();
if (cabsf(x) != cabsf(~x))
link_error();
+
+ if (cabsf(x) != cabsf(conjf(x)))
+ link_error();
+
+ if (fabsf(__imag__ x) != cabsf(__imag__ -conjf(x)))
+ link_error();
+
+ if (fabsf(x) != cabsf(__real__ ~ x))
+ link_error();
+
+ if (cabsf(x) != fabsf(cabsf(x)))
+ link_error();
+
+ if (fabsf(x) != cabsf(fabsf(x)))
+ link_error();
+
+ if (fabsf(x) != fabsf(conjf(x)))
+ link_error();
+
+ if (fabsf(x) != fabsf(- x))
+ link_error();
+
+ if (fabsf(__imag__ x) != cabsf(fabsf(__imag__ - x)))
+ link_error();
+
+ if (ccosf(x) != ccosf(conjf(x)))
+ link_error();
+
+ if (ccosf(__real__ x) != ccosf(__real__ - x))
+ link_error();
+
+ if (ccosf(ctanf(x)) != ccosf(ctanf(-conjf(x))))
+ link_error();
+
+ if (ctanf(x-y) != -ctanf(y-x))
+ link_error();
+
+ if (ccosf(x/y) != ccosf(-x/y))
+ link_error();
+
+ if (ccosf(x/y) != ccosf(x/-y))
+ link_error();
+
+ if (ccosf(x/y) != ccosf(-x/-y))
+ link_error();
+
+ if (ccosf(x*y) != ccosf(-x*y))
+ link_error();
+
+ if (ccosf(x*y) != ccosf(x*-y))
+ link_error();
+
+ if (ccosf(x*y) != ccosf(-x*-y))
+ link_error();
+
+ if (ccosf(ctanf(x/y)) != ccosf(-conjf(ctanf(x/-y))))
+ link_error();
+
+ if (fabsf(__imag__ ccosf(tanf(__real__ x / __imag__ y))) != fabsf(__imag__ ~ccosf(-conjf(-tanf(__real__ x / - __imag__ y)))))
+ link_error();
+
+ if (cabsf(i ? x : y) != cabsf(i ? -x : ~y))
+ link_error();
}
-void testl(__complex__ long double x)
+void testl(__complex__ long double x, __complex__ long double y, int i)
{
if (cabsl(x) != cabsl(-x))
link_error();
if (cabsl(x) != cabsl(~x))
link_error();
+
+ if (cabsl(x) != cabsl(conjl(x)))
+ link_error();
+
+ if (fabsl(__imag__ x) != cabsl(__imag__ -conjl(x)))
+ link_error();
+
+ if (fabsl(x) != cabsl(__real__ ~ x))
+ link_error();
+
+ if (cabsl(x) != fabsl(cabsl(x)))
+ link_error();
+
+ if (fabsl(x) != cabsl(fabsl(x)))
+ link_error();
+
+ if (fabsl(x) != fabsl(conjl(x)))
+ link_error();
+
+ if (fabsl(x) != fabsl(- x))
+ link_error();
+
+ if (fabsl(__imag__ x) != cabsl(fabsl(__imag__ - x)))
+ link_error();
+
+ if (ccosl(x) != ccosl(conjl(x)))
+ link_error();
+
+ if (ccosl(__real__ x) != ccosl(__real__ - x))
+ link_error();
+
+ if (ccosl(ctanl(x)) != ccosl(ctanl(-conjl(x))))
+ link_error();
+
+ if (ctanl(x-y) != -ctanl(y-x))
+ link_error();
+
+ if (ccosl(x/y) != ccosl(-x/y))
+ link_error();
+
+ if (ccosl(x/y) != ccosl(x/-y))
+ link_error();
+
+ if (ccosl(x/y) != ccosl(-x/-y))
+ link_error();
+
+ if (ccosl(x*y) != ccosl(-x*y))
+ link_error();
+
+ if (ccosl(x*y) != ccosl(x*-y))
+ link_error();
+
+ if (ccosl(x*y) != ccosl(-x*-y))
+ link_error();
+
+ if (ccosl(ctanl(x/y)) != ccosl(-conjl(ctanl(x/-y))))
+ link_error();
+
+ if (fabsl(__imag__ ccosl(tanl(__real__ x / __imag__ y))) != fabsl(__imag__ ~ccosl(-conjl(-tanl(__real__ x / - __imag__ y)))))
+ link_error();
+
+ if (cabsl(i ? x : y) != cabsl(i ? -x : ~y))
+ link_error();
}
int main()
{
- test(0.0);
- testf(0.0);
- testl(0.0);
+ test(0, 0, 0);
+ testf(0, 0, 0);
+ testl(0, 0, 0);
return 0;
}
diff -rup orig/egcc-SVN20070120/gcc/testsuite/gcc.dg/torture/builtin-symmetric-1.c egcc-SVN20070120/gcc/testsuite/gcc.dg/torture/builtin-symmetric-1.c
--- orig/egcc-SVN20070120/gcc/testsuite/gcc.dg/torture/builtin-symmetric-1.c 2006-11-12 20:01:31.000000000 -0500
+++ egcc-SVN20070120/gcc/testsuite/gcc.dg/torture/builtin-symmetric-1.c 2007-01-22 19:50:28.788669834 -0500
@@ -21,6 +21,16 @@ extern void link_error(int);
link_error(__LINE__); \
} while (0)
+/* Test that FUNC(-ARG) == FUNC(ARG), where ARG has a complex type. */
+#define TESTIT_EVEN_C(FUNC) do { \
+ if (__builtin_##FUNC##f(-cxf) != __builtin_##FUNC##f(cxf)) \
+ link_error(__LINE__); \
+ if (__builtin_##FUNC(-cx) != __builtin_##FUNC(cx)) \
+ link_error(__LINE__); \
+ if (__builtin_##FUNC##l(-cxl) != __builtin_##FUNC##l(cxl)) \
+ link_error(__LINE__); \
+ } while (0)
+
/* Test that FUNC(-VAR) == FUNC(VAR), where VAR has an int type. */
#define TESTIT_EVEN_I(FUNC,VAR) do { \
if (__builtin_##FUNC(-VAR) != __builtin_##FUNC(VAR)) \
@@ -37,13 +47,28 @@ extern void link_error(int);
link_error(__LINE__); \
} while (0)
+/* Test that -FUNC(ARG) == FUNC(-ARG), where ARG has a complex type. */
+#define TESTIT_ODD_C(FUNC) do { \
+ if (-__builtin_##FUNC##f(-cxf) != __builtin_##FUNC##f(cxf)) \
+ link_error(__LINE__); \
+ if (-__builtin_##FUNC(-cx) != __builtin_##FUNC(cx)) \
+ link_error(__LINE__); \
+ if (-__builtin_##FUNC##l(-cxl) != __builtin_##FUNC##l(cxl)) \
+ link_error(__LINE__); \
+ } while (0)
+
void foo (float xf, double x, long double xl,
+ __complex__ float cxf, __complex__ double cx, __complex__ long double cxl,
int i, long l, long long ll, __INTMAX_TYPE__ im)
{
TESTIT_EVEN(cos);
TESTIT_EVEN(cosh);
TESTIT_EVEN(fabs);
+ TESTIT_EVEN_C(ccos);
+ TESTIT_EVEN_C(ccosh);
+ TESTIT_EVEN_C(cabs);
+
TESTIT_EVEN_I(abs, i);
TESTIT_EVEN_I(imaxabs, im);
TESTIT_EVEN_I(labs, l);
@@ -67,10 +92,19 @@ void foo (float xf, double x, long doubl
TESTIT_ODD(tan);
TESTIT_ODD(tanh);
TESTIT_ODD(trunc);
+
+ TESTIT_ODD_C(casin);
+ TESTIT_ODD_C(casinh);
+ TESTIT_ODD_C(catan);
+ TESTIT_ODD_C(catanh);
+ TESTIT_ODD_C(csin);
+ TESTIT_ODD_C(csinh);
+ TESTIT_ODD_C(ctan);
+ TESTIT_ODD_C(ctanh);
}
int main()
{
- foo (1,1,1,1,1,1,1);
+ foo (1,1,1,1,1,1,1,1,1,1);
return 0;
}