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 <ja...@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. > > --- 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 <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)