@@ -82,6 +82,7 @@ STATISTIC(NumNoUnwind, "Number of functions marked as nounwind");
8282STATISTIC (NumNoFree, " Number of functions marked as nofree" );
8383STATISTIC (NumWillReturn, " Number of functions marked as willreturn" );
8484STATISTIC (NumNoSync, " Number of functions marked as nosync" );
85+ STATISTIC (NumCold, " Number of functions marked as cold" );
8586
8687STATISTIC (NumThinLinkNoRecurse,
8788 " Number of functions marked as norecurse during thinlink" );
@@ -1745,6 +1746,7 @@ static bool canReturn(Function &F) {
17451746 return false ;
17461747}
17471748
1749+
17481750// Set the noreturn function attribute if possible.
17491751static void addNoReturnAttrs (const SCCNodeSet &SCCNodes,
17501752 SmallSet<Function *, 8 > &Changed) {
@@ -1760,6 +1762,72 @@ static void addNoReturnAttrs(const SCCNodeSet &SCCNodes,
17601762 }
17611763}
17621764
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+
17631831static bool functionWillReturn (const Function &F) {
17641832 // We can infer and propagate function attributes only when we know that the
17651833 // definition we'll get at link time is *exactly* the definition we see now.
@@ -1853,6 +1921,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter,
18531921 addArgumentAttrs (Nodes.SCCNodes , Changed);
18541922 inferConvergent (Nodes.SCCNodes , Changed);
18551923 addNoReturnAttrs (Nodes.SCCNodes , Changed);
1924+ addColdAttrs (Nodes.SCCNodes , Changed);
18561925 addWillReturn (Nodes.SCCNodes , Changed);
18571926 addNoUndefAttrs (Nodes.SCCNodes , Changed);
18581927
0 commit comments