[llvm] r244894 - [WinEHPrepare] Update demotion logic

Joseph Tremoulet via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 13 07:30:10 PDT 2015


Author: josepht
Date: Thu Aug 13 09:30:10 2015
New Revision: 244894

URL: http://llvm.org/viewvc/llvm-project?rev=244894&view=rev
Log:
[WinEHPrepare] Update demotion logic

Summary:
Update the demotion logic in WinEHPrepare to avoid creating new cleanups by
walking predecessors as necessary to insert stores for EH-pad PHIs.

Also avoid creating stores for EH-pad PHIs that have no uses.

The store/load placement is still pretty naive.  Likely future improvements
(at least for optimized compiles) include:
 - Share loads for related uses as possible
 - Coalesce non-interfering use/def-related PHIs
 - Store at definition point rather than each PHI pred for non-interfering
   lifetimes.


Reviewers: rnk, majnemer

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D11955

Added:
    llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll
Modified:
    llvm/trunk/lib/CodeGen/WinEHPrepare.cpp

Modified: llvm/trunk/lib/CodeGen/WinEHPrepare.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/WinEHPrepare.cpp?rev=244894&r1=244893&r2=244894&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/WinEHPrepare.cpp (original)
+++ llvm/trunk/lib/CodeGen/WinEHPrepare.cpp Thu Aug 13 09:30:10 2015
@@ -121,7 +121,15 @@ private:
                            BasicBlock *EndBB);
 
   void processSEHCatchHandler(CatchHandler *Handler, BasicBlock *StartBB);
-
+  void insertPHIStores(PHINode *OriginalPHI, AllocaInst *SpillSlot);
+  void
+  insertPHIStore(BasicBlock *PredBlock, Value *PredVal, AllocaInst *SpillSlot,
+                 SmallVectorImpl<std::pair<BasicBlock *, Value *>> &Worklist);
+  AllocaInst *insertPHILoads(PHINode *PN, Function &F);
+  void replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot,
+                          DenseMap<BasicBlock *, Value *> &Loads, Function &F);
+  void demoteNonlocalUses(Value *V, std::set<BasicBlock *> &ColorsForBB,
+                          Function &F);
   bool prepareExplicitEH(Function &F);
   void numberFunclet(BasicBlock *InitialBB, BasicBlock *FuncletBB);
 
@@ -2951,7 +2959,6 @@ void WinEHPrepare::numberFunclet(BasicBl
 }
 
 bool WinEHPrepare::prepareExplicitEH(Function &F) {
-  LLVMContext &Context = F.getContext();
   // Remove unreachable blocks.  It is not valuable to assign them a color and
   // their existence can trick us into thinking values are alive when they are
   // not.
@@ -2981,98 +2988,31 @@ bool WinEHPrepare::prepareExplicitEH(Fun
       numberFunclet(CRI->getSuccessor(), EntryBlock);
   }
 
-  // Insert cleanuppads before EH blocks with PHI nodes.
+  // Strip PHI nodes off of EH pads.
+  SmallVector<PHINode *, 16> PHINodes;
   for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) {
     BasicBlock *BB = FI++;
-    // Skip any BBs which aren't EH pads.
     if (!BB->isEHPad())
       continue;
-    // Skip any cleanuppads, they can hold non-PHI instructions.
-    if (isa<CleanupPadInst>(BB->getFirstNonPHI()))
-      continue;
-    // Skip any EH pads without PHIs, we don't need to worry about demoting into
-    // them.
-    if (!isa<PHINode>(BB->begin()))
-      continue;
-
-    // Create our new cleanuppad BB, terminate it with a cleanupret.
-    auto *NewCleanupBB = BasicBlock::Create(
-        Context, Twine(BB->getName(), ".wineh.phibb"), &F, BB);
-    auto *CPI = CleanupPadInst::Create(Type::getVoidTy(Context), {BB}, "",
-                                       NewCleanupBB);
-    CleanupReturnInst::Create(Context, /*RetVal=*/nullptr, BB, NewCleanupBB);
-
-    // Update the funclet data structures to keep them in the loop.
-    BlockColors[NewCleanupBB].insert(NewCleanupBB);
-    FuncletBlocks[NewCleanupBB].insert(NewCleanupBB);
-
-    // Reparent PHIs from the old EH BB into the cleanuppad.
     for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) {
       Instruction *I = BI++;
       auto *PN = dyn_cast<PHINode>(I);
       // Stop at the first non-PHI.
       if (!PN)
         break;
-      PN->removeFromParent();
-      PN->insertBefore(CPI);
-    }
 
-    // Redirect predecessors from the old EH BB to the cleanuppad.
-    std::set<BasicBlock *> Preds;
-    Preds.insert(pred_begin(BB), pred_end(BB));
-    for (BasicBlock *Pred : Preds) {
-      // Don't redirect the new cleanuppad to itself!
-      if (Pred == NewCleanupBB)
-        continue;
-      TerminatorInst *TI = Pred->getTerminator();
-      for (unsigned TII = 0, TIE = TI->getNumSuccessors(); TII != TIE; ++TII) {
-        BasicBlock *Successor = TI->getSuccessor(TII);
-        if (Successor == BB)
-          TI->setSuccessor(TII, NewCleanupBB);
-      }
+      AllocaInst *SpillSlot = insertPHILoads(PN, F);
+      if (SpillSlot)
+        insertPHIStores(PN, SpillSlot);
+
+      PHINodes.push_back(PN);
     }
   }
 
-  // Get rid of polychromatic PHI nodes.
-  for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) {
-    BasicBlock *BB = FI++;
-    std::set<BasicBlock *> &ColorsForBB = BlockColors[BB];
-    bool IsEHPad = BB->isEHPad();
-    for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) {
-      Instruction *I = BI++;
-      auto *PN = dyn_cast<PHINode>(I);
-      // Stop at the first non-PHI node.
-      if (!PN)
-        break;
-
-      // EH pads cannot be lowered with PHI nodes prefacing them.
-      if (IsEHPad) {
-        // We should have removed PHIs from all non-cleanuppad blocks.
-        if (!isa<CleanupPadInst>(BB->getFirstNonPHI()))
-          report_fatal_error("Unexpected PHI on EH Pad");
-        DemotePHIToStack(PN);
-        continue;
-      }
-
-      // See if *all* the basic blocks involved in this PHI node are in the
-      // same, lone, color.  If so, demotion is not needed.
-      bool SameColor = ColorsForBB.size() == 1;
-      if (SameColor) {
-        for (unsigned PNI = 0, PNE = PN->getNumIncomingValues(); PNI != PNE;
-             ++PNI) {
-          BasicBlock *IncomingBB = PN->getIncomingBlock(PNI);
-          std::set<BasicBlock *> &ColorsForIncomingBB = BlockColors[IncomingBB];
-          // If the colors differ, bail out early and demote.
-          if (ColorsForIncomingBB != ColorsForBB) {
-            SameColor = false;
-            break;
-          }
-        }
-      }
-
-      if (!SameColor)
-        DemotePHIToStack(PN);
-    }
+  for (auto *PN : PHINodes) {
+    // There may be lingering uses on other EH PHIs being removed
+    PN->replaceAllUsesWith(UndefValue::get(PN->getType()));
+    PN->eraseFromParent();
   }
 
   // Turn all inter-funclet uses of a Value into loads and stores.
@@ -3086,93 +3026,13 @@ bool WinEHPrepare::prepareExplicitEH(Fun
         if (AI->isStaticAlloca())
           continue;
 
-      // FIXME: Our spill-placement algorithm is incredibly naive.  We should
-      // try to sink+hoist as much as possible to avoid redundant stores and reloads.
-      DenseMap<BasicBlock *, Value *> Loads;
-      AllocaInst *SpillSlot = nullptr;
-      for (Value::use_iterator UI = I->use_begin(), UE = I->use_end();
-           UI != UE;) {
-        Use &U = *UI++;
-        auto *UsingInst = cast<Instruction>(U.getUser());
-        BasicBlock *UsingBB = UsingInst->getParent();
-
-        // Is the Use inside a block which is colored with a subset of the Def?
-        // If so, we don't need to escape the Def because we will clone
-        // ourselves our own private copy.
-        std::set<BasicBlock *> &ColorsForUsingBB = BlockColors[UsingBB];
-        if (std::includes(ColorsForBB.begin(), ColorsForBB.end(),
-                          ColorsForUsingBB.begin(), ColorsForUsingBB.end()))
-          continue;
-
-        // Lazilly create the spill slot.  We spill immediately after the value
-        // in the BasicBlock.
-        // FIXME: This can be improved to spill at the block exit points.
-        if (!SpillSlot)
-          SpillSlot = new AllocaInst(I->getType(), nullptr,
-                                     Twine(I->getName(), ".wineh.spillslot"),
-                                     EntryBlock->begin());
-
-        if (auto *PN = dyn_cast<PHINode>(UsingInst)) {
-          // If this is a PHI node, we can't insert a load of the value before
-          // the use.  Instead insert the load in the predecessor block
-          // corresponding to the incoming value.
-          //
-          // Note that if there are multiple edges from a basic block to this
-          // PHI node that we cannot have multiple loads.  The problem is that
-          // the resulting PHI node will have multiple values (from each load)
-          // coming in from the same block, which is illegal SSA form.
-          // For this reason, we keep track of and reuse loads we insert.
-          BasicBlock *IncomingBlock = PN->getIncomingBlock(U);
-          Value *&V = Loads[IncomingBlock];
-          // Insert the load into the predecessor block
-          if (!V)
-            V = new LoadInst(SpillSlot, Twine(I->getName(), ".wineh.reload"),
-                             /*Volatile=*/false,
-                             IncomingBlock->getTerminator());
-          U.set(V);
-        } else {
-          // Reload right before the old use.
-          // FIXME: This can be improved to reload at a block entry point.
-          Value *V =
-              new LoadInst(SpillSlot, Twine(I->getName(), ".wineh.reload"),
-                           /*Volatile=*/false, UsingInst);
-          U.set(V);
-        }
-      }
-      if (SpillSlot) {
-        // Insert stores of the computed value into the stack slot.
-        // We have to be careful if I is an invoke instruction,
-        // because we can't insert the store AFTER the terminator instruction.
-        BasicBlock::iterator InsertPt;
-        if (!isa<TerminatorInst>(I)) {
-          InsertPt = I;
-          ++InsertPt;
-          // Don't insert before PHI nodes or EH pad instrs.
-          for (; isa<PHINode>(InsertPt) || InsertPt->isEHPad(); ++InsertPt)
-            ;
-        } else {
-          auto *II = cast<InvokeInst>(I);
-          // We cannot demote invoke instructions to the stack if their normal
-          // edge is critical. Therefore, split the critical edge and create a
-          // basic block into which the store can be inserted.
-          if (!II->getNormalDest()->getSinglePredecessor()) {
-            unsigned SuccNum = GetSuccessorNumber(BB, II->getNormalDest());
-            assert(isCriticalEdge(II, SuccNum) && "Expected a critical edge!");
-            BasicBlock *NewBlock = SplitCriticalEdge(II, SuccNum);
-            assert(NewBlock && "Unable to split critical edge.");
-            // Update the color mapping for the newly split edge.
-            std::set<BasicBlock *> &ColorsForUsingBB =
-                BlockColors[II->getParent()];
-            BlockColors[NewBlock] = ColorsForUsingBB;
-            for (BasicBlock *FuncletPad : ColorsForUsingBB)
-              FuncletBlocks[FuncletPad].insert(NewBlock);
-          }
-          InsertPt = II->getNormalDest()->getFirstInsertionPt();
-        }
-        new StoreInst(I, SpillSlot, InsertPt);
-      }
+      demoteNonlocalUses(I, ColorsForBB, F);
     }
   }
+  // Also demote function parameters used in funclets.
+  std::set<BasicBlock *> &ColorsForEntry = BlockColors[&F.getEntryBlock()];
+  for (Argument &Arg : F.args())
+    demoteNonlocalUses(&Arg, ColorsForEntry, F);
 
   // We need to clone all blocks which belong to multiple funclets.  Values are
   // remapped throughout the funclet to propogate both the new instructions
@@ -3259,3 +3119,187 @@ bool WinEHPrepare::prepareExplicitEH(Fun
   FuncletBlocks.clear();
   return true;
 }
+
+// TODO: Share loads when one use dominates another, or when a catchpad exit
+// dominates uses (needs dominators).
+AllocaInst *WinEHPrepare::insertPHILoads(PHINode *PN, Function &F) {
+  BasicBlock *PHIBlock = PN->getParent();
+  AllocaInst *SpillSlot = nullptr;
+
+  if (isa<CleanupPadInst>(PHIBlock->getFirstNonPHI())) {
+    // Insert a load in place of the PHI and replace all uses.
+    SpillSlot = new AllocaInst(PN->getType(), nullptr,
+                               Twine(PN->getName(), ".wineh.spillslot"),
+                               F.getEntryBlock().begin());
+    Value *V = new LoadInst(SpillSlot, Twine(PN->getName(), ".wineh.reload"),
+                            PHIBlock->getFirstInsertionPt());
+    PN->replaceAllUsesWith(V);
+    return SpillSlot;
+  }
+
+  DenseMap<BasicBlock *, Value *> Loads;
+  for (Value::use_iterator UI = PN->use_begin(), UE = PN->use_end();
+       UI != UE;) {
+    Use &U = *UI++;
+    auto *UsingInst = cast<Instruction>(U.getUser());
+    BasicBlock *UsingBB = UsingInst->getParent();
+    if (UsingBB->isEHPad()) {
+      // Use is on an EH pad phi.  Leave it alone; we'll insert loads and
+      // stores for it separately.
+      assert(isa<PHINode>(UsingInst));
+      continue;
+    }
+    replaceUseWithLoad(PN, U, SpillSlot, Loads, F);
+  }
+  return SpillSlot;
+}
+
+// TODO: improve store placement.  Inserting at def is probably good, but need
+// to be careful not to introduce interfering stores (needs liveness analysis).
+// TODO: identify related phi nodes that can share spill slots, and share them
+// (also needs liveness).
+void WinEHPrepare::insertPHIStores(PHINode *OriginalPHI,
+                                   AllocaInst *SpillSlot) {
+  // Use a worklist of (Block, Value) pairs -- the given Value needs to be
+  // stored to the spill slot by the end of the given Block.
+  SmallVector<std::pair<BasicBlock *, Value *>, 4> Worklist;
+
+  Worklist.push_back({OriginalPHI->getParent(), OriginalPHI});
+
+  while (!Worklist.empty()) {
+    BasicBlock *EHBlock;
+    Value *InVal;
+    std::tie(EHBlock, InVal) = Worklist.pop_back_val();
+
+    PHINode *PN = dyn_cast<PHINode>(InVal);
+    if (PN && PN->getParent() == EHBlock) {
+      // The value is defined by another PHI we need to remove, with no room to
+      // insert a store after the PHI, so each predecessor needs to store its
+      // incoming value.
+      for (unsigned i = 0, e = PN->getNumIncomingValues(); i < e; ++i) {
+        Value *PredVal = PN->getIncomingValue(i);
+
+        // Undef can safely be skipped.
+        if (isa<UndefValue>(PredVal))
+          continue;
+
+        insertPHIStore(PN->getIncomingBlock(i), PredVal, SpillSlot, Worklist);
+      }
+    } else {
+      // We need to store InVal, which dominates EHBlock, but can't put a store
+      // in EHBlock, so need to put stores in each predecessor.
+      for (BasicBlock *PredBlock : predecessors(EHBlock)) {
+        insertPHIStore(PredBlock, InVal, SpillSlot, Worklist);
+      }
+    }
+  }
+}
+
+void WinEHPrepare::insertPHIStore(
+    BasicBlock *PredBlock, Value *PredVal, AllocaInst *SpillSlot,
+    SmallVectorImpl<std::pair<BasicBlock *, Value *>> &Worklist) {
+
+  if (PredBlock->isEHPad() &&
+      !isa<CleanupPadInst>(PredBlock->getFirstNonPHI())) {
+    // Pred is unsplittable, so we need to queue it on the worklist.
+    Worklist.push_back({PredBlock, PredVal});
+    return;
+  }
+
+  // Otherwise, insert the store at the end of the basic block.
+  new StoreInst(PredVal, SpillSlot, PredBlock->getTerminator());
+}
+
+// TODO: Share loads for same-funclet uses (requires dominators if funclets
+// aren't properly nested).
+void WinEHPrepare::demoteNonlocalUses(Value *V,
+                                      std::set<BasicBlock *> &ColorsForBB,
+                                      Function &F) {
+  DenseMap<BasicBlock *, Value *> Loads;
+  AllocaInst *SpillSlot = nullptr;
+  for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE;) {
+    Use &U = *UI++;
+    auto *UsingInst = cast<Instruction>(U.getUser());
+    BasicBlock *UsingBB = UsingInst->getParent();
+
+    // Is the Use inside a block which is colored with a subset of the Def?
+    // If so, we don't need to escape the Def because we will clone
+    // ourselves our own private copy.
+    std::set<BasicBlock *> &ColorsForUsingBB = BlockColors[UsingBB];
+    if (std::includes(ColorsForBB.begin(), ColorsForBB.end(),
+                      ColorsForUsingBB.begin(), ColorsForUsingBB.end()))
+      continue;
+
+    replaceUseWithLoad(V, U, SpillSlot, Loads, F);
+  }
+  if (SpillSlot) {
+    // Insert stores of the computed value into the stack slot.
+    // We have to be careful if I is an invoke instruction,
+    // because we can't insert the store AFTER the terminator instruction.
+    BasicBlock::iterator InsertPt;
+    if (isa<Argument>(V)) {
+      InsertPt = F.getEntryBlock().getTerminator();
+    } else if (isa<TerminatorInst>(V)) {
+      auto *II = cast<InvokeInst>(V);
+      // We cannot demote invoke instructions to the stack if their normal
+      // edge is critical. Therefore, split the critical edge and create a
+      // basic block into which the store can be inserted.
+      if (!II->getNormalDest()->getSinglePredecessor()) {
+        unsigned SuccNum =
+            GetSuccessorNumber(II->getParent(), II->getNormalDest());
+        assert(isCriticalEdge(II, SuccNum) && "Expected a critical edge!");
+        BasicBlock *NewBlock = SplitCriticalEdge(II, SuccNum);
+        assert(NewBlock && "Unable to split critical edge.");
+        // Update the color mapping for the newly split edge.
+        std::set<BasicBlock *> &ColorsForUsingBB = BlockColors[II->getParent()];
+        BlockColors[NewBlock] = ColorsForUsingBB;
+        for (BasicBlock *FuncletPad : ColorsForUsingBB)
+          FuncletBlocks[FuncletPad].insert(NewBlock);
+      }
+      InsertPt = II->getNormalDest()->getFirstInsertionPt();
+    } else {
+      InsertPt = cast<Instruction>(V);
+      ++InsertPt;
+      // Don't insert before PHI nodes or EH pad instrs.
+      for (; isa<PHINode>(InsertPt) || InsertPt->isEHPad(); ++InsertPt)
+        ;
+    }
+    new StoreInst(V, SpillSlot, InsertPt);
+  }
+}
+
+void WinEHPrepare::replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot,
+                                      DenseMap<BasicBlock *, Value *> &Loads,
+                                      Function &F) {
+  // Lazilly create the spill slot.
+  if (!SpillSlot)
+    SpillSlot = new AllocaInst(V->getType(), nullptr,
+                               Twine(V->getName(), ".wineh.spillslot"),
+                               F.getEntryBlock().begin());
+
+  auto *UsingInst = cast<Instruction>(U.getUser());
+  if (auto *UsingPHI = dyn_cast<PHINode>(UsingInst)) {
+    // If this is a PHI node, we can't insert a load of the value before
+    // the use.  Instead insert the load in the predecessor block
+    // corresponding to the incoming value.
+    //
+    // Note that if there are multiple edges from a basic block to this
+    // PHI node that we cannot have multiple loads.  The problem is that
+    // the resulting PHI node will have multiple values (from each load)
+    // coming in from the same block, which is illegal SSA form.
+    // For this reason, we keep track of and reuse loads we insert.
+    BasicBlock *IncomingBlock = UsingPHI->getIncomingBlock(U);
+    Value *&Load = Loads[IncomingBlock];
+    // Insert the load into the predecessor block
+    if (!Load)
+      Load = new LoadInst(SpillSlot, Twine(V->getName(), ".wineh.reload"),
+                          /*Volatile=*/false, IncomingBlock->getTerminator());
+
+    U.set(Load);
+  } else {
+    // Reload right before the old use.
+    auto *Load = new LoadInst(SpillSlot, Twine(V->getName(), ".wineh.reload"),
+                              /*Volatile=*/false, UsingInst);
+    U.set(Load);
+  }
+}

Added: llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll?rev=244894&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll (added)
+++ llvm/trunk/test/CodeGen/WinEH/wineh-demotion.ll Thu Aug 13 09:30:10 2015
@@ -0,0 +1,296 @@
+; RUN: opt -mtriple=x86_x64-pc-windows-msvc -S -winehprepare  < %s | FileCheck %s
+
+declare i32 @__CxxFrameHandler3(...)
+
+declare void @f()
+
+declare i32 @g()
+
+declare void @h(i32)
+
+declare i1 @i()
+
+; CHECK-LABEL: @test1(
+define void @test1(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ; Spill slot should be inserted here
+  ; CHECK: [[Slot:%[^ ]+]] = alloca
+  ; Can't store for %phi at these defs because the lifetimes overlap
+  ; CHECK-NOT: store
+  %x = call i32 @g()
+  %y = call i32 @g()
+  br i1 %B, label %left, label %right
+left:
+  ; CHECK: left:
+  ; CHECK-NEXT: store i32 %x, i32* [[Slot]]
+  ; CHECK-NEXT: invoke void @f
+  invoke void @f()
+          to label %exit unwind label %merge
+right:
+  ; CHECK: right:
+  ; CHECK-NEXT: store i32 %y, i32* [[Slot]]
+  ; CHECK-NEXT: invoke void @f
+  invoke void @f()
+          to label %exit unwind label %merge
+merge:
+  ; CHECK: merge:
+  ; CHECK-NOT: = phi
+  %phi = phi i32 [ %x, %left ], [ %y, %right ]
+  catchpad void [] to label %catch unwind label %catchend
+
+catch:
+  ; CHECK: catch:
+  ; CHECK: [[Reload:%[^ ]+]] = load i32, i32* [[Slot]]
+  ; CHECK-NEXT: call void @h(i32 [[Reload]])
+  call void @h(i32 %phi)
+  catchret label %exit
+
+catchend:
+  catchendpad unwind to caller
+
+exit:
+  ret void
+}
+
+; CHECK-LABEL: @test2(
+define void @test2(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  br i1 %B, label %left, label %right
+left:
+  ; Need two stores here because %x and %y interfere so they need 2 slots
+  ; CHECK: left:
+  ; CHECK:   store i32 1, i32* [[Slot1:%[^ ]+]]
+  ; CHECK:   store i32 1, i32* [[Slot2:%[^ ]+]]
+  ; CHECK-NEXT: invoke void @f
+  invoke void @f()
+          to label %exit unwind label %merge.inner
+right:
+  ; Need two stores here because %x and %y interfere so they need 2 slots
+  ; CHECK: right:
+  ; CHECK-DAG:   store i32 2, i32* [[Slot1]]
+  ; CHECK-DAG:   store i32 2, i32* [[Slot2]]
+  ; CHECK: invoke void @f
+  invoke void @f()
+          to label %exit unwind label %merge.inner
+merge.inner:
+  ; CHECK: merge.inner:
+  ; CHECK-NOT: = phi
+  ; CHECK: catchpad void
+  %x = phi i32 [ 1, %left ], [ 2, %right ]
+  catchpad void [] to label %catch.inner unwind label %catchend.inner
+
+catch.inner:
+  ; Need just one store here because only %y is affected
+  ; CHECK: catch.inner:
+  %z = call i32 @g()
+  ; CHECK:   store i32 %z
+  ; CHECK-NEXT: invoke void @f
+  invoke void @f()
+          to label %catchret.inner unwind label %merge.outer
+
+catchret.inner:
+  catchret label %exit
+catchend.inner:
+  catchendpad unwind label %merge.outer
+
+merge.outer:
+  ; CHECK: merge.outer:
+  ; CHECK-NOT: = phi
+  ; CHECK: catchpad void
+  %y = phi i32 [ %x, %catchend.inner ], [ %z, %catch.inner ]
+  catchpad void [] to label %catch.outer unwind label %catchend.outer
+
+catchend.outer:
+  catchendpad unwind to caller
+
+catch.outer:
+  ; Need to load x and y from two different slots since they're both live
+  ; and can have different values (if we came from catch.inner)
+  ; CHECK: catch.outer:
+  ; CHECK-DAG: load i32, i32* [[Slot1]]
+  ; CHECK-DAG: load i32, i32* [[Slot2]]
+  ; CHECK: catchret label
+  call void @h(i32 %x)
+  call void @h(i32 %y)
+  catchret label %exit
+
+exit:
+  ret void
+}
+
+; CHECK-LABEL: @test3(
+define void @test3(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ; need to spill parameter %B and def %x since they're used in a funclet
+  ; CHECK: entry:
+  ; CHECK-DAG: store i1 %B, i1* [[SlotB:%[^ ]+]]
+  ; CHECK-DAG: store i32 %x, i32* [[SlotX:%[^ ]+]]
+  ; CHECK: invoke void @f
+  %x = call i32 @g()
+  invoke void @f()
+          to label %exit unwind label %catchpad
+
+catchpad:
+  catchpad void [] to label %catch unwind label %catchend
+
+catch:
+  ; Need to reload %B here
+  ; CHECK: catch:
+  ; CHECK: [[ReloadB:%[^ ]+]] = load i1, i1* [[SlotB]]
+  ; CHECK: br i1 [[ReloadB]]
+  br i1 %B, label %left, label %right
+left:
+  ; Use of %x is in a phi, so need reload here in pred
+  ; CHECK: left:
+  ; CHECK: [[ReloadX:%[^ ]+]] = load i32, i32* [[SlotX]]
+  ; CHECK: br label %merge
+  br label %merge
+right:
+  br label %merge
+merge:
+  ; CHECK: merge:
+  ; CHECK:   %phi = phi i32 [ [[ReloadX]], %left ]
+  %phi = phi i32 [ %x, %left ], [ 42, %right ]
+  call void @h(i32 %phi)
+  catchret label %exit
+
+catchend:
+  catchendpad unwind to caller
+
+exit:
+  ret void
+}
+
+; test4: don't need stores for %phi.inner, as its only use is to feed %phi.outer
+;        %phi.outer needs stores in %left, %right, and %join
+; CHECK-LABEL: @test4(
+define void @test4(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ; CHECK:      entry:
+  ; CHECK:        [[Slot:%[^ ]+]] = alloca
+  ; CHECK-NEXT:   br
+  br i1 %B, label %left, label %right
+left:
+  ; CHECK: left:
+  ; CHECK-NOT: store
+  ; CHECK: store i32 %l, i32* [[Slot]]
+  ; CHECK-NEXT: invoke void @f
+  %l = call i32 @g()
+  invoke void @f()
+          to label %join unwind label %catchpad.inner
+right:
+  ; CHECK: right:
+  ; CHECK-NOT: store
+  ; CHECK: store i32 %r, i32* [[Slot]]
+  ; CHECK-NEXT: invoke void @f
+  %r = call i32 @g()
+  invoke void @f()
+          to label %join unwind label %catchpad.inner
+catchpad.inner:
+   ; CHECK: catchpad.inner:
+   ; CHECK-NEXT: catchpad void
+   %phi.inner = phi i32 [ %l, %left ], [ %r, %right ]
+   catchpad void [] to label %catch.inner unwind label %catchend.inner
+catch.inner:
+   catchret label %join
+catchend.inner:
+   catchendpad unwind label  %catchpad.outer
+join:
+  ; CHECK: join:
+  ; CHECK-NOT: store
+  ; CHECK: store i32 %j, i32* [[Slot]]
+  ; CHECK-NEXT: invoke void @f
+   %j = call i32 @g()
+   invoke void @f()
+           to label %exit unwind label %catchpad.outer
+catchpad.outer:
+   ; CHECK: catchpad.outer:
+   ; CHECK-NEXT: catchpad void
+   %phi.outer = phi i32 [ %phi.inner, %catchend.inner ], [ %j, %join ]
+   catchpad void [] to label %catch.outer unwind label %catchend.outer
+catch.outer:
+   ; CHECK: catch.outer:
+   ; CHECK:   [[Reload:%[^ ]+]] = load i32, i32* [[Slot]]
+   ; CHECK:   call void @h(i32 [[Reload]])
+   call void @h(i32 %phi.outer)
+   catchret label %exit
+catchend.outer:
+   catchendpad unwind to caller
+exit:
+   ret void
+}
+
+; CHECK-LABEL: @test5(
+define void @test5() personality i32 (...)* @__CxxFrameHandler3 {
+entry:
+  ; need store for %phi.cleanup
+  ; CHECK:      entry:
+  ; CHECK:        store i32 1, i32* [[CleanupSlot:%[^ ]+]]
+  ; CHECK-NEXT:   invoke void @f
+  invoke void @f()
+          to label %invoke.cont unwind label %cleanup
+
+invoke.cont:
+  ; need store for %phi.cleanup
+  ; CHECK:      invoke.cont:
+  ; CHECK-NEXT:   store i32 2, i32* [[CleanupSlot]]
+  ; CHECK-NEXT:   invoke void @f
+  invoke void @f()
+          to label %invoke.cont2 unwind label %cleanup
+
+cleanup:
+  ; cleanup phi can be loaded at cleanup entry
+  ; CHECK: cleanup:
+  ; CHECK-NEXT: cleanuppad void
+  ; CHECK: [[CleanupReload:%[^ ]+]] = load i32, i32* [[CleanupSlot]]
+  %phi.cleanup = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
+  cleanuppad void []
+  %b = call i1 @i()
+  br i1 %b, label %left, label %right
+
+left:
+  ; CHECK: left:
+  ; CHECK:   call void @h(i32 [[CleanupReload]]
+  call void @h(i32 %phi.cleanup)
+  br label %merge
+
+right:
+  ; CHECK: right:
+  ; CHECK:   call void @h(i32 [[CleanupReload]]
+  call void @h(i32 %phi.cleanup)
+  br label %merge
+
+merge:
+  ; need store for %phi.catch
+  ; CHECK:      merge:
+  ; CHECK-NEXT:   store i32 [[CleanupReload]], i32* [[CatchSlot:%[^ ]+]]
+  ; CHECK-NEXT:   cleanupret void
+  cleanupret void unwind label %catchpad
+
+invoke.cont2:
+  ; need store for %phi.catch
+  ; CHECK:      invoke.cont2:
+  ; CHECK-NEXT:   store i32 3, i32* [[CatchSlot]]
+  ; CHECK-NEXT:   invoke void @f
+  invoke void @f()
+          to label %exit unwind label %catchpad
+
+catchpad:
+  ; CHECK: catchpad:
+  ; CHECK-NEXT: catchpad void
+  %phi.catch = phi i32 [ %phi.cleanup, %merge ], [ 3, %invoke.cont2 ]
+  catchpad void [] to label %catch unwind label %catchend
+
+catch:
+  ; CHECK: catch:
+  ; CHECK:   [[CatchReload:%[^ ]+]] = load i32, i32* [[CatchSlot]]
+  ; CHECK:   call void @h(i32 [[CatchReload]]
+  call void @h(i32 %phi.catch)
+  catchret label %exit
+
+catchend:
+  catchendpad unwind to caller
+
+exit:
+  ret void
+}




More information about the llvm-commits mailing list