[Patch] GVN fold conditional-branch on-the-fly

Daniel Berlin dberlin at dberlin.org
Fri Sep 6 20:03:01 PDT 2013


Is there a reason to not just use a better GVN algorithm?
Why add a set of complicated conditions to fold code on the fly, when
almost any other GVN algorithm than the current one would properly
detect the cases you are talking about.

In particular, you say: "GVN prove x > 0, making the else-clause dead.
If GVN were not able to prune
dead-edge <else-clause, JoinNode>, value "foo" (could be constant) would not
be able propagated to S3."

This is false.  It does not need to prune this edge, it just needs an
algorithm that doesn't consider the edge taken if the edge is provably
not taken.

There are plenty of these algorithms, most even more powerful than the
test cases you want done, for example,
http://dl.acm.org/citation.cfm?id=512536

GVN is getting more and more complicated, and is already a compile time sink.

Is this really the best way to add this functionality?


On Fri, Sep 6, 2013 at 7:26 PM, Shuxin Yang <shuxin.llvm at gmail.com> wrote:
> Hi,
>
>   The attached the patch is about GVN folding conditional branch on the fly.
>  The problem is tracked by
>    rdar://13017207, and
>    PR12036
>
>    With this patch, the "wc -l
> /the/resulting/*.ll/of-the-motivating-example"
> reduce from 109 to 86.
>
>    I also saw bunch of marginal improvement to multi/single-source testing
> cases.
> Among them, I guess following two are likely genuine, as I can reproduce the
> diff
> over and over again.
>
>  Applications/aha/aha               1.8524      1.7546 -5.2796372273807
>  Benchmarks/ASC_Sequoia/AMGmk/AMGmk     6.4171      6.1721 -3.81792398435429
>
> Thanks
> Shuxin
>
>
>
> 1. The Problem
> ===============
>
>   As GVN progress, it can reveal that some branch conditions are constants,
> which implies one target the the condition branch become dead. We need to
> either physically delete the dead code on-the-fly, or effectively "ignore"
> them. It would otherwise has negative impact to the performance.
>
>  Consider this example:
>
>  GVN prove x > 0, making the else-clause dead. If GVN were not able to prune
> dead-edge <else-clause, JoinNode>, value "foo" (could be constant) would not
> be able propagated to S3.
>
>  -------
>   x = ...
>   if(x)
>      y1 = ... foo ...;  // S1
>   else {
>      // this branch become dead
>      y2 = bar;  //S2
>   }
>
> JoinNode:
>   y3 = phi(y1, y2);
>        = y3 // S3
>  ----------
>
> 2. Possible Solutions:
> ======================
>
>  At first glance, it seems there are at least two ways to tackle this
> problem:
>
>  w1) run GVN multiple times.
>  w2) Just ignore the dead code, whithout physically remove them.
>     (as with the conditional constant propagation).
>  w3) Physically remove the dead code.
>
>  w1) is certainly viable, but some people are pretty picky at compile-time.
> They will be mad at me if solve the problem this way:-)
>
>  The problem of w2) is that we may end up with the code where definition
> dose not necessarily dominate its use. Follow above snippet. Suppose y1
> dosen't hold constant value. By ignoring the dead-code, the GVN will
> replace the y3 with y1...
>
>   The difficulty arising from w3) is that it is generally pretty difficult
> and expensive to incrementally update dominator-tree[1]. In some situations
> maintaining dom-tree is quite easy, and I believe they happen to be common
> cases.
>
> 3. My solution
> ==============
>
> 3.1. Notations:
> ==================
>     - block: ref the basic-block in cfg, also ref to the corresponding
>              to node in dom-tree unless otherwise noted.
>     - pred/succ : ref to the node relationship in CFG
>     - child/parent: ref to the node relationship in dom-tree
>     - IDOM(n):  immediat dominator of n.
>     - DF(n): dominance-frontier.
>     - LCA(set-of-blks) : least-common-ancestor of given set-of-blks in
> dom-tree.
> 3.2. Catch common-case in C
> ===========================
>     The common case in C language would be something like following:
>     -------
>     if (cond)  // condition-blk
>       then-clause (assuming non-empty, rooted by then-root)
>     else
>       else-clause (assuming non-empty, rooted by else-root)
>
>     join-node
>     ------------
>
>     Where the :
>        - join-node post-dominate then both-caluse,
>        - there is no control xfter between the two clause,
>        - they may have early return, but the exit-point it exclusive
>          meaning, the exit-point is only rechable by the then- or
>          else-clause.
>
>     The catch this patten, compiler just need to examine if
>       DF(then-root) == DF(else-clause) == { join-node }
>
>     After one of the dead clause is deleted, dom-tree can be simply
>   maintained by setting IDOM(join-node) = LCA(preds(join-node)).
>
> 3.3. Catch common case in C++
> ===============================
>
>   The exception handling make things bit hard. If a function
> dose not explictly declare, there will be a default EH-cliche assocated
> to it, and these "cliche" are shared by all the functions which do not
> have enclosing try-catch statement.
>
>   e.g. c++ code
>      ----
>      foo()
>      if(...)
>         bar()
>      else
>         lol()
>
>      join:
>        ...
>     -----
>
>   The corresponing CFG:
>     ------------------
>      foo()
>      potententally goto landing-pad :
>      if(...) {
>         bar()
>         potententally goto landing-pad :
>      else {
>         lol()
>         potententally goto landing-pad :
>      }
>
>      join:
>        ...
>
>      landing-pad :
>        unwind...; lol; resume brabra.
>      -----------------------
>
>   In this case, the trick we find for C no longer apply because
>  DF(the-root) = {join-node, landing-pad}. The good news is most function
>  call are not guarded by try-catch statements, which translate to the
>  landing-pad dominate all those cliches it is reachable! Maintain dom-tree
>  for the sub-cfg rooted by such landing-pad is trivial
> 3.4. The algorithm:
>  ====================
>    Combined 3.2 and 3.3. we have following algirhtm (to ease the
> description,
>    assume neither then- nor else-caluse is empty).
>
>   when come-across a cond-br B with constant condition.
>
>   1. let DR/LR be root node of dead-target/live-target;
>   2. get DF(DR) and DF(LR) (by walking the CFG several steps).
>      div DF() in two parts:
>       DF_lp(N) = { X | X in DF(N) && X is a landing part }
>       DF_nonlp(N) = DF(N) - DF_lp(N);
>
>   3. If following two conditions are satified, we find the pattern:
>     c1: DF_nonlp(DR) == DF_nonlp(LR) = { a-node-which-we-call-join-node}
>     c2: for all node N in DF_lp(*), N must dominate all nodes it is
> reachable.
>
>   4. delete edges connecting dead and live blocks.
>   5. for all nodes N which are adjancent to dead block, do
>       IDOM(N) = LCA(preds(N))
>
>   6. right before GVN finish, delete all dead blocks.
>
> References:
> [1] Some awesome guys,
>    An Incremental Algorithm for Maintaining the Dominator Tree of a
> Reducible Flowgraph,
>    http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.5.9293
>
>
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>



More information about the llvm-commits mailing list