From ccc0cf3d8de133f2e100ae3a69d0414479ae435f Mon Sep 17 00:00:00 2001 From: Joseph Tremoulet Date: Thu, 13 Aug 2015 14:30:10 +0000 Subject: [PATCH] [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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@244894 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/WinEHPrepare.cpp | 374 +++++++++++++++------------ test/CodeGen/WinEH/wineh-demotion.ll | 296 +++++++++++++++++++++ 2 files changed, 505 insertions(+), 165 deletions(-) create mode 100644 test/CodeGen/WinEH/wineh-demotion.ll diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 83ae82353aa..8ab098bc1f1 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -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> &Worklist); + AllocaInst *insertPHILoads(PHINode *PN, Function &F); + void replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot, + DenseMap &Loads, Function &F); + void demoteNonlocalUses(Value *V, std::set &ColorsForBB, + Function &F); bool prepareExplicitEH(Function &F); void numberFunclet(BasicBlock *InitialBB, BasicBlock *FuncletBB); @@ -2951,7 +2959,6 @@ void WinEHPrepare::numberFunclet(BasicBlock *InitialBB, BasicBlock *FuncletBB) { } 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(Function &F) { numberFunclet(CRI->getSuccessor(), EntryBlock); } - // Insert cleanuppads before EH blocks with PHI nodes. + // Strip PHI nodes off of EH pads. + SmallVector 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(BB->getFirstNonPHI())) - continue; - // Skip any EH pads without PHIs, we don't need to worry about demoting into - // them. - if (!isa(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(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 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 &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(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(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 &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(Function &F) { 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 Loads; - AllocaInst *SpillSlot = nullptr; - for (Value::use_iterator UI = I->use_begin(), UE = I->use_end(); - UI != UE;) { - Use &U = *UI++; - auto *UsingInst = cast(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 &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(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(I)) { - InsertPt = I; - ++InsertPt; - // Don't insert before PHI nodes or EH pad instrs. - for (; isa(InsertPt) || InsertPt->isEHPad(); ++InsertPt) - ; - } else { - auto *II = cast(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 &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 &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(Function &F) { 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(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 Loads; + for (Value::use_iterator UI = PN->use_begin(), UE = PN->use_end(); + UI != UE;) { + Use &U = *UI++; + auto *UsingInst = cast(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(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, 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(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(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> &Worklist) { + + if (PredBlock->isEHPad() && + !isa(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 &ColorsForBB, + Function &F) { + DenseMap Loads; + AllocaInst *SpillSlot = nullptr; + for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE;) { + Use &U = *UI++; + auto *UsingInst = cast(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 &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(V)) { + InsertPt = F.getEntryBlock().getTerminator(); + } else if (isa(V)) { + auto *II = cast(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 &ColorsForUsingBB = BlockColors[II->getParent()]; + BlockColors[NewBlock] = ColorsForUsingBB; + for (BasicBlock *FuncletPad : ColorsForUsingBB) + FuncletBlocks[FuncletPad].insert(NewBlock); + } + InsertPt = II->getNormalDest()->getFirstInsertionPt(); + } else { + InsertPt = cast(V); + ++InsertPt; + // Don't insert before PHI nodes or EH pad instrs. + for (; isa(InsertPt) || InsertPt->isEHPad(); ++InsertPt) + ; + } + new StoreInst(V, SpillSlot, InsertPt); + } +} + +void WinEHPrepare::replaceUseWithLoad(Value *V, Use &U, AllocaInst *&SpillSlot, + DenseMap &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(U.getUser()); + if (auto *UsingPHI = dyn_cast(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); + } +} diff --git a/test/CodeGen/WinEH/wineh-demotion.ll b/test/CodeGen/WinEH/wineh-demotion.ll new file mode 100644 index 00000000000..eb342bc388c --- /dev/null +++ b/test/CodeGen/WinEH/wineh-demotion.ll @@ -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 +} -- 2.34.1