@@ -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,65 @@ 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
+ if (any_of (*BB, [](Instruction &I) {
1770
+ if (auto *CB = dyn_cast<CallBase>(&I))
1771
+ return CB->hasFnAttr (Attribute::Cold);
1772
+ return false ;
1773
+ })) {
1774
+ Visited[BB] = true ;
1775
+ return true ;
1776
+ }
1777
+
1778
+ auto Succs = successors (BB);
1779
+ // We found a path that doesn't go through any cold callsite.
1780
+ if (Succs.empty ())
1781
+ return false ;
1782
+
1783
+ for (auto *Succ : Succs) {
1784
+ // Start with false, this is necessary to ensure we don't turn loops into
1785
+ // cold.
1786
+ auto R = Visited.try_emplace (Succ, false );
1787
+ if (!R.second ) {
1788
+ if (R.first ->second )
1789
+ continue ;
1790
+ return false ;
1791
+ }
1792
+ if (!allBBPathsGoThroughCold (Succ, Visited))
1793
+ return false ;
1794
+ Visited[Succ] = true ;
1795
+ }
1796
+
1797
+ return true ;
1798
+ }
1799
+
1800
+ static bool allPathsGoThroughCold (Function &F) {
1801
+ SmallDenseMap<BasicBlock *, bool , 16 > Visited;
1802
+ Visited[&F.front ()] = false ;
1803
+ return allBBPathsGoThroughCold (&F.front (), Visited);
1804
+ }
1805
+
1806
+ // Set the cold function attribute if possible.
1807
+ static void addColdAttrs (const SCCNodeSet &SCCNodes,
1808
+ SmallSet<Function *, 8 > &Changed) {
1809
+ for (Function *F : SCCNodes) {
1810
+ if (!F || !F->hasExactDefinition () || F->hasFnAttribute (Attribute::Naked) ||
1811
+ F->hasFnAttribute (Attribute::Cold) || F->hasFnAttribute (Attribute::Hot))
1812
+ continue ;
1813
+
1814
+ // Potential TODO: We could add attribute `cold` on functions with `coldcc`.
1815
+ if (allPathsGoThroughCold (*F)) {
1816
+ F->addFnAttr (Attribute::Cold);
1817
+ ++NumCold;
1818
+ Changed.insert (F);
1819
+ continue ;
1820
+ }
1821
+ }
1822
+ }
1823
+
1763
1824
static bool functionWillReturn (const Function &F) {
1764
1825
// We can infer and propagate function attributes only when we know that the
1765
1826
// definition we'll get at link time is *exactly* the definition we see now.
@@ -1789,6 +1850,7 @@ static bool functionWillReturn(const Function &F) {
1789
1850
});
1790
1851
}
1791
1852
1853
+
1792
1854
// Set the willreturn function attribute if possible.
1793
1855
static void addWillReturn (const SCCNodeSet &SCCNodes,
1794
1856
SmallSet<Function *, 8 > &Changed) {
@@ -1802,6 +1864,8 @@ static void addWillReturn(const SCCNodeSet &SCCNodes,
1802
1864
}
1803
1865
}
1804
1866
1867
+
1868
+
1805
1869
static SCCNodesResult createSCCNodeSet (ArrayRef<Function *> Functions) {
1806
1870
SCCNodesResult Res;
1807
1871
Res.HasUnknownCall = false ;
@@ -1853,6 +1917,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
1853
1917
addArgumentAttrs (Nodes.SCCNodes , Changed);
1854
1918
inferConvergent (Nodes.SCCNodes , Changed);
1855
1919
addNoReturnAttrs (Nodes.SCCNodes , Changed);
1920
+ addColdAttrs (Nodes.SCCNodes , Changed);
1856
1921
addWillReturn (Nodes.SCCNodes , Changed);
1857
1922
addNoUndefAttrs (Nodes.SCCNodes , Changed);
1858
1923
0 commit comments