block->bbFlags |= (BBF_INTERNAL | BBF_IMPORTED);
// This new first BB has an implicit ref, and no others.
- block->bbRefs = 1;
+ //
+ // But if we call this early, before fgLinkBasicBlocks,
+ // defer and let it handle adding the implicit ref.
+ //
+ block->bbRefs = fgComputePredsDone ? 1 : 0;
fgFirstBBScratch = fgFirstBB;
{
assert(fgFirstBBScratch == fgFirstBB);
assert(fgFirstBBScratch->bbFlags & BBF_INTERNAL);
- assert(fgFirstBBScratch->countOfInEdges() == 1);
+ if (fgComputePredsDone)
+ {
+ assert(fgFirstBBScratch->countOfInEdges() == 1);
+ }
// Normally, the first scratch block is a fall-through block. However, if the block after it was an empty
// BBJ_ALWAYS block, it might get removed, and the code that removes it will make the first scratch block
targetBlock->bbFlags |= BBF_BACKWARD_JUMP_TARGET;
}
-/*****************************************************************************
- *
- * Finally link up the bbJumpDest of the blocks together
- */
-
+//------------------------------------------------------------------------
+// fgLinkBasicBlocks: set block jump targets and add pred edges
+//
+// Notes:
+// Pred edges for BBJ_EHFILTERRET are set later by fgFindBasicBlocks.
+// Pred edges for BBJ_EHFINALLYRET are set later by impFixPredLists,
+// after setting up the callfinally blocks.
+//
void Compiler::fgLinkBasicBlocks()
{
- /* Create the basic block lookup tables */
-
+ // Create the basic block lookup tables
+ //
fgInitBBLookup();
- /* First block is always reachable */
+#ifdef DEBUG
+ // Verify blocks are in increasing bbNum order and
+ // all pred list info is in initial state.
+ //
+ fgDebugCheckBBNumIncreasing();
+ for (BasicBlock* const block : Blocks())
+ {
+ assert(block->bbPreds == nullptr);
+ assert(block->bbLastPred == nullptr);
+ assert(block->bbRefs == 0);
+ }
+#endif
+
+ // First block is always reachable
+ //
fgFirstBB->bbRefs = 1;
- /* Walk all the basic blocks, filling in the target addresses */
+ // Special args to fgAddRefPred so it will use the initialization fast path.
+ //
+ flowList* const oldEdge = nullptr;
+ bool const initializingPreds = true;
for (BasicBlock* const curBBdesc : Blocks())
{
case BBJ_COND:
case BBJ_ALWAYS:
case BBJ_LEAVE:
- curBBdesc->bbJumpDest = fgLookupBB(curBBdesc->bbJumpOffs);
- curBBdesc->bbJumpDest->bbRefs++;
+ {
+ BasicBlock* const jumpDest = fgLookupBB(curBBdesc->bbJumpOffs);
+ curBBdesc->bbJumpDest = jumpDest;
+ fgAddRefPred(jumpDest, curBBdesc, oldEdge, initializingPreds);
+
if (curBBdesc->bbJumpDest->bbNum <= curBBdesc->bbNum)
{
fgMarkBackwardJump(curBBdesc->bbJumpDest, curBBdesc);
}
- /* Is the next block reachable? */
-
+ // Is the next block reachable?
+ //
if (curBBdesc->KindIs(BBJ_ALWAYS, BBJ_LEAVE))
{
break;
{
BADCODE("Fall thru the end of a method");
}
+ }
// Fall through, the next block is also reachable
FALLTHROUGH;
case BBJ_NONE:
- curBBdesc->bbNext->bbRefs++;
+ fgAddRefPred(curBBdesc->bbNext, curBBdesc, oldEdge, initializingPreds);
break;
- case BBJ_EHFINALLYRET:
case BBJ_EHFILTERRET:
+ // We can't set up the pred list for these just yet.
+ // We do it in fgFindBasicBlocks.
+ break;
+
+ case BBJ_EHFINALLYRET:
+ // We can't set up the pred list for these just yet.
+ // We do it in impFixPredLists.
+ break;
+
case BBJ_THROW:
case BBJ_RETURN:
break;
case BBJ_SWITCH:
-
- unsigned jumpCnt;
- jumpCnt = curBBdesc->bbJumpSwt->bbsCount;
- BasicBlock** jumpPtr;
- jumpPtr = curBBdesc->bbJumpSwt->bbsDstTab;
+ {
+ unsigned jumpCnt = curBBdesc->bbJumpSwt->bbsCount;
+ BasicBlock** jumpPtr = curBBdesc->bbJumpSwt->bbsDstTab;
do
{
- *jumpPtr = fgLookupBB((unsigned)*(size_t*)jumpPtr);
- (*jumpPtr)->bbRefs++;
+ BasicBlock* jumpDest = fgLookupBB((unsigned)*(size_t*)jumpPtr);
+ *jumpPtr = jumpDest;
+ fgAddRefPred(jumpDest, curBBdesc, oldEdge, initializingPreds);
if ((*jumpPtr)->bbNum <= curBBdesc->bbNum)
{
fgMarkBackwardJump(*jumpPtr, curBBdesc);
noway_assert(*(jumpPtr - 1) == curBBdesc->bbNext);
break;
+ }
case BBJ_CALLFINALLY: // BBJ_CALLFINALLY and BBJ_EHCATCHRET don't appear until later
case BBJ_EHCATCHRET:
break;
}
}
+
+ // Pred lists now established.
+ //
+ fgComputePredsDone = true;
}
//------------------------------------------------------------------------
{
// Mark catch handler as successor.
block->bbJumpDest = hndBegBB;
+ fgAddRefPred(hndBegBB, block);
assert(block->bbJumpDest->bbCatchTyp == BBCT_FILTER_HANDLER);
break;
}
if (modified)
{
- // If we computed the cheap preds, don't let them leak out, in case other code doesn't maintain them properly.
- if (fgCheapPredsValid)
- {
- fgRemovePreds();
- }
-
JITDUMP("Added at least one basic block in fgNormalizeEH.\n");
fgRenumberBlocks();
-#ifdef DEBUG
// fgRenumberBlocks() will dump all the blocks and the handler table, so we don't need to do it here.
- fgVerifyHandlerTab();
-#endif
+ INDEBUG(fgVerifyHandlerTab());
}
else
{
// ...then we want to insert an empty, non-removable block outside the try to be the new first block of the
// handler.
BasicBlock* newHndStart = bbNewBasicBlock(BBJ_NONE);
- fgInsertBBbefore(eh->ebdHndBeg, newHndStart);
+ fgInsertBBbefore(handlerStart, newHndStart);
+ fgAddRefPred(handlerStart, newHndStart);
+
+ // Handler begins have an extra implicit ref count.
+ // bbNewBasicBlock has already handled this for newHndStart.
+ // Remove handlerStart's implicit ref count.
+ //
+ assert(newHndStart->bbRefs == 1);
+ assert(handlerStart->bbRefs >= 2);
+ handlerStart->bbRefs--;
#ifdef DEBUG
if (verbose)
// Note that this can only happen for nested 'try' regions, so we only need to look through the
// 'try' nesting hierarchy.
//
+ ArrayStack<BasicBlock*> interestingPreds(getAllocator(CMK_BasicBlock));
for (unsigned XTnum = 0; XTnum < compHndBBtabCount; XTnum++)
{
mutualTryLast = ehOuter->ebdTryLast;
mutualProtectIndex = ehOuterTryIndex;
- // We're going to need the preds. We compute them here, before inserting the new block,
- // so our logic to add/remove preds below is the same for both the first time preds are
- // created and subsequent times.
- if (!fgCheapPredsValid)
- {
- fgComputeCheapPreds();
- }
-
// We've got multiple 'try' blocks starting at the same place!
// Add a new first 'try' block for 'ehOuter' that will be outside 'eh'.
BasicBlock* newTryStart = bbNewBasicBlock(BBJ_NONE);
+ newTryStart->bbRefs = 0;
fgInsertBBbefore(insertBeforeBlk, newTryStart);
- insertBeforeBlk->bbRefs++;
+ fgAddRefPred(insertBeforeBlk, newTryStart);
-#ifdef DEBUG
- if (verbose)
+ // It's possible for a try to start at the beginning of a method. If so, we need
+ // to adjust the implicit ref counts as we've just created a new first bb
+ //
+ if (newTryStart == fgFirstBB)
{
- printf("'try' begin for EH#%u and EH#%u are same block; inserted new " FMT_BB
- " before " FMT_BB " "
- "as new 'try' begin for EH#%u.\n",
- ehOuterTryIndex, XTnum, newTryStart->bbNum, insertBeforeBlk->bbNum, ehOuterTryIndex);
+ assert(insertBeforeBlk->bbRefs >= 2);
+ insertBeforeBlk->bbRefs--;
+ newTryStart->bbRefs++;
}
-#endif // DEBUG
+
+ JITDUMP("'try' begin for EH#%u and EH#%u are same block; inserted new " FMT_BB " before " FMT_BB
+ " "
+ "as new 'try' begin for EH#%u.\n",
+ ehOuterTryIndex, XTnum, newTryStart->bbNum, insertBeforeBlk->bbNum, ehOuterTryIndex);
// The new block is the new 'try' begin.
ehOuter->ebdTryBeg = newTryStart;
// | |----------- BB04
// |------------------ BB05
- BasicBlockList* nextPred; // we're going to update the pred list as we go, so we need to keep
- // track of the next pred in case it gets deleted.
- for (BasicBlockList* pred = insertBeforeBlk->bbCheapPreds; pred != nullptr; pred = nextPred)
+ interestingPreds.Reset();
+ for (BasicBlock* predBlock : insertBeforeBlk->PredBlocks())
{
- nextPred = pred->next;
+ if ((predBlock == newTryStart) || BasicBlock::sameTryRegion(insertBeforeBlk, predBlock))
+ {
+ continue;
+ }
- // Who gets this predecessor?
- BasicBlock* predBlock = pred->block;
+ interestingPreds.Push(predBlock);
+ }
- if (!BasicBlock::sameTryRegion(insertBeforeBlk, predBlock))
- {
- // Move the edge to target newTryStart instead of insertBeforeBlk.
- fgAddCheapPred(newTryStart, predBlock);
- fgRemoveCheapPred(insertBeforeBlk, predBlock);
-
- // Now change pred branches.
- //
- // Since cheap preds contains dups (for switch duplicates), we will call
- // this once per dup.
- if (predBlock->bbJumpKind != BBJ_NONE)
- {
- fgReplaceJumpTarget(predBlock, newTryStart, insertBeforeBlk);
- }
+ while (interestingPreds.Height() > 0)
+ {
+ BasicBlock* const predBlock = interestingPreds.Pop();
- // Need to adjust ref counts here since we're retargeting edges.
- newTryStart->bbRefs++;
- assert(insertBeforeBlk->countOfInEdges() > 0);
- insertBeforeBlk->bbRefs--;
+ // Change pred branches.
+ //
+ if (predBlock->bbJumpKind != BBJ_NONE)
+ {
+ fgReplaceJumpTarget(predBlock, newTryStart, insertBeforeBlk);
+ }
-#ifdef DEBUG
- if (verbose)
- {
- printf("Redirect " FMT_BB " target from " FMT_BB " to " FMT_BB ".\n",
- predBlock->bbNum, insertBeforeBlk->bbNum, newTryStart->bbNum);
- }
-#endif // DEBUG
+ if ((predBlock->bbNext == newTryStart) && predBlock->bbFallsThrough())
+ {
+ fgRemoveRefPred(insertBeforeBlk, predBlock);
+ fgAddRefPred(newTryStart, predBlock);
}
- }
- // The new block (a fall-through block) is a new predecessor.
- fgAddCheapPred(insertBeforeBlk, newTryStart);
+ JITDUMP("Redirect " FMT_BB " target from " FMT_BB " to " FMT_BB ".\n", predBlock->bbNum,
+ insertBeforeBlk->bbNum, newTryStart->bbNum);
+ }
// We don't need to update the tryBeg block of other EH regions here because we are looping
// outwards in enclosing try index order, and we'll get to them later.
// shares a 'last' pointer
BasicBlock* newLast = bbNewBasicBlock(BBJ_NONE);
+ newLast->bbRefs = 0;
assert(insertAfterBlk != nullptr);
fgInsertBBafter(insertAfterBlk, newLast);
newLast->bbCodeOffsEnd = newLast->bbCodeOffs; // code size = 0. TODO: use BAD_IL_OFFSET instead?
newLast->inheritWeight(insertAfterBlk);
newLast->bbFlags |= BBF_INTERNAL;
-
- // The new block (a fall-through block) is a new predecessor.
- if (fgCheapPredsValid)
- {
- fgAddCheapPred(newLast, insertAfterBlk);
- }
+ fgAddRefPred(newLast, insertAfterBlk);
// Move the insert pointer. More enclosing equivalent 'last' blocks will be inserted after this.
insertAfterBlk = newLast;