diff mbox

integer overflow checking builtins in constant expressions

Message ID 5750CF25.1080801@gmail.com
State New
Headers show

Commit Message

Martin Sebor June 3, 2016, 12:28 a.m. UTC
>>> +    {
>>> +      /* Perform the computation in the target type and check for
>>> overflow.  */
>>> +      arg0 = fold_convert (type, arg0);
>>> +      arg1 = fold_convert (type, arg1);
>>> +
>>> +      if (tree result = size_binop_loc (loc, opcode, arg0, arg1))
>>> +    return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node)
>>> +                                  : build_zero_cst (boolean_type_node);
>>
>> This is wrong, it is not the computation of overflow that is intended.
>> The documentation says that we compute
>> (infinite_precision_signed_type) arg0
>> op (infinite_precision_signed_type) arg1 and then cast it to type, extend
>> again to infinite_precision_signed_type and compare.  And we have a
>> helper
>> function for that already.
>
> Thank you for the suggestion.  Using the helper instead is simpler
> (though my tests don't show any difference in the computed result
> so I'm not sure I see in what way the original code was wrong).
...
>>
>> Again, this is wrong, it should have used arith_overflowed_p.
>
> No.  As is apparent from the rest of hunk, we need the result or
> the computation here, not just the overflow bit.

It just dawned on me what the problem was and why both places
need arith_overflowed_p:  Converting the arguments to the result
type and doing the arithmetic and checking for overflow in it
would mask it in cases like:

   __builtin_add_overflow (SCHAR_MAX + 1, 1, (signed char*)0)

I've fixed it in the attached patch (and added test cases to
verify it).

Martin

Comments

Jakub Jelinek June 3, 2016, 7:06 a.m. UTC | #1
Hi!

On Thu, Jun 02, 2016 at 06:28:21PM -0600, Martin Sebor wrote:

First of all, can you please respond to the mail I've sent about
NULL argument issues (and proposal for __builtin_*_overflow_p)?

This patch as well as the nonnull attribute patch then depends on that
decision...

> +	{
> +	  tree type = TREE_TYPE (TREE_TYPE (t));
> +	  tree vflow = arith_overflowed_p (opcode, type, arg0, arg1)
> +	               ? integer_one_node : integer_zero_node;

This looks incorrect, the return type is TREE_TYPE (t), some complex integer
type, therefore vflow needs to be
      tree vflow = build_int_cst (TREE_TYPE (TREE_TYPE (t)),
				  arith_overflowed_p (opcode, type, arg0, arg1)
				  ? 1 : 0);
no?

	Jakub
Martin Sebor June 3, 2016, 3:29 p.m. UTC | #2
>> +	{
>> +	  tree type = TREE_TYPE (TREE_TYPE (t));
>> +	  tree vflow = arith_overflowed_p (opcode, type, arg0, arg1)
>> +	               ? integer_one_node : integer_zero_node;
>
> This looks incorrect, the return type is TREE_TYPE (t), some complex integer
> type, therefore vflow needs to be
>        tree vflow = build_int_cst (TREE_TYPE (TREE_TYPE (t)),
> 				  arith_overflowed_p (opcode, type, arg0, arg1)
> 				  ? 1 : 0);
> no?

I guess it didn't think it mattered since the complex type specifies
the types of the two members.  I don't mind changing it if it does
but I'd like to have a test case to go with it.  Maybe Jason can
help with that.

Martin
Jakub Jelinek June 3, 2016, 3:32 p.m. UTC | #3
On Fri, Jun 03, 2016 at 09:29:48AM -0600, Martin Sebor wrote:
> >>+	{
> >>+	  tree type = TREE_TYPE (TREE_TYPE (t));
> >>+	  tree vflow = arith_overflowed_p (opcode, type, arg0, arg1)
> >>+	               ? integer_one_node : integer_zero_node;
> >
> >This looks incorrect, the return type is TREE_TYPE (t), some complex integer
> >type, therefore vflow needs to be
> >       tree vflow = build_int_cst (TREE_TYPE (TREE_TYPE (t)),
> >				  arith_overflowed_p (opcode, type, arg0, arg1)
> >				  ? 1 : 0);
> >no?
> 
> I guess it didn't think it mattered since the complex type specifies
> the types of the two members.  I don't mind changing it if it does

Sure, it does.  But if there are any differences between the lhs and rhs
type (or e.g. in COMPLEX_EXPR args etc. in GENERIC), then it is invalid IL,
or for GIMPLE if the two types aren't compatible according to the GIMPLE
rules (useless conversion).

	Jakub
diff mbox

Patch

PR c++/70507 - integer overflow builtins not constant expressions
PR c/68120 - can't easily deal with integer overflow at compile time

gcc/cp/ChangeLog:
2016-06-02  Martin Sebor  <msebor@redhat.com>

	PR c++/70507
	PR c/68120
	* constexpr.c (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): Same.

gcc/ChangeLog:
2016-06-02  Martin Sebor  <msebor@redhat.com>

	PR c++/70507
	PR c/68120
	* builtins.c (fold_builtin_unordered_cmp): Handle integer arithmetic
	overflow built-ins.
	* doc/extend.texi (Integer Overflow Builtins): Update.

gcc/testsuite/ChangeLog:
2016-06-02  Martin Sebor  <msebor@redhat.com>

	PR c++/70507
	PR c/68120
	* 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.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 931d4a6..ada1904 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -64,6 +64,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "rtl-chkp.h"
 #include "internal-fn.h"
 #include "case-cfn-macros.h"
+#include "gimple-fold.h"
 
 
 struct target_builtins default_target_builtins;
@@ -7957,11 +7958,14 @@  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;
+
   switch (fcode)
     {
     case BUILT_IN_ADD_OVERFLOW:
+      opcode = PLUS_EXPR;
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
@@ -7971,6 +7975,7 @@  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
       ifn = IFN_ADD_OVERFLOW;
       break;
     case BUILT_IN_SUB_OVERFLOW:
+      opcode = MINUS_EXPR;
     case BUILT_IN_SSUB_OVERFLOW:
     case BUILT_IN_SSUBL_OVERFLOW:
     case BUILT_IN_SSUBLL_OVERFLOW:
@@ -7980,6 +7985,7 @@  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
       ifn = IFN_SUB_OVERFLOW;
       break;
     case BUILT_IN_MUL_OVERFLOW:
+      opcode = MULT_EXPR;
     case BUILT_IN_SMUL_OVERFLOW:
     case BUILT_IN_SMULL_OVERFLOW:
     case BUILT_IN_SMULLL_OVERFLOW:
@@ -7991,6 +7997,26 @@  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     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.  */
+  bool isnullp = integer_zerop (arg2);
+  tree type = TREE_TYPE (TREE_TYPE (arg2));
+
+  /* When the last argument to the type-generic built-in is a null pointer
+     and 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 but without storing the result.  */
+  if (opcode != ERROR_MARK && isnullp
+      && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      /* Perform the computation in the target type and check for overflow.  */
+      return (arith_overflowed_p (opcode, type, arg0, arg1)
+	      ? boolean_true_node : boolean_false_node);
+    }
+
   tree ctype = build_complex_type (type);
   tree call = build_call_expr_internal_loc (loc, ifn, ctype,
 					    2, arg0, arg1);
@@ -7998,6 +8024,11 @@  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
   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 (isnullp)
+    return ovfres;
+
+  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);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 482f8af..301efa78 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #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,70 @@  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)
+    {
+      if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location),
+					opcode, arg0, arg1))
+	{
+	  tree type = TREE_TYPE (TREE_TYPE (t));
+	  tree vflow = arith_overflowed_p (opcode, type, arg0, arg1)
+	               ? integer_one_node : integer_zero_node;
+
+	  /* Reset TREE_OVERFLOW to avoid warnings for the overflow.  */
+	  if (TREE_OVERFLOW (result))
+	    TREE_OVERFLOW (result) = 0;
+
+	  return build_complex (TREE_TYPE (t), result, vflow);
+	}
+    }
+
+  *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 +1335,8 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
   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 +4643,10 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 
 	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 +4657,27 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 		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 +4722,7 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 	      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.  */;
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 04702ee..f9db199 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -352,6 +352,32 @@  builtin_valid_in_constant_expr_p (const_tree decl)
     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_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_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_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:
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 2d4f028..d880821 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -9737,7 +9737,10 @@  compiler may also ignore this parameter.
 @section Built-in Functions to Perform Arithmetic with Overflow Checking
 
 The following built-in functions allow performing simple arithmetic operations
-together with checking whether the operations overflowed.
+together with checking whether the operations overflowed.  The first of the
+functions accepts either a pointer to an integer object or a null pointer
+constant cast to the appropriate type as the last argument.  The rest require
+a pointer to an object of the specified type as the last argument.
 
 @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a, @var{type2} b, @var{type3} *res)
 @deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res)
@@ -9747,21 +9750,50 @@  together with checking whether the operations overflowed.
 @deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
 @deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res)
 
-These 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 pointer argument points to and stored there.
-If the stored result is equal to the infinite precision result, the built-in
-functions return false, otherwise they return true.  As the addition is
-performed in infinite signed precision, these built-in functions have fully defined
-behavior for all argument values.
-
-The first built-in function allows arbitrary integral types for operands and
-the result type must be pointer to some integer type, the rest of the built-in
-functions have explicit integer types.
+These built-in functions promote the first two operands into infinite precision
+signed type and perform addition on those promoted operands.  The result is then
+converted to the type the third pointer argument points to, and for the first
+function when the pointer is not null, stored there.  If the converted result
+is equal to the infinite precision result, the built-in functions return
+@code{false}, otherwise they return @code{true} to indicate that an overflow
+has been detected.  Because the addition is performed in infinite precision,
+these built-in functions have fully defined behavior for all argument values
+and integer types.
+
+The first type-generic built-in function allows arbitrary integer types as
+the first two arguments and requires that a pointer to some possibly distinct
+integer type be passed to it as the third argument.  The pointed-to type is
+then used to determine the overflow.  As a result, this built-in function
+can be used to detect overflow in any arbitrary integer type, including
+@code{char} and @code{short}.  The remaining built-in functions take
+arguments of explicit integer types and make it possible to determine
+overflow only in the ranges of those types.  Since the only provided forms
+of these latter built-in functions are for the signed and unsigned variants
+of types @code{int}, @code{long}, and @code{long long}, they cannot be used
+to determine overflow in other integer types.
+
+To enable the efficient integer overflow detection at translation-time,
+in C and C++11 and later programs (but not in C++98), the first built-in
+may be invoked in constant integer expression contexts with a null pointer
+cast to a pointer to the appropriate integer type as the third 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(a, b) \
+   __builtin_add_overflow (a, b, (__typeof__ ((a) + (b)) *) 0)
+
+enum @{
+    A = INT_MAX, B = 3,
+    C = INT_ADD_OVERFLOW (A, B) ? 0 : A + B
+@};
+@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.
+For invocations of the built-in functions evaluated at run-time the compiler
+will attempt to make use of efficient hardware instructions such as conditional
+jump on overflow after addition, conditional jump on carry, etc.
 
 @end deftypefn
 
@@ -9773,9 +9805,9 @@  after addition, conditional jump on carry etc.
 @deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
 @deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res)
 
-These built-in functions are similar to the add overflow checking built-in
-functions above, except they perform subtraction, subtract the second argument
-from the first one, instead of addition.
+These built-in functions are analogous to the add overflow checking built-in
+functions above, except they subtract the second argument from the first one
+rather than adding it to it.
 
 @end deftypefn
 
@@ -9787,8 +9819,8 @@  from the first one, instead of addition.
 @deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res)
 @deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res)
 
-These built-in functions are similar to the add overflow checking built-in
-functions above, except they perform multiplication, instead of addition.
+These built-in functions are analogous to the add overflow checking built-in
+functions above, except they perform multiplication rather than addition.
 
 @end deftypefn
 
diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
index 69b5083..f2be467 100644
--- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
+++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
@@ -1,7 +1,10 @@ 
 /* { dg-do compile } */
 
+/* Verify that calls with fewer or more than 3 arguments to the generic
+   __builtin_op_overflow functions are rejected.  */
+
 int
-f1 (void)
+generic_0 (void)
 {
   int x = __builtin_add_overflow ();	/* { dg-error "not enough arguments to function" } */
   x += __builtin_sub_overflow ();	/* { dg-error "not enough arguments to function" } */
@@ -10,14 +13,121 @@  f1 (void)
 }
 
 int
-f2 (int a, int b, int *c, int d)
+generic_1 (int a)
+{
+  int x = __builtin_add_overflow (a);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (a);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (a);	/* { dg-error "not enough arguments to function" } */
+
+  /* Literal argument.  */
+  x += __builtin_add_overflow (1);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (2);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (3);	/* { dg-error "not enough arguments to function" } */
+  return x;
+}
+
+int
+generic_2 (int a, int b)
+{
+  int x = __builtin_add_overflow (a, b);/* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (a, b);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (a, b);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_add_overflow (a, 1);   /* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (a, 2);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (a, 3);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_add_overflow (4, b);   /* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow (5, b);	/* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow (6, b);	/* { dg-error "not enough arguments to function" } */
+  return x;
+}
+
+/* Verify that calls with the correct number of arguments to the generic
+   __builtin_op_overflow functions are accepted.  */
+
+int
+generic_3 (int a, int b, int c)
+{
+  int x = __builtin_add_overflow (a, b, &c);
+  x += __builtin_sub_overflow (a, b, &c);
+  x += __builtin_mul_overflow (a, b, &c);
+  x += __builtin_add_overflow (a, 1, &c);
+  x += __builtin_sub_overflow (a, 2, &c);
+  x += __builtin_mul_overflow (a, 3, &c);
+  x += __builtin_add_overflow (4, b, &c);
+  x += __builtin_sub_overflow (5, b, &c);
+  x += __builtin_mul_overflow (6, b, &c);
+  x += __builtin_add_overflow (7, 8, &c);
+  x += __builtin_sub_overflow (9, 10, &c);
+  x += __builtin_mul_overflow (11, 12, &c);
+  return x;
+}
+
+int
+generic_4 (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_sub_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
   x += __builtin_mul_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
   return x;
 }
 
+/* Verify that calls with fewer or more than 3 arguments to the type
+   specific forms of the __builtin_op_overflow functions are rejected.  */
+
+int
+generic_wrong_type (int a, int b)
+{
+  void *p = 0;
+  double d = 0;
+  int x = __builtin_add_overflow (a, b, p);   /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_sub_overflow (a, b, &p);     /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_mul_overflow (a, b, &d);     /* { dg-error "does not have pointer to integer type" } */
+
+  /* Also verify literal arguments.  */
+  x += __builtin_add_overflow (1, 1, p);   /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_sub_overflow (1, 1, &p);     /* { dg-error "does not have pointer to integer type" } */
+  x += __builtin_mul_overflow (1, 1, &d);     /* { dg-error "does not have pointer to integer type" } */
+  return x;
+}
+
+/* Verify that calls with fewer than 2 or more than 3 arguments to
+   the typed __builtin_op_overflow functions are rejected.  */
+int
+typed_0 (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" } */
+  return x;
+}
+
+int
+typed_1 (int a)
+{
+  int x = __builtin_sadd_overflow (a);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_ssub_overflow (a);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_smul_overflow (a);	/* { dg-error "too few arguments to function" } */
+  return x;
+}
+
+int
+typed_2 (int a, int b)
+{
+  int x = __builtin_sadd_overflow (a, b);  /* { dg-error "too few arguments to function" } */
+  x += __builtin_ssub_overflow (a, b);	   /* { dg-error "too few arguments to function" } */
+  x += __builtin_smul_overflow (a, b);	   /* { dg-error "too few arguments to function" } */
+  return x;
+}
+
+int
+typed_4 (int a, int b, int *c, int d)
+{
+  int x = __builtin_sadd_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_ssub_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
+  x += __builtin_smul_overflow (a, b, c, d);	/* { dg-error "too many arguments to function" } */
+  return x;
+}
+
 enum E { e0 = 0, e1 = 1 };
 
 #ifndef __cplusplus
diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c
new file mode 100644
index 0000000..8707416
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c
@@ -0,0 +1,501 @@ 
+/* 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.  */
+#if !__cplusplus || __cplusplus >= 201103L
+  /* Perform both a run-time test followed by a compile-time test.  */
+#  define G_TEST(op, T, x, y, vflow)					\
+  RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow);		\
+  StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y), (T*)0))
+#else
+  /* C++ 98 doesn't permit casts to pointer types to appear in constant
+     expressions.  Only perform the run-time test.  */
+#  define G_TEST(op, T, x, y, vflow)				\
+  RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow)
+#endif
+
+  /* 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;
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C
new file mode 100644
index 0000000..ec7e5a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C
@@ -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 (      1, 1, (int*)0),
+  a1 = __builtin_add_overflow (INT_MAX, 1, (int*)0)
+};
+
+Assert (a0 == 0);
+Assert (a1 == 1);
+
+enum Sub {
+  s0 = __builtin_sub_overflow (      1, 1, (int*)0),
+  s1 = __builtin_sub_overflow (INT_MIN, 1, (int*)0)
+};
+
+Assert (s0 == 0);
+Assert (s1 == 1);
+
+enum Mul {
+  m0 = __builtin_add_overflow (      1,       1, (int*)0),
+  m1 = __builtin_add_overflow (INT_MAX, INT_MAX, (int*)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 (x, y, (V*)0)>
+struct Add {
+  Assert (z == v);
+};
+
+template <class T, class U, class V,
+	  T x, U y, bool v, bool z = __builtin_sub_overflow (x, y, (V*)0)>
+struct Sub {
+  Assert (z == v);
+};
+
+template <class T, class U, class V,
+	  T x, U y, bool v, bool z = __builtin_mul_overflow (x, y, (V*)0)>
+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 (a, b, (int*)0) };
+#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
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C
new file mode 100644
index 0000000..7ca0033
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C
@@ -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);