Richard Biener <richard.guent...@gmail.com> writes:
>>>> diff --git a/gcc/fold-const.c b/gcc/fold-const.c
>>>> index de45a2c..7f00e72 100644
>>>> --- a/gcc/fold-const.c
>>>> +++ b/gcc/fold-const.c
>>>> @@ -319,7 +318,7 @@ fold_overflow_warning (const char* gmsgid, enum
>>> warn_strict_overflow_code wc)
>>>>  /* Return true if the built-in mathematical function specified by CODE
>>>>     is odd, i.e. -f(x) == f(-x).  */
>>>>
>>>> -static bool
>>>> +bool
>>>>  negate_mathfn_p (enum built_in_function code)
>>>>  {
>>>>    switch (code)
>>>
>>> Belongs more to builtins.[ch] if exported.
>>
>> The long-term plan is to abstract away whether it's a built-in function
>> or an internal function, in which case I hope to have a single predicate
>> that handles both.  I'm not sure where the code should go after that change.
>> Maybe a new file?
>
> Hmm, we'll see.  So just leave it in fold-const.c for now.

OK, thanks.

>>>> diff --git a/gcc/fold-const.h b/gcc/fold-const.h
>>>> index ee74dc8..4d5b24b 100644
>>>> --- a/gcc/fold-const.h
>>>> +++ b/gcc/fold-const.h
>>>> @@ -173,6 +173,7 @@ extern tree sign_bit_p (tree, const_tree);
>>>>  extern tree exact_inverse (tree, tree);
>>>>  extern tree const_unop (enum tree_code, tree, tree);
>>>>  extern tree const_binop (enum tree_code, tree, tree, tree);
>>>> +extern bool negate_mathfn_p (enum built_in_function);
>>>>
>>>>  /* Return OFF converted to a pointer offset type suitable as offset for
>>>>     POINTER_PLUS_EXPR.  Use location LOC for this conversion.  */
>>>> diff --git a/gcc/passes.def b/gcc/passes.def
>>>> index dc3f44c..36d2b3b 100644
>>>> --- a/gcc/passes.def
>>>> +++ b/gcc/passes.def
>>>> @@ -159,6 +159,7 @@ along with GCC; see the file COPYING3.  If not see
>>>>        /* After CCP we rewrite no longer addressed locals into SSA
>>>>          form if possible.  */
>>>>        NEXT_PASS (pass_complete_unrolli);
>>>> +      NEXT_PASS (pass_backprop);
>>>>        NEXT_PASS (pass_phiprop);
>>>>        NEXT_PASS (pass_forwprop);
>>>
>>> Any reason to not put this later?  I was thinking before reassoc.
>>
>> I think we're relying on FRE to notice the redundancy in the
>> builtins-*.c tests, once this pass has converted the version
>> with redundant sign ops to make it look like the version without.
>> reassoc is likely to be too late.
>
> There is PRE after reassoc (run as FRE at -O1).

Ah, OK.  It looks like that also runs after sincos though, whereas
I think we want an FRE between this pass and sincos.

>> I also thought it should go before rather than after some instance
>> of forwprop because the pass might expose more forward folding
>> opportunities.  E.g. if the sign of A = -B * B doesn't matter,
>> we'll end up with A = B * B, which might be foldable with uses of A.
>> It seems less likely that forwprop would expose more backprop
>> opportunities.
>
> Indeed.  I was asking because backprop runs after inlining but
> with nearly no effective scalar cleanup after it to cleanup after
> inlining.
>
> In principle it only depends on some kind of DCE (DSE?) to avoid
> false uses, right?

Yeah, that sounds right.  Complex control-flow leading up to uses
shouldn't be a problem, but phantom uses would be a blocker.
>
> It's probably ok where you put it, I just wanted to get an idea of your
> reasoning.
>
>>
>>>> +/* Make INFO describe all uses of RHS in ASSIGN.  */
>>>> +
>>>> +void
>>>> +backprop::process_assign_use (gassign *assign, tree rhs, usage_info *info)
>>>> +{
>>>> +  tree lhs = gimple_assign_lhs (assign);
>>>> +  switch (gimple_assign_rhs_code (assign))
>>>> +    {
>>>> +    case ABS_EXPR:
>>>> +      /* The sign of the input doesn't matter.  */
>>>> +      info->flags.ignore_sign = true;
>>>> +      break;
>>>> +
>>>> +    case COND_EXPR:
>>>> +      /* For A = B ? C : D, propagate information about all uses of A
>>>> +        to B and C.  */
>>>> +      if (rhs != gimple_assign_rhs1 (assign))
>>>> +       if (const usage_info *lhs_info = lookup_operand (lhs))
>>>
>>> Use && instead of nested if
>>
>> That means introducing an extra level of braces just for something
>> that that isn't needed by the first statement, i.e.:
>>
>>     {
>>       const usage_info *lhs_info;
>>       if (rhs != gimple_assign_rhs1 (assign)
>>           && (lhs_info = lookup_operand (lhs)))
>>         *info = *lhs_info;
>>       break;
>>     }
>>
>> There also used to be a strong preference for not embedding assignments
>> in && and || conditions.
>>
>> If there had been some other set-up for the lookup_operand call, we
>> would have had:
>>
>>       if (rhs != gimple_assign_rhs1 (assign))
>>         {
>>           ...
>>           if (const usage_info *lhs_info = lookup_operand (lhs))
>>             ..
>>         }
>>
>> and presumably that would have been OK.  So if the original really isn't,
>> acceptable, I'd rather write it as:
>>
>>       if (rhs != gimple_assign_rhs1 (assign))
>>         {
>>           const usage_info *lhs_info = lookup_operand (lhs);
>>           if (lhs_info)
>>             ..
>>         }
>>
>> I thought we'd embraced the idea of declarations in if conditions though.
>
> I missed the init in if ... yeah, there doesn't seem to be a good way
> though I prefer {} around the inner if at least which means the last
> form would be my reference.

OK.

>>>> +/* Process all uses of VAR and record or update the result in
>>>> +   M_INFO_MAP and M_VARS.  */
>>>> +
>>>> +void
>>>> +backprop::process_var (tree var)
>>>> +{
>>>> +  if (has_zero_uses (var))
>>>> +    return;
>>>> +
>>>> +  usage_info info;
>>>> +  intersect_uses (var, &info);
>>>
>>> It feels somewhat backward that we do a backward walk over all stmts
>>> (thus processing all use-stmts before defs) but then "forward" process
>>> uses of defs.  Wouldn't it be easier(?) to "intersect" incrementally
>>> by processing only uses for all stmts we walk?  Or is this about
>>> avoiding to allocate a 'info' for DEFs that end up with no interesting
>>> info?
>>
>> Yeah, that was the idea.  In practice very few SSA names end up
>> with any useful information.
>
> Ok, let's add a comment somewhere noting that and the choice of
> "backward forward propagation".

Will do.  I think I'll also rework it to be lazy, which should be
more efficient as well as more obvious.  Not sure why I didn't do
that in the first place.

>>>> +/* Strip all sign operations from the rvalue at *RHS_PTR in STMT.
>>>> +   Return true if something changed.  The caller is responsible
>>>> +   for the necessary bookkeeping.  */
>>>> +
>>>> +static bool
>>>> +strip_sign_op (gimple *stmt, tree *rhs_ptr)
>>>> +{
>>>> +  if (tree new_rhs = strip_sign_op (*rhs_ptr))
>>>> +    {
>>>> +      if (dump_file && (dump_flags & TDF_DETAILS))
>>>> +       note_replacement (stmt, *rhs_ptr, new_rhs);
>>>> +      *rhs_ptr = new_rhs;
>>>
>>> So it looks you are only changing stmts when the stmt result produces
>>> the same value.  Just double-checking, as otherwise you'd need to care
>>> about debug stmts ...
>>
>> No, it can change values, like the case you saw later for phis.
>> This applies to all the cases where the optimisation depends on the
>> propagated info for the lhs, rather than being inherent to the operation.
>> So e.g. we can change the value of A in A = B * C, if all uses of A
>> don't care about the sign.
>>
>> At the moment the only change we can make is that the result could be
>> the negative of its original value.
>
> Ok, so then there is the debug issue.  Consider
>
>   x = ...;
>
> which you change the sign for.  The user in gdb when printing 'x' needs to
> see the original value or "optimized out", not the negated value.  This means
> you have to replace the LHS of the stmt with a new SSA name which is
> best done (with proper debug effects) by removing the original stmt and
> replacing all uses of the LHS with the new stmt lhs.  Note that the
> same is true for all derived values, so even if you only change
>
>  _1 = ...;
>
> (no user visible value) then a derived value
>
>
>  x_2 = _1 + 2;
>
> might change sign.
>
> Well, you have to think about it at least ;)

OK, will fix.

Thanks,
Richard

Reply via email to