forked from OSchip/llvm-project
[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 llvm-svn: 244894
This commit is contained in:
parent
ef766a7e70
commit
c9ff914ced
|
@ -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(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<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(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<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(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<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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue