S/390: Do not raise inexact exception in lrint/lround functions in overflow case.

Message ID n7b1fe$eap$1@ger.gmane.org
State Committed
Headers

Commit Message

Stefan Liebler Jan. 15, 2016, 2:58 p.m. UTC
  Hi,

I get some math test-failures on s390 for float/double/ldouble for
various lrint/lround functions like:
lrint (0x1p64): Exception "Inexact" set
lrint (-0x1p64): Exception "Inexact" set
lround (0x1p64): Exception "Inexact" set
lround (-0x1p64): Exception "Inexact" set
...

GCC emits "convert to fixed" instructions for casting floating point
values to integer values. These instructions raise invalid and inexact
exceptions if the floating point value exceeds the integer type ranges.

This patch enables the various FIX_DBL_LONG_CONVERT_OVERFLOW macros in
order to avoid a cast from floating point to integer type and raise the
invalid exception with feraiseexcept.
The ldbl-128 rint/round functions are now using the same logic.

Bye Stefan

---
ChangeLog:

	* sysdeps/s390/fix-fp-int-convert-overflow.h: New File.
	* sysdeps/generic/fix-fp-int-convert-overflow.h
	(FIX_LDBL_LONG_CONVERT_OVERFLOW,
	FIX_LDBL_LLONG_CONVERT_OVERFLOW): New define.
	* sysdeps/arm/fix-fp-int-convert-overflow.h: Likewise.
	* sysdeps/mips/mips32/fpu/fix-fp-int-convert-overflow.h:
	Likewise.
	* sysdeps/ieee754/ldbl-128/s_lrintl.c (__lrintl):
	Avoid conversions to long int where inexact exceptions
	could be raised.
	* sysdeps/ieee754/ldbl-128/s_lroundl.c (__lroundl):
	Likewise.
	* sysdeps/ieee754/ldbl-128/s_llrintl.c (__llrintl):
	Avoid conversions to long long int where inexact exceptions
	could be raised.
	* sysdeps/ieee754/ldbl-128/s_llroundl.c (__llroundl):
	Likewise.
  

Comments

Joseph Myers Jan. 15, 2016, 10:24 p.m. UTC | #1
On Fri, 15 Jan 2016, Stefan Liebler wrote:

> Hi,
> 
> I get some math test-failures on s390 for float/double/ldouble for
> various lrint/lround functions like:
> lrint (0x1p64): Exception "Inexact" set
> lrint (-0x1p64): Exception "Inexact" set
> lround (0x1p64): Exception "Inexact" set
> lround (-0x1p64): Exception "Inexact" set
> ...
> 
> GCC emits "convert to fixed" instructions for casting floating point
> values to integer values. These instructions raise invalid and inexact
> exceptions if the floating point value exceeds the integer type ranges.
> 
> This patch enables the various FIX_DBL_LONG_CONVERT_OVERFLOW macros in
> order to avoid a cast from floating point to integer type and raise the
> invalid exception with feraiseexcept.
> The ldbl-128 rint/round functions are now using the same logic.

OK, given the usual bug filed in Bugzilla for a user-visible issue, listed 
in the ChangeLog entry and marked as FIXED with the appropriate milestone 
set when the fix is committed.
  

Patch

commit f0c5fb9ece180de6dcf069910b6fea0bf6f7a3ba
Author: Stefan Liebler <stli@linux.vnet.ibm.com>
Date:   Thu Jan 14 17:26:25 2016 +0100

    S/390: Do not raise inexact exception in lrint/lround functions in overflow case.
    
    GCC emits "convert to fixed" instructions for casting floating point values
    to integer values. These instructions raise invalid and inexact exceptions
    if the floating point value exceeds the integer type ranges.
    
    This patch enables the various FIX_DBL_LONG_CONVERT_OVERFLOW macros in order
    to avoid a cast from floating point to integer type and raise the invalid exception
    with feraiseexcept.
    The ldbl-128 rint/round functions are now using the same logic.

diff --git a/sysdeps/arm/fix-fp-int-convert-overflow.h b/sysdeps/arm/fix-fp-int-convert-overflow.h
index fcc2199..c5ec50a 100644
--- a/sysdeps/arm/fix-fp-int-convert-overflow.h
+++ b/sysdeps/arm/fix-fp-int-convert-overflow.h
@@ -25,8 +25,10 @@ 
    see <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59412>).  */
 #define FIX_FLT_LLONG_CONVERT_OVERFLOW 1
 #define FIX_DBL_LLONG_CONVERT_OVERFLOW 1
+#define FIX_LDBL_LLONG_CONVERT_OVERFLOW 0
 
 #define FIX_FLT_LONG_CONVERT_OVERFLOW 0
 #define FIX_DBL_LONG_CONVERT_OVERFLOW 0
+#define FIX_LDBL_LONG_CONVERT_OVERFLOW 0
 
 #endif /* fix-fp-int-convert-overflow.h */
diff --git a/sysdeps/generic/fix-fp-int-convert-overflow.h b/sysdeps/generic/fix-fp-int-convert-overflow.h
index 73884a6..fb68114 100644
--- a/sysdeps/generic/fix-fp-int-convert-overflow.h
+++ b/sysdeps/generic/fix-fp-int-convert-overflow.h
@@ -27,5 +27,7 @@ 
 #define FIX_FLT_LLONG_CONVERT_OVERFLOW 0
 #define FIX_DBL_LONG_CONVERT_OVERFLOW 0
 #define FIX_DBL_LLONG_CONVERT_OVERFLOW 0
+#define FIX_LDBL_LONG_CONVERT_OVERFLOW 0
+#define FIX_LDBL_LLONG_CONVERT_OVERFLOW 0
 
 #endif /* fix-fp-int-convert-overflow.h */
diff --git a/sysdeps/ieee754/ldbl-128/s_llrintl.c b/sysdeps/ieee754/ldbl-128/s_llrintl.c
index 445cde5..84fc576 100644
--- a/sysdeps/ieee754/ldbl-128/s_llrintl.c
+++ b/sysdeps/ieee754/ldbl-128/s_llrintl.c
@@ -24,6 +24,7 @@ 
 #include <math.h>
 
 #include <math_private.h>
+#include <fix-fp-int-convert-overflow.h>
 
 static const long double two112[2] =
 {
@@ -91,6 +92,12 @@  __llrintl (long double x)
 	  feraiseexcept (t == LLONG_MIN ? FE_INEXACT : FE_INVALID);
 	  return LLONG_MIN;
 	}
+      else if (FIX_LDBL_LLONG_CONVERT_OVERFLOW && x != (long double) LLONG_MIN)
+	{
+	  feraiseexcept (FE_INVALID);
+	  return sx == 0 ? LLONG_MAX : LLONG_MIN;
+	}
+
 #endif
       return (long long int) x;
     }
diff --git a/sysdeps/ieee754/ldbl-128/s_llroundl.c b/sysdeps/ieee754/ldbl-128/s_llroundl.c
index e5dd145..bfc81cc 100644
--- a/sysdeps/ieee754/ldbl-128/s_llroundl.c
+++ b/sysdeps/ieee754/ldbl-128/s_llroundl.c
@@ -23,7 +23,7 @@ 
 #include <math.h>
 
 #include <math_private.h>
-
+#include <fix-fp-int-convert-overflow.h>
 
 long long int
 __llroundl (long double x)
@@ -78,7 +78,14 @@  __llroundl (long double x)
 	 FE_INVALID must be raised and the return value is
 	 unspecified.  */
 #ifdef FE_INVALID
-      if (x <= (long double) LLONG_MIN - 0.5L)
+      if (FIX_LDBL_LLONG_CONVERT_OVERFLOW
+	  && !(sign == -1 && x > (long double) LLONG_MIN - 0.5L))
+	{
+	  feraiseexcept (FE_INVALID);
+	  return sign == 1 ? LLONG_MAX : LLONG_MIN;
+	}
+      else if (!FIX_LDBL_LLONG_CONVERT_OVERFLOW
+	       && x <= (long double) LLONG_MIN - 0.5L)
 	{
 	  /* If truncation produces LLONG_MIN, the cast will not raise
 	     the exception, but may raise "inexact".  */
diff --git a/sysdeps/ieee754/ldbl-128/s_lrintl.c b/sysdeps/ieee754/ldbl-128/s_lrintl.c
index ff4780a..23f828f 100644
--- a/sysdeps/ieee754/ldbl-128/s_lrintl.c
+++ b/sysdeps/ieee754/ldbl-128/s_lrintl.c
@@ -24,6 +24,7 @@ 
 #include <math.h>
 
 #include <math_private.h>
+#include <fix-fp-int-convert-overflow.h>
 
 static const long double two112[2] =
 {
@@ -120,6 +121,12 @@  __lrintl (long double x)
 	  feraiseexcept (t == LONG_MIN ? FE_INEXACT : FE_INVALID);
 	  return LONG_MIN;
 	}
+      else if (FIX_LDBL_LONG_CONVERT_OVERFLOW && x != (long double) LONG_MIN)
+	{
+	  feraiseexcept (FE_INVALID);
+	  return sx == 0 ? LONG_MAX : LONG_MIN;
+	}
+
 #endif
       return (long int) x;
     }
diff --git a/sysdeps/ieee754/ldbl-128/s_lroundl.c b/sysdeps/ieee754/ldbl-128/s_lroundl.c
index 34d2264..f032625 100644
--- a/sysdeps/ieee754/ldbl-128/s_lroundl.c
+++ b/sysdeps/ieee754/ldbl-128/s_lroundl.c
@@ -23,7 +23,7 @@ 
 #include <math.h>
 
 #include <math_private.h>
-
+#include <fix-fp-int-convert-overflow.h>
 
 long int
 __lroundl (long double x)
@@ -87,7 +87,14 @@  __lroundl (long double x)
 	 FE_INVALID must be raised and the return value is
 	 unspecified.  */
 #ifdef FE_INVALID
-      if (x <= (long double) LONG_MIN - 0.5L)
+      if (FIX_LDBL_LONG_CONVERT_OVERFLOW
+	  && !(sign == -1 && x > (long double) LONG_MIN - 0.5L))
+	{
+	  feraiseexcept (FE_INVALID);
+	  return sign == 1 ? LONG_MAX : LONG_MIN;
+	}
+      else if (!FIX_LDBL_LONG_CONVERT_OVERFLOW
+	       && x <= (long double) LONG_MIN - 0.5L)
 	{
 	  /* If truncation produces LONG_MIN, the cast will not raise
 	     the exception, but may raise "inexact".  */
diff --git a/sysdeps/mips/mips32/fpu/fix-fp-int-convert-overflow.h b/sysdeps/mips/mips32/fpu/fix-fp-int-convert-overflow.h
index fb2ec69..b9f4aea 100644
--- a/sysdeps/mips/mips32/fpu/fix-fp-int-convert-overflow.h
+++ b/sysdeps/mips/mips32/fpu/fix-fp-int-convert-overflow.h
@@ -25,6 +25,7 @@ 
    see <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59412>).  */
 #define FIX_FLT_LLONG_CONVERT_OVERFLOW 1
 #define FIX_DBL_LLONG_CONVERT_OVERFLOW 1
+#define FIX_LDBL_LLONG_CONVERT_OVERFLOW 0
 
 /* As of GCC 5 and binutils 2.25, for MIPS I GCC generates calls to
    assembler macros for conversions from floating point to integer
@@ -32,5 +33,6 @@ 
    lose exceptions.  */
 #define FIX_FLT_LONG_CONVERT_OVERFLOW (__mips == 1)
 #define FIX_DBL_LONG_CONVERT_OVERFLOW (__mips == 1)
+#define FIX_LDBL_LONG_CONVERT_OVERFLOW 0
 
 #endif /* fix-fp-int-convert-overflow.h */
diff --git a/sysdeps/s390/fix-fp-int-convert-overflow.h b/sysdeps/s390/fix-fp-int-convert-overflow.h
new file mode 100644
index 0000000..61279ed
--- /dev/null
+++ b/sysdeps/s390/fix-fp-int-convert-overflow.h
@@ -0,0 +1,33 @@ 
+/* Fix for conversion of floating point to integer overflow.  S390 version.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef FIX_FP_INT_CONVERT_OVERFLOW_H
+#define FIX_FP_INT_CONVERT_OVERFLOW_H	1
+
+/* GCC emits "convert to fixed" instructions for casting floating point values
+   to integer values. These instructions raise invalid and inexact exceptions
+   if the floating point value exceeds the integer type ranges.  */
+#define FIX_FLT_LLONG_CONVERT_OVERFLOW 1
+#define FIX_DBL_LLONG_CONVERT_OVERFLOW 1
+#define FIX_LDBL_LLONG_CONVERT_OVERFLOW 1
+
+#define FIX_FLT_LONG_CONVERT_OVERFLOW 1
+#define FIX_DBL_LONG_CONVERT_OVERFLOW 1
+#define FIX_LDBL_LONG_CONVERT_OVERFLOW 1
+
+#endif /* fix-fp-int-convert-overflow.h */