This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Fix PR66313
On Tue, Jun 13, 2017 at 12:48 PM, Richard Biener <rguenther@suse.de> wrote:
> On Tue, 13 Jun 2017, Richard Sandiford wrote:
>
>> Richard Biener <rguenther@suse.de> writes:
>> > On Tue, 13 Jun 2017, Richard Sandiford wrote:
>> >> Richard Biener <rguenther@suse.de> writes:
>> >> > So I've come back to PR66313 and found a solution to the tailrecursion
>> >> > missed optimization when fixing the factoring folding to use an unsigned
>> >> > type when we're not sure of overflow.
>> >> >
>> >> > The folding part is identical to my last try from 2015, the tailrecursion
>> >> > part makes us handle intermittent stmts that were introduced by foldings
>> >> > that "clobber" our quest walking the single-use chain of stmts between
>> >> > the call and the return (and failing at all stmts that are not part
>> >> > of said chain). A simple solution is to move the stmts that are not
>> >> > part of the chain and that we can move before the call. That handles
>> >> > the leaf conversions that now appear for tree-ssa/tailrecursion-6.c
>> >> >
>> >> > Bootstrapped on x86_64-unknown-linux-gnu, testing in progress.
>> >> >
>> >> > Richard.
>> >> >
>> >> > 2017-05-31 Richard Biener <rguenther@suse.de>
>> >> >
>> >> > PR middle-end/66313
>> >> > * fold-const.c (fold_plusminus_mult_expr): If the factored
>> >> > factor may be zero use a wrapping type for the inner operation.
>> >> > * tree-tailcall.c (independent_of_stmt_p): Pass in to_move bitmap
>> >> > and handle moved defs.
>> >> > (process_assignment): Properly guard the unary op case. Return a
>> >> > tri-state indicating that moving the stmt before the call may allow
>> >> > to continue. Pass through to_move.
>> >> > (find_tail_calls): Handle moving unrelated defs before
>> >> > the call.
>> >> >
>> >> > * c-c++-common/ubsan/pr66313.c: New testcase.
>> >> > * gcc.dg/tree-ssa/loop-15.c: Adjust.
>> >> >
>> >> > Index: gcc/fold-const.c
>> >> > ===================================================================
>> >> > *** gcc/fold-const.c.orig 2015-10-29 12:32:33.302782318 +0100
>> >> > --- gcc/fold-const.c 2015-10-29 14:08:39.936497739 +0100
>> >> > *************** fold_plusminus_mult_expr (location_t loc
>> >> > *** 6916,6925 ****
>> >> > }
>> >> > same = NULL_TREE;
>> >> >
>> >> > ! if (operand_equal_p (arg01, arg11, 0))
>> >> > ! same = arg01, alt0 = arg00, alt1 = arg10;
>> >> > ! else if (operand_equal_p (arg00, arg10, 0))
>> >> > same = arg00, alt0 = arg01, alt1 = arg11;
>> >> > else if (operand_equal_p (arg00, arg11, 0))
>> >> > same = arg00, alt0 = arg01, alt1 = arg10;
>> >> > else if (operand_equal_p (arg01, arg10, 0))
>> >> > --- 6916,6926 ----
>> >> > }
>> >> > same = NULL_TREE;
>> >> >
>> >> > ! /* Prefer factoring a common non-constant. */
>> >> > ! if (operand_equal_p (arg00, arg10, 0))
>> >> > same = arg00, alt0 = arg01, alt1 = arg11;
>> >> > + else if (operand_equal_p (arg01, arg11, 0))
>> >> > + same = arg01, alt0 = arg00, alt1 = arg10;
>> >> > else if (operand_equal_p (arg00, arg11, 0))
>> >> > same = arg00, alt0 = arg01, alt1 = arg10;
>> >> > else if (operand_equal_p (arg01, arg10, 0))
>> >> > *************** fold_plusminus_mult_expr (location_t loc
>> >> > *** 6974,6987 ****
>> >> > }
>> >> > }
>> >> >
>> >> > ! if (same)
>> >> > return fold_build2_loc (loc, MULT_EXPR, type,
>> >> > fold_build2_loc (loc, code, type,
>> >> > fold_convert_loc (loc, type, alt0),
>> >> > fold_convert_loc (loc, type, alt1)),
>> >> > fold_convert_loc (loc, type, same));
>> >> >
>> >> > ! return NULL_TREE;
>> >> > }
>> >> >
>> >> > /* Subroutine of native_encode_expr. Encode the INTEGER_CST
>> >> > --- 6975,7010 ----
>> >> > }
>> >> > }
>> >> >
>> >> > ! if (!same)
>> >> > ! return NULL_TREE;
>> >> > !
>> >> > ! if (! INTEGRAL_TYPE_P (type)
>> >> > ! || TYPE_OVERFLOW_WRAPS (type)
>> >> > ! /* We are neither factoring zero nor minus one. */
>> >> > ! || TREE_CODE (same) == INTEGER_CST)
>> >> > return fold_build2_loc (loc, MULT_EXPR, type,
>> >> > fold_build2_loc (loc, code, type,
>> >> > fold_convert_loc (loc, type, alt0),
>> >> > fold_convert_loc (loc, type, alt1)),
>> >> > fold_convert_loc (loc, type, same));
>> >> >
>> >> > ! /* Same may be zero and thus the operation 'code' may overflow. Likewise
>> >> > ! same may be minus one and thus the multiplication may overflow. Perform
>> >> > ! the operations in an unsigned type. */
>> >> > ! tree utype = unsigned_type_for (type);
>> >> > ! tree tem = fold_build2_loc (loc, code, utype,
>> >> > ! fold_convert_loc (loc, utype, alt0),
>> >> > ! fold_convert_loc (loc, utype, alt1));
>> >> > ! /* If the sum evaluated to a constant that is not -INF the multiplication
>> >> > ! cannot overflow. */
>> >> > ! if (TREE_CODE (tem) == INTEGER_CST
>> >> > ! && ! wi::eq_p (tem, wi::min_value (TYPE_PRECISION (utype), SIGNED)))
>> >> > ! return fold_build2_loc (loc, MULT_EXPR, type,
>> >> > ! fold_convert (type, tem), same);
>> >> > !
>> >> > ! return fold_convert_loc (loc, type,
>> >> > ! fold_build2_loc (loc, MULT_EXPR, utype, tem,
>> >> > ! fold_convert_loc (loc, utype, same)));
>> >> > }
>> >> >
>> >> > /* Subroutine of native_encode_expr. Encode the INTEGER_CST
>> >>
>> >> Sorry if you already know, but this part means that we can no longer
>> >> vectorise:
>> >>
>> >> int
>> >> f (int *x, int b1, int b2, int b3)
>> >> {
>> >> int foo = 0;
>> >> for (int i1 = 0; i1 < b1; ++i1)
>> >> for (int i2 = 0; i2 < b2; ++i2)
>> >> for (int i3 = 0; i3 < b3; ++i3)
>> >> foo += x[i1 * b2 * b3 + i2 * b3 + (i3 - 1)];
>> >> return foo;
>> >> }
>> >>
>> >> We now convert all the arithmetic in the [...] to unsigned int
>> >> and reassociate it so that the "- 1" is applied last. We then assume
>> >> that the overflow in this subtraction is well-defined and that the
>> >> &x[...] might not be linear for the inner loop.
>> >
>> > Can you file a PR?
>>
>> OK, filed as PR81082.
>>
>> > # i3_44 = PHI <i3_32(6), 0(4)>
>> > i3.2_7 = (unsigned int) i3_44;
>> > _9 = i3.2_7 + _34;
>> > _10 = (int) _9;
>> > _11 = (long unsigned int) _10;
>> > _12 = _11 * 4;
>> > _13 = x_30(D) + _12;
>> > _14 = *_13;
>> > ...
>> > i3_32 = i3_44 + 1;
>> >
>> > so we have *(x_30(D) + 4 * ((sizetype)(int)((unsigned){ 0, + , 1} + X))
>> >
>> > It might mean that we need to avoid this re-association. Not sure how
>> > to detect worthwhile vs. not worthwhile cases during folding. At least
>> > I see no easy way to recover from it in SCEV analysis unless the
>> > number of iterations is constrained more than in your example.
>>
>> Yeah, in the example that this was reduced from, not reassociating
>> is far more preferable to changing the types. But like you say,
>> I've no idea how we'd know that at this stage.
>
> In the past I played with not obfuscating things during FE lowering
> so early, namely not expose the * 4 but keep the original types of
> array / pointer indexes. On the original mem-ref branch I had
> a IDX_EXPR that allowed to chain those (a widen-multiply-accumulate
> op).
>
> That said, the context where this association is not interesting is
> address context and the multiplication to the byte offset.
>
> Of course in your case there's also b3 factored out which looks
> like a generally interesting transform (but eventually harmful in address
> context again).
>
> From the FE we get
>
> *(x + (sizetype) ((long unsigned int) (((i1 * b2) * b3 + i2 * b3) + (i3 +
> -1)) * 4))
>
> and SCEV uses the fact that the mults/adds have undefined behavior
> on overflow. The same missed optimization occurs if you make all those
> vars unsigned (with or without the folding in question):
But with unsigned type it's not missed optimization because
computation could overflow and result in non-scev address. In this
case the loop needs to be versioned under overflow/non-overflow to be
parallelized/vectorized, right? Or split for cases we can easily
infer boundary condition of overflow.
Thanks,
bin
>
> #define U unsigned
> int
> f (int *x, U int b1, U int b2, U int b3)
> {
> int foo = 0;
> for (U int i1 = 0; i1 < b1; ++i1)
> for (U int i2 = 0; i2 < b2; ++i2)
> for (U int i3 = 0; i3 < b3; ++i3)
> foo += x[i1 * b2 * b3 + i2 * b3 + (i3 - 1)];
> return foo;
> }
>
> It works again for unsigned long as there can be no valid object
> where the computation could wrap around.
>
> Richard.
>
> --
> Richard Biener <rguenther@suse.de>
> SUSE LINUX GmbH, GF: Felix Imendoerffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nuernberg)