blob: e8d53bbe383f1270080ff74f86e9a1d659bfd934 [file] [log] [blame]
//===- bolt/Passes/CallGraph.h ----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef BOLT_PASSES_CALLGRAPH_H
#define BOLT_PASSES_CALLGRAPH_H
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace llvm {
namespace bolt {
// TODO: find better place for this
inline int64_t hashCombine(const int64_t Seed, const int64_t Val) {
std::hash<int64_t> Hasher;
return Seed ^ (Hasher(Val) + 0x9e3779b9 + (Seed << 6) + (Seed >> 2));
}
/// A call graph class.
class CallGraph {
public:
using NodeId = size_t;
static constexpr NodeId InvalidId = -1;
template <typename T> class iterator_range {
T Begin;
T End;
public:
template <typename Container>
iterator_range(Container &&c) : Begin(c.begin()), End(c.end()) {}
iterator_range(T Begin, T End)
: Begin(std::move(Begin)), End(std::move(End)) {}
T begin() const { return Begin; }
T end() const { return End; }
};
class Arc {
public:
struct Hash {
int64_t operator()(const Arc &Arc) const;
};
Arc(NodeId S, NodeId D, double W = 0) : Src(S), Dst(D), Weight(W) {}
Arc(const Arc &) = delete;
friend bool operator==(const Arc &Lhs, const Arc &Rhs) {
return Lhs.Src == Rhs.Src && Lhs.Dst == Rhs.Dst;
}
NodeId src() const { return Src; }
NodeId dst() const { return Dst; }
double weight() const { return Weight; }
double avgCallOffset() const { return AvgCallOffset; }
double normalizedWeight() const { return NormalizedWeight; }
private:
friend class CallGraph;
NodeId Src{InvalidId};
NodeId Dst{InvalidId};
mutable double Weight{0};
mutable double NormalizedWeight{0};
mutable double AvgCallOffset{0};
};
using ArcsType = std::unordered_set<Arc, Arc::Hash>;
using ArcIterator = ArcsType::iterator;
using ArcConstIterator = ArcsType::const_iterator;
class Node {
public:
explicit Node(uint32_t Size, uint64_t Samples = 0)
: Size(Size), Samples(Samples) {}
uint32_t size() const { return Size; }
uint64_t samples() const { return Samples; }
const std::vector<NodeId> &successors() const { return Succs; }
const std::vector<NodeId> &predecessors() const { return Preds; }
private:
friend class CallGraph;
uint32_t Size;
uint64_t Samples;
// preds and succs contain no duplicate elements and self arcs are not
// allowed
std::vector<NodeId> Preds;
std::vector<NodeId> Succs;
};
size_t numNodes() const { return Nodes.size(); }
size_t numArcs() const { return Arcs.size(); }
const Node &getNode(const NodeId Id) const {
assert(Id < Nodes.size());
return Nodes[Id];
}
uint32_t size(const NodeId Id) const {
assert(Id < Nodes.size());
return Nodes[Id].Size;
}
uint64_t samples(const NodeId Id) const {
assert(Id < Nodes.size());
return Nodes[Id].Samples;
}
const std::vector<NodeId> &successors(const NodeId Id) const {
assert(Id < Nodes.size());
return Nodes[Id].Succs;
}
const std::vector<NodeId> &predecessors(const NodeId Id) const {
assert(Id < Nodes.size());
return Nodes[Id].Preds;
}
NodeId addNode(uint32_t Size, uint64_t Samples = 0);
const Arc &incArcWeight(NodeId Src, NodeId Dst, double W = 1.0,
double Offset = 0.0);
ArcIterator findArc(NodeId Src, NodeId Dst) {
return Arcs.find(Arc(Src, Dst));
}
ArcConstIterator findArc(NodeId Src, NodeId Dst) const {
return Arcs.find(Arc(Src, Dst));
}
iterator_range<ArcConstIterator> arcs() const {
return iterator_range<ArcConstIterator>(Arcs.begin(), Arcs.end());
}
iterator_range<std::vector<Node>::const_iterator> nodes() const {
return iterator_range<std::vector<Node>::const_iterator>(Nodes.begin(),
Nodes.end());
}
double density() const {
return double(Arcs.size()) / (Nodes.size() * Nodes.size());
}
// Initialize NormalizedWeight field for every arc
void normalizeArcWeights();
// Make sure that the sum of incoming arc weights is at least the number of
// samples for every node
void adjustArcWeights();
template <typename L> void printDot(char *fileName, L getLabel) const;
private:
void setSamples(const NodeId Id, uint64_t Samples) {
assert(Id < Nodes.size());
Nodes[Id].Samples = Samples;
}
std::vector<Node> Nodes;
ArcsType Arcs;
};
template <class L> void CallGraph::printDot(char *FileName, L GetLabel) const {
std::error_code EC;
raw_fd_ostream OS(std::string(FileName), EC, sys::fs::OF_None);
if (EC)
return;
OS << "digraph g {\n";
for (NodeId F = 0; F < Nodes.size(); F++) {
if (Nodes[F].samples() == 0)
continue;
OS << "f" << F << " [label=\"" << GetLabel(F)
<< "\\nsamples=" << Nodes[F].samples() << "\\nsize=" << Nodes[F].size()
<< "\"];\n";
}
for (NodeId F = 0; F < Nodes.size(); F++) {
if (Nodes[F].samples() == 0)
continue;
for (NodeId Dst : Nodes[F].successors()) {
ArcConstIterator Arc = findArc(F, Dst);
OS << "f" << F << " -> f" << Dst
<< " [label=\"normWgt=" << format("%.3lf", Arc->normalizedWeight())
<< ",weight=" << format("%.0lf", Arc->weight())
<< ",callOffset=" << format("%.1lf", Arc->avgCallOffset()) << "\"];\n";
}
}
OS << "}\n";
}
} // namespace bolt
} // namespace llvm
#endif