if (ehDsc->HasFilter())
{
ehDsc->ebdFilter->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
+
+ // The first block of a filter has an artifical extra refcount.
+ ehDsc->ebdFilter->bbRefs++;
}
ehDsc->ebdHndBeg->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
+
+ // The first block of a handler has an artificial extra refcount.
+ ehDsc->ebdHndBeg->bbRefs++;
}
fgModified = false;
fgExtendEHRegionBefore(block); // Update the EH table to make the prolog block the first block in the block's EH
// block.
- // fgExtendEHRegionBefore mucks with the bbRefs without updating the pred list, which we will
- // do below for this block. So, undo that change.
- assert(newHead->bbRefs > 0);
- newHead->bbRefs--;
- block->bbRefs++;
-
// Distribute the pred list between newHead and block. Incoming edges coming from outside
// the handler go to the prolog. Edges coming from with the handler are back-edges, and
// go to the existing 'block'.
#endif // DEBUG
HBtab->ebdTryBeg = bPrev;
bPrev->bbFlags |= BBF_TRY_BEG | BBF_DONT_REMOVE | BBF_HAS_LABEL;
+
// clear the TryBeg flag unless it begins another try region
if (!bbIsTryBeg(block))
{
HBtab->ebdHndBeg = bPrev;
bPrev->bbFlags |= BBF_DONT_REMOVE | BBF_HAS_LABEL;
+
+#if FEATURE_EH_FUNCLETS
+ if (fgFuncletsCreated)
+ {
+ assert((block->bbFlags & BBF_FUNCLET_BEG) != 0);
+ bPrev->bbFlags |= BBF_FUNCLET_BEG;
+ block->bbFlags &= ~BBF_FUNCLET_BEG;
+ }
+#endif // FEATURE_EH_FUNCLETS
+
bPrev->bbRefs++;
// If this is a handler for a filter, the last block of the filter will end with
HBtab->ebdFilter = bPrev;
bPrev->bbFlags |= BBF_DONT_REMOVE | BBF_HAS_LABEL;
+
+#if FEATURE_EH_FUNCLETS
+ if (fgFuncletsCreated)
+ {
+ assert((block->bbFlags & BBF_FUNCLET_BEG) != 0);
+ bPrev->bbFlags |= BBF_FUNCLET_BEG;
+ block->bbFlags &= ~BBF_FUNCLET_BEG;
+ }
+#endif // FEATURE_EH_FUNCLETS
+
bPrev->bbRefs++;
}
}
//
// Return Value:
// A block with the desired characteristics, so the new block will be inserted after this one.
-// If there is no suitable location, return nullptr. This should basically never happen.
-
+// If there is no suitable location, return nullptr. This should basically never happen except in the case of
+// single-block filters.
BasicBlock* Compiler::fgFindInsertPoint(unsigned regionIndex,
bool putInTryRegion,
BasicBlock* startBlk,
regionIndex, dspBool(putInTryRegion), startBlk->bbNum, (endBlk == nullptr) ? 0 : endBlk->bbNum,
(nearBlk == nullptr) ? 0 : nearBlk->bbNum, (jumpBlk == nullptr) ? 0 : jumpBlk->bbNum, dspBool(runRarely));
+ bool insertingIntoFilter = false;
+ if (!putInTryRegion)
+ {
+ EHblkDsc* const dsc = ehGetDsc(regionIndex - 1);
+ insertingIntoFilter = dsc->HasFilter() && (startBlk == dsc->ebdFilter) && (endBlk == dsc->ebdHndBeg);
+ }
+
bool reachedNear = false; // Have we reached 'nearBlk' in our search? If not, we'll keep searching.
bool inFilter = false; // Are we in a filter region that we need to skip?
BasicBlock* bestBlk =
{
// Record the fact that we entered a filter region, so we don't insert into filters...
// Unless the caller actually wanted the block inserted in this exact filter region.
- // Detect this by the fact that startBlk and endBlk point to the filter begin and end.
- if (putInTryRegion || (blk != startBlk) || (startBlk != ehGetDsc(regionIndex - 1)->ebdFilter) ||
- (endBlk != ehGetDsc(regionIndex - 1)->ebdHndBeg))
+ if (!insertingIntoFilter || (blk != startBlk))
{
inFilter = true;
}
bestBlk = goodBlk;
}
-DONE:;
+DONE:
+
+ // If we are inserting into a filter and the best block is the end of the filter region, we need to
+ // insert after its predecessor instead: the CLR ABI states that the terminal block of a filter region
+ // is its exit block. If the filter region consists of a single block, a new block cannot be inserted
+ // without either splitting the single block before inserting a new block or inserting the new block
+ // before the single block and updating the filter description such that the inserted block is marked
+ // as the entry block for the filter. This work must be done by the caller; this function returns
+ // `nullptr` to indicate this case.
+ if (insertingIntoFilter && (bestBlk == endBlk->bbPrev) && (bestBlk == startBlk))
+ {
+ assert(bestBlk != nullptr);
+ assert(bestBlk->bbJumpKind == BBJ_EHFILTERRET);
+ bestBlk = nullptr;
+ }
return bestBlk;
}
// Now find the insertion point.
afterBlk = fgFindInsertPoint(regionIndex, putInTryRegion, startBlk, endBlk, nearBlk, nullptr, runRarely);
+ // If afterBlk is nullptr, we must be inserting into a single-block filter region. Because the CLR ABI requires
+ // that control exits a filter via the last instruction in the filter range, this situation requires logically
+ // splitting the single block. In practice, we simply insert a new block at the beginning of the filter region
+ // that transfers control flow to the existing single block.
+ if (afterBlk == nullptr)
+ {
+ assert(putInFilter);
+
+ BasicBlock* newFilterEntryBlock = fgNewBBbefore(BBJ_ALWAYS, startBlk, true);
+ newFilterEntryBlock->bbJumpDest = startBlk;
+ fgAddRefPred(startBlk, newFilterEntryBlock);
+
+ afterBlk = newFilterEntryBlock;
+ }
+
_FoundAfterBlk:;
/* We have decided to insert the block after 'afterBlk'. */
}
/* Check the bbRefs */
- noway_assert(!checkBBRefs || block->bbRefs == blockRefs);
+ if (checkBBRefs)
+ {
+ if (block->bbRefs != blockRefs)
+ {
+ // Check to see if this block is the beginning of a filter or a handler and adjust the ref count
+ // appropriately.
+ for (EHblkDsc *HBtab = compHndBBtab, *HBtabEnd = &compHndBBtab[compHndBBtabCount]; HBtab != HBtabEnd;
+ HBtab++)
+ {
+ if (HBtab->ebdHndBeg == block)
+ {
+ blockRefs++;
+ }
+ if (HBtab->HasFilter() && (HBtab->ebdFilter == block))
+ {
+ blockRefs++;
+ }
+ }
+ }
+
+ assert(block->bbRefs == blockRefs);
+ }
/* Check that BBF_HAS_HANDLER is valid bbTryIndex */
if (block->hasTryIndex())