@@ -3655,12 +3655,21 @@ bool Compiler::optHoistThisLoop(FlowGraphNaturalLoop* loop, LoopHoistContext* ho
36553655 }
36563656#endif // FEATURE_MASKED_HW_INTRINSICS
36573657
3658- // Find the set of definitely-executed blocks. These will be given priority for hoisting.
3658+ // Find the set of definitely-executed blocks.
36593659 // Ideally, the definitely-executed blocks are the ones that post-dominate the entry block.
36603660 // Until we have post-dominators, we'll special-case for single-exit blocks.
36613661 //
3662- // TODO: We ought to consider hoisting more aggressively from conditionally executed blocks,
3663- // if they are frequently executed and it is safe to evaluate the tree early.
3662+ // Todo: it is not clear if this is a correctness requirement or a profitability heuristic.
3663+ // It seems like the latter. Ideally there are enough safeguards to prevent hoisting exception
3664+ // or side-effect dependent things. Note that HoistVisitor uses `m_canHoistSideEffects` to determine if it's
3665+ // ok to hoist a side-effect. It allows this only for the first block (the entry block), before any
3666+ // side-effect has been seen. After the first block, it assumes that there has been a side effect and
3667+ // no further side-effect can be hoisted. It is true that we don't analyze any program behavior in the
3668+ // flow graph between the entry block and the subsequent blocks, whether they be the next block dominating
3669+ // the exit block, or the pre-headers of nested loops.
3670+ //
3671+ // We really should consider hoisting from conditionally executed blocks, if they are frequently executed
3672+ // and it is safe to evaluate the tree early.
36643673 //
36653674 assert (m_dfsTree != nullptr );
36663675 BitVecTraits traits (m_dfsTree->PostOrderTraits ());
@@ -4116,7 +4125,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
41164125 };
41174126
41184127 ArrayStack<Value> m_valueStack;
4119- bool m_beforeSideEffect ;
4128+ bool m_canHoistSideEffects ;
41204129 FlowGraphNaturalLoop* m_loop;
41214130 LoopHoistContext* m_hoistContext;
41224131 BasicBlock* m_currentBlock;
@@ -4251,7 +4260,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
42514260 BitVec defExec)
42524261 : GenTreeVisitor(compiler)
42534262 , m_valueStack(compiler->getAllocator (CMK_LoopHoist))
4254- , m_beforeSideEffect (true )
4263+ , m_canHoistSideEffects (true )
42554264 , m_loop(loop)
42564265 , m_hoistContext(hoistContext)
42574266 , m_currentBlock(nullptr )
@@ -4263,26 +4272,46 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
42634272 void HoistBlock (BasicBlock* block)
42644273 {
42654274 m_currentBlock = block;
4266- for (Statement* const stmt : block->NonPhiStatements ())
4267- {
4268- WalkTree (stmt->GetRootNodePointer (), nullptr );
4269- Value& top = m_valueStack.TopRef ();
4270- assert (top.Node () == stmt->GetRootNode ());
42714275
4272- // hoist the top node?
4273- if (top.m_hoistable )
4274- {
4275- const bool defExecuted = BitVecOps::IsMember (m_traits, m_defExec, block->bbPostorderNum );
4276- m_compiler->optHoistCandidate (stmt->GetRootNode (), block, m_loop, m_hoistContext, defExecuted);
4277- }
4278- else
4276+ const weight_t blockWeight = block->getBBWeight (m_compiler);
4277+
4278+ JITDUMP (" \n HoistBlock " FMT_BB " (weight=%6s) of loop " FMT_LP " (head: " FMT_BB " )\n " , block->bbNum ,
4279+ refCntWtd2str (blockWeight, /* padForDecimalPlaces */ true ), m_loop->GetIndex (),
4280+ m_loop->GetHeader ()->bbNum );
4281+
4282+ if (blockWeight < (BB_UNITY_WEIGHT / 10 ))
4283+ {
4284+ JITDUMP (" block weight is too small to perform hoisting.\n " );
4285+ }
4286+ else
4287+ {
4288+ for (Statement* const stmt : block->NonPhiStatements ())
42794289 {
4280- JITDUMP ( " [%06u] %s: %s \n " , dspTreeID (top. Node ()),
4281- top. m_invariant ? " not hoistable " : " not invariant " , top. m_failReason );
4282- }
4290+ WalkTree (stmt-> GetRootNodePointer (), nullptr );
4291+ Value& top = m_valueStack. TopRef ( );
4292+ assert (top. Node () == stmt-> GetRootNode ());
42834293
4284- m_valueStack.Reset ();
4294+ // hoist the top node?
4295+ if (top.m_hoistable )
4296+ {
4297+ const bool defExecuted = BitVecOps::IsMember (m_traits, m_defExec, block->bbPostorderNum );
4298+ m_compiler->optHoistCandidate (stmt->GetRootNode (), block, m_loop, m_hoistContext, defExecuted);
4299+ }
4300+ else
4301+ {
4302+ JITDUMP (" [%06u] %s: %s\n " , dspTreeID (top.Node ()),
4303+ top.m_invariant ? " not hoistable" : " not invariant" , top.m_failReason );
4304+ }
4305+
4306+ m_valueStack.Reset ();
4307+ }
42854308 }
4309+
4310+ assert (!m_canHoistSideEffects || (block == m_loop->GetHeader ()));
4311+ // After visiting the first block (which is expected to always be
4312+ // the loop header) we can no longer hoist out side effecting trees
4313+ // as the next blocks could be conditionally executed.
4314+ m_canHoistSideEffects = false ;
42864315 }
42874316
42884317 fgWalkResult PreOrderVisit (GenTree** use, GenTree* user)
@@ -4458,10 +4487,11 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
44584487
44594488 if (treeIsHoistable)
44604489 {
4461- if (!m_beforeSideEffect )
4490+ if (!m_canHoistSideEffects )
44624491 {
44634492 // For now, we give up on an expression that might raise an exception if it is after the
4464- // first possible global side effect.
4493+ // first possible global side effect (and we assume we're after that if we're not in the first
4494+ // block).
44654495 // TODO-CQ: this is when we might do loop cloning.
44664496 //
44674497 if ((tree->gtFlags & GTF_EXCEPT) != 0 )
@@ -4484,24 +4514,24 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
44844514 }
44854515 }
44864516
4487- // Next check if we need to set 'm_beforeSideEffect ' to false.
4517+ // Next check if we need to set 'm_canHoistSideEffects ' to false.
44884518 //
44894519 // If we have already set it to false then we can skip these checks
44904520 //
4491- if (m_beforeSideEffect )
4521+ if (m_canHoistSideEffects )
44924522 {
44934523 // Is the value of the whole tree loop invariant?
44944524 if (!treeIsInvariant)
44954525 {
44964526 // We have a tree that is not loop invariant and we thus cannot hoist
44974527 assert (treeIsHoistable == false );
44984528
4499- // Check if we should clear m_beforeSideEffect .
4500- // If 'tree' can throw an exception then we need to set m_beforeSideEffect to false.
4529+ // Check if we should clear m_canHoistSideEffects .
4530+ // If 'tree' can throw an exception then we need to set m_canHoistSideEffects to false.
45014531 // Note that calls are handled below
45024532 if (tree->OperMayThrow (m_compiler) && !tree->IsCall ())
45034533 {
4504- m_beforeSideEffect = false ;
4534+ m_canHoistSideEffects = false ;
45054535 }
45064536 }
45074537
@@ -4517,19 +4547,19 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
45174547 GenTreeCall* call = tree->AsCall ();
45184548 if (!call->IsHelperCall ())
45194549 {
4520- m_beforeSideEffect = false ;
4550+ m_canHoistSideEffects = false ;
45214551 }
45224552 else
45234553 {
45244554 CorInfoHelpFunc helpFunc = eeGetHelperNum (call->gtCallMethHnd );
45254555 if (s_helperCallProperties.MutatesHeap (helpFunc))
45264556 {
4527- m_beforeSideEffect = false ;
4557+ m_canHoistSideEffects = false ;
45284558 }
45294559 else if (s_helperCallProperties.MayRunCctor (helpFunc) &&
45304560 (call->gtFlags & GTF_CALL_HOISTABLE) == 0 )
45314561 {
4532- m_beforeSideEffect = false ;
4562+ m_canHoistSideEffects = false ;
45334563 }
45344564
45354565 // Additional check for helper calls that throw exceptions
@@ -4541,7 +4571,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
45414571 // Does this helper call throw?
45424572 if (!s_helperCallProperties.NoThrow (helpFunc))
45434573 {
4544- m_beforeSideEffect = false ;
4574+ m_canHoistSideEffects = false ;
45454575 }
45464576 }
45474577 }
@@ -4562,8 +4592,8 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
45624592 if (isGloballyVisibleStore)
45634593 {
45644594 INDEBUG (failReason = " store to globally visible memory" );
4565- treeIsHoistable = false ;
4566- m_beforeSideEffect = false ;
4595+ treeIsHoistable = false ;
4596+ m_canHoistSideEffects = false ;
45674597 }
45684598 }
45694599 }
@@ -4667,22 +4697,8 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
46674697 };
46684698
46694699 HoistVisitor visitor (this , loop, hoistContext, traits, defExecuted);
4670- loop->VisitLoopBlocks ([&](BasicBlock* block) -> BasicBlockVisit {
4671- const weight_t blockWeight = block->getBBWeight (this );
4672-
4673- JITDUMP (" \n optHoistLoopBlocks " FMT_BB " (weight=%6s) of loop " FMT_LP " (head: " FMT_BB " )\n " ,
4674- block->bbNum , refCntWtd2str (blockWeight, /* padForDecimalPlaces */ true ), loop->GetIndex (),
4675- loop->GetHeader ()->bbNum );
4676-
4677- if (blockWeight < (BB_UNITY_WEIGHT / 10 ))
4678- {
4679- JITDUMP (" block weight is too small to perform hoisting.\n " );
4680- }
4681- else
4682- {
4683- visitor.HoistBlock (block);
4684- }
4685-
4700+ loop->VisitLoopBlocksReversePostOrder ([&](BasicBlock* block) -> BasicBlockVisit {
4701+ visitor.HoistBlock (block);
46864702 return BasicBlockVisit::Continue;
46874703 });
46884704
0 commit comments