diff mbox

integer overflow checking builtins in constant expressions

Message ID 57263154.5080401@gmail.com
State New
Headers show

Commit Message

Martin Sebor May 1, 2016, 4:39 p.m. UTC
c/68120 - can't easily deal with integer overflow at compile time,
is an enhancement request to make the integer overflow intrinsics
usable in constant expressions in C (in addition to letting them
be invoked with just two arguments).

The inability to use the built-ins in constant expressions also
limited to non-constexpr the contexts in which the patch for c++/
69517 - SEGV on a VLA with excess initializer elements, was able
to prevent the SEGV.  This limitation is noted in c++/70507 -
integer overflow builtins not constant expressions.

The attached patch implements the request in c/68120 for both
the C and C++ front-ends.  It stops short of providing the new
__builtin_add_wrapv function requested in c/68120.  It doesn't
seem necessary since the same functionality is available with
the patch via the existing built-ins.

With this enhancement in place it will be possible to add the
C++ VLA checking to constexpr functions and fully resolve c++/
69517 (which I plan to do next).

While testing the patch, I also noticed an minor inconsistency
in the text of the diagnostic GCC issues for invalid calls to
the built-ins with insufficient numbers of arguments:  for one
set of built-ins the error says: "not enough arguments," while
for another it says: "too few arguments."  I raised PR c/70883
- inconsistent error message for calls to __builtin_add_overflow
with too few arguments, for this and include a fix in this patch
as well.

Martin

PS The enhancement to call the type-generic built-ins with a null
pointer is not available in C++ 98 mode because GCC doesn't allow
null pointers in constant expressions.  Since C and later versions
of C++ do, it seems that it might be worthwhile to relax the rules
and accept them in C++ 98 as well so that the built-ins can be used
portably across all versions of C++.

Comments

Martin Sebor May 9, 2016, 4:38 p.m. UTC | #1
Pinging the following patch:
   https://gcc.gnu.org/ml/gcc-patches/2016-05/msg00013.html

On 05/01/2016 10:39 AM, Martin Sebor wrote:
> c/68120 - can't easily deal with integer overflow at compile time,
> is an enhancement request to make the integer overflow intrinsics
> usable in constant expressions in C (in addition to letting them
> be invoked with just two arguments).
>
> The inability to use the built-ins in constant expressions also
> limited to non-constexpr the contexts in which the patch for c++/
> 69517 - SEGV on a VLA with excess initializer elements, was able
> to prevent the SEGV.  This limitation is noted in c++/70507 -
> integer overflow builtins not constant expressions.
>
> The attached patch implements the request in c/68120 for both
> the C and C++ front-ends.  It stops short of providing the new
> __builtin_add_wrapv function requested in c/68120.  It doesn't
> seem necessary since the same functionality is available with
> the patch via the existing built-ins.
>
> With this enhancement in place it will be possible to add the
> C++ VLA checking to constexpr functions and fully resolve c++/
> 69517 (which I plan to do next).
>
> While testing the patch, I also noticed an minor inconsistency
> in the text of the diagnostic GCC issues for invalid calls to
> the built-ins with insufficient numbers of arguments:  for one
> set of built-ins the error says: "not enough arguments," while
> for another it says: "too few arguments."  I raised PR c/70883
> - inconsistent error message for calls to __builtin_add_overflow
> with too few arguments, for this and include a fix in this patch
> as well.
>
> Martin
>
> PS The enhancement to call the type-generic built-ins with a null
> pointer is not available in C++ 98 mode because GCC doesn't allow
> null pointers in constant expressions.  Since C and later versions
> of C++ do, it seems that it might be worthwhile to relax the rules
> and accept them in C++ 98 as well so that the built-ins can be used
> portably across all versions of C++.
>
Martin Sebor May 16, 2016, 7:29 p.m. UTC | #2
Ping 2 of the following patch:
   https://gcc.gnu.org/ml/gcc-patches/2016-05/msg00013.html

On 05/09/2016 10:38 AM, Martin Sebor wrote:
> Pinging the following patch:
>    https://gcc.gnu.org/ml/gcc-patches/2016-05/msg00013.html
>
> On 05/01/2016 10:39 AM, Martin Sebor wrote:
>> c/68120 - can't easily deal with integer overflow at compile time,
>> is an enhancement request to make the integer overflow intrinsics
>> usable in constant expressions in C (in addition to letting them
>> be invoked with just two arguments).
>>
>> The inability to use the built-ins in constant expressions also
>> limited to non-constexpr the contexts in which the patch for c++/
>> 69517 - SEGV on a VLA with excess initializer elements, was able
>> to prevent the SEGV.  This limitation is noted in c++/70507 -
>> integer overflow builtins not constant expressions.
>>
>> The attached patch implements the request in c/68120 for both
>> the C and C++ front-ends.  It stops short of providing the new
>> __builtin_add_wrapv function requested in c/68120.  It doesn't
>> seem necessary since the same functionality is available with
>> the patch via the existing built-ins.
>>
>> With this enhancement in place it will be possible to add the
>> C++ VLA checking to constexpr functions and fully resolve c++/
>> 69517 (which I plan to do next).
>>
>> While testing the patch, I also noticed an minor inconsistency
>> in the text of the diagnostic GCC issues for invalid calls to
>> the built-ins with insufficient numbers of arguments:  for one
>> set of built-ins the error says: "not enough arguments," while
>> for another it says: "too few arguments."  I raised PR c/70883
>> - inconsistent error message for calls to __builtin_add_overflow
>> with too few arguments, for this and include a fix in this patch
>> as well.
>>
>> Martin
>>
>> PS The enhancement to call the type-generic built-ins with a null
>> pointer is not available in C++ 98 mode because GCC doesn't allow
>> null pointers in constant expressions.  Since C and later versions
>> of C++ do, it seems that it might be worthwhile to relax the rules
>> and accept them in C++ 98 as well so that the built-ins can be used
>> portably across all versions of C++.
>>
>
Jason Merrill May 17, 2016, 6:54 p.m. UTC | #3
On 05/01/2016 12:39 PM, Martin Sebor wrote:
> +  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);
> +	}
> +    }

Should this be in the middle-end somewhere, perhaps shared with 
fold_builtin_arith_overflow?  I notice that the comment for that 
function says that it folds into normal arithmetic if the operation can 
never overflow, but I don't see any code that would accomplish that.

Jason
Martin Sebor May 31, 2016, 8:48 p.m. UTC | #4
On 05/17/2016 12:54 PM, Jason Merrill wrote:
> On 05/01/2016 12:39 PM, Martin Sebor wrote:
>> +  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);
>> +    }
>> +    }
>
> Should this be in the middle-end somewhere, perhaps shared with
> fold_builtin_arith_overflow?  I notice that the comment for that
> function says that it folds into normal arithmetic if the operation can
> never overflow, but I don't see any code that would accomplish that.

I'm not quite sure where to move this hunk so that it could be
shared or with what.

With the patch, fold_builtin_arith_overflow returns the overflow
bit alone when the last argument is null.  Otherwise it returns
a complex number with both the overflow bit and the result.  Here
we need both parts of the result.  I suppose I could factor out
the call to size_binop_loc into its own function, have it return
the overflow bit, and set a by-reference argument to the arithmetic
result so that it could be built into a complex result here but
that doesn't seem like it would gain us much, if anything.

Do you have any suggestions?

Martin
Jason Merrill May 31, 2016, 9:31 p.m. UTC | #5
On 05/31/2016 04:48 PM, Martin Sebor wrote:
> On 05/17/2016 12:54 PM, Jason Merrill wrote:
>> On 05/01/2016 12:39 PM, Martin Sebor wrote:
>>> +  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);
>>> +    }
>>> +    }
>>
>> Should this be in the middle-end somewhere, perhaps shared with
>> fold_builtin_arith_overflow?  I notice that the comment for that
>> function says that it folds into normal arithmetic if the operation can
>> never overflow, but I don't see any code that would accomplish that.
>
> I'm not quite sure where to move this hunk so that it could be
> shared or with what.
>
> With the patch, fold_builtin_arith_overflow returns the overflow
> bit alone when the last argument is null.  Otherwise it returns
> a complex number with both the overflow bit and the result.  Here
> we need both parts of the result.  I suppose I could factor out
> the call to size_binop_loc into its own function, have it return
> the overflow bit, and set a by-reference argument to the arithmetic
> result so that it could be built into a complex result here but
> that doesn't seem like it would gain us much, if anything.

Yeah, I'm not sure what I was thinking.  The patch is OK.

Jason
Jakub Jelinek May 31, 2016, 9:44 p.m. UTC | #6
On Tue, May 31, 2016 at 05:31:48PM -0400, Jason Merrill wrote:
> >I'm not quite sure where to move this hunk so that it could be
> >shared or with what.
> >
> >With the patch, fold_builtin_arith_overflow returns the overflow
> >bit alone when the last argument is null.  Otherwise it returns
> >a complex number with both the overflow bit and the result.  Here
> >we need both parts of the result.  I suppose I could factor out
> >the call to size_binop_loc into its own function, have it return
> >the overflow bit, and set a by-reference argument to the arithmetic
> >result so that it could be built into a complex result here but
> >that doesn't seem like it would gain us much, if anything.
> 
> Yeah, I'm not sure what I was thinking.  The patch is OK.

Sorry for not paying attention, but I think it is wrong to change the clang
compatibility builtins at all.  They are provided for clang compatibility,
nothing else, therefore we shouldn't change anything on them.
It is reasonable to extend the GNU builtins.

	Jakub
Jakub Jelinek May 31, 2016, 9:50 p.m. UTC | #7
On Sun, May 01, 2016 at 10:39:48AM -0600, Martin Sebor wrote:
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -7878,50 +7878,101 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
>  
>  static tree
>  fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
> -			     tree arg0, tree arg1, tree arg2)
> +			     tree arg0, tree arg1, tree arg2 = NULL_TREE)
>  {
>    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;
> +
> +  /* 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 names of each of the other overloads determines
> +     the target type and so the last argument can be omitted.  */
> +  tree type = NULL_TREE;
> +
>    switch (fcode)
>      {
>      case BUILT_IN_ADD_OVERFLOW:
> +      type = type ? type : TREE_TYPE (TREE_TYPE (arg2));
>      case BUILT_IN_SADD_OVERFLOW:
> +      type = type ? type : integer_type_node;
>      case BUILT_IN_SADDL_OVERFLOW:
> +      type = type ? type : long_integer_type_node;
>      case BUILT_IN_SADDLL_OVERFLOW:
> +      type = type ? type : long_long_integer_type_node;
>      case BUILT_IN_UADD_OVERFLOW:
> +      type = type ? type : unsigned_type_node;
>      case BUILT_IN_UADDL_OVERFLOW:
> +      type = type ? type : long_unsigned_type_node;
>      case BUILT_IN_UADDLL_OVERFLOW:
> +      type = type ? type : long_long_unsigned_type_node;
>        ifn = IFN_ADD_OVERFLOW;
> +      opcode = PLUS_EXPR;

...

I fail to understand this.  You set type to NULL_TREE, and then (not in any
kind of loop, nor any label you could goto to) do:
  type = type ? type : xxx;
That is necessarily equal to type = xxx;, isn't it?
>        break;
>      case BUILT_IN_SUB_OVERFLOW:
> +      type = type ? type : TREE_TYPE (TREE_TYPE (arg2));
>      case BUILT_IN_SSUB_OVERFLOW:
> +      type = type ? type : integer_type_node;
>      case BUILT_IN_SSUBL_OVERFLOW:
> +      type = type ? type : long_integer_type_node;
>      case BUILT_IN_SSUBLL_OVERFLOW:
> +      type = type ? type : long_long_integer_type_node;
>      case BUILT_IN_USUB_OVERFLOW:
> +      type = type ? type : unsigned_type_node;
>      case BUILT_IN_USUBL_OVERFLOW:
> +      type = type ? type : long_unsigned_type_node;
>      case BUILT_IN_USUBLL_OVERFLOW:
> +      type = type ? type : long_long_unsigned_type_node;
>        ifn = IFN_SUB_OVERFLOW;
> +      opcode = MINUS_EXPR;
>        break;
>      case BUILT_IN_MUL_OVERFLOW:
> +      type = type ? type : TREE_TYPE (TREE_TYPE (arg2));
>      case BUILT_IN_SMUL_OVERFLOW:
> +      type = type ? type : integer_type_node;
>      case BUILT_IN_SMULL_OVERFLOW:
> +      type = type ? type : long_integer_type_node;
>      case BUILT_IN_SMULLL_OVERFLOW:
> +      type = type ? type : long_long_integer_type_node;
>      case BUILT_IN_UMUL_OVERFLOW:
> +      type = type ? type : unsigned_type_node;
>      case BUILT_IN_UMULL_OVERFLOW:
> +      type = type ? type : long_unsigned_type_node;
>      case BUILT_IN_UMULLL_OVERFLOW:
> +      type = type ? type : long_long_unsigned_type_node;
>        ifn = IFN_MUL_OVERFLOW;
> +      opcode = MULT_EXPR;
>        break;
>      default:
>        gcc_unreachable ();
>      }
> +
> +  /* 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 ((!arg2 || zerop (arg2))

As I said in another mail, IMNSHO you don't want to change the clang
compatibility builtins, therefore arg2 should never be NULL.
As it is a pointer, shouldn't the above be integer_zerop instead?

Regarding the varargs vs. named args only different diagnostics, if you
think it should change, then IMHO you should submit as independent patch
a change to the diagnostics wording, so that the wording is the same, rather
than changing functions from fixed arguments to varargs (/ typegeneric).

	Jakub
Martin Sebor May 31, 2016, 10:44 p.m. UTC | #8
On 05/31/2016 03:50 PM, Jakub Jelinek wrote:
> On Sun, May 01, 2016 at 10:39:48AM -0600, Martin Sebor wrote:
>> --- a/gcc/builtins.c
>> +++ b/gcc/builtins.c
>> @@ -7878,50 +7878,101 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
>>
>>   static tree
>>   fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
>> -			     tree arg0, tree arg1, tree arg2)
>> +			     tree arg0, tree arg1, tree arg2 = NULL_TREE)
>>   {
>>     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;
>> +
>> +  /* 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 names of each of the other overloads determines
>> +     the target type and so the last argument can be omitted.  */
>> +  tree type = NULL_TREE;
>> +
>>     switch (fcode)
>>       {
>>       case BUILT_IN_ADD_OVERFLOW:
>> +      type = type ? type : TREE_TYPE (TREE_TYPE (arg2));
>>       case BUILT_IN_SADD_OVERFLOW:
>> +      type = type ? type : integer_type_node;
>>       case BUILT_IN_SADDL_OVERFLOW:
>> +      type = type ? type : long_integer_type_node;
>>       case BUILT_IN_SADDLL_OVERFLOW:
>> +      type = type ? type : long_long_integer_type_node;
>>       case BUILT_IN_UADD_OVERFLOW:
>> +      type = type ? type : unsigned_type_node;
>>       case BUILT_IN_UADDL_OVERFLOW:
>> +      type = type ? type : long_unsigned_type_node;
>>       case BUILT_IN_UADDLL_OVERFLOW:
>> +      type = type ? type : long_long_unsigned_type_node;
>>         ifn = IFN_ADD_OVERFLOW;
>> +      opcode = PLUS_EXPR;
>
> ...
>
> I fail to understand this.  You set type to NULL_TREE, and then (not in any
> kind of loop, nor any label you could goto to) do:
>    type = type ? type : xxx;
> That is necessarily equal to type = xxx;, isn't it?

Each label falls through to the next, so the test prevents
the subsequent labels from overwriting the value assigned
to type by the label that matched the switch expression.
I'm not exactly enamored with these repetitive tests but
the only alternative that occurred to me was to duplicate
the whole switch statement, and this seemed like the lesser
of the two evils.  If you have a suggestion for something
better I'd be happy to change it.

>> +  /* 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 ((!arg2 || zerop (arg2))
>
> As I said in another mail, IMNSHO you don't want to change the clang
> compatibility builtins, therefore arg2 should never be NULL.

Allowing the last argument to be null is the subject of
the enhancement request in c/68120 (one of the two PRs
driving this enhancement).  The argument can only be null
for these overloads and not the type-generic ones because
the latter use the type to which the pointer points to
determine the type in which to do the arithmetic.  Without
this change, C or C++ 98 users wouldn't be able to use the
built-ins in constant expressions (C++ 98 doesn't allow
casting a null pointer in those contexts).

> As it is a pointer, shouldn't the above be integer_zerop instead?

I suspect I didn't use integer_zerop because the argument
is a pointer and not integer, but I'll change it before
I commit the patch.

> Regarding the varargs vs. named args only different diagnostics, if you
> think it should change, then IMHO you should submit as independent patch
> a change to the diagnostics wording, so that the wording is the same, rather
> than changing functions from fixed arguments to varargs (/ typegeneric).

I'm not sure I know what part of the patch you're referring
to.  Are you suggesting to submit a separate patch for the
change from "not enough arguments" to "too few arguments"
below?

-		"not enough arguments to function %qE", fndecl);
+		"too few arguments to function %qE", fndecl);

Martin
Jakub Jelinek June 1, 2016, 7:52 a.m. UTC | #9
On Tue, May 31, 2016 at 04:44:45PM -0600, Martin Sebor wrote:
> >I fail to understand this.  You set type to NULL_TREE, and then (not in any
> >kind of loop, nor any label you could goto to) do:
> >   type = type ? type : xxx;
> >That is necessarily equal to type = xxx;, isn't it?
> 
> Each label falls through to the next, so the test prevents
> the subsequent labels from overwriting the value assigned
> to type by the label that matched the switch expression.
> I'm not exactly enamored with these repetitive tests but
> the only alternative that occurred to me was to duplicate
> the whole switch statement, and this seemed like the lesser
> of the two evils.  If you have a suggestion for something
> better I'd be happy to change it.

Ugh, I've missed missing break; Anyway, IMHO we shouldn't change
the old style builtins and therefore you really don't need this.

> >>+  /* 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 ((!arg2 || zerop (arg2))
> >
> >As I said in another mail, IMNSHO you don't want to change the clang
> >compatibility builtins, therefore arg2 should never be NULL.
> 
> Allowing the last argument to be null is the subject of
> the enhancement request in c/68120 (one of the two PRs
> driving this enhancement).  The argument can only be null
> for these overloads and not the type-generic ones because
> the latter use the type to which the pointer points to
> determine the type in which to do the arithmetic.  Without
> this change, C or C++ 98 users wouldn't be able to use the
> built-ins in constant expressions (C++ 98 doesn't allow
> casting a null pointer in those contexts).

Sorry, I don't understand this argument.  What is wrong on using
const int x = __builtin_add_overflow (1234567, 123456, (int *) NULL);
?
I don't get any warning/error on
#include <stddef.h>
int *const p = (int *) NULL;
in C89 nor C++98 with -pedantic-errors, not to mention that the
builtins are extensions anyway, so what do you accept/reject in its
arguments doesn't need to honor any specific rules.  Even if (int *) NULL
is not allowed for whatever reason, is (int *) 0 disallowed too?  It is
just a matter of what we document.

The clang compatibility builtins are severely limited and too ugly to live
with, they don't allow different types of the arguments, have just a very
limited set of accepted types, don't allow different result
type, how do you even handle a type for which you don't know if it is
unsigned, unsigned long or unsigned long long?
  size_t a, b, c, d;
...
  if (sizeof (size_t) == sizeof (unsigned long long))
    a = __builtin_uaddll_overflow (b, c, &d);
  else if (sizeof (size_t) == sizeof (unsigned long))
    a = __builtin_uaddl_overflow (b, c, &d);
  else if (sizeof (size_t) == sizeof (unsigned int))
    a = __builtin_uadd_overflow (b, c, &d);
  else
    abort ();
?  We really shouldn't encourage these at all.

> I suspect I didn't use integer_zerop because the argument
> is a pointer and not integer, but I'll change it before
> I commit the patch.

Pointers are treated the same as integers, they are represented as
INTEGER_CSTs too.
> 
> >Regarding the varargs vs. named args only different diagnostics, if you
> >think it should change, then IMHO you should submit as independent patch
> >a change to the diagnostics wording, so that the wording is the same, rather
> >than changing functions from fixed arguments to varargs (/ typegeneric).
> 
> I'm not sure I know what part of the patch you're referring
> to.  Are you suggesting to submit a separate patch for the
> change from "not enough arguments" to "too few arguments"
> below?
> 
> -		"not enough arguments to function %qE", fndecl);
> +		"too few arguments to function %qE", fndecl);

Yeah.  IMO that is an independent change, you can/should add a testcase for
it, and it can go in before or after the patch.

	Jakub
Martin Sebor June 1, 2016, 3:17 p.m. UTC | #10
>>> As I said in another mail, IMNSHO you don't want to change the clang
>>> compatibility builtins, therefore arg2 should never be NULL.
>>
>> Allowing the last argument to be null is the subject of
>> the enhancement request in c/68120 (one of the two PRs
>> driving this enhancement).  The argument can only be null
>> for these overloads and not the type-generic ones because
>> the latter use the type to which the pointer points to
>> determine the type in which to do the arithmetic.  Without
>> this change, C or C++ 98 users wouldn't be able to use the
>> built-ins in constant expressions (C++ 98 doesn't allow
>> casting a null pointer in those contexts).
>
> Sorry, I don't understand this argument.  What is wrong on using
> const int x = __builtin_add_overflow (1234567, 123456, (int *) NULL);

I see.  You meant that only the clang compatibility built-ins
(i.e. the typed ones like  __builtin_uadd_overflow) shouldn't
be allowed to take null pointer as the last argument, but the
type-generic ones should.  That would work for C, though it
won't satisfy the feature request in c/68120 which asks for
the last argument to be optional (it can't be there since it
determines the type of the operation).  It will not work for
C++ 98.  But those might be acceptable limitations (at least
in C there's a workaround).

> ?
> I don't get any warning/error on
> #include <stddef.h>
> int *const p = (int *) NULL;
> in C89 nor C++98 with -pedantic-errors,

That's because the above is not a constant expression.  If such
a cast were to be used in one like the enum below, GCC in C++ 98
mode would give:

   error: a cast to a type other than an integral or enumeration type 
cannot appear in a constant-expression
    enum { e = (long)(int*)0 };
                           ^

(Curiously, this is accepted with a pedantic warning in C mode.)

> not to mention that the
> builtins are extensions anyway, so what do you accept/reject in its
> arguments doesn't need to honor any specific rules.  Even if (int *) NULL
> is not allowed for whatever reason, is (int *) 0 disallowed too?  It is
> just a matter of what we document.

Since GCC accepts the above as an extension it was tempting
to change G++ to accept it as well.  But such a change seemed
beyond the scope of this patch.  Applying different type system
rules in a set of built-ins than in the rest of the language
wouldn't seem appropriate to me.  I would rather accept the
limitation that the typed built-ins won't be usable in C++ 98
unless Jason recommends to go down that route.

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.

>> I'm not sure I know what part of the patch you're referring
>> to.  Are you suggesting to submit a separate patch for the
>> change from "not enough arguments" to "too few arguments"
>> below?
>>
>> -		"not enough arguments to function %qE", fndecl);
>> +		"too few arguments to function %qE", fndecl);
>
> Yeah.  IMO that is an independent change, you can/should add a testcase for
> it, and it can go in before or after the patch.

I agree that independent substantive changes are best made
separately.

I made this one part of the patch because it affects the tests
that go with it (that's how I found the inconsistency, by my
tests failing), and because changing the spelling of a warning
seemed too trivial of a change to justify a patch of its own
and taking up all of your time to review and approve it.  But
since you insist I will submit it separately ahead of this
updated patch.

Martin
Jakub Jelinek June 1, 2016, 3:45 p.m. UTC | #11
On Wed, Jun 01, 2016 at 09:17:35AM -0600, Martin Sebor wrote:
> I see.  You meant that only the clang compatibility built-ins
> (i.e. the typed ones like  __builtin_uadd_overflow) shouldn't
> be allowed to take null pointer as the last argument, but the
> type-generic ones should.  That would work for C, though it
> won't satisfy the feature request in c/68120 which asks for
> the last argument to be optional (it can't be there since it
> determines the type of the operation).  It will not work for

I don't think we should make it optional.

> C++ 98.  But those might be acceptable limitations (at least
> in C there's a workaround).

__builtin_* is an extension.  Perhaps for C (any) and C++ (< C++14)
you could require that in constant expressions the last argument
of the builtin has to be a NULL pointer cast to some pointer type
(not necessarily a constant expression)?  This can be handled
simply by saving/restoring the constant expression context around
the parsing of the last argument of the builtin, and afterwards
verifying it has the required properties.

For C++14 and later, it would be nice if one could also do:
struct S { int val; bool ovf; };
constexpr S foo (int x, int y)
{
  S ret = { 0, false };
  ret.ovf = __builtin_add_overflow (x, y, &ret.val);
  return ret;
}
instead of doing:
  ret.ovf = __builtin_add_overflow (x, y, (int *) 0);
  ret.val = (unsigned) x + y;
or similar.
Does that work with your patch?

	Jakub
Martin Sebor June 1, 2016, 4:13 p.m. UTC | #12
>> C++ 98.  But those might be acceptable limitations (at least
>> in C there's a workaround).
>
> __builtin_* is an extension.  Perhaps for C (any) and C++ (< C++14)
> you could require that in constant expressions the last argument
> of the builtin has to be a NULL pointer cast to some pointer type
> (not necessarily a constant expression)?  This can be handled
> simply by saving/restoring the constant expression context around
> the parsing of the last argument of the builtin, and afterwards
> verifying it has the required properties.

I'll see if there's an easy way to make it work.

>
> For C++14 and later, it would be nice if one could also do:
> struct S { int val; bool ovf; };
> constexpr S foo (int x, int y)
> {
>    S ret = { 0, false };
>    ret.ovf = __builtin_add_overflow (x, y, &ret.val);
>    return ret;
> }
> instead of doing:
>    ret.ovf = __builtin_add_overflow (x, y, (int *) 0);
>    ret.val = (unsigned) x + y;
> or similar.
> Does that work with your patch?

Yes, it does (it's exercised by the constexpr-arith-overflow.C
test).  The patch is in part motivated by GCC needing to make
use of the overflow built-ins in constexpr functions (to detect
VLA bounds overflow).

Martin
Jason Merrill June 1, 2016, 6:46 p.m. UTC | #13
On 05/31/2016 06:44 PM, Martin Sebor wrote:
> Without this change, C or C++ 98 users wouldn't be able to use the
> built-ins in constant expressions (C++ 98 doesn't allow
> casting a null pointer in those contexts).

I can't imagine that C++98 users care about using these built-ins in 
constant expressions.

Jason
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
PR c/70883 - inconsistent error message for calls to __builtin_add_overflow
	with too few arguments

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

	PR c++/70507
	PR c/68120
	PR c/70883
	* 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.dg/builtin-constant_p-1.c: Adjust text of diagnostic.
	* gcc.dg/builtins-error.c:  Same.

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

	PR c++/70507
	PR c/68120
	PR c/70883
	* 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-04-30  Martin Sebor  <msebor@redhat.com>

	PR c/68120
	PR c/70883
	* c-common.c (builtin_function_validate_nargs): Adjust text
	of diagnostic.
	(check_builtin_function_arguments): Allow type-specific integer
	arithmetic overflow built-ins to take either 2 or three arguments.

gcc/ChangeLog:
2016-04-30  Martin Sebor  <msebor@redhat.com>

	PR c++/70507
	PR c/68120
	PR c/70883
	* builtin-types.def (BT_FN_BOOL_INT_INT_VAR, BT_FN_BOOL_LONG_LONG_VAR)
	(BT_FN_BOOL_LONGLONG_LONGLONG_VAR, BT_FN_BOOL_UINT_UINT_VAR)
	(BT_FN_BOOL_UINT_UINT_VAR, BT_FN_BOOL_ULONG_ULONG_VAR)
	(BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR): New.
	* builtins.c (fold_builtin_unordered_cmp): Handle integer arithmetic
	overflow built-ins.
	(fold_builtin_2): Handle invocations of type-specific integer arithmetic
	overflow built-ins with two arguments.
	* builtins.def (BUILT_IN_SADD_OVERFLOW, BUILT_IN_SADDL_OVERFLOW):
	Declare built-ins to take two or more arguments (constraining them
	to take two or three in c-common.c).
	(BUILT_IN_SADDLL_OVERFLOW, BUILT_IN_SSUB_OVERFLOW): Same.
	(BUILT_IN_SSUBL_OVERFLOW, BUILT_IN_SSUBLL_OVERFLOW): Same.
	(BUILT_IN_SMUL_OVERFLOW, BUILT_IN_SMULL_OVERFLOW): Same.
	(BUILT_IN_SMULLL_OVERFLOW, BUILT_IN_UADD_OVERFLOW): Same.
	(BUILT_IN_UADDL_OVERFLOW, BUILT_IN_UADDLL_OVERFLOW): Same.
	(BUILT_IN_USUB_OVERFLOW, BUILT_IN_USUBL_OVERFLOW): Same.
	(BUILT_IN_USUBLL_OVERFLOW, BUILT_IN_UMUL_OVERFLOW): Same.
	(BUILT_IN_UMULL_OVERFLOW, BUILT_IN_UMULLL_OVERFLOW): Same.
	* doc/extend.texi (Integer Overflow Builtins): Update.

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index 7fab9f8..ee63284 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -451,6 +451,16 @@  DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONG_ULONG_ULONGPTR, BT_BOOL, BT_ULONG,
 DEF_FUNCTION_TYPE_3 (BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, BT_BOOL,
 		     BT_ULONGLONG, BT_ULONGLONG, BT_PTR_ULONGLONG)
 
+DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_INT_INT_VAR, BT_BOOL, BT_INT, BT_INT)
+DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_LONG_LONG_VAR, BT_BOOL, BT_LONG, BT_LONG)
+DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_LONGLONG_LONGLONG_VAR, BT_BOOL,
+			 BT_LONGLONG, BT_LONGLONG)
+DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_UINT_UINT_VAR, BT_BOOL, BT_UINT, BT_UINT)
+DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_ULONG_ULONG_VAR, BT_BOOL, BT_ULONG,
+			 BT_ULONG)
+DEF_FUNCTION_TYPE_VAR_2 (BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, BT_BOOL,
+			 BT_ULONGLONG, BT_ULONGLONG)
+
 DEF_FUNCTION_TYPE_4 (BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR,
 		     BT_SIZE, BT_CONST_PTR, BT_SIZE, BT_SIZE, BT_FILEPTR)
 DEF_FUNCTION_TYPE_4 (BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG,
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 3d89baf..c43d08e 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -7878,50 +7878,101 @@  fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
 
 static tree
 fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
-			     tree arg0, tree arg1, tree arg2)
+			     tree arg0, tree arg1, tree arg2 = NULL_TREE)
 {
   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;
+
+  /* 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 names of each of the other overloads determines
+     the target type and so the last argument can be omitted.  */
+  tree type = NULL_TREE;
+
   switch (fcode)
     {
     case BUILT_IN_ADD_OVERFLOW:
+      type = type ? type : TREE_TYPE (TREE_TYPE (arg2));
     case BUILT_IN_SADD_OVERFLOW:
+      type = type ? type : integer_type_node;
     case BUILT_IN_SADDL_OVERFLOW:
+      type = type ? type : long_integer_type_node;
     case BUILT_IN_SADDLL_OVERFLOW:
+      type = type ? type : long_long_integer_type_node;
     case BUILT_IN_UADD_OVERFLOW:
+      type = type ? type : unsigned_type_node;
     case BUILT_IN_UADDL_OVERFLOW:
+      type = type ? type : long_unsigned_type_node;
     case BUILT_IN_UADDLL_OVERFLOW:
+      type = type ? type : long_long_unsigned_type_node;
       ifn = IFN_ADD_OVERFLOW;
+      opcode = PLUS_EXPR;
       break;
     case BUILT_IN_SUB_OVERFLOW:
+      type = type ? type : TREE_TYPE (TREE_TYPE (arg2));
     case BUILT_IN_SSUB_OVERFLOW:
+      type = type ? type : integer_type_node;
     case BUILT_IN_SSUBL_OVERFLOW:
+      type = type ? type : long_integer_type_node;
     case BUILT_IN_SSUBLL_OVERFLOW:
+      type = type ? type : long_long_integer_type_node;
     case BUILT_IN_USUB_OVERFLOW:
+      type = type ? type : unsigned_type_node;
     case BUILT_IN_USUBL_OVERFLOW:
+      type = type ? type : long_unsigned_type_node;
     case BUILT_IN_USUBLL_OVERFLOW:
+      type = type ? type : long_long_unsigned_type_node;
       ifn = IFN_SUB_OVERFLOW;
+      opcode = MINUS_EXPR;
       break;
     case BUILT_IN_MUL_OVERFLOW:
+      type = type ? type : TREE_TYPE (TREE_TYPE (arg2));
     case BUILT_IN_SMUL_OVERFLOW:
+      type = type ? type : integer_type_node;
     case BUILT_IN_SMULL_OVERFLOW:
+      type = type ? type : long_integer_type_node;
     case BUILT_IN_SMULLL_OVERFLOW:
+      type = type ? type : long_long_integer_type_node;
     case BUILT_IN_UMUL_OVERFLOW:
+      type = type ? type : unsigned_type_node;
     case BUILT_IN_UMULL_OVERFLOW:
+      type = type ? type : long_unsigned_type_node;
     case BUILT_IN_UMULLL_OVERFLOW:
+      type = type ? type : long_long_unsigned_type_node;
       ifn = IFN_MUL_OVERFLOW;
+      opcode = MULT_EXPR;
       break;
     default:
       gcc_unreachable ();
     }
+
+  /* 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 ((!arg2 || zerop (arg2))
+      && 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);
+  tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2, arg0, arg1);
   tree tgt = save_expr (call);
   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 (!arg2 || zerop (arg2))
+      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);
@@ -8171,6 +8222,29 @@  fold_builtin_2 (location_t loc, tree fndecl, tree arg0, tree arg1)
     case BUILT_IN_ATOMIC_IS_LOCK_FREE:
       return fold_builtin_atomic_is_lock_free (arg0, arg1);
 
+    case BUILT_IN_ADD_OVERFLOW:
+    case BUILT_IN_SUB_OVERFLOW:
+    case BUILT_IN_MUL_OVERFLOW:
+    case BUILT_IN_SADD_OVERFLOW:
+    case BUILT_IN_SADDL_OVERFLOW:
+    case BUILT_IN_SADDLL_OVERFLOW:
+    case BUILT_IN_SSUB_OVERFLOW:
+    case BUILT_IN_SSUBL_OVERFLOW:
+    case BUILT_IN_SSUBLL_OVERFLOW:
+    case BUILT_IN_SMUL_OVERFLOW:
+    case BUILT_IN_SMULL_OVERFLOW:
+    case BUILT_IN_SMULLL_OVERFLOW:
+    case BUILT_IN_UADD_OVERFLOW:
+    case BUILT_IN_UADDL_OVERFLOW:
+    case BUILT_IN_UADDLL_OVERFLOW:
+    case BUILT_IN_USUB_OVERFLOW:
+    case BUILT_IN_USUBL_OVERFLOW:
+    case BUILT_IN_USUBLL_OVERFLOW:
+    case BUILT_IN_UMUL_OVERFLOW:
+    case BUILT_IN_UMULL_OVERFLOW:
+    case BUILT_IN_UMULLL_OVERFLOW:
+      return fold_builtin_arith_overflow (loc, fcode, arg0, arg1);
+
     default:
       break;
     }
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 2fc7f65..34ebc22 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -706,29 +706,30 @@  DEF_C94_BUILTIN        (BUILT_IN_ISWXDIGIT, "iswxdigit", BT_FN_INT_WINT, ATTR_PU
 DEF_C94_BUILTIN        (BUILT_IN_TOWLOWER, "towlower", BT_FN_WINT_WINT, ATTR_PURE_NOTHROW_LEAF_LIST)
 DEF_C94_BUILTIN        (BUILT_IN_TOWUPPER, "towupper", BT_FN_WINT_WINT, ATTR_PURE_NOTHROW_LEAF_LIST)
 
-/* Category: integer overflow checking builtins.  */
+/* Category: type-generic integer overflow checking builtins.  */
 DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW, "add_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW, "sub_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW, "mul_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
-/* Clang compatibility.  */
-DEF_GCC_BUILTIN        (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SADDLL_OVERFLOW, "saddll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SSUB_OVERFLOW, "ssub_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SSUBL_OVERFLOW, "ssubl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SSUBLL_OVERFLOW, "ssubll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SMUL_OVERFLOW, "smul_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SMULL_OVERFLOW, "smull_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_SMULLL_OVERFLOW, "smulll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_LONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UADD_OVERFLOW, "uadd_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UADDL_OVERFLOW, "uaddl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UADDLL_OVERFLOW, "uaddll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_USUB_OVERFLOW, "usub_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_USUBL_OVERFLOW, "usubl_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_USUBLL_OVERFLOW, "usubll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UMUL_OVERFLOW, "umul_overflow", BT_FN_BOOL_UINT_UINT_UINTPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UMULL_OVERFLOW, "umull_overflow", BT_FN_BOOL_ULONG_ULONG_ULONGPTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_GCC_BUILTIN        (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_ULONGLONGPTR, ATTR_NOTHROW_LEAF_LIST)
+/* For Clang compatibility: typed integer overflow checking builtins, last
+   argument is optional.  */
+DEF_GCC_BUILTIN        (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SADDLL_OVERFLOW, "saddll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SSUB_OVERFLOW, "ssub_overflow", BT_FN_BOOL_INT_INT_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SSUBL_OVERFLOW, "ssubl_overflow", BT_FN_BOOL_LONG_LONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SSUBLL_OVERFLOW, "ssubll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SMUL_OVERFLOW, "smul_overflow", BT_FN_BOOL_INT_INT_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SMULL_OVERFLOW, "smull_overflow", BT_FN_BOOL_LONG_LONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_SMULLL_OVERFLOW, "smulll_overflow", BT_FN_BOOL_LONGLONG_LONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UADD_OVERFLOW, "uadd_overflow", BT_FN_BOOL_UINT_UINT_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UADDL_OVERFLOW, "uaddl_overflow", BT_FN_BOOL_ULONG_ULONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UADDLL_OVERFLOW, "uaddll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_USUB_OVERFLOW, "usub_overflow", BT_FN_BOOL_UINT_UINT_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_USUBL_OVERFLOW, "usubl_overflow", BT_FN_BOOL_ULONG_ULONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_USUBLL_OVERFLOW, "usubll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UMUL_OVERFLOW, "umul_overflow", BT_FN_BOOL_UINT_UINT_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UMULL_OVERFLOW, "umull_overflow", BT_FN_BOOL_ULONG_ULONG_VAR, ATTR_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN        (BUILT_IN_UMULLL_OVERFLOW, "umulll_overflow", BT_FN_BOOL_ULONGLONG_ULONGLONG_VAR, ATTR_NOTHROW_LEAF_LIST)
 
 /* Category: miscellaneous builtins.  */
 DEF_LIB_BUILTIN        (BUILT_IN_ABORT, "abort", BT_FN_VOID, ATTR_TMPURE_NORETURN_NOTHROW_LEAF_LIST)
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 1edc0bc..87a9d99 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -9806,7 +9806,7 @@  builtin_function_validate_nargs (tree fndecl, int nargs, int required)
   if (nargs < required)
     {
       error_at (input_location,
-		"not enough arguments to function %qE", fndecl);
+		"too few arguments to function %qE", fndecl);
       return false;
     }
   else if (nargs > required)
@@ -9828,6 +9828,11 @@  check_builtin_function_arguments (tree fndecl, int nargs, tree *args)
       || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
     return true;
 
+  /* Set to the number of required arguments when a built-in takes a fixed
+     number of arguments, otherwise negative non-zero when the built-in is
+     overloaded to accept different numbers of arguments.  */
+  int maxargs = -1;
+
   switch (DECL_FUNCTION_CODE (fndecl))
     {
     case BUILT_IN_ALLOCA_WITH_ALIGN:
@@ -9939,7 +9944,39 @@  check_builtin_function_arguments (tree fndecl, int nargs, tree *args)
     case BUILT_IN_ADD_OVERFLOW:
     case BUILT_IN_SUB_OVERFLOW:
     case BUILT_IN_MUL_OVERFLOW:
-      if (builtin_function_validate_nargs (fndecl, nargs, 3))
+      maxargs = 3;
+      /* Fall through.  */
+
+    case BUILT_IN_SADD_OVERFLOW:
+    case BUILT_IN_SADDL_OVERFLOW:
+    case BUILT_IN_SADDLL_OVERFLOW:
+
+    case BUILT_IN_SSUB_OVERFLOW:
+    case BUILT_IN_SSUBL_OVERFLOW:
+    case BUILT_IN_SSUBLL_OVERFLOW:
+
+    case BUILT_IN_SMUL_OVERFLOW:
+    case BUILT_IN_SMULL_OVERFLOW:
+    case BUILT_IN_SMULLL_OVERFLOW:
+
+    case BUILT_IN_UADD_OVERFLOW:
+    case BUILT_IN_UADDL_OVERFLOW:
+    case BUILT_IN_UADDLL_OVERFLOW:
+
+    case BUILT_IN_USUB_OVERFLOW:
+    case BUILT_IN_USUBL_OVERFLOW:
+    case BUILT_IN_USUBLL_OVERFLOW:
+
+    case BUILT_IN_UMUL_OVERFLOW:
+    case BUILT_IN_UMULL_OVERFLOW:
+    case BUILT_IN_UMULLL_OVERFLOW:
+
+      /* The type-generic integer overflow built-ins take exactly three
+	 arguments.  The typed built-ins take 2 or 3 arguments.  */
+      if (maxargs < 1)
+	maxargs = 2 + (nargs == 3);
+
+      if (builtin_function_validate_nargs (fndecl, nargs, maxargs))
 	{
 	  unsigned i;
 	  for (i = 0; i < 2; i++)
@@ -9949,8 +9986,11 @@  check_builtin_function_arguments (tree fndecl, int nargs, tree *args)
 		       "integral type", i + 1, fndecl);
 		return false;
 	      }
-	  if (TREE_CODE (TREE_TYPE (args[2])) != POINTER_TYPE
-	      || TREE_CODE (TREE_TYPE (TREE_TYPE (args[2]))) != INTEGER_TYPE)
+
+	  tree a3type = 2 < nargs ? TREE_TYPE (args[2]) : NULL_TREE;
+	  if (a3type
+	      && (TREE_CODE (a3type) != POINTER_TYPE
+		  || TREE_CODE (TREE_TYPE (a3type)) != INTEGER_TYPE))
 	    {
 	      error ("argument 3 in call to function %qE does not have "
 		     "pointer to integer type", fndecl);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 41f0b5c..b93e0f0 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 381c24f..6fe8a34 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -307,10 +307,36 @@  builtin_valid_in_constant_expr_p (const_tree decl)
     return false;
   switch (DECL_FUNCTION_CODE (decl))
     {
-    case BUILT_IN_CONSTANT_P:
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
+      /* 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:
+    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
       return true;
     default:
       return false;
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index a5a8b23..c6b6769 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -9731,58 +9731,90 @@  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 functions shown
+with a default argument may be invoked without explicitly providing it.
+The functions yield constant integer expressions when their arguments are
+and can be used in contexts where constant integer expressions are required.
 
 @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)
-@deftypefnx {Built-in Function} bool __builtin_saddl_overflow (long int a, long int b, long int *res)
-@deftypefnx {Built-in Function} bool __builtin_saddll_overflow (long long int a, long long int b, long int *res)
-@deftypefnx {Built-in Function} bool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res)
-@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.
-
-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.
+@deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b, int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_saddl_overflow (long int a, long int b, long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_saddll_overflow (long long int a, long long int b, long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_uadd_overflow (unsigned int a, unsigned int b, unsigned int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res = 0)
+
+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 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
+the type 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 built-ins 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.
+All but the type-generic built-ins may also be invoked without 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
+
+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
 
 @deftypefn {Built-in Function} bool __builtin_sub_overflow (@var{type1} a, @var{type2} b, @var{type3} *res)
-@deftypefnx {Built-in Function} bool __builtin_ssub_overflow (int a, int b, int *res)
-@deftypefnx {Built-in Function} bool __builtin_ssubl_overflow (long int a, long int b, long int *res)
-@deftypefnx {Built-in Function} bool __builtin_ssubll_overflow (long long int a, long long int b, long int *res)
-@deftypefnx {Built-in Function} bool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res)
-@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)
+@deftypefnx {Built-in Function} bool __builtin_ssub_overflow (int a, int b, int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_ssubl_overflow (long int a, long int b, long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_ssubll_overflow (long long int a, long long int b, long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_usub_overflow (unsigned int a, unsigned int b, unsigned int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_usubl_overflow (unsigned long int a, unsigned long int b, unsigned long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_usubll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res = 0)
 
-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
 
 @deftypefn {Built-in Function} bool __builtin_mul_overflow (@var{type1} a, @var{type2} b, @var{type3} *res)
-@deftypefnx {Built-in Function} bool __builtin_smul_overflow (int a, int b, int *res)
-@deftypefnx {Built-in Function} bool __builtin_smull_overflow (long int a, long int b, long int *res)
-@deftypefnx {Built-in Function} bool __builtin_smulll_overflow (long long int a, long long int b, long int *res)
-@deftypefnx {Built-in Function} bool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res)
-@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.
+@deftypefnx {Built-in Function} bool __builtin_smul_overflow (int a, int b, int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_smull_overflow (long int a, long int b, long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_smulll_overflow (long long int a, long long int b, long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_umul_overflow (unsigned int a, unsigned int b, unsigned int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_umull_overflow (unsigned long int a, unsigned long int b, unsigned long int *res = 0)
+@deftypefnx {Built-in Function} bool __builtin_umulll_overflow (unsigned long long int a, unsigned long long int b, unsigned long int *res = 0)
+
+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..7d09a95 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 the last argument of the wrong type to
+   the generic __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);
+  x += __builtin_ssub_overflow (a, b);
+  x += __builtin_smul_overflow (a, b);
+  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..dbd825f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c
@@ -0,0 +1,469 @@ 
+/* 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 sub(x, y) ((a) - (b))
+#define mul(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, x, y, vflow)				\
+  do {									\
+    volatile T a = (x), b = (y), 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, 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, 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(x, y)
+     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.  */
+#define T_TEST(op, T, x, y, vflow)					\
+  StaticAssert ((vflow) == __builtin_ ## op ## _overflow ((T)x, (T)y))
+
+  /* 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,           0,         0,         0);
+  T_TEST (saddl,  long,           0,         LONG_MAX,  0);
+  T_TEST (saddl,  long,           1,         LONG_MAX,  1);
+  T_TEST (saddl,  long,           LONG_MAX,  LONG_MAX,  1);
+  T_TEST (saddl,  long,           0,         LONG_MIN,  0);
+  T_TEST (saddl,  long,          -1,         LONG_MIN,  1);
+  T_TEST (saddl,  long,           LONG_MIN,  LONG_MIN,  1);
+
+  T_TEST (saddll, long long,      0,         0,          0);
+  T_TEST (saddll, long long,      0,         LLONG_MAX,  0);
+  T_TEST (saddll, long long,      1,         LLONG_MAX,  1);
+  T_TEST (saddll, long long,      LLONG_MAX, LLONG_MAX,  1);
+  T_TEST (saddll, long long,      0,         LLONG_MIN,  0);
+  T_TEST (saddll, long long,     -1,         LLONG_MIN,  1);
+  T_TEST (saddll, long long,      LLONG_MIN, LLONG_MIN,  1);
+
+  /* Unsigned int addition.  */
+  T_TEST (uadd,   unsigned char,  0,         0,         0);
+  T_TEST (uadd,   unsigned char,  0,         UCHAR_MAX, 0);
+  T_TEST (uadd,   unsigned char,  1,         UCHAR_MAX, 0);
+  T_TEST (uadd,   unsigned char,  UCHAR_MAX, UCHAR_MAX, 0);
+
+  T_TEST (uadd,   unsigned short, 0,         0,          0);
+  T_TEST (uadd,   unsigned short, 0,         USHRT_MAX,  0);
+  T_TEST (uadd,   unsigned short, 1,         USHRT_MAX,  0);
+  T_TEST (uadd,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  T_TEST (uadd,   unsigned,       0,         0,          0);
+  T_TEST (uadd,   unsigned,       0,         UINT_MAX,   0);
+  T_TEST (uadd,   unsigned,       1,         UINT_MAX,   1);
+  T_TEST (uadd,   unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+  /* Unsigned long addition.  */
+  T_TEST (uaddl,  unsigned long,   0,         0,         0);
+  T_TEST (uaddl,  unsigned long,   0,         ULONG_MAX, 0);
+  T_TEST (uaddl,  unsigned long,   1,         ULONG_MAX, 1);
+  T_TEST (uaddl,  unsigned long,   ULONG_MAX, ULONG_MAX, 1);
+
+  T_TEST (uaddl,  unsigned long long,  0,          0,          0);
+  T_TEST (uaddl,  unsigned long long,  0,          ULLONG_MAX, 0);
+  T_TEST (uaddl,  unsigned long long,  1,          ULLONG_MAX, 1);
+  T_TEST (uaddl,  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,           0,         0,          0);
+  T_TEST (ssubl,  long,           0,         LONG_MAX,   0);
+  T_TEST (ssubl,  long,           1,         LONG_MAX,   0);
+  T_TEST (ssubl,  long,           LONG_MAX,  LONG_MAX,   0);
+  T_TEST (ssubl,  long,           0,         LONG_MIN,   1);
+  T_TEST (ssubl,  long,          -1,         LONG_MIN,   0);
+  T_TEST (ssubl,  long,           LONG_MIN,  LONG_MIN,   0);
+
+  /* Signed long long subtraction.  */
+  T_TEST (ssubll, long long,      0,         0,          0);
+  T_TEST (ssubll, long long,      0,         LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      1,         LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      LLONG_MAX, LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      0,         LLONG_MIN,  1);
+  T_TEST (ssubll, long long,     -1,         LLONG_MIN,  0);
+  T_TEST (ssubll, long long,      LLONG_MIN, LLONG_MIN,  0);
+
+  /* Unsigned int subtraction.  */
+  T_TEST (usub,   unsigned char,  0,         0,          0);
+  T_TEST (usub,   unsigned char,  0,         UCHAR_MAX,  1);
+  T_TEST (usub,   unsigned char,  1,         UCHAR_MAX,  1);
+  T_TEST (usub,   unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+
+  T_TEST (usub,   unsigned short, 0,         0,          0);
+  T_TEST (usub,   unsigned short, 0,         USHRT_MAX,  1);
+  T_TEST (usub,   unsigned short, 1,         USHRT_MAX,  1);
+  T_TEST (usub,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  T_TEST (usub,   unsigned,       0,         0,          0);
+  T_TEST (usub,   unsigned,       0,         UINT_MAX,   1);
+  T_TEST (usub,   unsigned,       1,         UINT_MAX,   1);
+  T_TEST (usub,   unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+  /* Unsigned long subtraction.  */
+  T_TEST (usubl,  unsigned long,   0,         0,         0);
+  T_TEST (usubl,  unsigned long,   0,         ULONG_MAX, 1);
+  T_TEST (usubl,  unsigned long,   1,         ULONG_MAX, 1);
+  T_TEST (usubl,  unsigned long,   ULONG_MAX, ULONG_MAX, 0);
+
+  /* Unsigned long long subtraction.  */
+  T_TEST (usubl,  unsigned long long,  0,          0,          0);
+  T_TEST (usubl,  unsigned long long,  0,          ULLONG_MAX, 1);
+  T_TEST (usubl,  unsigned long long,  1,          ULLONG_MAX, 1);
+  T_TEST (usubl,  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..7be4ca4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C
@@ -0,0 +1,116 @@ 
+// 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
+ 
diff --git a/gcc/testsuite/gcc.dg/builtin-constant_p-1.c b/gcc/testsuite/gcc.dg/builtin-constant_p-1.c
index b0b34f4..f177fe3 100644
--- a/gcc/testsuite/gcc.dg/builtin-constant_p-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-constant_p-1.c
@@ -2,9 +2,9 @@ 
 
 int main()
 {
-  if (__builtin_constant_p ()) /* { dg-error "not enough" } */
+  if (__builtin_constant_p ()) /* { dg-error "too few arguments" } */
     return 0;
-  if (__builtin_constant_p (5, 6)) /* { dg-error "too many" } */
+  if (__builtin_constant_p (5, 6)) /* { dg-error "too many arguments" } */
     return 1;
   return 0;
 }
diff --git a/gcc/testsuite/gcc.dg/builtins-error.c b/gcc/testsuite/gcc.dg/builtins-error.c
index 9ddf1b1..945d239 100644
--- a/gcc/testsuite/gcc.dg/builtins-error.c
+++ b/gcc/testsuite/gcc.dg/builtins-error.c
@@ -23,19 +23,19 @@  int test1(struct X x)
 
 int test2(double x)
 {
-  if (x ==  1) return __builtin_fpclassify(1,2,3,4,5); /* { dg-error "not enough arguments" } */
-  if (x ==  2) return __builtin_isfinite(); /* { dg-error "not enough arguments" } */
-  if (x ==  3) return __builtin_isinf_sign(); /* { dg-error "not enough arguments" } */
-  if (x ==  4) return __builtin_isinf(); /* { dg-error "not enough arguments" } */
-  if (x ==  5) return __builtin_isnan(); /* { dg-error "not enough arguments" } */
-  if (x ==  6) return __builtin_isnormal(); /* { dg-error "not enough arguments" } */
-  if (x ==  7) return __builtin_isgreater(x); /* { dg-error "not enough arguments" } */
-  if (x ==  8) return __builtin_isgreaterequal(x); /* { dg-error "not enough arguments" } */
-  if (x ==  9) return __builtin_isless(x); /* { dg-error "not enough arguments" } */
-  if (x == 10) return __builtin_islessequal(x); /* { dg-error "not enough arguments" } */
-  if (x == 11) return __builtin_islessgreater(x); /* { dg-error "not enough arguments" } */
-  if (x == 12) return __builtin_isunordered(x); /* { dg-error "not enough arguments" } */
-  if (x == 13) return __builtin_signbit(); /* { dg-error "not enough arguments" } */
+  if (x ==  1) return __builtin_fpclassify(1,2,3,4,5); /* { dg-error "too few arguments" } */
+  if (x ==  2) return __builtin_isfinite(); /* { dg-error "too few arguments" } */
+  if (x ==  3) return __builtin_isinf_sign(); /* { dg-error "too few arguments" } */
+  if (x ==  4) return __builtin_isinf(); /* { dg-error "too few arguments" } */
+  if (x ==  5) return __builtin_isnan(); /* { dg-error "too few arguments" } */
+  if (x ==  6) return __builtin_isnormal(); /* { dg-error "too few arguments" } */
+  if (x ==  7) return __builtin_isgreater(x); /* { dg-error "too few arguments" } */
+  if (x ==  8) return __builtin_isgreaterequal(x); /* { dg-error "too few arguments" } */
+  if (x ==  9) return __builtin_isless(x); /* { dg-error "too few arguments" } */
+  if (x == 10) return __builtin_islessequal(x); /* { dg-error "too few arguments" } */
+  if (x == 11) return __builtin_islessgreater(x); /* { dg-error "too few arguments" } */
+  if (x == 12) return __builtin_isunordered(x); /* { dg-error "too few arguments" } */
+  if (x == 13) return __builtin_signbit(); /* { dg-error "too few arguments" } */
   return 0;
 }