This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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;
 }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]