@@ -82,6 +82,7 @@ STATISTIC(NumNoUnwind, "Number of functions marked as nounwind");
82
82
STATISTIC (NumNoFree, " Number of functions marked as nofree" );
83
83
STATISTIC (NumWillReturn, " Number of functions marked as willreturn" );
84
84
STATISTIC (NumNoSync, " Number of functions marked as nosync" );
85
+ STATISTIC (NumCold, " Number of functions marked as cold" );
85
86
86
87
STATISTIC (NumThinLinkNoRecurse,
87
88
" Number of functions marked as norecurse during thinlink" );
@@ -1745,6 +1746,7 @@ static bool canReturn(Function &F) {
1745
1746
return false ;
1746
1747
}
1747
1748
1749
+
1748
1750
// Set the noreturn function attribute if possible.
1749
1751
static void addNoReturnAttrs (const SCCNodeSet &SCCNodes,
1750
1752
SmallSet<Function *, 8 > &Changed) {
@@ -1760,6 +1762,72 @@ static void addNoReturnAttrs(const SCCNodeSet &SCCNodes,
1760
1762
}
1761
1763
}
1762
1764
1765
+ static bool
1766
+ allBBPathsGoThroughCold (BasicBlock *BB,
1767
+ SmallDenseMap<BasicBlock *, bool , 16 > &Visited) {
1768
+ // If BB contains a cold callsite this path through the CG is cold.
1769
+ // Ignore whether the instructions actually are guranteed to transfer
1770
+ // execution. Divergent behavior is considered unlikely.
1771
+ if (any_of (*BB, [](Instruction &I) {
1772
+ if (auto *CB = dyn_cast<CallBase>(&I))
1773
+ return CB->hasFnAttr (Attribute::Cold);
1774
+ return false ;
1775
+ })) {
1776
+ Visited[BB] = true ;
1777
+ return true ;
1778
+ }
1779
+
1780
+ auto Succs = successors (BB);
1781
+ // We found a path that doesn't go through any cold callsite.
1782
+ if (Succs.empty ())
1783
+ return false ;
1784
+
1785
+ // We didn't find a cold callsite in this BB, so check that all successors
1786
+ // contain a cold callsite (or that their successors do).
1787
+ // Potential TODO: We could use static branch hints to assume certain
1788
+ // successor paths are inherently cold, irrespective of if they contain a cold
1789
+ // callsite.
1790
+ for (auto *Succ : Succs) {
1791
+ // Start with false, this is necessary to ensure we don't turn loops into
1792
+ // cold.
1793
+ auto R = Visited.try_emplace (Succ, false );
1794
+ if (!R.second ) {
1795
+ if (R.first ->second )
1796
+ continue ;
1797
+ return false ;
1798
+ }
1799
+ if (!allBBPathsGoThroughCold (Succ, Visited))
1800
+ return false ;
1801
+ Visited[Succ] = true ;
1802
+ }
1803
+
1804
+ return true ;
1805
+ }
1806
+
1807
+ static bool allPathsGoThroughCold (Function &F) {
1808
+ SmallDenseMap<BasicBlock *, bool , 16 > Visited;
1809
+ Visited[&F.front ()] = false ;
1810
+ return allBBPathsGoThroughCold (&F.front (), Visited);
1811
+ }
1812
+
1813
+ // Set the cold function attribute if possible.
1814
+ static void addColdAttrs (const SCCNodeSet &SCCNodes,
1815
+ SmallSet<Function *, 8 > &Changed) {
1816
+ for (Function *F : SCCNodes) {
1817
+ if (!F || !F->hasExactDefinition () || F->hasFnAttribute (Attribute::Naked) ||
1818
+ F->hasFnAttribute (Attribute::Cold) || F->hasFnAttribute (Attribute::Hot))
1819
+ continue ;
1820
+
1821
+ // Potential TODO: We could add attribute `cold` on functions with `coldcc`.
1822
+ if (allPathsGoThroughCold (*F)) {
1823
+ F->addFnAttr (Attribute::Cold);
1824
+ ++NumCold;
1825
+ Changed.insert (F);
1826
+ continue ;
1827
+ }
1828
+ }
1829
+ }
1830
+
1763
1831
static bool functionWillReturn (const Function &F) {
1764
1832
// We can infer and propagate function attributes only when we know that the
1765
1833
// definition we'll get at link time is *exactly* the definition we see now.
@@ -1853,6 +1921,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
1853
1921
addArgumentAttrs (Nodes.SCCNodes , Changed);
1854
1922
inferConvergent (Nodes.SCCNodes , Changed);
1855
1923
addNoReturnAttrs (Nodes.SCCNodes , Changed);
1924
+ addColdAttrs (Nodes.SCCNodes , Changed);
1856
1925
addWillReturn (Nodes.SCCNodes , Changed);
1857
1926
addNoUndefAttrs (Nodes.SCCNodes , Changed);
1858
1927
0 commit comments