Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/coreclr/jit/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,8 @@ void BasicBlock::dspFlags() const
{BBF_NEEDS_GCPOLL, "gcpoll"},
{BBF_HAS_VALUE_PROFILE, "val-prof"},
{BBF_MAY_HAVE_BOUNDS_CHECKS, "bnds-chk"},
{BBF_ASYNC_RESUMPTION, "resume"},
{BBF_ASYNC_RESUMPTION, "a-resume"},
{BBF_CATCH_RESUMPTION, "c-resume"},
{BBF_THROW_HELPER, "throw-hlpr"},
};

Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,8 @@ enum BasicBlockFlags : uint64_t
BBF_HAS_NEWARR = MAKE_BBFLAG(34), // BB contains 'new' of an array type.
BBF_MAY_HAVE_BOUNDS_CHECKS = MAKE_BBFLAG(35), // BB *likely* has a bounds check (after rangecheck phase).
BBF_ASYNC_RESUMPTION = MAKE_BBFLAG(36), // Block is a resumption block in an async method
BBF_THROW_HELPER = MAKE_BBFLAG(37), // Block is a call to a throw helper
BBF_CATCH_RESUMPTION = MAKE_BBFLAG(37), // Block is a resumption from a catch
BBF_THROW_HELPER = MAKE_BBFLAG(38), // Block is a call to a throw helper

// The following are sets of flags.

Expand Down
71 changes: 66 additions & 5 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2253,7 +2253,13 @@ class FlowGraphTryRegion
EHblkDsc* m_ehDsc;
BitVec m_blocks;

// Edges from blocks outside the try region to blocks inside the try region.
// This includes edges from any catch handler, and edges from some ancestor
// region to some descendant region.
jitstd::vector<FlowEdge*> m_entryEdges;

bool m_requiresRuntimeResumption;
bool m_hasSideEntry;

FlowGraphTryRegion(EHblkDsc* ehDsc, FlowGraphTryRegions* regions);

Expand All @@ -2262,11 +2268,21 @@ class FlowGraphTryRegion
m_requiresRuntimeResumption = true;
}

bool IsMutualProtectWith(FlowGraphTryRegion* other)
void SetHasSideEntry()
{
m_hasSideEntry = true;
}

bool IsMutualProtectWith(FlowGraphTryRegion* other) const
{
return EHblkDsc::ebdIsSameTry(this->m_ehDsc, other->m_ehDsc);
}

void AddEntryEdge(FlowEdge* edge)
{
m_entryEdges.push_back(edge);
}

public:

template<typename TFunc>
Expand All @@ -2279,6 +2295,11 @@ class FlowGraphTryRegion
return m_ehDsc->HasCatchHandler();
}

const jitstd::vector<FlowEdge*>& EntryEdges() const
{
return m_entryEdges;
}

// True if resumption from a catch in this or in an enclosed
// try region requires runtime support.
//
Expand All @@ -2287,6 +2308,22 @@ class FlowGraphTryRegion
return m_requiresRuntimeResumption;
}

// True if control can enter the try via some block other than the header block.
//
bool HasSideEntry() const
{
return m_hasSideEntry;
}

FlowGraphTryRegion* EnclosingRegion() const;

BasicBlock* GetHeaderBlock() const
{
return m_ehDsc->ebdTryBeg;
}

bool CanEnumerateInReversePostOrder() const;

#ifdef DEBUG
static void Dump(FlowGraphTryRegion* region);
#endif
Expand All @@ -2296,28 +2333,48 @@ class FlowGraphTryRegion
class FlowGraphTryRegions
{
private:
Compiler* m_compiler;
FlowGraphDfsTree* m_dfsTree;
// Collection of try regions that were found, indexed by EhID
jitstd::vector<FlowGraphTryRegion*> m_tryRegions;

FlowGraphTryRegions(FlowGraphDfsTree* dfs, unsigned numRegions);
FlowGraphTryRegions(Compiler* comp, FlowGraphDfsTree* dfs, unsigned numRegions);

unsigned m_numRegions;
unsigned m_numTryCatchRegions;
bool m_tryRegionsIncludeHandlerBlocks;
bool m_hasMultipleEntryTryRegions;
BitVecTraits m_traits;

void SetHasMultipleEntryTryRegions()
{
m_hasMultipleEntryTryRegions = true;
}

public:

static FlowGraphTryRegions* Build(Compiler* comp, FlowGraphDfsTree* dfs, bool includeHandlerBlocks = false);

BitVecTraits GetBlockBitVecTraits()
BitVecTraits* GetBlockBitVecTraits()
{
return m_dfsTree->PostOrderTraits();
return &m_traits;
}

unsigned GetBlockIndex(BasicBlock* block) const
{
if (m_dfsTree != nullptr)
{
return block->bbPostorderNum;
}
else
{
return block->bbNum;
}
}

Compiler* GetCompiler() const
{
return m_dfsTree->GetCompiler();
return m_compiler;
}

FlowGraphTryRegion* GetTryRegionByHeader(BasicBlock* block);
Expand All @@ -2329,6 +2386,8 @@ class FlowGraphTryRegions

bool TryRegionsIncludeHandlerBlocks() const { return m_tryRegionsIncludeHandlerBlocks; }

bool HasMultipleEntryTryRegions() const { return m_hasMultipleEntryTryRegions; }

#ifdef DEBUG
static void Dump(FlowGraphTryRegions* regions);
#endif
Expand Down Expand Up @@ -5347,6 +5406,8 @@ class Compiler
#ifdef TARGET_WASM
jitstd::vector<WasmInterval*>* fgWasmIntervals = nullptr;
BasicBlock** fgIndexToBlockMap = nullptr;
bool fgWasmHasCatchResumptions = false;
FlowGraphTryRegions* fgTryRegions = nullptr;
#endif

FlowGraphDfsTree* m_dfsTree = nullptr;
Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5399,9 +5399,10 @@ void Compiler::fgVisitBlocksInTryAwareLoopAwareRPO(FlowGraphDfsTree* dfsTre
template <typename TFunc>
BasicBlockVisit FlowGraphTryRegion::VisitTryRegionBlocksReversePostOrder(TFunc func)
{
BitVecTraits traits = m_regions->GetBlockBitVecTraits();
assert(CanEnumerateInReversePostOrder());
FlowGraphDfsTree* const dfsTree = m_regions->GetDfsTree();
bool result = BitVecOps::VisitBitsReverse(&traits, m_blocks, [=](unsigned index) {
BitVecTraits* traits = m_regions->GetBlockBitVecTraits();
bool result = BitVecOps::VisitBitsReverse(traits, m_blocks, [=](unsigned index) {
assert(index < dfsTree->GetPostOrderCount());
return func(dfsTree->GetPostOrder(index)) == BasicBlockVisit::Continue;
});
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2752,11 +2752,22 @@ bool BBPredsChecker::CheckEhTryDsc(BasicBlock* block, BasicBlock* blockPred, EHb
// Async resumptions are allowed to jump into try blocks at any point. They
// are introduced late enough that the invariant of single entry is no
// longer necessary.
// TODO: revoke for wasm after SCC
if (blockPred->HasFlag(BBF_ASYNC_RESUMPTION))
{
return true;
}

#if defined(TARGET_WASM)
// Catch resumptions are allowed to jump into try blocks at any point.
// They are transients during Wasm control flow restructuring.
// TODO: revoke after SCC
if (m_compiler->fgWasmHasCatchResumptions && blockPred->HasFlag(BBF_CATCH_RESUMPTION))
Comment on lines +2762 to +2765
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is meant by a "catch resumption"?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A catch resumption is in-method code to handle resuming execution once a catch funclet has completed. Other runtimes can just set the IP in the catching frame, but in Wasm we can't do that.

{
return true;
}
#endif // defined(TARGET_WASM)

JITDUMP("Jump into the middle of try region: " FMT_BB " branches to " FMT_BB "\n", blockPred->bbNum, block->bbNum);
assert(!"Jump into middle of try region");
return false;
Expand Down
23 changes: 16 additions & 7 deletions src/coreclr/jit/fgwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1053,9 +1053,9 @@ PhaseStatus Compiler::fgWasmControlFlow()
//
// We don't install our DFS tree as "the" DFS tree as it is non-standard.
//
FgWasm fgWasm(this);
bool hasBlocksOnlyReachableViaEH = false;
FlowGraphDfsTree* dfsTree = fgWasm.WasmDfs(hasBlocksOnlyReachableViaEH);
FgWasm fgWasm(this);
bool hasBlocksOnlyReachableViaEH = false;
FlowGraphDfsTree* const dfsTree = fgWasm.WasmDfs(hasBlocksOnlyReachableViaEH);

if (hasBlocksOnlyReachableViaEH)
{
Expand All @@ -1067,17 +1067,25 @@ PhaseStatus Compiler::fgWasmControlFlow()
}

assert(dfsTree->IsForWasm());
FlowGraphNaturalLoops* loops = FlowGraphNaturalLoops::Find(dfsTree);
FlowGraphNaturalLoops* const loops = FlowGraphNaturalLoops::Find(dfsTree);

// We should have transformed these away earlier
//
assert(loops->ImproperLoopHeaders() == 0);

// Create descriptions of the try regions that can be enumerated in RPO
//
FlowGraphTryRegions* tryRegions = FlowGraphTryRegions::Build(this, dfsTree);
FlowGraphTryRegions* const tryRegions = FlowGraphTryRegions::Build(this, dfsTree);
JITDUMPEXEC(FlowGraphTryRegions::Dump(tryRegions));

// We cannot handle multiple entry try regions yet.
//
if (tryRegions->HasMultipleEntryTryRegions())
{
JITDUMP("\nThere are multiple entry try regions\n");
NYI_WASM("Multiple entry try regions");
}

// Our interval ends are at the starts of blocks, so we need a block that
// comes after all existing blocks. So allocate one extra slot.
//
Expand Down Expand Up @@ -2049,10 +2057,10 @@ PhaseStatus Compiler::fgWasmEhFlow()

if (commonEnclosingTryIndex == innermostDispatchingTryIndex)
{
JITDUMP("Continuation " FMT_BB " is within dispatching try EH#%02u; cannot handle this case yet\n",
JITDUMP("Continuation " FMT_BB " is within dispatching try EH#%02u, marking as catch resumption\n",
continuationBlock->bbNum, innermostDispatchingTryIndex);

NYI_WASM("WasmEHFlow: continuation is within dispatching try");
fgWasmHasCatchResumptions = true;
}

ArrayStack<BasicBlock*>* catchRetBlocks = getCatchRetBlocksForTryRegion(innermostDispatchingTryIndex);
Expand Down Expand Up @@ -2319,6 +2327,7 @@ void Compiler::fgWasmEhTransformTry(ArrayStack<BasicBlock*>* catchRetBlocks,
//
BBswtDesc* const swtDesc = new (this, CMK_BasicBlock) BBswtDesc(succs, succCount, cases, caseCount, true);
switchBlock->SetSwitch(swtDesc);
switchBlock->SetFlags(BBF_CATCH_RESUMPTION);

// Build the IR for the switch
//
Expand Down
Loading
Loading