Skip to content

Commit 9b77dd4

Browse files
JIT: Don't reorder handler blocks (dotnet#112292)
Splitting this out of dotnet#112004 to simplify diff triage. The cost of executing a handler region should be dominated by the runtime overhead of exception handling, so I don't think we're losing anything meaningful in not reordering them. In the case of finally regions, if they are sufficiently hot, the JIT should've copied them into the main method region.
1 parent 7122090 commit 9b77dd4

File tree

3 files changed

+75
-111
lines changed

3 files changed

+75
-111
lines changed

src/coreclr/jit/compiler.hpp

+15-5
Original file line numberDiff line numberDiff line change
@@ -5062,12 +5062,22 @@ void Compiler::fgVisitBlocksInLoopAwareRPO(FlowGraphDfsTree* dfsTree, FlowGraphN
50625062
}
50635063
};
50645064

5065-
LoopAwareVisitor visitor(dfsTree, loops, func);
5066-
5067-
for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--)
5065+
if (loops->NumLoops() == 0)
5066+
{
5067+
for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--)
5068+
{
5069+
BasicBlock* const block = dfsTree->GetPostOrder(i - 1);
5070+
func(block);
5071+
}
5072+
}
5073+
else
50685074
{
5069-
BasicBlock* const block = dfsTree->GetPostOrder(i - 1);
5070-
visitor.VisitBlock(block);
5075+
LoopAwareVisitor visitor(dfsTree, loops, func);
5076+
for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--)
5077+
{
5078+
BasicBlock* const block = dfsTree->GetPostOrder(i - 1);
5079+
visitor.VisitBlock(block);
5080+
}
50715081
}
50725082
}
50735083

src/coreclr/jit/fgopt.cpp

+57-98
Original file line numberDiff line numberDiff line change
@@ -4663,120 +4663,86 @@ void Compiler::fgDoReversePostOrderLayout()
46634663
}
46644664
#endif // DEBUG
46654665

4666-
// If LSRA didn't create any new blocks, we can reuse its loop-aware RPO traversal,
4667-
// which is cached in Compiler::fgBBs.
4668-
// If the cache isn't available, we need to recompute the loop-aware RPO.
4666+
// If LSRA didn't create any new blocks, we can reuse its flowgraph annotations.
46694667
//
4670-
BasicBlock** rpoSequence = fgBBs;
4671-
4672-
if (rpoSequence == nullptr)
4668+
if (m_dfsTree == nullptr)
46734669
{
4674-
assert(m_dfsTree == nullptr);
4675-
m_dfsTree = fgComputeDfs</* useProfile */ true>();
4676-
FlowGraphNaturalLoops* const loops = FlowGraphNaturalLoops::Find(m_dfsTree);
4677-
rpoSequence = new (this, CMK_BasicBlock) BasicBlock*[m_dfsTree->GetPostOrderCount()];
4678-
unsigned index = 0;
4679-
auto addToSequence = [rpoSequence, &index](BasicBlock* block) {
4680-
rpoSequence[index++] = block;
4681-
};
4682-
4683-
fgVisitBlocksInLoopAwareRPO(m_dfsTree, loops, addToSequence);
4670+
m_dfsTree = fgComputeDfs</* useProfile */ true>();
4671+
m_loops = FlowGraphNaturalLoops::Find(m_dfsTree);
46844672
}
46854673
else
46864674
{
4687-
assert(m_dfsTree != nullptr);
4675+
assert(m_loops != nullptr);
46884676
}
46894677

4690-
// Fast path: We don't have any EH regions, so just reorder the blocks
4691-
//
4692-
if (compHndBBtabCount == 0)
4693-
{
4694-
for (unsigned i = 1; i < m_dfsTree->GetPostOrderCount(); i++)
4678+
BasicBlock** const rpoSequence = new (this, CMK_BasicBlock) BasicBlock*[m_dfsTree->GetPostOrderCount()];
4679+
unsigned numBlocks = 0;
4680+
auto addToSequence = [rpoSequence, &numBlocks](BasicBlock* block) {
4681+
// Exclude handler regions from being reordered.
4682+
//
4683+
if (!block->hasHndIndex())
46954684
{
4696-
BasicBlock* const block = rpoSequence[i - 1];
4697-
BasicBlock* const blockToMove = rpoSequence[i];
4698-
4699-
if (!block->NextIs(blockToMove))
4700-
{
4701-
fgUnlinkBlock(blockToMove);
4702-
fgInsertBBafter(block, blockToMove);
4703-
}
4685+
rpoSequence[numBlocks++] = block;
47044686
}
4687+
};
47054688

4706-
fgMoveHotJumps</* hasEH */ false>();
4707-
4708-
return;
4709-
}
4689+
fgVisitBlocksInLoopAwareRPO(m_dfsTree, m_loops, addToSequence);
47104690

4711-
// The RPO will break up call-finally pairs, so save them before re-ordering
4691+
// Reorder blocks.
47124692
//
4713-
struct CallFinallyPair
4693+
for (unsigned i = 1; i < numBlocks; i++)
47144694
{
4715-
BasicBlock* callFinally;
4716-
BasicBlock* callFinallyRet;
4695+
BasicBlock* block = rpoSequence[i - 1];
4696+
BasicBlock* const blockToMove = rpoSequence[i];
47174697

4718-
// Constructor provided so we can call ArrayStack::Emplace
4719-
//
4720-
CallFinallyPair(BasicBlock* first, BasicBlock* second)
4721-
: callFinally(first)
4722-
, callFinallyRet(second)
4698+
if (block->NextIs(blockToMove))
47234699
{
4700+
continue;
47244701
}
4725-
};
4726-
4727-
ArrayStack<CallFinallyPair> callFinallyPairs(getAllocator());
47284702

4729-
for (EHblkDsc* const HBtab : EHClauses(this))
4730-
{
4731-
if (HBtab->HasFinallyHandler())
4703+
// Only reorder blocks within the same try region. We don't want to make them non-contiguous.
4704+
//
4705+
if (!BasicBlock::sameTryRegion(block, blockToMove))
47324706
{
4733-
for (BasicBlock* const pred : HBtab->ebdHndBeg->PredBlocks())
4734-
{
4735-
assert(pred->KindIs(BBJ_CALLFINALLY));
4736-
if (pred->isBBCallFinallyPair())
4737-
{
4738-
callFinallyPairs.Emplace(pred, pred->Next());
4739-
}
4740-
}
4707+
continue;
47414708
}
4742-
}
47434709

4744-
// Reorder blocks
4745-
//
4746-
for (unsigned i = 1; i < m_dfsTree->GetPostOrderCount(); i++)
4747-
{
4748-
BasicBlock* const block = rpoSequence[i - 1];
4749-
BasicBlock* const blockToMove = rpoSequence[i];
4710+
// Don't move call-finally pair tails independently.
4711+
// When we encounter the head, we will move the entire pair.
4712+
//
4713+
if (blockToMove->isBBCallFinallyPairTail())
4714+
{
4715+
continue;
4716+
}
47504717

4751-
// Only reorder blocks within the same EH region -- we don't want to make them non-contiguous
4718+
// Don't break up call-finally pairs by inserting something in the middle.
47524719
//
4753-
if (BasicBlock::sameEHRegion(block, blockToMove))
4720+
if (block->isBBCallFinallyPair())
47544721
{
4755-
// Don't reorder EH regions with filter handlers -- we want the filter to come first
4756-
//
4757-
if (block->hasHndIndex() && ehGetDsc(block->getHndIndex())->HasFilter())
4758-
{
4759-
continue;
4760-
}
4722+
block = block->Next();
4723+
}
47614724

4762-
if (!block->NextIs(blockToMove))
4763-
{
4764-
fgUnlinkBlock(blockToMove);
4765-
fgInsertBBafter(block, blockToMove);
4766-
}
4725+
if (blockToMove->isBBCallFinallyPair())
4726+
{
4727+
BasicBlock* const callFinallyRet = blockToMove->Next();
4728+
fgUnlinkRange(blockToMove, callFinallyRet);
4729+
fgMoveBlocksAfter(blockToMove, callFinallyRet, block);
4730+
}
4731+
else
4732+
{
4733+
fgUnlinkBlock(blockToMove);
4734+
fgInsertBBafter(block, blockToMove);
47674735
}
47684736
}
47694737

4770-
// Fix up call-finally pairs
4771-
//
4772-
for (int i = 0; i < callFinallyPairs.Height(); i++)
4738+
if (compHndBBtabCount == 0)
47734739
{
4774-
const CallFinallyPair& pair = callFinallyPairs.BottomRef(i);
4775-
fgUnlinkBlock(pair.callFinallyRet);
4776-
fgInsertBBafter(pair.callFinally, pair.callFinallyRet);
4740+
fgMoveHotJumps</* hasEH */ false>();
4741+
}
4742+
else
4743+
{
4744+
fgMoveHotJumps</* hasEH */ true>();
47774745
}
4778-
4779-
fgMoveHotJumps</* hasEH */ true>();
47804746
}
47814747

47824748
//-----------------------------------------------------------------------------
@@ -5133,14 +5099,6 @@ void Compiler::ThreeOptLayout::ConsiderEdge(FlowEdge* edge)
51335099
return;
51345100
}
51355101

5136-
// Don't waste time reordering within handler regions.
5137-
// Note that if a finally region is sufficiently hot,
5138-
// we should have cloned it into the main method body already.
5139-
if (srcBlk->hasHndIndex() || dstBlk->hasHndIndex())
5140-
{
5141-
return;
5142-
}
5143-
51445102
// For backward jumps, we will consider partitioning before 'srcBlk'.
51455103
// If 'srcBlk' is a BBJ_CALLFINALLYRET, this partition will split up a call-finally pair.
51465104
// Thus, don't consider edges out of BBJ_CALLFINALLYRET blocks.
@@ -5256,7 +5214,8 @@ void Compiler::ThreeOptLayout::Run()
52565214
// Initialize the current block order
52575215
for (BasicBlock* const block : compiler->Blocks(compiler->fgFirstBB, finalBlock))
52585216
{
5259-
if (!compiler->m_dfsTree->Contains(block))
5217+
// Exclude unreachable blocks and handler blocks from being reordered
5218+
if (!compiler->m_dfsTree->Contains(block) || block->hasHndIndex())
52605219
{
52615220
continue;
52625221
}
@@ -5289,14 +5248,14 @@ void Compiler::ThreeOptLayout::Run()
52895248
continue;
52905249
}
52915250

5292-
// Only reorder within EH regions to maintain contiguity.
5293-
if (!BasicBlock::sameEHRegion(block, next))
5251+
// Only reorder within try regions to maintain contiguity.
5252+
if (!BasicBlock::sameTryRegion(block, next))
52945253
{
52955254
continue;
52965255
}
52975256

5298-
// Don't move the entry of an EH region.
5299-
if (compiler->bbIsTryBeg(next) || compiler->bbIsHandlerBeg(next))
5257+
// Don't move the entry of a try region.
5258+
if (compiler->bbIsTryBeg(next))
53005259
{
53015260
continue;
53025261
}

src/coreclr/jit/lsra.cpp

+3-8
Original file line numberDiff line numberDiff line change
@@ -972,7 +972,7 @@ void LinearScan::setBlockSequence()
972972
FlowGraphDfsTree* const dfsTree = compiler->m_dfsTree;
973973
blockSequence = new (compiler, CMK_LSRA) BasicBlock*[compiler->fgBBcount];
974974

975-
if (compiler->opts.OptimizationEnabled() && dfsTree->HasCycle())
975+
if (compiler->opts.OptimizationEnabled())
976976
{
977977
// Ensure loop bodies are compact in the visitation order.
978978
compiler->m_loops = FlowGraphNaturalLoops::Find(dfsTree);
@@ -1333,15 +1333,10 @@ PhaseStatus LinearScan::doLinearScan()
13331333
compiler->compLSRADone = true;
13341334

13351335
// If edge resolution didn't create new blocks,
1336-
// cache the block sequence so it can be used as an initial layout during block reordering.
1337-
if (compiler->fgBBcount == bbSeqCount)
1338-
{
1339-
compiler->fgBBs = blockSequence;
1340-
}
1341-
else
1336+
// we can reuse the current flowgraph annotations during block layout.
1337+
if (compiler->fgBBcount != bbSeqCount)
13421338
{
13431339
assert(compiler->fgBBcount > bbSeqCount);
1344-
compiler->fgBBs = nullptr;
13451340
compiler->fgInvalidateDfsTree();
13461341
}
13471342

0 commit comments

Comments
 (0)