[PATCH] integer overflow checking builtins in constant expressions

Jakub Jelinek jakub@redhat.com
Mon Jun 6 12:36:00 GMT 2016


On Fri, Jun 03, 2016 at 02:09:44PM -0600, Martin Sebor wrote:
> I see.  I've made the change in the latest update to the patch
> but I wasn't able to create a test case to verify it.  Maybe
> that's because this is constexpr the COMPLEX_EXPR doesn't make
> it far enough to trigger a problem.  If there is a way to test
> it I'd appreciate a suggestion for how (otherwise, if not caught
> in a code review like in this case, it would be a ticking time
> bomb).

As you haven't been willing to try the __builtin_*_overflow_p
way, I've done that myself, it hasn't been really that hard.

I've also found another bug, when computing the value that is stored
in the constexpr function, you weren't converting the INTEGER_CST
args to type, so you could get incorrect type of the result or
other issues.

> It also occurred to me that a more robust solution might be to
> change build_complex to either enforce as a precondition that
> the members have a type that matches the complex type.  I've
> taken the liberty of making this change as part of this patch.

IMHO it doesn't belong to this patch, feel free to submit the tree.c
change separately.

> (It seems that an even better solution would be to have
> build_complex convert the arguments to the expected type
> so that callers don't have to worry about it.)

I diagree with that, build_complex is a low-level function, it shouldn't
perform such conversions.

2016-06-06  Martin Sebor  <msebor@redhat.com>
	    Jakub Jelinek  <jakub@redhat.com>

	PR c++/70507
	PR c/68120
	* builtins.def (BUILT_IN_ADD_OVERFLOW_P, BUILT_IN_SUB_OVERFLOW_P,
	BUILT_IN_MUL_OVERFLOW_P): New builtins.
	* builtins.c: Include gimple-fold.h.
	(fold_builtin_arith_overflow): Handle
	BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
	(fold_builtin_3): Likewise.
	* doc/extend.texi (Integer Overflow Builtins): Document
	__builtin_{add,sub,mul}_overflow_p.
gcc/c/
	* c-typeck.c (convert_arguments): Don't promote last argument
	of BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/cp/
	* constexpr.c: Include gimple-fold.h.
	(cxx_eval_internal_function): New function.
	(cxx_eval_call_expression): Call it.
	(potential_constant_expression_1): Handle integer arithmetic
	overflow built-ins.
	* tree.c (builtin_valid_in_constant_expr_p): Likewise.
gcc/c-family/
	* c-common.c (check_builtin_function_arguments): Handle
	BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/testsuite/
	* c-c++-common/builtin-arith-overflow-1.c: Add test cases.
	* c-c++-common/builtin-arith-overflow-2.c: New test.
	* g++.dg/cpp0x/constexpr-arith-overflow.C: New test.
	* g++.dg/cpp1y/constexpr-arith-overflow.C: New test.

--- gcc/builtins.c.jj	2016-06-03 21:25:16.595678286 +0200
+++ gcc/builtins.c	2016-06-06 11:48:06.800100603 +0200
@@ -64,6 +64,7 @@ along with GCC; see the file COPYING3.
 #include "rtl-chkp.h"
 #include "internal-fn.h"
 #include "case-cfn-macros.h"
+#include "gimple-fold.h"
 
 
 struct target_builtins default_target_builtins;
@@ -7943,18 +7944,28 @@ fold_builtin_unordered_cmp (location_t l
 /* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
    arithmetics if it can never overflow, or into internal functions that
    return both result of arithmetics and overflowed boolean flag in
-   a complex integer result, or some other check for overflow.  */
+   a complex integer result, or some other check for overflow.
+   Similarly fold __builtin_{add,sub,mul}_overflow_p to just the overflow
+   checking part of that.  */
 
 static tree
 fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
 			     tree arg0, tree arg1, tree arg2)
 {
   enum internal_fn ifn = IFN_LAST;
-  tree type = TREE_TYPE (TREE_TYPE (arg2));
-  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
+  /* The code of the expression corresponding to the type-generic
+     built-in, or ERROR_MARK for the type-specific ones.  */
+  enum tree_code opcode = ERROR_MARK;
+  bool ovf_only = false;
+
   switch (fcode)
     {
+    case BUILT_IN_ADD_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_ADD_OVERFLOW:
+      opcode = PLUS_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
@@ -7963,7 +7974,12 @@ fold_builtin_arith_overflow (location_t
     case BUILT_IN_UADDLL_OVERFLOW:
       ifn = IFN_ADD_OVERFLOW;
       break;
+    case BUILT_IN_SUB_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_SUB_OVERFLOW:
+      opcode = MINUS_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SSUB_OVERFLOW:
     case BUILT_IN_SSUBL_OVERFLOW:
     case BUILT_IN_SSUBLL_OVERFLOW:
@@ -7972,7 +7988,12 @@ fold_builtin_arith_overflow (location_t
     case BUILT_IN_USUBLL_OVERFLOW:
       ifn = IFN_SUB_OVERFLOW;
       break;
+    case BUILT_IN_MUL_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_MUL_OVERFLOW:
+      opcode = MULT_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SMUL_OVERFLOW:
     case BUILT_IN_SMULL_OVERFLOW:
     case BUILT_IN_SMULLL_OVERFLOW:
@@ -7984,6 +8005,25 @@ fold_builtin_arith_overflow (location_t
     default:
       gcc_unreachable ();
     }
+
+  /* For the "generic" overloads, the first two arguments can have different
+     types and the last argument determines the target type to use to check
+     for overflow.  The arguments of the other overloads all have the same
+     type.  */
+  tree type = ovf_only ? TREE_TYPE (arg2) : TREE_TYPE (TREE_TYPE (arg2));
+
+  /* For the __builtin_{add,sub,mul}_overflow_p builtins, when the first two
+     arguments are constant, attempt to fold the built-in call into a constant
+     expression indicating whether or not it detected an overflow.  */
+  if (ovf_only
+      && TREE_CODE (arg0) == INTEGER_CST
+      && TREE_CODE (arg1) == INTEGER_CST)
+    /* Perform the computation in the target type and check for overflow.  */
+    return omit_one_operand_loc (loc, boolean_type_node,
+				 arith_overflowed_p (opcode, type, arg0, arg1)
+				 ? boolean_true_node : boolean_false_node,
+				 arg2);
+
   tree ctype = build_complex_type (type);
   tree call = build_call_expr_internal_loc (loc, ifn, ctype,
 					    2, arg0, arg1);
@@ -7991,6 +8031,11 @@ fold_builtin_arith_overflow (location_t
   tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
   tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
   ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+
+  if (ovf_only)
+    return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
+
+  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
   tree store
     = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres);
   return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres);
@@ -8340,6 +8385,9 @@ fold_builtin_3 (location_t loc, tree fnd
     case BUILT_IN_ADD_OVERFLOW:
     case BUILT_IN_SUB_OVERFLOW:
     case BUILT_IN_MUL_OVERFLOW:
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
--- gcc/cp/constexpr.c.jj	2016-05-24 10:56:00.972164643 +0200
+++ gcc/cp/constexpr.c	2016-06-06 12:07:45.551788040 +0200
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.
 #include "builtins.h"
 #include "tree-inline.h"
 #include "ubsan.h"
+#include "gimple-fold.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)						\
@@ -1255,6 +1256,69 @@ cx_error_context (void)
   return r;
 }
 
+/* Evaluate a call T to a GCC internal function when possible and return
+   the evaluated result or, under the control of CTX, give an error, set
+   NON_CONSTANT_P, and return the unevaluated call T otherwise.  */
+
+static tree
+cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
+			    bool lval,
+			    bool *non_constant_p, bool *overflow_p)
+{
+  enum tree_code opcode = ERROR_MARK;
+
+  switch (CALL_EXPR_IFN (t))
+    {
+    case IFN_UBSAN_NULL:
+    case IFN_UBSAN_BOUNDS:
+    case IFN_UBSAN_VPTR:
+      return void_node;
+
+    case IFN_ADD_OVERFLOW:
+      opcode = PLUS_EXPR;
+      break;
+    case IFN_SUB_OVERFLOW:
+      opcode = MINUS_EXPR;
+      break;
+    case IFN_MUL_OVERFLOW:
+      opcode = MULT_EXPR;
+      break;
+
+    default:
+      if (!ctx->quiet)
+	error_at (EXPR_LOC_OR_LOC (t, input_location),
+		  "call to internal function %qE", t);
+      *non_constant_p = true;
+      return t;
+    }
+
+  /* Evaluate constant arguments using OPCODE and return a complex
+     number containing the result and the overflow bit.  */
+  tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval,
+					    non_constant_p, overflow_p);
+  tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval,
+					    non_constant_p, overflow_p);
+
+  if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      location_t loc = EXPR_LOC_OR_LOC (t, input_location);
+      tree type = TREE_TYPE (TREE_TYPE (t));
+      tree result = fold_binary_loc (loc, opcode, type,
+				     fold_convert_loc (loc, type, arg0),
+				     fold_convert_loc (loc, type, arg1));
+      tree ovf
+	= build_int_cst (type, arith_overflowed_p (opcode, type, arg0, arg1));
+      /* Reset TREE_OVERFLOW to avoid warnings for the overflow.  */
+      if (TREE_OVERFLOW (result))
+	TREE_OVERFLOW (result) = 0;
+
+      return build_complex (TREE_TYPE (t), result, ovf);
+    }
+
+  *non_constant_p = true;
+  return t;
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Evaluate the call expression tree T in the context of OLD_CALL expression
    evaluation.  */
@@ -1270,18 +1334,8 @@ cxx_eval_call_expression (const constexp
   bool depth_ok;
 
   if (fun == NULL_TREE)
-    switch (CALL_EXPR_IFN (t))
-      {
-      case IFN_UBSAN_NULL:
-      case IFN_UBSAN_BOUNDS:
-      case IFN_UBSAN_VPTR:
-	return void_node;
-      default:
-	if (!ctx->quiet)
-	  error_at (loc, "call to internal function");
-	*non_constant_p = true;
-	return t;
-      }
+    return cxx_eval_internal_function (ctx, t, lval,
+				       non_constant_p, overflow_p);
 
   if (TREE_CODE (fun) != FUNCTION_DECL)
     {
@@ -4588,6 +4642,10 @@ potential_constant_expression_1 (tree t,
 
 	if (fun == NULL_TREE)
 	  {
+	    /* Reset to allow the function to continue past the end
+	       of the block below.  Otherwise return early.  */
+	    bool bail = true;
+
 	    if (TREE_CODE (t) == CALL_EXPR
 		&& CALL_EXPR_FN (t) == NULL_TREE)
 	      switch (CALL_EXPR_IFN (t))
@@ -4598,16 +4656,27 @@ potential_constant_expression_1 (tree t,
 		case IFN_UBSAN_BOUNDS:
 		case IFN_UBSAN_VPTR:
 		  return true;
+
+		case IFN_ADD_OVERFLOW:
+		case IFN_SUB_OVERFLOW:
+		case IFN_MUL_OVERFLOW:
+		  bail = false;
+
 		default:
 		  break;
 		}
-	    /* fold_call_expr can't do anything with IFN calls.  */
-	    if (flags & tf_error)
-	      error_at (EXPR_LOC_OR_LOC (t, input_location),
-			"call to internal function");
-	    return false;
+
+	    if (bail)
+	      {
+		/* fold_call_expr can't do anything with IFN calls.  */
+		if (flags & tf_error)
+		  error_at (EXPR_LOC_OR_LOC (t, input_location),
+			    "call to internal function %qE", t);
+		return false;
+	      }
 	  }
-	if (is_overloaded_fn (fun))
+
+	if (fun && is_overloaded_fn (fun))
 	  {
 	    if (TREE_CODE (fun) == FUNCTION_DECL)
 	      {
@@ -4652,7 +4721,7 @@ potential_constant_expression_1 (tree t,
 	      i = num_artificial_parms_for (fun);
 	    fun = DECL_ORIGIN (fun);
 	  }
-	else
+	else if (fun)
           {
 	    if (RECUR (fun, rval))
 	      /* Might end up being a constant function pointer.  */;
--- gcc/cp/tree.c.jj	2016-05-13 22:23:03.024721307 +0200
+++ gcc/cp/tree.c	2016-06-06 11:54:23.675203508 +0200
@@ -352,6 +352,35 @@ builtin_valid_in_constant_expr_p (const_
     case BUILT_IN_FUNCTION:
     case BUILT_IN_LINE:
 
+      /* The following built-ins are valid in constant expressions
+	 when their arguments are.  */
+    case BUILT_IN_ADD_OVERFLOW:
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SADD_OVERFLOW:
+    case BUILT_IN_SADDL_OVERFLOW:
+    case BUILT_IN_SADDLL_OVERFLOW:
+    case BUILT_IN_UADD_OVERFLOW:
+    case BUILT_IN_UADDL_OVERFLOW:
+    case BUILT_IN_UADDLL_OVERFLOW:
+
+    case BUILT_IN_SUB_OVERFLOW:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_SSUB_OVERFLOW:
+    case BUILT_IN_SSUBL_OVERFLOW:
+    case BUILT_IN_SSUBLL_OVERFLOW:
+    case BUILT_IN_USUB_OVERFLOW:
+    case BUILT_IN_USUBL_OVERFLOW:
+    case BUILT_IN_USUBLL_OVERFLOW:
+
+    case BUILT_IN_MUL_OVERFLOW:
+    case BUILT_IN_MUL_OVERFLOW_P:
+    case BUILT_IN_SMUL_OVERFLOW:
+    case BUILT_IN_SMULL_OVERFLOW:
+    case BUILT_IN_SMULLL_OVERFLOW:
+    case BUILT_IN_UMUL_OVERFLOW:
+    case BUILT_IN_UMULL_OVERFLOW:
+    case BUILT_IN_UMULLL_OVERFLOW:
+
       /* These have constant results even if their operands are
 	 non-constant.  */
     case BUILT_IN_CONSTANT_P:
--- gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c.jj	2014-11-12 13:28:47.188688801 +0100
+++ gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c	2016-06-06 12:47:18.567054514 +0200
@@ -6,6 +6,9 @@ f1 (void)
   int x = __builtin_add_overflow ();	/* { dg-error "not enough arguments to function" } */
   x += __builtin_sub_overflow ();	/* { dg-error "not enough arguments to function" } */
   x += __builtin_mul_overflow ();	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_add_overflow_p ();	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow_p ();	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow_p ();	/* { dg-error "not enough arguments to function" } */
   return x;
 }
 
@@ -15,6 +18,10 @@ f2 (int a, int b, int *c, int d)
   int x = __builtin_add_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
   x += __builtin_sub_overflow (a, b, c, d, d, d);	/* { dg-error "too many arguments to function" } */
   x += __builtin_mul_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_add_overflow_p (a, b, d, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_sub_overflow_p (a, b, d, d, 1, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_mul_overflow_p (a, b, d, d);	/* { dg-error "too many arguments to function" } */
+  
   return x;
 }
 
@@ -33,6 +40,15 @@ f3 (float fa, int a, _Complex long int c
   x += __builtin_add_overflow (a, pb, c);	/* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
   x += __builtin_sub_overflow (a, eb, c);
   x += __builtin_mul_overflow (a, bb, c);
+  x += __builtin_add_overflow_p (fa, b, a);	/* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
+  x += __builtin_sub_overflow_p (ca, b, eb);	/* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
+  x += __builtin_mul_overflow_p (a, fb, bb);	/* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
+  x += __builtin_add_overflow_p (a, pb, a);	/* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
+  x += __builtin_sub_overflow_p (a, eb, eb);
+  x += __builtin_mul_overflow_p (a, bb, bb);
+  x += __builtin_add_overflow_p (a, b, fa);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
+  x += __builtin_sub_overflow_p (a, b, ca);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
+  x += __builtin_mul_overflow_p (a, b, c);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
   return x;
 }
 
--- gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c.jj	2016-06-06 11:37:40.770240477 +0200
+++ gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c	2016-06-06 12:52:32.419977756 +0200
@@ -0,0 +1,493 @@
+/* PR c/68120 - can't easily deal with integer overflow at compile time */
+/* { dg-do run } */
+/* { dg-additional-options "-Wno-long-long" } */
+
+#define SCHAR_MAX    __SCHAR_MAX__
+#define SHRT_MAX     __SHRT_MAX__
+#define INT_MAX	     __INT_MAX__
+#define LONG_MAX     __LONG_MAX__
+#define LLONG_MAX    __LONG_LONG_MAX__
+	
+#define SCHAR_MIN    (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN     (-__SHRT_MAX__ - 1)
+#define INT_MIN	     (-__INT_MAX__ - 1)
+#define LONG_MIN     (-__LONG_MAX__ - 1)
+#define LLONG_MIN    (-__LONG_LONG_MAX__ - 1)
+	
+#define UCHAR_MAX    (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX    (SHRT_MAX * 2U + 1)
+#define UINT_MAX     (INT_MAX * 2U + 1)
+#define ULONG_MAX    (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX   (LLONG_MAX * 2LLU + 1)
+	
+#define USCHAR_MIN   (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN    (-__USHRT_MAX__ - 1)
+#define UINT_MIN     (-__UINT_MAX__ - 1)
+#define ULONG_MIN    (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN   (-__ULONG_LONG_MAX__ - 1)
+
+/* Number of failed runtime assertions.  */
+int nfails;
+
+void __attribute__ ((noclone, noinline))
+runtime_assert (int expr, int line)
+{
+  if (!expr)
+    {
+      __builtin_printf ("line %i: assertion failed\n", line);
+      ++nfails;
+    }
+}
+
+/* Helper macros for run-time testing.  */
+#define add(x, y) ((x) + (y))
+#define sadd(x, y) ((x) + (y))
+#define saddl(x, y) ((x) + (y))
+#define saddll(x, y) ((x) + (y))
+#define uadd(x, y) ((x) + (y))
+#define uaddl(x, y) ((x) + (y))
+#define uaddll(x, y) ((x) + (y))
+#define sub(x, y) ((x) - (y))
+#define ssub(x, y) ((x) - (y))
+#define ssubl(x, y) ((x) - (y))
+#define ssubll(x, y) ((x) - (y))
+#define usub(x, y) ((x) - (y))
+#define usubl(x, y) ((x) - (y))
+#define usubll(x, y) ((x) - (y))
+#define mul(x, y) ((x) * (y))
+#define smul(x, y) ((x) * (y))
+#define smull(x, y) ((x) * (y))
+#define smulll(x, y) ((x) * (y))
+#define umul(x, y) ((x) * (y))
+#define umull(x, y) ((x) * (y))
+#define umulll(x, y) ((x) * (y))
+
+int main (void)
+{
+
+#if __cplusplus >= 201103L
+#  define StaticAssert(expr) static_assert ((expr), #expr)
+#elif __STDC_VERSION__ >= 201112L
+#  define StaticAssert(expr) _Static_assert ((expr), #expr)
+#else
+  /* The following pragma has no effect due to bug 70888 - #pragma
+     diagnostic ignored -Wlong-long ineffective with __LONG_LONG_MAX__
+     in c++98 mode.  */
+#  pragma GCC diagnostic ignored "-Wlong-long"
+#  pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+
+#  define CONCAT(a, b)  a ## b
+#  define CAT(a, b)     CONCAT (a, b)
+#  define StaticAssert(expr)					\
+     typedef int CAT (StaticAssert_, __LINE__) [1 - 2 * !(expr)]
+#endif
+
+  /* Make extra effort to prevent constant folding seeing the constant
+     values of the arguments and optimizing the run-time test into
+     a constant.  */
+#define RuntimeAssert(op, T, U, x, y, vflow)				\
+  do {									\
+    volatile T a = (x), b = (y);					\
+    U c = 0;								\
+    volatile int vf = __builtin_ ## op ## _overflow (a, b, &c);		\
+    runtime_assert ((vf == vflow), __LINE__);				\
+    if (vf == 0)							\
+      runtime_assert (op (a, b) == c, __LINE__);			\
+  } while (0)
+
+  /* Verify that each call to the type-generic __builtin_op_overflow(x, y)
+     yields a constant expression equal to z indicating whether or not
+     the constant expression (x op y) overflows when evaluated in type T.  */
+#  define G_TEST(op, T, x, y, vflow)					\
+  RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow);		\
+  StaticAssert ((vflow) == __builtin_ ## op ## _overflow_p ((x), (y), (T)0))
+
+  /* Addition.  */
+  G_TEST (add, signed char,    0,         0,         0);
+  G_TEST (add, signed char,    0,         SCHAR_MAX, 0);
+  G_TEST (add, signed char,    1,         SCHAR_MAX, 1);
+  G_TEST (add, signed char,    SCHAR_MAX, SCHAR_MAX, 1);
+  G_TEST (add, signed char,    0,         SCHAR_MIN, 0);
+  G_TEST (add, signed char,   -1,         SCHAR_MIN, 1);
+  /* Verify any slicing in the result type doesn't prevent the overflow
+     from being detected.  */
+  G_TEST (add, signed char,    UCHAR_MAX + 1, 0,     1);
+  G_TEST (add, signed char,    UCHAR_MAX + 1, 1,     1);
+  G_TEST (add, signed char,    1, UCHAR_MAX + 1,     1);
+
+  G_TEST (add, unsigned char,  0,        0,          0);
+  /* Verify any slicing in the result type doesn't prevent the overflow
+     from being detected.  */
+  G_TEST (add, unsigned char,  UCHAR_MAX + 1, 0,     1);
+  G_TEST (add, unsigned char,  UCHAR_MAX + 1, 1,     1);
+  G_TEST (add, unsigned char,  1, UCHAR_MAX + 1,     1);
+
+  G_TEST (add, short,          0,         0,         0);
+  G_TEST (add, short,          0,         SHRT_MAX,  0);
+  G_TEST (add, short,          1,         SHRT_MAX,  1);
+  G_TEST (add, short,          SHRT_MAX,  SHRT_MAX,  1);
+  G_TEST (add, short,          0,         SHRT_MIN,  0);
+  G_TEST (add, short,         -1,         SHRT_MIN,  1);
+  G_TEST (add, short,          SHRT_MIN,  SHRT_MIN,  1);
+
+  G_TEST (add, int,            0,         0,         0);
+  G_TEST (add, int,            0,         INT_MAX,   0);
+  G_TEST (add, int,            1,         INT_MAX,   1);
+  G_TEST (add, int,            INT_MAX,   INT_MAX,   1);
+  G_TEST (add, int,            0,         INT_MIN,   0);
+  G_TEST (add, int,           -1,         INT_MIN,   1);
+  G_TEST (add, int,            INT_MIN,   INT_MIN,   1);
+
+  G_TEST (add, long,           0,         0,         0);
+  G_TEST (add, long,           0,         LONG_MAX,  0);
+  G_TEST (add, long,           1,         LONG_MAX,  1);
+  G_TEST (add, long,           LONG_MAX,  LONG_MAX,  1);
+  G_TEST (add, long,           0,         LONG_MIN,  0);
+  G_TEST (add, long,          -1,         LONG_MIN,  1);
+  G_TEST (add, long,           LONG_MIN,  LONG_MIN,  1);
+
+  G_TEST (add, long long,      0,         0,          0);
+  G_TEST (add, long long,      0,         LLONG_MAX,  0);
+  G_TEST (add, long long,      1,         LLONG_MAX,  1);
+  G_TEST (add, long long,      LLONG_MAX, LLONG_MAX,  1);
+  G_TEST (add, long long,      0,         LLONG_MIN,  0);
+  G_TEST (add, long long,     -1,         LLONG_MIN,  1);
+  G_TEST (add, long long,      LLONG_MIN, LLONG_MIN,  1);
+
+  /* Subtraction */
+  G_TEST (sub, unsigned char,  0,         0,          0);
+  G_TEST (sub, unsigned char,  0,         UCHAR_MAX,  1);
+  G_TEST (sub, unsigned char,  1,         UCHAR_MAX,  1);
+
+  G_TEST (sub, unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+  G_TEST (sub, unsigned short, 0,         0,          0);
+  G_TEST (sub, unsigned short, 0,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, 1,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  G_TEST (sub, unsigned,       0,         0,          0);
+  G_TEST (sub, unsigned,       0,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       1,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+  G_TEST (sub, unsigned long,  0,         0,          0);
+  G_TEST (sub, unsigned long,  0,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  1,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  ULONG_MAX, ULONG_MAX,  0);
+
+  G_TEST (sub, unsigned long long,  0,          0,          0);
+  G_TEST (sub, unsigned long long,  0,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  1,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
+
+  G_TEST (sub, signed char,    0,         0,           0);
+  G_TEST (sub, signed char,    0,         SCHAR_MAX,   0);
+  G_TEST (sub, signed char,    1,         SCHAR_MAX,   0);
+  G_TEST (sub, signed char,    SCHAR_MAX, SCHAR_MAX,   0);
+  G_TEST (sub, signed char,    SCHAR_MIN,         1,   1);
+  G_TEST (sub, signed char,    0,         SCHAR_MIN,   1);
+  G_TEST (sub, signed char,   -1,         SCHAR_MIN,   0);
+
+  G_TEST (sub, short,          0,         0,           0);
+  G_TEST (sub, short,          0,         SHRT_MAX,    0);
+  G_TEST (sub, short,          1,         SHRT_MAX,    0);
+  G_TEST (sub, short,          SHRT_MAX,  SHRT_MAX,    0);
+  G_TEST (sub, short,          0,         SHRT_MIN,    1);
+  G_TEST (sub, short,         -1,         SHRT_MIN,    0);
+  G_TEST (sub, short,          SHRT_MIN,  SHRT_MIN,    0);
+
+  G_TEST (sub, int,            0,         0,           0);
+  G_TEST (sub, int,            0,         INT_MAX,     0);
+  G_TEST (sub, int,            1,         INT_MAX,     0);
+  G_TEST (sub, int,            INT_MAX,   INT_MAX,     0);
+  G_TEST (sub, int,            0,         INT_MIN,     1);
+  G_TEST (sub, int,           -1,         INT_MIN,     0);
+  G_TEST (sub, int,            INT_MIN,   INT_MIN,     0);
+
+  G_TEST (sub, long,           0,         0,           0);
+  G_TEST (sub, long,           0,         LONG_MAX,    0);
+  G_TEST (sub, long,           1,         LONG_MAX,    0);
+  G_TEST (sub, long,           LONG_MAX,  LONG_MAX,    0);
+  G_TEST (sub, long,           0,         LONG_MIN,    1);
+  G_TEST (sub, long,          -1,         LONG_MIN,    0);
+  G_TEST (sub, long,           LONG_MIN,  LONG_MIN,    0);
+
+  G_TEST (sub, long long,      0,           0,           0);
+  G_TEST (sub, long long,      0,           LLONG_MAX,   0);
+  G_TEST (sub, long long,      1,           LLONG_MAX,   0);
+  G_TEST (sub, long long,      LLONG_MAX,   LLONG_MAX,   0);
+  G_TEST (sub, long long,      0,           LLONG_MIN,   1);
+  G_TEST (sub, long long,     -1,           LLONG_MIN,   0);
+  G_TEST (sub, long long,      LLONG_MIN,   LLONG_MIN,   0);
+
+  G_TEST (sub, unsigned char,  0,         0,          0);
+  G_TEST (sub, unsigned char,  0,         UCHAR_MAX,  1);
+  G_TEST (sub, unsigned char,  1,         UCHAR_MAX,  1);
+  G_TEST (sub, unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+
+  G_TEST (sub, unsigned short, 0,         0,          0);
+  G_TEST (sub, unsigned short, 0,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, 1,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  G_TEST (sub, unsigned,       0,         0,          0);
+  G_TEST (sub, unsigned,       0,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       1,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+  G_TEST (sub, unsigned long,  0,         0,          0);
+  G_TEST (sub, unsigned long,  0,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  1,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  ULONG_MAX, ULONG_MAX,  0);
+
+  G_TEST (sub, unsigned long long,  0,          0,          0);
+  G_TEST (sub, unsigned long long,  0,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  1,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
+
+  /* Multiplication.  */
+  G_TEST (mul, unsigned char,  0,         0,          0);
+  G_TEST (mul, unsigned char,  0,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  1,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  2,         UCHAR_MAX,  1);
+  G_TEST (mul, unsigned char,  UCHAR_MAX, UCHAR_MAX,  1);
+
+  G_TEST (mul, unsigned short, 0,         0,          0);
+  G_TEST (mul, unsigned short, 0,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, 1,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, USHRT_MAX, 2,          1);
+  G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX,  1);
+
+  G_TEST (mul, unsigned,       0,         0,          0);
+  G_TEST (mul, unsigned,       0,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       1,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       2,         UINT_MAX,   1);
+  G_TEST (mul, unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+  G_TEST (mul, unsigned long,  0,         0,          0);
+  G_TEST (mul, unsigned long,  0,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  1,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  2,         ULONG_MAX,  1);
+  G_TEST (mul, unsigned long,  ULONG_MAX, ULONG_MAX,  1);
+
+  G_TEST (mul, unsigned long long,  0,          0,          0);
+  G_TEST (mul, unsigned long long,  0,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  1,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  2,          ULLONG_MAX, 1);
+  G_TEST (mul, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 1);
+
+  G_TEST (mul, signed char,  0,         0,           0);
+  G_TEST (mul, signed char,  0,         SCHAR_MAX,   0);
+  G_TEST (mul, signed char,  1,         SCHAR_MAX,   0);
+  G_TEST (mul, signed char,  SCHAR_MAX, SCHAR_MAX,   1);
+  G_TEST (mul, signed char,  SCHAR_MIN,         1,   0);
+  G_TEST (mul, signed char,  0,         SCHAR_MIN,   0);
+  G_TEST (mul, signed char, -1,         SCHAR_MIN,   1);
+
+  G_TEST (mul, short,        0,         0,           0);
+  G_TEST (mul, short,        0,         SHRT_MAX,    0);
+  G_TEST (mul, short,        1,         SHRT_MAX,    0);
+  G_TEST (mul, short,        SHRT_MAX,  SHRT_MAX,    1);
+  G_TEST (mul, short,        0,         SHRT_MIN,    0);
+  G_TEST (mul, short,       -1,         SHRT_MIN,    1);
+  G_TEST (mul, short,        SHRT_MIN,  SHRT_MIN,    1);
+
+  G_TEST (mul, int,          0,         0,           0);
+  G_TEST (mul, int,          0,         INT_MAX,     0);
+  G_TEST (mul, int,          1,         INT_MAX,     0);
+  G_TEST (mul, int,          INT_MAX,   INT_MAX,     1);
+  G_TEST (mul, int,          0,         INT_MIN,     0);
+  G_TEST (mul, int,         -1,         INT_MIN,     1);
+  G_TEST (mul, int,          INT_MIN,   INT_MIN,     1);
+
+  G_TEST (mul, long,         0,         0,           0);
+  G_TEST (mul, long,         0,         LONG_MAX,    0);
+  G_TEST (mul, long,         1,         LONG_MAX,    0);
+  G_TEST (mul, long,         LONG_MAX,  LONG_MAX,    1);
+  G_TEST (mul, long,         0,         LONG_MIN,    0);
+  G_TEST (mul, long,        -1,         LONG_MIN,    1);
+  G_TEST (mul, long,         LONG_MIN,  LONG_MIN,    1);
+
+  G_TEST (mul, long long,    0,           0,           0);
+  G_TEST (mul, long long,    0,           LLONG_MAX,   0);
+  G_TEST (mul, long long,    1,           LLONG_MAX,   0);
+  G_TEST (mul, long long,    LLONG_MAX,   LLONG_MAX,   1);
+  G_TEST (mul, long long,    0,           LLONG_MIN,   0);
+  G_TEST (mul, long long,   -1,           LLONG_MIN,   1);
+  G_TEST (mul, long long,    LLONG_MIN,   LLONG_MIN,   1);
+
+  G_TEST (mul, unsigned char,  0,         0,          0);
+  G_TEST (mul, unsigned char,  0,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  1,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  UCHAR_MAX, UCHAR_MAX,  1);
+
+  G_TEST (mul, unsigned short, 0,         0,          0);
+  G_TEST (mul, unsigned short, 0,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, 1,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX,  1);
+
+  G_TEST (mul, unsigned,       0,         0,          0);
+  G_TEST (mul, unsigned,       0,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       1,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+  G_TEST (mul, unsigned long,  0,         0,          0);
+  G_TEST (mul, unsigned long,  0,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  1,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  ULONG_MAX, ULONG_MAX,  1);
+
+  G_TEST (mul, unsigned long long,  0,          0,          0);
+  G_TEST (mul, unsigned long long,  0,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  1,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 1);
+
+  /* Verify that each call to the type-specific __builtin_op_overflow
+     evaluates to a (not-necessarily constant) expression indicating
+     whether or not the constant expression (x op y) overflows.
+     The type-specific forms of the built-ins detect overflow after
+     arithmetic promotions and so unlike the type-generic overloads
+     cannot detect overflow in char or short types.  */
+
+#define T_TEST(op, T, x, y, vflow)				\
+  RuntimeAssert (op, T, __typeof__ ((x) + (y)), x, y, vflow)
+
+  /* Signed int addition.  */
+  T_TEST (sadd,   signed char,    0,         0,         0);
+  T_TEST (sadd,   signed char,    0,         SCHAR_MAX, 0);
+  T_TEST (sadd,   signed char,    1,         SCHAR_MAX, 0);
+  T_TEST (sadd,   signed char,    SCHAR_MAX, SCHAR_MAX, 0);
+  T_TEST (sadd,   signed char,    0,         SCHAR_MIN, 0);
+  T_TEST (sadd,   signed char,   -1,         SCHAR_MIN, 0);
+
+  T_TEST (sadd,   short,          0,         0,         0);
+  T_TEST (sadd,   short,          0,         SHRT_MAX,  0);
+  T_TEST (sadd,   short,          1,         SHRT_MAX,  0);
+  T_TEST (sadd,   short,          SHRT_MAX,  SHRT_MAX,  0);
+  T_TEST (sadd,   short,          0,         SHRT_MIN,  0);
+  T_TEST (sadd,   short,         -1,         SHRT_MIN,  0);
+  T_TEST (sadd,   short,          SHRT_MIN,  SHRT_MIN,  0);
+
+  T_TEST (sadd,   int,            0,         0,         0);
+  T_TEST (sadd,   int,            0,         INT_MAX,   0);
+  T_TEST (sadd,   int,            1,         INT_MAX,   1);
+  T_TEST (sadd,   int,            INT_MAX,   INT_MAX,   1);
+  T_TEST (sadd,   int,            0,         INT_MIN,   0);
+  T_TEST (sadd,   int,           -1,         INT_MIN,   1);
+  T_TEST (sadd,   int,            INT_MIN,   INT_MIN,   1);
+
+  /* Signed long addition.  */
+  T_TEST (saddl,  long,           0L,        0L,        0);
+  T_TEST (saddl,  long,           0L,        LONG_MAX,  0);
+  T_TEST (saddl,  long,           1L,        LONG_MAX,  1);
+  T_TEST (saddl,  long,           LONG_MAX,  LONG_MAX,  1);
+  T_TEST (saddl,  long,           0L,        LONG_MIN,  0);
+  T_TEST (saddl,  long,          -1L,        LONG_MIN,  1);
+  T_TEST (saddl,  long,           LONG_MIN,  LONG_MIN,  1);
+
+  T_TEST (saddll, long long,      0LL,       0LL,        0);
+  T_TEST (saddll, long long,      0LL,       LLONG_MAX,  0);
+  T_TEST (saddll, long long,      1LL,       LLONG_MAX,  1);
+  T_TEST (saddll, long long,      LLONG_MAX, LLONG_MAX,  1);
+  T_TEST (saddll, long long,      0LL,       LLONG_MIN,  0);
+  T_TEST (saddll, long long,     -1LL,       LLONG_MIN,  1);
+  T_TEST (saddll, long long,      LLONG_MIN, LLONG_MIN,  1);
+
+  /* Unsigned int addition.  */
+  T_TEST (uadd,   unsigned char,  0U,        0U,         0);
+  T_TEST (uadd,   unsigned char,  0U,        UCHAR_MAX, 0);
+  T_TEST (uadd,   unsigned char,  1U,        UCHAR_MAX, 0);
+  T_TEST (uadd,   unsigned char,  UCHAR_MAX, UCHAR_MAX, 0);
+
+  T_TEST (uadd,   unsigned short, 0U,        0U,         0);
+  T_TEST (uadd,   unsigned short, 0U,        USHRT_MAX,  0);
+  T_TEST (uadd,   unsigned short, 1U,        USHRT_MAX,  0);
+  T_TEST (uadd,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  T_TEST (uadd,   unsigned,       0U,        0U,         0);
+  T_TEST (uadd,   unsigned,       0U,        UINT_MAX,   0);
+  T_TEST (uadd,   unsigned,       1U,        UINT_MAX,   1);
+  T_TEST (uadd,   unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+  /* Unsigned long addition.  */
+  T_TEST (uaddl,  unsigned long,  0UL,       0UL,       0);
+  T_TEST (uaddl,  unsigned long,  0UL,       ULONG_MAX, 0);
+  T_TEST (uaddl,  unsigned long,  1UL,       ULONG_MAX, 1);
+  T_TEST (uaddl,  unsigned long,  ULONG_MAX, ULONG_MAX, 1);
+
+  T_TEST (uaddll, unsigned long long, 0ULL,       0ULL,       0);
+  T_TEST (uaddll, unsigned long long, 0ULL,       ULLONG_MAX, 0);
+  T_TEST (uaddll, unsigned long long, 1ULL,       ULLONG_MAX, 1);
+  T_TEST (uaddll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
+
+  /* Signed int subtraction.  */
+  T_TEST (ssub,   signed char,    0,         0,          0);
+  T_TEST (ssub,   signed char,    0,         SCHAR_MAX,  0);
+  T_TEST (ssub,   signed char,    1,         SCHAR_MAX,  0);
+  T_TEST (ssub,   signed char,    SCHAR_MAX, SCHAR_MAX,  0);
+  T_TEST (ssub,   signed char,    0,         SCHAR_MIN,  0);
+  T_TEST (ssub,   signed char,   -1,         SCHAR_MIN,  0);
+
+  T_TEST (ssub,   short,          0,         0,          0);
+  T_TEST (ssub,   short,          0,         SHRT_MAX,   0);
+  T_TEST (ssub,   short,          1,         SHRT_MAX,   0);
+  T_TEST (ssub,   short,          SHRT_MAX,  SHRT_MAX,   0);
+  T_TEST (ssub,   short,          0,         SHRT_MIN,   0);
+  T_TEST (ssub,   short,         -1,         SHRT_MIN,   0);
+  T_TEST (ssub,   short,          SHRT_MIN,  SHRT_MIN,   0);
+
+  T_TEST (ssub,   int,            0,         0,          0);
+  T_TEST (ssub,   int,            0,         INT_MAX,    0);
+  T_TEST (ssub,   int,            1,         INT_MAX,    0);
+  T_TEST (ssub,   int,            INT_MAX,   INT_MAX,    0);
+  T_TEST (ssub,   int,            0,         INT_MIN,    1);
+  T_TEST (ssub,   int,           -1,         INT_MIN,    0);
+  T_TEST (ssub,   int,            INT_MIN,   INT_MIN,    0);
+
+  /* Signed long subtraction.  */
+  T_TEST (ssubl,  long,           0L,        0L,         0);
+  T_TEST (ssubl,  long,           0L,        LONG_MAX,   0);
+  T_TEST (ssubl,  long,           1L,        LONG_MAX,   0);
+  T_TEST (ssubl,  long,           LONG_MAX,  LONG_MAX,   0);
+  T_TEST (ssubl,  long,           0L,        LONG_MIN,   1);
+  T_TEST (ssubl,  long,          -1L,        LONG_MIN,   0);
+  T_TEST (ssubl,  long,           LONG_MIN,  LONG_MIN,   0);
+
+  /* Signed long long subtraction.  */
+  T_TEST (ssubll, long long,      0LL,       0LL,        0);
+  T_TEST (ssubll, long long,      0LL,       LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      1LL,       LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      LLONG_MAX, LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      0LL,       LLONG_MIN,  1);
+  T_TEST (ssubll, long long,     -1LL,       LLONG_MIN,  0);
+  T_TEST (ssubll, long long,      LLONG_MIN, LLONG_MIN,  0);
+
+  /* Unsigned int subtraction.  */
+  T_TEST (usub,   unsigned char,  0U,        0U,         0);
+  T_TEST (usub,   unsigned char,  0U,        UCHAR_MAX,  1);
+  T_TEST (usub,   unsigned char,  1U,        UCHAR_MAX,  1);
+  T_TEST (usub,   unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+
+  T_TEST (usub,   unsigned short, 0U,        0U,         0);
+  T_TEST (usub,   unsigned short, 0U,        USHRT_MAX,  1);
+  T_TEST (usub,   unsigned short, 1U,        USHRT_MAX,  1);
+  T_TEST (usub,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  T_TEST (usub,   unsigned,       0U,        0U,         0);
+  T_TEST (usub,   unsigned,       0U,        UINT_MAX,   1);
+  T_TEST (usub,   unsigned,       1U,        UINT_MAX,   1);
+  T_TEST (usub,   unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+  /* Unsigned long subtraction.  */
+  T_TEST (usubl,  unsigned long,  0UL,       0UL,       0);
+  T_TEST (usubl,  unsigned long,  0UL,       ULONG_MAX, 1);
+  T_TEST (usubl,  unsigned long,  1UL,       ULONG_MAX, 1);
+  T_TEST (usubl,  unsigned long,  ULONG_MAX, ULONG_MAX, 0);
+
+  /* Unsigned long long subtraction.  */
+  T_TEST (usubll, unsigned long long,  0ULL,       0ULL,       0);
+  T_TEST (usubll, unsigned long long,  0ULL,       ULLONG_MAX, 1);
+  T_TEST (usubll, unsigned long long,  1ULL,       ULLONG_MAX, 1);
+  T_TEST (usubll, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
+
+  return 0;
+}
--- gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C.jj	2016-06-06 11:37:40.771240464 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C	2016-06-06 13:55:24.551263688 +0200
@@ -0,0 +1,229 @@
+// Test to exercise that the type-specific integer arithmetic built-ins
+// with overflow checking can be used in C++ 14 constant expressions.
+// -Woverflow is disabled to prevent (bogus?) G++ warnings.
+// { dg-do compile { target c++14 } }
+// { dg-additional-options "-Wno-overflow" }
+
+#define SCHAR_MAX    __SCHAR_MAX__
+#define SHRT_MAX     __SHRT_MAX__
+#define INT_MAX	     __INT_MAX__
+#define LONG_MAX     __LONG_MAX__
+#define LLONG_MAX    __LONG_LONG_MAX__
+	
+#define SCHAR_MIN    (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN     (-__SHRT_MAX__ - 1)
+#define INT_MIN	     (-__INT_MAX__ - 1)
+#define LONG_MIN     (-__LONG_MAX__ - 1)
+#define LLONG_MIN    (-__LONG_LONG_MAX__ - 1)
+	
+#define UCHAR_MAX    (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX    (SHRT_MAX * 2U + 1)
+#define UINT_MAX     (INT_MAX * 2U + 1)
+#define ULONG_MAX    (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX   (LLONG_MAX * 2LLU + 1)
+	
+#define USCHAR_MIN   (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN    (-__USHRT_MAX__ - 1)
+#define UINT_MIN     (-__UINT_MAX__ - 1)
+#define ULONG_MIN    (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN   (-__ULONG_LONG_MAX__ - 1)
+
+// Helper macros.
+#define sadd(x, y) ((x) + (y))
+#define saddl(x, y) ((x) + (y))
+#define saddll(x, y) ((x) + (y))
+#define uadd(x, y) ((x) + (y))
+#define uaddl(x, y) ((x) + (y))
+#define uaddll(x, y) ((x) + (y))
+#define ssub(x, y) ((x) - (y))
+#define ssubl(x, y) ((x) - (y))
+#define ssubll(x, y) ((x) - (y))
+#define usub(x, y) ((x) - (y))
+#define usubl(x, y) ((x) - (y))
+#define usubll(x, y) ((x) - (y))
+#define smul(x, y) ((x) * (y))
+#define smull(x, y) ((x) * (y))
+#define smulll(x, y) ((x) * (y))
+#define umul(x, y) ((x) * (y))
+#define umull(x, y) ((x) * (y))
+#define umulll(x, y) ((x) * (y))
+
+// Result object.
+template <class T>
+struct Res
+{
+  constexpr Res (T a, bool v): z (a), v (v) { }
+  T z; bool v;
+};
+
+template <class T>
+constexpr bool operator== (Res<T> a, Res<T> b)
+{
+  return a.z == b.z && a.v == b.v;
+}
+
+#define StaticAssert(expr) static_assert ((expr), #expr)
+
+#define CONCAT(a, b)   a ## b
+#define CAT(a, b)      CONCAT (a, b)
+
+// Helper to determine the type of the result of the arithmetic
+// as specified by the built-ins.
+template <class T> struct ResType { typedef T type; };
+template <>        struct ResType<signed char> { typedef int type; };
+template <>        struct ResType<unsigned char> { typedef unsigned type; };
+template <>        struct ResType<signed short> { typedef int type; };
+template <>        struct ResType<unsigned short> { typedef unsigned type; };
+
+// Macro to define a single test case verifying that integer overflow
+// is detected when expected, and when not, that the result matches
+// the result computed using ordinary arithmetic.  The result cannot
+// be tested in the presence of overflow since it's not a core
+// constant expression.
+#define TEST(op, T, x, y, vflow)					\
+  constexpr Res<T> CAT (op, __LINE__)(T a, T b)				\
+  {									\
+    ResType<T>::type c = 0;						\
+    bool v = __builtin_ ## op ## _overflow (a, b, &c);			\
+    return Res<T>(c, v);						\
+  }									\
+  StaticAssert (vflow ? CAT (op, __LINE__)(x, y).v			\
+		: CAT (op, __LINE__)(x, y) == Res<T>(op (x, y), vflow))
+
+/* Signed int addition.  */
+TEST (sadd,   signed char,    0,         0,         0);
+TEST (sadd,   signed char,    0,         SCHAR_MAX, 0);
+TEST (sadd,   signed char,    1,         SCHAR_MAX, 0);
+TEST (sadd,   signed char,    SCHAR_MAX, SCHAR_MAX, 0);
+TEST (sadd,   signed char,    0,         SCHAR_MIN, 0);
+TEST (sadd,   signed char,   -1,         SCHAR_MIN, 0);
+
+TEST (sadd,   short,          0,         0,         0);
+TEST (sadd,   short,          0,         SHRT_MAX,  0);
+TEST (sadd,   short,          1,         SHRT_MAX,  0);
+TEST (sadd,   short,          SHRT_MAX,  SHRT_MAX,  0);
+TEST (sadd,   short,          0,         SHRT_MIN,  0);
+TEST (sadd,   short,         -1,         SHRT_MIN,  0);
+TEST (sadd,   short,          SHRT_MIN,  SHRT_MIN,  0);
+
+TEST (sadd,   int,            0,         0,         0);
+TEST (sadd,   int,            0,         INT_MAX,   0);
+TEST (sadd,   int,            1,         INT_MAX,   1);
+TEST (sadd,   int,            INT_MAX,   INT_MAX,   1);
+TEST (sadd,   int,            0,         INT_MIN,   0);
+TEST (sadd,   int,           -1,         INT_MIN,   1);
+TEST (sadd,   int,            INT_MIN,   INT_MIN,   1);
+
+/* Signed long addition.  */
+TEST (saddl,  long,           0L,        0L,        0);
+TEST (saddl,  long,           0L,        LONG_MAX,  0);
+TEST (saddl,  long,           1L,        LONG_MAX,  1);
+TEST (saddl,  long,           LONG_MAX,  LONG_MAX,  1);
+TEST (saddl,  long,           0L,        LONG_MIN,  0);
+TEST (saddl,  long,          -1L,        LONG_MIN,  1);
+TEST (saddl,  long,           LONG_MIN,  LONG_MIN,  1);
+
+TEST (saddll, long long,      0LL,       0LL,        0);
+TEST (saddll, long long,      0LL,       LLONG_MAX,  0);
+TEST (saddll, long long,      1LL,       LLONG_MAX,  1);
+TEST (saddll, long long,      LLONG_MAX, LLONG_MAX,  1);
+TEST (saddll, long long,      0LL,       LLONG_MIN,  0);
+TEST (saddll, long long,     -1LL,       LLONG_MIN,  1);
+TEST (saddll, long long,      LLONG_MIN, LLONG_MIN,  1);
+
+/* Unsigned int addition.  */
+TEST (uadd,   unsigned char,  0U,        0U,         0);
+TEST (uadd,   unsigned char,  0U,        UCHAR_MAX, 0);
+TEST (uadd,   unsigned char,  1U,        UCHAR_MAX, 0);
+TEST (uadd,   unsigned char,  UCHAR_MAX, UCHAR_MAX, 0);
+
+TEST (uadd,   unsigned short, 0U,        0U,         0);
+TEST (uadd,   unsigned short, 0U,        USHRT_MAX,  0);
+TEST (uadd,   unsigned short, 1U,        USHRT_MAX,  0);
+TEST (uadd,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+TEST (uadd,   unsigned,       0U,        0U,         0);
+TEST (uadd,   unsigned,       0U,        UINT_MAX,   0);
+TEST (uadd,   unsigned,       1U,        UINT_MAX,   1);
+TEST (uadd,   unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+/* Unsigned long addition.  */
+TEST (uaddl,  unsigned long,  0UL,       0UL,       0);
+TEST (uaddl,  unsigned long,  0UL,       ULONG_MAX, 0);
+TEST (uaddl,  unsigned long,  1UL,       ULONG_MAX, 1);
+TEST (uaddl,  unsigned long,  ULONG_MAX, ULONG_MAX, 1);
+
+TEST (uaddll, unsigned long long, 0ULL,       0ULL,       0);
+TEST (uaddll, unsigned long long, 0ULL,       ULLONG_MAX, 0);
+TEST (uaddll, unsigned long long, 1ULL,       ULLONG_MAX, 1);
+TEST (uaddll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
+
+/* Signed int subtraction.  */
+TEST (ssub,   signed char,    0,         0,          0);
+TEST (ssub,   signed char,    0,         SCHAR_MAX,  0);
+TEST (ssub,   signed char,    1,         SCHAR_MAX,  0);
+TEST (ssub,   signed char,    SCHAR_MAX, SCHAR_MAX,  0);
+TEST (ssub,   signed char,    0,         SCHAR_MIN,  0);
+TEST (ssub,   signed char,   -1,         SCHAR_MIN,  0);
+
+TEST (ssub,   short,          0,         0,          0);
+TEST (ssub,   short,          0,         SHRT_MAX,   0);
+TEST (ssub,   short,          1,         SHRT_MAX,   0);
+TEST (ssub,   short,          SHRT_MAX,  SHRT_MAX,   0);
+TEST (ssub,   short,          0,         SHRT_MIN,   0);
+TEST (ssub,   short,         -1,         SHRT_MIN,   0);
+TEST (ssub,   short,          SHRT_MIN,  SHRT_MIN,   0);
+
+TEST (ssub,   int,            0,         0,          0);
+TEST (ssub,   int,            0,         INT_MAX,    0);
+TEST (ssub,   int,            1,         INT_MAX,    0);
+TEST (ssub,   int,            INT_MAX,   INT_MAX,    0);
+TEST (ssub,   int,            0,         INT_MIN,    1);
+TEST (ssub,   int,           -1,         INT_MIN,    0);
+TEST (ssub,   int,            INT_MIN,   INT_MIN,    0);
+
+/* Signed long subtraction.  */
+TEST (ssubl,  long,           0L,        0L,         0);
+TEST (ssubl,  long,           0L,        LONG_MAX,   0);
+TEST (ssubl,  long,           1L,        LONG_MAX,   0);
+TEST (ssubl,  long,           LONG_MAX,  LONG_MAX,   0);
+TEST (ssubl,  long,           0L,        LONG_MIN,   1);
+TEST (ssubl,  long,          -1L,        LONG_MIN,   0);
+TEST (ssubl,  long,           LONG_MIN,  LONG_MIN,   0);
+
+/* Signed long long subtraction.  */
+TEST (ssubll, long long,      0LL,       0LL,        0);
+TEST (ssubll, long long,      0LL,       LLONG_MAX,  0);
+TEST (ssubll, long long,      1LL,       LLONG_MAX,  0);
+TEST (ssubll, long long,      LLONG_MAX, LLONG_MAX,  0);
+TEST (ssubll, long long,      0LL,       LLONG_MIN,  1);
+TEST (ssubll, long long,     -1LL,       LLONG_MIN,  0);
+TEST (ssubll, long long,      LLONG_MIN, LLONG_MIN,  0);
+
+/* Unsigned int subtraction.  */
+TEST (usub,   unsigned char,  0U,        0U,         0);
+TEST (usub,   unsigned char,  0U,        UCHAR_MAX,  1);
+TEST (usub,   unsigned char,  1U,        UCHAR_MAX,  1);
+TEST (usub,   unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+
+TEST (usub,   unsigned short, 0U,        0U,         0);
+TEST (usub,   unsigned short, 0U,        USHRT_MAX,  1);
+TEST (usub,   unsigned short, 1U,        USHRT_MAX,  1);
+TEST (usub,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+TEST (usub,   unsigned,       0U,        0U,         0);
+TEST (usub,   unsigned,       0U,        UINT_MAX,   1);
+TEST (usub,   unsigned,       1U,        UINT_MAX,   1);
+TEST (usub,   unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+/* Unsigned long subtraction.  */
+TEST (usubl,  unsigned long,  0UL,       0UL,       0);
+TEST (usubl,  unsigned long,  0UL,       ULONG_MAX, 1);
+TEST (usubl,  unsigned long,  1UL,       ULONG_MAX, 1);
+TEST (usubl,  unsigned long,  ULONG_MAX, ULONG_MAX, 0);
+
+/* Unsigned long long subtraction.  */
+TEST (usubll, unsigned long long,  0ULL,       0ULL,       0);
+TEST (usubll, unsigned long long,  0ULL,       ULLONG_MAX, 1);
+TEST (usubll, unsigned long long,  1ULL,       ULLONG_MAX, 1);
+TEST (usubll, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
--- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C.jj	2016-06-06 11:37:40.771240464 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C	2016-06-06 13:01:55.928674227 +0200
@@ -0,0 +1,212 @@
+// PR c++/70507 - integer overflow builtins not constant expressions
+// { dg-do compile { target c++11 } }
+
+#define SCHAR_MAX    __SCHAR_MAX__
+#define SHRT_MAX     __SHRT_MAX__
+#define INT_MAX	     __INT_MAX__
+#define LONG_MAX     __LONG_MAX__
+#define LLONG_MAX    __LONG_LONG_MAX__
+	
+#define SCHAR_MIN    (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN     (-__SHRT_MAX__ - 1)
+#define INT_MIN	     (-__INT_MAX__ - 1)
+#define LONG_MIN     (-__LONG_MAX__ - 1)
+#define LLONG_MIN    (-__LONG_LONG_MAX__ - 1)
+	
+#define UCHAR_MAX    (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX    (SHRT_MAX * 2U + 1)
+#define UINT_MAX     (INT_MAX * 2U + 1)
+#define ULONG_MAX    (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX   (LLONG_MAX * 2LLU + 1)
+	
+#define USCHAR_MIN   (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN    (-__USHRT_MAX__ - 1)
+#define UINT_MIN     (-__UINT_MAX__ - 1)
+#define ULONG_MIN    (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN   (-__ULONG_LONG_MAX__ - 1)
+
+#define Assert(expr) static_assert ((expr), #expr)
+
+template <class T>
+constexpr T add (T x, T y, T z = T ())
+{
+  return __builtin_add_overflow (x, y, &z) ? 0 : z;
+}
+
+template <class T>
+constexpr T sub (T x, T y, T z = T ())
+{
+  return __builtin_sub_overflow (x, y, &z) ? 0 : z;
+}
+
+template <class T>
+constexpr T mul (T x, T y, T z = T ())
+{
+  return __builtin_mul_overflow (x, y, &z) ? 0 : z;
+}
+
+#define TEST_ADD(T, x, y, z) Assert (z == add<T>(x, y))
+#define TEST_SUB(T, x, y, z) Assert (z == sub<T>(x, y))
+#define TEST_MUL(T, x, y, z) Assert (z == mul<T>(x, y))
+
+
+TEST_ADD (signed char,  0,         0,         0);
+TEST_ADD (signed char,  0,         SCHAR_MAX, SCHAR_MAX);
+TEST_ADD (signed char,  1,         SCHAR_MAX, 0);           // overflow
+TEST_ADD (signed char,  SCHAR_MAX, SCHAR_MAX, 0);           // overflow
+TEST_ADD (signed char,  0,         SCHAR_MIN, SCHAR_MIN);
+TEST_ADD (signed char, -1,         SCHAR_MIN, 0);           // overflow
+
+TEST_ADD (short,        0,         0,         0);
+TEST_ADD (short,        0,         SHRT_MAX,  SHRT_MAX);
+TEST_ADD (short,        1,         SHRT_MAX,  0);           // overflow
+TEST_ADD (short,        SHRT_MAX,  SHRT_MAX,  0);           // overflow
+TEST_ADD (short,        0,         SHRT_MIN,  SHRT_MIN);
+TEST_ADD (short,       -1,         SHRT_MIN,  0);           // overflow
+TEST_ADD (short,        SHRT_MIN,  SHRT_MIN,  0);           // overflow
+
+TEST_ADD (int,          0,         0,         0);
+TEST_ADD (int,          0,         INT_MAX,   INT_MAX);
+TEST_ADD (int,          1,         INT_MAX,   0);           // overflow
+TEST_ADD (int,          INT_MAX,   INT_MAX,   0);           // overflow
+TEST_ADD (int,          0,         INT_MIN,   INT_MIN);
+TEST_ADD (int,         -1,         INT_MIN,   0);           // overflow
+TEST_ADD (int,          INT_MIN,   INT_MIN,   0);           // overflow
+
+TEST_ADD (long,         0,         0,         0);
+TEST_ADD (long,         0,         LONG_MAX,  LONG_MAX);
+TEST_ADD (long,         1,         LONG_MAX,  0);           // overflow
+TEST_ADD (long,         LONG_MAX,  LONG_MAX,  0);           // overflow
+TEST_ADD (long,         0,         LONG_MIN,  LONG_MIN);
+TEST_ADD (long,        -1,         LONG_MIN,  0);           // overflow
+TEST_ADD (long,         LONG_MIN,  LONG_MIN,  0);           // overflow
+
+TEST_ADD (long long,    0,         0,          0);
+TEST_ADD (long long,    0,         LLONG_MAX,  LLONG_MAX);
+TEST_ADD (long long,    1,         LLONG_MAX,  0);          // overflow
+TEST_ADD (long long,    LLONG_MAX, LLONG_MAX,  0);          // overflow
+TEST_ADD (long long,    0,         LLONG_MIN,  LLONG_MIN);
+TEST_ADD (long long,   -1,         LLONG_MIN,  0);          // overflow
+TEST_ADD (long long,    LLONG_MIN, LLONG_MIN,  0);          // overflow
+
+TEST_ADD (unsigned char,  0,         0,         0);
+TEST_ADD (unsigned char,  0,         UCHAR_MAX, UCHAR_MAX);
+TEST_ADD (unsigned char,  1,         UCHAR_MAX, 0);         // overflow
+
+TEST_ADD (unsigned char,  UCHAR_MAX, UCHAR_MAX, 0);         // overflow
+TEST_ADD (unsigned short, 0,         0,          0);
+TEST_ADD (unsigned short, 0,         USHRT_MAX,  USHRT_MAX);
+TEST_ADD (unsigned short, 1,         USHRT_MAX,  0);        // overflow
+TEST_ADD (unsigned short, USHRT_MAX, USHRT_MAX,  0);        // overflow
+
+TEST_ADD (unsigned,       0,         0,          0);
+TEST_ADD (unsigned,       0,         UINT_MAX,   UINT_MAX);
+TEST_ADD (unsigned,       1,         UINT_MAX,   0);        // overflow
+TEST_ADD (unsigned,       UINT_MAX,  UINT_MAX,   0);        // overflow
+
+TEST_ADD (unsigned long,  0,         0,         0);
+TEST_ADD (unsigned long,  0,         ULONG_MAX, ULONG_MAX);
+TEST_ADD (unsigned long,  1,         ULONG_MAX, 0);         // overflow
+TEST_ADD (unsigned long,  ULONG_MAX, ULONG_MAX, 0);         // overflow
+
+TEST_ADD (unsigned long long,  0,          0,          0);
+TEST_ADD (unsigned long long,  0,          ULLONG_MAX, ULLONG_MAX);
+TEST_ADD (unsigned long long,  1,          ULLONG_MAX, 0);  // overflow
+TEST_ADD (unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);  // overflow
+
+
+// Make sure the built-ins are accepted in the following contexts
+// where constant expressions are required and that they return
+// the expected overflow value.
+
+namespace Enum {
+
+enum Add {
+  a0 = __builtin_add_overflow_p (      1, 1, 0),
+  a1 = __builtin_add_overflow_p (INT_MAX, 1, 0)
+};
+
+Assert (a0 == 0);
+Assert (a1 == 1);
+
+enum Sub {
+  s0 = __builtin_sub_overflow_p (      1, 1, 0),
+  s1 = __builtin_sub_overflow_p (INT_MIN, 1, 0)
+};
+
+Assert (s0 == 0);
+Assert (s1 == 1);
+
+enum Mul {
+  m0 = __builtin_add_overflow_p (      1,       1, 0),
+  m1 = __builtin_add_overflow_p (INT_MAX, INT_MAX, 0)
+};
+
+Assert (m0 == 0);
+Assert (m1 == 1);
+
+}   // namespace Enum
+
+namespace TemplateArg {
+
+template <class T, class U, class V,
+	  T x, U y, bool v, bool z = __builtin_add_overflow_p (x, y, V ())>
+struct Add {
+  Assert (z == v);
+};
+
+template <class T, class U, class V,
+	  T x, U y, bool v, bool z = __builtin_sub_overflow_p (x, y, V ())>
+struct Sub {
+  Assert (z == v);
+};
+
+template <class T, class U, class V,
+	  T x, U y, bool v, bool z = __builtin_mul_overflow_p (x, y, V ())>
+struct Mul {
+  Assert (z == v);
+};
+
+template struct Add<int, int, int,  1,       1, false>;
+template struct Add<int, int, int,  1, INT_MAX, true>;
+
+template struct Sub<int, int, int,  1,       1, false>;
+template struct Sub<int, int, int, -2, INT_MAX, true>;
+
+template struct Mul<int, int, int,  1,               1, false>;
+template struct Mul<int, int, int,  2, INT_MAX / 2 + 1, true>;
+
+}   // namespace TemplateArg
+
+#if __cplusplus >= 201402L
+
+namespace Initializer {
+
+struct Result {
+  int res;
+  bool vflow;
+};
+
+constexpr Result
+add_vflow (int a, int b)
+{
+#if 1
+  Result res = { a + b, __builtin_add_overflow_p (a, b, int ()) };
+#else
+  // The following fails to compile because of c++/71391 - error
+  // on aggregate initialization with side-effects in a constexpr
+  // function
+  int c = 0;
+  Result res = { 0, __builtin_add_overflow (a, b, &c) };
+  res.c = c;
+#endif
+  return res;
+}
+
+constexpr Result sum = add_vflow (123, 456);
+Assert (sum.res == 123 + 456);
+Assert (!sum.vflow);
+
+}   // namespace Initializer
+
+#endif   // __cplusplus >= 201402L
--- gcc/c/c-typeck.c.jj	2016-06-03 21:25:15.000000000 +0200
+++ gcc/c/c-typeck.c	2016-06-06 13:40:18.588949985 +0200
@@ -3150,6 +3150,7 @@ convert_arguments (location_t loc, vec<l
   const bool type_generic = fundecl
     && lookup_attribute ("type generic", TYPE_ATTRIBUTES (TREE_TYPE (fundecl)));
   bool type_generic_remove_excess_precision = false;
+  bool type_generic_overflow_p = false;
   tree selector;
 
   /* Change pointer to function to the function itself for
@@ -3179,8 +3180,15 @@ convert_arguments (location_t loc, vec<l
 	  type_generic_remove_excess_precision = true;
 	  break;
 
+	case BUILT_IN_ADD_OVERFLOW_P:
+	case BUILT_IN_SUB_OVERFLOW_P:
+	case BUILT_IN_MUL_OVERFLOW_P:
+	  /* The last argument of these type-generic builtins
+	     should not be promoted.  */
+	  type_generic_overflow_p = true;
+	  break;
+
 	default:
-	  type_generic_remove_excess_precision = false;
 	  break;
 	}
     }
@@ -3430,9 +3438,12 @@ convert_arguments (location_t loc, vec<l
 	      parmval = convert (double_type_node, val);
 	    }
 	}
-      else if (excess_precision && !type_generic)
+      else if ((excess_precision && !type_generic)
+	       || (type_generic_overflow_p && parmnum == 2))
 	/* A "double" argument with excess precision being passed
-	   without a prototype or in variable arguments.  */
+	   without a prototype or in variable arguments.
+	   The last argument of __builtin_*_overflow_p should not be
+	   promoted.  */
 	parmval = convert (valtype, val);
       else if ((invalid_func_diag =
 		targetm.calls.invalid_arg_for_unprototyped_fn (typelist, fundecl, val)))
--- gcc/builtins.def.jj	2016-06-03 21:25:17.000000000 +0200
+++ gcc/builtins.def	2016-06-06 11:38:19.320739229 +0200
@@ -710,6 +710,9 @@ DEF_C94_BUILTIN        (BUILT_IN_TOWUPPE
 DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW, "add_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW, "sub_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW, "mul_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
+DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW_P, "add_overflow_p", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
+DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW_P, "sub_overflow_p", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
+DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW_P, "mul_overflow_p", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 /* Clang compatibility.  */
 DEF_GCC_BUILTIN        (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
--- gcc/c-family/c-common.c.jj	2016-06-01 21:26:28.000000000 +0200
+++ gcc/c-family/c-common.c	2016-06-06 11:53:11.583140114 +0200
@@ -9966,6 +9966,23 @@ check_builtin_function_arguments (locati
 	}
       return false;
 
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
+	{
+	  unsigned i;
+	  for (i = 0; i < 3; i++)
+	    if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
+	      {
+		error_at (ARG_LOCATION (i), "argument %u in call to function "
+			  "%qE does not have integral type", i + 1, fndecl);
+		return false;
+	      }
+	  return true;
+	}
+      return false;
+
     default:
       return true;
     }
--- gcc/doc/extend.texi.jj	2016-06-03 21:25:14.619703978 +0200
+++ gcc/doc/extend.texi	2016-06-06 14:02:12.956005088 +0200
@@ -9865,6 +9865,47 @@ functions above, except they perform mul
 
 @end deftypefn
 
+The following built-in functions allow checking if simple arithmetic operation
+would overflow.
+
+@deftypefn {Built-in Function} bool __builtin_add_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
+@deftypefnx {Built-in Function} bool __builtin_sub_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
+@deftypefnx {Built-in Function} bool __builtin_mul_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
+
+These built-in functions are similar to @code{__builtin_add_overflow},
+@code{__builtin_sub_overflow}, or @code{__builtin_mul_overflow}, except that
+they don't store the result of the arithmetic operation anywhere and the
+last argument is not a pointer, but some integral expression.
+
+The built-in functions promote the first two operands into infinite precision signed type
+and perform addition on those promoted operands. The result is then
+cast to the type the third argument.  If the cast result is equal to the infinite
+precision result, the built-in functions return false, otherwise they return true.
+The value of the third argument is ignored, just the side-effects in the third argument
+are evaluated, and no integral argument promotions are performed on the last argument.
+
+For example, the following macro can be used to portably check, at
+compile-time, whether or not adding two constant integers will overflow,
+and perform the addition only when it is known to be safe and not to trigger
+a @option{-Woverflow} warning.
+
+@smallexample
+#define INT_ADD_OVERFLOW_P(a, b) \
+   __builtin_add_overflow_p (a, b, (__typeof__ ((a) + (b))) 0)
+
+enum @{
+    A = INT_MAX, B = 3,
+    C = INT_ADD_OVERFLOW_P (A, B) ? 0 : A + B,
+    D = __builtin_add_overflow_p (1, SCHAR_MAX, (signed char) 0)
+@};
+@end smallexample
+
+The compiler will attempt to use hardware instructions to implement
+these built-in functions where possible, like conditional jump on overflow
+after addition, conditional jump on carry etc.
+ 
+@end deftypefn
+
 @node x86 specific memory model extensions for transactional memory
 @section x86-Specific Memory Model Extensions for Transactional Memory
 


	Jakub



More information about the Gcc-patches mailing list