1 //===- bolt/Passes/PettisAndHansen.cpp ------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // The file implements Pettis and Hansen code-layout algorithm.
11 //===----------------------------------------------------------------------===//
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"
18 #include <unordered_map>
20 #define DEBUG_TYPE "hfsort"
25 using NodeId
= CallGraph::NodeId
;
26 using Arc
= CallGraph::Arc
;
27 using Node
= CallGraph::Node
;
32 ClusterArc(Cluster
*Ca
, Cluster
*Cb
, double W
= 0)
33 : C1(std::min(Ca
, Cb
)), C2(std::max(Ca
, Cb
)), Weight(W
) {}
35 friend bool operator==(const ClusterArc
&Lhs
, const ClusterArc
&Rhs
) {
36 return Lhs
.C1
== Rhs
.C1
&& Lhs
.C2
== Rhs
.C2
;
41 mutable double Weight
;
44 class ClusterArcHash
{
46 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
));
52 using ClusterArcSet
= std::unordered_set
<ClusterArc
, ClusterArcHash
>;
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();
60 double C1headC2head
= 0;
61 double C1headC2tail
= 0;
62 double C1tailC2head
= 0;
63 double C1tailC2tail
= 0;
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();
80 const double Max
= std::max(std::max(C1headC2head
, C1headC2tail
),
81 std::max(C1tailC2head
, C1tailC2tail
));
83 if (C1headC2head
== Max
) {
86 } else if (C1headC2tail
== Max
) {
90 } else if (C1tailC2tail
== Max
) {
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
;
103 Clusters
.reserve(Cg
.numNodes());
105 for (NodeId F
= 0; F
< Cg
.numNodes(); F
++) {
106 if (Cg
.samples(F
) == 0)
108 Clusters
.emplace_back(F
, Cg
.getNode(F
));
109 FuncCluster
[F
] = &Clusters
.back();
115 auto insertOrInc
= [&](Cluster
*C1
, Cluster
*C2
, double Weight
) {
116 auto Res
= Carcs
.emplace(C1
, C2
, Weight
);
118 Res
.first
->Weight
+= Weight
;
121 // Create a std::vector of cluster arcs
123 for (const Arc
&Arc
: Cg
.arcs()) {
124 if (Arc
.weight() == 0)
127 Cluster
*const S
= FuncCluster
[Arc
.src()];
128 Cluster
*const D
= FuncCluster
[Arc
.dst()];
130 // ignore if s or d is nullptr
132 if (S
== nullptr || D
== nullptr)
140 insertOrInc(S
, D
, Arc
.weight());
143 // Find an arc with max weight and merge its nodes
145 while (!Carcs
.empty()) {
147 std::max_element(Carcs
.begin(), Carcs
.end(),
148 [&](const ClusterArc
&Carc1
, const ClusterArc
&Carc2
) {
149 return Carc1
.Weight
< Carc2
.Weight
;
152 ClusterArc Max
= *Maxpos
;
155 Cluster
*const C1
= Max
.C1
;
156 Cluster
*const C2
= Max
.C2
;
158 if (C1
->size() + C2
->size() > MaxClusterSize
)
161 if (C1
->frozen() || C2
->frozen())
164 // order functions and merge cluster
166 orderFuncs(Cg
, C1
, C2
);
168 LLVM_DEBUG(dbgs() << format("merging %s -> %s: %.1f\n",
169 C2
->toString().c_str(), C1
->toString().c_str(),
172 // update carcs: merge C1arcs to C2arcs
174 std::unordered_map
<ClusterArc
, Cluster
*, ClusterArcHash
> C2arcs
;
175 for (const ClusterArc
&Carc
: Carcs
) {
177 C2arcs
.emplace(Carc
, Carc
.C2
);
179 C2arcs
.emplace(Carc
, Carc
.C1
);
182 for (auto It
: C2arcs
) {
183 Cluster
*const C
= It
.second
;
184 ClusterArc
const C2arc
= It
.first
;
186 insertOrInc(C
, C1
, C2arc
.Weight
);
190 // update FuncCluster
192 for (NodeId F
: C2
->targets())
195 C1
->merge(*C2
, Max
.Weight
);
199 // Return the set of Clusters that are left, which are the ones that
200 // didn't get merged.
202 std::set
<Cluster
*> LiveClusters
;
203 std::vector
<Cluster
> OutClusters
;
205 for (NodeId Fid
: Funcs
)
206 LiveClusters
.insert(FuncCluster
[Fid
]);
207 for (Cluster
*C
: LiveClusters
)
208 OutClusters
.push_back(std::move(*C
));
210 llvm::sort(OutClusters
, compareClustersDensity
);