diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index bbeb1390ef2ccf..4851dd5a90e31f 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -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"}, }; diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 1ab4a449912f9e..40d6e0f1f62bea 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -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. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index a0c79914d7b328..3eb54e4643516f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -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 m_entryEdges; + bool m_requiresRuntimeResumption; + bool m_hasSideEntry; FlowGraphTryRegion(EHblkDsc* ehDsc, FlowGraphTryRegions* regions); @@ -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 @@ -2279,6 +2295,11 @@ class FlowGraphTryRegion return m_ehDsc->HasCatchHandler(); } + const jitstd::vector& EntryEdges() const + { + return m_entryEdges; + } + // True if resumption from a catch in this or in an enclosed // try region requires runtime support. // @@ -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 @@ -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 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); @@ -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 @@ -5347,6 +5406,8 @@ class Compiler #ifdef TARGET_WASM jitstd::vector* fgWasmIntervals = nullptr; BasicBlock** fgIndexToBlockMap = nullptr; + bool fgWasmHasCatchResumptions = false; + FlowGraphTryRegions* fgTryRegions = nullptr; #endif FlowGraphDfsTree* m_dfsTree = nullptr; diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index efb1238c225591..22a49071fbbf10 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -5399,9 +5399,10 @@ void Compiler::fgVisitBlocksInTryAwareLoopAwareRPO(FlowGraphDfsTree* dfsTre template 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; }); diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 275d0775045cab..caa55a6590a3c0 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -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)) + { + 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; diff --git a/src/coreclr/jit/fgwasm.cpp b/src/coreclr/jit/fgwasm.cpp index aa8c89fd522ab5..95e36e7d376ae8 100644 --- a/src/coreclr/jit/fgwasm.cpp +++ b/src/coreclr/jit/fgwasm.cpp @@ -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) { @@ -1067,7 +1067,7 @@ PhaseStatus Compiler::fgWasmControlFlow() } assert(dfsTree->IsForWasm()); - FlowGraphNaturalLoops* loops = FlowGraphNaturalLoops::Find(dfsTree); + FlowGraphNaturalLoops* const loops = FlowGraphNaturalLoops::Find(dfsTree); // We should have transformed these away earlier // @@ -1075,9 +1075,17 @@ PhaseStatus Compiler::fgWasmControlFlow() // 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. // @@ -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* catchRetBlocks = getCatchRetBlocksForTryRegion(innermostDispatchingTryIndex); @@ -2319,6 +2327,7 @@ void Compiler::fgWasmEhTransformTry(ArrayStack* 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 // diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index f8c7c1dbb7a3e5..98d9431a8ff8a1 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -7548,15 +7548,19 @@ void BlockReachabilitySets::Dump() // FlowGraphTryRegions::FlowGraphTryRegions: Constructor for FlowGraphTryRegions. // // Arguments: +// // dfsTree -- DFS tree for the flow graph // numRegions -- Number of try regions in the method // -FlowGraphTryRegions::FlowGraphTryRegions(FlowGraphDfsTree* dfsTree, unsigned numRegions) - : m_dfsTree(dfsTree) - , m_tryRegions(numRegions, nullptr, dfsTree->GetCompiler()->getAllocator(CMK_BasicBlock)) +FlowGraphTryRegions::FlowGraphTryRegions(Compiler* comp, FlowGraphDfsTree* dfsTree, unsigned numRegions) + : m_compiler(comp) + , m_dfsTree(dfsTree) + , m_tryRegions(numRegions, nullptr, comp->getAllocator(CMK_BasicBlock)) , m_numRegions(0) , m_numTryCatchRegions(0) , m_tryRegionsIncludeHandlerBlocks(false) + , m_hasMultipleEntryTryRegions(false) + , m_traits((dfsTree == nullptr) ? comp->fgBBNumMax + 1 : dfsTree->GetPostOrderCount(), comp) { } @@ -7599,10 +7603,12 @@ FlowGraphTryRegion::FlowGraphTryRegion(EHblkDsc* ehDsc, FlowGraphTryRegions* reg , m_parent(nullptr) , m_ehDsc(ehDsc) , m_blocks(BitVecOps::UninitVal()) + , m_entryEdges(regions->GetCompiler()->getAllocator(CMK_BasicBlock)) , m_requiresRuntimeResumption(false) + , m_hasSideEntry(false) { - BitVecTraits traits = regions->GetBlockBitVecTraits(); - m_blocks = BitVecOps::MakeEmpty(&traits); + BitVecTraits* const traits = regions->GetBlockBitVecTraits(); + m_blocks = BitVecOps::MakeEmpty(traits); } //------------------------------------------------------------------------ @@ -7610,7 +7616,7 @@ FlowGraphTryRegion::FlowGraphTryRegion(EHblkDsc* ehDsc, FlowGraphTryRegions* reg // // Arguments: // comp -- Compiler instance -// dfsTree -- DFS tree for the flow graph +// dfsTree -- DFS tree for the flow graph (optional, can be nullptr) // includeHandlerBlocks -- include blocks in handlers inside the try // // Returns: @@ -7622,7 +7628,7 @@ FlowGraphTryRegions* FlowGraphTryRegions::Build(Compiler* comp, FlowGraphDfsTree // collection if we've deleted some EH regions. // unsigned const numTryRegions = comp->compEHID; - FlowGraphTryRegions* regions = new (comp, CMK_BasicBlock) FlowGraphTryRegions(dfsTree, numTryRegions); + FlowGraphTryRegions* regions = new (comp, CMK_BasicBlock) FlowGraphTryRegions(comp, dfsTree, numTryRegions); assert(numTryRegions >= comp->compHndBBtabCount); regions->m_tryRegionsIncludeHandlerBlocks = includeHandlerBlocks; @@ -7657,9 +7663,7 @@ FlowGraphTryRegions* FlowGraphTryRegions::Build(Compiler* comp, FlowGraphDfsTree } } - // Collect the postorder numbers of each block in each region - // - BitVecTraits traits = regions->m_dfsTree->PostOrderTraits(); + BitVecTraits* const traits = regions->GetBlockBitVecTraits(); for (BasicBlock* block : comp->Blocks()) { @@ -7681,8 +7685,62 @@ FlowGraphTryRegions* FlowGraphTryRegions::Build(Compiler* comp, FlowGraphDfsTree // while (region != nullptr) { - BitVecOps::AddElemD(&traits, region->m_blocks, block->bbPostorderNum); + BitVecOps::AddElemD(traits, region->m_blocks, regions->GetBlockIndex(block)); + + // Enumerate block's pred edges to find the try entry edges. + // + for (FlowEdge* const edge : block->PredEdges()) + { + BasicBlock* const predBlock = edge->getSourceBlock(); + + // Disregard catchret edges, these are modelled by + // catch resumptions now. + // + if (predBlock->KindIs(BBJ_EHCATCHRET)) + { + continue; + } + + // Disregard edges from within the try region + // + if (comp->bbInTryRegions(tryIndex, predBlock)) + { + continue; + } + + // "Normal" entry edges. + // + if (block == dsc->ebdTryBeg) + { + region->AddEntryEdge(edge); + continue; + } + + // Async resumption and catch resumption entry edges + // + if (predBlock->HasAnyFlag(BBF_ASYNC_RESUMPTION | BBF_CATCH_RESUMPTION)) + { + JITDUMP("Found %s resumption edge from " FMT_BB " to " FMT_BB "\n", + predBlock->HasFlag(BBF_ASYNC_RESUMPTION) ? "async" : "catch", predBlock->bbNum, + block->bbNum); + + region->AddEntryEdge(edge); + region->SetHasSideEntry(); + regions->SetHasMultipleEntryTryRegions(); + continue; + } + + JITDUMP("Unexpected try region entry edge from " FMT_BB " to " FMT_BB "\n", predBlock->bbNum, + block->bbNum); + assert(!"Unexpected try region entry edge"); + } + region = region->m_parent; + + if (region != nullptr) + { + tryIndex = comp->ehGetIndex(region->m_ehDsc); + } } } else @@ -7711,13 +7769,45 @@ FlowGraphTryRegions* FlowGraphTryRegions::Build(Compiler* comp, FlowGraphDfsTree //------------------------------------------------------------------------ // FlowGraphTryRegion::NumBlocks: Return the number of blocks in the try region. // -// Arguments: -// region -- region of interest +// Returns: +// Number of blocks in the region at the time of FlowGraphTryRegions::Build +// Includes blocks in enclosed try regions. Includes blocks in enclosed +// handler regions, if FlowGraphTryRegions::Build was called with includeHandlerBlocks == true. // unsigned FlowGraphTryRegion::NumBlocks() const { - BitVecTraits traits = m_regions->GetBlockBitVecTraits(); - return BitVecOps::Count(&traits, m_blocks); + BitVecTraits* const traits = m_regions->GetBlockBitVecTraits(); + return BitVecOps::Count(traits, m_blocks); +} + +//------------------------------------------------------------------------ +// FlowGraphTryRegion::EnclosingRegion: Return the enclosing non-mutual-protect +// region, or nullptr. +// +// Returns: +// Enclosing non-mutual-protect region, or nullptr if there is none. +// Note any enclosing region will have a distinct header block. +// +FlowGraphTryRegion* FlowGraphTryRegion::EnclosingRegion() const +{ + FlowGraphTryRegion* ancestor = m_parent; + while (ancestor != nullptr && IsMutualProtectWith(ancestor)) + { + ancestor = ancestor->m_parent; + } + return ancestor; +} + +//------------------------------------------------------------------------ +// FlowGraphTryRegion::CanEnumerateInReversePostOrder: check if the +// try region can be enumerated in reverse post order +// +// Returns: +// True if so. +// +bool FlowGraphTryRegion::CanEnumerateInReversePostOrder() const +{ + return m_regions->GetDfsTree() != nullptr; } #ifdef DEBUG @@ -7750,10 +7840,42 @@ void FlowGraphTryRegion::Dump(FlowGraphTryRegion* region) printf(" [outermost]:"); } - region->VisitTryRegionBlocksReversePostOrder([](BasicBlock* block) { - printf(" " FMT_BB, block->bbNum); - return BasicBlockVisit::Continue; - }); + if (region->CanEnumerateInReversePostOrder()) + { + printf(" [rpo]:"); + + region->VisitTryRegionBlocksReversePostOrder([](BasicBlock* block) { + printf(" " FMT_BB, block->bbNum); + return BasicBlockVisit::Continue; + }); + } + else + { + printf(" [bbNum]:"); + + BitVecOps::Iter iterator(regions->GetBlockBitVecTraits(), region->m_blocks); + unsigned int index; + while (iterator.NextElem(&index)) + { + printf(" " FMT_BB, index); + } + } + + printf(" [entries]: "); + for (FlowEdge* const edge : region->EntryEdges()) + { + BasicBlock* const predBlock = edge->getSourceBlock(); + BasicBlock* const succBlock = edge->getDestinationBlock(); + printf(" " FMT_BB "->" FMT_BB, predBlock->bbNum, succBlock->bbNum); + if (predBlock->HasFlag(BBF_ASYNC_RESUMPTION)) + { + printf("[async]"); + } + if (predBlock->HasFlag(BBF_CATCH_RESUMPTION)) + { + printf("[catch]"); + } + } } //------------------------------------------------------------------------