xref: /llvm-project/bolt/lib/Passes/PettisAndHansen.cpp (revision d2c876993625ce9b36bdd7ccc5e0c4cb04f32fb9)
1 //===- bolt/Passes/PettisAndHansen.cpp ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // The file implements Pettis and Hansen code-layout algorithm.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "bolt/Passes/HFSort.h"
14 #include "llvm/Support/Debug.h"
15 #include "llvm/Support/Format.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include <set>
18 #include <unordered_map>
19 
20 #define DEBUG_TYPE "hfsort"
21 
22 namespace llvm {
23 namespace bolt {
24 
25 using NodeId = CallGraph::NodeId;
26 using Arc = CallGraph::Arc;
27 using Node = CallGraph::Node;
28 
29 namespace {
30 class ClusterArc {
31 public:
ClusterArc(Cluster * Ca,Cluster * Cb,double W=0)32   ClusterArc(Cluster *Ca, Cluster *Cb, double W = 0)
33       : C1(std::min(Ca, Cb)), C2(std::max(Ca, Cb)), Weight(W) {}
34 
operator ==(const ClusterArc & Lhs,const ClusterArc & Rhs)35   friend bool operator==(const ClusterArc &Lhs, const ClusterArc &Rhs) {
36     return Lhs.C1 == Rhs.C1 && Lhs.C2 == Rhs.C2;
37   }
38 
39   Cluster *const C1;
40   Cluster *const C2;
41   mutable double Weight;
42 };
43 
44 class ClusterArcHash {
45 public:
operator ()(const ClusterArc & Arc) const46   int64_t operator()(const ClusterArc &Arc) const {
47     std::hash<int64_t> Hasher;
48     return hashCombine(Hasher(int64_t(Arc.C1)), int64_t(Arc.C2));
49   }
50 };
51 
52 using ClusterArcSet = std::unordered_set<ClusterArc, ClusterArcHash>;
53 
orderFuncs(const CallGraph & Cg,Cluster * C1,Cluster * C2)54 void orderFuncs(const CallGraph &Cg, Cluster *C1, Cluster *C2) {
55   NodeId C1head = C1->targets().front();
56   NodeId C1tail = C1->targets().back();
57   NodeId C2head = C2->targets().front();
58   NodeId C2tail = C2->targets().back();
59 
60   double C1headC2head = 0;
61   double C1headC2tail = 0;
62   double C1tailC2head = 0;
63   double C1tailC2tail = 0;
64 
65   for (const Arc &Arc : Cg.arcs()) {
66     if ((Arc.src() == C1head && Arc.dst() == C2head) ||
67         (Arc.dst() == C1head && Arc.src() == C2head))
68       C1headC2head += Arc.weight();
69     else if ((Arc.src() == C1head && Arc.dst() == C2tail) ||
70              (Arc.dst() == C1head && Arc.src() == C2tail))
71       C1headC2tail += Arc.weight();
72     else if ((Arc.src() == C1tail && Arc.dst() == C2head) ||
73              (Arc.dst() == C1tail && Arc.src() == C2head))
74       C1tailC2head += Arc.weight();
75     else if ((Arc.src() == C1tail && Arc.dst() == C2tail) ||
76              (Arc.dst() == C1tail && Arc.src() == C2tail))
77       C1tailC2tail += Arc.weight();
78   }
79 
80   const double Max = std::max(std::max(C1headC2head, C1headC2tail),
81                               std::max(C1tailC2head, C1tailC2tail));
82 
83   if (C1headC2head == Max) {
84     // flip C1
85     C1->reverseTargets();
86   } else if (C1headC2tail == Max) {
87     // flip C1 C2
88     C1->reverseTargets();
89     C2->reverseTargets();
90   } else if (C1tailC2tail == Max) {
91     // flip C2
92     C2->reverseTargets();
93   }
94 }
95 } // namespace
96 
pettisAndHansen(const CallGraph & Cg)97 std::vector<Cluster> pettisAndHansen(const CallGraph &Cg) {
98   // indexed by NodeId, keeps its current cluster
99   std::vector<Cluster *> FuncCluster(Cg.numNodes(), nullptr);
100   std::vector<Cluster> Clusters;
101   std::vector<NodeId> Funcs;
102 
103   Clusters.reserve(Cg.numNodes());
104 
105   for (NodeId F = 0; F < Cg.numNodes(); F++) {
106     if (Cg.samples(F) == 0)
107       continue;
108     Clusters.emplace_back(F, Cg.getNode(F));
109     FuncCluster[F] = &Clusters.back();
110     Funcs.push_back(F);
111   }
112 
113   ClusterArcSet Carcs;
114 
115   auto insertOrInc = [&](Cluster *C1, Cluster *C2, double Weight) {
116     auto Res = Carcs.emplace(C1, C2, Weight);
117     if (!Res.second)
118       Res.first->Weight += Weight;
119   };
120 
121   // Create a std::vector of cluster arcs
122 
123   for (const Arc &Arc : Cg.arcs()) {
124     if (Arc.weight() == 0)
125       continue;
126 
127     Cluster *const S = FuncCluster[Arc.src()];
128     Cluster *const D = FuncCluster[Arc.dst()];
129 
130     // ignore if s or d is nullptr
131 
132     if (S == nullptr || D == nullptr)
133       continue;
134 
135     // ignore self-edges
136 
137     if (S == D)
138       continue;
139 
140     insertOrInc(S, D, Arc.weight());
141   }
142 
143   // Find an arc with max weight and merge its nodes
144 
145   while (!Carcs.empty()) {
146     auto Maxpos =
147         std::max_element(Carcs.begin(), Carcs.end(),
148                          [&](const ClusterArc &Carc1, const ClusterArc &Carc2) {
149                            return Carc1.Weight < Carc2.Weight;
150                          });
151 
152     ClusterArc Max = *Maxpos;
153     Carcs.erase(Maxpos);
154 
155     Cluster *const C1 = Max.C1;
156     Cluster *const C2 = Max.C2;
157 
158     if (C1->size() + C2->size() > MaxClusterSize)
159       continue;
160 
161     if (C1->frozen() || C2->frozen())
162       continue;
163 
164     // order functions and merge cluster
165 
166     orderFuncs(Cg, C1, C2);
167 
168     LLVM_DEBUG(dbgs() << format("merging %s -> %s: %.1f\n",
169                                 C2->toString().c_str(), C1->toString().c_str(),
170                                 Max.Weight));
171 
172     // update carcs: merge C1arcs to C2arcs
173 
174     std::unordered_map<ClusterArc, Cluster *, ClusterArcHash> C2arcs;
175     for (const ClusterArc &Carc : Carcs) {
176       if (Carc.C1 == C2)
177         C2arcs.emplace(Carc, Carc.C2);
178       if (Carc.C2 == C2)
179         C2arcs.emplace(Carc, Carc.C1);
180     }
181 
182     for (auto It : C2arcs) {
183       Cluster *const C = It.second;
184       ClusterArc const C2arc = It.first;
185 
186       insertOrInc(C, C1, C2arc.Weight);
187       Carcs.erase(C2arc);
188     }
189 
190     // update FuncCluster
191 
192     for (NodeId F : C2->targets())
193       FuncCluster[F] = C1;
194 
195     C1->merge(*C2, Max.Weight);
196     C2->clear();
197   }
198 
199   // Return the set of Clusters that are left, which are the ones that
200   // didn't get merged.
201 
202   std::set<Cluster *> LiveClusters;
203   std::vector<Cluster> OutClusters;
204 
205   for (NodeId Fid : Funcs)
206     LiveClusters.insert(FuncCluster[Fid]);
207   for (Cluster *C : LiveClusters)
208     OutClusters.push_back(std::move(*C));
209 
210   llvm::sort(OutClusters, compareClustersDensity);
211 
212   return OutClusters;
213 }
214 
215 } // namespace bolt
216 } // namespace llvm
217