Skip to content

Commit e405e02

Browse files
committed
[CGData][OutlinedHashTree] Define OutlinedHashTree
This defines the OutlinedHashTree class. It contains sequences of stable hash values of instructions that have been outlined. This OutlinedHashTree can be used to track the outlined instruction sequences across modules. A trie structure is used in its implementation, allowing for a compact sharing of common prefixes.
1 parent b6824c9 commit e405e02

10 files changed

+699
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//===- OutlinedHashTree.h --------------------------------------*- C++ -*-===//
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+
// This defines the OutlinedHashTree class. It contains sequences of stable
10+
// hash values of instructions that have been outlined. This OutlinedHashTree
11+
// can be used to track the outlined instruction sequences across modules.
12+
//
13+
//===---------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CODEGENDATA_OUTLINEDHASHTREE_H
16+
#define LLVM_CODEGENDATA_OUTLINEDHASHTREE_H
17+
18+
#include "llvm/ADT/StableHashing.h"
19+
#include "llvm/ObjectYAML/YAML.h"
20+
#include "llvm/Support/raw_ostream.h"
21+
22+
#include <unordered_map>
23+
#include <vector>
24+
25+
namespace llvm {
26+
27+
/// A HashNode is an entry in an OutlinedHashTree, holding a hash value
28+
/// and a collection of Successors (other HashNodes). If a HashNode has
29+
/// a positive terminal value (Terminals > 0), it signifies the end of
30+
/// a hash sequence with that occurrence count.
31+
struct HashNode {
32+
/// The hash value of the node.
33+
stable_hash Hash;
34+
/// The number of terminals in the sequence ending at this node.
35+
unsigned Terminals;
36+
/// The successors of this node.
37+
std::unordered_map<stable_hash, std::unique_ptr<HashNode>> Successors;
38+
};
39+
40+
/// HashNodeStable is the serialized, stable, and compact representation
41+
/// of a HashNode.
42+
struct HashNodeStable {
43+
llvm::yaml::Hex64 Hash;
44+
unsigned Terminals;
45+
std::vector<unsigned> SuccessorIds;
46+
};
47+
48+
class OutlinedHashTree {
49+
50+
using EdgeCallbackFn =
51+
std::function<void(const HashNode *, const HashNode *)>;
52+
using NodeCallbackFn = std::function<void(const HashNode *)>;
53+
54+
using HashSequence = std::vector<stable_hash>;
55+
using HashSequencePair = std::pair<std::vector<stable_hash>, unsigned>;
56+
57+
/// Walks every edge and node in the OutlinedHashTree and calls CallbackEdge
58+
/// for the edges and CallbackNode for the nodes with the stable_hash for
59+
/// the source and the stable_hash of the sink for an edge. These generic
60+
/// callbacks can be used to traverse a OutlinedHashTree for the purpose of
61+
/// print debugging or serializing it.
62+
void walkGraph(EdgeCallbackFn CallbackEdge,
63+
NodeCallbackFn CallbackNode) const;
64+
65+
public:
66+
/// Walks the nodes of a OutlinedHashTree using walkGraph.
67+
void walkVertices(NodeCallbackFn Callback) const {
68+
walkGraph([](const HashNode *A, const HashNode *B) {}, Callback);
69+
}
70+
71+
/// Release all hash nodes except the root hash node.
72+
void clear() {
73+
assert(getRoot()->Hash == 0 && getRoot()->Terminals == 0);
74+
getRoot()->Successors.clear();
75+
}
76+
77+
/// \returns true if the hash tree has only the root node.
78+
bool empty() { return size() == 1; }
79+
80+
/// \returns the size of a OutlinedHashTree by traversing it. If
81+
/// \p GetTerminalCountOnly is true, it only counts the terminal nodes
82+
/// (meaning it returns the size of the number of hash sequences in a
83+
/// OutlinedHashTree).
84+
size_t size(bool GetTerminalCountOnly = false) const {
85+
size_t Size = 0;
86+
walkVertices([&Size, GetTerminalCountOnly](const HashNode *N) {
87+
Size += (N && (!GetTerminalCountOnly || N->Terminals));
88+
});
89+
return Size;
90+
}
91+
92+
/// \returns the depth of a OutlinedHashTree by traversing it.
93+
size_t depth() const {
94+
size_t Size = 0;
95+
std::unordered_map<const HashNode *, size_t> DepthMap;
96+
97+
walkGraph(
98+
[&DepthMap](const HashNode *Src, const HashNode *Dst) {
99+
size_t Depth = DepthMap[Src];
100+
DepthMap[Dst] = Depth + 1;
101+
},
102+
[&Size, &DepthMap](const HashNode *N) {
103+
Size = std::max(Size, DepthMap[N]);
104+
});
105+
106+
return Size;
107+
}
108+
109+
/// \returns the root hash node of a OutlinedHashTree.
110+
const HashNode *getRoot() const { return Root.get(); }
111+
HashNode *getRoot() { return Root.get(); }
112+
113+
/// Inserts a \p Sequence into the this tree. The last node in the sequence
114+
/// will increase Terminals.
115+
void insert(const HashSequencePair &SequencePair);
116+
117+
/// Merge a \p OtherTree into this Tree.
118+
void merge(const OutlinedHashTree *OtherTree);
119+
120+
/// \returns the matching count if \p Sequence exists in a OutlinedHashTree.
121+
unsigned find(const HashSequence &Sequence) const;
122+
123+
OutlinedHashTree() { Root = std::make_unique<HashNode>(); }
124+
125+
private:
126+
std::unique_ptr<HashNode> Root;
127+
};
128+
129+
} // namespace llvm
130+
131+
#endif
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===- OutlinedHashTreeRecord.h --------------------------------*- C++ -*-===//
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+
// This defines the OutlinedHashTreeRecord class. This class holds the outlined
10+
// hash tree for both serialization and deserialization processes. It utilizes
11+
// two data formats for serialization: raw binary data and YAML.
12+
// These two formats can be used interchangeably.
13+
//
14+
//===---------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_CODEGENDATA_OUTLINEDHASHTREERECORD_H
17+
#define LLVM_CODEGENDATA_OUTLINEDHASHTREERECORD_H
18+
19+
#include "llvm/CodeGenData/OutlinedHashTree.h"
20+
21+
namespace llvm {
22+
23+
using IdHashNodeStableMapTy = std::map<unsigned, HashNodeStable>;
24+
using IdHashNodeMapTy = std::map<unsigned, HashNode *>;
25+
using HashNodeIdMapTy = std::unordered_map<const HashNode *, unsigned>;
26+
27+
struct OutlinedHashTreeRecord {
28+
std::unique_ptr<OutlinedHashTree> HashTree;
29+
30+
OutlinedHashTreeRecord() { HashTree = std::make_unique<OutlinedHashTree>(); }
31+
OutlinedHashTreeRecord(std::unique_ptr<OutlinedHashTree> HashTree)
32+
: HashTree(std::move(HashTree)){};
33+
34+
/// Serialize the outlined hash tree to a raw_ostream.
35+
void serialize(raw_ostream &OS) const;
36+
/// Deserialize the outlined hash tree from a raw_ostream.
37+
void deserialize(const unsigned char *&Ptr);
38+
/// Serialize the outlined hash tree to a YAML stream.
39+
void serializeYAML(yaml::Output &YOS) const;
40+
/// Deserialize the outlined hash tree from a YAML stream.
41+
void deserializeYAML(yaml::Input &YIS);
42+
43+
/// Merge the other outlined hash tree into this one.
44+
void merge(const OutlinedHashTreeRecord &Other) {
45+
HashTree->merge(Other.HashTree.get());
46+
}
47+
48+
/// \returns true if the outlined hash tree is empty.
49+
bool empty() const { return HashTree->empty(); }
50+
51+
/// Print the outlined hash tree in a YAML format.
52+
void print(raw_ostream &OS = llvm::errs()) const {
53+
yaml::Output YOS(OS);
54+
serializeYAML(YOS);
55+
}
56+
57+
private:
58+
/// Convert the outlined hash tree to stable data.
59+
void convertToStableData(IdHashNodeStableMapTy &IdNodeStableMap) const;
60+
61+
/// Convert the stable data back to the outlined hash tree.
62+
void convertFromStableData(const IdHashNodeStableMapTy &IdNodeStableMap);
63+
};
64+
65+
} // end namespace llvm
66+
67+
#endif // LLVM_CODEGENDATA_OUTLINEDHASHTREERECORD_H

llvm/lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_subdirectory(InterfaceStub)
1010
add_subdirectory(IRPrinter)
1111
add_subdirectory(IRReader)
1212
add_subdirectory(CodeGen)
13+
add_subdirectory(CodeGenData)
1314
add_subdirectory(CodeGenTypes)
1415
add_subdirectory(BinaryFormat)
1516
add_subdirectory(Bitcode)

llvm/lib/CodeGenData/CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
add_llvm_component_library(LLVMCodeGenData
2+
OutlinedHashTree.cpp
3+
OutlinedHashTreeRecord.cpp
4+
5+
ADDITIONAL_HEADER_DIRS
6+
${LLVM_MAIN_INCLUDE_DIR}/llvm/CodeGenData
7+
8+
DEPENDS
9+
intrinsics_gen
10+
11+
LINK_COMPONENTS
12+
Core
13+
Support
14+
)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//===-- OutlinedHashTree.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+
// An OutlinedHashTree is a Trie that contains sequences of stable hash values
10+
// of instructions that have been outlined. This OutlinedHashTree can be used
11+
// to understand the outlined instruction sequences collected across modules.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#include "llvm/CodeGenData/OutlinedHashTree.h"
16+
17+
#include <stack>
18+
#include <tuple>
19+
20+
#define DEBUG_TYPE "outlined-hash-tree"
21+
22+
using namespace llvm;
23+
24+
void OutlinedHashTree::walkGraph(EdgeCallbackFn CallbackEdge,
25+
NodeCallbackFn CallbackNode) const {
26+
std::stack<const HashNode *> Stack;
27+
Stack.push(getRoot());
28+
29+
while (!Stack.empty()) {
30+
const auto *Current = Stack.top();
31+
Stack.pop();
32+
CallbackNode(Current);
33+
34+
// Sorted walk for the stable output.
35+
std::map<stable_hash, const HashNode *> SortedSuccessors;
36+
for (const auto &P : Current->Successors)
37+
SortedSuccessors[P.first] = P.second.get();
38+
39+
for (const auto &P : SortedSuccessors) {
40+
CallbackEdge(Current, P.second);
41+
Stack.push(P.second);
42+
}
43+
}
44+
}
45+
46+
void OutlinedHashTree::insert(const HashSequencePair &SequencePair) {
47+
const auto &Sequence = SequencePair.first;
48+
unsigned Count = SequencePair.second;
49+
50+
HashNode *Current = getRoot();
51+
for (stable_hash StableHash : Sequence) {
52+
auto I = Current->Successors.find(StableHash);
53+
if (I == Current->Successors.end()) {
54+
std::unique_ptr<HashNode> Next = std::make_unique<HashNode>();
55+
HashNode *NextPtr = Next.get();
56+
NextPtr->Hash = StableHash;
57+
Current->Successors.emplace(StableHash, std::move(Next));
58+
Current = NextPtr;
59+
continue;
60+
}
61+
Current = I->second.get();
62+
}
63+
Current->Terminals += Count;
64+
}
65+
66+
void OutlinedHashTree::merge(const OutlinedHashTree *Tree) {
67+
HashNode *Dst = getRoot();
68+
const HashNode *Src = Tree->getRoot();
69+
70+
std::stack<std::pair<HashNode *, const HashNode *>> Stack;
71+
Stack.push({Dst, Src});
72+
73+
while (!Stack.empty()) {
74+
auto [DstNode, SrcNode] = Stack.top();
75+
Stack.pop();
76+
77+
if (!SrcNode)
78+
continue;
79+
DstNode->Terminals += SrcNode->Terminals;
80+
81+
for (auto &[Hash, NextSrcNode] : SrcNode->Successors) {
82+
HashNode *NextDstNode;
83+
auto I = DstNode->Successors.find(Hash);
84+
if (I == DstNode->Successors.end()) {
85+
auto NextDst = std::make_unique<HashNode>();
86+
NextDstNode = NextDst.get();
87+
NextDstNode->Hash = Hash;
88+
DstNode->Successors.emplace(Hash, std::move(NextDst));
89+
} else
90+
NextDstNode = I->second.get();
91+
92+
Stack.push({NextDstNode, NextSrcNode.get()});
93+
}
94+
}
95+
}
96+
97+
unsigned OutlinedHashTree::find(const HashSequence &Sequence) const {
98+
const HashNode *Current = getRoot();
99+
for (stable_hash StableHash : Sequence) {
100+
const auto I = Current->Successors.find(StableHash);
101+
if (I == Current->Successors.end())
102+
return 0;
103+
Current = I->second.get();
104+
}
105+
return Current->Terminals;
106+
}

0 commit comments

Comments
 (0)