summaryrefslogtreecommitdiff
path: root/lib/CodeGen/WinEHPrepare.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/CodeGen/WinEHPrepare.cpp')
-rw-r--r--lib/CodeGen/WinEHPrepare.cpp345
1 files changed, 277 insertions, 68 deletions
diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp
index 848e8032e7..b368f3b175 100644
--- a/lib/CodeGen/WinEHPrepare.cpp
+++ b/lib/CodeGen/WinEHPrepare.cpp
@@ -18,6 +18,7 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Analysis/LibCallSemantics.h"
#include "llvm/CodeGen/WinEHFuncInfo.h"
@@ -86,6 +87,8 @@ private:
bool prepareExceptionHandlers(Function &F,
SmallVectorImpl<LandingPadInst *> &LPads);
void promoteLandingPadValues(LandingPadInst *LPad);
+ void demoteValuesLiveAcrossHandlers(Function &F,
+ SmallVectorImpl<LandingPadInst *> &LPads);
void completeNestedLandingPad(Function *ParentFn,
LandingPadInst *OutlinedLPad,
const LandingPadInst *OriginalLPad,
@@ -326,6 +329,10 @@ static cl::opt<bool>
cl::desc("Prepare functions with SEH personalities"));
bool WinEHPrepare::runOnFunction(Function &Fn) {
+ // No need to prepare outlined handlers.
+ if (Fn.hasFnAttribute("wineh-parent"))
+ return false;
+
SmallVector<LandingPadInst *, 4> LPads;
SmallVector<ResumeInst *, 4> Resumes;
for (BasicBlock &BB : Fn) {
@@ -369,8 +376,258 @@ void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<DominatorTreeWrapperPass>();
}
+static bool isSelectorDispatch(BasicBlock *BB, BasicBlock *&CatchHandler,
+ Constant *&Selector, BasicBlock *&NextBB);
+
+// Finds blocks reachable from the starting set Worklist. Does not follow unwind
+// edges or blocks listed in StopPoints.
+static void findReachableBlocks(SmallPtrSetImpl<BasicBlock *> &ReachableBBs,
+ SetVector<BasicBlock *> &Worklist,
+ const SetVector<BasicBlock *> *StopPoints) {
+ while (!Worklist.empty()) {
+ BasicBlock *BB = Worklist.pop_back_val();
+
+ // Don't cross blocks that we should stop at.
+ if (StopPoints && StopPoints->count(BB))
+ continue;
+
+ if (!ReachableBBs.insert(BB).second)
+ continue; // Already visited.
+
+ // Don't follow unwind edges of invokes.
+ if (auto *II = dyn_cast<InvokeInst>(BB->getTerminator())) {
+ Worklist.insert(II->getNormalDest());
+ continue;
+ }
+
+ // Otherwise, follow all successors.
+ Worklist.insert(succ_begin(BB), succ_end(BB));
+ }
+}
+
+/// Find all points where exceptional control rejoins normal control flow via
+/// llvm.eh.endcatch. Add them to the normal bb reachability worklist.
+static void findCXXEHReturnPoints(Function &F,
+ SetVector<BasicBlock *> &EHReturnBlocks) {
+ for (auto BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) {
+ BasicBlock *BB = BBI;
+ for (Instruction &I : *BB) {
+ if (match(&I, m_Intrinsic<Intrinsic::eh_endcatch>())) {
+ // Split the block after the call to llvm.eh.endcatch if there is
+ // anything other than an unconditional branch, or if the successor
+ // starts with a phi.
+ auto *Br = dyn_cast<BranchInst>(I.getNextNode());
+ if (!Br || !Br->isUnconditional() ||
+ isa<PHINode>(Br->getSuccessor(0)->begin())) {
+ DEBUG(dbgs() << "splitting block " << BB->getName()
+ << " with llvm.eh.endcatch\n");
+ BBI = BB->splitBasicBlock(I.getNextNode(), "ehreturn");
+ }
+ // The next BB is normal control flow.
+ EHReturnBlocks.insert(BB->getTerminator()->getSuccessor(0));
+ break;
+ }
+ }
+ }
+}
+
+static bool isCatchAllLandingPad(const BasicBlock *BB) {
+ const LandingPadInst *LP = BB->getLandingPadInst();
+ if (!LP)
+ return false;
+ unsigned N = LP->getNumClauses();
+ return (N > 0 && LP->isCatch(N - 1) &&
+ isa<ConstantPointerNull>(LP->getClause(N - 1)));
+}
+
+/// Find all points where exceptions control rejoins normal control flow via
+/// selector dispatch.
+static void findSEHEHReturnPoints(Function &F,
+ SetVector<BasicBlock *> &EHReturnBlocks) {
+ for (auto BBI = F.begin(), BBE = F.end(); BBI != BBE; ++BBI) {
+ BasicBlock *BB = BBI;
+ // If the landingpad is a catch-all, treat the whole lpad as if it is
+ // reachable from normal control flow.
+ // FIXME: This is imprecise. We need a better way of identifying where a
+ // catch-all starts and cleanups stop. As far as LLVM is concerned, there
+ // is no difference.
+ if (isCatchAllLandingPad(BB)) {
+ EHReturnBlocks.insert(BB);
+ continue;
+ }
+
+ BasicBlock *CatchHandler;
+ BasicBlock *NextBB;
+ Constant *Selector;
+ if (isSelectorDispatch(BB, CatchHandler, Selector, NextBB)) {
+ // Split the edge if there is a phi node. Returning from EH to a phi node
+ // is just as impossible as having a phi after an indirectbr.
+ if (isa<PHINode>(CatchHandler->begin())) {
+ DEBUG(dbgs() << "splitting EH return edge from " << BB->getName()
+ << " to " << CatchHandler->getName() << '\n');
+ BBI = CatchHandler = SplitCriticalEdge(
+ BB, std::find(succ_begin(BB), succ_end(BB), CatchHandler));
+ }
+ EHReturnBlocks.insert(CatchHandler);
+ }
+ }
+}
+
+/// Ensure that all values live into and out of exception handlers are stored
+/// in memory.
+/// FIXME: This falls down when values are defined in one handler and live into
+/// another handler. For example, a cleanup defines a value used only by a
+/// catch handler.
+void WinEHPrepare::demoteValuesLiveAcrossHandlers(
+ Function &F, SmallVectorImpl<LandingPadInst *> &LPads) {
+ DEBUG(dbgs() << "Demoting values live across exception handlers in function "
+ << F.getName() << '\n');
+
+ // Build a set of all non-exceptional blocks and exceptional blocks.
+ // - Non-exceptional blocks are blocks reachable from the entry block while
+ // not following invoke unwind edges.
+ // - Exceptional blocks are blocks reachable from landingpads. Analysis does
+ // not follow llvm.eh.endcatch blocks, which mark a transition from
+ // exceptional to normal control.
+ SmallPtrSet<BasicBlock *, 4> NormalBlocks;
+ SmallPtrSet<BasicBlock *, 4> EHBlocks;
+ SetVector<BasicBlock *> EHReturnBlocks;
+ SetVector<BasicBlock *> Worklist;
+
+ if (Personality == EHPersonality::MSVC_CXX)
+ findCXXEHReturnPoints(F, EHReturnBlocks);
+ else
+ findSEHEHReturnPoints(F, EHReturnBlocks);
+
+ DEBUG({
+ dbgs() << "identified the following blocks as EH return points:\n";
+ for (BasicBlock *BB : EHReturnBlocks)
+ dbgs() << " " << BB->getName() << '\n';
+ });
+
+ // Join points should not have phis at this point, unless they are a
+ // landingpad, in which case we will demote their phis later.
+#ifndef NDEBUG
+ for (BasicBlock *BB : EHReturnBlocks)
+ assert((BB->isLandingPad() || !isa<PHINode>(BB->begin())) &&
+ "non-lpad EH return block has phi");
+#endif
+
+ // Normal blocks are the blocks reachable from the entry block and all EH
+ // return points.
+ Worklist = EHReturnBlocks;
+ Worklist.insert(&F.getEntryBlock());
+ findReachableBlocks(NormalBlocks, Worklist, nullptr);
+ DEBUG({
+ dbgs() << "marked the following blocks as normal:\n";
+ for (BasicBlock *BB : NormalBlocks)
+ dbgs() << " " << BB->getName() << '\n';
+ });
+
+ // Exceptional blocks are the blocks reachable from landingpads that don't
+ // cross EH return points.
+ Worklist.clear();
+ for (auto *LPI : LPads)
+ Worklist.insert(LPI->getParent());
+ findReachableBlocks(EHBlocks, Worklist, &EHReturnBlocks);
+ DEBUG({
+ dbgs() << "marked the following blocks as exceptional:\n";
+ for (BasicBlock *BB : EHBlocks)
+ dbgs() << " " << BB->getName() << '\n';
+ });
+
+ SetVector<Argument *> ArgsToDemote;
+ SetVector<Instruction *> InstrsToDemote;
+ for (BasicBlock &BB : F) {
+ bool IsNormalBB = NormalBlocks.count(&BB);
+ bool IsEHBB = EHBlocks.count(&BB);
+ if (!IsNormalBB && !IsEHBB)
+ continue; // Blocks that are neither normal nor EH are unreachable.
+ for (Instruction &I : BB) {
+ for (Value *Op : I.operands()) {
+ // Don't demote static allocas, constants, and labels.
+ if (isa<Constant>(Op) || isa<BasicBlock>(Op) || isa<InlineAsm>(Op))
+ continue;
+ auto *AI = dyn_cast<AllocaInst>(Op);
+ if (AI && AI->isStaticAlloca())
+ continue;
+
+ if (auto *Arg = dyn_cast<Argument>(Op)) {
+ if (IsEHBB) {
+ DEBUG(dbgs() << "Demoting argument " << *Arg
+ << " used by EH instr: " << I << "\n");
+ ArgsToDemote.insert(Arg);
+ }
+ continue;
+ }
+
+ auto *OpI = cast<Instruction>(Op);
+ BasicBlock *OpBB = OpI->getParent();
+ // If a value is produced and consumed in the same BB, we don't need to
+ // demote it.
+ if (OpBB == &BB)
+ continue;
+ bool IsOpNormalBB = NormalBlocks.count(OpBB);
+ bool IsOpEHBB = EHBlocks.count(OpBB);
+ if (IsNormalBB != IsOpNormalBB || IsEHBB != IsOpEHBB) {
+ DEBUG({
+ dbgs() << "Demoting instruction live in-out from EH:\n";
+ dbgs() << "Instr: " << *OpI << '\n';
+ dbgs() << "User: " << I << '\n';
+ });
+ InstrsToDemote.insert(OpI);
+ }
+ }
+ }
+ }
+
+ // Demote values live into and out of handlers.
+ // FIXME: This demotion is inefficient. We should insert spills at the point
+ // of definition, insert one reload in each handler that uses the value, and
+ // insert reloads in the BB used to rejoin normal control flow.
+ Instruction *AllocaInsertPt = F.getEntryBlock().getFirstInsertionPt();
+ for (Instruction *I : InstrsToDemote)
+ DemoteRegToStack(*I, false, AllocaInsertPt);
+
+ // Demote arguments separately, and only for uses in EH blocks.
+ for (Argument *Arg : ArgsToDemote) {
+ auto *Slot = new AllocaInst(Arg->getType(), nullptr,
+ Arg->getName() + ".reg2mem", AllocaInsertPt);
+ SmallVector<User *, 4> Users(Arg->user_begin(), Arg->user_end());
+ for (User *U : Users) {
+ auto *I = dyn_cast<Instruction>(U);
+ if (I && EHBlocks.count(I->getParent())) {
+ auto *Reload = new LoadInst(Slot, Arg->getName() + ".reload", false, I);
+ U->replaceUsesOfWith(Arg, Reload);
+ }
+ }
+ new StoreInst(Arg, Slot, AllocaInsertPt);
+ }
+
+ // Demote landingpad phis, as the landingpad will be removed from the machine
+ // CFG.
+ for (LandingPadInst *LPI : LPads) {
+ BasicBlock *BB = LPI->getParent();
+ while (auto *Phi = dyn_cast<PHINode>(BB->begin()))
+ DemotePHIToStack(Phi, AllocaInsertPt);
+ }
+
+ DEBUG(dbgs() << "Demoted " << InstrsToDemote.size() << " instructions and "
+ << ArgsToDemote.size() << " arguments for WinEHPrepare\n\n");
+}
+
bool WinEHPrepare::prepareExceptionHandlers(
Function &F, SmallVectorImpl<LandingPadInst *> &LPads) {
+ // Don't run on functions that are already prepared.
+ for (LandingPadInst *LPad : LPads) {
+ BasicBlock *LPadBB = LPad->getParent();
+ for (Instruction &Inst : *LPadBB)
+ if (match(&Inst, m_Intrinsic<Intrinsic::eh_actions>()))
+ return false;
+ }
+
+ demoteValuesLiveAcrossHandlers(F, LPads);
+
// These containers are used to re-map frame variables that are used in
// outlined catch and cleanup handlers. They will be populated as the
// handlers are outlined.
@@ -391,11 +648,9 @@ bool WinEHPrepare::prepareExceptionHandlers(
bool LPadHasActionList = false;
BasicBlock *LPadBB = LPad->getParent();
for (Instruction &Inst : *LPadBB) {
- if (auto *IntrinCall = dyn_cast<IntrinsicInst>(&Inst)) {
- if (IntrinCall->getIntrinsicID() == Intrinsic::eh_actions) {
- LPadHasActionList = true;
- break;
- }
+ if (match(&Inst, m_Intrinsic<Intrinsic::eh_actions>())) {
+ LPadHasActionList = true;
+ break;
}
// FIXME: This is here to help with the development of nested landing pad
// outlining. It should be removed when that is finished.
@@ -493,14 +748,17 @@ bool WinEHPrepare::prepareExceptionHandlers(
CallInst::Create(ActionIntrin, ActionArgs, "recover", NewLPadBB);
// Add an indirect branch listing possible successors of the catch handlers.
- IndirectBrInst *Branch = IndirectBrInst::Create(Recover, 0, NewLPadBB);
+ SetVector<BasicBlock *> ReturnTargets;
for (ActionHandler *Action : Actions) {
if (auto *CatchAction = dyn_cast<CatchHandler>(Action)) {
- for (auto *Target : CatchAction->getReturnTargets()) {
- Branch->addDestination(Target);
- }
+ const auto &CatchTargets = CatchAction->getReturnTargets();
+ ReturnTargets.insert(CatchTargets.begin(), CatchTargets.end());
}
}
+ IndirectBrInst *Branch =
+ IndirectBrInst::Create(Recover, ReturnTargets.size(), NewLPadBB);
+ for (BasicBlock *Target : ReturnTargets)
+ Branch->addDestination(Target);
} // End for each landingpad
// If nothing got outlined, there is no more processing to be done.
@@ -543,51 +801,10 @@ bool WinEHPrepare::prepareExceptionHandlers(
// Finally, replace all of the temporary allocas for frame variables used in
// the outlined handlers with calls to llvm.framerecover.
- BasicBlock::iterator II = Entry->getFirstInsertionPt();
- Instruction *AllocaInsertPt = II;
for (auto &VarInfoEntry : FrameVarInfo) {
Value *ParentVal = VarInfoEntry.first;
TinyPtrVector<AllocaInst *> &Allocas = VarInfoEntry.second;
-
- // If the mapped value isn't already an alloca, we need to spill it if it
- // is a computed value or copy it if it is an argument.
- AllocaInst *ParentAlloca = dyn_cast<AllocaInst>(ParentVal);
- if (!ParentAlloca) {
- if (auto *Arg = dyn_cast<Argument>(ParentVal)) {
- // Lower this argument to a copy and then demote that to the stack.
- // We can't just use the argument location because the handler needs
- // it to be in the frame allocation block.
- // Use 'select i8 true, %arg, undef' to simulate a 'no-op' instruction.
- Value *TrueValue = ConstantInt::getTrue(Context);
- Value *UndefValue = UndefValue::get(Arg->getType());
- Instruction *SI =
- SelectInst::Create(TrueValue, Arg, UndefValue,
- Arg->getName() + ".tmp", AllocaInsertPt);
- Arg->replaceAllUsesWith(SI);
- // Reset the select operand, because it was clobbered by the RAUW above.
- SI->setOperand(1, Arg);
- ParentAlloca = DemoteRegToStack(*SI, true, SI);
- } else if (auto *PN = dyn_cast<PHINode>(ParentVal)) {
- ParentAlloca = DemotePHIToStack(PN, AllocaInsertPt);
- } else {
- Instruction *ParentInst = cast<Instruction>(ParentVal);
- // FIXME: This is a work-around to temporarily handle the case where an
- // instruction that is only used in handlers is not sunk.
- // Without uses, DemoteRegToStack would just eliminate the value.
- // This will fail if ParentInst is an invoke.
- if (ParentInst->getNumUses() == 0) {
- BasicBlock::iterator InsertPt = ParentInst;
- ++InsertPt;
- ParentAlloca =
- new AllocaInst(ParentInst->getType(), nullptr,
- ParentInst->getName() + ".reg2mem",
- AllocaInsertPt);
- new StoreInst(ParentInst, ParentAlloca, InsertPt);
- } else {
- ParentAlloca = DemoteRegToStack(*ParentInst, true, AllocaInsertPt);
- }
- }
- }
+ AllocaInst *ParentAlloca = cast<AllocaInst>(ParentVal);
// FIXME: We should try to sink unescaped allocas from the parent frame into
// the child frame. If the alloca is escaped, we have to use the lifetime
@@ -1357,31 +1574,23 @@ WinEHFrameVariableMaterializer::WinEHFrameVariableMaterializer(
}
Value *WinEHFrameVariableMaterializer::materializeValueFor(Value *V) {
- // If we're asked to materialize a value that is an instruction, we
- // temporarily create an alloca in the outlined function and add this
- // to the FrameVarInfo map. When all the outlining is complete, we'll
- // collect these into a structure, spilling non-alloca values in the
- // parent frame as necessary, and replace these temporary allocas with
- // GEPs referencing the frame allocation block.
-
- // If the value is an alloca, the mapping is direct.
+ // If we're asked to materialize a static alloca, we temporarily create an
+ // alloca in the outlined function and add this to the FrameVarInfo map. When
+ // all the outlining is complete, we'll replace these temporary allocas with
+ // calls to llvm.framerecover.
if (auto *AV = dyn_cast<AllocaInst>(V)) {
+ assert(AV->isStaticAlloca() &&
+ "cannot materialize un-demoted dynamic alloca");
AllocaInst *NewAlloca = dyn_cast<AllocaInst>(AV->clone());
Builder.Insert(NewAlloca, AV->getName());
FrameVarInfo[AV].push_back(NewAlloca);
return NewAlloca;
}
- // For other types of instructions or arguments, we need an alloca based on
- // the value's type and a load of the alloca. The alloca will be replaced
- // by a GEP, but the load will stay. In the parent function, the value will
- // be spilled to a location in the frame allocation block.
if (isa<Instruction>(V) || isa<Argument>(V)) {
- AllocaInst *NewAlloca =
- Builder.CreateAlloca(V->getType(), nullptr, "eh.temp.alloca");
- FrameVarInfo[V].push_back(NewAlloca);
- LoadInst *NewLoad = Builder.CreateLoad(NewAlloca, V->getName() + ".reload");
- return NewLoad;
+ errs() << "Failed to demote instruction used in exception handler:\n";
+ errs() << " " << *V << '\n';
+ report_fatal_error("WinEHPrepare failed to demote instruction");
}
// Don't materialize other values.