//===----------------------------------------------------------------------===//
//
// This pass lowers LLVM IR exception handling into something closer to what the
-// backend wants. It snifs the personality function to see which kind of
-// preparation is necessary. If the personality function uses the Itanium LSDA,
-// this pass delegates to the DWARF EH preparation pass.
+// backend wants for functions using a personality function from a runtime
+// provided by MSVC. Functions with other personality functions are left alone
+// and may be prepared by other passes. In particular, all supported MSVC
+// personality functions require cleanup code to be outlined, and the C++
+// personality requires catch handler code to be outlined.
//
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/Passes.h"
-#include "llvm/Analysis/LibCallSemantics.h"
-#include "llvm/IR/Function.h"
-#include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/Instructions.h"
-#include "llvm/IR/IntrinsicInst.h"
-#include "llvm/IR/Module.h"
-#include "llvm/IR/PatternMatch.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/EHPersonalities.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/WinEHFuncInfo.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/MC/MCSymbol.h"
#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
-#include <memory>
+#include "llvm/Transforms/Utils/SSAUpdater.h"
using namespace llvm;
-using namespace llvm::PatternMatch;
#define DEBUG_TYPE "winehprepare"
+static cl::opt<bool> DisableDemotion(
+ "disable-demotion", cl::Hidden,
+ cl::desc(
+ "Clone multicolor basic blocks but do not demote cross funclet values"),
+ cl::init(false));
+
+static cl::opt<bool> DisableCleanups(
+ "disable-cleanups", cl::Hidden,
+ cl::desc("Do not remove implausible terminators or other similar cleanups"),
+ cl::init(false));
+
namespace {
+
class WinEHPrepare : public FunctionPass {
- std::unique_ptr<FunctionPass> DwarfPrepare;
-
public:
static char ID; // Pass identification, replacement for typeid.
- WinEHPrepare(const TargetMachine *TM = nullptr)
- : FunctionPass(ID), DwarfPrepare(createDwarfEHPass(TM)) {}
+ WinEHPrepare(const TargetMachine *TM = nullptr) : FunctionPass(ID) {}
bool runOnFunction(Function &Fn) override;
}
private:
- bool prepareCPPEHHandlers(Function &F,
- SmallVectorImpl<LandingPadInst *> &LPads);
- bool outlineCatchHandler(Function *SrcFn, Constant *SelectorType,
- LandingPadInst *LPad, StructType *EHDataStructTy);
-};
+ 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);
+ bool prepareExplicitEH(Function &F);
+ void colorFunclets(Function &F);
-class WinEHCatchDirector : public CloningDirector {
-public:
- WinEHCatchDirector(LandingPadInst *LPI, Value *Selector, Value *EHObj)
- : LPI(LPI), CurrentSelector(Selector->stripPointerCasts()), EHObj(EHObj),
- SelectorIDType(Type::getInt32Ty(LPI->getContext())),
- Int8PtrType(Type::getInt8PtrTy(LPI->getContext())) {}
+ void demotePHIsOnFunclets(Function &F);
+ void cloneCommonBlocks(Function &F);
+ void removeImplausibleInstructions(Function &F);
+ void cleanupPreparedFunclets(Function &F);
+ void verifyPreparedFunclets(Function &F);
- CloningAction handleInstruction(ValueToValueMapTy &VMap,
- const Instruction *Inst,
- BasicBlock *NewBB) override;
+ // All fields are reset by runOnFunction.
+ EHPersonality Personality = EHPersonality::Unknown;
-private:
- LandingPadInst *LPI;
- Value *CurrentSelector;
- Value *EHObj;
- Type *SelectorIDType;
- Type *Int8PtrType;
-
- const Value *ExtractedEHPtr;
- const Value *ExtractedSelector;
- const Value *EHPtrStoreAddr;
- const Value *SelectorStoreAddr;
+ DenseMap<BasicBlock *, ColorVector> BlockColors;
+ MapVector<BasicBlock *, std::vector<BasicBlock *>> FuncletBlocks;
};
+
} // end anonymous namespace
char WinEHPrepare::ID = 0;
return new WinEHPrepare(TM);
}
-static bool isMSVCPersonality(EHPersonality Pers) {
- return Pers == EHPersonality::MSVC_Win64SEH ||
- Pers == EHPersonality::MSVC_CXX;
+bool WinEHPrepare::runOnFunction(Function &Fn) {
+ if (!Fn.hasPersonalityFn())
+ return false;
+
+ // Classify the personality to see what kind of preparation we need.
+ Personality = classifyEHPersonality(Fn.getPersonalityFn());
+
+ // Do nothing if this is not a funclet-based personality.
+ if (!isFuncletEHPersonality(Personality))
+ return false;
+
+ return prepareExplicitEH(Fn);
+}
+
+bool WinEHPrepare::doFinalization(Module &M) { return false; }
+
+void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {}
+
+static int addUnwindMapEntry(WinEHFuncInfo &FuncInfo, int ToState,
+ const BasicBlock *BB) {
+ CxxUnwindMapEntry UME;
+ UME.ToState = ToState;
+ UME.Cleanup = BB;
+ FuncInfo.CxxUnwindMap.push_back(UME);
+ return FuncInfo.getLastStateNumber();
}
-bool WinEHPrepare::runOnFunction(Function &Fn) {
- SmallVector<LandingPadInst *, 4> LPads;
- SmallVector<ResumeInst *, 4> Resumes;
- for (BasicBlock &BB : Fn) {
- if (auto *LP = BB.getLandingPadInst())
- LPads.push_back(LP);
- if (auto *Resume = dyn_cast<ResumeInst>(BB.getTerminator()))
- Resumes.push_back(Resume);
+static void addTryBlockMapEntry(WinEHFuncInfo &FuncInfo, int TryLow,
+ int TryHigh, int CatchHigh,
+ ArrayRef<const CatchPadInst *> Handlers) {
+ WinEHTryBlockMapEntry TBME;
+ TBME.TryLow = TryLow;
+ TBME.TryHigh = TryHigh;
+ TBME.CatchHigh = CatchHigh;
+ assert(TBME.TryLow <= TBME.TryHigh);
+ for (const CatchPadInst *CPI : Handlers) {
+ WinEHHandlerType HT;
+ Constant *TypeInfo = cast<Constant>(CPI->getArgOperand(0));
+ if (TypeInfo->isNullValue())
+ HT.TypeDescriptor = nullptr;
+ else
+ HT.TypeDescriptor = cast<GlobalVariable>(TypeInfo->stripPointerCasts());
+ HT.Adjectives = cast<ConstantInt>(CPI->getArgOperand(1))->getZExtValue();
+ HT.Handler = CPI->getParent();
+ if (isa<ConstantPointerNull>(CPI->getArgOperand(2)))
+ HT.CatchObj.Alloca = nullptr;
+ else
+ HT.CatchObj.Alloca = cast<AllocaInst>(CPI->getArgOperand(2));
+ TBME.HandlerArray.push_back(HT);
}
+ FuncInfo.TryBlockMap.push_back(TBME);
+}
- // No need to prepare functions that lack landing pads.
- if (LPads.empty())
- return false;
+static BasicBlock *getCleanupRetUnwindDest(const CleanupPadInst *CleanupPad) {
+ for (const User *U : CleanupPad->users())
+ if (const auto *CRI = dyn_cast<CleanupReturnInst>(U))
+ return CRI->getUnwindDest();
+ return nullptr;
+}
- // Classify the personality to see what kind of preparation we need.
- EHPersonality Pers = classifyEHPersonality(LPads.back()->getPersonalityFn());
+static void calculateStateNumbersForInvokes(const Function *Fn,
+ WinEHFuncInfo &FuncInfo) {
+ auto *F = const_cast<Function *>(Fn);
+ DenseMap<BasicBlock *, ColorVector> BlockColors = colorEHFunclets(*F);
+ for (BasicBlock &BB : *F) {
+ auto *II = dyn_cast<InvokeInst>(BB.getTerminator());
+ if (!II)
+ continue;
- // Delegate through to the DWARF pass if this is unrecognized.
- if (!isMSVCPersonality(Pers))
- return DwarfPrepare->runOnFunction(Fn);
+ auto &BBColors = BlockColors[&BB];
+ assert(BBColors.size() == 1 && "multi-color BB not removed by preparation");
+ BasicBlock *FuncletEntryBB = BBColors.front();
- // FIXME: This only returns true if the C++ EH handlers were outlined.
- // When that code is complete, it should always return whatever
- // prepareCPPEHHandlers returns.
- if (Pers == EHPersonality::MSVC_CXX && prepareCPPEHHandlers(Fn, LPads))
- return true;
+ BasicBlock *FuncletUnwindDest;
+ auto *FuncletPad =
+ dyn_cast<FuncletPadInst>(FuncletEntryBB->getFirstNonPHI());
+ assert(FuncletPad || FuncletEntryBB == &Fn->getEntryBlock());
+ if (!FuncletPad)
+ FuncletUnwindDest = nullptr;
+ else if (auto *CatchPad = dyn_cast<CatchPadInst>(FuncletPad))
+ FuncletUnwindDest = CatchPad->getCatchSwitch()->getUnwindDest();
+ else if (auto *CleanupPad = dyn_cast<CleanupPadInst>(FuncletPad))
+ FuncletUnwindDest = getCleanupRetUnwindDest(CleanupPad);
+ else
+ llvm_unreachable("unexpected funclet pad!");
- // FIXME: SEH Cleanups are unimplemented. Replace them with unreachable.
- if (Resumes.empty())
- return false;
+ BasicBlock *InvokeUnwindDest = II->getUnwindDest();
+ int BaseState = -1;
+ if (FuncletUnwindDest == InvokeUnwindDest) {
+ auto BaseStateI = FuncInfo.FuncletBaseStateMap.find(FuncletPad);
+ if (BaseStateI != FuncInfo.FuncletBaseStateMap.end())
+ BaseState = BaseStateI->second;
+ }
- for (ResumeInst *Resume : Resumes) {
- IRBuilder<>(Resume).CreateUnreachable();
- Resume->eraseFromParent();
+ if (BaseState != -1) {
+ FuncInfo.InvokeStateMap[II] = BaseState;
+ } else {
+ Instruction *PadInst = InvokeUnwindDest->getFirstNonPHI();
+ assert(FuncInfo.EHPadStateMap.count(PadInst) && "EH Pad has no state!");
+ FuncInfo.InvokeStateMap[II] = FuncInfo.EHPadStateMap[PadInst];
+ }
}
+}
- return true;
+// Given BB which ends in an unwind edge, return the EHPad that this BB belongs
+// to. If the unwind edge came from an invoke, return null.
+static const BasicBlock *getEHPadFromPredecessor(const BasicBlock *BB,
+ Value *ParentPad) {
+ const TerminatorInst *TI = BB->getTerminator();
+ if (isa<InvokeInst>(TI))
+ return nullptr;
+ if (auto *CatchSwitch = dyn_cast<CatchSwitchInst>(TI)) {
+ if (CatchSwitch->getParentPad() != ParentPad)
+ return nullptr;
+ return BB;
+ }
+ assert(!TI->isEHPad() && "unexpected EHPad!");
+ auto *CleanupPad = cast<CleanupReturnInst>(TI)->getCleanupPad();
+ if (CleanupPad->getParentPad() != ParentPad)
+ return nullptr;
+ return CleanupPad->getParent();
}
-bool WinEHPrepare::doFinalization(Module &M) {
- return DwarfPrepare->doFinalization(M);
+static void calculateCXXStateNumbers(WinEHFuncInfo &FuncInfo,
+ const Instruction *FirstNonPHI,
+ int ParentState) {
+ const BasicBlock *BB = FirstNonPHI->getParent();
+ assert(BB->isEHPad() && "not a funclet!");
+
+ if (auto *CatchSwitch = dyn_cast<CatchSwitchInst>(FirstNonPHI)) {
+ assert(FuncInfo.EHPadStateMap.count(CatchSwitch) == 0 &&
+ "shouldn't revist catch funclets!");
+
+ SmallVector<const CatchPadInst *, 2> Handlers;
+ for (const BasicBlock *CatchPadBB : CatchSwitch->handlers()) {
+ auto *CatchPad = cast<CatchPadInst>(CatchPadBB->getFirstNonPHI());
+ Handlers.push_back(CatchPad);
+ }
+ int TryLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr);
+ FuncInfo.EHPadStateMap[CatchSwitch] = TryLow;
+ for (const BasicBlock *PredBlock : predecessors(BB))
+ if ((PredBlock = getEHPadFromPredecessor(PredBlock,
+ CatchSwitch->getParentPad())))
+ calculateCXXStateNumbers(FuncInfo, PredBlock->getFirstNonPHI(),
+ TryLow);
+ int CatchLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr);
+
+ // catchpads are separate funclets in C++ EH due to the way rethrow works.
+ int TryHigh = CatchLow - 1;
+ for (const auto *CatchPad : Handlers) {
+ FuncInfo.FuncletBaseStateMap[CatchPad] = CatchLow;
+ for (const User *U : CatchPad->users()) {
+ const auto *UserI = cast<Instruction>(U);
+ if (auto *InnerCatchSwitch = dyn_cast<CatchSwitchInst>(UserI))
+ if (InnerCatchSwitch->getUnwindDest() == CatchSwitch->getUnwindDest())
+ calculateCXXStateNumbers(FuncInfo, UserI, CatchLow);
+ if (auto *InnerCleanupPad = dyn_cast<CleanupPadInst>(UserI))
+ if (getCleanupRetUnwindDest(InnerCleanupPad) ==
+ CatchSwitch->getUnwindDest())
+ calculateCXXStateNumbers(FuncInfo, UserI, CatchLow);
+ }
+ }
+ int CatchHigh = FuncInfo.getLastStateNumber();
+ addTryBlockMapEntry(FuncInfo, TryLow, TryHigh, CatchHigh, Handlers);
+ DEBUG(dbgs() << "TryLow[" << BB->getName() << "]: " << TryLow << '\n');
+ DEBUG(dbgs() << "TryHigh[" << BB->getName() << "]: " << TryHigh << '\n');
+ DEBUG(dbgs() << "CatchHigh[" << BB->getName() << "]: " << CatchHigh
+ << '\n');
+ } else {
+ auto *CleanupPad = cast<CleanupPadInst>(FirstNonPHI);
+
+ // It's possible for a cleanup to be visited twice: it might have multiple
+ // cleanupret instructions.
+ if (FuncInfo.EHPadStateMap.count(CleanupPad))
+ return;
+
+ int CleanupState = addUnwindMapEntry(FuncInfo, ParentState, BB);
+ FuncInfo.EHPadStateMap[CleanupPad] = CleanupState;
+ DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB "
+ << BB->getName() << '\n');
+ for (const BasicBlock *PredBlock : predecessors(BB)) {
+ if ((PredBlock = getEHPadFromPredecessor(PredBlock,
+ CleanupPad->getParentPad()))) {
+ calculateCXXStateNumbers(FuncInfo, PredBlock->getFirstNonPHI(),
+ CleanupState);
+ }
+ }
+ for (const User *U : CleanupPad->users()) {
+ const auto *UserI = cast<Instruction>(U);
+ if (UserI->isEHPad())
+ report_fatal_error("Cleanup funclets for the MSVC++ personality cannot "
+ "contain exceptional actions");
+ }
+ }
}
-void WinEHPrepare::getAnalysisUsage(AnalysisUsage &AU) const {
- DwarfPrepare->getAnalysisUsage(AU);
+static int addSEHExcept(WinEHFuncInfo &FuncInfo, int ParentState,
+ const Function *Filter, const BasicBlock *Handler) {
+ SEHUnwindMapEntry Entry;
+ Entry.ToState = ParentState;
+ Entry.IsFinally = false;
+ Entry.Filter = Filter;
+ Entry.Handler = Handler;
+ FuncInfo.SEHUnwindMap.push_back(Entry);
+ return FuncInfo.SEHUnwindMap.size() - 1;
}
-bool WinEHPrepare::prepareCPPEHHandlers(
- Function &F, SmallVectorImpl<LandingPadInst *> &LPads) {
- // FIXME: Find all frame variable references in the handlers
- // to populate the structure elements.
- SmallVector<Type *, 2> AllocStructTys;
- AllocStructTys.push_back(Type::getInt32Ty(F.getContext())); // EH state
- AllocStructTys.push_back(Type::getInt8PtrTy(F.getContext())); // EH object
- StructType *EHDataStructTy =
- StructType::create(F.getContext(), AllocStructTys,
- "struct." + F.getName().str() + ".ehdata");
- bool HandlersOutlined = false;
-
- for (LandingPadInst *LPad : LPads) {
- // Look for evidence that this landingpad has already been processed.
- bool LPadHasActionList = false;
- BasicBlock *LPadBB = LPad->getParent();
- for (Instruction &Inst : LPadBB->getInstList()) {
- // FIXME: Make this an intrinsic.
- if (auto *Call = dyn_cast<CallInst>(&Inst))
- if (Call->getCalledFunction()->getName() == "llvm.eh.actions") {
- LPadHasActionList = true;
- break;
- }
+static int addSEHFinally(WinEHFuncInfo &FuncInfo, int ParentState,
+ const BasicBlock *Handler) {
+ SEHUnwindMapEntry Entry;
+ Entry.ToState = ParentState;
+ Entry.IsFinally = true;
+ Entry.Filter = nullptr;
+ Entry.Handler = Handler;
+ FuncInfo.SEHUnwindMap.push_back(Entry);
+ return FuncInfo.SEHUnwindMap.size() - 1;
+}
+
+static void calculateSEHStateNumbers(WinEHFuncInfo &FuncInfo,
+ const Instruction *FirstNonPHI,
+ int ParentState) {
+ const BasicBlock *BB = FirstNonPHI->getParent();
+ assert(BB->isEHPad() && "no a funclet!");
+
+ if (auto *CatchSwitch = dyn_cast<CatchSwitchInst>(FirstNonPHI)) {
+ assert(FuncInfo.EHPadStateMap.count(CatchSwitch) == 0 &&
+ "shouldn't revist catch funclets!");
+
+ // Extract the filter function and the __except basic block and create a
+ // state for them.
+ assert(CatchSwitch->getNumHandlers() == 1 &&
+ "SEH doesn't have multiple handlers per __try");
+ const auto *CatchPad =
+ cast<CatchPadInst>((*CatchSwitch->handler_begin())->getFirstNonPHI());
+ const BasicBlock *CatchPadBB = CatchPad->getParent();
+ const Constant *FilterOrNull =
+ cast<Constant>(CatchPad->getArgOperand(0)->stripPointerCasts());
+ const Function *Filter = dyn_cast<Function>(FilterOrNull);
+ assert((Filter || FilterOrNull->isNullValue()) &&
+ "unexpected filter value");
+ int TryState = addSEHExcept(FuncInfo, ParentState, Filter, CatchPadBB);
+
+ // Everything in the __try block uses TryState as its parent state.
+ FuncInfo.EHPadStateMap[CatchSwitch] = TryState;
+ DEBUG(dbgs() << "Assigning state #" << TryState << " to BB "
+ << CatchPadBB->getName() << '\n');
+ for (const BasicBlock *PredBlock : predecessors(BB))
+ if ((PredBlock = getEHPadFromPredecessor(PredBlock,
+ CatchSwitch->getParentPad())))
+ calculateSEHStateNumbers(FuncInfo, PredBlock->getFirstNonPHI(),
+ TryState);
+
+ // Everything in the __except block unwinds to ParentState, just like code
+ // outside the __try.
+ for (const User *U : CatchPad->users()) {
+ const auto *UserI = cast<Instruction>(U);
+ if (auto *InnerCatchSwitch = dyn_cast<CatchSwitchInst>(UserI))
+ if (InnerCatchSwitch->getUnwindDest() == CatchSwitch->getUnwindDest())
+ calculateSEHStateNumbers(FuncInfo, UserI, ParentState);
+ if (auto *InnerCleanupPad = dyn_cast<CleanupPadInst>(UserI))
+ if (getCleanupRetUnwindDest(InnerCleanupPad) ==
+ CatchSwitch->getUnwindDest())
+ calculateSEHStateNumbers(FuncInfo, UserI, ParentState);
}
+ } else {
+ auto *CleanupPad = cast<CleanupPadInst>(FirstNonPHI);
- // If we've already outlined the handlers for this landingpad,
- // there's nothing more to do here.
- if (LPadHasActionList)
- continue;
+ // It's possible for a cleanup to be visited twice: it might have multiple
+ // cleanupret instructions.
+ if (FuncInfo.EHPadStateMap.count(CleanupPad))
+ return;
- for (unsigned Idx = 0, NumClauses = LPad->getNumClauses(); Idx < NumClauses;
- ++Idx) {
- if (LPad->isCatch(Idx))
- HandlersOutlined =
- outlineCatchHandler(&F, LPad->getClause(Idx), LPad, EHDataStructTy);
- } // End for each clause
- } // End for each landingpad
+ int CleanupState = addSEHFinally(FuncInfo, ParentState, BB);
+ FuncInfo.EHPadStateMap[CleanupPad] = CleanupState;
+ DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB "
+ << BB->getName() << '\n');
+ for (const BasicBlock *PredBlock : predecessors(BB))
+ if ((PredBlock =
+ getEHPadFromPredecessor(PredBlock, CleanupPad->getParentPad())))
+ calculateSEHStateNumbers(FuncInfo, PredBlock->getFirstNonPHI(),
+ CleanupState);
+ for (const User *U : CleanupPad->users()) {
+ const auto *UserI = cast<Instruction>(U);
+ if (UserI->isEHPad())
+ report_fatal_error("Cleanup funclets for the SEH personality cannot "
+ "contain exceptional actions");
+ }
+ }
+}
- return HandlersOutlined;
+static bool isTopLevelPadForMSVC(const Instruction *EHPad) {
+ if (auto *CatchSwitch = dyn_cast<CatchSwitchInst>(EHPad))
+ return isa<ConstantTokenNone>(CatchSwitch->getParentPad()) &&
+ CatchSwitch->unwindsToCaller();
+ if (auto *CleanupPad = dyn_cast<CleanupPadInst>(EHPad))
+ return isa<ConstantTokenNone>(CleanupPad->getParentPad()) &&
+ getCleanupRetUnwindDest(CleanupPad) == nullptr;
+ if (isa<CatchPadInst>(EHPad))
+ return false;
+ llvm_unreachable("unexpected EHPad!");
}
-bool WinEHPrepare::outlineCatchHandler(Function *SrcFn, Constant *SelectorType,
- LandingPadInst *LPad,
- StructType *EHDataStructTy) {
- Module *M = SrcFn->getParent();
- LLVMContext &Context = M->getContext();
-
- // Create a new function to receive the handler contents.
- Type *Int8PtrType = Type::getInt8PtrTy(Context);
- std::vector<Type *> ArgTys;
- ArgTys.push_back(Int8PtrType);
- ArgTys.push_back(Int8PtrType);
- FunctionType *FnType = FunctionType::get(Int8PtrType, ArgTys, false);
- Function *CatchHandler = Function::Create(
- FnType, GlobalVariable::ExternalLinkage, SrcFn->getName() + ".catch", M);
-
- // Generate a standard prolog to setup the frame recovery structure.
- IRBuilder<> Builder(Context);
- BasicBlock *Entry = BasicBlock::Create(Context, "catch.entry");
- CatchHandler->getBasicBlockList().push_front(Entry);
- Builder.SetInsertPoint(Entry);
- Builder.SetCurrentDebugLocation(LPad->getDebugLoc());
-
- // The outlined handler will be called with the parent's frame pointer as
- // its second argument. To enable the handler to access variables from
- // the parent frame, we use that pointer to get locate a special block
- // of memory that was allocated using llvm.eh.allocateframe for this
- // purpose. During the outlining process we will determine which frame
- // variables are used in handlers and create a structure that maps these
- // variables into the frame allocation block.
- //
- // The frame allocation block also contains an exception state variable
- // used by the runtime and a pointer to the exception object pointer
- // which will be filled in by the runtime for use in the handler.
- Function *RecoverFrameFn =
- Intrinsic::getDeclaration(M, Intrinsic::framerecover);
- Value *RecoverArgs[] = {Builder.CreateBitCast(SrcFn, Int8PtrType, ""),
- &(CatchHandler->getArgumentList().back())};
- CallInst *EHAlloc =
- Builder.CreateCall(RecoverFrameFn, RecoverArgs, "eh.alloc");
- Value *EHData =
- Builder.CreateBitCast(EHAlloc, EHDataStructTy->getPointerTo(), "ehdata");
- Value *EHObjPtr =
- Builder.CreateConstInBoundsGEP2_32(EHData, 0, 1, "eh.obj.ptr");
-
- // This will give us a raw pointer to the exception object, which
- // corresponds to the formal parameter of the catch statement. If the
- // handler uses this object, we will generate code during the outlining
- // process to cast the pointer to the appropriate type and deference it
- // as necessary. The un-outlined landing pad code represents the
- // exception object as the result of the llvm.eh.begincatch call.
- Value *EHObj = Builder.CreateLoad(EHObjPtr, false, "eh.obj");
-
- ValueToValueMapTy VMap;
-
- // FIXME: Map other values referenced in the filter handler.
-
- WinEHCatchDirector Director(LPad, SelectorType, EHObj);
-
- SmallVector<ReturnInst *, 8> Returns;
- ClonedCodeInfo InlinedFunctionInfo;
-
- BasicBlock::iterator II = LPad;
-
- CloneAndPruneIntoFromInst(CatchHandler, SrcFn, ++II, VMap,
- /*ModuleLevelChanges=*/false, Returns, "",
- &InlinedFunctionInfo,
- SrcFn->getParent()->getDataLayout(), &Director);
-
- // Move all the instructions in the first cloned block into our entry block.
- BasicBlock *FirstClonedBB = std::next(Function::iterator(Entry));
- Entry->getInstList().splice(Entry->end(), FirstClonedBB->getInstList());
- FirstClonedBB->eraseFromParent();
+void llvm::calculateSEHStateNumbers(const Function *Fn,
+ WinEHFuncInfo &FuncInfo) {
+ // Don't compute state numbers twice.
+ if (!FuncInfo.SEHUnwindMap.empty())
+ return;
- return true;
+ for (const BasicBlock &BB : *Fn) {
+ if (!BB.isEHPad())
+ continue;
+ const Instruction *FirstNonPHI = BB.getFirstNonPHI();
+ if (!isTopLevelPadForMSVC(FirstNonPHI))
+ continue;
+ ::calculateSEHStateNumbers(FuncInfo, FirstNonPHI, -1);
+ }
+
+ calculateStateNumbersForInvokes(Fn, FuncInfo);
}
-CloningDirector::CloningAction WinEHCatchDirector::handleInstruction(
- ValueToValueMapTy &VMap, const Instruction *Inst, BasicBlock *NewBB) {
- // Intercept instructions which extract values from the landing pad aggregate.
- if (auto *Extract = dyn_cast<ExtractValueInst>(Inst)) {
- if (Extract->getAggregateOperand() == LPI) {
- assert(Extract->getNumIndices() == 1 &&
- "Unexpected operation: extracting both landing pad values");
- assert((*(Extract->idx_begin()) == 0 || *(Extract->idx_begin()) == 1) &&
- "Unexpected operation: extracting an unknown landing pad element");
-
- if (*(Extract->idx_begin()) == 0) {
- // Element 0 doesn't directly corresponds to anything in the WinEH scheme.
- // It will be stored to a memory location, then later loaded and finally
- // the loaded value will be used as the argument to an llvm.eh.begincatch
- // call. We're tracking it here so that we can skip the store and load.
- ExtractedEHPtr = Inst;
- } else {
- // Element 1 corresponds to the filter selector. We'll map it to 1 for
- // matching purposes, but it will also probably be stored to memory and
- // reloaded, so we need to track the instuction so that we can map the
- // loaded value too.
- VMap[Inst] = ConstantInt::get(SelectorIDType, 1);
- ExtractedSelector = Inst;
+void llvm::calculateWinCXXEHStateNumbers(const Function *Fn,
+ WinEHFuncInfo &FuncInfo) {
+ // Return if it's already been done.
+ if (!FuncInfo.EHPadStateMap.empty())
+ return;
+
+ for (const BasicBlock &BB : *Fn) {
+ if (!BB.isEHPad())
+ continue;
+ const Instruction *FirstNonPHI = BB.getFirstNonPHI();
+ if (!isTopLevelPadForMSVC(FirstNonPHI))
+ continue;
+ calculateCXXStateNumbers(FuncInfo, FirstNonPHI, -1);
+ }
+
+ calculateStateNumbersForInvokes(Fn, FuncInfo);
+}
+
+static int addClrEHHandler(WinEHFuncInfo &FuncInfo, int ParentState,
+ ClrHandlerType HandlerType, uint32_t TypeToken,
+ const BasicBlock *Handler) {
+ ClrEHUnwindMapEntry Entry;
+ Entry.Parent = ParentState;
+ Entry.Handler = Handler;
+ Entry.HandlerType = HandlerType;
+ Entry.TypeToken = TypeToken;
+ FuncInfo.ClrEHUnwindMap.push_back(Entry);
+ return FuncInfo.ClrEHUnwindMap.size() - 1;
+}
+
+void llvm::calculateClrEHStateNumbers(const Function *Fn,
+ WinEHFuncInfo &FuncInfo) {
+ // Return if it's already been done.
+ if (!FuncInfo.EHPadStateMap.empty())
+ return;
+
+ SmallVector<std::pair<const Instruction *, int>, 8> Worklist;
+
+ // Each pad needs to be able to refer to its parent, so scan the function
+ // looking for top-level handlers and seed the worklist with them.
+ for (const BasicBlock &BB : *Fn) {
+ if (!BB.isEHPad())
+ continue;
+ if (BB.isLandingPad())
+ report_fatal_error("CoreCLR EH cannot use landingpads");
+ const Instruction *FirstNonPHI = BB.getFirstNonPHI();
+ if (!isTopLevelPadForMSVC(FirstNonPHI))
+ continue;
+ // queue this with sentinel parent state -1 to mean unwind to caller.
+ Worklist.emplace_back(FirstNonPHI, -1);
+ }
+
+ while (!Worklist.empty()) {
+ const Instruction *Pad;
+ int ParentState;
+ std::tie(Pad, ParentState) = Worklist.pop_back_val();
+
+ Value *ParentPad;
+ int PredState;
+ if (const CleanupPadInst *Cleanup = dyn_cast<CleanupPadInst>(Pad)) {
+ // A cleanup can have multiple exits; don't re-process after the first.
+ if (FuncInfo.EHPadStateMap.count(Cleanup))
+ continue;
+ // CoreCLR personality uses arity to distinguish faults from finallies.
+ const BasicBlock *PadBlock = Cleanup->getParent();
+ ClrHandlerType HandlerType =
+ (Cleanup->getNumOperands() ? ClrHandlerType::Fault
+ : ClrHandlerType::Finally);
+ int NewState =
+ addClrEHHandler(FuncInfo, ParentState, HandlerType, 0, PadBlock);
+ FuncInfo.EHPadStateMap[Cleanup] = NewState;
+ // Propagate the new state to all preds of the cleanup
+ ParentPad = Cleanup->getParentPad();
+ PredState = NewState;
+ } else if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(Pad)) {
+ SmallVector<const CatchPadInst *, 1> Handlers;
+ for (const BasicBlock *CatchPadBB : CatchSwitch->handlers()) {
+ const auto *Catch = cast<CatchPadInst>(CatchPadBB->getFirstNonPHI());
+ Handlers.push_back(Catch);
+ }
+ FuncInfo.EHPadStateMap[CatchSwitch] = ParentState;
+ int NewState = ParentState;
+ for (auto HandlerI = Handlers.rbegin(), HandlerE = Handlers.rend();
+ HandlerI != HandlerE; ++HandlerI) {
+ const CatchPadInst *Catch = *HandlerI;
+ const BasicBlock *PadBlock = Catch->getParent();
+ uint32_t TypeToken = static_cast<uint32_t>(
+ cast<ConstantInt>(Catch->getArgOperand(0))->getZExtValue());
+ NewState = addClrEHHandler(FuncInfo, NewState, ClrHandlerType::Catch,
+ TypeToken, PadBlock);
+ FuncInfo.EHPadStateMap[Catch] = NewState;
}
+ for (const auto *CatchPad : Handlers) {
+ for (const User *U : CatchPad->users()) {
+ const auto *UserI = cast<Instruction>(U);
+ if (UserI->isEHPad())
+ Worklist.emplace_back(UserI, ParentState);
+ }
+ }
+ PredState = NewState;
+ ParentPad = CatchSwitch->getParentPad();
+ } else {
+ llvm_unreachable("Unexpected EH pad");
+ }
- // Tell the caller not to clone this instruction.
- return CloningDirector::SkipInstruction;
+ // Queue all predecessors with the given state
+ for (const BasicBlock *Pred : predecessors(Pad->getParent())) {
+ if ((Pred = getEHPadFromPredecessor(Pred, ParentPad)))
+ Worklist.emplace_back(Pred->getFirstNonPHI(), PredState);
}
- // Other extract value instructions just get cloned.
- return CloningDirector::CloneInstruction;
}
- if (auto *Store = dyn_cast<StoreInst>(Inst)) {
- // Look for and suppress stores of the extracted landingpad values.
- const Value *StoredValue = Store->getValueOperand();
- if (StoredValue == ExtractedEHPtr) {
- EHPtrStoreAddr = Store->getPointerOperand();
- return CloningDirector::SkipInstruction;
+ calculateStateNumbersForInvokes(Fn, FuncInfo);
+}
+
+void WinEHPrepare::colorFunclets(Function &F) {
+ BlockColors = colorEHFunclets(F);
+
+ // Invert the map from BB to colors to color to BBs.
+ for (BasicBlock &BB : F) {
+ ColorVector &Colors = BlockColors[&BB];
+ for (BasicBlock *Color : Colors)
+ FuncletBlocks[Color].push_back(&BB);
+ }
+}
+
+void llvm::calculateCatchReturnSuccessorColors(const Function *Fn,
+ WinEHFuncInfo &FuncInfo) {
+ for (const BasicBlock &BB : *Fn) {
+ const auto *CatchRet = dyn_cast<CatchReturnInst>(BB.getTerminator());
+ if (!CatchRet)
+ continue;
+ // A 'catchret' returns to the outer scope's color.
+ Value *ParentPad = CatchRet->getParentPad();
+ const BasicBlock *Color;
+ if (isa<ConstantTokenNone>(ParentPad))
+ Color = &Fn->getEntryBlock();
+ else
+ Color = cast<Instruction>(ParentPad)->getParent();
+ // Record the catchret successor's funclet membership.
+ FuncInfo.CatchRetSuccessorColorMap[CatchRet] = Color;
+ }
+}
+
+void WinEHPrepare::demotePHIsOnFunclets(Function &F) {
+ // 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++;
+ if (!BB->isEHPad())
+ continue;
+ 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;
+
+ AllocaInst *SpillSlot = insertPHILoads(PN, F);
+ if (SpillSlot)
+ insertPHIStores(PN, SpillSlot);
+
+ PHINodes.push_back(PN);
+ }
+ }
+
+ for (auto *PN : PHINodes) {
+ // There may be lingering uses on other EH PHIs being removed
+ PN->replaceAllUsesWith(UndefValue::get(PN->getType()));
+ PN->eraseFromParent();
+ }
+}
+
+void WinEHPrepare::cloneCommonBlocks(Function &F) {
+ // We need to clone all blocks which belong to multiple funclets. Values are
+ // remapped throughout the funclet to propogate both the new instructions
+ // *and* the new basic blocks themselves.
+ for (auto &Funclets : FuncletBlocks) {
+ BasicBlock *FuncletPadBB = Funclets.first;
+ std::vector<BasicBlock *> &BlocksInFunclet = Funclets.second;
+ Value *FuncletToken;
+ if (FuncletPadBB == &F.getEntryBlock())
+ FuncletToken = ConstantTokenNone::get(F.getContext());
+ else
+ FuncletToken = FuncletPadBB->getFirstNonPHI();
+
+ std::vector<std::pair<BasicBlock *, BasicBlock *>> Orig2Clone;
+ ValueToValueMapTy VMap;
+ for (BasicBlock *BB : BlocksInFunclet) {
+ ColorVector &ColorsForBB = BlockColors[BB];
+ // We don't need to do anything if the block is monochromatic.
+ size_t NumColorsForBB = ColorsForBB.size();
+ if (NumColorsForBB == 1)
+ continue;
+
+ DEBUG_WITH_TYPE("winehprepare-coloring",
+ dbgs() << " Cloning block \'" << BB->getName()
+ << "\' for funclet \'" << FuncletPadBB->getName()
+ << "\'.\n");
+
+ // Create a new basic block and copy instructions into it!
+ BasicBlock *CBB =
+ CloneBasicBlock(BB, VMap, Twine(".for.", FuncletPadBB->getName()));
+ // Insert the clone immediately after the original to ensure determinism
+ // and to keep the same relative ordering of any funclet's blocks.
+ CBB->insertInto(&F, BB->getNextNode());
+
+ // Add basic block mapping.
+ VMap[BB] = CBB;
+
+ // Record delta operations that we need to perform to our color mappings.
+ Orig2Clone.emplace_back(BB, CBB);
+ }
+
+ // If nothing was cloned, we're done cloning in this funclet.
+ if (Orig2Clone.empty())
+ continue;
+
+ // Update our color mappings to reflect that one block has lost a color and
+ // another has gained a color.
+ for (auto &BBMapping : Orig2Clone) {
+ BasicBlock *OldBlock = BBMapping.first;
+ BasicBlock *NewBlock = BBMapping.second;
+
+ BlocksInFunclet.push_back(NewBlock);
+ ColorVector &NewColors = BlockColors[NewBlock];
+ assert(NewColors.empty() && "A new block should only have one color!");
+ NewColors.push_back(FuncletPadBB);
+
+ DEBUG_WITH_TYPE("winehprepare-coloring",
+ dbgs() << " Assigned color \'" << FuncletPadBB->getName()
+ << "\' to block \'" << NewBlock->getName()
+ << "\'.\n");
+
+ BlocksInFunclet.erase(
+ std::remove(BlocksInFunclet.begin(), BlocksInFunclet.end(), OldBlock),
+ BlocksInFunclet.end());
+ ColorVector &OldColors = BlockColors[OldBlock];
+ OldColors.erase(
+ std::remove(OldColors.begin(), OldColors.end(), FuncletPadBB),
+ OldColors.end());
+
+ DEBUG_WITH_TYPE("winehprepare-coloring",
+ dbgs() << " Removed color \'" << FuncletPadBB->getName()
+ << "\' from block \'" << OldBlock->getName()
+ << "\'.\n");
}
- if (StoredValue == ExtractedSelector) {
- SelectorStoreAddr = Store->getPointerOperand();
- return CloningDirector::SkipInstruction;
+
+ // Loop over all of the instructions in this funclet, fixing up operand
+ // references as we go. This uses VMap to do all the hard work.
+ for (BasicBlock *BB : BlocksInFunclet)
+ // Loop over all instructions, fixing each one as we find it...
+ for (Instruction &I : *BB)
+ RemapInstruction(&I, VMap,
+ RF_IgnoreMissingEntries | RF_NoModuleLevelChanges);
+
+ // Catchrets targeting cloned blocks need to be updated separately from
+ // the loop above because they are not in the current funclet.
+ SmallVector<CatchReturnInst *, 2> FixupCatchrets;
+ for (auto &BBMapping : Orig2Clone) {
+ BasicBlock *OldBlock = BBMapping.first;
+ BasicBlock *NewBlock = BBMapping.second;
+
+ FixupCatchrets.clear();
+ for (BasicBlock *Pred : predecessors(OldBlock))
+ if (auto *CatchRet = dyn_cast<CatchReturnInst>(Pred->getTerminator()))
+ if (CatchRet->getParentPad() == FuncletToken)
+ FixupCatchrets.push_back(CatchRet);
+
+ for (CatchReturnInst *CatchRet : FixupCatchrets)
+ CatchRet->setSuccessor(NewBlock);
}
- // Any other store just gets cloned.
- return CloningDirector::CloneInstruction;
+ auto UpdatePHIOnClonedBlock = [&](PHINode *PN, bool IsForOldBlock) {
+ unsigned NumPreds = PN->getNumIncomingValues();
+ for (unsigned PredIdx = 0, PredEnd = NumPreds; PredIdx != PredEnd;
+ ++PredIdx) {
+ BasicBlock *IncomingBlock = PN->getIncomingBlock(PredIdx);
+ bool EdgeTargetsFunclet;
+ if (auto *CRI =
+ dyn_cast<CatchReturnInst>(IncomingBlock->getTerminator())) {
+ EdgeTargetsFunclet = (CRI->getParentPad() == FuncletToken);
+ } else {
+ ColorVector &IncomingColors = BlockColors[IncomingBlock];
+ assert(!IncomingColors.empty() && "Block not colored!");
+ assert((IncomingColors.size() == 1 ||
+ llvm::all_of(IncomingColors,
+ [&](BasicBlock *Color) {
+ return Color != FuncletPadBB;
+ })) &&
+ "Cloning should leave this funclet's blocks monochromatic");
+ EdgeTargetsFunclet = (IncomingColors.front() == FuncletPadBB);
+ }
+ if (IsForOldBlock != EdgeTargetsFunclet)
+ continue;
+ PN->removeIncomingValue(IncomingBlock, /*DeletePHIIfEmpty=*/false);
+ // Revisit the next entry.
+ --PredIdx;
+ --PredEnd;
+ }
+ };
+
+ for (auto &BBMapping : Orig2Clone) {
+ BasicBlock *OldBlock = BBMapping.first;
+ BasicBlock *NewBlock = BBMapping.second;
+ for (Instruction &OldI : *OldBlock) {
+ auto *OldPN = dyn_cast<PHINode>(&OldI);
+ if (!OldPN)
+ break;
+ UpdatePHIOnClonedBlock(OldPN, /*IsForOldBlock=*/true);
+ }
+ for (Instruction &NewI : *NewBlock) {
+ auto *NewPN = dyn_cast<PHINode>(&NewI);
+ if (!NewPN)
+ break;
+ UpdatePHIOnClonedBlock(NewPN, /*IsForOldBlock=*/false);
+ }
+ }
+
+ // Check to see if SuccBB has PHI nodes. If so, we need to add entries to
+ // the PHI nodes for NewBB now.
+ for (auto &BBMapping : Orig2Clone) {
+ BasicBlock *OldBlock = BBMapping.first;
+ BasicBlock *NewBlock = BBMapping.second;
+ for (BasicBlock *SuccBB : successors(NewBlock)) {
+ for (Instruction &SuccI : *SuccBB) {
+ auto *SuccPN = dyn_cast<PHINode>(&SuccI);
+ if (!SuccPN)
+ break;
+
+ // Ok, we have a PHI node. Figure out what the incoming value was for
+ // the OldBlock.
+ int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock);
+ if (OldBlockIdx == -1)
+ break;
+ Value *IV = SuccPN->getIncomingValue(OldBlockIdx);
+
+ // Remap the value if necessary.
+ if (auto *Inst = dyn_cast<Instruction>(IV)) {
+ ValueToValueMapTy::iterator I = VMap.find(Inst);
+ if (I != VMap.end())
+ IV = I->second;
+ }
+
+ SuccPN->addIncoming(IV, NewBlock);
+ }
+ }
+ }
+
+ for (ValueToValueMapTy::value_type VT : VMap) {
+ // If there were values defined in BB that are used outside the funclet,
+ // then we now have to update all uses of the value to use either the
+ // original value, the cloned value, or some PHI derived value. This can
+ // require arbitrary PHI insertion, of which we are prepared to do, clean
+ // these up now.
+ SmallVector<Use *, 16> UsesToRename;
+
+ auto *OldI = dyn_cast<Instruction>(const_cast<Value *>(VT.first));
+ if (!OldI)
+ continue;
+ auto *NewI = cast<Instruction>(VT.second);
+ // Scan all uses of this instruction to see if it is used outside of its
+ // funclet, and if so, record them in UsesToRename.
+ for (Use &U : OldI->uses()) {
+ Instruction *UserI = cast<Instruction>(U.getUser());
+ BasicBlock *UserBB = UserI->getParent();
+ ColorVector &ColorsForUserBB = BlockColors[UserBB];
+ assert(!ColorsForUserBB.empty());
+ if (ColorsForUserBB.size() > 1 ||
+ *ColorsForUserBB.begin() != FuncletPadBB)
+ UsesToRename.push_back(&U);
+ }
+
+ // If there are no uses outside the block, we're done with this
+ // instruction.
+ if (UsesToRename.empty())
+ continue;
+
+ // We found a use of OldI outside of the funclet. Rename all uses of OldI
+ // that are outside its funclet to be uses of the appropriate PHI node
+ // etc.
+ SSAUpdater SSAUpdate;
+ SSAUpdate.Initialize(OldI->getType(), OldI->getName());
+ SSAUpdate.AddAvailableValue(OldI->getParent(), OldI);
+ SSAUpdate.AddAvailableValue(NewI->getParent(), NewI);
+
+ while (!UsesToRename.empty())
+ SSAUpdate.RewriteUseAfterInsertions(*UsesToRename.pop_back_val());
+ }
}
+}
+
+void WinEHPrepare::removeImplausibleInstructions(Function &F) {
+ // Remove implausible terminators and replace them with UnreachableInst.
+ for (auto &Funclet : FuncletBlocks) {
+ BasicBlock *FuncletPadBB = Funclet.first;
+ std::vector<BasicBlock *> &BlocksInFunclet = Funclet.second;
+ Instruction *FirstNonPHI = FuncletPadBB->getFirstNonPHI();
+ auto *FuncletPad = dyn_cast<FuncletPadInst>(FirstNonPHI);
+ auto *CatchPad = dyn_cast_or_null<CatchPadInst>(FuncletPad);
+ auto *CleanupPad = dyn_cast_or_null<CleanupPadInst>(FuncletPad);
+
+ for (BasicBlock *BB : BlocksInFunclet) {
+ for (Instruction &I : *BB) {
+ CallSite CS(&I);
+ if (!CS)
+ continue;
- if (auto *Load = dyn_cast<LoadInst>(Inst)) {
- // Look for loads of (previously suppressed) landingpad values.
- // The EHPtr load can be ignored (it should only be used as
- // an argument to llvm.eh.begincatch), but the selector value
- // needs to be mapped to a constant value of 1 to be used to
- // simplify the branching to always flow to the current handler.
- const Value *LoadAddr = Load->getPointerOperand();
- if (LoadAddr == EHPtrStoreAddr) {
- VMap[Inst] = UndefValue::get(Int8PtrType);
- return CloningDirector::SkipInstruction;
+ Value *FuncletBundleOperand = nullptr;
+ if (auto BU = CS.getOperandBundle(LLVMContext::OB_funclet))
+ FuncletBundleOperand = BU->Inputs.front();
+
+ if (FuncletBundleOperand == FuncletPad)
+ continue;
+
+ // Skip call sites which are nounwind intrinsics.
+ auto *CalledFn =
+ dyn_cast<Function>(CS.getCalledValue()->stripPointerCasts());
+ if (CalledFn && CalledFn->isIntrinsic() && CS.doesNotThrow())
+ continue;
+
+ // This call site was not part of this funclet, remove it.
+ if (CS.isInvoke()) {
+ // Remove the unwind edge if it was an invoke.
+ removeUnwindEdge(BB);
+ // Get a pointer to the new call.
+ BasicBlock::iterator CallI =
+ std::prev(BB->getTerminator()->getIterator());
+ auto *CI = cast<CallInst>(&*CallI);
+ changeToUnreachable(CI, /*UseLLVMTrap=*/false);
+ } else {
+ changeToUnreachable(&I, /*UseLLVMTrap=*/false);
+ }
+
+ // There are no more instructions in the block (except for unreachable),
+ // we are done.
+ break;
+ }
+
+ TerminatorInst *TI = BB->getTerminator();
+ // CatchPadInst and CleanupPadInst can't transfer control to a ReturnInst.
+ bool IsUnreachableRet = isa<ReturnInst>(TI) && FuncletPad;
+ // The token consumed by a CatchReturnInst must match the funclet token.
+ bool IsUnreachableCatchret = false;
+ if (auto *CRI = dyn_cast<CatchReturnInst>(TI))
+ IsUnreachableCatchret = CRI->getCatchPad() != CatchPad;
+ // The token consumed by a CleanupReturnInst must match the funclet token.
+ bool IsUnreachableCleanupret = false;
+ if (auto *CRI = dyn_cast<CleanupReturnInst>(TI))
+ IsUnreachableCleanupret = CRI->getCleanupPad() != CleanupPad;
+ if (IsUnreachableRet || IsUnreachableCatchret ||
+ IsUnreachableCleanupret) {
+ changeToUnreachable(TI, /*UseLLVMTrap=*/false);
+ } else if (isa<InvokeInst>(TI)) {
+ if (Personality == EHPersonality::MSVC_CXX && CleanupPad) {
+ // Invokes within a cleanuppad for the MSVC++ personality never
+ // transfer control to their unwind edge: the personality will
+ // terminate the program.
+ removeUnwindEdge(BB);
+ }
+ }
}
- if (LoadAddr == SelectorStoreAddr) {
- VMap[Inst] = ConstantInt::get(SelectorIDType, 1);
- return CloningDirector::SkipInstruction;
+ }
+}
+
+void WinEHPrepare::cleanupPreparedFunclets(Function &F) {
+ // Clean-up some of the mess we made by removing useles PHI nodes, trivial
+ // branches, etc.
+ for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) {
+ BasicBlock *BB = &*FI++;
+ SimplifyInstructionsInBlock(BB);
+ ConstantFoldTerminator(BB, /*DeleteDeadConditions=*/true);
+ MergeBlockIntoPredecessor(BB);
+ }
+
+ // We might have some unreachable blocks after cleaning up some impossible
+ // control flow.
+ removeUnreachableBlocks(F);
+}
+
+void WinEHPrepare::verifyPreparedFunclets(Function &F) {
+ for (BasicBlock &BB : F) {
+ size_t NumColors = BlockColors[&BB].size();
+ assert(NumColors == 1 && "Expected monochromatic BB!");
+ if (NumColors == 0)
+ report_fatal_error("Uncolored BB!");
+ if (NumColors > 1)
+ report_fatal_error("Multicolor BB!");
+ if (!DisableDemotion) {
+ bool EHPadHasPHI = BB.isEHPad() && isa<PHINode>(BB.begin());
+ assert(!EHPadHasPHI && "EH Pad still has a PHI!");
}
+ }
+}
+
+bool WinEHPrepare::prepareExplicitEH(Function &F) {
+ // 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.
+ removeUnreachableBlocks(F);
+
+ // Determine which blocks are reachable from which funclet entries.
+ colorFunclets(F);
+
+ cloneCommonBlocks(F);
+
+ if (!DisableDemotion)
+ demotePHIsOnFunclets(F);
+
+ if (!DisableCleanups) {
+ DEBUG(verifyFunction(F));
+ removeImplausibleInstructions(F);
+
+ DEBUG(verifyFunction(F));
+ cleanupPreparedFunclets(F);
+ }
+
+ DEBUG(verifyPreparedFunclets(F));
+ // Recolor the CFG to verify that all is well.
+ DEBUG(colorFunclets(F));
+ DEBUG(verifyPreparedFunclets(F));
+
+ BlockColors.clear();
+ 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;
+ Instruction *EHPad = PHIBlock->getFirstNonPHI();
- // Any other loads just get cloned.
- return CloningDirector::CloneInstruction;
+ if (!isa<TerminatorInst>(EHPad)) {
+ // If the EHPad isn't a terminator, then we can insert a load in this block
+ // that will dominate all uses.
+ SpillSlot = new AllocaInst(PN->getType(), nullptr,
+ Twine(PN->getName(), ".wineh.spillslot"),
+ &F.getEntryBlock().front());
+ Value *V = new LoadInst(SpillSlot, Twine(PN->getName(), ".wineh.reload"),
+ &*PHIBlock->getFirstInsertionPt());
+ PN->replaceAllUsesWith(V);
+ return SpillSlot;
}
- if (match(Inst, m_Intrinsic<Intrinsic::eh_begincatch>())) {
- // The argument to the call is some form of the first element of the
- // landingpad aggregate value, but that doesn't matter. It isn't used
- // here.
- // The return value of this instruction, however, is used to access the
- // EH object pointer. We have generated an instruction to get that value
- // from the EH alloc block, so we can just map to that here.
- VMap[Inst] = EHObj;
- return CloningDirector::SkipInstruction;
+ // Otherwise, we have a PHI on a terminator EHPad, and we give up and insert
+ // loads of the slot before every use.
+ 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());
+ if (isa<PHINode>(UsingInst) && UsingInst->getParent()->isEHPad()) {
+ // Use is on an EH pad phi. Leave it alone; we'll insert loads and
+ // stores for it separately.
+ continue;
+ }
+ replaceUseWithLoad(PN, U, SpillSlot, Loads, F);
}
- if (match(Inst, m_Intrinsic<Intrinsic::eh_endcatch>())) {
- auto *IntrinCall = dyn_cast<IntrinsicInst>(Inst);
- // It might be interesting to track whether or not we are inside a catch
- // function, but that might make the algorithm more brittle than it needs
- // to be.
-
- // The end catch call can occur in one of two places: either in a
- // landingpad
- // block that is part of the catch handlers exception mechanism, or at the
- // end of the catch block. If it occurs in a landing pad, we must skip it
- // and continue so that the landing pad gets cloned.
- // FIXME: This case isn't fully supported yet and shouldn't turn up in any
- // of the test cases until it is.
- if (IntrinCall->getParent()->isLandingPad())
- return CloningDirector::SkipInstruction;
-
- // If an end catch occurs anywhere else the next instruction should be an
- // unconditional branch instruction that we want to replace with a return
- // to the the address of the branch target.
- const BasicBlock *EndCatchBB = IntrinCall->getParent();
- const TerminatorInst *Terminator = EndCatchBB->getTerminator();
- const BranchInst *Branch = dyn_cast<BranchInst>(Terminator);
- assert(Branch && Branch->isUnconditional());
- assert(std::next(BasicBlock::const_iterator(IntrinCall)) ==
- BasicBlock::const_iterator(Branch));
-
- ReturnInst::Create(NewBB->getContext(),
- BlockAddress::get(Branch->getSuccessor(0)), NewBB);
-
- // We just added a terminator to the cloned block.
- // Tell the caller to stop processing the current basic block so that
- // the branch instruction will be skipped.
- return CloningDirector::StopCloningBB;
+ 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);
+ }
+ }
}
- if (match(Inst, m_Intrinsic<Intrinsic::eh_typeid_for>())) {
- auto *IntrinCall = dyn_cast<IntrinsicInst>(Inst);
- Value *Selector = IntrinCall->getArgOperand(0)->stripPointerCasts();
- // This causes a replacement that will collapse the landing pad CFG based
- // on the filter function we intend to match.
- if (Selector == CurrentSelector)
- VMap[Inst] = ConstantInt::get(SelectorIDType, 1);
- else
- VMap[Inst] = ConstantInt::get(SelectorIDType, 0);
- // Tell the caller not to clone this instruction.
- return CloningDirector::SkipInstruction;
+}
+
+void WinEHPrepare::insertPHIStore(
+ BasicBlock *PredBlock, Value *PredVal, AllocaInst *SpillSlot,
+ SmallVectorImpl<std::pair<BasicBlock *, Value *>> &Worklist) {
+
+ if (PredBlock->isEHPad() &&
+ isa<TerminatorInst>(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());
+}
+
+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().front());
+
+ 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);
+ if (auto *CatchRet =
+ dyn_cast<CatchReturnInst>(IncomingBlock->getTerminator())) {
+ // Putting a load above a catchret and use on the phi would still leave
+ // a cross-funclet def/use. We need to split the edge, change the
+ // catchret to target the new block, and put the load there.
+ BasicBlock *PHIBlock = UsingInst->getParent();
+ BasicBlock *NewBlock = SplitEdge(IncomingBlock, PHIBlock);
+ // SplitEdge gives us:
+ // IncomingBlock:
+ // ...
+ // br label %NewBlock
+ // NewBlock:
+ // catchret label %PHIBlock
+ // But we need:
+ // IncomingBlock:
+ // ...
+ // catchret label %NewBlock
+ // NewBlock:
+ // br label %PHIBlock
+ // So move the terminators to each others' blocks and swap their
+ // successors.
+ BranchInst *Goto = cast<BranchInst>(IncomingBlock->getTerminator());
+ Goto->removeFromParent();
+ CatchRet->removeFromParent();
+ IncomingBlock->getInstList().push_back(CatchRet);
+ NewBlock->getInstList().push_back(Goto);
+ Goto->setSuccessor(0, PHIBlock);
+ CatchRet->setSuccessor(NewBlock);
+ // Update the color mapping for the newly split edge.
+ ColorVector &ColorsForPHIBlock = BlockColors[PHIBlock];
+ BlockColors[NewBlock] = ColorsForPHIBlock;
+ for (BasicBlock *FuncletPad : ColorsForPHIBlock)
+ FuncletBlocks[FuncletPad].push_back(NewBlock);
+ // Treat the new block as incoming for load insertion.
+ IncomingBlock = NewBlock;
+ }
+ 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);
}
+}
- // Continue with the default cloning behavior.
- return CloningDirector::CloneInstruction;
+void WinEHFuncInfo::addIPToStateRange(const InvokeInst *II,
+ MCSymbol *InvokeBegin,
+ MCSymbol *InvokeEnd) {
+ assert(InvokeStateMap.count(II) &&
+ "should get invoke with precomputed state");
+ LabelToStateMap[InvokeBegin] = std::make_pair(InvokeStateMap[II], InvokeEnd);
}
+
+WinEHFuncInfo::WinEHFuncInfo() {}