1*f23d57a6SLeonard Grey //===- SectionPriorities.cpp ----------------------------------------------===//
2*f23d57a6SLeonard Grey //
3*f23d57a6SLeonard Grey // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*f23d57a6SLeonard Grey // See https://llvm.org/LICENSE.txt for license information.
5*f23d57a6SLeonard Grey // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*f23d57a6SLeonard Grey //
7*f23d57a6SLeonard Grey //===----------------------------------------------------------------------===//
8*f23d57a6SLeonard Grey ///
9*f23d57a6SLeonard Grey /// This is based on the ELF port, see ELF/CallGraphSort.cpp for the details
10*f23d57a6SLeonard Grey /// about the algorithm.
11*f23d57a6SLeonard Grey ///
12*f23d57a6SLeonard Grey //===----------------------------------------------------------------------===//
13*f23d57a6SLeonard Grey 
14*f23d57a6SLeonard Grey #include "SectionPriorities.h"
15*f23d57a6SLeonard Grey #include "Config.h"
16*f23d57a6SLeonard Grey #include "InputFiles.h"
17*f23d57a6SLeonard Grey #include "Symbols.h"
18*f23d57a6SLeonard Grey #include "Target.h"
19*f23d57a6SLeonard Grey #include "lld/Common/ErrorHandler.h"
20*f23d57a6SLeonard Grey #include "llvm/ADT/DenseMap.h"
21*f23d57a6SLeonard Grey #include "llvm/ADT/MapVector.h"
22*f23d57a6SLeonard Grey #include "llvm/Support/TimeProfiler.h"
23*f23d57a6SLeonard Grey #include "llvm/Support/raw_ostream.h"
24*f23d57a6SLeonard Grey #include <numeric>
25*f23d57a6SLeonard Grey 
26*f23d57a6SLeonard Grey using namespace llvm;
27*f23d57a6SLeonard Grey using namespace lld;
28*f23d57a6SLeonard Grey using namespace lld::macho;
29*f23d57a6SLeonard Grey 
30*f23d57a6SLeonard Grey namespace {
31*f23d57a6SLeonard Grey struct Edge {
32*f23d57a6SLeonard Grey   int from;
33*f23d57a6SLeonard Grey   uint64_t weight;
34*f23d57a6SLeonard Grey };
35*f23d57a6SLeonard Grey 
36*f23d57a6SLeonard Grey struct Cluster {
37*f23d57a6SLeonard Grey   Cluster(int sec, size_t s) : next(sec), prev(sec), size(s) {}
38*f23d57a6SLeonard Grey 
39*f23d57a6SLeonard Grey   double getDensity() const {
40*f23d57a6SLeonard Grey     if (size == 0)
41*f23d57a6SLeonard Grey       return 0;
42*f23d57a6SLeonard Grey     return double(weight) / double(size);
43*f23d57a6SLeonard Grey   }
44*f23d57a6SLeonard Grey 
45*f23d57a6SLeonard Grey   int next;
46*f23d57a6SLeonard Grey   int prev;
47*f23d57a6SLeonard Grey   uint64_t size;
48*f23d57a6SLeonard Grey   uint64_t weight = 0;
49*f23d57a6SLeonard Grey   uint64_t initialWeight = 0;
50*f23d57a6SLeonard Grey   Edge bestPred = {-1, 0};
51*f23d57a6SLeonard Grey };
52*f23d57a6SLeonard Grey 
53*f23d57a6SLeonard Grey class CallGraphSort {
54*f23d57a6SLeonard Grey public:
55*f23d57a6SLeonard Grey   CallGraphSort();
56*f23d57a6SLeonard Grey 
57*f23d57a6SLeonard Grey   DenseMap<const InputSection *, size_t> run();
58*f23d57a6SLeonard Grey 
59*f23d57a6SLeonard Grey private:
60*f23d57a6SLeonard Grey   std::vector<Cluster> clusters;
61*f23d57a6SLeonard Grey   std::vector<const InputSection *> sections;
62*f23d57a6SLeonard Grey };
63*f23d57a6SLeonard Grey // Maximum amount the combined cluster density can be worse than the original
64*f23d57a6SLeonard Grey // cluster to consider merging.
65*f23d57a6SLeonard Grey constexpr int MAX_DENSITY_DEGRADATION = 8;
66*f23d57a6SLeonard Grey } // end anonymous namespace
67*f23d57a6SLeonard Grey 
68*f23d57a6SLeonard Grey using SectionPair = std::pair<const InputSection *, const InputSection *>;
69*f23d57a6SLeonard Grey 
70*f23d57a6SLeonard Grey // Take the edge list in config->callGraphProfile, resolve symbol names to
71*f23d57a6SLeonard Grey // Symbols, and generate a graph between InputSections with the provided
72*f23d57a6SLeonard Grey // weights.
73*f23d57a6SLeonard Grey CallGraphSort::CallGraphSort() {
74*f23d57a6SLeonard Grey   MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
75*f23d57a6SLeonard Grey   DenseMap<const InputSection *, int> secToCluster;
76*f23d57a6SLeonard Grey 
77*f23d57a6SLeonard Grey   auto getOrCreateCluster = [&](const InputSection *isec) -> int {
78*f23d57a6SLeonard Grey     auto res = secToCluster.try_emplace(isec, clusters.size());
79*f23d57a6SLeonard Grey     if (res.second) {
80*f23d57a6SLeonard Grey       sections.push_back(isec);
81*f23d57a6SLeonard Grey       clusters.emplace_back(clusters.size(), isec->getSize());
82*f23d57a6SLeonard Grey     }
83*f23d57a6SLeonard Grey     return res.first->second;
84*f23d57a6SLeonard Grey   };
85*f23d57a6SLeonard Grey 
86*f23d57a6SLeonard Grey   // Create the graph
87*f23d57a6SLeonard Grey   for (std::pair<SectionPair, uint64_t> &c : profile) {
88*f23d57a6SLeonard Grey     const auto fromSec = c.first.first->canonical();
89*f23d57a6SLeonard Grey     const auto toSec = c.first.second->canonical();
90*f23d57a6SLeonard Grey     uint64_t weight = c.second;
91*f23d57a6SLeonard Grey     // Ignore edges between input sections belonging to different output
92*f23d57a6SLeonard Grey     // sections.  This is done because otherwise we would end up with clusters
93*f23d57a6SLeonard Grey     // containing input sections that can't actually be placed adjacently in the
94*f23d57a6SLeonard Grey     // output.  This messes with the cluster size and density calculations.  We
95*f23d57a6SLeonard Grey     // would also end up moving input sections in other output sections without
96*f23d57a6SLeonard Grey     // moving them closer to what calls them.
97*f23d57a6SLeonard Grey     if (fromSec->parent != toSec->parent)
98*f23d57a6SLeonard Grey       continue;
99*f23d57a6SLeonard Grey 
100*f23d57a6SLeonard Grey     int from = getOrCreateCluster(fromSec);
101*f23d57a6SLeonard Grey     int to = getOrCreateCluster(toSec);
102*f23d57a6SLeonard Grey 
103*f23d57a6SLeonard Grey     clusters[to].weight += weight;
104*f23d57a6SLeonard Grey 
105*f23d57a6SLeonard Grey     if (from == to)
106*f23d57a6SLeonard Grey       continue;
107*f23d57a6SLeonard Grey 
108*f23d57a6SLeonard Grey     // Remember the best edge.
109*f23d57a6SLeonard Grey     Cluster &toC = clusters[to];
110*f23d57a6SLeonard Grey     if (toC.bestPred.from == -1 || toC.bestPred.weight < weight) {
111*f23d57a6SLeonard Grey       toC.bestPred.from = from;
112*f23d57a6SLeonard Grey       toC.bestPred.weight = weight;
113*f23d57a6SLeonard Grey     }
114*f23d57a6SLeonard Grey   }
115*f23d57a6SLeonard Grey   for (Cluster &c : clusters)
116*f23d57a6SLeonard Grey     c.initialWeight = c.weight;
117*f23d57a6SLeonard Grey }
118*f23d57a6SLeonard Grey 
119*f23d57a6SLeonard Grey // It's bad to merge clusters which would degrade the density too much.
120*f23d57a6SLeonard Grey static bool isNewDensityBad(Cluster &a, Cluster &b) {
121*f23d57a6SLeonard Grey   double newDensity = double(a.weight + b.weight) / double(a.size + b.size);
122*f23d57a6SLeonard Grey   return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
123*f23d57a6SLeonard Grey }
124*f23d57a6SLeonard Grey 
125*f23d57a6SLeonard Grey // Find the leader of V's belonged cluster (represented as an equivalence
126*f23d57a6SLeonard Grey // class). We apply union-find path-halving technique (simple to implement) in
127*f23d57a6SLeonard Grey // the meantime as it decreases depths and the time complexity.
128*f23d57a6SLeonard Grey static int getLeader(std::vector<int> &leaders, int v) {
129*f23d57a6SLeonard Grey   while (leaders[v] != v) {
130*f23d57a6SLeonard Grey     leaders[v] = leaders[leaders[v]];
131*f23d57a6SLeonard Grey     v = leaders[v];
132*f23d57a6SLeonard Grey   }
133*f23d57a6SLeonard Grey   return v;
134*f23d57a6SLeonard Grey }
135*f23d57a6SLeonard Grey 
136*f23d57a6SLeonard Grey static void mergeClusters(std::vector<Cluster> &cs, Cluster &into, int intoIdx,
137*f23d57a6SLeonard Grey                           Cluster &from, int fromIdx) {
138*f23d57a6SLeonard Grey   int tail1 = into.prev, tail2 = from.prev;
139*f23d57a6SLeonard Grey   into.prev = tail2;
140*f23d57a6SLeonard Grey   cs[tail2].next = intoIdx;
141*f23d57a6SLeonard Grey   from.prev = tail1;
142*f23d57a6SLeonard Grey   cs[tail1].next = fromIdx;
143*f23d57a6SLeonard Grey   into.size += from.size;
144*f23d57a6SLeonard Grey   into.weight += from.weight;
145*f23d57a6SLeonard Grey   from.size = 0;
146*f23d57a6SLeonard Grey   from.weight = 0;
147*f23d57a6SLeonard Grey }
148*f23d57a6SLeonard Grey 
149*f23d57a6SLeonard Grey // Group InputSections into clusters using the Call-Chain Clustering heuristic
150*f23d57a6SLeonard Grey // then sort the clusters by density.
151*f23d57a6SLeonard Grey DenseMap<const InputSection *, size_t> CallGraphSort::run() {
152*f23d57a6SLeonard Grey   const uint64_t maxClusterSize = target->getPageSize();
153*f23d57a6SLeonard Grey 
154*f23d57a6SLeonard Grey   // Cluster indices sorted by density.
155*f23d57a6SLeonard Grey   std::vector<int> sorted(clusters.size());
156*f23d57a6SLeonard Grey   // For union-find.
157*f23d57a6SLeonard Grey   std::vector<int> leaders(clusters.size());
158*f23d57a6SLeonard Grey 
159*f23d57a6SLeonard Grey   std::iota(leaders.begin(), leaders.end(), 0);
160*f23d57a6SLeonard Grey   std::iota(sorted.begin(), sorted.end(), 0);
161*f23d57a6SLeonard Grey 
162*f23d57a6SLeonard Grey   llvm::stable_sort(sorted, [&](int a, int b) {
163*f23d57a6SLeonard Grey     return clusters[a].getDensity() > clusters[b].getDensity();
164*f23d57a6SLeonard Grey   });
165*f23d57a6SLeonard Grey 
166*f23d57a6SLeonard Grey   for (int l : sorted) {
167*f23d57a6SLeonard Grey     // The cluster index is the same as the index of its leader here because
168*f23d57a6SLeonard Grey     // clusters[L] has not been merged into another cluster yet.
169*f23d57a6SLeonard Grey     Cluster &c = clusters[l];
170*f23d57a6SLeonard Grey 
171*f23d57a6SLeonard Grey     // Don't consider merging if the edge is unlikely.
172*f23d57a6SLeonard Grey     if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
173*f23d57a6SLeonard Grey       continue;
174*f23d57a6SLeonard Grey 
175*f23d57a6SLeonard Grey     int predL = getLeader(leaders, c.bestPred.from);
176*f23d57a6SLeonard Grey     // Already in the same cluster.
177*f23d57a6SLeonard Grey     if (l == predL)
178*f23d57a6SLeonard Grey       continue;
179*f23d57a6SLeonard Grey 
180*f23d57a6SLeonard Grey     Cluster *predC = &clusters[predL];
181*f23d57a6SLeonard Grey     if (c.size + predC->size > maxClusterSize)
182*f23d57a6SLeonard Grey       continue;
183*f23d57a6SLeonard Grey 
184*f23d57a6SLeonard Grey     if (isNewDensityBad(*predC, c))
185*f23d57a6SLeonard Grey       continue;
186*f23d57a6SLeonard Grey 
187*f23d57a6SLeonard Grey     leaders[l] = predL;
188*f23d57a6SLeonard Grey     mergeClusters(clusters, *predC, predL, c, l);
189*f23d57a6SLeonard Grey   }
190*f23d57a6SLeonard Grey   // Sort remaining non-empty clusters by density.
191*f23d57a6SLeonard Grey   sorted.clear();
192*f23d57a6SLeonard Grey   for (int i = 0, e = (int)clusters.size(); i != e; ++i)
193*f23d57a6SLeonard Grey     if (clusters[i].size > 0)
194*f23d57a6SLeonard Grey       sorted.push_back(i);
195*f23d57a6SLeonard Grey   llvm::stable_sort(sorted, [&](int a, int b) {
196*f23d57a6SLeonard Grey     return clusters[a].getDensity() > clusters[b].getDensity();
197*f23d57a6SLeonard Grey   });
198*f23d57a6SLeonard Grey 
199*f23d57a6SLeonard Grey   DenseMap<const InputSection *, size_t> orderMap;
200*f23d57a6SLeonard Grey 
201*f23d57a6SLeonard Grey   // Sections will be sorted by decreasing order. Absent sections will have
202*f23d57a6SLeonard Grey   // priority 0 and be placed at the end of sections.
203*f23d57a6SLeonard Grey   // NB: This is opposite from COFF/ELF to be compatible with the existing
204*f23d57a6SLeonard Grey   // order-file code.
205*f23d57a6SLeonard Grey   int curOrder = clusters.size();
206*f23d57a6SLeonard Grey   for (int leader : sorted) {
207*f23d57a6SLeonard Grey     for (int i = leader;;) {
208*f23d57a6SLeonard Grey       orderMap[sections[i]] = curOrder--;
209*f23d57a6SLeonard Grey       i = clusters[i].next;
210*f23d57a6SLeonard Grey       if (i == leader)
211*f23d57a6SLeonard Grey         break;
212*f23d57a6SLeonard Grey     }
213*f23d57a6SLeonard Grey   }
214*f23d57a6SLeonard Grey   if (!config->printSymbolOrder.empty()) {
215*f23d57a6SLeonard Grey     std::error_code ec;
216*f23d57a6SLeonard Grey     raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
217*f23d57a6SLeonard Grey     if (ec) {
218*f23d57a6SLeonard Grey       error("cannot open " + config->printSymbolOrder + ": " + ec.message());
219*f23d57a6SLeonard Grey       return orderMap;
220*f23d57a6SLeonard Grey     }
221*f23d57a6SLeonard Grey     // Print the symbols ordered by C3, in the order of decreasing curOrder
222*f23d57a6SLeonard Grey     // Instead of sorting all the orderMap, just repeat the loops above.
223*f23d57a6SLeonard Grey     for (int leader : sorted)
224*f23d57a6SLeonard Grey       for (int i = leader;;) {
225*f23d57a6SLeonard Grey         const InputSection *isec = sections[i];
226*f23d57a6SLeonard Grey         // Search all the symbols in the file of the section
227*f23d57a6SLeonard Grey         // and find out a Defined symbol with name that is within the
228*f23d57a6SLeonard Grey         // section.
229*f23d57a6SLeonard Grey         for (Symbol *sym : isec->getFile()->symbols) {
230*f23d57a6SLeonard Grey           if (auto *d = dyn_cast_or_null<Defined>(sym)) {
231*f23d57a6SLeonard Grey             if (d->isec == isec)
232*f23d57a6SLeonard Grey               os << sym->getName() << "\n";
233*f23d57a6SLeonard Grey           }
234*f23d57a6SLeonard Grey         }
235*f23d57a6SLeonard Grey         i = clusters[i].next;
236*f23d57a6SLeonard Grey         if (i == leader)
237*f23d57a6SLeonard Grey           break;
238*f23d57a6SLeonard Grey       }
239*f23d57a6SLeonard Grey   }
240*f23d57a6SLeonard Grey 
241*f23d57a6SLeonard Grey   return orderMap;
242*f23d57a6SLeonard Grey }
243*f23d57a6SLeonard Grey 
244*f23d57a6SLeonard Grey // Sort sections by the profile data provided by __LLVM,__cg_profile sections.
245*f23d57a6SLeonard Grey //
246*f23d57a6SLeonard Grey // This first builds a call graph based on the profile data then merges sections
247*f23d57a6SLeonard Grey // according to the C³ heuristic. All clusters are then sorted by a density
248*f23d57a6SLeonard Grey // metric to further improve locality.
249*f23d57a6SLeonard Grey DenseMap<const InputSection *, size_t> macho::computeCallGraphProfileOrder() {
250*f23d57a6SLeonard Grey   TimeTraceScope timeScope("Call graph profile sort");
251*f23d57a6SLeonard Grey   return CallGraphSort().run();
252*f23d57a6SLeonard Grey }
253