Skip to content

Commit

Permalink
[WinEH] Update CoreCLR EH state numbering
Browse files Browse the repository at this point in the history
Fix the CLR state numbering to generate correct tables for the new funclet
EH representation, and update the lit test to verify them.

Functionally, there are four changes:
 - Arity of cleanuppad parameters needs to use getNumArgOperands() instead
   of getNumOperands() now that cleanuppad has a parent token operand.
 - We assign a catchswitch the state of its first catch, not its unwind
   destination.
 - Don't queue catchpads' users -- this could visit handlers for inner
   tries before handlers for outer tries, and we'll visit everything we
   need to, in correct order, by queuing pads' predecessors. Change the
   initial worklist conditions from isToplevelPadForMSVC to a suitably
   implemented isToplevelPadForCLR accordingly.
 - Adjust getEHPadFromPredecessor to allow CLR state numbering to call it
   and have preds returned regardless of their parentage.

Also change some variable names to improve readability.
  • Loading branch information
JosephTremoulet committed Dec 18, 2015
1 parent eb1a3f7 commit 71ee52e
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 109 deletions.
132 changes: 97 additions & 35 deletions lib/CodeGen/WinEHPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,20 +201,22 @@ static void calculateStateNumbersForInvokes(const Function *Fn,
}

// 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.
// to. If the unwind edge came from an invoke, return null. If ParentPad is
// non-null, EHPads that do not have it as their ParentPad will also be ignored
// (returning 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)
if (ParentPad && CatchSwitch->getParentPad() != ParentPad)
return nullptr;
return BB;
}
assert(!TI->isEHPad() && "unexpected EHPad!");
auto *CleanupPad = cast<CleanupReturnInst>(TI)->getCleanupPad();
if (CleanupPad->getParentPad() != ParentPad)
if (ParentPad && CleanupPad->getParentPad() != ParentPad)
return nullptr;
return CleanupPad->getParent();
}
Expand Down Expand Up @@ -437,6 +439,78 @@ static int addClrEHHandler(WinEHFuncInfo &FuncInfo, int ParentState,
return FuncInfo.ClrEHUnwindMap.size() - 1;
}

static bool isTopLevelPadForCLR(const Instruction *EHPad) {
// For the CLR, a pad is "top-level" if it is not a catchpad and exceptions
// that propagate out of it continue straight up to the function's caller.

// A catchswitch is directly annotated as unwinding to caller or not.
// Note that upstream code might mark a catchswitch as "unwinds to caller"
// when really what it proved is that the catchswitch never takes its unwind
// edge (e.g. SimplifyUnreachable). If we hit that case here, we will end up
// reporting such a catchswitch as top-level -- i.e. not inside any protected
// region. The "missing" entries in the EH table should be benign, as they
// would describe an unwind that can never happen.
if (const auto *CSI = dyn_cast<CatchSwitchInst>(EHPad))
return CSI->unwindsToCaller();

// Rather than redundantly mark catchpads and their enclosing catchswitches
// as toplevel when both unwind to caller, just rely on visiting the catchpad
// as a consequence of visiting its catchswitch.
if (isa<CatchPadInst>(EHPad))
return false;

// The pad is a cleanuppad, which is the tricky case because what we want
// to know is if exceptions propagating out of it escape to the caller or
// not, but don't have direct linkage to that information. The information
// will be available on any cleanuprets for the cleanuppad, as well as any
// nested catchpads. Likewise for nested cleanuppads, which makes the
// problem recursive, so use a worklist, seeded with the given pad.
SmallVector<const CleanupPadInst *, 4> Worklist;
SmallPtrSet<const CleanupPadInst *, 4> UselessCleanups;
do {
Worklist.push_back(cast<CleanupPadInst>(EHPad));
do {
const CleanupPadInst *Cleanup = Worklist.pop_back_val();
for (const User *U : Cleanup->users()) {
if (const auto *CRI = dyn_cast<CleanupReturnInst>(U))
return CRI->unwindsToCaller();
if (const auto *CSI = dyn_cast<CatchSwitchInst>(U)) {
if (CSI->hasUnwindDest())
return false;
// This catchswitch is annotated as "unwinds to caller", but we can't
// trust that means what it says (e.g. SimplifyUnreachable might have
// rewritten it that way because it initially unwound to an inner
// cleanuppad containing unreachable). So just ignore this user.
continue;
}
// FIXME: This should also be checking for invokes in the cleanup; their
// unwind dest must also agree. Calls in the cleanup have the same
// issue as catchswitch, that they might not necessarily imply unwinding
// to caller.
if (const auto *CPI = dyn_cast<CleanupPadInst>(U))
if (UselessCleanups.count(CPI))
Worklist.push_back(CPI);
}
} while (!Worklist.empty());
// Couldn't find any unwind edges within the cleanup. We have to keep it
// within its parent funclet, but can assume that it is not in any try
// regions within the parent, so try to find the parent's unwind dest.
const Value *Parent = cast<CleanupPadInst>(EHPad)->getParentPad();
// Check for the "none" sentinel that indicates this cleanup isn't nested
// in another funclet, which implies it is top-level.
if (isa<ConstantTokenNone>(Parent))
return true;
// As above, catchpads are never considered toplevel, because their
// catchswitches may be and they will be visited from their catchswitch.
if (isa<CatchPadInst>(Parent))
return false;
// Parent must be a cleanuppad; report this one as useless so we don't
// revisit it, and recurse up to the parent.
UselessCleanups.insert(cast<CleanupPadInst>(EHPad));
EHPad = cast<Instruction>(Parent);
} while (true);
}

void llvm::calculateClrEHStateNumbers(const Function *Fn,
WinEHFuncInfo &FuncInfo) {
// Return if it's already been done.
Expand All @@ -453,7 +527,7 @@ void llvm::calculateClrEHStateNumbers(const Function *Fn,
if (BB.isLandingPad())
report_fatal_error("CoreCLR EH cannot use landingpads");
const Instruction *FirstNonPHI = BB.getFirstNonPHI();
if (!isTopLevelPadForMSVC(FirstNonPHI))
if (!isTopLevelPadForCLR(FirstNonPHI))
continue;
// queue this with sentinel parent state -1 to mean unwind to caller.
Worklist.emplace_back(FirstNonPHI, -1);
Expand All @@ -464,58 +538,46 @@ void llvm::calculateClrEHStateNumbers(const Function *Fn,
int ParentState;
std::tie(Pad, ParentState) = Worklist.pop_back_val();

Value *ParentPad;
int PredState;
int SelfState;
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 =
(Cleanup->getNumArgOperands() ? ClrHandlerType::Fault
: ClrHandlerType::Finally);
SelfState =
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;
// Walk the catchpads in reverse order since the early ones are reported
// like descendants of the later ones in the EH tables.
int NewState = ParentState;
for (auto HandlerI = Handlers.rbegin(), HandlerE = Handlers.rend();
HandlerI != HandlerE; ++HandlerI) {
const CatchPadInst *Catch = *HandlerI;
const BasicBlock *PadBlock = Catch->getParent();
SmallVector<const BasicBlock *, 4> CatchBlocks(CatchSwitch->handlers());
for (auto CBI = CatchBlocks.rbegin(), CBE = CatchBlocks.rend();
CBI != CBE; ++CBI) {
const auto *Catch = cast<CatchPadInst>((*CBI)->getFirstNonPHI());
uint32_t TypeToken = static_cast<uint32_t>(
cast<ConstantInt>(Catch->getArgOperand(0))->getZExtValue());
NewState = addClrEHHandler(FuncInfo, NewState, ClrHandlerType::Catch,
TypeToken, PadBlock);
TypeToken, *CBI);
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();
// The catchswitch uses the same state number as the first catch (or, if
// we ever see an empty catchswitch, the state number of its successor).
SelfState = NewState;
} else {
llvm_unreachable("Unexpected EH pad");
}

// Record this pad's state.
FuncInfo.EHPadStateMap[Pad] = SelfState;

// 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);
if ((Pred = getEHPadFromPredecessor(Pred, nullptr)))
Worklist.emplace_back(Pred->getFirstNonPHI(), SelfState);
}
}

Expand Down
Loading

0 comments on commit 71ee52e

Please sign in to comment.