Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/coreclr/jit/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,30 @@ FlowEdge* Compiler::BlockPredsWithEH(BasicBlock* blk)
}
}

if (ehblk->HasFinallyOrFaultHandler() && (ehblk->ebdHndBeg == blk))
{
// block is a finally or fault handler; all enclosing filters are predecessors
unsigned enclosing = ehblk->ebdEnclosingTryIndex;
while (enclosing != EHblkDsc::NO_ENCLOSING_INDEX)
{
EHblkDsc* enclosingDsc = ehGetDsc(enclosing);
if (enclosingDsc->HasFilter())
{
for (BasicBlock* filterBlk = enclosingDsc->ebdFilter; filterBlk != enclosingDsc->ebdHndBeg;
filterBlk = filterBlk->bbNext)
{
res = new (this, CMK_FlowEdge) FlowEdge(filterBlk, res);

assert(filterBlk->VisitEHSecondPassSuccs(this, [blk](BasicBlock* succ) {
return succ == blk ? BasicBlockVisit::Abort : BasicBlockVisit::Continue;
}) == BasicBlockVisit::Abort);
}
}

enclosing = enclosingDsc->ebdEnclosingTryIndex;
}
}

#ifdef DEBUG
unsigned hash = SsaStressHashHelper();
if (hash != 0)
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/jit/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -1221,9 +1221,14 @@ struct BasicBlock : private LIR::Range
}
};

template <typename TFunc>
BasicBlockVisit VisitEHSecondPassSuccs(Compiler* comp, TFunc func);

template <typename TFunc>
BasicBlockVisit VisitAllSuccs(Compiler* comp, TFunc func);

bool HasPotentialEHSuccs(Compiler* comp);

// BBSuccList: adapter class for forward iteration of block successors, using range-based `for`,
// normally used via BasicBlock::Succs(), e.g.:
// for (BasicBlock* const target : block->Succs()) ...
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2224,6 +2224,7 @@ class Compiler

bool bbInCatchHandlerILRange(BasicBlock* blk);
bool bbInFilterILRange(BasicBlock* blk);
bool bbInFilterBBRange(BasicBlock* blk);
bool bbInTryRegions(unsigned regionIndex, BasicBlock* blk);
bool bbInExnFlowRegions(unsigned regionIndex, BasicBlock* blk);
bool bbInHandlerRegions(unsigned regionIndex, BasicBlock* blk);
Expand Down Expand Up @@ -4907,7 +4908,7 @@ class Compiler
void fgPerNodeLocalVarLiveness(GenTreeHWIntrinsic* hwintrinsic);
#endif // FEATURE_HW_INTRINSICS

VARSET_VALRET_TP fgGetHandlerLiveVars(BasicBlock* block);
void fgAddHandlerLiveVars(BasicBlock* block, VARSET_TP& ehHandlerLiveVars);

void fgLiveVarAnalysis(bool updateInternalOnly = false);

Expand Down
156 changes: 134 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,94 @@ inline EHblkDsc* Compiler::ehGetBlockHndDsc(BasicBlock* block)
return BasicBlockVisit::Abort; \
}

//------------------------------------------------------------------------------
// VisitEHSecondPassSuccs: Given a block, if it is a filter (i.e. invoked in
// first-pass EH), then visit all successors that control may flow to as part
// of second-pass EH.
//
// Arguments:
// comp - Compiler instance
// func - Callback
//
// Returns:
// Whether or not the visiting should proceed.
//
// Remarks:
// This function handles the semantics of first and second pass EH where the
// EH subsystem may invoke any enclosed finally/fault right after invoking a
// filter.
//
template <typename TFunc>
BasicBlockVisit BasicBlock::VisitEHSecondPassSuccs(Compiler* comp, TFunc func)
{
if (!hasHndIndex())
{
return BasicBlockVisit::Continue;
}

const unsigned thisHndIndex = getHndIndex();
EHblkDsc* enclosingHBtab = comp->ehGetDsc(thisHndIndex);

if (!enclosingHBtab->InFilterRegionBBRange(this))
{
return BasicBlockVisit::Continue;
}

assert(enclosingHBtab->HasFilter());

// Search the EH table for enclosed regions.
//
// All the enclosed regions will be lower numbered and
// immediately prior to and contiguous with the enclosing
// region in the EH tab.
unsigned index = thisHndIndex;

while (index > 0)
{
index--;
bool inTry;
unsigned enclosingIndex = comp->ehGetEnclosingRegionIndex(index, &inTry);
bool isEnclosed = false;

// To verify this is an enclosed region, search up
// through the enclosing regions until we find the
// region associated with the filter.
while (enclosingIndex != EHblkDsc::NO_ENCLOSING_INDEX)
{
if (enclosingIndex == thisHndIndex)
{
isEnclosed = true;
break;
}

enclosingIndex = comp->ehGetEnclosingRegionIndex(enclosingIndex, &inTry);
}

// If we found an enclosed region, check if the region
// is a try fault or try finally, and if so, invoke the callback
// for the enclosed region's handler.
if (isEnclosed)
{
if (inTry)
{
EHblkDsc* enclosedHBtab = comp->ehGetDsc(index);

if (enclosedHBtab->HasFinallyOrFaultHandler())
{
RETURN_ON_ABORT(func(enclosedHBtab->ebdHndBeg));
}
}
}
// Once we run across a non-enclosed region, we can stop searching.
else
{
break;
}
}

return BasicBlockVisit::Continue;
}

//------------------------------------------------------------------------------
// VisitEHSuccessors: Given a block inside a handler region, visit all handlers
// that control may flow to as part of EH.
Expand All @@ -329,41 +417,39 @@ inline EHblkDsc* Compiler::ehGetBlockHndDsc(BasicBlock* block)
// if a basic block BB1 occurs in a try block, we consider the first basic
// block BB2 of the corresponding handler to be an "EH successor" of BB1.
//
// TODO-BUG: This function currently does not take into account that filters
// are invoked in the first pass of EH, which means that enclosed finally
// blocks may be successors of filter blocks (as part of the second pass of
// EH). See fgGetHandlerLiveVars for code that does take this into account.
//
template <typename TFunc>
static BasicBlockVisit VisitEHSuccessors(Compiler* comp, BasicBlock* block, TFunc func)
{
EHblkDsc* eh = comp->ehGetBlockExnFlowDsc(block);
if (eh == nullptr)
if (!block->HasPotentialEHSuccs(comp))
{
return BasicBlockVisit::Continue;
}

while (true)
EHblkDsc* eh = comp->ehGetBlockExnFlowDsc(block);
if (eh != nullptr)
{
// If the original block whose EH successors we're iterating over
// is a BBJ_CALLFINALLY, that finally clause's first block
// will be yielded as a normal successor. Don't also yield as
// an exceptional successor.
BasicBlock* flowBlock = eh->ExFlowBlock();
if (!block->KindIs(BBJ_CALLFINALLY) || (block->bbJumpDest != flowBlock))
while (true)
{
RETURN_ON_ABORT(func(flowBlock));
}
// If the original block whose EH successors we're iterating over
// is a BBJ_CALLFINALLY, that finally clause's first block
// will be yielded as a normal successor. Don't also yield as
// an exceptional successor.
BasicBlock* flowBlock = eh->ExFlowBlock();
if (!block->KindIs(BBJ_CALLFINALLY) || (block->bbJumpDest != flowBlock))
{
RETURN_ON_ABORT(func(flowBlock));
}

if (eh->ebdEnclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX)
{
break;
}
if (eh->ebdEnclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX)
{
break;
}

eh = comp->ehGetDsc(eh->ebdEnclosingTryIndex);
eh = comp->ehGetDsc(eh->ebdEnclosingTryIndex);
}
}

return BasicBlockVisit::Continue;
return block->VisitEHSecondPassSuccs(comp, func);
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -567,6 +653,32 @@ BasicBlockVisit BasicBlock::VisitAllSuccs(Compiler* comp, TFunc func)

#undef RETURN_ON_ABORT

//------------------------------------------------------------------------------
// HasPotentialEHSuccs: Fast check to see if this block could have successors
// that control may flow to as part of EH.
//
// Arguments:
// comp - Compiler instance
//
// Returns:
// True if so.
//
inline bool BasicBlock::HasPotentialEHSuccs(Compiler* comp)
{
if (hasTryIndex())
{
return true;
}

EHblkDsc* hndDesc = comp->ehGetBlockHndDsc(this);
if (hndDesc == nullptr)
{
return false;
}

return hndDesc->InFilterRegionBBRange(this);
}

#if defined(FEATURE_EH_FUNCLETS)

/*****************************************************************************
Expand Down
24 changes: 23 additions & 1 deletion src/coreclr/jit/jiteh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,28 @@ bool Compiler::bbInFilterILRange(BasicBlock* blk)
return HBtab->InFilterRegionILRange(blk);
}

//------------------------------------------------------------------------
// bbInFilterBBRange:
// Check if this block is part of a filter.
//
// Arguments:
// blk - The block
//
// Return Value:
// True if the block is part of a filter clause. Otherwise false.
//
bool Compiler::bbInFilterBBRange(BasicBlock* blk)
{
EHblkDsc* HBtab = ehGetBlockHndDsc(blk);

if (HBtab == nullptr)
{
return false;
}

return HBtab->InFilterRegionBBRange(blk);
}

// Given a handler region, find the innermost try region that contains it.
// NOTE: handlerIndex is 1-based (0 means no handler).
unsigned short Compiler::bbFindInnermostTryRegionContainingHandlerRegion(unsigned handlerIndex)
Expand Down Expand Up @@ -434,7 +456,7 @@ bool Compiler::bbInTryRegions(unsigned regionIndex, BasicBlock* blk)
// Notes:
// For this check, a funclet is considered to be in the region it was
// extracted from.

//
bool Compiler::bbInExnFlowRegions(unsigned regionIndex, BasicBlock* blk)
{
assert(regionIndex < EHblkDsc::NO_ENCLOSING_INDEX);
Expand Down
Loading