On Wed, Jun 01, 2016 at 09:10:52PM -0600, Martin Sebor wrote:
> @@ -7957,8 +7957,8 @@ fold_builtin_arith_overflow (location_t loc, enum
> built_in_function fcode,
> tree arg0, tree arg1, tree arg2)
> {
> enum internal_fn ifn = IFN_LAST;
> - tree type = TREE_TYPE (TREE_TYPE (arg2));
> - tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
> + enum tree_code opcode;
> +
> switch (fcode)
> {
> case BUILT_IN_ADD_OVERFLOW:
> @@ -7969,6 +7969,7 @@ fold_builtin_arith_overflow (location_t loc, enum
> built_in_function fcode,
> case BUILT_IN_UADDL_OVERFLOW:
> case BUILT_IN_UADDLL_OVERFLOW:
> ifn = IFN_ADD_OVERFLOW;
> + opcode = PLUS_EXPR;
> break;
> case BUILT_IN_SUB_OVERFLOW:
> case BUILT_IN_SSUB_OVERFLOW:
> @@ -7978,6 +7979,7 @@ fold_builtin_arith_overflow (location_t loc, enum
> built_in_function fcode,
> case BUILT_IN_USUBL_OVERFLOW:
> case BUILT_IN_USUBLL_OVERFLOW:
> ifn = IFN_SUB_OVERFLOW;
> + opcode = MINUS_EXPR;
> break;
> case BUILT_IN_MUL_OVERFLOW:
> case BUILT_IN_SMUL_OVERFLOW:
> @@ -7987,10 +7989,35 @@ fold_builtin_arith_overflow (location_t loc, enum
> built_in_function fcode,
> case BUILT_IN_UMULL_OVERFLOW:
> case BUILT_IN_UMULLL_OVERFLOW:
> ifn = IFN_MUL_OVERFLOW;
> + opcode = MULT_EXPR;
> break;
> default:
> gcc_unreachable ();
> }
> +
> + /* For the "generic" overloads, the first two arguments can have different
> + types and the last argument determines the target type to use to check
> + for overflow. The arguments of the other overloads all have the same
> + type. */
> + tree type = TREE_TYPE (TREE_TYPE (arg2));
> + bool isnullp = integer_zerop (arg2);
> +
> + /* When the last argument is a null pointer and the first two arguments
> + are constant, attempt to fold the built-in call into a constant
> + expression indicating whether or not it detected an overflow but
> + without storing the result. */
> + if (isnullp
> + && TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
Doesn't this mean you allow NULL arg2 even for BUILT_IN_ADDL_OVERFLOW and
similar clang builtins? I'd set opcode to ERROR_MARK first, and then do:
switch (fcode)
{
case BUILT_IN_ADD_OVERFLOW:
opcode = PLUS_EXPR;
/* FALLTHROUGH */
case BUILT_IN_SADD_OVERFLOW:
case BUILT_IN_SADDL_OVERFLOW:
case BUILT_IN_SADDLL_OVERFLOW:
case BUILT_IN_UADD_OVERFLOW:
case BUILT_IN_UADDL_OVERFLOW:
case BUILT_IN_UADDLL_OVERFLOW:
ifn = IFN_ADD_OVERFLOW;
break;
and similarly, then
bool isnullp = opcode != ERROR_MARK ? integer_zerop (arg2) : false;
or so.
> + {
> + /* Perform the computation in the target type and check for overflow.
> */
> + arg0 = fold_convert (type, arg0);
> + arg1 = fold_convert (type, arg1);
> +
> + if (tree result = size_binop_loc (loc, opcode, arg0, arg1))
> + return TREE_OVERFLOW (result) ? build_one_cst (boolean_type_node)
> + : build_zero_cst (boolean_type_node);
This is wrong, it is not the computation of overflow that is intended.
The documentation says that we compute (infinite_precision_signed_type) arg0
op (infinite_precision_signed_type) arg1 and then cast it to type, extend
again to infinite_precision_signed_type and compare. And we have a helper
function for that already.
Furthermore, we have boolean_true_node and boolean_false_node.
Thus, I'd expect here:
return (arith_overflowed_p (opcode, type, arg0, arg1)
? boolean_true_node : boolean_false_node);
> + tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval,
> + non_constant_p, overflow_p);
> + tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval,
> + non_constant_p, overflow_p);
> +
> + if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
> + {
> + if (tree result = size_binop_loc (EXPR_LOC_OR_LOC (t, input_location),
> + opcode, arg0, arg1))
> + {
> + if (TREE_OVERFLOW (result))
> + {
> + /* Reset TREE_OVERFLOW to avoid warnings for the overflow. */
> + TREE_OVERFLOW (result) = 0;
Again, this is wrong, it should have used arith_overflowed_p.
> @@ -1270,18 +1332,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx,
> tree t,
> bool depth_ok;
>
> if (fun == NULL_TREE)
> - switch (CALL_EXPR_IFN (t))
> - {
> - case IFN_UBSAN_NULL:
> - case IFN_UBSAN_BOUNDS:
> - case IFN_UBSAN_VPTR:
> - return void_node;
> - default:
> - if (!ctx->quiet)
> - error_at (loc, "call to internal function");
> - *non_constant_p = true;
> - return t;
> - }
> + return cxx_eval_internal_function (ctx, t, lval,
> + non_constant_p, overflow_p);
>
> if (TREE_CODE (fun) != FUNCTION_DECL)
> {
> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> index 04702ee..f9db199 100644
> --- a/gcc/cp/tree.c
> +++ b/gcc/cp/tree.c
> @@ -352,6 +352,32 @@ builtin_valid_in_constant_expr_p (const_tree decl)
> case BUILT_IN_FUNCTION:
> case BUILT_IN_LINE:
>
> + /* The following built-ins are valid in constant expressions
> + when their arguments are. */
> + case BUILT_IN_ADD_OVERFLOW:
> + case BUILT_IN_SADD_OVERFLOW:
> + case BUILT_IN_SADDL_OVERFLOW:
> + case BUILT_IN_SADDLL_OVERFLOW:
> + case BUILT_IN_UADD_OVERFLOW:
> + case BUILT_IN_UADDL_OVERFLOW:
> + case BUILT_IN_UADDLL_OVERFLOW:
> +
> + case BUILT_IN_SUB_OVERFLOW:
> + case BUILT_IN_SSUB_OVERFLOW:
> + case BUILT_IN_SSUBL_OVERFLOW:
> + case BUILT_IN_SSUBLL_OVERFLOW:
> + case BUILT_IN_USUB_OVERFLOW:
> + case BUILT_IN_USUBL_OVERFLOW:
> + case BUILT_IN_USUBLL_OVERFLOW:
> +
> + case BUILT_IN_MUL_OVERFLOW:
> + case BUILT_IN_SMUL_OVERFLOW:
> + case BUILT_IN_SMULL_OVERFLOW:
> + case BUILT_IN_SMULLL_OVERFLOW:
> + case BUILT_IN_UMUL_OVERFLOW:
> + case BUILT_IN_UMULL_OVERFLOW:
> + case BUILT_IN_UMULLL_OVERFLOW:
> +
> /* These have constant results even if their operands are
> non-constant. */
> case BUILT_IN_CONSTANT_P:
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 2d4f028..00435f7 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -9737,7 +9737,10 @@ compiler may also ignore this parameter.
> @section Built-in Functions to Perform Arithmetic with Overflow Checking
>
> The following built-in functions allow performing simple arithmetic
> operations
> -together with checking whether the operations overflowed.
> +together with checking whether the operations overflowed. The first of the
> +functions accepts either a pointer to an integer object or a null pointer to
> +integer as the last argument. The rest require a pointer to an object of
> +the specified type as the last argument.
>
> @deftypefn {Built-in Function} bool __builtin_add_overflow (@var{type1} a,
> @var{type2} b, @var{type3} *res)
> @deftypefnx {Built-in Function} bool __builtin_sadd_overflow (int a, int b,
> int *res)
> @@ -9747,21 +9750,50 @@ together with checking whether the operations
> overflowed.
> @deftypefnx {Built-in Function} bool __builtin_uaddl_overflow (unsigned long
> int a, unsigned long int b, unsigned long int *res)
> @deftypefnx {Built-in Function} bool __builtin_uaddll_overflow (unsigned
> long long int a, unsigned long long int b, unsigned long int *res)
>
> -These built-in functions promote the first two operands into infinite
> precision signed
> -type and perform addition on those promoted operands. The result is then
> -cast to the type the third pointer argument points to and stored there.
> -If the stored result is equal to the infinite precision result, the built-in
> -functions return false, otherwise they return true. As the addition is
> -performed in infinite signed precision, these built-in functions have fully
> defined
> -behavior for all argument values.
> -
> -The first built-in function allows arbitrary integral types for operands and
> -the result type must be pointer to some integer type, the rest of the
> built-in
> -functions have explicit integer types.
> +These built-in functions promote the first two operands into infinite
> precision
> +signed type and perform addition on those promoted operands. The result is
> then
> +converted to the type the third pointer argument points to, and for the first
> +function when the pointer is not null, stored there. If the converted result
> +is equal to the infinite precision result, the built-in functions return
> +@code{false}, otherwise they return @code{true} to indicate that an overflow
> +has been detected. Because the addition is performed in infinite precision,
> +these built-in functions have fully defined behavior for all argument values
> +and integer types.
> +
> +The first type-generic built-in function allows arbitrary integer types as
> +the first two arguments and requires that a pointer to some possibly distinct
> +integer type be passed to it as the third argument. The pointed-to type is
> +then used to determine the overflow. As a result, this built-in function
> +can be used to detect overflow in any arbitrary integer type, including
> +@code{char} and @code{short}. The remaining built-in functions take
> +arguments of explicit integer types and make it possible to determine
> +overflow only in the ranges of those types. Since the only provided forms
> +of these latter built-in functions are for the signed and unsigned variants
> +of types @code{int}, @code{long}, and @code{long long}, they cannot be used
> +to determine overflow in other integer types.
> +
> +To enable the efficient integer overflow detection at translation-time,
> +in C and C++ 11 and later programs (but not in C++ 98), the first built-in
AFAIK the docs use either C++98, C++11 (without space), or
ISO/IEC 14882:2011 etc., but not C++ 98 or C++ 11.
Also, perhaps just a documentation thing, it would be good to clarify the
NULL last argument. From the POV of generating efficient code, I think we
should say something that the last argument (for the GNU builtins) must be
either a pointer to a valid object, or NULL/nullptr constant cast expression
cast to a pointer type, but nothing else. That is actually what your patch
implements. But, I'd like to make sure that
int *p = NULL;
if (__builtin_add_overflow (a, b, p))
...
is actually not valid, otherwise we unnecessarily pessimize many of the GNU
style calls (those that don't pass &var), where instead of
tem = ADD_OVERFLOW (a, b);
*p = REALPART_EXPR (tem);
ovf = IMAGPART_EXPR (tem);
we'd need to emit instead
tem = ADD_OVERFLOW (a, b);
ovf = IMAGPART_EXPR (tem);
if (p != NULL)
*p = REALPART_EXPR (tem);
Jakub