On Tue, 4 Jun 2024, Jakub Jelinek wrote:
> Hi!
>
> The following testcase is miscompiled, because we use save_expr
> on the .{ADD,SUB,MUL}_OVERFLOW call we are creating, but if the first
> two operands are not INTEGER_CSTs (in that case we just fold it right away)
> but are TREE_READONLY/!TREE_SIDE_EFFECTS, save_expr doesn't actually
> create a SAVE_EXPR at all and so we lower it to
> *arg2 = REALPART_EXPR (.ADD_OVERFLOW (arg0, arg1)), \
> IMAGPART_EXPR (.ADD_OVERFLOW (arg0, arg1))
> which evaluates the ifn twice and just hope it will be CSEd back.
> As *arg2 aliases *arg0, that is not the case.
> The builtins are really never const/pure as they store into what
> the third arguments points to, so after handling the INTEGER_CST+INTEGER_CST
> case, I think we should just always use SAVE_EXPR. Just building SAVE_EXPR
> by hand and setting TREE_SIDE_EFFECTS on it doesn't work, because
> c_fully_fold optimizes it away again, so the following patch marks the
> ifn calls as TREE_SIDE_EFFECTS (but doesn't do it for the
> __builtin_{add,sub,mul}_overflow_p case which were designed for use
> especially in constant expressions and don't really evaluate the
> realpart side, so we don't really need a SAVE_EXPR in that case).
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK.
Thanks,
Richard.
> 2024-06-04 Jakub Jelinek <[email protected]>
>
> PR middle-end/108789
> * builtins.cc (fold_builtin_arith_overflow): For ovf_only,
> don't call save_expr and don't build REALPART_EXPR, otherwise
> set TREE_SIDE_EFFECTS on call before calling save_expr.
> (fold_builtin_addc_subc): Set TREE_SIDE_EFFECTS on call before
> calling save_expr.
>
> * gcc.c-torture/execute/pr108789.c: New test.
>
> --- gcc/builtins.cc.jj 2024-04-05 09:19:47.899050410 +0200
> +++ gcc/builtins.cc 2024-06-03 17:27:11.071693074 +0200
> @@ -10042,7 +10042,21 @@ fold_builtin_arith_overflow (location_t
> tree ctype = build_complex_type (type);
> tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
> arg0, arg1);
> - tree tgt = save_expr (call);
> + tree tgt;
> + if (ovf_only)
> + {
> + tgt = call;
> + intres = NULL_TREE;
> + }
> + else
> + {
> + /* Force SAVE_EXPR even for calls which satisfy tree_invariant_p_1,
> + as while the call itself is const, the REALPART_EXPR store is
> + certainly not. And in any case, we want just one call,
> + not multiple and trying to CSE them later. */
> + TREE_SIDE_EFFECTS (call) = 1;
> + tgt = save_expr (call);
> + }
> intres = build1_loc (loc, REALPART_EXPR, type, tgt);
> ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
> ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
> @@ -10354,11 +10368,17 @@ fold_builtin_addc_subc (location_t loc,
> tree ctype = build_complex_type (type);
> tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
> args[0], args[1]);
> + /* Force SAVE_EXPR even for calls which satisfy tree_invariant_p_1,
> + as while the call itself is const, the REALPART_EXPR store is
> + certainly not. And in any case, we want just one call,
> + not multiple and trying to CSE them later. */
> + TREE_SIDE_EFFECTS (call) = 1;
> tree tgt = save_expr (call);
> tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
> tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
> call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
> intres, args[2]);
> + TREE_SIDE_EFFECTS (call) = 1;
> tgt = save_expr (call);
> intres = build1_loc (loc, REALPART_EXPR, type, tgt);
> tree ovfres2 = build1_loc (loc, IMAGPART_EXPR, type, tgt);
> --- gcc/testsuite/gcc.c-torture/execute/pr108789.c.jj 2024-06-03
> 17:15:01.143366766 +0200
> +++ gcc/testsuite/gcc.c-torture/execute/pr108789.c 2024-06-03
> 17:12:55.189036744 +0200
> @@ -0,0 +1,39 @@
> +/* PR middle-end/108789 */
> +
> +int
> +add (unsigned *r, const unsigned *a, const unsigned *b)
> +{
> + return __builtin_add_overflow (*a, *b, r);
> +}
> +
> +int
> +mul (unsigned *r, const unsigned *a, const unsigned *b)
> +{
> + return __builtin_mul_overflow (*a, *b, r);
> +}
> +
> +int
> +main ()
> +{
> + unsigned x;
> +
> + /* 1073741824U + 1073741824U should not overflow. */
> + x = (__INT_MAX__ + 1U) / 2;
> + if (add (&x, &x, &x))
> + __builtin_abort ();
> +
> + /* 256U * 256U should not overflow */
> + x = 1U << (sizeof (int) * __CHAR_BIT__ / 4);
> + if (mul (&x, &x, &x))
> + __builtin_abort ();
> +
> + /* 2147483648U + 2147483648U should overflow */
> + x = __INT_MAX__ + 1U;
> + if (!add (&x, &x, &x))
> + __builtin_abort ();
> +
> + /* 65536U * 65536U should overflow */
> + x = 1U << (sizeof (int) * __CHAR_BIT__ / 2);
> + if (!mul (&x, &x, &x))
> + __builtin_abort ();
> +}
>
> Jakub
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)