builtins: Force SAVE_EXPR for __builtin_{add,sub,mul}_overflow and __builtin{add,sub}c [PR108789]
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). 2024-06-04 Jakub Jelinek <jakub@redhat.com> 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. (cherry picked from commit b8e28381cb5c0cddfe5201faf799d8b27f5d7d6c)
This commit is contained in:
parent
1c1bc2553f
commit
f9af4a05e0
2 changed files with 60 additions and 1 deletions
|
@ -10042,7 +10042,21 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
|
|||
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, enum built_in_function fcode,
|
|||
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);
|
||||
|
|
39
gcc/testsuite/gcc.c-torture/execute/pr108789.c
Normal file
39
gcc/testsuite/gcc.c-torture/execute/pr108789.c
Normal file
|
@ -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 ();
|
||||
}
|
Loading…
Add table
Reference in a new issue