| //===-- OutlinedHashTree.cpp ----------------------------------------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // An OutlinedHashTree is a Trie that contains sequences of stable hash values | |
| // of instructions that have been outlined. This OutlinedHashTree can be used | |
| // to understand the outlined instruction sequences collected across modules. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CGData/OutlinedHashTree.h" | |
| #define DEBUG_TYPE "outlined-hash-tree" | |
| using namespace llvm; | |
| void OutlinedHashTree::walkGraph(NodeCallbackFn CallbackNode, | |
| EdgeCallbackFn CallbackEdge, | |
| bool SortedWalk) const { | |
| SmallVector<const HashNode *> Stack; | |
| Stack.emplace_back(getRoot()); | |
| while (!Stack.empty()) { | |
| const auto *Current = Stack.pop_back_val(); | |
| if (CallbackNode) | |
| CallbackNode(Current); | |
| auto HandleNext = [&](const HashNode *Next) { | |
| if (CallbackEdge) | |
| CallbackEdge(Current, Next); | |
| Stack.emplace_back(Next); | |
| }; | |
| if (SortedWalk) { | |
| SmallVector<std::pair<stable_hash, const HashNode *>> SortedSuccessors; | |
| for (const auto &[Hash, Successor] : Current->Successors) | |
| SortedSuccessors.emplace_back(Hash, Successor.get()); | |
| llvm::sort(SortedSuccessors); | |
| for (const auto &P : SortedSuccessors) | |
| HandleNext(P.second); | |
| } else { | |
| for (const auto &P : Current->Successors) | |
| HandleNext(P.second.get()); | |
| } | |
| } | |
| } | |
| size_t OutlinedHashTree::size(bool GetTerminalCountOnly) const { | |
| size_t Size = 0; | |
| walkGraph([&Size, GetTerminalCountOnly](const HashNode *N) { | |
| Size += (N && (!GetTerminalCountOnly || N->Terminals)); | |
| }); | |
| return Size; | |
| } | |
| size_t OutlinedHashTree::depth() const { | |
| size_t Size = 0; | |
| DenseMap<const HashNode *, size_t> DepthMap; | |
| walkGraph([&Size, &DepthMap]( | |
| const HashNode *N) { Size = std::max(Size, DepthMap[N]); }, | |
| [&DepthMap](const HashNode *Src, const HashNode *Dst) { | |
| size_t Depth = DepthMap[Src]; | |
| DepthMap[Dst] = Depth + 1; | |
| }); | |
| return Size; | |
| } | |
| void OutlinedHashTree::insert(const HashSequencePair &SequencePair) { | |
| auto &[Sequence, Count] = SequencePair; | |
| HashNode *Current = getRoot(); | |
| for (stable_hash StableHash : Sequence) { | |
| auto I = Current->Successors.find(StableHash); | |
| if (I == Current->Successors.end()) { | |
| std::unique_ptr<HashNode> Next = std::make_unique<HashNode>(); | |
| HashNode *NextPtr = Next.get(); | |
| NextPtr->Hash = StableHash; | |
| Current->Successors.try_emplace(StableHash, std::move(Next)); | |
| Current = NextPtr; | |
| } else | |
| Current = I->second.get(); | |
| } | |
| if (Count) | |
| Current->Terminals = Current->Terminals.value_or(0) + Count; | |
| } | |
| void OutlinedHashTree::merge(const OutlinedHashTree *Tree) { | |
| HashNode *Dst = getRoot(); | |
| const HashNode *Src = Tree->getRoot(); | |
| SmallVector<std::pair<HashNode *, const HashNode *>> Stack; | |
| Stack.emplace_back(Dst, Src); | |
| while (!Stack.empty()) { | |
| auto [DstNode, SrcNode] = Stack.pop_back_val(); | |
| if (!SrcNode) | |
| continue; | |
| if (SrcNode->Terminals) | |
| DstNode->Terminals = DstNode->Terminals.value_or(0) + *SrcNode->Terminals; | |
| for (auto &[Hash, NextSrcNode] : SrcNode->Successors) { | |
| HashNode *NextDstNode; | |
| auto I = DstNode->Successors.find(Hash); | |
| if (I == DstNode->Successors.end()) { | |
| auto NextDst = std::make_unique<HashNode>(); | |
| NextDstNode = NextDst.get(); | |
| NextDstNode->Hash = Hash; | |
| DstNode->Successors.try_emplace(Hash, std::move(NextDst)); | |
| } else | |
| NextDstNode = I->second.get(); | |
| Stack.emplace_back(NextDstNode, NextSrcNode.get()); | |
| } | |
| } | |
| } | |
| std::optional<unsigned> | |
| OutlinedHashTree::find(const HashSequence &Sequence) const { | |
| const HashNode *Current = getRoot(); | |
| for (stable_hash StableHash : Sequence) { | |
| const auto I = Current->Successors.find(StableHash); | |
| if (I == Current->Successors.end()) | |
| return 0; | |
| Current = I->second.get(); | |
| } | |
| return Current->Terminals; | |
| } |