#include "llvm/CodeGen/Passes.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/CFG.h"
#include "llvm/Analysis/LibCallSemantics.h"
#include "llvm/CodeGen/WinEHFuncInfo.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 "llvm/Transforms/Utils/SSAUpdater.h"
using namespace llvm;
#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 {
public:
static char ID; WinEHPrepare(const TargetMachine *TM = nullptr) : FunctionPass(ID) {}
bool runOnFunction(Function &Fn) override;
bool doFinalization(Module &M) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
const char *getPassName() const override {
return "Windows exception handling preparation";
}
private:
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, SetVector<BasicBlock *> &ColorsForBB,
Function &F);
bool prepareExplicitEH(Function &F,
SmallVectorImpl<BasicBlock *> &EntryBlocks);
void replaceTerminatePadWithCleanup(Function &F);
void colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks);
void resolveFuncletAncestry(Function &F,
SmallVectorImpl<BasicBlock *> &EntryBlocks);
void resolveFuncletAncestryForPath(
Function &F, SmallVectorImpl<BasicBlock *> &FuncletPath,
std::map<BasicBlock *, BasicBlock *> &IdentityMap);
void makeFuncletEdgeUnreachable(BasicBlock *Parent, BasicBlock *Child);
BasicBlock *cloneFuncletForParent(Function &F, BasicBlock *FuncletEntry,
BasicBlock *Parent);
void updateTerminatorsAfterFuncletClone(
Function &F, BasicBlock *OrigFunclet, BasicBlock *CloneFunclet,
BasicBlock *OrigBlock, BasicBlock *CloneBlock, BasicBlock *CloneParent,
ValueToValueMapTy &VMap,
std::map<BasicBlock *, BasicBlock *> &Orig2Clone);
void demotePHIsOnFunclets(Function &F);
void demoteUsesBetweenFunclets(Function &F);
void demoteArgumentUses(Function &F);
void cloneCommonBlocks(Function &F,
SmallVectorImpl<BasicBlock *> &EntryBlocks);
void removeImplausibleTerminators(Function &F);
void cleanupPreparedFunclets(Function &F);
void verifyPreparedFunclets(Function &F);
EHPersonality Personality = EHPersonality::Unknown;
std::map<BasicBlock *, SetVector<BasicBlock *>> BlockColors;
std::map<BasicBlock *, std::set<BasicBlock *>> FuncletBlocks;
std::map<BasicBlock *, std::vector<BasicBlock *>> FuncletChildren;
std::map<BasicBlock *, std::vector<BasicBlock *>> FuncletParents;
bool FuncletCloningRequired = false;
std::map<BasicBlock *, std::map<BasicBlock *, BasicBlock*>> EstrangedBlocks;
};
}
char WinEHPrepare::ID = 0;
INITIALIZE_TM_PASS(WinEHPrepare, "winehprepare", "Prepare Windows exceptions",
false, false)
FunctionPass *llvm::createWinEHPass(const TargetMachine *TM) {
return new WinEHPrepare(TM);
}
static void findFuncletEntryPoints(Function &Fn,
SmallVectorImpl<BasicBlock *> &EntryBlocks) {
EntryBlocks.push_back(&Fn.getEntryBlock());
for (BasicBlock &BB : Fn) {
Instruction *First = BB.getFirstNonPHI();
if (!First->isEHPad())
continue;
assert(!isa<LandingPadInst>(First) &&
"landingpad cannot be used with funclet EH personality");
if (!isa<CatchEndPadInst>(First) && !isa<CleanupEndPadInst>(First))
EntryBlocks.push_back(&BB);
}
}
bool WinEHPrepare::runOnFunction(Function &Fn) {
if (!Fn.hasPersonalityFn())
return false;
Personality = classifyEHPersonality(Fn.getPersonalityFn());
if (!isFuncletEHPersonality(Personality))
return false;
removeUnreachableBlocks(Fn);
SmallVector<BasicBlock *, 4> EntryBlocks;
findFuncletEntryPoints(Fn, EntryBlocks);
return prepareExplicitEH(Fn, EntryBlocks);
}
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();
}
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);
}
static const CatchPadInst *getSingleCatchPadPredecessor(const BasicBlock *BB) {
for (const BasicBlock *PredBlock : predecessors(BB))
if (auto *CPI = dyn_cast<CatchPadInst>(PredBlock->getFirstNonPHI()))
return CPI;
return nullptr;
}
static void
findCatchPadsForCatchEndPad(const BasicBlock *CatchEndBB,
SmallVectorImpl<const CatchPadInst *> &Handlers) {
const CatchPadInst *CPI = getSingleCatchPadPredecessor(CatchEndBB);
while (CPI) {
Handlers.push_back(CPI);
CPI = getSingleCatchPadPredecessor(CPI->getParent());
}
std::reverse(Handlers.begin(), Handlers.end());
}
static const BasicBlock *getEHPadFromPredecessor(const BasicBlock *BB) {
const TerminatorInst *TI = BB->getTerminator();
if (isa<InvokeInst>(TI))
return nullptr;
if (TI->isEHPad())
return BB;
return cast<CleanupReturnInst>(TI)->getCleanupPad()->getParent();
}
static void calculateExplicitCXXStateNumbers(WinEHFuncInfo &FuncInfo,
const BasicBlock &BB,
int ParentState) {
assert(BB.isEHPad());
const Instruction *FirstNonPHI = BB.getFirstNonPHI();
if (isa<CatchPadInst>(FirstNonPHI))
return;
if (isa<CatchEndPadInst>(FirstNonPHI)) {
SmallVector<const CatchPadInst *, 2> Handlers;
findCatchPadsForCatchEndPad(&BB, Handlers);
const BasicBlock *FirstTryPad = Handlers.front()->getParent();
int TryLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr);
FuncInfo.EHPadStateMap[Handlers.front()] = TryLow;
for (const BasicBlock *PredBlock : predecessors(FirstTryPad))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, TryLow);
int CatchLow = addUnwindMapEntry(FuncInfo, ParentState, nullptr);
FuncInfo.EHPadStateMap[FirstNonPHI] = CatchLow;
int TryHigh = CatchLow - 1;
for (const BasicBlock *PredBlock : predecessors(&BB))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, CatchLow);
int CatchHigh = FuncInfo.getLastStateNumber();
addTryBlockMapEntry(FuncInfo, TryLow, TryHigh, CatchHigh, Handlers);
DEBUG(dbgs() << "TryLow[" << FirstTryPad->getName() << "]: " << TryLow
<< '\n');
DEBUG(dbgs() << "TryHigh[" << FirstTryPad->getName() << "]: " << TryHigh
<< '\n');
DEBUG(dbgs() << "CatchHigh[" << FirstTryPad->getName() << "]: " << CatchHigh
<< '\n');
} else if (isa<CleanupPadInst>(FirstNonPHI)) {
if (FuncInfo.EHPadStateMap.count(FirstNonPHI))
return;
int CleanupState = addUnwindMapEntry(FuncInfo, ParentState, &BB);
FuncInfo.EHPadStateMap[FirstNonPHI] = CleanupState;
DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB "
<< BB.getName() << '\n');
for (const BasicBlock *PredBlock : predecessors(&BB))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, CleanupState);
} else if (auto *CEPI = dyn_cast<CleanupEndPadInst>(FirstNonPHI)) {
BasicBlock *CleanupBlock = CEPI->getCleanupPad()->getParent();
calculateExplicitCXXStateNumbers(FuncInfo, *CleanupBlock, ParentState);
FuncInfo.EHPadStateMap[FirstNonPHI] = ParentState;
for (const BasicBlock *PredBlock : predecessors(&BB))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitCXXStateNumbers(FuncInfo, *PredBlock, ParentState);
} else if (isa<TerminatePadInst>(FirstNonPHI)) {
report_fatal_error("Not yet implemented!");
} else {
llvm_unreachable("unexpected EH Pad!");
}
}
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;
}
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 calculateExplicitSEHStateNumbers(WinEHFuncInfo &FuncInfo,
const BasicBlock &BB,
int ParentState) {
assert(BB.isEHPad());
const Instruction *FirstNonPHI = BB.getFirstNonPHI();
if (isa<CatchPadInst>(FirstNonPHI))
return;
if (isa<CatchEndPadInst>(FirstNonPHI)) {
SmallVector<const CatchPadInst *, 1> Handlers;
findCatchPadsForCatchEndPad(&BB, Handlers);
assert(Handlers.size() == 1 &&
"SEH doesn't have multiple handlers per __try");
const CatchPadInst *CPI = Handlers.front();
const BasicBlock *CatchPadBB = CPI->getParent();
const Constant *FilterOrNull =
cast<Constant>(CPI->getArgOperand(0)->stripPointerCasts());
const Function *Filter = dyn_cast<Function>(FilterOrNull);
assert((Filter || FilterOrNull->isNullValue()) &&
"unexpected filter value");
int TryState = addSEHExcept(FuncInfo, ParentState, Filter, CatchPadBB);
FuncInfo.EHPadStateMap[CPI] = TryState;
DEBUG(dbgs() << "Assigning state #" << TryState << " to BB "
<< CatchPadBB->getName() << '\n');
for (const BasicBlock *PredBlock : predecessors(CatchPadBB))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, TryState);
FuncInfo.EHPadStateMap[FirstNonPHI] = ParentState;
DEBUG(dbgs() << "Assigning state #" << ParentState << " to BB "
<< BB.getName() << '\n');
for (const BasicBlock *PredBlock : predecessors(&BB))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, ParentState);
} else if (isa<CleanupPadInst>(FirstNonPHI)) {
if (FuncInfo.EHPadStateMap.count(FirstNonPHI))
return;
int CleanupState = addSEHFinally(FuncInfo, ParentState, &BB);
FuncInfo.EHPadStateMap[FirstNonPHI] = CleanupState;
DEBUG(dbgs() << "Assigning state #" << CleanupState << " to BB "
<< BB.getName() << '\n');
for (const BasicBlock *PredBlock : predecessors(&BB))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, CleanupState);
} else if (auto *CEPI = dyn_cast<CleanupEndPadInst>(FirstNonPHI)) {
BasicBlock *CleanupBlock = CEPI->getCleanupPad()->getParent();
calculateExplicitSEHStateNumbers(FuncInfo, *CleanupBlock, ParentState);
FuncInfo.EHPadStateMap[FirstNonPHI] = ParentState;
DEBUG(dbgs() << "Assigning state #" << ParentState << " to BB "
<< BB.getName() << '\n');
for (const BasicBlock *PredBlock : predecessors(&BB))
if ((PredBlock = getEHPadFromPredecessor(PredBlock)))
calculateExplicitSEHStateNumbers(FuncInfo, *PredBlock, ParentState);
} else if (isa<TerminatePadInst>(FirstNonPHI)) {
report_fatal_error("Not yet implemented!");
} else {
llvm_unreachable("unexpected EH Pad!");
}
}
static bool doesEHPadUnwindToCaller(const Instruction *EHPad) {
auto *CPI = dyn_cast<CleanupPadInst>(EHPad);
if (!CPI)
return EHPad->mayThrow();
if (CPI->use_empty())
return true;
const Instruction *User = CPI->user_back();
if (auto *CRI = dyn_cast<CleanupReturnInst>(User))
return CRI->unwindsToCaller();
return cast<CleanupEndPadInst>(User)->unwindsToCaller();
}
void llvm::calculateSEHStateNumbers(const Function *Fn,
WinEHFuncInfo &FuncInfo) {
if (!FuncInfo.SEHUnwindMap.empty())
return;
for (const BasicBlock &BB : *Fn) {
if (!BB.isEHPad() || !doesEHPadUnwindToCaller(BB.getFirstNonPHI()))
continue;
calculateExplicitSEHStateNumbers(FuncInfo, BB, -1);
}
}
void llvm::calculateWinCXXEHStateNumbers(const Function *Fn,
WinEHFuncInfo &FuncInfo) {
if (!FuncInfo.EHPadStateMap.empty())
return;
for (const BasicBlock &BB : *Fn) {
if (!BB.isEHPad())
continue;
if (BB.isLandingPad())
report_fatal_error("MSVC C++ EH cannot use landingpads");
const Instruction *FirstNonPHI = BB.getFirstNonPHI();
if (!doesEHPadUnwindToCaller(FirstNonPHI))
continue;
calculateExplicitCXXStateNumbers(FuncInfo, BB, -1);
}
}
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) {
if (!FuncInfo.EHPadStateMap.empty())
return;
SmallVector<std::pair<const Instruction *, int>, 8> Worklist;
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 (!doesEHPadUnwindToCaller(FirstNonPHI))
continue;
Worklist.emplace_back(FirstNonPHI, -1);
}
while (!Worklist.empty()) {
const Instruction *Pad;
int ParentState;
std::tie(Pad, ParentState) = Worklist.pop_back_val();
int PredState;
if (const CleanupEndPadInst *EndPad = dyn_cast<CleanupEndPadInst>(Pad)) {
FuncInfo.EHPadStateMap[EndPad] = ParentState;
Worklist.emplace_back(EndPad->getCleanupPad(), ParentState);
PredState = ParentState;
} else if (const CleanupPadInst *Cleanup = dyn_cast<CleanupPadInst>(Pad)) {
if (FuncInfo.EHPadStateMap.count(Pad))
continue;
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;
PredState = NewState;
} else if (const CatchEndPadInst *EndPad = dyn_cast<CatchEndPadInst>(Pad)) {
FuncInfo.EHPadStateMap[EndPad] = ParentState;
PredState = ParentState;
} else if (const CatchPadInst *Catch = dyn_cast<CatchPadInst>(Pad)) {
const BasicBlock *PadBlock = Catch->getParent();
uint32_t TypeToken = static_cast<uint32_t>(
cast<ConstantInt>(Catch->getArgOperand(0))->getZExtValue());
int NewState = addClrEHHandler(FuncInfo, ParentState,
ClrHandlerType::Catch, TypeToken, PadBlock);
FuncInfo.EHPadStateMap[Catch] = NewState;
PredState = NewState;
} else {
llvm_unreachable("Unexpected EH pad");
}
for (const BasicBlock *Pred : predecessors(Pad->getParent())) {
if ((Pred = getEHPadFromPredecessor(Pred)))
Worklist.emplace_back(Pred->getFirstNonPHI(), PredState);
}
}
}
void WinEHPrepare::replaceTerminatePadWithCleanup(Function &F) {
if (Personality != EHPersonality::MSVC_CXX)
return;
for (BasicBlock &BB : F) {
Instruction *First = BB.getFirstNonPHI();
auto *TPI = dyn_cast<TerminatePadInst>(First);
if (!TPI)
continue;
if (TPI->getNumArgOperands() != 1)
report_fatal_error(
"Expected a unary terminatepad for MSVC C++ personalities!");
auto *TerminateFn = dyn_cast<Function>(TPI->getArgOperand(0));
if (!TerminateFn)
report_fatal_error("Function operand expected in terminatepad for MSVC "
"C++ personalities!");
auto *CPI = CleanupPadInst::Create(
BB.getContext(), {}, Twine("terminatepad.for.", BB.getName()), &BB);
auto *CallTerminate = CallInst::Create(TerminateFn, {}, &BB);
CallTerminate->setDoesNotThrow();
CallTerminate->setDoesNotReturn();
CallTerminate->setCallingConv(TerminateFn->getCallingConv());
CleanupReturnInst::Create(CPI, TPI->getUnwindDest(), &BB);
TPI->eraseFromParent();
}
}
static void
colorFunclets(Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks,
std::map<BasicBlock *, SetVector<BasicBlock *>> &BlockColors,
std::map<BasicBlock *, std::set<BasicBlock *>> &FuncletBlocks) {
SmallVector<std::pair<BasicBlock *, BasicBlock *>, 16> Worklist;
BasicBlock *EntryBlock = &F.getEntryBlock();
DEBUG_WITH_TYPE("winehprepare-coloring", dbgs() << "\nColoring funclets for "
<< F.getName() << "\n");
Worklist.push_back({EntryBlock, EntryBlock});
while (!Worklist.empty()) {
BasicBlock *Visiting;
BasicBlock *Color;
std::tie(Visiting, Color) = Worklist.pop_back_val();
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << "Visiting " << Visiting->getName() << ", "
<< Color->getName() << "\n");
Instruction *VisitingHead = Visiting->getFirstNonPHI();
if (VisitingHead->isEHPad() && !isa<CatchEndPadInst>(VisitingHead) &&
!isa<CleanupEndPadInst>(VisitingHead)) {
FuncletBlocks[Visiting].insert(Visiting);
for (User *U : VisitingHead->users()) {
if (auto *Exit = dyn_cast<TerminatorInst>(U)) {
for (BasicBlock *Succ : successors(Exit->getParent()))
if (!isa<CatchEndPadInst>(*Succ->getFirstNonPHI()))
if (BlockColors[Succ].insert(Color)) {
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Assigned color \'"
<< Color->getName() << "\' to block \'"
<< Succ->getName() << "\'.\n");
Worklist.push_back({Succ, Color});
}
}
}
if (CatchPadInst *CatchPad = dyn_cast<CatchPadInst>(VisitingHead)) {
BasicBlock *NormalSucc = CatchPad->getNormalDest();
if (BlockColors[NormalSucc].insert(Visiting)) {
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Assigned color \'" << Visiting->getName()
<< "\' to block \'" << NormalSucc->getName()
<< "\'.\n");
Worklist.push_back({NormalSucc, Visiting});
}
BasicBlock *UnwindSucc = CatchPad->getUnwindDest();
if (BlockColors[UnwindSucc].insert(Color)) {
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Assigned color \'" << Color->getName()
<< "\' to block \'" << UnwindSucc->getName()
<< "\'.\n");
Worklist.push_back({UnwindSucc, Color});
}
continue;
}
if (!isa<TerminatePadInst>(VisitingHead))
Color = Visiting;
} else {
FuncletBlocks[Color].insert(Visiting);
}
TerminatorInst *Terminator = Visiting->getTerminator();
if (isa<CleanupReturnInst>(Terminator) ||
isa<CatchReturnInst>(Terminator) ||
isa<CleanupEndPadInst>(Terminator)) {
continue;
}
for (BasicBlock *Succ : successors(Visiting)) {
if (isa<CatchEndPadInst>(Succ->getFirstNonPHI())) {
continue;
}
if (BlockColors[Succ].insert(Color)) {
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Assigned color \'" << Color->getName()
<< "\' to block \'" << Succ->getName()
<< "\'.\n");
Worklist.push_back({Succ, Color});
}
}
}
}
static BasicBlock *getEndPadForCatch(CatchPadInst *Catch) {
BasicBlock *NextUnwindDest = Catch->getUnwindDest();
auto *UnwindTerminator = NextUnwindDest->getTerminator();
while (auto *NextCatch = dyn_cast<CatchPadInst>(UnwindTerminator)) {
NextUnwindDest = NextCatch->getUnwindDest();
UnwindTerminator = NextUnwindDest->getTerminator();
}
assert(isa<CatchEndPadInst>(UnwindTerminator));
return NextUnwindDest;
}
static void updateClonedEHPadUnwindToParent(
BasicBlock *UnwindDest, BasicBlock *OrigBlock, BasicBlock *CloneBlock,
std::vector<BasicBlock *> &OrigParents, BasicBlock *CloneParent) {
auto updateUnwindTerminator = [](BasicBlock *BB) {
auto *Terminator = BB->getTerminator();
if (isa<CatchEndPadInst>(Terminator) ||
isa<CleanupEndPadInst>(Terminator)) {
removeUnwindEdge(BB);
} else {
assert(isa<CleanupEndPadInst>(Terminator) ||
isa<CleanupReturnInst>(Terminator));
for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE; ++SI)
(*SI)->removePredecessor(BB);
BB->getTerminator()->eraseFromParent();
new UnreachableInst(BB->getContext(), BB);
}
};
assert(UnwindDest->isEHPad());
auto *EHPadInst = UnwindDest->getFirstNonPHI();
if (isa<CatchEndPadInst>(EHPadInst)) {
auto *CloneParentCatch =
dyn_cast<CatchPadInst>(CloneParent->getFirstNonPHI());
if (!CloneParentCatch ||
getEndPadForCatch(CloneParentCatch) != UnwindDest) {
DEBUG_WITH_TYPE(
"winehprepare-coloring",
dbgs() << " removing unwind destination of clone block \'"
<< CloneBlock->getName() << "\'.\n");
updateUnwindTerminator(CloneBlock);
}
assert(OrigParents.size() >= 2);
if (OrigParents.size() != 2)
return;
assert(OrigParents.front() == CloneParent ||
OrigParents.back() == CloneParent);
BasicBlock *OrigParent;
if (OrigParents.front() == CloneParent)
OrigParent = OrigParents.back();
else
OrigParent = OrigParents.front();
auto *OrigParentCatch =
dyn_cast<CatchPadInst>(OrigParent->getFirstNonPHI());
if (!OrigParentCatch || getEndPadForCatch(OrigParentCatch) != UnwindDest) {
DEBUG_WITH_TYPE(
"winehprepare-coloring",
dbgs() << " removing unwind destination of original block \'"
<< OrigBlock << "\'.\n");
updateUnwindTerminator(OrigBlock);
}
} else if (auto *CleanupEnd = dyn_cast<CleanupEndPadInst>(EHPadInst)) {
auto *CloneParentCP =
dyn_cast<CleanupPadInst>(CloneParent->getFirstNonPHI());
auto *EndedCP = CleanupEnd->getCleanupPad();
if (EndedCP == CloneParentCP) {
assert(isa<CatchEndPadInst>(OrigBlock->getFirstNonPHI()));
DEBUG_WITH_TYPE(
"winehprepare-coloring",
dbgs() << " removing unwind destination of original block \'"
<< OrigBlock->getName() << "\'.\n");
updateUnwindTerminator(OrigBlock);
} else {
assert(isa<CatchEndPadInst>(CloneBlock->getFirstNonPHI()));
DEBUG_WITH_TYPE(
"winehprepare-coloring",
dbgs() << " removing unwind destination of clone block \'"
<< CloneBlock->getName() << "\'.\n");
updateUnwindTerminator(CloneBlock);
}
} else {
assert(isa<CatchPadInst>(EHPadInst) || isa<CleanupPadInst>(EHPadInst) ||
isa<TerminatePadInst>(EHPadInst));
}
}
static void updateCatchTerminators(
Function &F, CatchPadInst *OrigCatch, CatchPadInst *CloneCatch,
std::vector<BasicBlock *> &OrigParents, BasicBlock *CloneParent,
ValueToValueMapTy &VMap,
std::map<BasicBlock *, SetVector<BasicBlock *>> &BlockColors,
std::map<BasicBlock *, std::set<BasicBlock *>> &FuncletBlocks) {
Instruction *I = CloneCatch->getUnwindDest()->getFirstNonPHI();
if (auto *CEP = dyn_cast<CatchEndPadInst>(I)) {
assert(BlockColors[CEP->getParent()].size() == 1);
BasicBlock *CEPFunclet = *(BlockColors[CEP->getParent()].begin());
BasicBlock *CEPCloneParent = nullptr;
CatchPadInst *PredCatch = nullptr;
if (CEPFunclet == CloneParent) {
for (auto *Parent : OrigParents) {
if (Parent != CloneParent) {
CEPCloneParent = Parent;
break;
}
}
PredCatch = OrigCatch;
} else {
CEPCloneParent = CloneParent;
PredCatch = CloneCatch;
}
assert(CEPCloneParent && PredCatch);
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Cloning catchendpad \'"
<< CEP->getParent()->getName() << "\' for funclet \'"
<< CEPCloneParent->getName() << "\'.\n");
BasicBlock *ClonedCEP = CloneBasicBlock(
CEP->getParent(), VMap, Twine(".from.", CEPCloneParent->getName()));
ClonedCEP->insertInto(&F, CEP->getParent()->getNextNode());
PredCatch->setUnwindDest(ClonedCEP);
FuncletBlocks[CEPCloneParent].insert(ClonedCEP);
BlockColors[ClonedCEP].insert(CEPCloneParent);
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Assigning color \'"
<< CEPCloneParent->getName() << "\' to block \'"
<< ClonedCEP->getName() << "\'.\n");
auto *ClonedCEPInst = cast<CatchEndPadInst>(ClonedCEP->getTerminator());
if (auto *Dest = ClonedCEPInst->getUnwindDest())
updateClonedEHPadUnwindToParent(Dest, OrigCatch->getUnwindDest(),
CloneCatch->getUnwindDest(), OrigParents,
CloneParent);
}
}
void WinEHPrepare::updateTerminatorsAfterFuncletClone(
Function &F, BasicBlock *OrigFunclet, BasicBlock *CloneFunclet,
BasicBlock *OrigBlock, BasicBlock *CloneBlock, BasicBlock *CloneParent,
ValueToValueMapTy &VMap, std::map<BasicBlock *, BasicBlock *> &Orig2Clone) {
TerminatorInst *CloneTerminator = CloneBlock->getTerminator();
if (!CloneTerminator->isExceptional())
return;
if (auto *CloneCatch = dyn_cast<CatchPadInst>(CloneTerminator)) {
auto *OrigCatch = cast<CatchPadInst>(OrigBlock->getTerminator());
updateCatchTerminators(F, OrigCatch, CloneCatch,
FuncletParents[OrigFunclet], CloneParent, VMap,
BlockColors, FuncletBlocks);
} else if (auto *CRI = dyn_cast<CatchReturnInst>(CloneTerminator)) {
if (FuncletBlocks[CloneParent].count(CRI->getSuccessor())) {
BasicBlock *OrigParent;
if (FuncletParents[OrigFunclet].front() == CloneParent)
OrigParent = FuncletParents[OrigFunclet].back();
else
OrigParent = FuncletParents[OrigFunclet].front();
for (succ_iterator SI = succ_begin(OrigBlock), SE = succ_end(OrigBlock);
SI != SE; ++SI)
(*SI)->removePredecessor(OrigBlock);
BasicBlock *LostBlock = EstrangedBlocks[OrigParent][CRI->getSuccessor()];
auto *OrigCatchRet = cast<CatchReturnInst>(OrigBlock->getTerminator());
if (LostBlock) {
OrigCatchRet->setSuccessor(LostBlock);
} else {
OrigCatchRet->eraseFromParent();
new UnreachableInst(OrigBlock->getContext(), OrigBlock);
}
} else {
for (succ_iterator SI = succ_begin(CloneBlock), SE = succ_end(CloneBlock);
SI != SE; ++SI)
(*SI)->removePredecessor(CloneBlock);
BasicBlock *LostBlock = EstrangedBlocks[CloneParent][CRI->getSuccessor()];
if (LostBlock) {
CRI->setSuccessor(LostBlock);
} else {
CRI->eraseFromParent();
new UnreachableInst(CloneBlock->getContext(), CloneBlock);
}
}
} else if (isa<CleanupReturnInst>(CloneTerminator) ||
isa<CleanupEndPadInst>(CloneTerminator)) {
BasicBlock *UnwindDest = nullptr;
if (auto *CRI = dyn_cast<CleanupReturnInst>(CloneTerminator))
UnwindDest = CRI->getUnwindDest();
else if (auto *CEI = dyn_cast<CleanupEndPadInst>(CloneTerminator))
UnwindDest = CEI->getUnwindDest();
if (!UnwindDest)
return;
updateClonedEHPadUnwindToParent(UnwindDest, OrigBlock, CloneBlock,
FuncletParents[OrigFunclet], CloneParent);
}
}
BasicBlock *WinEHPrepare::cloneFuncletForParent(Function &F,
BasicBlock *FuncletEntry,
BasicBlock *Parent) {
std::set<BasicBlock *> &BlocksInFunclet = FuncletBlocks[FuncletEntry];
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << "Cloning funclet \'" << FuncletEntry->getName()
<< "\' for parent \'" << Parent->getName() << "\'.\n");
std::map<BasicBlock *, BasicBlock *> Orig2Clone;
ValueToValueMapTy VMap;
for (BasicBlock *BB : BlocksInFunclet) {
BasicBlock *CBB =
CloneBasicBlock(BB, VMap, Twine(".from.", Parent->getName()));
CBB->insertInto(&F, BB->getNextNode());
VMap[BB] = CBB;
Orig2Clone[BB] = CBB;
}
BasicBlock *ClonedFunclet = Orig2Clone[FuncletEntry];
assert(ClonedFunclet);
std::set<BasicBlock *> &ClonedBlocks = FuncletBlocks[ClonedFunclet];
for (auto &BBMapping : Orig2Clone) {
BasicBlock *NewBlock = BBMapping.second;
ClonedBlocks.insert(NewBlock);
BlockColors[NewBlock].insert(ClonedFunclet);
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Assigning color \'" << ClonedFunclet->getName()
<< "\' to block \'" << NewBlock->getName()
<< "\'.\n");
for (Instruction &I : *NewBlock)
RemapInstruction(&I, VMap, RF_IgnoreMissingEntries);
}
for (auto &BBMapping : Orig2Clone) {
BasicBlock *OldBlock = BBMapping.first;
BasicBlock *NewBlock = BBMapping.second;
updateTerminatorsAfterFuncletClone(F, FuncletEntry, ClonedFunclet, OldBlock,
NewBlock, Parent, VMap, Orig2Clone);
for (BasicBlock *SuccBB : successors(NewBlock)) {
for (Instruction &SuccI : *SuccBB) {
auto *SuccPN = dyn_cast<PHINode>(&SuccI);
if (!SuccPN)
break;
int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock);
if (OldBlockIdx == -1)
break;
Value *IV = SuccPN->getIncomingValue(OldBlockIdx);
if (auto *Inst = dyn_cast<Instruction>(IV)) {
ValueToValueMapTy::iterator I = VMap.find(Inst);
if (I != VMap.end())
IV = I->second;
}
SuccPN->addIncoming(IV, NewBlock);
}
}
}
std::vector<BasicBlock *> &Parents = FuncletParents[FuncletEntry];
Parents.erase(std::remove(Parents.begin(), Parents.end(), Parent),
Parents.end());
assert(std::find(FuncletParents[ClonedFunclet].begin(),
FuncletParents[ClonedFunclet].end(),
Parent) == std::end(FuncletParents[ClonedFunclet]));
FuncletParents[ClonedFunclet].push_back(Parent);
for (auto *Child : FuncletChildren[FuncletEntry]) {
assert(std::find(FuncletChildren[ClonedFunclet].begin(),
FuncletChildren[ClonedFunclet].end(),
Child) == std::end(FuncletChildren[ClonedFunclet]));
FuncletChildren[ClonedFunclet].push_back(Child);
assert(std::find(FuncletParents[Child].begin(), FuncletParents[Child].end(),
ClonedFunclet) == std::end(FuncletParents[Child]));
FuncletParents[Child].push_back(ClonedFunclet);
}
for (auto *U : FuncletEntry->users()) {
auto *UT = dyn_cast<TerminatorInst>(U);
if (!UT)
continue;
BasicBlock *UBB = UT->getParent();
assert(BlockColors[UBB].size() == 1);
BasicBlock *UFunclet = *(BlockColors[UBB].begin());
assert(UFunclet != FuncletEntry);
if (UFunclet == Parent) {
FuncletEntry->removePredecessor(UBB);
TerminatorInst *Terminator = UBB->getTerminator();
RemapInstruction(Terminator, VMap, RF_IgnoreMissingEntries);
}
}
assert(std::all_of(pred_begin(FuncletEntry), pred_end(FuncletEntry),
[&ClonedFunclet](BasicBlock *Pred) {
return std::find(pred_begin(ClonedFunclet),
pred_end(ClonedFunclet),
Pred) == pred_end(ClonedFunclet);
}));
std::vector<PHINode *> PHIsToErase;
for (Instruction &I : *ClonedFunclet) {
auto *PN = dyn_cast<PHINode>(&I);
if (!PN)
break;
for (auto *Pred : predecessors(FuncletEntry))
PN->removeIncomingValue(Pred, false);
}
for (auto *PN : PHIsToErase)
PN->eraseFromParent();
for (auto &It : FuncletChildren[Parent]) {
if (It == FuncletEntry) {
It = ClonedFunclet;
break;
}
}
return ClonedFunclet;
}
void WinEHPrepare::makeFuncletEdgeUnreachable(BasicBlock *Parent,
BasicBlock *Child) {
for (BasicBlock *BB : FuncletBlocks[Parent]) {
TerminatorInst *Terminator = BB->getTerminator();
if (!Terminator->isExceptional())
continue;
BasicBlock *UnwindDest = nullptr;
if (auto *I = dyn_cast<InvokeInst>(Terminator))
UnwindDest = I->getUnwindDest();
else if (auto *I = dyn_cast<CatchEndPadInst>(Terminator))
UnwindDest = I->getUnwindDest();
else if (auto *I = dyn_cast<TerminatePadInst>(Terminator))
UnwindDest = I->getUnwindDest();
if (UnwindDest == Child)
removeUnwindEdge(BB);
}
std::vector<BasicBlock *> &Children = FuncletChildren[Parent];
Children.erase(std::remove(Children.begin(), Children.end(), Child),
Children.end());
}
static void updateSiblingToSiblingUnwind(
BasicBlock *CurFunclet,
std::map<BasicBlock *, SetVector<BasicBlock *>> &BlockColors,
std::map<BasicBlock *, std::set<BasicBlock *>> &FuncletBlocks,
std::map<BasicBlock *, std::vector<BasicBlock *>> &FuncletParents,
std::map<BasicBlock *, std::vector<BasicBlock *>> &FuncletChildren,
std::map<BasicBlock *, BasicBlock *> &Funclet2Orig) {
for (BasicBlock *ChildFunclet : FuncletChildren[CurFunclet]) {
for (auto *BB : FuncletBlocks[ChildFunclet]) {
TerminatorInst *Terminator = BB->getTerminator();
if (!Terminator->isExceptional())
continue;
BasicBlock *UnwindDest = nullptr;
if (auto *I = dyn_cast<CatchPadInst>(Terminator)) {
UnwindDest = I->getUnwindDest();
if (isa<CatchEndPadInst>(Terminator)) {
assert(BlockColors[UnwindDest].size() == 1 &&
"Cloned catchpad unwinds to an pad with multiple parents.");
assert(FuncletParents[UnwindDest].front() == CurFunclet &&
"Cloned catchpad unwinds to the wrong parent.");
continue;
}
} else {
if (auto *I = dyn_cast<CleanupReturnInst>(Terminator))
UnwindDest = I->getUnwindDest();
else if (auto *I = dyn_cast<CleanupEndPadInst>(Terminator))
UnwindDest = I->getUnwindDest();
if (!UnwindDest)
continue;
}
Instruction *EHPad = UnwindDest->getFirstNonPHI();
if (!isa<CleanupPadInst>(EHPad) && !isa<CatchPadInst>(EHPad) &&
!isa<TerminatePadInst>(EHPad))
continue;
if (FuncletParents[UnwindDest].size() == 1 &&
FuncletParents[UnwindDest].front() == CurFunclet)
continue;
BasicBlock *DestOrig = Funclet2Orig[UnwindDest];
BasicBlock *TargetSibling = nullptr;
for (auto &Mapping : Funclet2Orig) {
if (Mapping.second != DestOrig)
continue;
BasicBlock *MappedFunclet = Mapping.first;
if (FuncletParents[MappedFunclet].size() == 1 &&
FuncletParents[MappedFunclet].front() == CurFunclet) {
TargetSibling = MappedFunclet;
}
}
assert(TargetSibling && "Funclet unwinds to unexpected destination.");
if (auto *I = dyn_cast<CatchPadInst>(Terminator))
I->setUnwindDest(TargetSibling);
else if (auto *I = dyn_cast<CleanupReturnInst>(Terminator))
I->setUnwindDest(TargetSibling);
else if (auto *I = dyn_cast<CleanupEndPadInst>(Terminator))
I->setUnwindDest(TargetSibling);
}
}
for (BasicBlock *ChildFunclet : FuncletChildren[CurFunclet]) {
bool NeedOrigInvokeRemapping = false;
for (auto *BB : FuncletBlocks[ChildFunclet]) {
TerminatorInst *Terminator = BB->getTerminator();
auto *II = dyn_cast<InvokeInst>(Terminator);
if (!II)
continue;
BasicBlock *UnwindDest = II->getUnwindDest();
assert(UnwindDest && "Invoke unwinds to a null destination.");
assert(UnwindDest->isEHPad() && "Invoke does not unwind to an EH pad.");
auto *EHPadInst = UnwindDest->getFirstNonPHI();
if (isa<CleanupEndPadInst>(EHPadInst)) {
assert(isa<CleanupPadInst>(ChildFunclet->getFirstNonPHI()) &&
"Unwinding to cleanup end pad from a non cleanup pad funclet.");
assert(FuncletBlocks[ChildFunclet].count(UnwindDest) &&
"Unwind destination for invoke was not updated during cloning.");
} else if (isa<CatchEndPadInst>(EHPadInst)) {
BasicBlock *OrigFunclet = Funclet2Orig[ChildFunclet];
auto *CurCatch = cast<CatchPadInst>(ChildFunclet->getFirstNonPHI());
auto *OrigCatch = cast<CatchPadInst>(OrigFunclet->getFirstNonPHI());
if (OrigCatch->getUnwindDest() == UnwindDest) {
II->setUnwindDest(CurCatch->getUnwindDest());
} else if (CurCatch->getUnwindDest() == UnwindDest) {
NeedOrigInvokeRemapping = true;
} else {
assert((getEndPadForCatch(OrigCatch) == UnwindDest ||
getEndPadForCatch(CurCatch) == UnwindDest) &&
"Invoke within catch pad unwinds to an invalid catch end pad.");
BasicBlock *CurCatchEnd = getEndPadForCatch(CurCatch);
if (CurCatchEnd == UnwindDest)
NeedOrigInvokeRemapping = true;
else
II->setUnwindDest(CurCatchEnd);
}
}
}
if (NeedOrigInvokeRemapping) {
BasicBlock *OrigFunclet = Funclet2Orig[ChildFunclet];
for (auto *BB : FuncletBlocks[OrigFunclet]) {
auto *II = dyn_cast<InvokeInst>(BB->getTerminator());
if (!II)
continue;
BasicBlock *UnwindDest = II->getUnwindDest();
assert(UnwindDest && "Invoke unwinds to a null destination.");
assert(UnwindDest->isEHPad() && "Invoke does not unwind to an EH pad.");
auto *CEP = dyn_cast<CatchEndPadInst>(UnwindDest->getFirstNonPHI());
if (!CEP)
continue;
auto *OrigCatch = cast<CatchPadInst>(OrigFunclet->getFirstNonPHI());
BasicBlock *OrigCatchEnd = getEndPadForCatch(OrigCatch);
if (OrigCatchEnd != UnwindDest)
II->setUnwindDest(OrigCatchEnd);
}
}
}
}
void WinEHPrepare::resolveFuncletAncestry(
Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks) {
if (!FuncletCloningRequired)
return;
std::map<BasicBlock *, BasicBlock *> Funclet2Orig;
for (auto *Funclet : EntryBlocks)
Funclet2Orig[Funclet] = Funclet;
SmallVector<BasicBlock *, 4> FuncletPath;
FuncletPath.push_back(&(F.getEntryBlock()));
resolveFuncletAncestryForPath(F, FuncletPath, Funclet2Orig);
}
void WinEHPrepare::resolveFuncletAncestryForPath(
Function &F, SmallVectorImpl<BasicBlock *> &FuncletPath,
std::map<BasicBlock *, BasicBlock *> &Funclet2Orig) {
bool ClonedAnyChildren = false;
BasicBlock *CurFunclet = FuncletPath.back();
std::vector<BasicBlock *> Children(FuncletChildren[CurFunclet]);
for (BasicBlock *ChildFunclet : Children) {
bool IsCyclic = false;
BasicBlock *ChildIdentity = Funclet2Orig[ChildFunclet];
for (BasicBlock *Ancestor : FuncletPath) {
BasicBlock *AncestorIdentity = Funclet2Orig[Ancestor];
if (AncestorIdentity == ChildIdentity) {
IsCyclic = true;
break;
}
}
if (IsCyclic) {
makeFuncletEdgeUnreachable(CurFunclet, ChildFunclet);
continue;
}
if (FuncletParents[ChildFunclet].size() > 1) {
ChildFunclet = cloneFuncletForParent(F, ChildFunclet, CurFunclet);
Funclet2Orig[ChildFunclet] = ChildIdentity;
ClonedAnyChildren = true;
}
FuncletPath.push_back(ChildFunclet);
resolveFuncletAncestryForPath(F, FuncletPath, Funclet2Orig);
FuncletPath.pop_back();
}
if (!ClonedAnyChildren)
return;
updateSiblingToSiblingUnwind(CurFunclet, BlockColors, FuncletBlocks,
FuncletParents, FuncletChildren, Funclet2Orig);
}
void WinEHPrepare::colorFunclets(Function &F,
SmallVectorImpl<BasicBlock *> &EntryBlocks) {
::colorFunclets(F, EntryBlocks, BlockColors, FuncletBlocks);
for (BasicBlock *FuncletEntry : EntryBlocks) {
SetVector<BasicBlock *> &ColorMapItem = BlockColors[FuncletEntry];
if (ColorMapItem.size() > 1)
FuncletCloningRequired = true;
for (BasicBlock *Parent : ColorMapItem) {
assert(std::find(FuncletChildren[Parent].begin(),
FuncletChildren[Parent].end(),
FuncletEntry) == std::end(FuncletChildren[Parent]));
FuncletChildren[Parent].push_back(FuncletEntry);
assert(std::find(FuncletParents[FuncletEntry].begin(),
FuncletParents[FuncletEntry].end(),
Parent) == std::end(FuncletParents[FuncletEntry]));
FuncletParents[FuncletEntry].push_back(Parent);
}
ColorMapItem.clear();
ColorMapItem.insert(FuncletEntry);
}
}
void llvm::calculateCatchReturnSuccessorColors(const Function *Fn,
WinEHFuncInfo &FuncInfo) {
SmallVector<BasicBlock *, 4> EntryBlocks;
findFuncletEntryPoints(const_cast<Function &>(*Fn), EntryBlocks);
std::map<BasicBlock *, SetVector<BasicBlock *>> BlockColors;
std::map<BasicBlock *, std::set<BasicBlock *>> FuncletBlocks;
colorFunclets(const_cast<Function &>(*Fn), EntryBlocks, BlockColors,
FuncletBlocks);
for (BasicBlock *FuncletEntry : EntryBlocks) {
BlockColors[FuncletEntry].clear();
BlockColors[FuncletEntry].insert(FuncletEntry);
}
for (auto &Funclet : FuncletBlocks) {
BasicBlock *FuncletPadBB = Funclet.first;
Instruction *FirstNonPHI = FuncletPadBB->getFirstNonPHI();
auto *CatchPad = dyn_cast<CatchPadInst>(FirstNonPHI);
if (!CatchPad)
continue;
for (User *Exit : CatchPad->users()) {
auto *CatchReturn = dyn_cast<CatchReturnInst>(Exit);
if (!CatchReturn)
continue;
BasicBlock *CatchRetSuccessor = CatchReturn->getSuccessor();
SetVector<BasicBlock *> &SuccessorColors = BlockColors[CatchRetSuccessor];
assert(SuccessorColors.size() == 1 && "Expected BB to be monochrome!");
BasicBlock *Color = *SuccessorColors.begin();
FuncInfo.CatchRetSuccessorColorMap[CatchReturn] = Color;
}
}
}
void WinEHPrepare::demotePHIsOnFunclets(Function &F) {
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);
if (!PN)
break;
AllocaInst *SpillSlot = insertPHILoads(PN, F);
if (SpillSlot)
insertPHIStores(PN, SpillSlot);
PHINodes.push_back(PN);
}
}
for (auto *PN : PHINodes) {
PN->replaceAllUsesWith(UndefValue::get(PN->getType()));
PN->eraseFromParent();
}
}
void WinEHPrepare::demoteUsesBetweenFunclets(Function &F) {
for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) {
BasicBlock *BB = &*FI++;
SetVector<BasicBlock *> &ColorsForBB = BlockColors[BB];
for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) {
Instruction *I = &*BI++;
if (auto *AI = dyn_cast<AllocaInst>(I))
if (AI->isStaticAlloca())
continue;
demoteNonlocalUses(I, ColorsForBB, F);
}
}
}
void WinEHPrepare::demoteArgumentUses(Function &F) {
SetVector<BasicBlock *> &ColorsForEntry = BlockColors[&F.getEntryBlock()];
for (Argument &Arg : F.args())
demoteNonlocalUses(&Arg, ColorsForEntry, F);
}
void WinEHPrepare::cloneCommonBlocks(
Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks) {
for (BasicBlock *FuncletPadBB : EntryBlocks) {
std::set<BasicBlock *> &BlocksInFunclet = FuncletBlocks[FuncletPadBB];
std::map<BasicBlock *, BasicBlock *> Orig2Clone;
ValueToValueMapTy VMap;
for (auto BlockIt = BlocksInFunclet.begin(),
BlockEnd = BlocksInFunclet.end();
BlockIt != BlockEnd;) {
BasicBlock *BB = *BlockIt++;
SetVector<BasicBlock *> &ColorsForBB = BlockColors[BB];
size_t NumColorsForBB = ColorsForBB.size();
if (NumColorsForBB == 1)
continue;
if (isa<CatchEndPadInst>(BB->getFirstNonPHI())) {
assert(
FuncletCloningRequired &&
"Found multi-colored catchendpad with no multi-parent funclets.");
BasicBlock *CatchParent = nullptr;
for (BasicBlock *PredBB : predecessors(BB)) {
if (isa<CatchPadInst>(PredBB->getTerminator())) {
CatchParent = PredBB;
break;
}
}
assert(CatchParent && "No catchpad found for catchendpad.");
BasicBlock *CorrectColor = FuncletParents[CatchParent].front();
assert(FuncletBlocks[CorrectColor].count(BB));
assert(BlockColors[BB].count(CorrectColor));
for (auto It = BlockColors[BB].begin(), End = BlockColors[BB].end();
It != End; ) {
auto Temp = It++;
BasicBlock *ContainingFunclet = *Temp;
if (ContainingFunclet != CorrectColor) {
FuncletBlocks[ContainingFunclet].erase(BB);
BlockColors[BB].remove(ContainingFunclet);
}
}
assert(BlockColors[BB].size() == 1);
continue;
}
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Cloning block \'" << BB->getName()
<< "\' for funclet \'" << FuncletPadBB->getName()
<< "\'.\n");
BasicBlock *CBB =
CloneBasicBlock(BB, VMap, Twine(".for.", FuncletPadBB->getName()));
CBB->insertInto(&F, BB->getNextNode());
VMap[BB] = CBB;
Orig2Clone[BB] = CBB;
}
if (Orig2Clone.empty())
continue;
for (auto &BBMapping : Orig2Clone) {
BasicBlock *OldBlock = BBMapping.first;
BasicBlock *NewBlock = BBMapping.second;
BlocksInFunclet.insert(NewBlock);
BlockColors[NewBlock].insert(FuncletPadBB);
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Assigned color \'" << FuncletPadBB->getName()
<< "\' to block \'" << NewBlock->getName()
<< "\'.\n");
BlocksInFunclet.erase(OldBlock);
BlockColors[OldBlock].remove(FuncletPadBB);
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Removed color \'" << FuncletPadBB->getName()
<< "\' from block \'" << OldBlock->getName()
<< "\'.\n");
if (FuncletCloningRequired) {
for (auto *Pred : predecessors(OldBlock)) {
auto *Terminator = Pred->getTerminator();
if (!isa<CatchReturnInst>(Terminator))
continue;
bool BlockWillBeEstranged = false;
for (auto *Color : BlockColors[Pred]) {
if (FuncletParents[Color].size() > 1) {
BlockWillBeEstranged = true;
break; }
}
if (BlockWillBeEstranged) {
EstrangedBlocks[FuncletPadBB][OldBlock] = NewBlock;
DEBUG_WITH_TYPE("winehprepare-coloring",
dbgs() << " Saved mapping of estranged block \'"
<< NewBlock->getName() << "\' for \'"
<< FuncletPadBB->getName() << "\'.\n");
break; }
}
}
}
for (BasicBlock *BB : BlocksInFunclet)
for (Instruction &I : *BB)
RemapInstruction(&I, VMap,
RF_IgnoreMissingEntries | RF_NoModuleLevelChanges);
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;
int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock);
if (OldBlockIdx == -1)
break;
Value *IV = SuccPN->getIncomingValue(OldBlockIdx);
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) {
SmallVector<Use *, 16> UsesToRename;
auto *OldI = dyn_cast<Instruction>(const_cast<Value *>(VT.first));
if (!OldI)
continue;
auto *NewI = cast<Instruction>(VT.second);
for (Use &U : OldI->uses()) {
Instruction *UserI = cast<Instruction>(U.getUser());
BasicBlock *UserBB = UserI->getParent();
SetVector<BasicBlock *> &ColorsForUserBB = BlockColors[UserBB];
assert(!ColorsForUserBB.empty());
if (ColorsForUserBB.size() > 1 ||
*ColorsForUserBB.begin() != FuncletPadBB)
UsesToRename.push_back(&U);
}
if (UsesToRename.empty())
continue;
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::removeImplausibleTerminators(Function &F) {
for (auto &Funclet : FuncletBlocks) {
BasicBlock *FuncletPadBB = Funclet.first;
std::set<BasicBlock *> &BlocksInFunclet = Funclet.second;
Instruction *FirstNonPHI = FuncletPadBB->getFirstNonPHI();
auto *CatchPad = dyn_cast<CatchPadInst>(FirstNonPHI);
auto *CleanupPad = dyn_cast<CleanupPadInst>(FirstNonPHI);
for (BasicBlock *BB : BlocksInFunclet) {
TerminatorInst *TI = BB->getTerminator();
bool IsUnreachableRet = isa<ReturnInst>(TI) && (CatchPad || CleanupPad);
bool IsUnreachableCatchret = false;
if (auto *CRI = dyn_cast<CatchReturnInst>(TI))
IsUnreachableCatchret = CRI->getCatchPad() != CatchPad;
bool IsUnreachableCleanupret = false;
if (auto *CRI = dyn_cast<CleanupReturnInst>(TI))
IsUnreachableCleanupret = CRI->getCleanupPad() != CleanupPad;
bool IsUnreachableCleanupendpad = false;
if (auto *CEPI = dyn_cast<CleanupEndPadInst>(TI))
IsUnreachableCleanupendpad = CEPI->getCleanupPad() != CleanupPad;
if (IsUnreachableRet || IsUnreachableCatchret ||
IsUnreachableCleanupret || IsUnreachableCleanupendpad) {
for (BasicBlock *SuccBB : TI->successors())
SuccBB->removePredecessor(BB);
if (IsUnreachableCleanupendpad) {
for (pred_iterator PI = pred_begin(BB), PE = pred_end(BB);
PI != PE;) {
BasicBlock *Pred = *PI++;
removeUnwindEdge(Pred);
}
}
new UnreachableInst(BB->getContext(), TI);
TI->eraseFromParent();
}
}
}
}
void WinEHPrepare::cleanupPreparedFunclets(Function &F) {
for (Function::iterator FI = F.begin(), FE = F.end(); FI != FE;) {
BasicBlock *BB = &*FI++;
SimplifyInstructionsInBlock(BB);
ConstantFoldTerminator(BB, true);
MergeBlockIntoPredecessor(BB);
}
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!");
if (EHPadHasPHI)
report_fatal_error("EH Pad still has a PHI!");
}
}
}
bool WinEHPrepare::prepareExplicitEH(
Function &F, SmallVectorImpl<BasicBlock *> &EntryBlocks) {
replaceTerminatePadWithCleanup(F);
colorFunclets(F, EntryBlocks);
if (!DisableDemotion) {
demotePHIsOnFunclets(F);
demoteUsesBetweenFunclets(F);
demoteArgumentUses(F);
}
cloneCommonBlocks(F, EntryBlocks);
resolveFuncletAncestry(F, EntryBlocks);
if (!DisableCleanups) {
removeImplausibleTerminators(F);
cleanupPreparedFunclets(F);
}
verifyPreparedFunclets(F);
BlockColors.clear();
FuncletBlocks.clear();
FuncletChildren.clear();
FuncletParents.clear();
EstrangedBlocks.clear();
FuncletCloningRequired = false;
return true;
}
AllocaInst *WinEHPrepare::insertPHILoads(PHINode *PN, Function &F) {
BasicBlock *PHIBlock = PN->getParent();
AllocaInst *SpillSlot = nullptr;
if (isa<CleanupPadInst>(PHIBlock->getFirstNonPHI())) {
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;
}
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()) {
assert(isa<PHINode>(UsingInst));
continue;
}
replaceUseWithLoad(PN, U, SpillSlot, Loads, F);
}
return SpillSlot;
}
void WinEHPrepare::insertPHIStores(PHINode *OriginalPHI,
AllocaInst *SpillSlot) {
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) {
for (unsigned i = 0, e = PN->getNumIncomingValues(); i < e; ++i) {
Value *PredVal = PN->getIncomingValue(i);
if (isa<UndefValue>(PredVal))
continue;
insertPHIStore(PN->getIncomingBlock(i), PredVal, SpillSlot, Worklist);
}
} else {
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())) {
Worklist.push_back({PredBlock, PredVal});
return;
}
new StoreInst(PredVal, SpillSlot, PredBlock->getTerminator());
}
static bool isBlockColorSetEquivalent(SetVector<BasicBlock *> &SetA,
SetVector<BasicBlock *> &SetB) {
if (SetA.size() != SetB.size())
return false;
for (auto *Color : SetA)
if (!SetB.count(Color))
return false;
return true;
}
void WinEHPrepare::demoteNonlocalUses(Value *V,
SetVector<BasicBlock *> &ColorsForBB,
Function &F) {
if (isa<CatchPadInst>(V) || isa<CleanupPadInst>(V))
return;
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();
SetVector<BasicBlock *> &ColorsForUsingBB = BlockColors[UsingBB];
if (isBlockColorSetEquivalent(ColorsForUsingBB, ColorsForBB))
continue;
replaceUseWithLoad(V, U, SpillSlot, Loads, F);
}
if (SpillSlot) {
BasicBlock::iterator InsertPt;
if (isa<Argument>(V)) {
InsertPt = F.getEntryBlock().getTerminator()->getIterator();
} else if (isa<TerminatorInst>(V)) {
auto *II = cast<InvokeInst>(V);
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.");
SetVector<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)->getIterator();
++InsertPt;
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) {
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)) {
BasicBlock *IncomingBlock = UsingPHI->getIncomingBlock(U);
if (auto *CatchRet =
dyn_cast<CatchReturnInst>(IncomingBlock->getTerminator())) {
BasicBlock *PHIBlock = UsingInst->getParent();
BasicBlock *NewBlock = SplitEdge(IncomingBlock, PHIBlock);
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);
SetVector<BasicBlock *> &ColorsForPHIBlock = BlockColors[PHIBlock];
BlockColors[NewBlock] = ColorsForPHIBlock;
for (BasicBlock *FuncletPad : ColorsForPHIBlock)
FuncletBlocks[FuncletPad].insert(NewBlock);
IncomingBlock = NewBlock;
}
Value *&Load = Loads[IncomingBlock];
if (!Load)
Load = new LoadInst(SpillSlot, Twine(V->getName(), ".wineh.reload"),
false, IncomingBlock->getTerminator());
U.set(Load);
} else {
auto *Load = new LoadInst(SpillSlot, Twine(V->getName(), ".wineh.reload"),
false, UsingInst);
U.set(Load);
}
}
void WinEHFuncInfo::addIPToStateRange(const BasicBlock *PadBB,
MCSymbol *InvokeBegin,
MCSymbol *InvokeEnd) {
assert(PadBB->isEHPad() && EHPadStateMap.count(PadBB->getFirstNonPHI()) &&
"should get EH pad BB with precomputed state");
InvokeToStateMap[InvokeBegin] =
std::make_pair(EHPadStateMap[PadBB->getFirstNonPHI()], InvokeEnd);
}