[PATCH] D29092: PR31729: [GVNHoist] Don't hoist unsafe scalars at -Oz

Aditya Kumar via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 23 21:14:12 PST 2017


hiraditya added a comment.

In https://reviews.llvm.org/D29092#685307, @dberlin wrote:

> In https://reviews.llvm.org/D29092#685273, @mehdi_amini wrote:
>
> > In https://reviews.llvm.org/D29092#685265, @dberlin wrote:
> >
> > > (Though i quibble with this part:
> > >
> > >   // The called function could have undefined behavior or
> > >   // side-effects, even if marked readnone nounwind.
> > >   
> > >
> > > I'm not sure what we think those side effects are, but we should be modeling them or ignoring them.
> >
> >
> > Divide by zero?
> >
> > The only current solution we have for this now is to be conservative with respect to calls. There is a new attribute implemented here https://reviews.llvm.org/D20116 that will help making it "less conservative", by allowing to speculate if there can't be UB whatever arguments are passed to the function. 
> >  This wouldn't capture this for instance:
> >
> >   // can be speculated if we know that the argument is never 0
> >   int inverse(int n) { return 1/n; }
> >
> >
> > From your GCC experience, do you have a suggestion how to address/model this?
>
>
> First:
>
>   int __attribute__((noinline))   inverse(int n) { return 1/n; }
>    int main(int argc)
>    {
>  
>     int a = 0;
>     for (int i = 0; i < 5; ++i)
>         a+= inverse(argc);
>   return 0
>   }
>  
>
>
> Is optimized to return 0 at all opt levels (ie it does not consider it enough of a side effect to keep it alive)
>  (it's the same whether it's inlined or not)
>  If you change it to return a, you get:
>
>   main (int argc)
>    {
>      int i;
>      int a;
>      int _16;
>      _16 = inverse (argc_5(D));
>      # a_14 = PHI <a_7(3), 0(2)>
>      # i_15 = PHI <i_8(3), 0(2)>
>      a_7 = a_14 + _16;
>      i_8 = i_15 + 1;
>      if (i_8 != 5)
>        goto <bb 3>;
>      else
>        goto <bb 4>;
>      return a_7;
>  
>    }
>  
>
>
> Note the hoist.
>
> It then unrolls the loop
>  resulting in:
>
>   main (int argc)
>    {
>      int a;
>      int _16;
>  
>      _16 = inverse (argc_5(D));
>      a_2 = _16 * 5;
>      return a_2;
>  
>    }
>  
>  
>
>
> If you change it to something it can't prove always executes, it still hoists it to the pre-header, just not above loop condition (obviously).
>  So i guess my answer is: We are being too conservative here.


Since gcc's gvn hoist works in tandem with gvn-pre, the hoist of a call which occurs only on one branch may have been done by gvn-pre. IIUC gcc's gvn-hoist only hoists instructions which are anticipable (IN) but not available (OUT), that too with basic blocks with > 1 successors (ref: gcc/tree-ssa-pre.c:3499). It is unlikely gvn-hoist is hoisting the call in this example. Another limitation of gcc's gvn-hoist is that it only hoists when at least one immediate successor has the instruction in avail_out. So even if an expression is very busy it may not get hoisted. I understand that this allows them to
not have any cost model for hoisting too far but code-size wise there will be missed opportunities.

I agree the design of llvm's gvn-hoist  is not very clean mostly because it went multiple iterations, your ideas helped very much to write an optimistic algorithm and incorporate Memory SSA.

> See https://reviews.llvm.org/D8688#a1aa52c6
>  computeBarriers, for a way to compute the barriers once and be able to constant time check whether they are in the way.
>  (since you have dfs numbers for the instructions)

Thanks, I'll study them.


https://reviews.llvm.org/D29092





More information about the llvm-commits mailing list