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]

Re: [PATCH] Fold isnan to UNORDERED_EXPR


!isnan(x) && !finite(x) => isinf(x)

Not unless in a truthvalue as isinf (-inf) = -1 on some platforms. But yes, you may also want


	!isnan(x) && !isinf(x) => finite(x)
	!finite(x) && !isinf(x) => isnan(x)

I wonder if this is overkill though.

BTW, finite can be expressed as (x - x) == 0.0 if -fno-trapping-math. The same caveats about soft-float apply... but more about this later.

!isnan(x) && !isnan(y) && isless(x,y) => x < y

The correct folding is "isless (x, y)" unless -fno-trapping-math. Whatever the setting of -fno-trapping-math, this is most easily done if we can get it through these steps:


	ORDERED(x, y) && !UNGE (x, y)
	!(UNORDERED (x, y) || UNGE (x, y)) in invert_truthvalue
	!UNGE (x, y) using combine_comparisons
	LT (x, y) if -fno-trapping-math

All but the first should already be in place. There's the bug (?) in DOM that prevents recursively folding the result of invert_truthvalue. I'll see if Diego's promised full redundancy elimination can do something about it.

It would be more interesting to be able to fold

ORDERED (x, y) && LT (x, y) => !UNGE (x, y)

That's !isnan(x) && !isnan(y) && (x < y) => isless(x, y).

Introducing calls to "isnan" is much harder than calls to "unorddf2",
as we can't ever be sure isnan is available in libm.a, but we can
always rely on functions in libgcc, or to be supported by the target.

Or you can use an isnan optab and libfunc. I tried a quick patch to add a new isnan optab, and enhance prepare_float_lib_cmp to use it for x == x, x != x, ORDERED (x, x) and UNORDERED (x, x). It works if fp-bit.c is used, but it otherwise fails because I cannot see how to do it generically in libgcc2.c (and even if I did it, I would not know how to disable the libgcc2.c version if fp-bit.c is in use).


isnan is not covered by TARGET_C99_FUNCTIONS because C99 mandates to implement it as a macro. OTOH, this means that isnan is implemented rarely enough as a function that we'd rarely lose by expanding it to unorddf2. If a macro, it may as well do ({typeof (x) __z = x; __z != __z}), and that's as bad as unorddf2; or ({typeof (x) __z = x; __builtin_isunordered(__z, __z)}), and there you have it -- unorddf2.

It still seems to me that folding to UNORDERED_EXPR is still a useful thing, because it allows one to easily enter isnan in the fold-const.c machinery, and it does the right thing in 99% of the cases. SAVE_EXPRs also are less of a burden now that they are lowered to temporaries in the gimplification process.

The attached patch bootstrapped and regtested i686-pc-linux-gnu, ok for mainline?

Paolo
gcc/ChangeLog:
2004-06-30  Paolo Bonzini  <bonzini@gnu.org>

	* builtins.c (fold_builtin_classify): New.
	(fold_builtin_unordered_cmp): Fix commentary.
	(fold_builtin): Use it.
	* builtins.def: Define builtins for isnan,
	isnanf, isnanl, finite, finitef, finitel,
	isinf, isinff, isinfl.

gcc/testsuite/ChangeLog:
2004-06-30  Paolo Bonzini  <bonzini@gnu.org>

	* builtins-43.c, builtins-44.c, builtins-45.c: New.

Index: builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.342
diff -u -r1.342 builtins.c
--- builtins.c	20 Jun 2004 17:03:02 -0000	1.342
+++ builtins.c	30 Jun 2004 10:39:52 -0000
@@ -7608,12 +7608,98 @@
   return fold (build1 (ABS_EXPR, type, arg));
 }
 
+/* Fold a call to __builtin_isnan(), __builtin_isinf, __builtin_finite.
+   EXP is the CALL_EXPR for the call.  */
+
+static tree
+fold_builtin_classify (tree exp, int builtin_index)
+{
+  tree fndecl = get_callee_fndecl (exp);
+  tree arglist = TREE_OPERAND (exp, 1);
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  tree arg;
+  REAL_VALUE_TYPE r;
+
+  if (!validate_arglist (arglist, REAL_TYPE, VOID_TYPE))
+    {
+      /* Check that we have exactly one argument.  */
+      if (arglist == 0)
+	{
+	  error ("too few arguments to function `%s'",
+		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+	  return error_mark_node;
+	}
+      else if (TREE_CHAIN (arglist) != 0)
+	{
+	  error ("too many arguments to function `%s'",
+		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+	  return error_mark_node;
+	}
+      else
+	{
+	  error ("non-floating-point argument to function `%s'",
+		 IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+	  return error_mark_node;
+	}
+    }
+
+  arg = TREE_VALUE (arglist);
+  switch (builtin_index)
+    {
+    case BUILT_IN_ISINF:
+      if (!MODE_HAS_INFINITIES (TYPE_MODE (TREE_TYPE (arg))))
+        return omit_one_operand (type, integer_zero_node, arg);
+
+      if (TREE_CODE (arg) == REAL_CST)
+	{
+	  r = TREE_REAL_CST (arg);
+	  if (real_isinf (&r))
+	    return real_compare (GT_EXPR, &r, &dconst0)
+		   ? integer_one_node : integer_minus_one_node;
+	  else
+	    return integer_zero_node;
+	}
+
+      return NULL_TREE;
+
+    case BUILT_IN_FINITE:
+      if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg)))
+          && !MODE_HAS_INFINITIES (TYPE_MODE (TREE_TYPE (arg))))
+        return omit_one_operand (type, integer_zero_node, arg);
+
+      if (TREE_CODE (arg) == REAL_CST)
+	{
+	  r = TREE_REAL_CST (arg);
+	  return real_isinf (&r) || real_isnan (&r)
+		 ? integer_zero_node : integer_one_node;
+	}
+
+      return NULL_TREE;
+
+    case BUILT_IN_ISNAN:
+      if (!MODE_HAS_NANS (TYPE_MODE (TREE_TYPE (arg))))
+        return omit_one_operand (type, integer_zero_node, arg);
+
+      if (TREE_CODE (arg) == REAL_CST)
+	{
+	  r = TREE_REAL_CST (arg);
+	  return real_isnan (&r) ? integer_one_node : integer_zero_node;
+	}
+
+      arg = builtin_save_expr (arg);
+      return fold (build2 (UNORDERED_EXPR, type, arg, arg));
+
+    default:
+      abort ();
+    }
+}
+
 /* Fold a call to an unordered comparison function such as
-   __builtin_isgreater().  ARGLIST is the funtion's argument list
-   and TYPE is the functions return type.  UNORDERED_CODE and
-   ORDERED_CODE are comparison codes that give the opposite of
-   the desired result.  UNORDERED_CODE is used for modes that can
-   hold NaNs and ORDERED_CODE is used for the rest.  */
+   __builtin_isgreater().  EXP is the CALL_EXPR for the call.
+   UNORDERED_CODE and ORDERED_CODE are comparison codes that give
+   the opposite of the desired result.  UNORDERED_CODE is used
+   for modes that can hold NaNs and ORDERED_CODE is used for
+   the rest.  */
 
 static tree
 fold_builtin_unordered_cmp (tree exp,
@@ -8244,6 +8330,21 @@
     case BUILT_IN_COPYSIGNL:
       return fold_builtin_copysign (arglist, type);
 
+    case BUILT_IN_FINITE:
+    case BUILT_IN_FINITEF:
+    case BUILT_IN_FINITEL:
+      return fold_builtin_classify (exp, BUILT_IN_FINITE);
+
+    case BUILT_IN_ISINF:
+    case BUILT_IN_ISINFF:
+    case BUILT_IN_ISINFL:
+      return fold_builtin_classify (exp, BUILT_IN_ISINF);
+
+    case BUILT_IN_ISNAN:
+    case BUILT_IN_ISNANF:
+    case BUILT_IN_ISNANL:
+      return fold_builtin_classify (exp, BUILT_IN_ISNAN);
+
     case BUILT_IN_ISGREATER:
       return fold_builtin_unordered_cmp (exp, UNLE_EXPR, LE_EXPR);
     case BUILT_IN_ISGREATEREQUAL:
Index: builtins.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.def,v
retrieving revision 1.86
diff -u -r1.86 builtins.def
--- builtins.def	28 Jun 2004 21:50:51 -0000	1.86
+++ builtins.def	30 Jun 2004 10:39:52 -0000
@@ -571,6 +571,15 @@
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_GETTEXT, "gettext", BT_FN_STRING_CONST_STRING, ATTR_FORMAT_ARG_1)
 DEF_C99_BUILTIN        (BUILT_IN_IMAXABS, "imaxabs", BT_FN_INTMAX_INTMAX, ATTR_CONST_NOTHROW_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_INIT_DWARF_REG_SIZES, "init_dwarf_reg_size_table", BT_FN_VOID_PTR, ATTR_NULL)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FINITE, "finite", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FINITEF, "finitef", BT_FN_INT_FLOAT, ATTR_CONST_NOTHROW_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_FINITEL, "finitel", BT_FN_INT_LONGDOUBLE, ATTR_CONST_NOTHROW_LIST)
+DEF_C99_C90RES_BUILTIN (BUILT_IN_ISINF, "isinf", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_ISINFF, "isinff", BT_FN_INT_FLOAT, ATTR_CONST_NOTHROW_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_ISINFL, "isinfl", BT_FN_INT_LONGDOUBLE, ATTR_CONST_NOTHROW_LIST)
+DEF_C99_C90RES_BUILTIN (BUILT_IN_ISNAN, "isnan", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_ISNANF, "isnanf", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_ISNANL, "isnanl", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_ISGREATER, "isgreater", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_ISGREATEREQUAL, "isgreaterequal", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_ISLESS, "isless", BT_FN_INT_VAR, ATTR_CONST_NOTHROW_LIST)
Index: testsuite/gcc.dg/builtins-43.c
===================================================================
RCS file: testsuite/gcc.dg/builtins-43.c
diff -N testsuite/gcc.dg/builtins-43.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gcc.dg/builtins-43.c	30 Jun 2004 10:39:52 -0000
@@ -0,0 +1,53 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -fno-trapping-math -fdump-tree-generic -fdump-tree-optimized" } */
+  
+extern void f(int);
+extern void link_error ();
+
+extern float x;
+extern double y;
+extern long double z;
+
+int
+main ()
+{
+  double nan = __builtin_nan ("");
+  float nanf = __builtin_nanf ("");
+  long double nanl = __builtin_nanl ("");
+
+  if (!__builtin_isnan (nan))
+    link_error ();
+  if (!__builtin_isnan (nanf))
+    link_error ();
+  if (!__builtin_isnanf (nanf))
+    link_error ();
+  if (!__builtin_isnan (nanl))
+    link_error ();
+  if (!__builtin_isnanl (nanl))
+    link_error ();
+
+  if (__builtin_isnan (4.0))
+    link_error ();
+  if (__builtin_isnan (4.0))
+    link_error ();
+  if (__builtin_isnanf (4.0))
+    link_error ();
+  if (__builtin_isnan (4.0))
+    link_error ();
+  if (__builtin_isnanl (4.0))
+    link_error ();
+
+  f (__builtin_isnan (x));
+  f (__builtin_isnan (y));
+  f (__builtin_isnanf (y));
+  f (__builtin_isnan (z));
+  f (__builtin_isnanl (z));
+}
+
+
+/* Check that all instances of __builtin_isnan were folded.  */
+/* { dg-final { scan-tree-dump-times "isnan" 0 "generic" } } */
+
+/* Check that all instances of link_error were subject to DCE.  */
+/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */
+
Index: testsuite/gcc.dg/builtins-44.c
===================================================================
RCS file: testsuite/gcc.dg/builtins-44.c
diff -N testsuite/gcc.dg/builtins-44.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gcc.dg/builtins-44.c	30 Jun 2004 10:39:52 -0000
@@ -0,0 +1,55 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -fno-trapping-math -fdump-tree-optimized" } */
+  
+extern void f(int);
+extern void link_error ();
+
+extern float x;
+extern double y;
+extern long double z;
+
+int
+main ()
+{
+  double pinf = __builtin_inf ();
+  float pinff = __builtin_inff ();
+  long double pinfl = __builtin_infl ();
+
+  if (__builtin_isinf (pinf) != 1)
+    link_error ();
+  if (__builtin_isinf (pinff) != 1)
+    link_error ();
+  if (__builtin_isinff (pinff) != 1)
+    link_error ();
+  if (__builtin_isinf (pinfl) != 1)
+    link_error ();
+  if (__builtin_isinfl (pinfl) != 1)
+    link_error ();
+
+  if (__builtin_isinf (-pinf) != -1)
+    link_error ();
+  if (__builtin_isinf (-pinff) != -1)
+    link_error ();
+  if (__builtin_isinff (-pinff) != -1)
+    link_error ();
+  if (__builtin_isinf (-pinfl) != -1)
+    link_error ();
+  if (__builtin_isinfl (-pinfl) != -1)
+    link_error ();
+
+  if (__builtin_isinf (4.0))
+    link_error ();
+  if (__builtin_isinf (4.0))
+    link_error ();
+  if (__builtin_isinff (4.0))
+    link_error ();
+  if (__builtin_isinf (4.0))
+    link_error ();
+  if (__builtin_isinfl (4.0))
+    link_error ();
+}
+
+
+/* Check that all instances of link_error were subject to DCE.  */
+/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */
+
Index: testsuite/gcc.dg/builtins-45.c
===================================================================
RCS file: testsuite/gcc.dg/builtins-45.c
diff -N testsuite/gcc.dg/builtins-45.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gcc.dg/builtins-45.c	30 Jun 2004 10:39:52 -0000
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O1 -fno-trapping-math -fdump-tree-optimized" } */
+  
+extern void f(int);
+extern void link_error ();
+
+extern float x;
+extern double y;
+extern long double z;
+
+int
+main ()
+{
+  double nan = __builtin_nan ("");
+  float nanf = __builtin_nanf ("");
+  long double nanl = __builtin_nanl ("");
+
+  double pinf = __builtin_inf ();
+  float pinff = __builtin_inff ();
+  long double pinfl = __builtin_infl ();
+
+  if (__builtin_finite (pinf))
+    link_error ();
+  if (__builtin_finitef (pinff))
+    link_error ();
+  if (__builtin_finitel (pinfl))
+    link_error ();
+
+  if (__builtin_finite (nan))
+    link_error ();
+  if (__builtin_finitef (nanf))
+    link_error ();
+  if (__builtin_finitel (nanl))
+    link_error ();
+
+  if (!__builtin_finite (4.0))
+    link_error ();
+  if (!__builtin_finitef (4.0))
+    link_error ();
+  if (!__builtin_finitel (4.0))
+    link_error ();
+}
+
+
+/* Check that all instances of link_error were subject to DCE.  */
+/* { dg-final { scan-tree-dump-times "link_error" 0 "optimized" } } */
+

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