@@ -1773,6 +1773,94 @@ void Compiler::optPerformStaticOptimizations(FlowGraphNaturalLoop* loop,
1773
1773
}
1774
1774
}
1775
1775
1776
+ // ------------------------------------------------------------------------
1777
+ // optShouldCloneLoop: Decide if a loop that can be cloned should be cloned.
1778
+ //
1779
+ // Arguments:
1780
+ // loop - the current loop for which the optimizations are performed.
1781
+ // context - data structure where all loop cloning info is kept.
1782
+ //
1783
+ // Returns:
1784
+ // true if expected performance gain from cloning is worth the potential
1785
+ // size increase.
1786
+ //
1787
+ // Remarks:
1788
+ // This is a simple-minded heuristic meant to avoid "runaway" cloning
1789
+ // where large loops are cloned.
1790
+ //
1791
+ // We estimate the size cost of cloning by summing up the number of
1792
+ // tree nodes in all statements in all blocks in the loop.
1793
+ //
1794
+ // This value is compared to a hard-coded threshold, and if bigger,
1795
+ // then the method returns false.
1796
+ //
1797
+ bool Compiler::optShouldCloneLoop (FlowGraphNaturalLoop* loop, LoopCloneContext* context)
1798
+ {
1799
+ // Compute loop size
1800
+ //
1801
+ unsigned loopSize = 0 ;
1802
+
1803
+ // For now we use a very simplistic model where each tree node
1804
+ // has the same code size.
1805
+ //
1806
+ // CostSz is not available until later.
1807
+ //
1808
+ struct TreeCostWalker : GenTreeVisitor<TreeCostWalker>
1809
+ {
1810
+ enum
1811
+ {
1812
+ DoPreOrder = true ,
1813
+ };
1814
+
1815
+ unsigned m_nodeCount;
1816
+
1817
+ TreeCostWalker (Compiler* comp)
1818
+ : GenTreeVisitor(comp)
1819
+ , m_nodeCount(0 )
1820
+ {
1821
+ }
1822
+
1823
+ fgWalkResult PreOrderVisit (GenTree** use, GenTree* user)
1824
+ {
1825
+ m_nodeCount++;
1826
+ return WALK_CONTINUE;
1827
+ }
1828
+
1829
+ void Reset ()
1830
+ {
1831
+ m_nodeCount = 0 ;
1832
+ }
1833
+ unsigned Cost ()
1834
+ {
1835
+ return m_nodeCount;
1836
+ }
1837
+ };
1838
+
1839
+ TreeCostWalker costWalker (this );
1840
+
1841
+ loop->VisitLoopBlocks ([&](BasicBlock* block) {
1842
+ weight_t normalizedWeight = block->getBBWeight (this );
1843
+ for (Statement* const stmt : block->Statements ())
1844
+ {
1845
+ costWalker.Reset ();
1846
+ costWalker.WalkTree (stmt->GetRootNodePointer (), nullptr );
1847
+ loopSize += costWalker.Cost ();
1848
+ }
1849
+ return BasicBlockVisit::Continue;
1850
+ });
1851
+
1852
+ int const sizeLimit = JitConfig.JitCloneLoopsSizeLimit ();
1853
+
1854
+ if ((sizeLimit >= 0 ) && (loopSize >= (unsigned )sizeLimit))
1855
+ {
1856
+ JITDUMP (" Loop cloning: rejecting loop " FMT_LP " of size %u, size limit %d\n " , loop->GetIndex (), loopSize,
1857
+ sizeLimit);
1858
+ return false ;
1859
+ }
1860
+
1861
+ return true ;
1862
+ }
1863
+
1776
1864
// ----------------------------------------------------------------------------
1777
1865
// optIsLoopClonable: Determine whether this loop can be cloned.
1778
1866
//
@@ -2563,7 +2651,7 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
2563
2651
2564
2652
assert (compCurBB->lastStmt () == info->stmt );
2565
2653
info->context ->EnsureLoopOptInfo (info->loop ->GetIndex ())
2566
- ->Push (new (this , CMK_LoopOpt) LcTypeTestOptInfo (info->stmt , indir, lclNum, clsHnd));
2654
+ ->Push (new (this , CMK_LoopOpt) LcTypeTestOptInfo (compCurBB, info->stmt , indir, lclNum, clsHnd));
2567
2655
}
2568
2656
}
2569
2657
else if (optIsHandleOrIndirOfHandle (relopOp2, GTF_ICON_FTN_ADDR))
@@ -2644,7 +2732,7 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop
2644
2732
assert (iconHandle->IsIconHandle (GTF_ICON_FTN_ADDR));
2645
2733
assert (compCurBB->lastStmt () == info->stmt );
2646
2734
LcMethodAddrTestOptInfo* optInfo = new (this , CMK_LoopOpt)
2647
- LcMethodAddrTestOptInfo (info->stmt , indir, lclNum, (void *)iconHandle->IconValue (),
2735
+ LcMethodAddrTestOptInfo (compCurBB, info->stmt , indir, lclNum, (void *)iconHandle->IconValue (),
2648
2736
relopOp2 != iconHandle DEBUG_ARG (
2649
2737
(CORINFO_METHOD_HANDLE)iconHandle->gtTargetHandle ));
2650
2738
info->context ->EnsureLoopOptInfo (info->loop ->GetIndex ())->Push (optInfo);
@@ -2944,6 +3032,10 @@ PhaseStatus Compiler::optCloneLoops()
2944
3032
// No need to clone.
2945
3033
context.CancelLoopOptInfo (loop->GetIndex ());
2946
3034
}
3035
+ else if (!optShouldCloneLoop (loop, &context))
3036
+ {
3037
+ context.CancelLoopOptInfo (loop->GetIndex ());
3038
+ }
2947
3039
}
2948
3040
}
2949
3041
0 commit comments