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

Shuxin Yang shuxin.llvm at gmail.com
Fri Sep 6 19:26:13 PDT 2013


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



-------------- next part --------------
Index: include/llvm/Analysis/Dominators.h
===================================================================
--- include/llvm/Analysis/Dominators.h	(revision 190205)
+++ include/llvm/Analysis/Dominators.h	(working copy)
@@ -532,6 +532,28 @@
     DomTreeNodes.erase(BB);
   }
 
+  /// Erase the entire sub-tree rooted by \Root from the dominator tree.
+  void eraseSubtree(NodeT *Root) {
+    DomTreeNodeBase<NodeT> *RN = getNode(Root);
+    assert(RN && "Removing node that isn't in dominator tree.");
+    
+    // Remove all the descendants.
+    if (!RN->Children.empty()) {
+      SmallVector<DomTreeNodeBase<NodeT> *, 4> Kids;
+      for (int I = 0, E = RN->Children.size(); I < E; I++)
+        Kids.push_back(RN->Children[I]);
+    
+      while(!Kids.empty()) {
+        DomTreeNodeBase<NodeT> *K = Kids.pop_back_val();
+        for (int I = 0, E = K->Children.size(); I < E; I++)
+          Kids.push_back(K->Children[I]);
+        DomTreeNodes.erase(K->getBlock());
+      }
+      RN->Children.clear();
+    }
+    eraseNode(Root);
+  }
+
   /// splitBlock - BB is split and now it has one successor. Update dominator
   /// tree to reflect this change.
   void splitBlock(NodeT* NewBB) {
@@ -850,6 +872,11 @@
     DT->eraseNode(BB);
   }
 
+  /// Erase the entire sub-tree rooted by \Root from the dominator tree.
+  inline void eraseSubtree(BasicBlock *Root) {
+    DT->eraseSubtree(Root);
+  }
+
   /// splitBlock - BB is split and now it has one successor. Update dominator
   /// tree to reflect this change.
   inline void splitBlock(BasicBlock* NewBB) {
Index: lib/Transforms/Scalar/GVN.cpp
===================================================================
--- lib/Transforms/Scalar/GVN.cpp	(revision 190205)
+++ lib/Transforms/Scalar/GVN.cpp	(working copy)
@@ -21,6 +21,7 @@
 #include "llvm/ADT/DepthFirstIterator.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AliasAnalysis.h"
 #include "llvm/Analysis/CFG.h"
@@ -45,6 +46,7 @@
 #include "llvm/Support/PatternMatch.h"
 #include "llvm/Target/TargetLibraryInfo.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
+#include "llvm/Transforms/Utils/Local.h"
 #include "llvm/Transforms/Utils/SSAUpdater.h"
 #include <vector>
 using namespace llvm;
@@ -62,6 +64,8 @@
                                cl::init(true), cl::Hidden);
 static cl::opt<bool> EnableLoadPRE("enable-load-pre", cl::init(true));
 
+static cl::opt<bool> EnableFoldCondBr("gvn-fold-condbr", cl::init(true));
+
 // Maximum allowed recursion depth.
 static cl::opt<uint32_t>
 MaxRecurseDepth("max-recurse-depth", cl::Hidden, cl::init(1000), cl::ZeroOrMore,
@@ -576,6 +580,8 @@
     DominatorTree *DT;
     const DataLayout *TD;
     const TargetLibraryInfo *TLI;
+    DenseMap<BasicBlock *, bool> SimpleLandPads;
+    SetVector<BasicBlock *> DeadBlocks;
 
     ValueTable VN;
 
@@ -687,6 +693,7 @@
     // Other helper routines
     bool processInstruction(Instruction *I);
     bool processBlock(BasicBlock *BB);
+    bool tryFoldCondBranch(BasicBlock *BB);
     void dump(DenseMap<uint32_t, Value*> &d);
     bool iterateOnFunction(Function &F);
     bool performPRE(Function &F);
@@ -698,6 +705,10 @@
     unsigned replaceAllDominatedUsesWith(Value *From, Value *To,
                                          const BasicBlockEdge &Root);
     bool propagateEquality(Value *LHS, Value *RHS, const BasicBlockEdge &Root);
+
+    bool isSimpleLandingPad(BasicBlock *B);
+    void pruneDeadIncidentEdge(BasicBlock *B);
+    void deleteDeadBlocks();
   };
 
   char GVN::ID = 0;
@@ -2280,6 +2291,8 @@
 
 /// runOnFunction - This is the main transformation entry point for a function.
 bool GVN::runOnFunction(Function& F) {
+  DEBUG(dbgs() << "GVN on function: " << F.getName() << "\n";);
+
   if (!NoLoads)
     MD = &getAnalysis<MemoryDependenceAnalysis>();
   DT = &getAnalysis<DominatorTree>();
@@ -2319,6 +2332,8 @@
     }
   }
 
+  deleteDeadBlocks();
+
   // FIXME: Should perform GVN again after PRE does something.  PRE can move
   // computations into blocks where they become fully redundant.  Note that
   // we can't do this until PRE's critical edge splitting updates memdep.
@@ -2329,8 +2344,10 @@
   return Changed;
 }
 
+bool GVN::processBlock(BasicBlock *BB) {
+  if (DeadBlocks.count(BB))
+    return false;
 
-bool GVN::processBlock(BasicBlock *BB) {
   // FIXME: Kill off InstrsToErase by doing erasing eagerly in a helper function
   // (and incrementing BI before processing an instruction).
   assert(InstrsToErase.empty() &&
@@ -2368,6 +2385,8 @@
       ++BI;
   }
 
+  ChangedFunction |= tryFoldCondBranch(BB);
+
   return ChangedFunction;
 }
 
@@ -2628,3 +2647,312 @@
     }
   }
 }
+
+// Try to figure out the dominance-frontier (DF) of given block by walking
+// the CFG several steps, starting from the given block. The "VisitLimit"
+// specifies the maximum number of blocks to be visited before it bails out.
+//
+// The DF is divided into two classes: non-landing-pad blocks and landing-pads
+// blocks. On success, these two classes of blocks will be returned via 
+// "RegularDF" and "LandPadDF" respectively, and "true" will be returned.
+//
+static bool tryGetDomFrontier(BasicBlock *BB,
+                              DominatorTree *DT,
+                              SmallVectorImpl<BasicBlock *> &RegularDF,
+                              SmallVectorImpl<BasicBlock *> &LandPadDF,
+                              SmallVectorImpl<BasicBlock *> *DomBlks = 0,
+                              int VisitLimit = 16) {
+  RegularDF.clear();
+  LandPadDF.clear();
+  if (DomBlks)
+    DomBlks->clear();
+
+  SetVector<BasicBlock *> Visited;
+  SmallVector<BasicBlock *, 32> WL;
+  int Count = 0;
+
+  // BFS the CFG starting from given BB
+  WL.push_back(BB);
+  while (!WL.empty()) {
+    BasicBlock *T = WL.pop_back_val();
+    if (Visited.count(T))
+      continue;
+
+    // Just give up if we come across such nasty situation.
+    if (T->hasAddressTaken())
+      return false;
+
+    Visited.insert(T);
+    if (DT->dominates(BB, T)) {
+      // Walk the CFG one step further.
+      for (succ_iterator I = succ_begin(T), E = succ_end(T); I != E; I++)
+        WL.push_back(*I); 
+    
+      if (DomBlks)
+        DomBlks->push_back(T);
+    } else {
+      // Come across a block belonging to DF.
+      if (!T->isLandingPad())
+        RegularDF.push_back(T);
+      else if (std::find(LandPadDF.begin(), LandPadDF.end(), T) == 
+               LandPadDF.end())
+        LandPadDF.push_back(T);
+    }
+
+    if (Count++ > VisitLimit)
+      return false;
+  }
+
+  return true;
+}
+
+// Return true if it is easy to prove that given "BB" dominates all
+// blocks reachable from "BB".
+//
+// Returning false dosen't necessarily mean the "BB" dosen't dominates
+// its reachable blocks. It could be the case when the "BB" has too
+// many reachable blocks, and the this function just give a conservative
+// answer in order to save compile time.
+//
+static bool dominateReachableBBs(BasicBlock *BB, DominatorTree *DT) {
+  SmallVector<BasicBlock *, 4> RegularDF;
+  SmallVector<BasicBlock *, 4> LandPadDF;
+
+  if (!tryGetDomFrontier(BB, DT, RegularDF, LandPadDF))
+    return false;
+
+  return RegularDF.empty() && LandPadDF.empty();
+}
+
+// Return true if the given block is a "simple" landing pad. A landing pad is
+// "simple" if it dorminates all blocks it is reachable.
+bool GVN::isSimpleLandingPad(BasicBlock *B) {
+  assert(B->isLandingPad() && "Not landing pad");
+  DenseMap<BasicBlock *, bool>::iterator I = SimpleLandPads.find(B);
+
+  if (I != SimpleLandPads.end())
+    return (*I).second;
+
+  bool D = dominateReachableBBs(B, DT);
+  SimpleLandPads[B] = D;
+
+  return D;
+}
+
+// Prune those edges which emanate from a dead block and is incident to the
+// given BB.
+void GVN::pruneDeadIncidentEdge(BasicBlock *BB) {
+  SmallVector<BasicBlock *, 4> Preds(pred_begin(BB), pred_end(BB));
+  while (!Preds.empty()) {
+    BasicBlock *P = Preds.pop_back_val();
+    if (!DeadBlocks.count(P))
+      continue;
+
+    // Update BB's phi nodes.
+    BB->removePredecessor(P);
+
+    // Physically unlink the dead predecessor and this block
+    if (TerminatorInst *PredBr = P->getTerminator()) {
+      MD->removeInstruction(PredBr);
+      if (!PredBr->use_empty())
+        PredBr->replaceAllUsesWith(UndefValue::get(PredBr->getType()));
+      PredBr->eraseFromParent();
+    }
+  }
+}
+
+// Let IDOM(B) denote the immediate dominator of block B. This function is to
+// set IDOM(BB) to be the least-common-ancestor of IDOM(Pred) for all the BB's
+// immediate predecessor "Pred" in CFG.
+// 
+static void updatePosInDomTree(BasicBlock *BB, DominatorTree *DT) {
+  SmallVector<BasicBlock *, 4> Preds(pred_begin(BB), pred_end(BB));
+
+  int PredNum = Preds.size();
+  assert(PredNum != 0 && "BB is dead");
+
+  if (PredNum == 1) {
+    DT->changeImmediateDominator(BB, Preds[0]);
+    return;
+  }
+
+  BasicBlock *ImmDom = DT->getNode(Preds[0])->getBlock();
+  for (int i = 1; i < PredNum; i++) {
+    BasicBlock *B = DT->getNode(Preds[i])->getBlock();
+    ImmDom = DT->findNearestCommonDominator(ImmDom, B);
+  }
+  DT->changeImmediateDominator(BB, ImmDom);
+}
+
+//   As GVN progress, it may reveal some conditions are constants. Compiler
+// needs to eliminate dead code on-the-fly. Otherwise, dead-edges incident
+// to live join-block will obscure or disable optimization opportunities.
+//
+//  The major difficuty of DCE on-the-fly is to incrementally mantain
+// the dominator tree(DT), which is in general pretty complicated and
+// expensive. We try to catch some common C/C++ cases which only entails
+// simple maintenance to the DT.
+//
+//   The cases we try to catch can be described by following pseudo-code:
+// To ease description, assume neither then- nor else-clause is empty, and
+// they are rooted by then-root, and else-root, respectively.
+//
+//  -------------
+//   if (cond)
+//     then-clause
+//   else
+//     else-clause
+//
+//   join-node:
+// ----------------
+//
+//   Let DF(n) denote dominance-frontier of n. We requires:
+//   - The intersection of DF(then-root) and DF(else-root) is { join-node }
+//   - DF(*) - {join-node} is a set of "simple" landing-pad. (A landing-pad
+//     is landing-pad is simple if dominate all blocks it is reachable.
+//
+//   In these cases, maintaining DT is trivial. Suppose then-clause is
+// removed. DT can be incrementally updated by let immediate-dominator(N) be
+// least-common-ancestor(N's CFG predecessors) for all the N in DF(then-clause).
+// 
+bool GVN::tryFoldCondBranch(BasicBlock *BB) {
+  if (!EnableFoldCondBr)
+    return false;
+
+  BranchInst *BI = dyn_cast_or_null<BranchInst>(BB->getTerminator());
+  if (!BI || BI->isUnconditional())
+    return false;
+
+  ConstantInt *Cond = dyn_cast<ConstantInt>(BI->getCondition());
+  if (!Cond)
+    return false;
+
+  // Step 1: Try to figure out the structure of the if-then-else statement.
+  //
+  BasicBlock *T = BI->getSuccessor(0);
+  BasicBlock *F = BI->getSuccessor(1);
+  BasicBlock *DeadEdgeTgt = Cond->getZExtValue() ? F : T;
+  BasicBlock *LiveEdgeTgt = Cond->getZExtValue() ? T : F;
+  BasicBlock *JoinBB = 0;
+
+  if (DeadEdgeTgt == LiveEdgeTgt || DT->dominates(DeadEdgeTgt, BB)) {
+    // Two special cases: 
+    //  case 1: Both then-clause and else-clause are empty
+    //  case 2: The dead-edge to be pruned is a back-edge.
+    //  
+    //  In both cases, we just need to fold the conditional branch to
+    // unconditional branch; the dominator-tree dosen't need to be updated.
+    // I guess following three statements can handle such special cases:
+    // ------------------------------------------
+    // MD->removeInstruction(BB->getTerminator());
+    // ConstantFoldTerminator(BB, true, TLI);
+    // return true;
+    // ------------------------------------------
+    // 
+    // However, I don't catch such situations in the benchmarks I have, and
+    // have to ignore them for now.
+    return false;
+  }
+
+  SmallVector<BasicBlock *, 8> RegularDF, LandPadDF, RegularDF2, 
+                               LandPadDF2, DeadBlks;
+
+  if (!DeadEdgeTgt->getUniquePredecessor())
+    JoinBB = DeadEdgeTgt;
+  else {
+    if (!tryGetDomFrontier(DeadEdgeTgt, DT, RegularDF, LandPadDF, &DeadBlks) ||
+        RegularDF.size() != 1)
+      return false;
+
+    JoinBB = *RegularDF.begin();
+  }
+
+  if (!DT->dominates(BB, JoinBB))
+    return false;
+
+  if (JoinBB != LiveEdgeTgt) {
+    if (!tryGetDomFrontier(LiveEdgeTgt, DT, RegularDF2, LandPadDF2) ||
+        RegularDF2.size() != 1)
+      return false;
+
+    if (JoinBB != *RegularDF2.begin())
+      return false;
+  }
+
+  // Give up if the dominance-frontiers include a landing-pad which dosen't
+  // dominate all blocks it could reach.
+  //
+  for (SmallVector<BasicBlock *, 8>::iterator I = LandPadDF.begin(),
+        E = LandPadDF.end(); I != E; I++) {
+    BasicBlock *LP = *I;
+    if (!isSimpleLandingPad(LP))
+      return false;
+  }
+
+  for (SmallVector<BasicBlock *, 8>::iterator I = LandPadDF2.begin(),
+        E = LandPadDF2.end(); I != E; I++) {
+    BasicBlock *LP = *I;
+    if (!isSimpleLandingPad(LP))
+      return false;
+  }
+
+  // Add dead blocks just revealed.
+  DeadBlocks.insert(DeadBlks.begin(), DeadBlks.end());
+
+  // Step 2: The pattern is detected, start transformation.
+
+  // Step 2.1: Prune dead edges.
+  if (JoinBB != DeadEdgeTgt) {
+    // Prune edges emanating from dead blocks
+    pruneDeadIncidentEdge(JoinBB);
+    for (SmallVectorImpl<BasicBlock *>::iterator I = LandPadDF.begin(),
+          E = LandPadDF.end(); I != E; I++)
+      pruneDeadIncidentEdge(*I);
+  } else {
+     // The dead edge "<BB, JoinBB>", which is corresponding to the empty
+     // dead-clause, will be deleted by ConstantFoldTerminator() in Step 2.2.
+  }
+
+  // Step 2.2. Convert the conditional branch to unconditional branch.
+  //   
+  MD->removeInstruction(BB->getTerminator());
+  ConstantFoldTerminator(BB, true, TLI);
+
+  MD->invalidateCachedPredecessors();
+
+  // Step 2.3: Update the dominator-tree for those nodes which were adjancent
+  //   to dead blocks, or connected to dead edges.
+  //
+  updatePosInDomTree(JoinBB, DT);
+  for (SmallVectorImpl<BasicBlock *>::iterator I = LandPadDF.begin(),
+         E = LandPadDF.end(); I != E; I++)
+    updatePosInDomTree(*I, DT);
+
+  return true;
+}
+
+void GVN::deleteDeadBlocks() {
+  for (SetVector<BasicBlock *>::iterator I = DeadBlocks.begin(),
+         E = DeadBlocks.end(); I != E; I++)
+    if (TerminatorInst *T = (*I)->getTerminator())
+      T->eraseFromParent(); 
+
+  for (SetVector<BasicBlock *>::iterator I = DeadBlocks.begin(),
+         E = DeadBlocks.end(); I != E; I++) {
+    BasicBlock *BB = *I;
+
+    if (DT->getNode(BB))
+      DT->eraseSubtree(BB);
+
+    while (!BB->empty()) {
+      Instruction &I = BB->back();
+      if (!I.use_empty())
+        I.replaceAllUsesWith(UndefValue::get(I.getType()));
+      BB->getInstList().pop_back();
+    }
+  
+    BB->dropAllReferences();
+    BB->eraseFromParent();
+  }
+  DeadBlocks.clear();
+}
Index: test/Transforms/GVN/cond_br.ll
===================================================================
--- test/Transforms/GVN/cond_br.ll	(revision 0)
+++ test/Transforms/GVN/cond_br.ll	(working copy)
@@ -0,0 +1,56 @@
+; RUN: opt -basicaa -gvn -S < %s | FileCheck %s
+ at y = external global i32
+ at z = external global i32
+
+; Function Attrs: nounwind ssp uwtable
+define void @foo(i32 %x) {
+; CHECK: @foo(i32 %x)
+; CHECK: %.pre = load i32* @y
+; CHECK: call void @bar(i32 %.pre)
+
+  %t = sub i32 %x, %x
+  %cmp = icmp sgt i32 %t, 2
+  br i1 %cmp, label %if.then, label %entry.if.end_crit_edge
+
+entry.if.end_crit_edge:                           ; preds = %entry
+  %.pre = load i32* @y, align 4
+  br label %if.end
+
+if.then:                                          ; preds = %entry
+  %add = add nsw i32 %x, 3
+  store i32 %add, i32* @y, align 4
+  br label %if.end
+
+if.end:                                           ; preds = %entry.if.end_crit_edge, %if.then
+  %1 = phi i32 [ %.pre, %entry.if.end_crit_edge ], [ %add, %if.then ]
+  tail call void @bar(i32 %1)
+  ret void
+}
+
+define void @foo2(i32 %x) {
+; CHECK: @foo2(i32 %x)
+; CHECK: %.pre = load i32* @y
+; CHECK: tail call void @bar(i32 %.pre)
+entry:
+  %t = sub i32 %x, %x
+  %cmp = icmp sgt i32 %t, 2
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:                                          ; preds = %entry
+  %add = add nsw i32 %x, 3
+  store i32 %add, i32* @y, align 4
+  br label %if.end
+
+if.else:                                          ; preds = %entry
+  store i32 1, i32* @z, align 4
+  %.pre = load i32* @y, align 4
+  br label %if.end
+
+if.end:                                           ; preds = %if.else, %if.then
+  %0 = phi i32 [ %.pre, %if.else ], [ %add, %if.then ]
+  tail call void @bar(i32 %0)
+  ret void
+}
+
+declare void @bar(i32)
+
Index: test/Transforms/GVN/local-pre.ll
===================================================================
--- test/Transforms/GVN/local-pre.ll	(revision 190205)
+++ test/Transforms/GVN/local-pre.ll	(working copy)
@@ -1,9 +1,9 @@
 ; RUN: opt < %s -gvn -enable-pre -S | grep "b.pre"
 
-define i32 @main(i32 %p) {
+define i32 @main(i32 %p, i32 %q) {
 block1:
-  
-	br i1 true, label %block2, label %block3
+    %cmp = icmp eq i32 %p, %q 
+	br i1 %cmp, label %block2, label %block3
 
 block2:
  %a = add i32 %p, 1
Index: test/Transforms/GVN/rle-nonlocal.ll
===================================================================
--- test/Transforms/GVN/rle-nonlocal.ll	(revision 190205)
+++ test/Transforms/GVN/rle-nonlocal.ll	(working copy)
@@ -1,8 +1,9 @@
 ; RUN: opt < %s -basicaa -gvn -S | FileCheck %s
 
-define i32 @main(i32** %p) {
+define i32 @main(i32** %p, i32 %x, i32 %y) {
 block1:
-	br i1 true, label %block2, label %block3
+    %cmp = icmp eq i32 %x, %y
+	br i1 %cmp , label %block2, label %block3
 
 block2:
  %a = load i32** %p
Index: test/Transforms/GVN/rle-semidominated.ll
===================================================================
--- test/Transforms/GVN/rle-semidominated.ll	(revision 190205)
+++ test/Transforms/GVN/rle-semidominated.ll	(working copy)
@@ -1,9 +1,10 @@
 ; RUN: opt < %s -basicaa -gvn -S | grep "DEAD = phi i32 "
 
-define i32 @main(i32* %p) {
+define i32 @main(i32* %p, i32 %x, i32 %y) {
 block1:
   %z = load i32* %p
-	br i1 true, label %block2, label %block3
+  %cmp = icmp eq i32 %x, %y
+	br i1 %cmp, label %block2, label %block3
 
 block2:
  br label %block4
Index: test/Transforms/GVN/rle.ll
===================================================================
--- test/Transforms/GVN/rle.ll	(revision 190205)
+++ test/Transforms/GVN/rle.ll	(working copy)
@@ -357,13 +357,14 @@
 ; CHECK: ret i8 %A
 }
 
-define i32 @chained_load(i32** %p) {
+define i32 @chained_load(i32** %p, i32 %x, i32 %y) {
 block1:
   %A = alloca i32*
 
   %z = load i32** %p
   store i32* %z, i32** %A
-  br i1 true, label %block2, label %block3
+  %cmp = icmp eq i32 %x, %y
+  br i1 %cmp, label %block2, label %block3
 
 block2:
  %a = load i32** %p
@@ -427,10 +428,11 @@
   ret i32 0
 }
 
-define i32 @phi_trans3(i32* %p) {
+define i32 @phi_trans3(i32* %p, i32 %x, i32 %y, i32 %z) {
 ; CHECK-LABEL: @phi_trans3(
 block1:
-  br i1 true, label %block2, label %block3
+  %cmpxy = icmp eq i32 %x, %y
+  br i1 %cmpxy, label %block2, label %block3
 
 block2:
  store i32 87, i32* %p
@@ -443,7 +445,7 @@
 
 block4:
   %A = phi i32 [-1, %block2], [42, %block3]
-  br i1 true, label %block5, label %exit
+  br i1 %cmpxy, label %block5, label %exit
   
 ; CHECK: block4:
 ; CHECK-NEXT: %D = phi i32 [ 87, %block2 ], [ 97, %block3 ]  
@@ -451,11 +453,11 @@
 
 block5:
   %B = add i32 %A, 1
-  br i1 true, label %block6, label %exit
+  br i1 %cmpxy, label %block6, label %exit
   
 block6:
   %C = getelementptr i32* %p, i32 %B
-  br i1 true, label %block7, label %exit
+  br i1 %cmpxy, label %block7, label %exit
   
 block7:
   %D = load i32* %C


More information about the llvm-commits mailing list