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