diff mbox

integer overflow checking builtins in constant expressions

Message ID 574FA3BC.8090603@gmail.com
State New
Headers show

Commit Message

Martin Sebor June 2, 2016, 3:10 a.m. UTC
> Unless I hear otherwise, I'll go ahead and change the patch
> to have only the type-generic built-ins accept a null pointer
> as an argument.

Since Jason doesn't think making it work in C++ 98 is important
I modified the patch to keep the Clang-compatibility built-ins
unchanged and to remove the default argument from the others.
Attached is an updated version intended to be applied on top
of the c/70883 patch.

Martin

Comments

Jakub Jelinek June 2, 2016, 7:23 a.m. UTC | #1
On Wed, Jun 01, 2016 at 09:10:52PM -0600, Martin Sebor wrote:
> @@ -7957,8 +7957,8 @@ 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);
> +  enum tree_code opcode;
> +
>    switch (fcode)
>      {
>      case BUILT_IN_ADD_OVERFLOW:
> @@ -7969,6 +7969,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
>      case BUILT_IN_UADDL_OVERFLOW:
>      case BUILT_IN_UADDLL_OVERFLOW:
>        ifn = IFN_ADD_OVERFLOW;
> +      opcode = PLUS_EXPR;
>        break;
>      case BUILT_IN_SUB_OVERFLOW:
>      case BUILT_IN_SSUB_OVERFLOW:
> @@ -7978,6 +7979,7 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
>      case BUILT_IN_USUBL_OVERFLOW:
>      case BUILT_IN_USUBLL_OVERFLOW:
>        ifn = IFN_SUB_OVERFLOW;
> +      opcode = MINUS_EXPR;
>        break;
>      case BUILT_IN_MUL_OVERFLOW:
>      case BUILT_IN_SMUL_OVERFLOW:
> @@ -7987,10 +7989,35 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
>      case BUILT_IN_UMULL_OVERFLOW:
>      case BUILT_IN_UMULLL_OVERFLOW:
>        ifn = IFN_MUL_OVERFLOW;
> +      opcode = MULT_EXPR;
>        break;
>      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 = TREE_TYPE (TREE_TYPE (arg2));
> +  bool isnullp = integer_zerop (arg2);
> +
> +  /* When the last argument 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 (isnullp
> +      && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)

Doesn't this mean you allow NULL arg2 even for BUILT_IN_ADDL_OVERFLOW and
similar clang builtins?  I'd set opcode to ERROR_MARK first, and then do:
  switch (fcode)
    {
    case BUILT_IN_ADD_OVERFLOW:
      opcode = PLUS_EXPR;
      /* FALLTHROUGH */
    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:
      ifn = IFN_ADD_OVERFLOW;
      break;
and similarly, then
  bool isnullp = opcode != ERROR_MARK ? integer_zerop (arg2) : false;
or so.

> +    {
> +      /* 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.

Furthermore, we have boolean_true_node and boolean_false_node.

Thus, I'd expect here:
    return (arith_overflowed_p (opcode, type, arg0, arg1)
	    ? boolean_true_node : boolean_false_node);

> +  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))
> +	{
> +	  if (TREE_OVERFLOW (result))
> +	    {
> +	      /* Reset TREE_OVERFLOW to avoid warnings for the overflow.  */
> +	      TREE_OVERFLOW (result) = 0;

Again, this is wrong, it should have used arith_overflowed_p.

> @@ -1270,18 +1332,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)
>      {
> 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..00435f7 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 to
> +integer 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

AFAIK the docs use either C++98, C++11 (without space), or
ISO/IEC 14882:2011 etc., but not C++ 98 or C++ 11.

Also, perhaps just a documentation thing, it would be good to clarify the
NULL last argument.  From the POV of generating efficient code, I think we
should say something that the last argument (for the GNU builtins) must be
either a pointer to a valid object, or NULL/nullptr constant cast expression
cast to a pointer type, but nothing else.  That is actually what your patch
implements.  But, I'd like to make sure that
  int *p = NULL;
  if (__builtin_add_overflow (a, b, p))
    ...
is actually not valid, otherwise we unnecessarily pessimize many of the GNU
style calls (those that don't pass &var), where instead of
  tem = ADD_OVERFLOW (a, b);
  *p = REALPART_EXPR (tem);
  ovf = IMAGPART_EXPR (tem);
we'd need to emit instead
  tem = ADD_OVERFLOW (a, b);
  ovf = IMAGPART_EXPR (tem);
  if (p != NULL)
    *p = REALPART_EXPR (tem);

	Jakub
Jakub Jelinek June 2, 2016, 7:34 a.m. UTC | #2
On Thu, Jun 02, 2016 at 09:23:16AM +0200, Jakub Jelinek wrote:
> Also, perhaps just a documentation thing, it would be good to clarify the
> NULL last argument.  From the POV of generating efficient code, I think we
> should say something that the last argument (for the GNU builtins) must be
> either a pointer to a valid object, or NULL/nullptr constant cast expression
> cast to a pointer type, but nothing else.  That is actually what your patch
> implements.  But, I'd like to make sure that
>   int *p = NULL;
>   if (__builtin_add_overflow (a, b, p))
>     ...
> is actually not valid, otherwise we unnecessarily pessimize many of the GNU
> style calls (those that don't pass &var), where instead of
>   tem = ADD_OVERFLOW (a, b);
>   *p = REALPART_EXPR (tem);
>   ovf = IMAGPART_EXPR (tem);
> we'd need to emit instead
>   tem = ADD_OVERFLOW (a, b);
>   ovf = IMAGPART_EXPR (tem);
>   if (p != NULL)
>     *p = REALPART_EXPR (tem);

Though, perhaps that is too ugly, that it has different semantics for
  __builtin_add_overflow (a, b, (int *) NULL)
and for
  int *p = NULL;
  __builtin_add_overflow (a, b, p)
Maybe the cleanest would be to just add 3 extra builtins, again,
typegeneric,
  __builtin_{add,sub,mul}_overflow_p
where either the arguments would be instead of integertype1, integertype2,
integertype3 * rather integertype1, integertype2, integertype3
and we'd only care about the type, not value, of the last argument,
so use it like __builtin_add_overflow_p (a, b, (__typeof ((a) + (b))) 0)
or handle those 3 similarly to __builtin_va_arg, and use
__builtin_add_overflow_p (a, b, int);
I think I prefer the former though.

	Jakub
Martin Sebor June 3, 2016, 3:07 p.m. UTC | #3
On 06/02/2016 01:34 AM, Jakub Jelinek wrote:
> On Thu, Jun 02, 2016 at 09:23:16AM +0200, Jakub Jelinek wrote:
>> Also, perhaps just a documentation thing, it would be good to clarify the
>> NULL last argument.  From the POV of generating efficient code, I think we
>> should say something that the last argument (for the GNU builtins) must be
>> either a pointer to a valid object, or NULL/nullptr constant cast expression
>> cast to a pointer type, but nothing else.  That is actually what your patch
>> implements.  But, I'd like to make sure that
>>    int *p = NULL;
>>    if (__builtin_add_overflow (a, b, p))
>>      ...
>> is actually not valid, otherwise we unnecessarily pessimize many of the GNU
>> style calls (those that don't pass &var), where instead of
>>    tem = ADD_OVERFLOW (a, b);
>>    *p = REALPART_EXPR (tem);
>>    ovf = IMAGPART_EXPR (tem);
>> we'd need to emit instead
>>    tem = ADD_OVERFLOW (a, b);
>>    ovf = IMAGPART_EXPR (tem);
>>    if (p != NULL)
>>      *p = REALPART_EXPR (tem);
>
> Though, perhaps that is too ugly, that it has different semantics for
>    __builtin_add_overflow (a, b, (int *) NULL)
> and for
>    int *p = NULL;
>    __builtin_add_overflow (a, b, p)
> Maybe the cleanest would be to just add 3 extra builtins, again,
> typegeneric,
>    __builtin_{add,sub,mul}_overflow_p
> where either the arguments would be instead of integertype1, integertype2,
> integertype3 * rather integertype1, integertype2, integertype3
> and we'd only care about the type, not value, of the last argument,
> so use it like __builtin_add_overflow_p (a, b, (__typeof ((a) + (b))) 0)
> or handle those 3 similarly to __builtin_va_arg, and use
> __builtin_add_overflow_p (a, b, int);
> I think I prefer the former though.

I'm not sure I understand your concern but at this point I would
prefer to keep things as they are.  I like that the functionality
that was requested in c/68120 could be provided under the existing
interface, and I'm not fond of the idea of adding yet another set
of built-ins just to get at a bit that's already available via
the existing ones (in C++ 11, even in constexpr contexts, provided
the built-ins were allowed there).  I've also spent far more time
on this patch than I had planned and I'm way behind on the tasks
I was asked to work on.

That said, in c/68120 the requester commented that he's considering
submitting a request for yet another enhancement in this area, so
I think letting him play with what we've got now for a bit will be
an opportunity to get his feedback and tweak the API further based
on it.

Martin
Jakub Jelinek June 3, 2016, 3:23 p.m. UTC | #4
On Fri, Jun 03, 2016 at 09:07:09AM -0600, Martin Sebor wrote:
> I'm not sure I understand your concern but at this point I would
> prefer to keep things as they are.  I like that the functionality

My concern is that if you declare that NULL is valid third argument for e.g.
__builtin_add_overflow, then unless we add some complicated wording into the
docs, we have to penalize the code we emit for e.g.
bool
foo (int a, int b, int *p)
{
  return __builtin_add_overflow (a, b, p);
}
While we could previously emit
	addl	%edi, %esi
	movl	%esi, (%rdx)
	seto	%al
	retq
you suddenly have to emit (and your patch doesn't do that):
	addl	%edi, %esi
	seto	%al
	testq	%rdx, %rdx
	je	.L1
	movl	%esi, (%rdx)
.L1:
	retq
because you can't at compile time prove if p is NULL (then you wouldn't
store the result) or not.

Trying to document that the third argument may be NULL, but only if it is
constant NULL pointer expression or something like that (what exactly?)
might not be very easily understandable and clear to users.

Which is why I've suggested just not allowing (like before) the third
argument to be NULL, and just add new 3 builtins for the test for overflow,
but don't store it anywhere.  They would just be folded early to the same
internal function.  And when adding the 3 new builtins, we can also choose
a different calling convention that would allow the C++98/C constant
expressions, by not having the third argument a pointer (we don't need to
dereference anything), but e.g. just any integer where we ignore the value
(well, evaluate for side-effects) and only care about the type.

	Jakub
Martin Sebor June 3, 2016, 4:22 p.m. UTC | #5
On 06/03/2016 09:23 AM, Jakub Jelinek wrote:
> On Fri, Jun 03, 2016 at 09:07:09AM -0600, Martin Sebor wrote:
>> I'm not sure I understand your concern but at this point I would
>> prefer to keep things as they are.  I like that the functionality
>
> My concern is that if you declare that NULL is valid third argument for e.g.
> __builtin_add_overflow, then unless we add some complicated wording into the
> docs,

Thanks the clarification.

> Trying to document that the third argument may be NULL, but only if it is
> constant NULL pointer expression or something like that (what exactly?)
> might not be very easily understandable and clear to users.

I think the updated wording is quite clear:

   The first of the functions accepts either a pointer to an integer
   object or a null pointer constant.

/A null pointer constant/ is a basic term defined by both C and C++
so it should be familiar to all C++ programmers.  (To make it 100%
correct we should perhaps say:

   "...or a null pointer constant cast to a pointer to integer."

even though that should be obvious since the functions won't
accept a bare NULL.)

> Which is why I've suggested just not allowing (like before) the third
> argument to be NULL, and just add new 3 builtins for the test for overflow,
> but don't store it anywhere.  They would just be folded early to the same
> internal function.  And when adding the 3 new builtins, we can also choose
> a different calling convention that would allow the C++98/C constant
> expressions, by not having the third argument a pointer (we don't need to
> dereference anything), but e.g. just any integer where we ignore the value
> (well, evaluate for side-effects) and only care about the type.

I understand what you're suggesting and it's something I could
have easily done when I started on it a few weeks ago but I'm
afraid really out of cycles to continue to tweak the patch.
I think it's good enough as it is for now, gives the requester
what they asked for and lets me finish the C++ VLA bounds
checking, which is the main reason why I did this work to begin
with.  We can revisit this idea when we get the requester's
feedback (and after I've made some headway on my pending tasks).
Does that sound reasonable?

Martin
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/testsuite/ChangeLog:
2016-06-01  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.

gcc/cp/ChangeLog:
2016-06-01  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.
	* tree.c (builtin_valid_in_constant_expr_p): Handle integer
	arithmetic overflow built-ins.

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

	PR c/68120
	(check_builtin_function_arguments): Allow type-specific integer
	arithmetic overflow built-ins to take either 2 or three arguments.

gcc/ChangeLog:
2016-06-01  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.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 931d4a6..4620553 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -7957,8 +7957,8 @@  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);
+  enum tree_code opcode;
+
   switch (fcode)
     {
     case BUILT_IN_ADD_OVERFLOW:
@@ -7969,6 +7969,7 @@  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     case BUILT_IN_UADDL_OVERFLOW:
     case BUILT_IN_UADDLL_OVERFLOW:
       ifn = IFN_ADD_OVERFLOW;
+      opcode = PLUS_EXPR;
       break;
     case BUILT_IN_SUB_OVERFLOW:
     case BUILT_IN_SSUB_OVERFLOW:
@@ -7978,6 +7979,7 @@  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     case BUILT_IN_USUBL_OVERFLOW:
     case BUILT_IN_USUBLL_OVERFLOW:
       ifn = IFN_SUB_OVERFLOW;
+      opcode = MINUS_EXPR;
       break;
     case BUILT_IN_MUL_OVERFLOW:
     case BUILT_IN_SMUL_OVERFLOW:
@@ -7987,10 +7989,35 @@  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     case BUILT_IN_UMULL_OVERFLOW:
     case BUILT_IN_UMULLL_OVERFLOW:
       ifn = IFN_MUL_OVERFLOW;
+      opcode = MULT_EXPR;
       break;
     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 = TREE_TYPE (TREE_TYPE (arg2));
+  bool isnullp = integer_zerop (arg2);
+
+  /* When the last argument 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 (isnullp
+      && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      /* 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);
+    }
+
   tree ctype = build_complex_type (type);
   tree call = build_call_expr_internal_loc (loc, ifn, ctype,
 					    2, arg0, arg1);
@@ -7998,6 +8025,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..c77e939 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1255,6 +1255,68 @@  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");
+      *non_constant_p = true;
+      return t;
+    }
+
+  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))
+	{
+	  if (TREE_OVERFLOW (result))
+	    {
+	      /* Reset TREE_OVERFLOW to avoid warnings for the overflow.  */
+	      TREE_OVERFLOW (result) = 0;
+
+	      return build_complex (TREE_TYPE (t), result, integer_one_node);
+	    }
+
+	  return build_complex (TREE_TYPE (t), result, integer_zero_node);
+	}
+    }
+
+  *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 +1332,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)
     {
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..00435f7 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 to
+integer 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..4fc58d1 100644
--- a/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
+++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
@@ -1,23 +1,133 @@ 
 /* { dg-do compile } */
 
+/* Verify that calls with fewer or more than 3 arguments to the generic
+   __builtin_op_overflow functions are rejected.  */
+
+int
+generic_0 (void)
+{
+  int x = __builtin_add_overflow ();	/* { dg-error "too few arguments to function" } */
+  x += __builtin_sub_overflow ();	/* { dg-error "too few arguments to function" } */
+  x += __builtin_mul_overflow ();	/* { dg-error "too few arguments to function" } */
+  return x;
+}
+
 int
-f1 (void)
+generic_1 (int a)
 {
-  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" } */
+  int x = __builtin_add_overflow (a);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_sub_overflow (a);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_mul_overflow (a);	/* { dg-error "too few arguments to function" } */
+
+  /* Literal argument.  */
+  x += __builtin_add_overflow (1);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_sub_overflow (2);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_mul_overflow (3);	/* { dg-error "too few arguments to function" } */
   return x;
 }
 
 int
-f2 (int a, int b, int *c, int d)
+generic_2 (int a, int b)
+{
+  int x = __builtin_add_overflow (a, b);/* { dg-error "too few arguments to function" } */
+  x += __builtin_sub_overflow (a, b);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_mul_overflow (a, b);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_add_overflow (a, 1);   /* { dg-error "too few arguments to function" } */
+  x += __builtin_sub_overflow (a, 2);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_mul_overflow (a, 3);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_add_overflow (4, b);   /* { dg-error "too few arguments to function" } */
+  x += __builtin_sub_overflow (5, b);	/* { dg-error "too few arguments to function" } */
+  x += __builtin_mul_overflow (6, b);	/* { dg-error "too few 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 "too few arguments to function" } */
+  x += __builtin_sub_overflow ();	/* { dg-error "too few arguments to function" } */
+  x += __builtin_mul_overflow ();	/* { dg-error "too few 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..66e0388
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c
@@ -0,0 +1,499 @@ 
+/* 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) ((a) + (b))
+#define sadd(x, y) ((a) + (b))
+#define saddl(x, y) ((a) + (b))
+#define saddll(x, y) ((a) + (b))
+#define uadd(x, y) ((a) + (b))
+#define uaddl(x, y) ((a) + (b))
+#define uaddll(x, y) ((a) + (b))
+#define sub(x, y) ((a) - (b))
+#define ssub(x, y) ((a) - (b))
+#define ssubl(x, y) ((a) - (b))
+#define ssubll(x, y) ((a) - (b))
+#define usub(x, y) ((a) - (b))
+#define usubl(x, y) ((a) - (b))
+#define usubll(x, y) ((a) - (b))
+#define mul(x, y) ((a) * (b))
+#define smul(x, y) ((a) * (b))
+#define smull(x, y) ((a) * (b))
+#define smulll(x, y) ((a) * (b))
+#define umul(x, y) ((a) * (b))
+#define umull(x, y) ((a) * (b))
+#define umulll(x, y) ((a) * (b))
+
+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, T, 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, T, 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);
+
+  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
+     yields a constant expression equal to z indicating whether or not
+     the constant expression (x op y) overflows.  The type-specific
+     overloads detect overflow after arithmetic promotions and so unlike
+     the type-generic overloads cannot detect overflow in char or short
+     types.  */
+
+#if !__cplusplus || __cplusplus >= 201103L
+  /* Perform both a run-time test followed by a compile-time test.  */
+#  define T_TEST(op, T, x, y, vflow)					\
+  RuntimeAssert(op, T, decltype ((x) + (y)), x, y, vflow);		\
+  StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((x), (y),	\
+			       (decltype ((x) + (y))*)0))
+#else
+  /* C++ 98 doesn't permit casts to pointer types to appear in constant
+     expressions.  Only perform the run-time test.  */
+#  define T_TEST(op, T, x, y, vflow)		\
+  RuntimeAssert (op, T, __typeof__ ((x) + (y)), x, y, vflow)
+#endif
+
+  /* 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..93e26e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C
@@ -0,0 +1,115 @@ 
+// 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