1*0fca6ea1SDimitry Andric //===-- OutlinedHashTree.cpp ----------------------------------------------===//
2*0fca6ea1SDimitry Andric //
3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0fca6ea1SDimitry Andric //
7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
8*0fca6ea1SDimitry Andric //
9*0fca6ea1SDimitry Andric // An OutlinedHashTree is a Trie that contains sequences of stable hash values
10*0fca6ea1SDimitry Andric // of instructions that have been outlined. This OutlinedHashTree can be used
11*0fca6ea1SDimitry Andric // to understand the outlined instruction sequences collected across modules.
12*0fca6ea1SDimitry Andric //
13*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
14*0fca6ea1SDimitry Andric
15*0fca6ea1SDimitry Andric #include "llvm/CodeGenData/OutlinedHashTree.h"
16*0fca6ea1SDimitry Andric
17*0fca6ea1SDimitry Andric #define DEBUG_TYPE "outlined-hash-tree"
18*0fca6ea1SDimitry Andric
19*0fca6ea1SDimitry Andric using namespace llvm;
20*0fca6ea1SDimitry Andric
walkGraph(NodeCallbackFn CallbackNode,EdgeCallbackFn CallbackEdge,bool SortedWalk) const21*0fca6ea1SDimitry Andric void OutlinedHashTree::walkGraph(NodeCallbackFn CallbackNode,
22*0fca6ea1SDimitry Andric EdgeCallbackFn CallbackEdge,
23*0fca6ea1SDimitry Andric bool SortedWalk) const {
24*0fca6ea1SDimitry Andric SmallVector<const HashNode *> Stack;
25*0fca6ea1SDimitry Andric Stack.emplace_back(getRoot());
26*0fca6ea1SDimitry Andric
27*0fca6ea1SDimitry Andric while (!Stack.empty()) {
28*0fca6ea1SDimitry Andric const auto *Current = Stack.pop_back_val();
29*0fca6ea1SDimitry Andric if (CallbackNode)
30*0fca6ea1SDimitry Andric CallbackNode(Current);
31*0fca6ea1SDimitry Andric
32*0fca6ea1SDimitry Andric auto HandleNext = [&](const HashNode *Next) {
33*0fca6ea1SDimitry Andric if (CallbackEdge)
34*0fca6ea1SDimitry Andric CallbackEdge(Current, Next);
35*0fca6ea1SDimitry Andric Stack.emplace_back(Next);
36*0fca6ea1SDimitry Andric };
37*0fca6ea1SDimitry Andric if (SortedWalk) {
38*0fca6ea1SDimitry Andric SmallVector<std::pair<stable_hash, const HashNode *>> SortedSuccessors;
39*0fca6ea1SDimitry Andric for (const auto &[Hash, Successor] : Current->Successors)
40*0fca6ea1SDimitry Andric SortedSuccessors.emplace_back(Hash, Successor.get());
41*0fca6ea1SDimitry Andric llvm::sort(SortedSuccessors);
42*0fca6ea1SDimitry Andric for (const auto &P : SortedSuccessors)
43*0fca6ea1SDimitry Andric HandleNext(P.second);
44*0fca6ea1SDimitry Andric } else {
45*0fca6ea1SDimitry Andric for (const auto &P : Current->Successors)
46*0fca6ea1SDimitry Andric HandleNext(P.second.get());
47*0fca6ea1SDimitry Andric }
48*0fca6ea1SDimitry Andric }
49*0fca6ea1SDimitry Andric }
50*0fca6ea1SDimitry Andric
size(bool GetTerminalCountOnly) const51*0fca6ea1SDimitry Andric size_t OutlinedHashTree::size(bool GetTerminalCountOnly) const {
52*0fca6ea1SDimitry Andric size_t Size = 0;
53*0fca6ea1SDimitry Andric walkGraph([&Size, GetTerminalCountOnly](const HashNode *N) {
54*0fca6ea1SDimitry Andric Size += (N && (!GetTerminalCountOnly || N->Terminals));
55*0fca6ea1SDimitry Andric });
56*0fca6ea1SDimitry Andric return Size;
57*0fca6ea1SDimitry Andric }
58*0fca6ea1SDimitry Andric
depth() const59*0fca6ea1SDimitry Andric size_t OutlinedHashTree::depth() const {
60*0fca6ea1SDimitry Andric size_t Size = 0;
61*0fca6ea1SDimitry Andric DenseMap<const HashNode *, size_t> DepthMap;
62*0fca6ea1SDimitry Andric walkGraph([&Size, &DepthMap](
63*0fca6ea1SDimitry Andric const HashNode *N) { Size = std::max(Size, DepthMap[N]); },
64*0fca6ea1SDimitry Andric [&DepthMap](const HashNode *Src, const HashNode *Dst) {
65*0fca6ea1SDimitry Andric size_t Depth = DepthMap[Src];
66*0fca6ea1SDimitry Andric DepthMap[Dst] = Depth + 1;
67*0fca6ea1SDimitry Andric });
68*0fca6ea1SDimitry Andric return Size;
69*0fca6ea1SDimitry Andric }
70*0fca6ea1SDimitry Andric
insert(const HashSequencePair & SequencePair)71*0fca6ea1SDimitry Andric void OutlinedHashTree::insert(const HashSequencePair &SequencePair) {
72*0fca6ea1SDimitry Andric auto &[Sequence, Count] = SequencePair;
73*0fca6ea1SDimitry Andric HashNode *Current = getRoot();
74*0fca6ea1SDimitry Andric
75*0fca6ea1SDimitry Andric for (stable_hash StableHash : Sequence) {
76*0fca6ea1SDimitry Andric auto I = Current->Successors.find(StableHash);
77*0fca6ea1SDimitry Andric if (I == Current->Successors.end()) {
78*0fca6ea1SDimitry Andric std::unique_ptr<HashNode> Next = std::make_unique<HashNode>();
79*0fca6ea1SDimitry Andric HashNode *NextPtr = Next.get();
80*0fca6ea1SDimitry Andric NextPtr->Hash = StableHash;
81*0fca6ea1SDimitry Andric Current->Successors.emplace(StableHash, std::move(Next));
82*0fca6ea1SDimitry Andric Current = NextPtr;
83*0fca6ea1SDimitry Andric } else
84*0fca6ea1SDimitry Andric Current = I->second.get();
85*0fca6ea1SDimitry Andric }
86*0fca6ea1SDimitry Andric if (Count)
87*0fca6ea1SDimitry Andric Current->Terminals = (Current->Terminals ? *Current->Terminals : 0) + Count;
88*0fca6ea1SDimitry Andric }
89*0fca6ea1SDimitry Andric
merge(const OutlinedHashTree * Tree)90*0fca6ea1SDimitry Andric void OutlinedHashTree::merge(const OutlinedHashTree *Tree) {
91*0fca6ea1SDimitry Andric HashNode *Dst = getRoot();
92*0fca6ea1SDimitry Andric const HashNode *Src = Tree->getRoot();
93*0fca6ea1SDimitry Andric SmallVector<std::pair<HashNode *, const HashNode *>> Stack;
94*0fca6ea1SDimitry Andric Stack.emplace_back(Dst, Src);
95*0fca6ea1SDimitry Andric
96*0fca6ea1SDimitry Andric while (!Stack.empty()) {
97*0fca6ea1SDimitry Andric auto [DstNode, SrcNode] = Stack.pop_back_val();
98*0fca6ea1SDimitry Andric if (!SrcNode)
99*0fca6ea1SDimitry Andric continue;
100*0fca6ea1SDimitry Andric if (SrcNode->Terminals)
101*0fca6ea1SDimitry Andric DstNode->Terminals =
102*0fca6ea1SDimitry Andric (DstNode->Terminals ? *DstNode->Terminals : 0) + *SrcNode->Terminals;
103*0fca6ea1SDimitry Andric for (auto &[Hash, NextSrcNode] : SrcNode->Successors) {
104*0fca6ea1SDimitry Andric HashNode *NextDstNode;
105*0fca6ea1SDimitry Andric auto I = DstNode->Successors.find(Hash);
106*0fca6ea1SDimitry Andric if (I == DstNode->Successors.end()) {
107*0fca6ea1SDimitry Andric auto NextDst = std::make_unique<HashNode>();
108*0fca6ea1SDimitry Andric NextDstNode = NextDst.get();
109*0fca6ea1SDimitry Andric NextDstNode->Hash = Hash;
110*0fca6ea1SDimitry Andric DstNode->Successors.emplace(Hash, std::move(NextDst));
111*0fca6ea1SDimitry Andric } else
112*0fca6ea1SDimitry Andric NextDstNode = I->second.get();
113*0fca6ea1SDimitry Andric
114*0fca6ea1SDimitry Andric Stack.emplace_back(NextDstNode, NextSrcNode.get());
115*0fca6ea1SDimitry Andric }
116*0fca6ea1SDimitry Andric }
117*0fca6ea1SDimitry Andric }
118*0fca6ea1SDimitry Andric
119*0fca6ea1SDimitry Andric std::optional<unsigned>
find(const HashSequence & Sequence) const120*0fca6ea1SDimitry Andric OutlinedHashTree::find(const HashSequence &Sequence) const {
121*0fca6ea1SDimitry Andric const HashNode *Current = getRoot();
122*0fca6ea1SDimitry Andric for (stable_hash StableHash : Sequence) {
123*0fca6ea1SDimitry Andric const auto I = Current->Successors.find(StableHash);
124*0fca6ea1SDimitry Andric if (I == Current->Successors.end())
125*0fca6ea1SDimitry Andric return 0;
126*0fca6ea1SDimitry Andric Current = I->second.get();
127*0fca6ea1SDimitry Andric }
128*0fca6ea1SDimitry Andric return Current->Terminals;
129*0fca6ea1SDimitry Andric }
130