diff options
author | Jakub Jelinek <jakub@redhat.com> | 2024-06-04 12:28:01 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2024-06-11 12:35:39 +0200 |
commit | 91a371254494934e191e3060ae2a86905eb4b2b2 (patch) | |
tree | faffc9b1245ce45c85cb820ae56637b053fd320c | |
parent | bda8c28e6fcdbe0b486b54616877eec32c86d322 (diff) | |
download | gcc-91a371254494934e191e3060ae2a86905eb4b2b2.zip gcc-91a371254494934e191e3060ae2a86905eb4b2b2.tar.gz gcc-91a371254494934e191e3060ae2a86905eb4b2b2.tar.bz2 |
builtins: Force SAVE_EXPR for __builtin_{add,sub,mul}_overflow [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.
* gcc.c-torture/execute/pr108789.c: New test.
(cherry picked from commit b8e28381cb5c0cddfe5201faf799d8b27f5d7d6c)
-rw-r--r-- | gcc/builtins.cc | 16 | ||||
-rw-r--r-- | gcc/testsuite/gcc.c-torture/execute/pr108789.c | 39 |
2 files changed, 54 insertions, 1 deletions
diff --git a/gcc/builtins.cc b/gcc/builtins.cc index 57929a4..f919470 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -9180,7 +9180,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); diff --git a/gcc/testsuite/gcc.c-torture/execute/pr108789.c b/gcc/testsuite/gcc.c-torture/execute/pr108789.c new file mode 100644 index 0000000..32ee19b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/pr108789.c @@ -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 (); +} |