@@ -3655,12 +3655,21 @@ bool Compiler::optHoistThisLoop(FlowGraphNaturalLoop* loop, LoopHoistContext* ho
3655
3655
}
3656
3656
#endif // FEATURE_MASKED_HW_INTRINSICS
3657
3657
3658
- // Find the set of definitely-executed blocks. These will be given priority for hoisting.
3658
+ // Find the set of definitely-executed blocks.
3659
3659
// Ideally, the definitely-executed blocks are the ones that post-dominate the entry block.
3660
3660
// Until we have post-dominators, we'll special-case for single-exit blocks.
3661
3661
//
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.
3664
3673
//
3665
3674
assert (m_dfsTree != nullptr );
3666
3675
BitVecTraits traits (m_dfsTree->PostOrderTraits ());
@@ -4116,7 +4125,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4116
4125
};
4117
4126
4118
4127
ArrayStack<Value> m_valueStack;
4119
- bool m_beforeSideEffect ;
4128
+ bool m_canHoistSideEffects ;
4120
4129
FlowGraphNaturalLoop* m_loop;
4121
4130
LoopHoistContext* m_hoistContext;
4122
4131
BasicBlock* m_currentBlock;
@@ -4251,7 +4260,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4251
4260
BitVec defExec)
4252
4261
: GenTreeVisitor(compiler)
4253
4262
, m_valueStack(compiler->getAllocator (CMK_LoopHoist))
4254
- , m_beforeSideEffect (true )
4263
+ , m_canHoistSideEffects (true )
4255
4264
, m_loop(loop)
4256
4265
, m_hoistContext(hoistContext)
4257
4266
, m_currentBlock(nullptr )
@@ -4263,26 +4272,46 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4263
4272
void HoistBlock (BasicBlock* block)
4264
4273
{
4265
4274
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 ());
4271
4275
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 ())
4279
4289
{
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 ());
4283
4293
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
+ }
4285
4308
}
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 ;
4286
4315
}
4287
4316
4288
4317
fgWalkResult PreOrderVisit (GenTree** use, GenTree* user)
@@ -4458,10 +4487,11 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4458
4487
4459
4488
if (treeIsHoistable)
4460
4489
{
4461
- if (!m_beforeSideEffect )
4490
+ if (!m_canHoistSideEffects )
4462
4491
{
4463
4492
// 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).
4465
4495
// TODO-CQ: this is when we might do loop cloning.
4466
4496
//
4467
4497
if ((tree->gtFlags & GTF_EXCEPT) != 0 )
@@ -4484,24 +4514,24 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4484
4514
}
4485
4515
}
4486
4516
4487
- // Next check if we need to set 'm_beforeSideEffect ' to false.
4517
+ // Next check if we need to set 'm_canHoistSideEffects ' to false.
4488
4518
//
4489
4519
// If we have already set it to false then we can skip these checks
4490
4520
//
4491
- if (m_beforeSideEffect )
4521
+ if (m_canHoistSideEffects )
4492
4522
{
4493
4523
// Is the value of the whole tree loop invariant?
4494
4524
if (!treeIsInvariant)
4495
4525
{
4496
4526
// We have a tree that is not loop invariant and we thus cannot hoist
4497
4527
assert (treeIsHoistable == false );
4498
4528
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.
4501
4531
// Note that calls are handled below
4502
4532
if (tree->OperMayThrow (m_compiler) && !tree->IsCall ())
4503
4533
{
4504
- m_beforeSideEffect = false ;
4534
+ m_canHoistSideEffects = false ;
4505
4535
}
4506
4536
}
4507
4537
@@ -4517,19 +4547,19 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4517
4547
GenTreeCall* call = tree->AsCall ();
4518
4548
if (!call->IsHelperCall ())
4519
4549
{
4520
- m_beforeSideEffect = false ;
4550
+ m_canHoistSideEffects = false ;
4521
4551
}
4522
4552
else
4523
4553
{
4524
4554
CorInfoHelpFunc helpFunc = eeGetHelperNum (call->gtCallMethHnd );
4525
4555
if (s_helperCallProperties.MutatesHeap (helpFunc))
4526
4556
{
4527
- m_beforeSideEffect = false ;
4557
+ m_canHoistSideEffects = false ;
4528
4558
}
4529
4559
else if (s_helperCallProperties.MayRunCctor (helpFunc) &&
4530
4560
(call->gtFlags & GTF_CALL_HOISTABLE) == 0 )
4531
4561
{
4532
- m_beforeSideEffect = false ;
4562
+ m_canHoistSideEffects = false ;
4533
4563
}
4534
4564
4535
4565
// Additional check for helper calls that throw exceptions
@@ -4541,7 +4571,7 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4541
4571
// Does this helper call throw?
4542
4572
if (!s_helperCallProperties.NoThrow (helpFunc))
4543
4573
{
4544
- m_beforeSideEffect = false ;
4574
+ m_canHoistSideEffects = false ;
4545
4575
}
4546
4576
}
4547
4577
}
@@ -4562,8 +4592,8 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4562
4592
if (isGloballyVisibleStore)
4563
4593
{
4564
4594
INDEBUG (failReason = " store to globally visible memory" );
4565
- treeIsHoistable = false ;
4566
- m_beforeSideEffect = false ;
4595
+ treeIsHoistable = false ;
4596
+ m_canHoistSideEffects = false ;
4567
4597
}
4568
4598
}
4569
4599
}
@@ -4667,22 +4697,8 @@ void Compiler::optHoistLoopBlocks(FlowGraphNaturalLoop* loop,
4667
4697
};
4668
4698
4669
4699
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);
4686
4702
return BasicBlockVisit::Continue;
4687
4703
});
4688
4704
0 commit comments