[PATCH take 2] middle-end: provide generic implementation for isinf

Kaveh R. GHAZI ghazi@caip.rutgers.edu
Sat Jun 16 05:09:00 GMT 2007


This patch is my second attempt at generating generic code to handle
fpclassify functions.  The original thread started here:
http://gcc.gnu.org/ml/gcc-patches/2007-06/msg00752.html

Although I'm calling this "take 2", this time I'm doing "isinf" instead of
"finite".  However I intend to address as many of the fpclassify functions
as possible in later patches if this approach is approved.  I followed
rth's recommendation in PR target/30652 which was to convert isinf(x) to
isgreater(fabs(x),MAX), where MAX is FLT_MAX, DBL_MAX or LDBL_MAX
depending on the type of 'x'.  In order to grab those values, I moved the
MAX generation code from c-cppbuiltin.c to real.c so that the middle-end
could call it as well.

This method should be correct with regards to FP exceptions and
signaling NaNs.  And the transformation occurs only after optabs in
case a particular target wants to provide a specific mechanism that's
better, smaller, faster... queue theme music from "$6M man". :-)

Tested on sparc-sun-solaris2.10, no regressions and the expanded
testcases pass.

Okay for mainline?

		Thanks,
		--Kaveh


2007-06-15  Kaveh R. Ghazi  <ghazi@caip.rutgers.edu>

	PR target/30652

	* builtins.c (expand_builtin_interclass_mathfn): Provide a
	generic fallback for isinf.
	* c-cppbuiltin.c (builtin_define_float_constants): Move FP max
	calculation code ...
	* real.c (get_max_float): ... to here.
	* real.h (get_max_float): New.

testsuite:
	* gcc.dg/pr28796-1.c: Add more cases.
	* gcc.dg/pr28796-2.c: Likewise.

diff -rup orig/egcc-SVN20070615/gcc/builtins.c egcc-SVN20070615/gcc/builtins.c
--- orig/egcc-SVN20070615/gcc/builtins.c	2007-06-12 14:38:41.000000000 -0400
+++ egcc-SVN20070615/gcc/builtins.c	2007-06-15 11:57:55.941544755 -0400
@@ -2271,6 +2271,30 @@ expand_builtin_interclass_mathfn (tree e
       return target;
     }

+  /* If there is no optab, try generic code.  */
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+      tree result;
+
+    CASE_FLT_FN (BUILT_IN_ISINF):
+      {
+	/* isinf(x) -> isgreater(fabs(x),DBL_MAX).  */
+	tree const isle_fn = built_in_decls[BUILT_IN_ISGREATER];
+	tree const type = TREE_TYPE (arg);
+	REAL_VALUE_TYPE r;
+	char buf[128];
+
+	get_max_float (REAL_MODE_FORMAT (mode), buf, sizeof (buf));
+	real_from_string (&r, buf);
+	result = build_call_expr (isle_fn, 2,
+				  fold_build1 (ABS_EXPR, type, arg),
+				  build_real (type, r));
+	return expand_expr (result, target, VOIDmode, EXPAND_NORMAL);
+      }
+    default:
+      break;
+    }
+
   target = expand_call (exp, target, target == const0_rtx);

   return target;
diff -rup orig/egcc-SVN20070615/gcc/c-cppbuiltin.c egcc-SVN20070615/gcc/c-cppbuiltin.c
--- orig/egcc-SVN20070615/gcc/c-cppbuiltin.c	2007-06-07 23:05:51.000000000 -0400
+++ egcc-SVN20070615/gcc/c-cppbuiltin.c	2007-06-15 11:57:48.676026895 -0400
@@ -200,31 +200,8 @@ builtin_define_float_constants (const ch
   /* Since, for the supported formats, B is always a power of 2, we
      construct the following numbers directly as a hexadecimal
      constants.  */
-
-  /* The maximum representable finite floating-point number,
-     (1 - b**-p) * b**emax  */
-  {
-    int i, n;
-    char *p;
-
-    strcpy (buf, "0x0.");
-    n = fmt->p;
-    for (i = 0, p = buf + 4; i + 3 < n; i += 4)
-      *p++ = 'f';
-    if (i < n)
-      *p++ = "08ce"[n - i];
-    sprintf (p, "p%d", fmt->emax);
-    if (fmt->pnan < fmt->p)
-      {
-	/* This is an IBM extended double format made up of two IEEE
-	   doubles.  The value of the long double is the sum of the
-	   values of the two parts.  The most significant part is
-	   required to be the value of the long double rounded to the
-	   nearest double.  Rounding means we need a slightly smaller
-	   value for LDBL_MAX.  */
-	buf[4 + fmt->pnan / 4] = "7bde"[fmt->pnan % 4];
-      }
-  }
+  get_max_float (fmt, buf, sizeof (buf));
+
   sprintf (name, "__%s_MAX__", name_prefix);
   builtin_define_with_hex_fp_value (name, type, decimal_dig, buf, fp_suffix, fp_cast);

diff -rup orig/egcc-SVN20070615/gcc/real.c egcc-SVN20070615/gcc/real.c
--- orig/egcc-SVN20070615/gcc/real.c	2007-06-12 14:38:41.000000000 -0400
+++ egcc-SVN20070615/gcc/real.c	2007-06-15 12:00:08.567831834 -0400
@@ -4835,3 +4835,35 @@ real_isinteger (const REAL_VALUE_TYPE *c
   real_trunc (&cint, mode, c);
   return real_identical (c, &cint);
 }
+
+/* Write into BUF the maximum representable finite floating-point
+   number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
+   float string.  LEN is the size of BUF, and the buffer must be large
+   enough to contain the resulting string.  */
+
+void
+get_max_float (const struct real_format *fmt, char *buf, size_t len)
+{
+  int i, n;
+  char *p;
+
+  strcpy (buf, "0x0.");
+  n = fmt->p;
+  for (i = 0, p = buf + 4; i + 3 < n; i += 4)
+    *p++ = 'f';
+  if (i < n)
+    *p++ = "08ce"[n - i];
+  sprintf (p, "p%d", fmt->emax);
+  if (fmt->pnan < fmt->p)
+    {
+      /* This is an IBM extended double format made up of two IEEE
+	 doubles.  The value of the long double is the sum of the
+	 values of the two parts.  The most significant part is
+	 required to be the value of the long double rounded to the
+	 nearest double.  Rounding means we need a slightly smaller
+	 value for LDBL_MAX.  */
+      buf[4 + fmt->pnan / 4] = "7bde"[fmt->pnan % 4];
+    }
+
+  gcc_assert (strlen (buf) < len);
+}
diff -rup orig/egcc-SVN20070615/gcc/real.h egcc-SVN20070615/gcc/real.h
--- orig/egcc-SVN20070615/gcc/real.h	2007-06-12 14:38:41.000000000 -0400
+++ egcc-SVN20070615/gcc/real.h	2007-06-15 11:55:11.107721214 -0400
@@ -438,4 +438,8 @@ extern void mpfr_from_real (mpfr_ptr, co
 /* Check whether the real constant value given is an integer.  */
 extern bool real_isinteger (const REAL_VALUE_TYPE *c, enum machine_mode mode);

+/* Write into BUF the maximum representable finite floating-point
+   number, (1 - b**-p) * b**emax for a given FP format FMT as a hex
+   float string.  BUF must be large enough to contain the result.  */
+extern void get_max_float (const struct real_format *, char *, size_t);
 #endif /* ! GCC_REAL_H */
diff -rup orig/egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-1.c egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-1.c
--- orig/egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-1.c	2006-10-24 13:50:51.000000000 -0400
+++ egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-1.c	2007-06-15 11:22:18.709713436 -0400
@@ -1,17 +1,48 @@
 /* { dg-do link } */
 /* { dg-options "-ffinite-math-only" } */

+extern void link_error(void);
+
 float f;
+double d;
+long double ld;

 int main()
 {
   if (__builtin_isunordered (f, f) != 0)
     link_error ();
+  if (__builtin_isunordered (d, d) != 0)
+    link_error ();
+  if (__builtin_isunordered (ld, ld) != 0)
+    link_error ();
+
   if (__builtin_isnan (f) != 0)
     link_error ();
+  if (__builtin_isnan (d) != 0)
+    link_error ();
+  if (__builtin_isnan (ld) != 0)
+    link_error ();
+  if (__builtin_isnanf (f) != 0)
+    link_error ();
+  if (__builtin_isnanl (ld) != 0)
+    link_error ();
+
   if (__builtin_finite (f) != 1)
     link_error ();
+  if (__builtin_finite (d) != 1)
+    link_error ();
+  if (__builtin_finite (ld) != 1)
+    link_error ();
+  if (__builtin_finitef (f) != 1)
+    link_error ();
+  if (__builtin_finitel (ld) != 1)
+    link_error ();
+
   if (f != f)
     link_error ();
+  if (d != d)
+    link_error ();
+  if (ld != ld)
+    link_error ();
   return 0;
 }
diff -rup orig/egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-2.c egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-2.c
--- orig/egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-2.c	2007-04-20 20:02:27.000000000 -0400
+++ egcc-SVN20070615/gcc/testsuite/gcc.dg/pr28796-2.c	2007-06-15 11:43:50.265136348 -0400
@@ -4,19 +4,79 @@

 extern void abort (void);

-void foo(float f)
+void __attribute__ ((__noinline__))
+foo_1 (float f, double d, long double ld,
+       int res_unord, int res_isnan, int res_isinf, int res_isfin)
 {
-  if (__builtin_isunordered (f, f) != 1)
+  if (__builtin_isunordered (f, 0) != res_unord)
     abort ();
-  if (__builtin_isnan (f) != 1)
+  if (__builtin_isunordered (0, f) != res_unord)
     abort ();
-  if (__builtin_finite (f) != 0)
+  if (__builtin_isunordered (d, 0) != res_unord)
+    abort ();
+  if (__builtin_isunordered (0, d) != res_unord)
+    abort ();
+  if (__builtin_isunordered (ld, 0) != res_unord)
+    abort ();
+  if (__builtin_isunordered (0, ld) != res_unord)
+    abort ();
+
+  if (__builtin_isnan (f) != res_isnan)
+    abort ();
+  if (__builtin_isnan (d) != res_isnan)
+    abort ();
+  if (__builtin_isnan (ld) != res_isnan)
+    abort ();
+  if (__builtin_isnanf (f) != res_isnan)
+    abort ();
+  if (__builtin_isnanl (ld) != res_isnan)
+    abort ();
+
+  if (__builtin_isinf (f) != res_isinf)
+    abort ();
+  if (__builtin_isinf (d) != res_isinf)
+    abort ();
+  if (__builtin_isinf (ld) != res_isinf)
+    abort ();
+  if (__builtin_isinff (f) != res_isinf)
+    abort ();
+  if (__builtin_isinfl (ld) != res_isinf)
+    abort ();
+
+  if (__builtin_finite (f) != res_isfin)
+    abort ();
+  if (__builtin_finite (d) != res_isfin)
     abort ();
 }

+void __attribute__ ((__noinline__))
+foo (float f, double d, long double ld,
+     int res_unord, int res_isnan, int res_isinf, int res_isfin)
+{
+  foo_1 (f, d, ld, res_unord, res_isnan, res_isinf, res_isfin);
+  foo_1 (-f, -d, -ld, res_unord, res_isnan, res_isinf, res_isfin);
+}
+
 int main()
 {
-  float f = __builtin_nanf("");
-  foo(f);
+  float f;
+  double d;
+  long double ld;
+
+  f = __builtin_nanf(""); d = __builtin_nan(""); ld = __builtin_nanl("");
+  foo(f, d, ld, /*unord=*/ 1, /*isnan=*/ 1, /*isinf=*/ 0, /*isfin=*/ 0);
+
+  f = __builtin_inff(); d = __builtin_inf(); ld = __builtin_infl();
+  foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 1, /*isfin=*/ 0);
+
+  f = 0; d = 0; ld = 0;
+  foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1);
+
+  f = __FLT_MIN__; d = __DBL_MIN__; ld = __LDBL_MIN__;
+  foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1);
+
+  f = __FLT_MAX__; d = __DBL_MAX__; ld = __LDBL_MAX__;
+  foo(f, d, ld, /*unord=*/ 0, /*isnan=*/ 0, /*isinf=*/ 0, /*isfin=*/ 1);
+
   return 0;
 }



More information about the Gcc-patches mailing list