[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