public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Implement -fsanitize=float-cast-overflow
@ 2014-05-13 17:08 Marek Polacek
  2014-05-13 17:38 ` Jakub Jelinek
  2014-05-13 18:11 ` Joseph S. Myers
  0 siblings, 2 replies; 16+ messages in thread
From: Marek Polacek @ 2014-05-13 17:08 UTC (permalink / raw)
  To: GCC Patches; +Cc: Jakub Jelinek, Joseph S. Myers

Here's an attempt to add the -fsanitize=float-cast-overflow
instrumentation.  It should issue a runtime error when a floating-point
to integer type conversion overflows.  Eventually it should instrument
even floating-point to floating-point conversions to detect e.g.
(float)1e39 overflow, but I'd like to settle first on float to int
before implementing that.

Current patch should detect e.g.
unsigned char uc = 250.0;
int i = (float) __INT_MAX__ + 1;
and similar.

In essence, the gist of this instrumentation is:
if (x u<= TYPE_MIN - 1.0 || x u>= TYPE_MAX + 1.0)
  __ubsan_builtin ();
this checks even +-Inf for free, and because the comparison is
unordered, it detects even NaNs.

The question is how this should interact with feenableexcept (FE_INVALID)
- currently it in some cases raises the Floating point exception, so I
didn't include that in the testsite...  I don't know floating-point
stuff well enough to judge what would be best.

Regtested/bootstrapped on x86_64.  Ubsan testsuite passes with
-m32/-m64 on x86_64/ppc64.  Ran bootstrap-ubsan.

2014-05-13  Marek Polacek  <polacek@redhat.com>

	* convert.c: Include "ubsan.h".
	(convert_to_integer): Instrument float to integer conversion.
	* flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_CAST and or
	it into SANITIZE_UNDEFINED.
	* opts.c (common_handle_option): Add -fsanitize=float-cast-overflow.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
	BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT): New built-ins.
	* ubsan.c (get_ubsan_type_info_for_type): Handle REAL_TYPEs.
	(ubsan_instrument_float_cast): New function.
	* ubsan.h (ubsan_instrument_float_cast): Declare.
	* doc/invoke.texi: Document -fsanitize=float-cast-overflow.

	* c-c++-common/ubsan/float-cast-overflow-1.c: New test.

diff --git gcc/convert.c gcc/convert.c
index 91c1da2..de9e4b3 100644
--- gcc/convert.c
+++ gcc/convert.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "target.h"
 #include "langhooks.h"
+#include "ubsan.h"
 
 /* Convert EXPR to some pointer or reference type TYPE.
    EXPR must be pointer, reference, integer, enumeral, or literal zero;
@@ -394,6 +395,7 @@ convert_to_integer (tree type, tree expr)
   tree intype = TREE_TYPE (expr);
   unsigned int inprec = element_precision (intype);
   unsigned int outprec = element_precision (type);
+  location_t loc = EXPR_LOCATION (expr);
 
   /* An INTEGER_TYPE cannot be incomplete, but an ENUMERAL_TYPE can
      be.  Consider `enum E = { a, b = (enum E) 3 };'.  */
@@ -844,7 +846,15 @@ convert_to_integer (tree type, tree expr)
       return build1 (CONVERT_EXPR, type, expr);
 
     case REAL_TYPE:
-      return build1 (FIX_TRUNC_EXPR, type, expr);
+      if (flag_sanitize & SANITIZE_FLOAT_CAST)
+	{
+	  expr = save_expr (expr);
+	  tree check = ubsan_instrument_float_cast (loc, type, expr);
+	  expr = build1 (FIX_TRUNC_EXPR, type, expr);
+	  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
+	}
+      else
+	return build1 (FIX_TRUNC_EXPR, type, expr);
 
     case FIXED_POINT_TYPE:
       return build1 (FIXED_CONVERT_EXPR, type, expr);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index da9694c..85d8a10 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5426,6 +5426,13 @@ Detect floating-point division by zero.  Unlike other similar options,
 @option{-fsanitize=undefined}, since floating-point division by zero can
 be a legitimate way of obtaining infinities and NaNs.
 
+@item -fsanitize=float-cast-overflow
+@opindex fsanitize=float-cast-overflow
+
+This option enables floating-point type to integer conversion checking.
+We check that the result of the conversion does not overflow.
+This option does not work well with @code{FE_INVALID} exceptions enabled.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions
diff --git gcc/flag-types.h gcc/flag-types.h
index caf4039..2965734 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -229,9 +229,11 @@ enum sanitize_code {
   SANITIZE_BOOL = 1 << 10,
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
+  SANITIZE_FLOAT_CAST = 1 << 13,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
 		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_FLOAT_CAST
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git gcc/opts.c gcc/opts.c
index f15852d..9d38690 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1463,6 +1463,8 @@ common_handle_option (struct gcc_options *opts,
 	      { "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
 	      { "float-divide-by-zero", SANITIZE_FLOAT_DIVIDE,
 		sizeof "float-divide-by-zero" - 1 },
+	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
+		sizeof "float-cast-overflow" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index 6184b5a..a2f7ff0 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -371,3 +371,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT,
 		      "__ubsan_handle_load_invalid_value_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
+		      "__ubsan_handle_float_cast_overflow",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
+		      "__ubsan_handle_float_cast_overflow_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
index e69de29..a184541 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
@@ -0,0 +1,244 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+/* { dg-additional-options "-msse -mfpmath=sse" { target { { i?86-*-* x86_64-*-* } && ia32 } } } */
+
+#include <limits.h>
+
+int
+main (void)
+{
+  const double inf = __builtin_inf ();
+  const double nan = __builtin_nan ("");
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)	\
+  (VAR) = (VAL) - 5.0;			\
+  (VAR) = (VAL) - 1.5;			\
+  (VAR) = (VAL) - 1.0;			\
+  (VAR) = (VAL) - 0.9999999;		\
+  (VAR) = (VAL) - 0.5;			\
+  (VAR) = (VAL) - 0.0000001;		\
+  (VAR) = (VAL) - 0.0;			\
+  (VAR) = (VAL);			\
+  (VAR) = (VAL) + 0.0;			\
+  (VAR) = (VAL) + 0.0000001;		\
+  (VAR) = (VAL) + 0.5;			\
+  (VAR) = (VAL) + 0.9999999;		\
+  (VAR) = (VAL) + 1.0;			\
+  (VAR) = (VAL) + 1.5;			\
+  (VAR) = (VAL) + 5.0;
+
+#define CHECK_NONNUMBERS(VAR)		\
+  (VAR) = nan;				\
+  (VAR) = -nan;				\
+  (VAR) = inf;				\
+  (VAR) = -inf;
+
+  volatile signed char sc;
+  d = SCHAR_MIN;
+  CHECK_BOUNDARY (sc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (sc, d);
+  d = SCHAR_MAX;
+  CHECK_BOUNDARY (sc, d);
+  CHECK_NONNUMBERS (sc);
+
+  volatile unsigned char uc;
+  d = UCHAR_MAX;
+  CHECK_BOUNDARY (uc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (uc, d);
+  CHECK_NONNUMBERS (uc);
+
+  volatile short int s;
+  d = SHRT_MIN;
+  CHECK_BOUNDARY (s, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s, d);
+  d = SHRT_MAX;
+  CHECK_BOUNDARY (s, d);
+  CHECK_NONNUMBERS (s);
+
+  volatile unsigned short int us;
+  d = USHRT_MAX;
+  CHECK_BOUNDARY (us, d);
+  d = 0.0;
+  CHECK_BOUNDARY (us, d);
+  CHECK_NONNUMBERS (us);
+
+  volatile int i;
+  d = INT_MIN;
+  CHECK_BOUNDARY (i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (i, d);
+  d = INT_MAX;
+  CHECK_BOUNDARY (i, d);
+  CHECK_NONNUMBERS (i);
+
+  volatile unsigned int u;
+  d = UINT_MAX;
+  CHECK_BOUNDARY (u, d);
+  d = 0.0;
+  CHECK_BOUNDARY (u, d);
+  CHECK_NONNUMBERS (u);
+
+  volatile long l;
+  /* 64-bit vs 32-bit longs matter causes too much of a headache.  */
+  d = 0.0;
+  CHECK_BOUNDARY (l, d);
+  CHECK_NONNUMBERS (l);
+
+  volatile unsigned long ul;
+  d = 0.0;
+  CHECK_BOUNDARY (ul, d);
+  CHECK_NONNUMBERS (ul);
+
+  volatile long long ll;
+  d = LLONG_MIN;
+  CHECK_BOUNDARY (ll, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ll, d);
+  d = LLONG_MAX;
+  CHECK_BOUNDARY (ll, d);
+  CHECK_NONNUMBERS (ll);
+
+  volatile unsigned long long ull;
+  d = ULLONG_MAX;
+  CHECK_BOUNDARY (ull, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ull, d);
+  CHECK_NONNUMBERS (ull);
+
+  return 0;
+}
+
+/* { dg-output "value -133 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32773 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32772 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65540 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/ubsan.c gcc/ubsan.c
index d9d740c..b8d724b 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -267,9 +267,14 @@ static unsigned short
 get_ubsan_type_info_for_type (tree type)
 {
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
-  int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
-  gcc_assert (prec != -1);
-  return (prec << 1) | !TYPE_UNSIGNED (type);
+  if (TREE_CODE (type) == REAL_TYPE)
+    return tree_to_uhwi (TYPE_SIZE (type));
+  else
+    {
+      int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
+      gcc_assert (prec != -1);
+      return (prec << 1) | !TYPE_UNSIGNED (type);
+    }
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
@@ -891,6 +896,52 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
 }
 
+/* Instrument float point-to-integer conversion.  TYPE is an integer type of
+   destination, EXPR is floating-point expression.  */
+
+tree
+ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
+{
+  tree expr_type = TREE_TYPE (expr);
+  tree t, tt, fn;
+
+  tree min = TYPE_MIN_VALUE (type);
+  tree max = TYPE_MAX_VALUE (type);
+  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
+  min = fold_build2 (MINUS_EXPR, expr_type,
+		     build_real_from_int_cst (expr_type, min),
+		     build_one_cst (expr_type));
+  max = fold_build2 (PLUS_EXPR, expr_type,
+		     build_real_from_int_cst (expr_type, max),
+		     build_one_cst (expr_type));
+
+  if (flag_sanitize_undefined_trap_on_error)
+    fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      /* Create the __ubsan_handle_float_cast_overflow fn call.  */
+      tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
+				     NULL,
+				     ubsan_type_descriptor (expr_type, false),
+				     ubsan_type_descriptor (type, false),
+				     NULL_TREE);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
+	  : BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT;
+      fn = builtin_decl_explicit (bcode);
+      fn = build_call_expr_loc (loc, fn, 2,
+				build_fold_addr_expr_loc (loc, data),
+				ubsan_encode_value (expr, false));
+    }
+
+  t = fold_build2 (UNLE_EXPR, boolean_type_node, expr, min);
+  tt = fold_build2 (UNGE_EXPR, boolean_type_node, expr, max);
+  return fold_build3 (COND_EXPR, void_type_node,
+		      fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt),
+		      fn, integer_zero_node);
+}
+
 namespace {
 
 const pass_data pass_data_ubsan =
diff --git gcc/ubsan.h gcc/ubsan.h
index 67cc6e9..b008419 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -44,6 +44,7 @@ extern tree ubsan_type_descriptor (tree, bool);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);
+extern tree ubsan_instrument_float_cast (location_t, tree, tree);
 
 #endif  /* GCC_UBSAN_H  */
 
	Marek

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-13 17:08 [PATCH] Implement -fsanitize=float-cast-overflow Marek Polacek
@ 2014-05-13 17:38 ` Jakub Jelinek
  2014-05-13 18:11 ` Joseph S. Myers
  1 sibling, 0 replies; 16+ messages in thread
From: Jakub Jelinek @ 2014-05-13 17:38 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches, Joseph S. Myers

On Tue, May 13, 2014 at 07:08:01PM +0200, Marek Polacek wrote:
> In essence, the gist of this instrumentation is:
> if (x u<= TYPE_MIN - 1.0 || x u>= TYPE_MAX + 1.0)
>   __ubsan_builtin ();
> this checks even +-Inf for free, and because the comparison is
> unordered, it detects even NaNs.
> 
> The question is how this should interact with feenableexcept (FE_INVALID)
> - currently it in some cases raises the Floating point exception, so I
> didn't include that in the testsite...  I don't know floating-point
> stuff well enough to judge what would be best.

What kind of exceptions do you get and where?  For sNaN I guess an exception
should be expectable, not sure if we raise one when casting sNaN to integer
without instrumentation.

> --- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
> +++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
> @@ -0,0 +1,244 @@
> +/* { dg-do run } */

I'd say you should limit the test to ilp32 || lp64 targets.

> +/* { dg-options "-fsanitize=float-cast-overflow" } */
> +/* { dg-additional-options "-msse -mfpmath=sse" { target { { i?86-*-* x86_64-*-* } && ia32 } } } */

And this needs to be guarded by && sse2_runtime.  Supposedly
you should also pass -msse2 rather than -msse, the test uses double
rather than float.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-13 17:08 [PATCH] Implement -fsanitize=float-cast-overflow Marek Polacek
  2014-05-13 17:38 ` Jakub Jelinek
@ 2014-05-13 18:11 ` Joseph S. Myers
  2014-05-14 11:38   ` Jakub Jelinek
  1 sibling, 1 reply; 16+ messages in thread
From: Joseph S. Myers @ 2014-05-13 18:11 UTC (permalink / raw)
  To: Marek Polacek; +Cc: GCC Patches, Jakub Jelinek

On Tue, 13 May 2014, Marek Polacek wrote:

> Here's an attempt to add the -fsanitize=float-cast-overflow
> instrumentation.  It should issue a runtime error when a floating-point
> to integer type conversion overflows.  Eventually it should instrument

As with divide-by-zero, this should not be part of -fsanitize=undefined 
because under Annex F this is not undefined behavior (instead it raises 
"invalid" and returns an unspecified value, C11 F.4).

> even floating-point to floating-point conversions to detect e.g.
> (float)1e39 overflow, but I'd like to settle first on float to int
> before implementing that.

I think that should be a separate option.  If the type has infinities, 
it's not undefined even in the absence of Annex F (because infinities 
count as part of the range of the type).  And of course overflow depends 
on the rounding mode when the type of the result is a floating-point type.

(However, conversions of integers to floating point can probably count the 
same as conversions of floating point to floating point if you add such an 
option.  __int128 to float can overflow.)

It would be a good idea for the testcases to cover conversions to 
__int128 / unsigned __int128, where supported.

What do you do for overflowing conversions to bit-fields?  I think the 
correct rule is:

* For C, if the floating-point value, truncated toward 0, is outside the 
range of a signed or unsigned type of the specified number of bits, then 
you should get the (invalid, unspecified value (this isn't actually 
implemented in GCC)), and get a runtime error for the new option.

* For C++, bit-fields don't count as separate types, so it should act as 
converting to the declared type and then converting from that to the 
bit-field (as a modulo operation).

Thus, for an unsigned:1 bit-field, for example, values outside the 
interval (-1, 2) would produce the error for C, but only those outside 
(-1, 0x1p32) would do so for C++ (presuming 32-bit int).

> +  tree min = TYPE_MIN_VALUE (type);
> +  tree max = TYPE_MAX_VALUE (type);
> +  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
> +  min = fold_build2 (MINUS_EXPR, expr_type,
> +		     build_real_from_int_cst (expr_type, min),
> +		     build_one_cst (expr_type));
> +  max = fold_build2 (PLUS_EXPR, expr_type,
> +		     build_real_from_int_cst (expr_type, max),
> +		     build_one_cst (expr_type));

It looks to me like this will first round the max value to the 
floating-point type, then add 1 to the rounded value and round again.  
Which I think is in fact safe at least for IEEE binary floating-point 
types, but that isn't immediately obvious.

Possible issues:

* Does the folding of the addition occur in all cases for IBM long double?

* Is this correct for decimal floating point?  There, the overflow 
condition (value >= max+1) should be using a value of (max+1) rounded 
upward rather than to-nearest, if max+1 isn't exactly representable (and 
in general it isn't - powers of two 0x1p24 and above aren't representable 
in decimal32, 0x1p54 and above in decimal64, 0x1p113 and above in 
decimal128, so you just need to find a case where the double-rounding 
computation you have produces the wrong value).

* Likewise, (value <= min-1) for both binary and decimal floating point - 
you need to round once, away from 0.  For float converted to signed int, 
the relevant condition is values < -0x1p31 - 1, i.e. <= 0x1.000002p31f 
once you allow for which values are representable as float, which is not 
min-1 (min-1 rounds to -0x1p31, but a conversion of that to signed int is 
fully defined with no exceptions).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-13 18:11 ` Joseph S. Myers
@ 2014-05-14 11:38   ` Jakub Jelinek
  2014-05-14 17:37     ` Joseph S. Myers
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-05-14 11:38 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Marek Polacek, GCC Patches

On Tue, May 13, 2014 at 06:11:15PM +0000, Joseph S. Myers wrote:
> > +  tree min = TYPE_MIN_VALUE (type);
> > +  tree max = TYPE_MAX_VALUE (type);
> > +  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
> > +  min = fold_build2 (MINUS_EXPR, expr_type,
> > +		     build_real_from_int_cst (expr_type, min),
> > +		     build_one_cst (expr_type));
> > +  max = fold_build2 (PLUS_EXPR, expr_type,
> > +		     build_real_from_int_cst (expr_type, max),
> > +		     build_one_cst (expr_type));
> 
> It looks to me like this will first round the max value to the 
> floating-point type, then add 1 to the rounded value and round again.  
> Which I think is in fact safe at least for IEEE binary floating-point 
> types, but that isn't immediately obvious.
> 
> Possible issues:
> 
> * Does the folding of the addition occur in all cases for IBM long double?
> 
> * Is this correct for decimal floating point?  There, the overflow 
> condition (value >= max+1) should be using a value of (max+1) rounded 
> upward rather than to-nearest, if max+1 isn't exactly representable (and 
> in general it isn't - powers of two 0x1p24 and above aren't representable 
> in decimal32, 0x1p54 and above in decimal64, 0x1p113 and above in 
> decimal128, so you just need to find a case where the double-rounding 
> computation you have produces the wrong value).
> 
> * Likewise, (value <= min-1) for both binary and decimal floating point - 
> you need to round once, away from 0.  For float converted to signed int, 
> the relevant condition is values < -0x1p31 - 1, i.e. <= 0x1.000002p31f 
> once you allow for which values are representable as float, which is not 
> min-1 (min-1 rounds to -0x1p31, but a conversion of that to signed int is 
> fully defined with no exceptions).

So what do you see as the way to handle this properly?
I mean, for REAL_MODE_FORMAT (TYPE_MODE (expr_type))->b == 2 supposedly to avoid issues with
rounding of the max we could just
  REAL_VALUE_TYPE maxval = dconst1;
  SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + TYPE_PRECISION (type) - !TYPE_UNSIGNED (type));
  real_convert (&maxval, TYPE_MODE (expr_type), &maxval);
  max = build_real (expr_type, maxval);
or so, then supposedly max is always the smallest representable binary
floating point value above or equal to TYPE_MAX_VALUE + 1.0.
For the min value, if it is unsigned, then -1.0 is ok for all binary or
decimal floats, if it is signed, then supposedly we could do the above
with s/max/min/;s/dconst1/dconstm1/; and, after the real_convert
do inexact = real_arithmetic (&newminval, MINUS_EXPR, &minval, &dconst1);
if !inexact just min = build_real (expr_type, newminval); and be done with
it (the question is if for IBM double double this will DTRT for
LONG_LONG_MIN, which I think should be that the high double will contain
(double) LONG_LONG_MIN and the low double -1.0).  For inexact
(which should be the same thing as if result of real_arithmetic + real_convert
is the same as original minval) we need to subtract more than one, dunno if
we should just compute it from the REAL_EXP and precision, or just keep
subtracing powers of two until after real_convert it is no longer bitwise
identical to original minval.  We don't have anything close to
real_nextafter nor real_convert variant that can round for arbitrary
rounding modes.
Any preferences how to implement this?

For _Decimal*, no idea unfortunately, perhaps for the first iteration
ubsan should ignore decimal to int conversions.

	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-14 11:38   ` Jakub Jelinek
@ 2014-05-14 17:37     ` Joseph S. Myers
  2014-05-15 19:09       ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Joseph S. Myers @ 2014-05-14 17:37 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

On Wed, 14 May 2014, Jakub Jelinek wrote:

> So what do you see as the way to handle this properly?
> I mean, for REAL_MODE_FORMAT (TYPE_MODE (expr_type))->b == 2 supposedly to avoid issues with
> rounding of the max we could just
>   REAL_VALUE_TYPE maxval = dconst1;
>   SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + TYPE_PRECISION (type) - !TYPE_UNSIGNED (type));
>   real_convert (&maxval, TYPE_MODE (expr_type), &maxval);
>   max = build_real (expr_type, maxval);
> or so, then supposedly max is always the smallest representable binary
> floating point value above or equal to TYPE_MAX_VALUE + 1.0.

Yes.  Either the power of 2 is exactly representable, or it rounds up to 
+Inf, and in either case >= (or unordered) is the right test.

> For the min value, if it is unsigned, then -1.0 is ok for all binary or
> decimal floats, if it is signed, then supposedly we could do the above

Yes.

> with s/max/min/;s/dconst1/dconstm1/; and, after the real_convert
> do inexact = real_arithmetic (&newminval, MINUS_EXPR, &minval, &dconst1);
> if !inexact just min = build_real (expr_type, newminval); and be done with
> it (the question is if for IBM double double this will DTRT for
> LONG_LONG_MIN, which I think should be that the high double will contain
> (double) LONG_LONG_MIN and the low double -1.0).  For inexact
> (which should be the same thing as if result of real_arithmetic + real_convert
> is the same as original minval) we need to subtract more than one, dunno if
> we should just compute it from the REAL_EXP and precision, or just keep
> subtracing powers of two until after real_convert it is no longer bitwise
> identical to original minval.  We don't have anything close to
> real_nextafter nor real_convert variant that can round for arbitrary
> rounding modes.
> Any preferences how to implement this?

In the inexact case but where the power of 2 is representable, you could 
always handle it as < min rather than <= min-1 - although computing the 
actual nextafter value based on the precision of the floating-point type 
shouldn't be hard and would allow <= to be used everywhere.

(If min overflows to -Inf, then <= -Inf is correct, use of < would be an 
incorrect test.)

> For _Decimal*, no idea unfortunately, perhaps for the first iteration
> ubsan should ignore decimal to int conversions.

Yes, that seems reasonable.  (Computing the exact max+1 or min-1 as an 
MPFR value and then using mpfr_snprintf (then decimal_real_from_string) 
would be one way of converting to decimal with a controlled rounding 
direction.)

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-14 17:37     ` Joseph S. Myers
@ 2014-05-15 19:09       ` Jakub Jelinek
  2014-05-15 21:29         ` Joseph S. Myers
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-05-15 19:09 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Marek Polacek, GCC Patches

On Wed, May 14, 2014 at 05:37:25PM +0000, Joseph S. Myers wrote:
> > with s/max/min/;s/dconst1/dconstm1/; and, after the real_convert
> > do inexact = real_arithmetic (&newminval, MINUS_EXPR, &minval, &dconst1);
> > if !inexact just min = build_real (expr_type, newminval); and be done with
> > it (the question is if for IBM double double this will DTRT for
> > LONG_LONG_MIN, which I think should be that the high double will contain
> > (double) LONG_LONG_MIN and the low double -1.0).  For inexact
> > (which should be the same thing as if result of real_arithmetic + real_convert
> > is the same as original minval) we need to subtract more than one, dunno if
> > we should just compute it from the REAL_EXP and precision, or just keep
> > subtracing powers of two until after real_convert it is no longer bitwise
> > identical to original minval.  We don't have anything close to
> > real_nextafter nor real_convert variant that can round for arbitrary
> > rounding modes.
> > Any preferences how to implement this?
> 
> In the inexact case but where the power of 2 is representable, you could 
> always handle it as < min rather than <= min-1 - although computing the 
> actual nextafter value based on the precision of the floating-point type 
> shouldn't be hard and would allow <= to be used everywhere.

Here is incremental implementation for the binary floating point formats
(and, it seems also the C/C++ bitfields are handled properly already by
Marek's patch).
I've tried
struct S { int i : 17; } s;

void
f1 (float x)
{
  s.i = x;
}

long long
f2 (float x)
{
  return x;
}

__int128_t
f3 (long double x)
{
  return x;
}

__int128_t
f4 (float x)
{
  return x;
}

__uint128_t
f5 (float x)
{
  return x;
}

long long
f6 (long double x)
{
  return x;
}

on both x86_64 and ppc64 -mlong-double-128 and manually inspected the
numbers and they looked ok, for ppc64 f6 was using:
u<= { 9223372036854775808.0 + 0.0 }
u>= {-9223372036854775808.0 + -1.0 }
and f3
u<= { 170141183460469231731687303715884105728.0 + 0.0 }
u>= { -170141183460469231731687303715884105728.0 + -4194304.0 }
which I believe is correct.  Of course the above needs to be transformed
into two real testcases that will actually test that valid in-range values
don't complain and out of range do, will leave that to Marek.

> > For _Decimal*, no idea unfortunately, perhaps for the first iteration
> > ubsan should ignore decimal to int conversions.
> 
> Yes, that seems reasonable.  (Computing the exact max+1 or min-1 as an 
> MPFR value and then using mpfr_snprintf (then decimal_real_from_string) 
> would be one way of converting to decimal with a controlled rounding 
> direction.)

If prec < HOST_BITS_PER_WIDE_INT, then we can just snprintf
normally, otherwise yes, we could e.g. use
char *buf = XALLOCAVEC (char, prec / 2);
mpfr_t m;
mpfr_init2 (m, prec + 2);
mpfr_set_ui_2exp (m, 1, prec - !uns_p);
mpfr_snprintf (buf, prec / 2, "%f", m);
// buf should be TYPE_MAX_VALUE + 1.0 here?
if (!uns_p)
{
mpfr_set_si_2exp (m, -1, prec - 1);
mpfr_sub_ui (m, m, 1, GMP_RNDN);
mpfr_snprintf (buf, prec / 2, "%f", m);
// buf should be TYPE_MIN_VALUE - 1.0 here?
}
But I think we can't use decimal_real_from_string, we'd need a variant
of that function that would allow specification of the rounding mode
and which of the 3 _DecimalN types it is, and supposedly
  decNumber dn;
  decContext set;
  decContextDefault (&set, DEC_INIT_DECIMAL{32,64,128});
  set.traps = 0;
  set.round = {DEC_ROUND_CEILING,DEC_ROUND_FLOOR};
  decNumberFromString (&dn, s, &set);
then if not DEC_INIT_DECIMAL128 supposedly convert there and back
to DEC_INIT_DECIMAL128 and then convert to REAL_FORMAT.
But my _Decimal knowledge is very limited, so I'll leave that to
the DFP folks (unless Marek is interested).

--- gcc/convert.c
+++ gcc/convert.c
@@ -851,6 +851,8 @@
 	  expr = save_expr (expr);
 	  tree check = ubsan_instrument_float_cast (loc, type, expr);
 	  expr = build1 (FIX_TRUNC_EXPR, type, expr);
+	  if (check == NULL)
+	    return expr;
 	  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
 	}
       else
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -903,17 +903,62 @@
 ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
 {
   tree expr_type = TREE_TYPE (expr);
-  tree t, tt, fn;
+  tree t, tt, fn, min, max;
+  enum machine_mode mode = TYPE_MODE (expr_type);
+  int prec = TYPE_PRECISION (type);
+  bool uns_p = TYPE_UNSIGNED (type);
 
-  tree min = TYPE_MIN_VALUE (type);
-  tree max = TYPE_MAX_VALUE (type);
-  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
-  min = fold_build2 (MINUS_EXPR, expr_type,
-		     build_real_from_int_cst (expr_type, min),
-		     build_one_cst (expr_type));
-  max = fold_build2 (PLUS_EXPR, expr_type,
-		     build_real_from_int_cst (expr_type, max),
-		     build_one_cst (expr_type));
+  /* Float to integer conversion first truncates toward zero, so
+     even signed char c = 127.875f; is not problematic.
+     Therefore, we should complain only if EXPR is unordered or smaller
+     or equal than TYPE_MIN_VALUE - 1.0 or greater or equal than
+     TYPE_MAX_VALUE + 1.0.  */
+  if (REAL_MODE_FORMAT (mode)->b == 2)
+    {
+      /* For maximum, TYPE_MAX_VALUE might not be representable
+	 in EXPR_TYPE, e.g. if TYPE is 64-bit long long and
+	 EXPR_TYPE is IEEE single float, but TYPE_MAX_VALUE + 1.0 is
+	 either representable or infinity.  */
+      REAL_VALUE_TYPE maxval = dconst1;
+      SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + prec - !uns_p);
+      real_convert (&maxval, mode, &maxval);
+      max = build_real (expr_type, maxval);
+
+      /* For unsigned, assume -1.0 is always representable.  */
+      if (uns_p)
+	min = build_minus_one_cst (expr_type);
+      else
+	{
+	  /* TYPE_MIN_VALUE is generally representable (or -inf),
+	     but TYPE_MIN_VALUE - 1.0 might not be.  */
+	  REAL_VALUE_TYPE minval = dconstm1, minval2;
+	  SET_REAL_EXP (&minval, REAL_EXP (&minval) + prec - 1);
+	  real_convert (&minval, mode, &minval);
+	  real_arithmetic (&minval2, MINUS_EXPR, &minval, &dconst1);
+	  real_convert (&minval2, mode, &minval2);
+	  if (real_compare (EQ_EXPR, &minval, &minval2)
+	      && !real_isinf (&minval))
+	    {
+	      /* If TYPE_MIN_VALUE - 1.0 is not representable and
+		 rounds to TYPE_MIN_VALUE, we need to subtract
+		 more.  As REAL_MODE_FORMAT (mode)->p is the number
+		 of base digits, we want to subtract a number that
+		 will be 1 << (REAL_MODE_FORMAT (mode)->p - 1)
+		 times smaller than minval.  */
+	      minval2 = dconst1;
+	      gcc_assert (prec > REAL_MODE_FORMAT (mode)->p);
+	      SET_REAL_EXP (&minval2,
+			    REAL_EXP (&minval2) + prec - 1
+			    - REAL_MODE_FORMAT (mode)->p + 1);
+	      real_arithmetic (&minval2, MINUS_EXPR, &minval, &minval2);
+	      real_convert (&minval2, mode, &minval2);
+	    }
+	  min = build_real (expr_type, minval2);
+	}
+    }
+  /* Only binary floating point supported right now.  */
+  else
+    return NULL_TREE;
 
   if (flag_sanitize_undefined_trap_on_error)
     fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);


	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-15 19:09       ` Jakub Jelinek
@ 2014-05-15 21:29         ` Joseph S. Myers
  2014-05-16  7:37           ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Joseph S. Myers @ 2014-05-15 21:29 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

On Thu, 15 May 2014, Jakub Jelinek wrote:

> But I think we can't use decimal_real_from_string, we'd need a variant
> of that function that would allow specification of the rounding mode

My point is that you can use "%.*RUe" or "%.*RDe" formats (for max and min 
respectively), with an appropriate precision, and let MPFR do the rounding 
to an appropriate number of decimal digits in the right direction (to 
produce a value that's exactly representable in the relevant DFP type, as 
long as it's in range).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-15 21:29         ` Joseph S. Myers
@ 2014-05-16  7:37           ` Jakub Jelinek
  2014-05-16 15:54             ` Joseph S. Myers
  2014-05-20 20:33             ` [PATCH] Implement -fsanitize=float-cast-overflow (take 2) Marek Polacek
  0 siblings, 2 replies; 16+ messages in thread
From: Jakub Jelinek @ 2014-05-16  7:37 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Marek Polacek, GCC Patches

On Thu, May 15, 2014 at 09:29:44PM +0000, Joseph S. Myers wrote:
> On Thu, 15 May 2014, Jakub Jelinek wrote:
>
> > But I think we can't use decimal_real_from_string, we'd need a variant
> > of that function that would allow specification of the rounding mode
>
> My point is that you can use "%.*RUe" or "%.*RDe" formats (for max and min
> respectively), with an appropriate precision, and let MPFR do the rounding
> to an appropriate number of decimal digits in the right direction (to
> produce a value that's exactly representable in the relevant DFP type, as
> long as it's in range).

You are right, that seems to work.  So new incremental patch.

BTW, for IBM long double
__int128_t f3 (long double x) { return x; }
the
u>= { -170141183460469231731687303715884105728.0 + -4194304.0 }
check is actually imprecise, while all correct long double values will be
properly accepted, if high double is exactly
-170141183460469231731687303715884105728.0 and low double is in
[-1.0, -4194304.0), then the unspecified conversion will not be
diagnosed, but I'm afraid there is nothing we can (easily) do about it,
because { -170141183460469231731687303715884105728.0 + -1.0 }, while
representable in IBM long double, is not representable in our
REAL_VALUE_TYPE, because we assume fixed precision, while IBM long double
has variable.  Guess the combination of IBM long double and __int128_t
(for long long it is still ok) is rare enough that we don't need to care
about it (alternative would be e.g. to compare against addition of
those two numbers with some optimization barrier/volatile that would
avoid it from being optimized into a single long double number.

--- gcc/convert.c
+++ gcc/convert.c
@@ -851,6 +851,8 @@
 	  expr = save_expr (expr);
 	  tree check = ubsan_instrument_float_cast (loc, type, expr);
 	  expr = build1 (FIX_TRUNC_EXPR, type, expr);
+	  if (check == NULL)
+	    return expr;
 	  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
 	}
       else
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -47,6 +47,8 @@
 #include "asan.h"
 #include "gimplify-me.h"
 #include "intl.h"
+#include "realmpfr.h"
+#include "dfp.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -903,17 +905,95 @@
 ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
 {
   tree expr_type = TREE_TYPE (expr);
-  tree t, tt, fn;
+  tree t, tt, fn, min, max;
+  enum machine_mode mode = TYPE_MODE (expr_type);
+  int prec = TYPE_PRECISION (type);
+  bool uns_p = TYPE_UNSIGNED (type);
 
-  tree min = TYPE_MIN_VALUE (type);
-  tree max = TYPE_MAX_VALUE (type);
-  /* Add/subtract 1.0 so we can avoid truncating the value of EXPR.  */
-  min = fold_build2 (MINUS_EXPR, expr_type,
-		     build_real_from_int_cst (expr_type, min),
-		     build_one_cst (expr_type));
-  max = fold_build2 (PLUS_EXPR, expr_type,
-		     build_real_from_int_cst (expr_type, max),
-		     build_one_cst (expr_type));
+  /* Float to integer conversion first truncates toward zero, so
+     even signed char c = 127.875f; is not problematic.
+     Therefore, we should complain only if EXPR is unordered or smaller
+     or equal than TYPE_MIN_VALUE - 1.0 or greater or equal than
+     TYPE_MAX_VALUE + 1.0.  */
+  if (REAL_MODE_FORMAT (mode)->b == 2)
+    {
+      /* For maximum, TYPE_MAX_VALUE might not be representable
+	 in EXPR_TYPE, e.g. if TYPE is 64-bit long long and
+	 EXPR_TYPE is IEEE single float, but TYPE_MAX_VALUE + 1.0 is
+	 either representable or infinity.  */
+      REAL_VALUE_TYPE maxval = dconst1;
+      SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + prec - !uns_p);
+      real_convert (&maxval, mode, &maxval);
+      max = build_real (expr_type, maxval);
+
+      /* For unsigned, assume -1.0 is always representable.  */
+      if (uns_p)
+	min = build_minus_one_cst (expr_type);
+      else
+	{
+	  /* TYPE_MIN_VALUE is generally representable (or -inf),
+	     but TYPE_MIN_VALUE - 1.0 might not be.  */
+	  REAL_VALUE_TYPE minval = dconstm1, minval2;
+	  SET_REAL_EXP (&minval, REAL_EXP (&minval) + prec - 1);
+	  real_convert (&minval, mode, &minval);
+	  real_arithmetic (&minval2, MINUS_EXPR, &minval, &dconst1);
+	  real_convert (&minval2, mode, &minval2);
+	  if (real_compare (EQ_EXPR, &minval, &minval2)
+	      && !real_isinf (&minval))
+	    {
+	      /* If TYPE_MIN_VALUE - 1.0 is not representable and
+		 rounds to TYPE_MIN_VALUE, we need to subtract
+		 more.  As REAL_MODE_FORMAT (mode)->p is the number
+		 of base digits, we want to subtract a number that
+		 will be 1 << (REAL_MODE_FORMAT (mode)->p - 1)
+		 times smaller than minval.  */
+	      minval2 = dconst1;
+	      gcc_assert (prec > REAL_MODE_FORMAT (mode)->p);
+	      SET_REAL_EXP (&minval2,
+			    REAL_EXP (&minval2) + prec - 1
+			    - REAL_MODE_FORMAT (mode)->p + 1);
+	      real_arithmetic (&minval2, MINUS_EXPR, &minval, &minval2);
+	      real_convert (&minval2, mode, &minval2);
+	    }
+	  min = build_real (expr_type, minval2);
+	}
+    }
+  else if (REAL_MODE_FORMAT (mode)->b == 10)
+    {
+      /* For _Decimal128 up to 34 decimal digits, - sign,
+	 dot, e, exponent.  */
+      char buf[64];
+      mpfr_t m;
+      int p = REAL_MODE_FORMAT (mode)->p;
+      REAL_VALUE_TYPE maxval, minval;
+
+      /* Use mpfr_snprintf rounding to compute the smallest
+	 representable decimal number greater or equal than
+	 1 << (prec - !uns_p).  */
+      mpfr_init2 (m, prec + 2);
+      mpfr_set_ui_2exp (m, 1, prec - !uns_p, GMP_RNDN);
+      mpfr_snprintf (buf, sizeof buf, "%.*RUe", p - 1, m);
+      decimal_real_from_string (&maxval, buf);
+      max = build_real (expr_type, maxval);
+
+      /* For unsigned, assume -1.0 is always representable.  */
+      if (uns_p)
+	min = build_minus_one_cst (expr_type);
+      else
+	{
+	  /* Use mpfr_snprintf rounding to compute the largest
+	     representable decimal number less or equal than
+	     (-1 << (prec - 1)) - 1.  */
+	  mpfr_set_si_2exp (m, -1, prec - 1, GMP_RNDN);
+	  mpfr_sub_ui (m, m, 1, GMP_RNDN);
+	  mpfr_snprintf (buf, sizeof buf, "%.*RDe", p - 1, m);
+	  decimal_real_from_string (&minval, buf);
+	  min = build_real (expr_type, minval);
+	}
+      mpfr_clear (m);
+    }
+  else
+    return NULL_TREE;
 
   if (flag_sanitize_undefined_trap_on_error)
     fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);


	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow
  2014-05-16  7:37           ` Jakub Jelinek
@ 2014-05-16 15:54             ` Joseph S. Myers
  2014-05-20 20:33             ` [PATCH] Implement -fsanitize=float-cast-overflow (take 2) Marek Polacek
  1 sibling, 0 replies; 16+ messages in thread
From: Joseph S. Myers @ 2014-05-16 15:54 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Marek Polacek, GCC Patches

On Fri, 16 May 2014, Jakub Jelinek wrote:

> has variable.  Guess the combination of IBM long double and __int128_t
> (for long long it is still ok) is rare enough that we don't need to care
> about it (alternative would be e.g. to compare against addition of

There are existing problems with that combination anyway.  
fp-int-convert-timode.c has tests disabled for it because the compile-time 
and runtime conversions don't agree (whether for this issue or some other, 
I don't know).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow (take 2)
  2014-05-16  7:37           ` Jakub Jelinek
  2014-05-16 15:54             ` Joseph S. Myers
@ 2014-05-20 20:33             ` Marek Polacek
  2014-05-20 21:50               ` Joseph S. Myers
  1 sibling, 1 reply; 16+ messages in thread
From: Marek Polacek @ 2014-05-20 20:33 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Joseph S. Myers, GCC Patches

Thanks for all your help.  This is updated patch which:

* uses mix/min handling that Jakub kindly wrote,
* limited a test to ilp32 || lp64 + used sse2_runtime (but it's likely
  I screwed it up as usually),
* adds a testcase for (unsigned) __int128,
* adds testcases for C and C++ bit-fields,
* makes -fsanitize=float-cast-overflow be not enabled by default,
* is tested with -m32/-m64 on x86_64/ppc64,
* is missing tests for long doubles/-mlong-double-128,
* doesn't instrument _Decimal to integer conversions yet.

Comments?

2014-05-20  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	* builtins.def: Change SANITIZE_FLOAT_DIVIDE to SANITIZE_NONDEFAULT.
	* gcc.c (sanitize_spec_function): Likewise.
	* convert.c (convert_to_integer): Include "ubsan.h".  Add floating-point
	to integer instrumentation.
	* doc/invoke.texi: Document -fsanitize=float-cast-overflow.
	* flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_CAST and
	SANITIZE_NONDEFAULT.
	* opts.c (common_handle_option): Handle -fsanitize=float-cast-overflow.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
	BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT): Add.
	* ubsan.c: Include "realmpfr.h" and "dfp.h".
	(get_ubsan_type_info_for_type): Handle REAL_TYPEs.
	(ubsan_instrument_float_cast): New function.
	* ubsan.h (ubsan_instrument_float_cast): Declare.

	* c-c++-common/ubsan/float-cast-overflow-1.c: New test.
	* c-c++-common/ubsan/float-cast-overflow-2.c: New test.
	* g++.dg/ubsan/float-cast-overflow-bf.C: New test.
	* gcc.dg/ubsan/float-cast-overflow-bf.c: New test.

diff --git gcc/builtins.def gcc/builtins.def
index d400ecb..cd823a3 100644
--- gcc/builtins.def
+++ gcc/builtins.def
@@ -176,7 +176,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
-				| SANITIZE_UNDEFINED | SANITIZE_FLOAT_DIVIDE)))
+				| SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT)))
 
 #undef DEF_CILKPLUS_BUILTIN
 #define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS)  \
diff --git gcc/convert.c gcc/convert.c
index 91c1da2..b8f3671 100644
--- gcc/convert.c
+++ gcc/convert.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "target.h"
 #include "langhooks.h"
+#include "ubsan.h"
 
 /* Convert EXPR to some pointer or reference type TYPE.
    EXPR must be pointer, reference, integer, enumeral, or literal zero;
@@ -394,6 +395,7 @@ convert_to_integer (tree type, tree expr)
   tree intype = TREE_TYPE (expr);
   unsigned int inprec = element_precision (intype);
   unsigned int outprec = element_precision (type);
+  location_t loc = EXPR_LOCATION (expr);
 
   /* An INTEGER_TYPE cannot be incomplete, but an ENUMERAL_TYPE can
      be.  Consider `enum E = { a, b = (enum E) 3 };'.  */
@@ -844,7 +846,17 @@ convert_to_integer (tree type, tree expr)
       return build1 (CONVERT_EXPR, type, expr);
 
     case REAL_TYPE:
-      return build1 (FIX_TRUNC_EXPR, type, expr);
+      if (flag_sanitize & SANITIZE_FLOAT_CAST)
+	{
+	  expr = save_expr (expr);
+	  tree check = ubsan_instrument_float_cast (loc, type, expr);
+	  expr = build1 (FIX_TRUNC_EXPR, type, expr);
+	  if (check == NULL)
+	    return expr;
+	  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
+	}
+      else
+	return build1 (FIX_TRUNC_EXPR, type, expr);
 
     case FIXED_POINT_TYPE:
       return build1 (FIXED_CONVERT_EXPR, type, expr);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 5b1b0f1..659c265 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5427,6 +5427,13 @@ Detect floating-point division by zero.  Unlike other similar options,
 @option{-fsanitize=undefined}, since floating-point division by zero can
 be a legitimate way of obtaining infinities and NaNs.
 
+@item -fsanitize=float-cast-overflow
+@opindex fsanitize=float-cast-overflow
+
+This option enables floating-point type to integer conversion checking.
+We check that the result of the conversion does not overflow.
+This option does not work well with @code{FE_INVALID} exceptions enabled.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions
diff --git gcc/flag-types.h gcc/flag-types.h
index caf4039..ed00046 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -229,9 +229,11 @@ enum sanitize_code {
   SANITIZE_BOOL = 1 << 10,
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
+  SANITIZE_FLOAT_CAST = 1 << 13,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
+  SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git gcc/gcc.c gcc/gcc.c
index 7bea6d7..9ac18e6 100644
--- gcc/gcc.c
+++ gcc/gcc.c
@@ -8170,7 +8170,7 @@ sanitize_spec_function (int argc, const char **argv)
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
-    return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_FLOAT_DIVIDE))
+    return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT))
 	    && !flag_sanitize_undefined_trap_on_error) ? "" : NULL;
   if (strcmp (argv[0], "leak") == 0)
     return ((flag_sanitize
diff --git gcc/opts.c gcc/opts.c
index 5f4b2e3..2f4f913 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1463,6 +1463,8 @@ common_handle_option (struct gcc_options *opts,
 	      { "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
 	      { "float-divide-by-zero", SANITIZE_FLOAT_DIVIDE,
 		sizeof "float-divide-by-zero" - 1 },
+	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
+		sizeof "float-cast-overflow" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index 6184b5a..a2f7ff0 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -371,3 +371,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT,
 		      "__ubsan_handle_load_invalid_value_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
+		      "__ubsan_handle_float_cast_overflow",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
+		      "__ubsan_handle_float_cast_overflow_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
index e69de29..fadd8a8 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
@@ -0,0 +1,229 @@
+/* { dg-do run { target { lp64 || ilp32 } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { sse2_runtime && ia32 } } } */
+
+#include <limits.h>
+
+int
+main (void)
+{
+  const double inf = __builtin_inf ();
+  const double nan = __builtin_nan ("");
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)	\
+  (VAR) = (VAL) - 5.0;			\
+  (VAR) = (VAL) - 1.5;			\
+  (VAR) = (VAL) - 1.0;			\
+  (VAR) = (VAL) - 0.9999999;		\
+  (VAR) = (VAL) - 0.5;			\
+  (VAR) = (VAL) - 0.0000001;		\
+  (VAR) = (VAL) - 0.0;			\
+  (VAR) = (VAL);			\
+  (VAR) = (VAL) + 0.0;			\
+  (VAR) = (VAL) + 0.0000001;		\
+  (VAR) = (VAL) + 0.5;			\
+  (VAR) = (VAL) + 0.9999999;		\
+  (VAR) = (VAL) + 1.0;			\
+  (VAR) = (VAL) + 1.5;			\
+  (VAR) = (VAL) + 5.0;
+
+#define CHECK_NONNUMBERS(VAR)		\
+  (VAR) = nan;				\
+  (VAR) = -nan;			\
+  (VAR) = inf;				\
+  (VAR) = -inf;
+
+  volatile signed char sc;
+  d = SCHAR_MIN;
+  CHECK_BOUNDARY (sc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (sc, d);
+  d = SCHAR_MAX;
+  CHECK_BOUNDARY (sc, d);
+  CHECK_NONNUMBERS (sc);
+
+  volatile unsigned char uc;
+  d = UCHAR_MAX;
+  CHECK_BOUNDARY (uc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (uc, d);
+  CHECK_NONNUMBERS (uc);
+
+  volatile short int s;
+  d = SHRT_MIN;
+  CHECK_BOUNDARY (s, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s, d);
+  d = SHRT_MAX;
+  CHECK_BOUNDARY (s, d);
+  CHECK_NONNUMBERS (s);
+
+  volatile unsigned short int us;
+  d = USHRT_MAX;
+  CHECK_BOUNDARY (us, d);
+  d = 0.0;
+  CHECK_BOUNDARY (us, d);
+  CHECK_NONNUMBERS (us);
+
+  volatile int i;
+  d = INT_MIN;
+  CHECK_BOUNDARY (i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (i, d);
+  d = INT_MAX;
+  CHECK_BOUNDARY (i, d);
+  CHECK_NONNUMBERS (i);
+
+  volatile unsigned int u;
+  d = UINT_MAX;
+  CHECK_BOUNDARY (u, d);
+  d = 0.0;
+  CHECK_BOUNDARY (u, d);
+  CHECK_NONNUMBERS (u);
+
+  volatile long l;
+  /* 64-bit vs 32-bit longs matter causes too much of a headache.  */
+  d = 0.0;
+  CHECK_BOUNDARY (l, d);
+  CHECK_NONNUMBERS (l);
+
+  volatile unsigned long ul;
+  d = 0.0;
+  CHECK_BOUNDARY (ul, d);
+  CHECK_NONNUMBERS (ul);
+
+  volatile long long ll;
+  d = LLONG_MIN;
+  CHECK_BOUNDARY (ll, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ll, d);
+  d = LLONG_MAX;
+  CHECK_BOUNDARY (ll, d);
+  CHECK_NONNUMBERS (ll);
+
+  volatile unsigned long long ull;
+  d = ULLONG_MAX;
+  CHECK_BOUNDARY (ull, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ull, d);
+  CHECK_NONNUMBERS (ull);
+
+  return 0;
+}
+
+/* { dg-output "value -133 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32773 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32772 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65540 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c
index e69de29..af593ba 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c
@@ -0,0 +1,98 @@
+/* { dg-do run } */
+/* { dg-require-effective-target int128 } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#define INT128_MAX (__int128) (((unsigned __int128) 1 << ((__SIZEOF_INT128__ * __CHAR_BIT__) - 1)) - 1)
+#define INT128_MIN (-INT128_MAX - 1)
+#define UINT128_MAX ((2 * (unsigned __int128) INT128_MAX) + 1)
+
+int
+main (void)
+{
+  const double inf = __builtin_inf ();
+  const double nan = __builtin_nan ("");
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)	\
+  (VAR) = (VAL) - 5.0;			\
+  (VAR) = (VAL) - 1.5;			\
+  (VAR) = (VAL) - 1.0;			\
+  (VAR) = (VAL) - 0.9999999;		\
+  (VAR) = (VAL) - 0.5;			\
+  (VAR) = (VAL) - 0.0000001;		\
+  (VAR) = (VAL) - 0.0;			\
+  (VAR) = (VAL);			\
+  (VAR) = (VAL) + 0.0;			\
+  (VAR) = (VAL) + 0.0000001;		\
+  (VAR) = (VAL) + 0.5;			\
+  (VAR) = (VAL) + 0.9999999;		\
+  (VAR) = (VAL) + 1.0;			\
+  (VAR) = (VAL) + 1.5;			\
+  (VAR) = (VAL) + 5.0;
+
+#define CHECK_NONNUMBERS(VAR)		\
+  (VAR) = nan;				\
+  (VAR) = -nan;				\
+  (VAR) = inf;				\
+  (VAR) = -inf;
+
+  __int128 i;
+  d = INT128_MIN;
+  CHECK_BOUNDARY (i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (i, d);
+  d = INT128_MAX;
+  CHECK_BOUNDARY (i, d);
+  CHECK_NONNUMBERS (i);
+
+  unsigned __int128 u;
+  d = UINT128_MAX;
+  CHECK_BOUNDARY (u, d);
+  d = 0.0;
+  CHECK_BOUNDARY (u, d);
+  CHECK_NONNUMBERS (u);
+
+  return 0;
+}
+
+/* { dg-output "runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value nan is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -nan is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value inf is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -inf is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -5 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -1.5 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value nan is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -nan is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value inf is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -inf is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C
index e69de29..20eb13c 100644
--- gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C
+++ gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C
@@ -0,0 +1,60 @@
+// { dg-do run { target { int32 } } }
+// { dg-options "-fsanitize=float-cast-overflow" }
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-__INT_MAX__ - 1)
+#define UINT_MAX 2 * (unsigned) __INT_MAX__ + 1
+
+struct S
+{
+  int i:1;
+} s;
+
+struct T
+{
+  unsigned int i:1;
+} t;
+
+int
+main (void)
+{
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)        \
+  (VAR) = (VAL) - 1.5;                  \
+  (VAR) = (VAL) - 1.0;                  \
+  (VAR) = (VAL) - 0.5;                  \
+  (VAR) = (VAL) - 0.0000001;            \
+  (VAR) = (VAL) - 0.0;                  \
+  (VAR) = (VAL);                        \
+  (VAR) = (VAL) + 0.0;                  \
+  (VAR) = (VAL) + 0.0000001;            \
+  (VAR) = (VAL) + 0.5;                  \
+  (VAR) = (VAL) + 1.0;                  \
+  (VAR) = (VAL) + 1.5;
+
+  /* Signed bit-field.  (INT_MIN, INT_MAX) is valid.  */
+  d = INT_MIN;
+  CHECK_BOUNDARY (s.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s.i, d);
+  d = INT_MAX;
+  CHECK_BOUNDARY (s.i, d);
+
+  /* Unsigned bit-field.  (0, UINT_MAX) is valid.  */
+  d = UINT_MAX;
+  CHECK_BOUNDARY (t.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (t.i, d);
+
+  return 0;
+}
+
+/* { dg-output "value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c
index e69de29..62aeefc 100644
--- gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c
+++ gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c
@@ -0,0 +1,76 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+struct
+{
+  int i:1;
+} s;
+
+struct
+{
+  unsigned int i:1;
+} t;
+
+int
+main (void)
+{
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)        \
+  (VAR) = (VAL) - 1.5;                  \
+  (VAR) = (VAL) - 1.0;                  \
+  (VAR) = (VAL) - 0.9999999;            \
+  (VAR) = (VAL) - 0.5;                  \
+  (VAR) = (VAL) - 0.0000001;            \
+  (VAR) = (VAL) - 0.0;                  \
+  (VAR) = (VAL);                        \
+  (VAR) = (VAL) + 0.0;                  \
+  (VAR) = (VAL) + 0.0000001;            \
+  (VAR) = (VAL) + 0.5;                  \
+  (VAR) = (VAL) + 0.9999999;            \
+  (VAR) = (VAL) + 1.0;                  \
+  (VAR) = (VAL) + 1.5;
+
+  /* Signed bit-field.  (-1, 0) is valid.  */
+  d = -1.0;
+  CHECK_BOUNDARY (s.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s.i, d);
+  d = 1.0;
+  CHECK_BOUNDARY (s.i, d);
+
+  /* Unsigned bit-field.  (0, 1) is valid.  */
+  d = -1.0;
+  CHECK_BOUNDARY (t.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (t.i, d);
+  d = 1.0;
+  CHECK_BOUNDARY (t.i, d);
+
+  return 0;
+}
+
+/* { dg-output "value -2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/ubsan.c gcc/ubsan.c
index 11461d0..cc5556f 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -47,6 +47,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "asan.h"
 #include "gimplify-me.h"
 #include "intl.h"
+#include "realmpfr.h"
+#include "dfp.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -267,9 +269,14 @@ static unsigned short
 get_ubsan_type_info_for_type (tree type)
 {
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
-  int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
-  gcc_assert (prec != -1);
-  return (prec << 1) | !TYPE_UNSIGNED (type);
+  if (TREE_CODE (type) == REAL_TYPE)
+    return tree_to_uhwi (TYPE_SIZE (type));
+  else
+    {
+      int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
+      gcc_assert (prec != -1);
+      return (prec << 1) | !TYPE_UNSIGNED (type);
+    }
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
@@ -891,6 +898,130 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
 }
 
+/* Instrument float point-to-integer conversion.  TYPE is an integer type of
+   destination, EXPR is floating-point expression.  */
+
+tree
+ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
+{
+  tree expr_type = TREE_TYPE (expr);
+  tree t, tt, fn, min, max;
+  enum machine_mode mode = TYPE_MODE (expr_type);
+  int prec = TYPE_PRECISION (type);
+  bool uns_p = TYPE_UNSIGNED (type);
+
+  /* Float to integer conversion first truncates toward zero, so
+     even signed char c = 127.875f; is not problematic.
+     Therefore, we should complain only if EXPR is unordered or smaller
+     or equal than TYPE_MIN_VALUE - 1.0 or greater or equal than
+     TYPE_MAX_VALUE + 1.0.  */
+  if (REAL_MODE_FORMAT (mode)->b == 2)
+    {
+      /* For maximum, TYPE_MAX_VALUE might not be representable
+	 in EXPR_TYPE, e.g. if TYPE is 64-bit long long and
+	 EXPR_TYPE is IEEE single float, but TYPE_MAX_VALUE + 1.0 is
+	 either representable or infinity.  */
+      REAL_VALUE_TYPE maxval = dconst1;
+      SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + prec - !uns_p);
+      real_convert (&maxval, mode, &maxval);
+      max = build_real (expr_type, maxval);
+
+      /* For unsigned, assume -1.0 is always representable.  */
+      if (uns_p)
+	min = build_minus_one_cst (expr_type);
+      else
+	{
+	  /* TYPE_MIN_VALUE is generally representable (or -inf),
+	     but TYPE_MIN_VALUE - 1.0 might not be.  */
+	  REAL_VALUE_TYPE minval = dconstm1, minval2;
+	  SET_REAL_EXP (&minval, REAL_EXP (&minval) + prec - 1);
+	  real_convert (&minval, mode, &minval);
+	  real_arithmetic (&minval2, MINUS_EXPR, &minval, &dconst1);
+	  real_convert (&minval2, mode, &minval2);
+	  if (real_compare (EQ_EXPR, &minval, &minval2)
+	      && !real_isinf (&minval))
+	    {
+	      /* If TYPE_MIN_VALUE - 1.0 is not representable and
+		 rounds to TYPE_MIN_VALUE, we need to subtract
+		 more.  As REAL_MODE_FORMAT (mode)->p is the number
+		 of base digits, we want to subtract a number that
+		 will be 1 << (REAL_MODE_FORMAT (mode)->p - 1)
+		 times smaller than minval.  */
+	      minval2 = dconst1;
+	      gcc_assert (prec > REAL_MODE_FORMAT (mode)->p);
+	      SET_REAL_EXP (&minval2,
+			    REAL_EXP (&minval2) + prec - 1
+			    - REAL_MODE_FORMAT (mode)->p + 1);
+	      real_arithmetic (&minval2, MINUS_EXPR, &minval, &minval2);
+	      real_convert (&minval2, mode, &minval2);
+	    }
+	  min = build_real (expr_type, minval2);
+	}
+    }
+  else if (REAL_MODE_FORMAT (mode)->b == 10)
+    {
+      /* For _Decimal128 up to 34 decimal digits, - sign,
+	 dot, e, exponent.  */
+      char buf[64];
+      mpfr_t m;
+      int p = REAL_MODE_FORMAT (mode)->p;
+      REAL_VALUE_TYPE maxval, minval;
+
+      /* Use mpfr_snprintf rounding to compute the smallest
+	 representable decimal number greater or equal than
+	 1 << (prec - !uns_p).  */
+      mpfr_init2 (m, prec + 2);
+      mpfr_set_ui_2exp (m, 1, prec - !uns_p, GMP_RNDN);
+      mpfr_snprintf (buf, sizeof buf, "%.*RUe", p - 1, m);
+      decimal_real_from_string (&maxval, buf);
+      max = build_real (expr_type, maxval);
+
+      /* For unsigned, assume -1.0 is always representable.  */
+      if (uns_p)
+	min = build_minus_one_cst (expr_type);
+      else
+	{
+	  /* Use mpfr_snprintf rounding to compute the largest
+	     representable decimal number less or equal than
+	     (-1 << (prec - 1)) - 1.  */
+	  mpfr_set_si_2exp (m, -1, prec - 1, GMP_RNDN);
+	  mpfr_sub_ui (m, m, 1, GMP_RNDN);
+	  mpfr_snprintf (buf, sizeof buf, "%.*RDe", p - 1, m);
+	  decimal_real_from_string (&minval, buf);
+	  min = build_real (expr_type, minval);
+	}
+      mpfr_clear (m);
+    }
+  else
+    return NULL_TREE;
+
+  if (flag_sanitize_undefined_trap_on_error)
+    fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      /* Create the __ubsan_handle_float_cast_overflow fn call.  */
+      tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
+				     NULL,
+				     ubsan_type_descriptor (expr_type, false),
+				     ubsan_type_descriptor (type, false),
+				     NULL_TREE);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
+	  : BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT;
+      fn = builtin_decl_explicit (bcode);
+      fn = build_call_expr_loc (loc, fn, 2,
+				build_fold_addr_expr_loc (loc, data),
+				ubsan_encode_value (expr, false));
+    }
+
+  t = fold_build2 (UNLE_EXPR, boolean_type_node, expr, min);
+  tt = fold_build2 (UNGE_EXPR, boolean_type_node, expr, max);
+  return fold_build3 (COND_EXPR, void_type_node,
+		      fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt),
+		      fn, integer_zero_node);
+}
+
 namespace {
 
 const pass_data pass_data_ubsan =
diff --git gcc/ubsan.h gcc/ubsan.h
index 67cc6e9..b008419 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -44,6 +44,7 @@ extern tree ubsan_type_descriptor (tree, bool);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);
+extern tree ubsan_instrument_float_cast (location_t, tree, tree);
 
 #endif  /* GCC_UBSAN_H  */
 
	Marek

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow (take 2)
  2014-05-20 20:33             ` [PATCH] Implement -fsanitize=float-cast-overflow (take 2) Marek Polacek
@ 2014-05-20 21:50               ` Joseph S. Myers
  2014-05-21 12:51                 ` Marek Polacek
  0 siblings, 1 reply; 16+ messages in thread
From: Joseph S. Myers @ 2014-05-20 21:50 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Jakub Jelinek, GCC Patches

On Tue, 20 May 2014, Marek Polacek wrote:

> * is missing tests for long doubles/-mlong-double-128,

Also missing tests for float - as far as I can see, only double is tested.  
Ideally all of float, double, long double, __float128 (where supported), 
__float80 (where supported) would be tested (the functionality supported 
for __fp16 (ARM) is a bit more restricted) - hopefully using some shared 
macros to avoid too much duplication between tests.

> * doesn't instrument _Decimal to integer conversions yet.

So the code

> +  else if (REAL_MODE_FORMAT (mode)->b == 10)
> +    {
> +      /* For _Decimal128 up to 34 decimal digits, - sign,
> +	 dot, e, exponent.  */

isn't actually being used yet?

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow (take 2)
  2014-05-20 21:50               ` Joseph S. Myers
@ 2014-05-21 12:51                 ` Marek Polacek
  2014-05-21 14:46                   ` Jakub Jelinek
  2014-05-21 17:50                   ` [PATCH] Implement -fsanitize=float-cast-overflow (take 2) Joseph S. Myers
  0 siblings, 2 replies; 16+ messages in thread
From: Marek Polacek @ 2014-05-21 12:51 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Jakub Jelinek, GCC Patches

On Tue, May 20, 2014 at 09:50:10PM +0000, Joseph S. Myers wrote:
> On Tue, 20 May 2014, Marek Polacek wrote:
> 
> > * is missing tests for long doubles/-mlong-double-128,
> 
> Also missing tests for float - as far as I can see, only double is tested.  
> Ideally all of float, double, long double, __float128 (where supported), 
> __float80 (where supported) would be tested (the functionality supported 
> for __fp16 (ARM) is a bit more restricted) - hopefully using some shared 
> macros to avoid too much duplication between tests.
 
Ok, I've added some tests for float, long double, __float128 and
__float80.  A snag was in __float128 type: libubsan supposedly can't
handle __float128 values and prints "0".  libubsan also can't handle
__float80 types with -m32 it seems.
Common macros moved to float-cast.h.

> > * doesn't instrument _Decimal to integer conversions yet.
> 
> So the code
> 
> > +  else if (REAL_MODE_FORMAT (mode)->b == 10)
> > +    {
> > +      /* For _Decimal128 up to 34 decimal digits, - sign,
> > +	 dot, e, exponent.  */
> 
> isn't actually being used yet?

Yes.  I suspect adding support for _Decimal* shouldn't be hard,
what's needed is to find the place where the conversion from _Decimal
to integer is taking place and add similar code as is in convert.c.
Jakub says he's writing another testcase that tests various type
combination conversions and max/min values - so we'll get even more
coverage.

I tested the following with -m32/-m64 on x86_64 and ppc64.

2014-05-21  Marek Polacek  <polacek@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	* builtins.def: Change SANITIZE_FLOAT_DIVIDE to SANITIZE_NONDEFAULT.
	* gcc.c (sanitize_spec_function): Likewise.
	* convert.c (convert_to_integer): Include "ubsan.h".  Add floating-point
	to integer instrumentation.
	* doc/invoke.texi: Document -fsanitize=float-cast-overflow.
	* flag-types.h (enum sanitize_code): Add SANITIZE_FLOAT_CAST and
	SANITIZE_NONDEFAULT.
	* opts.c (common_handle_option): Handle -fsanitize=float-cast-overflow.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
	BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT): Add.
	* ubsan.c: Include "realmpfr.h" and "dfp.h".
	(get_ubsan_type_info_for_type): Handle REAL_TYPEs.
	(ubsan_instrument_float_cast): New function.
	* ubsan.h (ubsan_instrument_float_cast): Declare.

	* c-c++-common/ubsan/float-cast-overflow-1.c: New test.
	* c-c++-common/ubsan/float-cast-overflow-2.c: New test.
	* c-c++-common/ubsan/float-cast-overflow-3.c: New test.
	* c-c++-common/ubsan/float-cast-overflow-4.c: New test.
	* c-c++-common/ubsan/float-cast-overflow-5.c: New test.
	* c-c++-common/ubsan/float-cast-overflow-6.c: New test.
	* c-c++-common/ubsan/float-cast.h: New test.
	* g++.dg/ubsan/float-cast-overflow-bf.C: New test.
	* gcc.dg/ubsan/float-cast-overflow-bf.c: New test.

diff --git gcc/builtins.def gcc/builtins.def
index d400ecb..cd823a3 100644
--- gcc/builtins.def
+++ gcc/builtins.def
@@ -176,7 +176,7 @@ along with GCC; see the file COPYING3.  If not see
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,    \
 	       true, true, true, ATTRS, true, \
 	      (flag_sanitize & (SANITIZE_ADDRESS | SANITIZE_THREAD \
-				| SANITIZE_UNDEFINED | SANITIZE_FLOAT_DIVIDE)))
+				| SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT)))
 
 #undef DEF_CILKPLUS_BUILTIN
 #define DEF_CILKPLUS_BUILTIN(ENUM, NAME, TYPE, ATTRS)  \
diff --git gcc/convert.c gcc/convert.c
index 91c1da2..b8f3671 100644
--- gcc/convert.c
+++ gcc/convert.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h"
 #include "target.h"
 #include "langhooks.h"
+#include "ubsan.h"
 
 /* Convert EXPR to some pointer or reference type TYPE.
    EXPR must be pointer, reference, integer, enumeral, or literal zero;
@@ -394,6 +395,7 @@ convert_to_integer (tree type, tree expr)
   tree intype = TREE_TYPE (expr);
   unsigned int inprec = element_precision (intype);
   unsigned int outprec = element_precision (type);
+  location_t loc = EXPR_LOCATION (expr);
 
   /* An INTEGER_TYPE cannot be incomplete, but an ENUMERAL_TYPE can
      be.  Consider `enum E = { a, b = (enum E) 3 };'.  */
@@ -844,7 +846,17 @@ convert_to_integer (tree type, tree expr)
       return build1 (CONVERT_EXPR, type, expr);
 
     case REAL_TYPE:
-      return build1 (FIX_TRUNC_EXPR, type, expr);
+      if (flag_sanitize & SANITIZE_FLOAT_CAST)
+	{
+	  expr = save_expr (expr);
+	  tree check = ubsan_instrument_float_cast (loc, type, expr);
+	  expr = build1 (FIX_TRUNC_EXPR, type, expr);
+	  if (check == NULL)
+	    return expr;
+	  return fold_build2 (COMPOUND_EXPR, TREE_TYPE (expr), check, expr);
+	}
+      else
+	return build1 (FIX_TRUNC_EXPR, type, expr);
 
     case FIXED_POINT_TYPE:
       return build1 (FIXED_CONVERT_EXPR, type, expr);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 5b1b0f1..659c265 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -5427,6 +5427,13 @@ Detect floating-point division by zero.  Unlike other similar options,
 @option{-fsanitize=undefined}, since floating-point division by zero can
 be a legitimate way of obtaining infinities and NaNs.
 
+@item -fsanitize=float-cast-overflow
+@opindex fsanitize=float-cast-overflow
+
+This option enables floating-point type to integer conversion checking.
+We check that the result of the conversion does not overflow.
+This option does not work well with @code{FE_INVALID} exceptions enabled.
+
 @item -fsanitize-recover
 @opindex fsanitize-recover
 By default @option{-fsanitize=undefined} sanitization (and its suboptions
diff --git gcc/flag-types.h gcc/flag-types.h
index caf4039..ed00046 100644
--- gcc/flag-types.h
+++ gcc/flag-types.h
@@ -229,9 +229,11 @@ enum sanitize_code {
   SANITIZE_BOOL = 1 << 10,
   SANITIZE_ENUM = 1 << 11,
   SANITIZE_FLOAT_DIVIDE = 1 << 12,
+  SANITIZE_FLOAT_CAST = 1 << 13,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM,
+  SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST
 };
 
 /* flag_vtable_verify initialization levels. */
diff --git gcc/gcc.c gcc/gcc.c
index 7bea6d7..9ac18e6 100644
--- gcc/gcc.c
+++ gcc/gcc.c
@@ -8170,7 +8170,7 @@ sanitize_spec_function (int argc, const char **argv)
   if (strcmp (argv[0], "thread") == 0)
     return (flag_sanitize & SANITIZE_THREAD) ? "" : NULL;
   if (strcmp (argv[0], "undefined") == 0)
-    return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_FLOAT_DIVIDE))
+    return ((flag_sanitize & (SANITIZE_UNDEFINED | SANITIZE_NONDEFAULT))
 	    && !flag_sanitize_undefined_trap_on_error) ? "" : NULL;
   if (strcmp (argv[0], "leak") == 0)
     return ((flag_sanitize
diff --git gcc/opts.c gcc/opts.c
index 5f4b2e3..2f4f913 100644
--- gcc/opts.c
+++ gcc/opts.c
@@ -1463,6 +1463,8 @@ common_handle_option (struct gcc_options *opts,
 	      { "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
 	      { "float-divide-by-zero", SANITIZE_FLOAT_DIVIDE,
 		sizeof "float-divide-by-zero" - 1 },
+	      { "float-cast-overflow", SANITIZE_FLOAT_CAST,
+		sizeof "float-cast-overflow" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
diff --git gcc/sanitizer.def gcc/sanitizer.def
index 6184b5a..a2f7ff0 100644
--- gcc/sanitizer.def
+++ gcc/sanitizer.def
@@ -371,3 +371,11 @@ DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE_ABORT,
 		      "__ubsan_handle_load_invalid_value_abort",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW,
+		      "__ubsan_handle_float_cast_overflow",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT,
+		      "__ubsan_handle_float_cast_overflow_abort",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NORETURN_NOTHROW_LEAF_LIST)
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
index e69de29..249731d 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-1.c
@@ -0,0 +1,204 @@
+/* { dg-do run { target { lp64 || ilp32 } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+/* { dg-additional-options "-msse2 -mfpmath=sse" { target { sse2_runtime && ia32 } } } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+  const double inf = __builtin_inf ();
+  const double nan = __builtin_nan ("");
+  volatile double d;
+
+  volatile signed char sc;
+  d = SCHAR_MIN;
+  CHECK_BOUNDARY (sc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (sc, d);
+  d = SCHAR_MAX;
+  CHECK_BOUNDARY (sc, d);
+  CHECK_NONNUMBERS (sc);
+
+  volatile unsigned char uc;
+  d = UCHAR_MAX;
+  CHECK_BOUNDARY (uc, d);
+  d = 0.0;
+  CHECK_BOUNDARY (uc, d);
+  CHECK_NONNUMBERS (uc);
+
+  volatile short int s;
+  d = SHRT_MIN;
+  CHECK_BOUNDARY (s, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s, d);
+  d = SHRT_MAX;
+  CHECK_BOUNDARY (s, d);
+  CHECK_NONNUMBERS (s);
+
+  volatile unsigned short int us;
+  d = USHRT_MAX;
+  CHECK_BOUNDARY (us, d);
+  d = 0.0;
+  CHECK_BOUNDARY (us, d);
+  CHECK_NONNUMBERS (us);
+
+  volatile int i;
+  d = INT_MIN;
+  CHECK_BOUNDARY (i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (i, d);
+  d = INT_MAX;
+  CHECK_BOUNDARY (i, d);
+  CHECK_NONNUMBERS (i);
+
+  volatile unsigned int u;
+  d = UINT_MAX;
+  CHECK_BOUNDARY (u, d);
+  d = 0.0;
+  CHECK_BOUNDARY (u, d);
+  CHECK_NONNUMBERS (u);
+
+  volatile long l;
+  /* 64-bit vs 32-bit longs matter causes too much of a headache.  */
+  d = 0.0;
+  CHECK_BOUNDARY (l, d);
+  CHECK_NONNUMBERS (l);
+
+  volatile unsigned long ul;
+  d = 0.0;
+  CHECK_BOUNDARY (ul, d);
+  CHECK_NONNUMBERS (ul);
+
+  volatile long long ll;
+  d = LLONG_MIN;
+  CHECK_BOUNDARY (ll, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ll, d);
+  d = LLONG_MAX;
+  CHECK_BOUNDARY (ll, d);
+  CHECK_NONNUMBERS (ll);
+
+  volatile unsigned long long ull;
+  d = ULLONG_MAX;
+  CHECK_BOUNDARY (ull, d);
+  d = 0.0;
+  CHECK_BOUNDARY (ull, d);
+  CHECK_NONNUMBERS (ull);
+
+  return 0;
+}
+
+/* { dg-output "value -133 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32773 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768.5 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32772 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65540 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 9.22337e\\\+18 is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.84467e\\\+19 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c
index e69de29..15eacc9 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-2.c
@@ -0,0 +1,73 @@
+/* { dg-do run } */
+/* { dg-require-effective-target int128 } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include "float-cast.h"
+
+int
+main (void)
+{
+  const double inf = __builtin_inf ();
+  const double nan = __builtin_nan ("");
+  volatile double d;
+
+  __int128 i;
+  d = INT128_MIN;
+  CHECK_BOUNDARY (i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (i, d);
+  d = INT128_MAX;
+  CHECK_BOUNDARY (i, d);
+  CHECK_NONNUMBERS (i);
+
+  unsigned __int128 u;
+  d = UINT128_MAX;
+  CHECK_BOUNDARY (u, d);
+  d = 0.0;
+  CHECK_BOUNDARY (u, d);
+  CHECK_NONNUMBERS (u);
+
+  return 0;
+}
+
+/* { dg-output "runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 1.70141e\\\+38 is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value nan is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -nan is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value inf is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -inf is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value 3.40282e\\\+38 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -5 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -1.5 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value nan is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -nan is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value inf is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*runtime error: value -inf is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-3.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-3.c
index e69de29..2200e66 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-3.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-3.c
@@ -0,0 +1,40 @@
+/* { dg-do run { target { lp64 || ilp32 } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+  volatile float f;
+
+  volatile signed char s;
+  f = SCHAR_MIN;
+  CHECK_BOUNDARY (s, f);
+  f = 0.0;
+  CHECK_BOUNDARY (s, f);
+  f = SCHAR_MAX;
+  CHECK_BOUNDARY (s, f);
+
+  volatile unsigned char u;
+  f = UCHAR_MAX;
+  CHECK_BOUNDARY (u, f);
+  f = 0.0;
+  CHECK_BOUNDARY (u, f);
+
+  return 0;
+}
+
+/* { dg-output "value -133* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-4.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-4.c
index e69de29..7704aa9 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-4.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-4.c
@@ -0,0 +1,52 @@
+/* { dg-do run { target { lp64 } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+  const long double inf = __builtin_infl ();
+  const long double nan = __builtin_nanl ("");
+  volatile long double ld;
+
+  volatile int i;
+  ld = INT_MIN;
+  CHECK_BOUNDARY (i, ld);
+  ld = 0.0l;
+  CHECK_BOUNDARY (i, ld);
+  ld = INT_MAX;
+  CHECK_BOUNDARY (i, ld);
+  CHECK_NONNUMBERS (i);
+
+  volatile unsigned int u;
+  ld = UINT_MAX;
+  CHECK_BOUNDARY (u, ld);
+  ld = 0.0l;
+  CHECK_BOUNDARY (u, ld);
+  CHECK_NONNUMBERS (u);
+
+  return 0;
+}
+
+/* { dg-output "value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -nan is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value inf is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -inf is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-5.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-5.c
index e69de29..44910ee 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-5.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-5.c
@@ -0,0 +1,40 @@
+/* { dg-do run { target i?86-*-* x86_64-*-* ia64-*-* } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+  volatile __float128 f;
+
+  volatile signed char s;
+  f = SCHAR_MIN;
+  CHECK_BOUNDARY (s, f);
+  f = 0.0q;
+  CHECK_BOUNDARY (s, f);
+  f = SCHAR_MAX;
+  CHECK_BOUNDARY (s, f);
+
+  volatile unsigned char u;
+  f = UCHAR_MAX;
+  CHECK_BOUNDARY (u, f);
+  f = 0.0q;
+  CHECK_BOUNDARY (u, f);
+
+  return 0;
+}
+
+/* { dg-output "value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[^\n\r]* is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-6.c gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-6.c
index e69de29..f51a2c8 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-6.c
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-6.c
@@ -0,0 +1,40 @@
+/* { dg-do run { target { { x86_64-*-* ia64-*-* } && { ! { ia32 } } } } } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+#include <limits.h>
+#include "float-cast.h"
+
+int
+main (void)
+{
+  volatile __float80 f;
+
+  volatile signed char s;
+  f = SCHAR_MIN;
+  CHECK_BOUNDARY (s, f);
+  f = 0.0w;
+  CHECK_BOUNDARY (s, f);
+  f = SCHAR_MAX;
+  CHECK_BOUNDARY (s, f);
+
+  volatile unsigned char u;
+  f = UCHAR_MAX;
+  CHECK_BOUNDARY (u, f);
+  f = 0.0w;
+  CHECK_BOUNDARY (u, f);
+
+  return 0;
+}
+
+/* { dg-output "value -133 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 132 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 260 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/c-c++-common/ubsan/float-cast.h gcc/testsuite/c-c++-common/ubsan/float-cast.h
index e69de29..166da8f 100644
--- gcc/testsuite/c-c++-common/ubsan/float-cast.h
+++ gcc/testsuite/c-c++-common/ubsan/float-cast.h
@@ -0,0 +1,28 @@
+/* Various macros for -fsanitize=float-cast-overflow testing.  */
+
+#define INT128_MAX (__int128) (((unsigned __int128) 1 << ((__SIZEOF_INT128__ * __CHAR_BIT__) - 1)) - 1)
+#define INT128_MIN (-INT128_MAX - 1)
+#define UINT128_MAX ((2 * (unsigned __int128) INT128_MAX) + 1)
+
+#define CHECK_BOUNDARY(VAR, VAL)	\
+  (VAR) = (VAL) - 5.0;			\
+  (VAR) = (VAL) - 1.5;			\
+  (VAR) = (VAL) - 1.0;			\
+  (VAR) = (VAL) - 0.75;			\
+  (VAR) = (VAL) - 0.5;			\
+  (VAR) = (VAL) - 0.0000001;		\
+  (VAR) = (VAL) - 0.0;			\
+  (VAR) = (VAL);			\
+  (VAR) = (VAL) + 0.0;			\
+  (VAR) = (VAL) + 0.0000001;		\
+  (VAR) = (VAL) + 0.5;			\
+  (VAR) = (VAL) + 0.75;			\
+  (VAR) = (VAL) + 1.0;			\
+  (VAR) = (VAL) + 1.5;			\
+  (VAR) = (VAL) + 5.0;
+
+#define CHECK_NONNUMBERS(VAR)		\
+  (VAR) = nan;				\
+  (VAR) = -nan;				\
+  (VAR) = inf;				\
+  (VAR) = -inf;
diff --git gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C
index e69de29..d1df76d 100644
--- gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C
+++ gcc/testsuite/g++.dg/ubsan/float-cast-overflow-bf.C
@@ -0,0 +1,62 @@
+// { dg-do run { target { int32 } } }
+// { dg-options "-fsanitize=float-cast-overflow" }
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-__INT_MAX__ - 1)
+#define UINT_MAX 2 * (unsigned) __INT_MAX__ + 1
+
+struct S
+{
+  int i:1;
+} s;
+
+struct T
+{
+  unsigned int i:1;
+} t;
+
+int
+main (void)
+{
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)        \
+  (VAR) = (VAL) - 1.5;                  \
+  (VAR) = (VAL) - 1.0;                  \
+  (VAR) = (VAL) - 0.75;                  \
+  (VAR) = (VAL) - 0.5;                  \
+  (VAR) = (VAL) - 0.0000001;            \
+  (VAR) = (VAL) - 0.0;                  \
+  (VAR) = (VAL);                        \
+  (VAR) = (VAL) + 0.0;                  \
+  (VAR) = (VAL) + 0.0000001;            \
+  (VAR) = (VAL) + 0.5;                  \
+  (VAR) = (VAL) + 0.75;                  \
+  (VAR) = (VAL) + 1.0;                  \
+  (VAR) = (VAL) + 1.5;
+
+  /* Signed bit-field.  (INT_MIN, INT_MAX) is valid.  */
+  d = INT_MIN;
+  CHECK_BOUNDARY (s.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s.i, d);
+  d = INT_MAX;
+  CHECK_BOUNDARY (s.i, d);
+
+  /* Unsigned bit-field.  (0, UINT_MAX) is valid.  */
+  d = UINT_MAX;
+  CHECK_BOUNDARY (t.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (t.i, d);
+
+  return 0;
+}
+
+/* { dg-output "value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.14748e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 4.29497e\\\+09 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c
index e69de29..298d0d9 100644
--- gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c
+++ gcc/testsuite/gcc.dg/ubsan/float-cast-overflow-bf.c
@@ -0,0 +1,72 @@
+/* { dg-do run } */
+/* { dg-options "-fsanitize=float-cast-overflow" } */
+
+struct
+{
+  int i:1;
+} s;
+
+struct
+{
+  unsigned int i:1;
+} t;
+
+int
+main (void)
+{
+  volatile double d;
+
+#define CHECK_BOUNDARY(VAR, VAL)        \
+  (VAR) = (VAL) - 1.5;                  \
+  (VAR) = (VAL) - 1.0;                  \
+  (VAR) = (VAL) - 0.5;                  \
+  (VAR) = (VAL) - 0.0000001;            \
+  (VAR) = (VAL) - 0.0;                  \
+  (VAR) = (VAL);                        \
+  (VAR) = (VAL) + 0.0;                  \
+  (VAR) = (VAL) + 0.0000001;            \
+  (VAR) = (VAL) + 0.5;                  \
+  (VAR) = (VAL) + 1.0;                  \
+  (VAR) = (VAL) + 1.5;
+
+  /* Signed bit-field.  (-1, 0) is valid.  */
+  d = -1.0;
+  CHECK_BOUNDARY (s.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (s.i, d);
+  d = 1.0;
+  CHECK_BOUNDARY (s.i, d);
+
+  /* Unsigned bit-field.  (0, 1) is valid.  */
+  d = -1.0;
+  CHECK_BOUNDARY (t.i, d);
+  d = 0.0;
+  CHECK_BOUNDARY (t.i, d);
+  d = 1.0;
+  CHECK_BOUNDARY (t.i, d);
+
+  return 0;
+}
+
+/* { dg-output "value -2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 2.5 is outside the range of representable values of type\[^\n\r]*(\n|\r\n|\r)" } */
diff --git gcc/ubsan.c gcc/ubsan.c
index 11461d0..cc5556f 100644
--- gcc/ubsan.c
+++ gcc/ubsan.c
@@ -47,6 +47,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "asan.h"
 #include "gimplify-me.h"
 #include "intl.h"
+#include "realmpfr.h"
+#include "dfp.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -267,9 +269,14 @@ static unsigned short
 get_ubsan_type_info_for_type (tree type)
 {
   gcc_assert (TYPE_SIZE (type) && tree_fits_uhwi_p (TYPE_SIZE (type)));
-  int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
-  gcc_assert (prec != -1);
-  return (prec << 1) | !TYPE_UNSIGNED (type);
+  if (TREE_CODE (type) == REAL_TYPE)
+    return tree_to_uhwi (TYPE_SIZE (type));
+  else
+    {
+      int prec = exact_log2 (tree_to_uhwi (TYPE_SIZE (type)));
+      gcc_assert (prec != -1);
+      return (prec << 1) | !TYPE_UNSIGNED (type);
+    }
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
@@ -891,6 +898,130 @@ instrument_bool_enum_load (gimple_stmt_iterator *gsi)
   gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
 }
 
+/* Instrument float point-to-integer conversion.  TYPE is an integer type of
+   destination, EXPR is floating-point expression.  */
+
+tree
+ubsan_instrument_float_cast (location_t loc, tree type, tree expr)
+{
+  tree expr_type = TREE_TYPE (expr);
+  tree t, tt, fn, min, max;
+  enum machine_mode mode = TYPE_MODE (expr_type);
+  int prec = TYPE_PRECISION (type);
+  bool uns_p = TYPE_UNSIGNED (type);
+
+  /* Float to integer conversion first truncates toward zero, so
+     even signed char c = 127.875f; is not problematic.
+     Therefore, we should complain only if EXPR is unordered or smaller
+     or equal than TYPE_MIN_VALUE - 1.0 or greater or equal than
+     TYPE_MAX_VALUE + 1.0.  */
+  if (REAL_MODE_FORMAT (mode)->b == 2)
+    {
+      /* For maximum, TYPE_MAX_VALUE might not be representable
+	 in EXPR_TYPE, e.g. if TYPE is 64-bit long long and
+	 EXPR_TYPE is IEEE single float, but TYPE_MAX_VALUE + 1.0 is
+	 either representable or infinity.  */
+      REAL_VALUE_TYPE maxval = dconst1;
+      SET_REAL_EXP (&maxval, REAL_EXP (&maxval) + prec - !uns_p);
+      real_convert (&maxval, mode, &maxval);
+      max = build_real (expr_type, maxval);
+
+      /* For unsigned, assume -1.0 is always representable.  */
+      if (uns_p)
+	min = build_minus_one_cst (expr_type);
+      else
+	{
+	  /* TYPE_MIN_VALUE is generally representable (or -inf),
+	     but TYPE_MIN_VALUE - 1.0 might not be.  */
+	  REAL_VALUE_TYPE minval = dconstm1, minval2;
+	  SET_REAL_EXP (&minval, REAL_EXP (&minval) + prec - 1);
+	  real_convert (&minval, mode, &minval);
+	  real_arithmetic (&minval2, MINUS_EXPR, &minval, &dconst1);
+	  real_convert (&minval2, mode, &minval2);
+	  if (real_compare (EQ_EXPR, &minval, &minval2)
+	      && !real_isinf (&minval))
+	    {
+	      /* If TYPE_MIN_VALUE - 1.0 is not representable and
+		 rounds to TYPE_MIN_VALUE, we need to subtract
+		 more.  As REAL_MODE_FORMAT (mode)->p is the number
+		 of base digits, we want to subtract a number that
+		 will be 1 << (REAL_MODE_FORMAT (mode)->p - 1)
+		 times smaller than minval.  */
+	      minval2 = dconst1;
+	      gcc_assert (prec > REAL_MODE_FORMAT (mode)->p);
+	      SET_REAL_EXP (&minval2,
+			    REAL_EXP (&minval2) + prec - 1
+			    - REAL_MODE_FORMAT (mode)->p + 1);
+	      real_arithmetic (&minval2, MINUS_EXPR, &minval, &minval2);
+	      real_convert (&minval2, mode, &minval2);
+	    }
+	  min = build_real (expr_type, minval2);
+	}
+    }
+  else if (REAL_MODE_FORMAT (mode)->b == 10)
+    {
+      /* For _Decimal128 up to 34 decimal digits, - sign,
+	 dot, e, exponent.  */
+      char buf[64];
+      mpfr_t m;
+      int p = REAL_MODE_FORMAT (mode)->p;
+      REAL_VALUE_TYPE maxval, minval;
+
+      /* Use mpfr_snprintf rounding to compute the smallest
+	 representable decimal number greater or equal than
+	 1 << (prec - !uns_p).  */
+      mpfr_init2 (m, prec + 2);
+      mpfr_set_ui_2exp (m, 1, prec - !uns_p, GMP_RNDN);
+      mpfr_snprintf (buf, sizeof buf, "%.*RUe", p - 1, m);
+      decimal_real_from_string (&maxval, buf);
+      max = build_real (expr_type, maxval);
+
+      /* For unsigned, assume -1.0 is always representable.  */
+      if (uns_p)
+	min = build_minus_one_cst (expr_type);
+      else
+	{
+	  /* Use mpfr_snprintf rounding to compute the largest
+	     representable decimal number less or equal than
+	     (-1 << (prec - 1)) - 1.  */
+	  mpfr_set_si_2exp (m, -1, prec - 1, GMP_RNDN);
+	  mpfr_sub_ui (m, m, 1, GMP_RNDN);
+	  mpfr_snprintf (buf, sizeof buf, "%.*RDe", p - 1, m);
+	  decimal_real_from_string (&minval, buf);
+	  min = build_real (expr_type, minval);
+	}
+      mpfr_clear (m);
+    }
+  else
+    return NULL_TREE;
+
+  if (flag_sanitize_undefined_trap_on_error)
+    fn = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
+  else
+    {
+      /* Create the __ubsan_handle_float_cast_overflow fn call.  */
+      tree data = ubsan_create_data ("__ubsan_float_cast_overflow_data", NULL,
+				     NULL,
+				     ubsan_type_descriptor (expr_type, false),
+				     ubsan_type_descriptor (type, false),
+				     NULL_TREE);
+      enum built_in_function bcode
+	= flag_sanitize_recover
+	  ? BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW
+	  : BUILT_IN_UBSAN_HANDLE_FLOAT_CAST_OVERFLOW_ABORT;
+      fn = builtin_decl_explicit (bcode);
+      fn = build_call_expr_loc (loc, fn, 2,
+				build_fold_addr_expr_loc (loc, data),
+				ubsan_encode_value (expr, false));
+    }
+
+  t = fold_build2 (UNLE_EXPR, boolean_type_node, expr, min);
+  tt = fold_build2 (UNGE_EXPR, boolean_type_node, expr, max);
+  return fold_build3 (COND_EXPR, void_type_node,
+		      fold_build2 (TRUTH_OR_EXPR, boolean_type_node, t, tt),
+		      fn, integer_zero_node);
+}
+
 namespace {
 
 const pass_data pass_data_ubsan =
diff --git gcc/ubsan.h gcc/ubsan.h
index 67cc6e9..b008419 100644
--- gcc/ubsan.h
+++ gcc/ubsan.h
@@ -44,6 +44,7 @@ extern tree ubsan_type_descriptor (tree, bool);
 extern tree ubsan_encode_value (tree, bool = false);
 extern bool is_ubsan_builtin_p (tree);
 extern tree ubsan_build_overflow_builtin (tree_code, location_t, tree, tree, tree);
+extern tree ubsan_instrument_float_cast (location_t, tree, tree);
 
 #endif  /* GCC_UBSAN_H  */
 
	Marek

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow (take 2)
  2014-05-21 12:51                 ` Marek Polacek
@ 2014-05-21 14:46                   ` Jakub Jelinek
  2014-05-21 18:52                     ` Jakub Jelinek
  2014-05-21 17:50                   ` [PATCH] Implement -fsanitize=float-cast-overflow (take 2) Joseph S. Myers
  1 sibling, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-05-21 14:46 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Joseph S. Myers, GCC Patches

[-- Attachment #1: Type: text/plain, Size: 2662 bytes --]

On Wed, May 21, 2014 at 02:51:00PM +0200, Marek Polacek wrote:
> On Tue, May 20, 2014 at 09:50:10PM +0000, Joseph S. Myers wrote:
> > On Tue, 20 May 2014, Marek Polacek wrote:
> > 
> > > * is missing tests for long doubles/-mlong-double-128,
> > 
> > Also missing tests for float - as far as I can see, only double is tested.  
> > Ideally all of float, double, long double, __float128 (where supported), 
> > __float80 (where supported) would be tested (the functionality supported 
> > for __fp16 (ARM) is a bit more restricted) - hopefully using some shared 
> > macros to avoid too much duplication between tests.
>  
> Ok, I've added some tests for float, long double, __float128 and
> __float80.  A snag was in __float128 type: libubsan supposedly can't
> handle __float128 values and prints "0".  libubsan also can't handle
> __float80 types with -m32 it seems.
> Common macros moved to float-cast.h.

Clearly libubsan doesn't support _Decimal* either, so I guess for now
we should just avoid emitting any __ubsan_* diagnostics for any of the
__float{80,128} or _Decimal{32,64,128} types for now, and work with upstream
to add support for that (given the problematic design where a type
is identified only by kind and bitsize, I guess for _Decimal* we need to use
a different (new) kind, not sure what can be done about __float{80,128},
as libubsan treats both bitsize 80 and 128 as long double.

Note that libstdc++ doesn't support conversion of _Decimal{32,64,128}
nor std::decimal::decimal{32,64,128} to strings, and not sure if libubsan
would like to be linked against something that rarely used as libdecnumber.
So, I guess at least for now, we should have a way to tell libubsan
about those types (both decimal and __float{80,128}, but if libubsan
can't easily print those values into strings, it should just print
<unprintable value> or something similar for now.

> Yes.  I suspect adding support for _Decimal* shouldn't be hard,
> what's needed is to find the place where the conversion from _Decimal
> to integer is taking place and add similar code as is in convert.c.
> Jakub says he's writing another testcase that tests various type
> combination conversions and max/min values - so we'll get even more
> coverage.

Here is a testcase that (IMHO, not tested with your patch) should
test various boundary cases that shouldn't result in undefined behavior.
I've tried to keep it portable across various architectures, assumes
primarily two's complement and (likely) only supports binary and decimal
floating point formats.
Seems _Decimal{32,64,128} is not convertible
to/from int128 right now, so that is disabled in the test for now.

	Jakub

[-- Attachment #2: float-cast-overflow-3.c --]
[-- Type: text/plain, Size: 8608 bytes --]

/* { dg-do run } */
/* { dg-options "-fsanitize=float-cast-overflow -fno-sanitize-recover" } */
/* FIXME: When _DecimalXX <-> {signed, unsigned} __int128 conversions are
   supported, -DBROKEN_DECIMAL_INT128 can be removed.  */
/* { dg-additional-options "-DUSE_DFP -DBROKEN_DECIMAL_INT128" { target dfp } } */

#define CVTFN(type1, type2) \
__attribute__((noinline)) type1	\
cvt_##type1##_##type2 (type2 x)	\
{				\
  return x;			\
}

typedef signed char sc;
#define sc_MIN (-__SCHAR_MAX__ - 1)
#define sc_MAX __SCHAR_MAX__
typedef unsigned char uc;
#define uc_MIN 0
#define uc_MAX (2U * __SCHAR_MAX__ + 1U)
typedef char c;
#define c_MIN ((((char) -1) > (char) 0) ? uc_MIN : sc_MIN)
#define c_MAX ((((char) -1) > (char) 0) ? uc_MAX : sc_MAX)
typedef signed short ss;
#define ss_MIN (-__SHRT_MAX__ - 1)
#define ss_MAX __SHRT_MAX__
typedef unsigned short us;
#define us_MIN 0
#define us_MAX (2U * __SHRT_MAX__ + 1U)
typedef signed int si;
#define si_MIN (-__INT_MAX__ - 1)
#define si_MAX __INT_MAX__
typedef unsigned int ui;
#define ui_MIN 0
#define ui_MAX (2U * __INT_MAX__ + 1U)
typedef signed long sl;
#define sl_MIN (-__LONG_MAX__ - 1L)
#define sl_MAX __LONG_MAX__
typedef unsigned long ul;
#define ul_MIN 0L
#define ul_MAX (2UL * __LONG_MAX__ + 1UL)
typedef signed long long sll;
#define sll_MIN (-__LONG_LONG_MAX__ - 1LL)
#define sll_MAX __LONG_LONG_MAX__
typedef unsigned long long ull;
#define ull_MIN 0LL
#define ull_MAX (2ULL * __LONG_LONG_MAX__ + 1ULL)
#ifdef __SIZEOF_INT128__
typedef signed __int128 si128;
# define si128_MAX \
  ((signed __int128) ((((unsigned __int128) 1) \
		       << (__CHAR_BIT__ * __SIZEOF_INT128__ - 1)) - 1))
# define si128_MIN (-si128_MAX - 1)
typedef unsigned __int128 ui128;
#define ui128_MIN ((unsigned __int128) 0)
#define ui128_MAX (((unsigned __int128) 2) * si128_MAX + 1)
#endif

#ifdef __SIZEOF_INT128__
# define CVTS128(type2) CVTFN (si128, type2) CVTFN (ui128, type2)
#else
# define CVTS128(type2)
#endif

#define CVTS(type2) \
  CVTFN (sc, type2) CVTFN (c, type2) CVTFN (uc, type2)	\
  CVTFN (ss, type2) CVTFN (us, type2)			\
  CVTFN (si, type2) CVTFN (ui, type2)			\
  CVTFN (sl, type2) CVTFN (ul, type2)			\
  CVTFN (sll, type2) CVTFN (ull, type2)			\
  CVTS128 (type2)

#ifdef __SIZEOF_INT128__
# define TWO ((unsigned __int128) 2)
# define M1U ((unsigned __int128) -1)
# define MAXS (__CHAR_BIT__ * __SIZEOF_INT128__)
# define MAXT unsigned __int128
#else
# define TWO 2ULL
# define M1U -1ULL
# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
# define MAXT unsigned long long
#endif

typedef float f;
#define f_RADIX 2
#define f_MANT_DIG __FLT_MANT_DIG__
#define f_MAX ((TWO << (f_MANT_DIG - 1)) - 1)
typedef double d;
#define d_RADIX 2
#define d_MANT_DIG __DBL_MANT_DIG__
#define d_MAX ((TWO << (d_MANT_DIG - 1)) - 1)
typedef long double ld;
#define ld_RADIX 2
#define ld_MANT_DIG __LDBL_MANT_DIG__
#define ld_MAX \
  (ld_MANT_DIG > MAXS ? M1U : (TWO << (ld_MANT_DIG > MAXS \
				       ? 0 : ld_MANT_DIG - 1)) - 1)
CVTS (f)
CVTS (d)
CVTS (ld)
#ifdef __SIZEOF_FLOAT80__
typedef __float80 f80;
# define f80_RADIX 2
# define f80_MANT_DIG 64
# define f80_MAX ((TWO << (d_MANT_DIG - 1)) - 1)
CVTS (f80)
#endif
#ifdef __SIZEOF_FLOAT128__
typedef __float128 f128;
# define f128_RADIX 2
# define f128_MANT_DIG 113
# define f128_MAX \
  (f128_MANT_DIG > MAXS ? M1U : (TWO << (f128_MANT_DIG > MAXS \
					 ? 0 : f128_MANT_DIG - 1)) - 1)
CVTS (f128)
#endif
#ifdef USE_DFP
typedef _Decimal32 d32;
# define d32_RADIX 10
# define d32_MANT_DIG __DEC32_MANT_DIG__
# if d32_MANT_DIG == 7
#  define d32_MAX 9999999ULL
# endif
typedef _Decimal64 d64;
# define d64_RADIX 10
# define d64_MANT_DIG __DEC64_MANT_DIG__
# if d64_MANT_DIG == 16
#  define d64_MAX 9999999999999999ULL
# endif
typedef _Decimal128 d128;
# define d128_RADIX 10
# define d128_MANT_DIG __DEC128_MANT_DIG__
# ifdef __SIZEOF_FLOAT128__
#  if d128_MANT_DIG == 34
/* #define d128_MAX 1ed09bead87c0378d8e63ffffffff */
#   define d128_MAX \
  ((((unsigned __int128) 0x1ed09bead87c0) << 64) + 0x378d8e63ffffffffULL)
#  else
#   define d128_MAX M1U
#  endif
# endif
# ifdef BROKEN_DECIMAL_INT128
#  undef CVTS128
#  define CVTS128(type2)
# endif
CVTS (d32)
CVTS (d64)
CVTS (d128)
#endif

extern
#ifdef __cplusplus
"C"
#endif
void abort ();
#define P(n) __builtin_printf ("%d\n", n)

#define TEST(type1, type2) \
  if (cvt_##type1##_##type2 (-0.5f) != 0) abort ();		\
  if (cvt_##type1##_##type2 (0.5f) != 0) abort ();		\
  if (cvt_##type1##_##type2 (-0.75f) != 0) abort ();		\
  if (cvt_##type1##_##type2 (0.75f) != 0) abort ();		\
  if (type1##_MIN)						\
    {								\
      /* For RADIX 2 type1##_MIN should be always */		\
      /* exactly representable in type2.  */			\
      if (type2##_RADIX == 2					\
	  || type1##_MAX <= type2##_MAX)			\
	{							\
	  if (cvt_##type1##_##type2 (type1##_MIN)		\
	      != type1##_MIN) abort ();				\
	  if (cvt_##type1##_##type2 ((type2) -0.75f		\
				     + type1##_MIN)		\
	      != type1##_MIN) abort ();				\
	}							\
      else							\
	{							\
	  type2 min = type1##_MIN;				\
	  /* tem could be below minimum here due to */		\
	  /* rounding.  */					\
	  MAXT add = 1;						\
	  while (add)						\
	    {							\
	      volatile type2 tem = type1##_MIN + (type1) add;	\
	      if (tem != min)					\
		break;						\
	      MAXT newadd = add * type2##_RADIX;		\
	      if (newadd < add || newadd > type1##_MAX)		\
		add = 0;					\
	      else						\
		add = newadd;					\
	    }							\
	  if (add)						\
	    {							\
	      MAXT newadd					\
		= (-(type1##_MIN + (type1) add)) % add;		\
	      volatile type2 tem = type1##_MIN + (type1) newadd;\
	      volatile type2 tem2 = type1##_MIN + (type1) add;	\
	      if (tem == tem2)					\
		add = newadd;					\
	      else						\
		{						\
		  newadd += add;				\
		  if (newadd < add || newadd > type1##_MAX)	\
		    add = 0;					\
		  else						\
		    {						\
		      tem = type1##_MIN + (type1) newadd;	\
		      if (tem == tem2)				\
			add = newadd;				\
		      else					\
			add = 0;				\
		    }						\
		}						\
	    }							\
	  if (add						\
	      && cvt_##type1##_##type2 (type1##_MIN		\
					+ (type1) add)		\
		 != type1##_MIN + (type1) add) abort ();	\
	}							\
    }								\
  if (type1##_MAX <= type2##_MAX)				\
    {								\
      if (cvt_##type1##_##type2 (type1##_MAX) != type1##_MAX)	\
	abort ();						\
      volatile type2 tem = ((type2) 0.75f) + type1##_MAX;	\
      volatile type2 tem2 = ((type2) 1.0f) + type1##_MAX;	\
      if (tem < tem2						\
	  && cvt_##type1##_##type2 ((type2) 0.75f + type1##_MAX)\
	     != type1##_MAX) abort ();				\
    }								\
  else								\
    {								\
      type2 max = type1##_MAX;					\
      /* tem could be above maximum here due to rounding.  */	\
      MAXT sub = 1;						\
      while (sub)						\
	{							\
	  volatile type2 tem = type1##_MAX - sub;		\
	  if (tem != max)					\
	    break;						\
	  MAXT newsub = sub * type2##_RADIX;			\
	  if (newsub < sub || newsub > type1##_MAX)		\
	    sub = 0;						\
	  else							\
	    sub = newsub;					\
	}							\
      if (sub)							\
	{							\
	  MAXT newsub = ((type1##_MAX - sub) % sub);		\
	  volatile type2 tem = type1##_MAX - newsub;		\
	  volatile type2 tem2 = type1##_MAX - sub;		\
	  if (tem == tem2)					\
	    sub = newsub;					\
	  else							\
	    {							\
	      newsub += sub;					\
	      if (newsub < sub || newsub > type1##_MAX)		\
		sub = 0;					\
	      else						\
		{						\
		  tem = type1##_MAX - newsub;			\
		  if (tem == tem2)				\
		    sub = newsub;				\
		  else						\
		    sub = 0;					\
		}						\
	    }							\
	}							\
      if (sub							\
	  && cvt_##type1##_##type2 (type1##_MAX - sub)		\
	     != type1##_MAX - sub) abort ();			\
    }


#ifdef __SIZEOF_INT128__
# define TESTS128(type2) TEST (si128, type2) TEST (ui128, type2)
#else
# define TESTS128(type2)
#endif

#define TESTS(type2) \
  TEST (sc, type2) TEST (c, type2) TEST (uc, type2)	\
  TEST (ss, type2) TEST (us, type2)			\
  TEST (si, type2) TEST (ui, type2)			\
  TEST (sl, type2) TEST (ul, type2)			\
  TEST (sll, type2) TEST (ull, type2)			\
  TESTS128 (type2)

int
main ()
{
  TESTS (f)
  TESTS (d)
  TESTS (ld)
#ifdef f80_MAX
  TESTS (f80)
#endif
#ifdef f128_MAX
  TESTS (f128)
#endif
#ifdef BROKEN_DECIMAL_INT128
# undef TESTS128
# define TESTS128(type2)
# undef TWO
# undef M1U
# undef MAXS
# undef MAXT
# define TWO 2ULL
# define M1U -1ULL
# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
# define MAXT unsigned long long
#endif
#ifdef d32_MAX
  TESTS (d32)
#endif
#ifdef d64_MAX
  TESTS (d64)
#endif
#ifdef d128_MAX
  TESTS (d128)
#endif
  return 0;
}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow (take 2)
  2014-05-21 12:51                 ` Marek Polacek
  2014-05-21 14:46                   ` Jakub Jelinek
@ 2014-05-21 17:50                   ` Joseph S. Myers
  1 sibling, 0 replies; 16+ messages in thread
From: Joseph S. Myers @ 2014-05-21 17:50 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Jakub Jelinek, GCC Patches

On Wed, 21 May 2014, Marek Polacek wrote:

> On Tue, May 20, 2014 at 09:50:10PM +0000, Joseph S. Myers wrote:
> > On Tue, 20 May 2014, Marek Polacek wrote:
> > 
> > > * is missing tests for long doubles/-mlong-double-128,
> > 
> > Also missing tests for float - as far as I can see, only double is tested.  
> > Ideally all of float, double, long double, __float128 (where supported), 
> > __float80 (where supported) would be tested (the functionality supported 
> > for __fp16 (ARM) is a bit more restricted) - hopefully using some shared 
> > macros to avoid too much duplication between tests.
>  
> Ok, I've added some tests for float, long double, __float128 and
> __float80.  A snag was in __float128 type: libubsan supposedly can't
> handle __float128 values and prints "0".  libubsan also can't handle
> __float80 types with -m32 it seems.

__float80 is normally the same as long double (in terms of representation 
anyway, whether or not it's a distinct type).  On x86, it's only 
-mlong-double-128 (64-bit Bionic) that means they are different; on IA64 
it's HP-UX where they are different.  (On x86_64, typically __float80 and 
__float128 will both have the same size, although different 
representations.)

It looks to me from i386.c like __float80 is just a built-in typedef for 
long double if long double is XFmode, but __float128 is always distinct 
(always created with a separate make_node (REAL_TYPE) call) (although for 
IA64 HP-UX, __float128 looks like it is a typedef - and for IA64 in 
general, it looks like __float80 is always distinct).  I don't know if 
cleanup in this area is worthwhile at this point - FWIW, draft TS 18661-3, 
soon to go to PDTS ballot, makes its types such as _Float64x and _Float128 
all distinct from each other and from the standard types such as long 
double.

I'd hope that if a type has the same representation as one of the standard 
types, it could be handled the same in libubsan without needing any 
special support there (so you'd need new support for __float128, but not 
for __float80 when that has the same representation as long double) - just 
as libgcc just needs one set of XFmode functions and one set of TFmode 
functions even if there are multiple types with those modes.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Implement -fsanitize=float-cast-overflow (take 2)
  2014-05-21 14:46                   ` Jakub Jelinek
@ 2014-05-21 18:52                     ` Jakub Jelinek
  2014-05-22  8:06                       ` [PATCH] Fix LTO decimal ICE Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2014-05-21 18:52 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Joseph S. Myers, GCC Patches

On Wed, May 21, 2014 at 04:46:14PM +0200, Jakub Jelinek wrote:
> Here is a testcase that (IMHO, not tested with your patch) should
> test various boundary cases that shouldn't result in undefined behavior.
> I've tried to keep it portable across various architectures, assumes
> primarily two's complement and (likely) only supports binary and decimal
> floating point formats.
> Seems _Decimal{32,64,128} is not convertible
> to/from int128 right now, so that is disabled in the test for now.

Here is updated testcase + some also for the invalid values.
Compared to the previous testcase, this one I've actually tested with
Marek's patch, there were several issues.
On x86_64-linux, it mostly passes, with a few FAILs:
unix/-m32
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)
FAIL: c-c++-common/ubsan/float-cast-overflow-8.c  -O2  execution test
FAIL: c-c++-common/ubsan/float-cast-overflow-8.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  execution test
FAIL: c-c++-common/ubsan/float-cast-overflow-8.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  execution test
FAIL: c-c++-common/ubsan/float-cast-overflow-9.c  -O2  execution test
FAIL: c-c++-common/ubsan/float-cast-overflow-9.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  execution test
FAIL: c-c++-common/ubsan/float-cast-overflow-9.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  execution test
unix/-m64
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (internal compiler error)
FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)

The LTO ICEs on float-cast-overflow-{7,10}.c seems to be related to decimal support:
/usr/src/gcc/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-7.c:147:1: internal compiler error: in decimal_to_decnumber, at dfp.c:138
0x116b4cb decimal_to_decnumber
        ../../gcc/dfp.c:138
0x116b56f encode_decimal32(real_format const*, long*, real_value const*)
        ../../gcc/dfp.c:165
0x9cebb5 encode_decimal_single
        ../../gcc/real.c:4495
0x9ccb0b real_to_target_fmt(long*, real_value const*, real_format const*)
        ../../gcc/real.c:2774
0x9ccb6d real_to_target(long*, real_value const*, machine_mode)
        ../../gcc/real.c:2789
0xa7c7ff simplify_immed_subreg
        ../../gcc/simplify-rtx.c:5290
0xa7d328 simplify_subreg(machine_mode, rtx_def*, machine_mode, unsigned int)
        ../../gcc/simplify-rtx.c:5516
0xa7de2e simplify_gen_subreg(machine_mode, rtx_def*, machine_mode, unsigned int)
        ../../gcc/simplify-rtx.c:5721
The execution test FAILs for -m32 are:
==4494==Sanitizer CHECK failed: ../../../../../libsanitizer/ubsan/ubsan_value.cc:98 ((0 && "unexpected floating point bit width")) != (0) (0, 0)
supposedly on the first __float80 or long double message (on x86_64-linux __float80 seems to work, but
__float128 randomly crashes the testcase in some invocations, in others it finishes, therefore
I haven't enabled it at all right now in float-cast-overflow-9.c).
We are passing 96 for long double and __float80 bitsize on x86_64 -m32, while
libubsan handles only 80 and 128.  Can you talk to upstream what the bitsize is meant to actually mean,
and/or check what clang/llvm provide in those cases?

--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-7.c.jj	2014-05-21 19:29:41.199209108 +0200
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-7.c	2014-05-21 20:31:01.949441320 +0200
@@ -0,0 +1,196 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -fno-sanitize-recover" } */
+/* FIXME: When _DecimalXX <-> {signed, unsigned} __int128 conversions are
+   supported, -DBROKEN_DECIMAL_INT128 can be removed.  */
+/* { dg-additional-options "-DUSE_DFP -DBROKEN_DECIMAL_INT128" { target dfp } } */
+
+#define USE_FLT_DBL_LDBL
+#ifdef __SIZEOF_INT128__
+#define USE_INT128
+#endif
+#ifdef __SIZEOF_FLOAT80__
+#define USE_FLOAT80
+#endif
+#ifdef __SIZEOF_FLOAT128__
+#define USE_FLOAT128
+#endif
+
+#include "float-cast-overflow-7.h"
+
+#define TEST(type1, type2) \
+  if (cvt_##type1##_##type2 (-0.5f) != 0) abort ();		\
+  if (cvt_##type1##_##type2 (0.5f) != 0) abort ();		\
+  if (cvt_##type1##_##type2 (-0.75f) != 0) abort ();		\
+  if (cvt_##type1##_##type2 (0.75f) != 0) abort ();		\
+  if (type1##_MIN)						\
+    {								\
+      /* For RADIX 2 type1##_MIN should be always */		\
+      /* exactly representable in type2.  */			\
+      if (type2##_RADIX == 2					\
+	  || type1##_MAX <= type2##_MAX)			\
+	{							\
+	  if (cvt_##type1##_##type2 (type1##_MIN)		\
+	      != type1##_MIN) abort ();				\
+	  volatile type2 tem = ((type2) -0.75f) + type1##_MIN;	\
+	  volatile type2 tem2 = ((type2) -1.0f) + type1##_MIN;	\
+	  if (tem != tem2					\
+	      && cvt_##type1##_##type2 ((type2) -0.75f		\
+					+ type1##_MIN)		\
+		 != type1##_MIN) abort ();			\
+	}							\
+      else							\
+	{							\
+	  type2 min = type1##_MIN;				\
+	  /* tem could be below minimum here due to */		\
+	  /* rounding.  */					\
+	  MAXT add = 1;						\
+	  while (add)						\
+	    {							\
+	      volatile type2 tem = type1##_MIN + (type1) add;	\
+	      if (tem != min)					\
+		break;						\
+	      MAXT newadd = add * type2##_RADIX;		\
+	      if (newadd < add || newadd > type1##_MAX)		\
+		add = 0;					\
+	      else						\
+		add = newadd;					\
+	    }							\
+	  if (add)						\
+	    {							\
+	      MAXT newadd					\
+		= (-(type1##_MIN + (type1) add)) % add;		\
+	      volatile type2 tem = type1##_MIN + (type1) newadd;\
+	      volatile type2 tem2 = type1##_MIN + (type1) add;	\
+	      if (tem == tem2)					\
+		add = newadd;					\
+	      else						\
+		{						\
+		  newadd += add;				\
+		  if (newadd < add || newadd > type1##_MAX)	\
+		    add = 0;					\
+		  else						\
+		    {						\
+		      tem = type1##_MIN + (type1) newadd;	\
+		      if (tem == tem2)				\
+			add = newadd;				\
+		      else					\
+			add = 0;				\
+		    }						\
+		}						\
+	    }							\
+	  if (add						\
+	      && cvt_##type1##_##type2 (type1##_MIN		\
+					+ (type1) add)		\
+		 != type1##_MIN + (type1) add) abort ();	\
+	}							\
+    }								\
+  if (type1##_MAX <= type2##_MAX)				\
+    {								\
+      if (cvt_##type1##_##type2 (type1##_MAX) != type1##_MAX)	\
+	abort ();						\
+      volatile type2 tem = ((type2) 0.75f) + type1##_MAX;	\
+      volatile type2 tem2 = ((type2) 1.0f) + type1##_MAX;	\
+      if (tem < tem2						\
+	  && cvt_##type1##_##type2 ((type2) 0.75f + type1##_MAX)\
+	     != type1##_MAX) abort ();				\
+    }								\
+  else								\
+    {								\
+      type2 max = type1##_MAX;					\
+      /* tem could be above maximum here due to rounding.  */	\
+      MAXT sub = 1;						\
+      while (sub)						\
+	{							\
+	  volatile type2 tem = type1##_MAX - sub;		\
+	  if (tem != max)					\
+	    break;						\
+	  MAXT newsub = sub * type2##_RADIX;			\
+	  if (newsub < sub || newsub > type1##_MAX)		\
+	    sub = 0;						\
+	  else							\
+	    sub = newsub;					\
+	}							\
+      if (sub)							\
+	{							\
+	  MAXT newsub = ((type1##_MAX - sub) % sub);		\
+	  volatile type2 tem = type1##_MAX - newsub;		\
+	  volatile type2 tem2 = type1##_MAX - sub;		\
+	  if (tem == tem2)					\
+	    sub = newsub;					\
+	  else							\
+	    {							\
+	      newsub += sub;					\
+	      if (newsub < sub || newsub > type1##_MAX)		\
+		sub = 0;					\
+	      else						\
+		{						\
+		  tem = type1##_MAX - newsub;			\
+		  if (tem == tem2)				\
+		    sub = newsub;				\
+		  else						\
+		    sub = 0;					\
+		}						\
+	    }							\
+	}							\
+      if (sub							\
+	  && cvt_##type1##_##type2 (type1##_MAX - sub)		\
+	     != type1##_MAX - sub) abort ();			\
+    }
+
+
+#ifdef si128_MAX
+# define TESTS128(type2) TEST (si128, type2) TEST (ui128, type2)
+#else
+# define TESTS128(type2)
+#endif
+
+#define TESTS(type2) \
+  TEST (sc, type2) TEST (c, type2) TEST (uc, type2)	\
+  TEST (ss, type2) TEST (us, type2)			\
+  TEST (si, type2) TEST (ui, type2)			\
+  TEST (sl, type2) TEST (ul, type2)			\
+  TEST (sll, type2) TEST (ull, type2)			\
+  TESTS128 (type2)
+
+int
+main ()
+{
+#ifdef f_MAX
+  TESTS (f)
+#endif
+#ifdef d_MAX
+  TESTS (d)
+#endif
+#ifdef ld_MAX
+  TESTS (ld)
+#endif
+#ifdef f80_MAX
+  TESTS (f80)
+#endif
+#ifdef f128_MAX
+  TESTS (f128)
+#endif
+#ifdef BROKEN_DECIMAL_INT128
+# undef TESTS128
+# define TESTS128(type2)
+# undef TWO
+# undef M1U
+# undef MAXS
+# undef MAXT
+# define TWO 2ULL
+# define M1U -1ULL
+# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
+# define MAXT unsigned long long
+#endif
+#ifdef d32_MAX
+  TESTS (d32)
+#endif
+#ifdef d64_MAX
+  TESTS (d64)
+#endif
+#ifdef d128_MAX
+  TESTS (d128)
+#endif
+  return 0;
+}
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-7.h.jj	2014-05-21 20:28:32.637160646 +0200
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-7.h	2014-05-21 20:32:59.647867656 +0200
@@ -0,0 +1,151 @@
+#define CVTFN(type1, type2) \
+__attribute__((noinline)) type1	\
+cvt_##type1##_##type2 (type2 x)	\
+{				\
+  return x;			\
+}
+
+typedef signed char sc;
+#define sc_MIN (-__SCHAR_MAX__ - 1)
+#define sc_MAX __SCHAR_MAX__
+typedef unsigned char uc;
+#define uc_MIN 0
+#define uc_MAX (2U * __SCHAR_MAX__ + 1U)
+typedef char c;
+#define c_MIN ((((char) -1) > (char) 0) ? uc_MIN : sc_MIN)
+#define c_MAX ((((char) -1) > (char) 0) ? uc_MAX : sc_MAX)
+typedef signed short ss;
+#define ss_MIN (-__SHRT_MAX__ - 1)
+#define ss_MAX __SHRT_MAX__
+typedef unsigned short us;
+#define us_MIN 0
+#define us_MAX (2U * __SHRT_MAX__ + 1U)
+typedef signed int si;
+#define si_MIN (-__INT_MAX__ - 1)
+#define si_MAX __INT_MAX__
+typedef unsigned int ui;
+#define ui_MIN 0
+#define ui_MAX (2U * __INT_MAX__ + 1U)
+typedef signed long sl;
+#define sl_MIN (-__LONG_MAX__ - 1L)
+#define sl_MAX __LONG_MAX__
+typedef unsigned long ul;
+#define ul_MIN 0L
+#define ul_MAX (2UL * __LONG_MAX__ + 1UL)
+typedef signed long long sll;
+#define sll_MIN (-__LONG_LONG_MAX__ - 1LL)
+#define sll_MAX __LONG_LONG_MAX__
+typedef unsigned long long ull;
+#define ull_MIN 0LL
+#define ull_MAX (2ULL * __LONG_LONG_MAX__ + 1ULL)
+#ifdef USE_INT128
+typedef signed __int128 si128;
+# define si128_MAX \
+  ((signed __int128) ((((unsigned __int128) 1) \
+		       << (__CHAR_BIT__ * __SIZEOF_INT128__ - 1)) - 1))
+# define si128_MIN (-si128_MAX - 1)
+typedef unsigned __int128 ui128;
+#define ui128_MIN ((unsigned __int128) 0)
+#define ui128_MAX (((unsigned __int128) 2) * si128_MAX + 1)
+#endif
+
+#ifdef si128_MAX
+# define CVTS128(type2) CVTFN (si128, type2) CVTFN (ui128, type2)
+#else
+# define CVTS128(type2)
+#endif
+
+#define CVTS(type2) \
+  CVTFN (sc, type2) CVTFN (c, type2) CVTFN (uc, type2)	\
+  CVTFN (ss, type2) CVTFN (us, type2)			\
+  CVTFN (si, type2) CVTFN (ui, type2)			\
+  CVTFN (sl, type2) CVTFN (ul, type2)			\
+  CVTFN (sll, type2) CVTFN (ull, type2)			\
+  CVTS128 (type2)
+
+#ifdef __SIZEOF_INT128__
+# define TWO ((unsigned __int128) 2)
+# define M1U ((unsigned __int128) -1)
+# define MAXS (__CHAR_BIT__ * __SIZEOF_INT128__)
+# define MAXT unsigned __int128
+#else
+# define TWO 2ULL
+# define M1U -1ULL
+# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
+# define MAXT unsigned long long
+#endif
+
+#ifdef USE_FLT_DBL_LDBL
+typedef float f;
+#define f_RADIX 2
+#define f_MANT_DIG __FLT_MANT_DIG__
+#define f_MAX ((TWO << (f_MANT_DIG - 1)) - 1)
+typedef double d;
+#define d_RADIX 2
+#define d_MANT_DIG __DBL_MANT_DIG__
+#define d_MAX ((TWO << (d_MANT_DIG - 1)) - 1)
+typedef long double ld;
+#define ld_RADIX 2
+#define ld_MANT_DIG __LDBL_MANT_DIG__
+#define ld_MAX \
+  (ld_MANT_DIG > MAXS ? M1U : (TWO << (ld_MANT_DIG > MAXS \
+				       ? 0 : ld_MANT_DIG - 1)) - 1)
+CVTS (f)
+CVTS (d)
+CVTS (ld)
+#endif
+#ifdef USE_FLOAT80
+typedef __float80 f80;
+# define f80_RADIX 2
+# define f80_MANT_DIG 64
+# define f80_MAX ((TWO << (f80_MANT_DIG - 1)) - 1)
+CVTS (f80)
+#endif
+#ifdef USE_FLOAT128
+typedef __float128 f128;
+# define f128_RADIX 2
+# define f128_MANT_DIG 113
+# define f128_MAX \
+  (f128_MANT_DIG > MAXS ? M1U : (TWO << (f128_MANT_DIG > MAXS \
+					 ? 0 : f128_MANT_DIG - 1)) - 1)
+CVTS (f128)
+#endif
+#ifdef USE_DFP
+typedef _Decimal32 d32;
+# define d32_RADIX 10
+# define d32_MANT_DIG __DEC32_MANT_DIG__
+# if d32_MANT_DIG == 7
+#  define d32_MAX 9999999ULL
+# endif
+typedef _Decimal64 d64;
+# define d64_RADIX 10
+# define d64_MANT_DIG __DEC64_MANT_DIG__
+# if d64_MANT_DIG == 16
+#  define d64_MAX 9999999999999999ULL
+# endif
+typedef _Decimal128 d128;
+# define d128_RADIX 10
+# define d128_MANT_DIG __DEC128_MANT_DIG__
+# if d128_MANT_DIG == 34
+#  ifdef __SIZEOF_INT128__
+/* #define d128_MAX 0x1ed09bead87c0378d8e63ffffffff */
+#   define d128_MAX \
+  ((((unsigned __int128) 0x1ed09bead87c0) << 64) + 0x378d8e63ffffffffULL)
+#  else
+#   define d128_MAX M1U
+#  endif
+# endif
+# ifdef BROKEN_DECIMAL_INT128
+#  undef CVTS128
+#  define CVTS128(type2)
+# endif
+CVTS (d32)
+CVTS (d64)
+CVTS (d128)
+#endif
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void abort ();
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-8.c.jj	2014-05-21 19:29:43.971196452 +0200
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-8.c	2014-05-21 20:36:22.833896108 +0200
@@ -0,0 +1,206 @@
+/* { dg-do run } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -fsanitize-recover -DUSE_FLT_DBL_LDBL" } */
+/* { dg-additional-options "-DUSE_INT128" { target int128 } } */
+
+#include "float-cast-overflow-7.h"
+
+#define TEST(type1, type2) \
+  if (type1##_MIN)						\
+    {								\
+      type2 min = type1##_MIN;					\
+      type2 add = -1.0;						\
+      while (1)							\
+	{							\
+	  volatile type2 tem = min + add;			\
+	  if (tem != min)					\
+	    {							\
+	      volatile type1 tem3 = cvt_##type1##_##type2 (tem);\
+	      break;						\
+	    }							\
+	  add = add * type2##_RADIX;				\
+	  if (min == add)					\
+	    break;						\
+	}							\
+      volatile type1 tem3 = cvt_##type1##_##type2 (min);	\
+    }								\
+  else								\
+    {								\
+      volatile type1 tem3 = cvt_##type1##_##type2 (-1.0f);	\
+    }								\
+  {								\
+    type2 max = type1##_MAX;					\
+    type2 add = 1.0;						\
+    while (1)							\
+      {								\
+	volatile type2 tem = max + add;				\
+	if (tem != max)						\
+	  {							\
+	    volatile type1 tem3 = cvt_##type1##_##type2 (tem);	\
+	    break;						\
+	  }							\
+	add = add * type2##_RADIX;				\
+	if (max == add)						\
+	  break;						\
+      }								\
+  }
+
+#ifdef si128_MAX
+# define TESTS128(type2) TEST (si128, type2) TEST (ui128, type2)
+#else
+# define TESTS128(type2)
+#endif
+
+#define TESTS(type2) \
+  TEST (sc, type2) TEST (c, type2) TEST (uc, type2)	\
+  TEST (ss, type2) TEST (us, type2)			\
+  TEST (si, type2) TEST (ui, type2)			\
+  TEST (sl, type2) TEST (ul, type2)			\
+  TEST (sll, type2) TEST (ull, type2)			\
+  TESTS128 (type2)
+
+int
+main ()
+{
+#ifdef f_MAX
+  TESTS (f)
+#endif
+#ifdef d_MAX
+  TESTS (d)
+#endif
+#ifdef ld_MAX
+  TESTS (ld)
+#endif
+#ifdef f80_MAX
+  TESTS (f80)
+#endif
+#ifdef f128_MAX
+  TESTS (f128)
+#endif
+#ifdef BROKEN_DECIMAL_INT128
+# undef TESTS128
+# define TESTS128(type2)
+# undef TWO
+# undef M1U
+# undef MAXS
+# define TWO 2ULL
+# define M1U -1ULL
+# define MAXS (__CHAR_BIT__ * __SIZEOF_LONG_LONG__)
+#endif
+#ifdef d32_MAX
+  TESTS (d32)
+#endif
+#ifdef d64_MAX
+  TESTS (d64)
+#endif
+#ifdef d128_MAX
+  TESTS (d128)
+#endif
+  return 0;
+}
+
+/* float */
+/* { dg-output "value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "value \[0-9.e+-]* is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value (128|256) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* No error for float and __int128 unsigned max value, as ui128_MAX is +Inf in float.  */
+/* double */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value (128|256) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* long double */
+/* { dg-output "\[^\n\r]*value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value (128|256) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" { target { ilp32 || lp64 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" { target { ! { ilp32 || lp64 } } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target { int128 } } } */
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-9.c.jj	2014-05-21 20:37:17.090638364 +0200
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-9.c	2014-05-21 20:37:13.084663434 +0200
@@ -0,0 +1,34 @@
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -fsanitize-recover -DUSE_FLOAT80 -UUSE_FLOAT128" } */
+/* { dg-additional-options "-DUSE_INT128" { target int128 } } */
+
+#include "float-cast-overflow-8.c"
+
+/* __float80 */
+/* { dg-output "value -129 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 128 is outside the range of representable values of type 'signed char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value (-129|-1) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value (128|256) is outside the range of representable values of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 256 is outside the range of representable values of type 'unsigned char'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -32769 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 32768 is outside the range of representable values of type 'short int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value 65536 is outside the range of representable values of type 'short unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type 'long long unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
+/* { dg-output "\[^\n\r]*value -1 is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
+/* { dg-output "\[^\n\r]*value \[0-9.e+-]* is outside the range of representable values of type '__int128 unsigned'\[^\n\r]*(\n|\r\n|\r)" { target int128 } } */
--- gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-10.c.jj	2014-05-21 20:37:36.863534281 +0200
+++ gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-10.c	2014-05-21 20:38:37.923238252 +0200
@@ -0,0 +1,8 @@
+/* { dg-do run { target dfp } } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O2" } } */
+/* { dg-options "-fsanitize=float-cast-overflow -fsanitize-recover" } */
+/* FIXME: When _DecimalXX <-> {signed, unsigned} __int128 conversions are
+   supported, -DBROKEN_DECIMAL_INT128 can be removed.  */
+/* { dg-additional-options "-DUSE_DFP -DBROKEN_DECIMAL_INT128" } */
+
+#include "float-cast-overflow-8.c"


	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH] Fix LTO decimal ICE
  2014-05-21 18:52                     ` Jakub Jelinek
@ 2014-05-22  8:06                       ` Jakub Jelinek
  0 siblings, 0 replies; 16+ messages in thread
From: Jakub Jelinek @ 2014-05-22  8:06 UTC (permalink / raw)
  To: Marek Polacek; +Cc: Joseph S. Myers, GCC Patches

On Wed, May 21, 2014 at 08:52:03PM +0200, Jakub Jelinek wrote:
> FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (internal compiler error)
> FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
> FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (internal compiler error)
> FAIL: c-c++-common/ubsan/float-cast-overflow-10.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)
> FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (internal compiler error)
> FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fno-use-linker-plugin -flto-partition=none  (test for excess errors)
> FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (internal compiler error)
> FAIL: c-c++-common/ubsan/float-cast-overflow-7.c  -O2 -flto -fuse-linker-plugin -fno-fat-lto-objects  (test for excess errors)
> 
> The LTO ICEs on float-cast-overflow-{7,10}.c seems to be related to decimal support:
> /usr/src/gcc/gcc/testsuite/c-c++-common/ubsan/float-cast-overflow-7.c:147:1: internal compiler error: in decimal_to_decnumber, at dfp.c:138
> 0x116b4cb decimal_to_decnumber
>         ../../gcc/dfp.c:138
...

This bug is because we leave garbage in REAL_VALUE_TYPE padding bits, but
then use memcmp on REAL_VALUE_TYPE objects.  All other places use memset
first to clear the padding bits, so I've committed this fix as obvious
to trunk and 4.9 (without testcase, because I couldn't reproduce it on
anything smaller than float-cast-overflow-{7,10}.c and those require
further gcc patches.

> The execution test FAILs for -m32 are:
> ==4494==Sanitizer CHECK failed: ../../../../../libsanitizer/ubsan/ubsan_value.cc:98 ((0 && "unexpected floating point bit width")) != (0) (0, 0)

This one can be fixed by handling 96 the same as 80 and 128, apparently even 
clang/llvm uses 96 on i?86 and crashes in libubsan the same way.  Marek, can
you please handle the LLVM bureaucracy?

2014-05-22  Jakub Jelinek  <jakub@redhat.com>

	* tree-streamer-in.c (unpack_ts_real_cst_value_fields): Make sure
	all padding bits in REAL_VALUE_TYPE are cleared.

--- gcc/tree-streamer-in.c.jj	2014-05-20 16:37:05.000000000 +0200
+++ gcc/tree-streamer-in.c	2014-05-22 09:40:01.300112220 +0200
@@ -168,6 +168,9 @@ unpack_ts_real_cst_value_fields (struct
   REAL_VALUE_TYPE r;
   REAL_VALUE_TYPE *rp;
 
+  /* Clear all bits of the real value type so that we can later do
+     bitwise comparisons to see if two values are the same.  */
+  memset (&r, 0, sizeof r);
   r.cl = (unsigned) bp_unpack_value (bp, 2);
   r.decimal = (unsigned) bp_unpack_value (bp, 1);
   r.sign = (unsigned) bp_unpack_value (bp, 1);


	Jakub

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2014-05-22  8:06 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-13 17:08 [PATCH] Implement -fsanitize=float-cast-overflow Marek Polacek
2014-05-13 17:38 ` Jakub Jelinek
2014-05-13 18:11 ` Joseph S. Myers
2014-05-14 11:38   ` Jakub Jelinek
2014-05-14 17:37     ` Joseph S. Myers
2014-05-15 19:09       ` Jakub Jelinek
2014-05-15 21:29         ` Joseph S. Myers
2014-05-16  7:37           ` Jakub Jelinek
2014-05-16 15:54             ` Joseph S. Myers
2014-05-20 20:33             ` [PATCH] Implement -fsanitize=float-cast-overflow (take 2) Marek Polacek
2014-05-20 21:50               ` Joseph S. Myers
2014-05-21 12:51                 ` Marek Polacek
2014-05-21 14:46                   ` Jakub Jelinek
2014-05-21 18:52                     ` Jakub Jelinek
2014-05-22  8:06                       ` [PATCH] Fix LTO decimal ICE Jakub Jelinek
2014-05-21 17:50                   ` [PATCH] Implement -fsanitize=float-cast-overflow (take 2) Joseph S. Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).