From f4021904d0654df8f6c0a36ecc0e2c48eafbb49d Mon Sep 17 00:00:00 2001 From: Bruce Forstall Date: Fri, 7 Apr 2023 14:56:17 -0700 Subject: [PATCH] Introduce BBJ_EHFAULTRET (#84467) The IL `endfinally` and `endfault` instructions are aliases, imported in the JIT IR as `BBJ_EHFINALLYRET`. Introduce `BBJ_EHFAULTRET` for cases within `fault` clauses. This simplifies some code, and makes it more clear when writing code that `fault` clauses need to be considered, separately from `finally` clauses. To do this, after importing EH clauses, convert any `BBJ_EHFINALLYRET` as necessary. Also, try/finally cloning, which (sometimes) converts `finally` clauses to `fault` clauses, needs to update the corresponding `BBJ_EHFINALLYRET`. When creating new try/fault clauses for synchronized functions, use `BBJ_EHFAULTRET` now. Fixes #84307 --- src/coreclr/jit/block.cpp | 34 +++++++------------------ src/coreclr/jit/block.h | 24 ++++++++++++++++-- src/coreclr/jit/codegencommon.cpp | 3 ++- src/coreclr/jit/codegenlinear.cpp | 3 +++ src/coreclr/jit/codegenxarch.cpp | 2 +- src/coreclr/jit/fgbasic.cpp | 38 +++++++++++++++++++++------- src/coreclr/jit/fgdiagnostic.cpp | 8 +++++- src/coreclr/jit/fgehopt.cpp | 25 ++++++++++++++---- src/coreclr/jit/fgflow.cpp | 46 ++++++++++++++++------------------ src/coreclr/jit/fgopt.cpp | 3 +++ src/coreclr/jit/fgprofile.cpp | 5 ++-- src/coreclr/jit/fgprofilesynthesis.cpp | 3 +++ src/coreclr/jit/flowgraph.cpp | 2 +- src/coreclr/jit/importer.cpp | 1 + src/coreclr/jit/jiteh.cpp | 43 +++++++++++++++++++++++++++++++ src/coreclr/jit/liveness.cpp | 2 ++ src/coreclr/jit/lsrabuild.cpp | 2 +- src/coreclr/jit/optimizer.cpp | 8 ++++-- 18 files changed, 177 insertions(+), 75 deletions(-) diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 927367f..ce4415d 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -609,6 +609,10 @@ void BasicBlock::dspJumpKind() printf(" (finret)"); break; + case BBJ_EHFAULTRET: + printf(" (falret)"); + break; + case BBJ_EHFILTERRET: printf(" (fltret)"); break; @@ -1023,6 +1027,7 @@ bool BasicBlock::bbFallsThrough() const { case BBJ_THROW: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: case BBJ_EHCATCHRET: case BBJ_RETURN: @@ -1060,6 +1065,7 @@ unsigned BasicBlock::NumSucc() const case BBJ_THROW: case BBJ_RETURN: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: return 0; @@ -1147,30 +1153,17 @@ unsigned BasicBlock::NumSucc(Compiler* comp) { case BBJ_THROW: case BBJ_RETURN: + case BBJ_EHFAULTRET: return 0; case BBJ_EHFINALLYRET: - { // We may call this method before we realize we have invalid IL. Tolerate. // if (!hasHndIndex()) { return 0; } - - // The first block of the handler is labelled with the catch type. - BasicBlock* hndBeg = comp->fgFirstBlockOfHandler(this); - if (hndBeg->bbCatchTyp == BBCT_FINALLY) - { - return comp->fgNSuccsOfFinallyRet(this); - } - else - { - assert(hndBeg->bbCatchTyp == BBCT_FAULT); // We can only BBJ_EHFINALLYRET from FINALLY and FAULT. - // A FAULT block has no successors. - return 0; - } - } + return comp->fgNSuccsOfFinallyRet(this); case BBJ_CALLFINALLY: case BBJ_ALWAYS: @@ -1615,16 +1608,7 @@ bool BasicBlock::hasEHBoundaryIn() const // bool BasicBlock::hasEHBoundaryOut() const { - bool returnVal = false; - if (bbJumpKind == BBJ_EHFILTERRET) - { - returnVal = true; - } - - if (bbJumpKind == BBJ_EHFINALLYRET) - { - returnVal = true; - } + bool returnVal = KindIs(BBJ_EHFILTERRET, BBJ_EHFINALLYRET, BBJ_EHFAULTRET); #if FEATURE_EH_FUNCLETS if (bbJumpKind == BBJ_EHCATCHRET) diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 27a1d72..1b7e8d6 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -59,7 +59,8 @@ typedef BitVec_ValRet_T ASSERT_VALRET_TP; enum BBjumpKinds : BYTE { - BBJ_EHFINALLYRET,// block ends with 'endfinally' (for finally or fault) + BBJ_EHFINALLYRET,// block ends with 'endfinally' (for finally) + BBJ_EHFAULTRET, // block ends with 'endfinally' (IL alias for 'endfault') (for fault) BBJ_EHFILTERRET, // block ends with 'endfilter' BBJ_EHCATCHRET, // block ends with a leave out of a catch (only #if defined(FEATURE_EH_FUNCLETS)) BBJ_THROW, // block ends with 'throw' @@ -74,6 +75,24 @@ enum BBjumpKinds : BYTE BBJ_COUNT }; +#ifdef DEBUG +const char* const BBjumpKindNames[] = { + "BBJ_EHFINALLYRET", + "BBJ_EHFAULTRET", + "BBJ_EHFILTERRET", + "BBJ_EHCATCHRET", + "BBJ_THROW", + "BBJ_RETURN", + "BBJ_NONE", + "BBJ_ALWAYS", + "BBJ_LEAVE", + "BBJ_CALLFINALLY", + "BBJ_COND", + "BBJ_SWITCH", + "BBJ_COUNT" +}; +#endif // DEBUG + // clang-format on struct GenTree; @@ -829,7 +848,7 @@ struct BasicBlock : private LIR::Range // GetSucc() without a Compiler*. // // The behavior of NumSucc()/GetSucc() is different when passed a Compiler* for blocks that end in: - // (1) BBJ_EHFINALLYRET (a return from a finally or fault block) + // (1) BBJ_EHFINALLYRET (a return from a finally block) // (2) BBJ_EHFILTERRET (a return from EH filter block) // (3) BBJ_SWITCH // @@ -1656,6 +1675,7 @@ inline BasicBlock::BBSuccList::BBSuccList(const BasicBlock* block) case BBJ_THROW: case BBJ_RETURN: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: // We don't need m_succs. m_begin = nullptr; diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 8099db8..fb6a1c8 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -412,6 +412,7 @@ void CodeGen::genMarkLabelsForCodegen() break; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: case BBJ_RETURN: case BBJ_THROW: @@ -7857,7 +7858,7 @@ void CodeGen::genReturn(GenTree* treeNode) #else // !FEATURE_EH_FUNCLETS // Don't generate stack checks for x86 finally/filter EH returns: these are not invoked // with the same SP as the main function. See also CodeGen::genEHFinallyOrFilterRet(). - if ((compiler->compCurBB->bbJumpKind == BBJ_EHFINALLYRET) || (compiler->compCurBB->bbJumpKind == BBJ_EHFILTERRET)) + if (compiler->compCurBB->KindIs(BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET)) { doStackPointerCheck = false; } diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 498e5ca..d3dd18d 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -629,6 +629,7 @@ void CodeGen::genCodeForBBlist() case BBJ_RETURN: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: // These are the "epilog follows" case, handled in the emitter. @@ -715,6 +716,7 @@ void CodeGen::genCodeForBBlist() FALLTHROUGH; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: genReserveFuncletEpilog(block); break; @@ -726,6 +728,7 @@ void CodeGen::genCodeForBBlist() break; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: genEHFinallyOrFilterRet(block); break; diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 2c6a493..7dee61b 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -355,7 +355,7 @@ void CodeGen::genEHFinallyOrFilterRet(BasicBlock* block) assert(block->lastNode() != nullptr); assert(block->lastNode()->OperGet() == GT_RETFILT); - if (block->bbJumpKind == BBJ_EHFINALLYRET) + if (block->KindIs(BBJ_EHFINALLYRET, BBJ_EHFAULTRET)) { assert(block->lastNode()->AsOp()->gtOp1 == nullptr); // op1 == nullptr means endfinally diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 868c02d..8176fa6 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -2802,6 +2802,7 @@ void Compiler::fgLinkBasicBlocks() // We do it in impFixPredLists. break; + case BBJ_EHFAULTRET: case BBJ_THROW: case BBJ_RETURN: break; @@ -3064,6 +3065,7 @@ unsigned Compiler::fgMakeBasicBlocks(const BYTE* codeAddr, IL_OFFSET codeSize, F break; case CEE_ENDFINALLY: + // Start with BBJ_EHFINALLYRET; change to BBJ_EHFAULTRET later if it's in a 'fault' clause. jmpKind = BBJ_EHFINALLYRET; break; @@ -3782,6 +3784,13 @@ void Compiler::fgFindBasicBlocks() if (!block->hasHndIndex()) { block->setHndIndex(XTnum); + + // If the most nested EH handler region of this block is a 'fault' region, then change any + // BBJ_EHFINALLYRET that were imported to BBJ_EHFAULTRET. + if ((hndBegBB->bbCatchTyp == BBCT_FAULT) && block->KindIs(BBJ_EHFINALLYRET)) + { + block->bbJumpKind = BBJ_EHFAULTRET; + } } // All blocks in a catch handler or filter are rarely run, except the entry @@ -4060,6 +4069,7 @@ void Compiler::fgCheckBasicBlockControlFlow() break; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: if (!blk->hasHndIndex()) // must be part of a handler @@ -4077,17 +4087,28 @@ void Compiler::fgCheckBasicBlockControlFlow() BADCODE("Unexpected endfilter"); } } - // endfinally allowed only in a finally/fault block - else if (!HBtab->HasFinallyOrFaultHandler()) + else if (blk->bbJumpKind == BBJ_EHFILTERRET) { - BADCODE("Unexpected endfinally"); + // endfinally allowed only in a finally block + if (!HBtab->HasFinallyHandler()) + { + BADCODE("Unexpected endfinally"); + } + } + else if (blk->bbJumpKind == BBJ_EHFAULTRET) + { + // 'endfault' (alias of IL 'endfinally') allowed only in a fault block + if (!HBtab->HasFaultHandler()) + { + BADCODE("Unexpected endfault"); + } } // The handler block should be the innermost block // Exception blocks are listed, innermost first. if (blk->hasTryIndex() && (blk->getTryIndex() < blk->getHndIndex())) { - BADCODE("endfinally / endfilter in nested try block"); + BADCODE("endfinally / endfault / endfilter in nested try block"); } break; @@ -6298,8 +6319,7 @@ BasicBlock* Compiler::fgFindInsertPoint(unsigned regionIndex, #endif // DEBUG JITDUMP("fgFindInsertPoint(regionIndex=%u, putInTryRegion=%s, startBlk=" FMT_BB ", endBlk=" FMT_BB - ", nearBlk=" FMT_BB ", " - "jumpBlk=" FMT_BB ", runRarely=%s)\n", + ", nearBlk=" FMT_BB ", jumpBlk=" FMT_BB ", runRarely=%s)\n", regionIndex, dspBool(putInTryRegion), startBlk->bbNum, (endBlk == nullptr) ? 0 : endBlk->bbNum, (nearBlk == nullptr) ? 0 : nearBlk->bbNum, (jumpBlk == nullptr) ? 0 : jumpBlk->bbNum, dspBool(runRarely)); @@ -6697,10 +6717,10 @@ _FoundAfterBlk:; /* We have decided to insert the block after 'afterBlk'. */ noway_assert(afterBlk != nullptr); - JITDUMP("fgNewBBinRegion(jumpKind=%u, tryIndex=%u, hndIndex=%u, putInFilter=%s, runRarely=%s, insertAtEnd=%s): " + JITDUMP("fgNewBBinRegion(jumpKind=%s, tryIndex=%u, hndIndex=%u, putInFilter=%s, runRarely=%s, insertAtEnd=%s): " "inserting after " FMT_BB "\n", - jumpKind, tryIndex, hndIndex, dspBool(putInFilter), dspBool(runRarely), dspBool(insertAtEnd), - afterBlk->bbNum); + BBjumpKindNames[jumpKind], tryIndex, hndIndex, dspBool(putInFilter), dspBool(runRarely), + dspBool(insertAtEnd), afterBlk->bbNum); return fgNewBBinRegionWorker(jumpKind, afterBlk, regionIndex, putInTryRegion); } diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 8a36ae8..4b7e4d8 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -105,6 +105,7 @@ void Compiler::fgDebugCheckUpdate() { case BBJ_CALLFINALLY: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: case BBJ_RETURN: /* for BBJ_ALWAYS is probably just a GOTO, but will have to be treated */ @@ -1994,6 +1995,10 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 * printf("%*s (finret)", maxBlockNumWidth - 2, ""); break; + case BBJ_EHFAULTRET: + printf("%*s (falret)", maxBlockNumWidth - 2, ""); + break; + case BBJ_EHFILTERRET: printf("%*s (fltret)", maxBlockNumWidth - 2, ""); break; @@ -2633,9 +2638,10 @@ bool BBPredsChecker::CheckJump(BasicBlock* blockPred, BasicBlock* block) assert(CheckEHFinallyRet(blockPred, block)); return true; + case BBJ_EHFAULTRET: case BBJ_THROW: case BBJ_RETURN: - assert(!"THROW and RETURN block cannot be in the predecessor list!"); + assert(!"EHFAULTRET, THROW, and RETURN block cannot be in the predecessor list!"); break; case BBJ_SWITCH: diff --git a/src/coreclr/jit/fgehopt.cpp b/src/coreclr/jit/fgehopt.cpp index 5e1dd11..0d6fedf 100644 --- a/src/coreclr/jit/fgehopt.cpp +++ b/src/coreclr/jit/fgehopt.cpp @@ -1035,7 +1035,7 @@ PhaseStatus Compiler::fgCloneFinally() // statement after the finally, so they can share the clone. // // Clone the finally body, and splice it into the flow graph - // within in the parent region of the try. + // within the parent region of the try. // const unsigned finallyTryIndex = firstBlock->bbTryIndex; BasicBlock* insertAfter = nullptr; @@ -1048,6 +1048,10 @@ PhaseStatus Compiler::fgCloneFinally() { BasicBlock* newBlock; + // Avoid asserts when `fgNewBBinRegion` verifies the handler table, by mapping any cloned finally + // return blocks to BBJ_ALWAYS (which we would do below if we didn't do it here). + BBjumpKinds bbNewJumpKind = (block->bbJumpKind == BBJ_EHFINALLYRET) ? BBJ_ALWAYS : block->bbJumpKind; + if (block == firstBlock) { // Put first cloned finally block into the appropriate @@ -1055,7 +1059,7 @@ PhaseStatus Compiler::fgCloneFinally() // callfinallys, depending on the EH implementation. const unsigned hndIndex = 0; BasicBlock* const nearBlk = cloneInsertAfter; - newBlock = fgNewBBinRegion(block->bbJumpKind, finallyTryIndex, hndIndex, nearBlk); + newBlock = fgNewBBinRegion(bbNewJumpKind, finallyTryIndex, hndIndex, nearBlk); // If the clone ends up just after the finally, adjust // the stopping point for finally traversal. @@ -1069,7 +1073,7 @@ PhaseStatus Compiler::fgCloneFinally() { // Put subsequent blocks in the same region... const bool extendRegion = true; - newBlock = fgNewBBafter(block->bbJumpKind, insertAfter, extendRegion); + newBlock = fgNewBBafter(bbNewJumpKind, insertAfter, extendRegion); } cloneBBCount++; @@ -1121,7 +1125,7 @@ PhaseStatus Compiler::fgCloneFinally() JITDUMP("Cloned finally blocks are: " FMT_BB " ... " FMT_BB "\n", blockMap[firstBlock]->bbNum, blockMap[lastBlock]->bbNum); - // Redirect redirect any branches within the newly-cloned + // Redirect any branches within the newly-cloned // finally, and any finally returns to jump to the return // point. for (BasicBlock* block = firstBlock; block != nextBlock; block = block->bbNext) @@ -1134,7 +1138,7 @@ PhaseStatus Compiler::fgCloneFinally() GenTree* finallyRetExpr = finallyRet->GetRootNode(); assert(finallyRetExpr->gtOper == GT_RETFILT); fgRemoveStmt(newBlock, finallyRet); - newBlock->bbJumpKind = BBJ_ALWAYS; + assert(newBlock->bbJumpKind == BBJ_ALWAYS); // we mapped this above already newBlock->bbJumpDest = normalCallFinallyReturn; fgAddRefPred(normalCallFinallyReturn, newBlock); @@ -1231,6 +1235,17 @@ PhaseStatus Compiler::fgCloneFinally() JITDUMP("All callfinallys retargeted; changing finally to fault.\n"); HBtab->ebdHandlerType = EH_HANDLER_FAULT_WAS_FINALLY; firstBlock->bbCatchTyp = BBCT_FAULT; + + // Change all BBJ_EHFINALLYRET to BBJ_EHFAULTRET in the now-fault region. + BasicBlock* const hndBegIter = HBtab->ebdHndBeg; + BasicBlock* const hndEndIter = HBtab->ebdHndLast->bbNext; + for (BasicBlock* block = hndBegIter; block != hndEndIter; block = block->bbNext) + { + if (block->bbJumpKind == BBJ_EHFINALLYRET) + { + block->bbJumpKind = BBJ_EHFAULTRET; + } + } } else { diff --git a/src/coreclr/jit/fgflow.cpp b/src/coreclr/jit/fgflow.cpp index 5f265ea..040cd37 100644 --- a/src/coreclr/jit/fgflow.cpp +++ b/src/coreclr/jit/fgflow.cpp @@ -416,6 +416,7 @@ void Compiler::fgRemoveBlockAsPred(BasicBlock* block) } break; + case BBJ_EHFAULTRET: case BBJ_THROW: case BBJ_RETURN: break; @@ -451,46 +452,41 @@ BasicBlock* Compiler::fgSuccOfFinallyRet(BasicBlock* block, unsigned i) void Compiler::fgSuccOfFinallyRetWork(BasicBlock* block, unsigned i, BasicBlock** bres, unsigned* nres) { - assert(block->hasHndIndex()); // Otherwise, endfinally outside a finally/fault block? + assert(block->hasHndIndex()); // Otherwise, endfinally outside a finally block? unsigned hndIndex = block->getHndIndex(); EHblkDsc* ehDsc = ehGetDsc(hndIndex); - assert(ehDsc->HasFinallyOrFaultHandler()); // Otherwise, endfinally outside a finally/fault block. + assert(ehDsc->HasFinallyHandler()); // Otherwise, endfinally outside a finally block. *bres = nullptr; unsigned succNum = 0; - if (ehDsc->HasFinallyHandler()) - { - BasicBlock* begBlk; - BasicBlock* endBlk; - ehGetCallFinallyBlockRange(hndIndex, &begBlk, &endBlk); + BasicBlock* begBlk; + BasicBlock* endBlk; + ehGetCallFinallyBlockRange(hndIndex, &begBlk, &endBlk); - BasicBlock* finBeg = ehDsc->ebdHndBeg; + BasicBlock* finBeg = ehDsc->ebdHndBeg; - for (BasicBlock* bcall = begBlk; bcall != endBlk; bcall = bcall->bbNext) + for (BasicBlock* bcall = begBlk; bcall != endBlk; bcall = bcall->bbNext) + { + if (bcall->bbJumpKind != BBJ_CALLFINALLY || bcall->bbJumpDest != finBeg) { - if (bcall->bbJumpKind != BBJ_CALLFINALLY || bcall->bbJumpDest != finBeg) - { - continue; - } + continue; + } - assert(bcall->isBBCallAlwaysPair()); + assert(bcall->isBBCallAlwaysPair()); - if (succNum == i) - { - *bres = bcall->bbNext; - return; - } - succNum++; + if (succNum == i) + { + *bres = bcall->bbNext; + return; } + succNum++; } - assert(i == ~0u || ehDsc->HasFaultHandler()); // Should reach here only for fault blocks. - if (i == ~0u) - { - *nres = succNum; - } + + assert(i == ~0u); + *nres = succNum; } Compiler::SwitchUniqueSuccSet Compiler::GetDescriptorForSwitch(BasicBlock* switchBlk) diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 84e6ce0..aae721e 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -2387,6 +2387,7 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext) } break; + case BBJ_EHFAULTRET: case BBJ_THROW: case BBJ_RETURN: /* no jumps or fall through blocks to set here */ @@ -2908,6 +2909,7 @@ bool Compiler::fgOptimizeEmptyBlock(BasicBlock* block) case BBJ_RETURN: case BBJ_EHCATCHRET: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: /* leave them as is */ @@ -6605,6 +6607,7 @@ unsigned Compiler::fgGetCodeEstimate(BasicBlock* block) costSz = 1; // We place a int3 after the code for a throw block break; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: costSz = 1; break; diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index d4f5252..cac016a 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -945,7 +945,6 @@ void Compiler::WalkSpanningTree(SpanningTreeVisitor* visitor) __fallthrough; case BBJ_RETURN: - { // Pseudo-edge back to method entry. // @@ -960,6 +959,7 @@ void Compiler::WalkSpanningTree(SpanningTreeVisitor* visitor) break; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHCATCHRET: case BBJ_EHFILTERRET: case BBJ_LEAVE: @@ -4581,6 +4581,7 @@ PhaseStatus Compiler::fgComputeEdgeWeights() case BBJ_COND: case BBJ_SWITCH: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: if (edge->edgeWeightMax() > bSrc->bbWeight) { @@ -5279,7 +5280,7 @@ bool Compiler::fgDebugCheckOutgoingProfileData(BasicBlock* block, ProfileChecks // const unsigned numSuccs = block->NumSucc(this); - if ((numSuccs > 0) && !block->KindIs(BBJ_EHFINALLYRET, BBJ_EHFILTERRET)) + if ((numSuccs > 0) && !block->KindIs(BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET)) { weight_t const blockWeight = block->bbWeight; weight_t outgoingWeightMin = 0; diff --git a/src/coreclr/jit/fgprofilesynthesis.cpp b/src/coreclr/jit/fgprofilesynthesis.cpp index b49ba3f..a63f6b8 100644 --- a/src/coreclr/jit/fgprofilesynthesis.cpp +++ b/src/coreclr/jit/fgprofilesynthesis.cpp @@ -137,6 +137,7 @@ void ProfileSynthesis::AssignLikelihoods() case BBJ_THROW: case BBJ_RETURN: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: // No successor cases // (todo: finally ret may have succs) break; @@ -503,6 +504,7 @@ void ProfileSynthesis::RepairLikelihoods() case BBJ_THROW: case BBJ_RETURN: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: // No successor cases // Nothing to do. break; @@ -580,6 +582,7 @@ void ProfileSynthesis::BlendLikelihoods() case BBJ_THROW: case BBJ_RETURN: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: // No successor cases // Nothing to do. break; diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 50c31d5..44983ff 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1590,7 +1590,7 @@ void Compiler::fgAddSyncMethodEnterExit() // It gets an artificial ref count. assert(!tryLastBB->bbFallsThrough()); - BasicBlock* faultBB = fgNewBBafter(BBJ_EHFINALLYRET, tryLastBB, false); + BasicBlock* faultBB = fgNewBBafter(BBJ_EHFAULTRET, tryLastBB, false); assert(tryLastBB->bbNext == faultBB); assert(faultBB->bbNext == nullptr); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 2ed7762..efebeb0 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -11833,6 +11833,7 @@ SPILLSTACK: case BBJ_EHCATCHRET: case BBJ_RETURN: case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: case BBJ_THROW: BADCODE("can't have 'unreached' end of BB with non-empty stack"); diff --git a/src/coreclr/jit/jiteh.cpp b/src/coreclr/jit/jiteh.cpp index c28ac4b..74186dd 100644 --- a/src/coreclr/jit/jiteh.cpp +++ b/src/coreclr/jit/jiteh.cpp @@ -3482,6 +3482,49 @@ void Compiler::fgVerifyHandlerTab() { assert((block->bbFlags & BBF_TRY_BEG) == 0); } + + // Check for legal block types + switch (block->bbJumpKind) + { + case BBJ_EHFINALLYRET: + { + // Can only exist within a 'finally' handler + EHblkDsc* ehDsc = ehGetDsc(block->getHndIndex()); + assert(ehDsc->HasFinallyHandler()); + break; + } + + case BBJ_EHFAULTRET: + { + // Can only exist within a 'fault' handler + EHblkDsc* ehDsc = ehGetDsc(block->getHndIndex()); + assert(ehDsc->HasFaultHandler()); + break; + } + + case BBJ_EHFILTERRET: + { + // Can only exist within a filter region of a 'try/filter/filter-handler' handler + EHblkDsc* ehDsc = ehGetDsc(block->getHndIndex()); + assert(ehDsc->HasFilter()); + // Make sure it's in the filter region itself. + assert((blockNumMap[ehDsc->ebdFilter->bbNum] <= blockNumMap[block->bbNum]) && + (blockNumMap[block->bbNum] < blockNumMap[ehDsc->ebdHndBeg->bbNum])); + break; + } + + case BBJ_EHCATCHRET: + { + // Can only exist within a 'catch' region of a 'try/catch' handler + EHblkDsc* ehDsc = ehGetDsc(block->getHndIndex()); + assert(ehDsc->HasCatchHandler()); + break; + } + + default: + // No EH-related requirements. + break; + } } } diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index 4e93f03..c0dbf79 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -399,6 +399,7 @@ void Compiler::fgPerBlockLocalVarLiveness() switch (block->bbJumpKind) { case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_THROW: case BBJ_RETURN: VarSetOps::AssignNoCopy(this, block->bbLiveOut, VarSetOps::MakeEmpty(this)); @@ -940,6 +941,7 @@ void Compiler::fgExtendDbgLifetimes() break; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_RETURN: break; diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index bb0a107..ad51b9f 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -1517,7 +1517,7 @@ void LinearScan::buildUpperVectorSaveRefPositions(GenTree* tree, LsraLocation cu VarSetOps::Iter iter(compiler, liveLargeVectors); unsigned varIndex = 0; bool blockAlwaysReturn = - compiler->compCurBB->KindIs(BBJ_THROW, BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET); + compiler->compCurBB->KindIs(BBJ_THROW, BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET); while (iter.NextElem(&varIndex)) { diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 96790a3..046d2a7 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -1765,10 +1765,12 @@ public: return false; } - if (bottom->KindIs(BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, BBJ_CALLFINALLY, BBJ_SWITCH)) + if (bottom->KindIs(BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, BBJ_CALLFINALLY, + BBJ_SWITCH)) { JITDUMP(" bottom odd jump kind\n"); - // BBJ_EHFINALLYRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a loop. + // BBJ_EHFINALLYRET, BBJ_EHFAULTRET, BBJ_EHFILTERRET, BBJ_EHCATCHRET, and BBJ_CALLFINALLY can never form a + // loop. // BBJ_SWITCH that has a backward jump appears only for labeled break. return false; } @@ -2511,6 +2513,7 @@ private: break; case BBJ_EHFINALLYRET: + case BBJ_EHFAULTRET: case BBJ_EHFILTERRET: // The "try" associated with this "finally" must be in the same loop, so the // finally block will return control inside the loop. @@ -2820,6 +2823,7 @@ void Compiler::optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap, R case BBJ_THROW: case BBJ_RETURN: case BBJ_EHFILTERRET: + case BBJ_EHFAULTRET: case BBJ_EHFINALLYRET: case BBJ_EHCATCHRET: // These have no jump destination to update. -- 2.7.4