Files
revng-revng/lib/Yield/CallGraphs/CallGraphSlices.cpp
2025-02-13 13:10:51 +02:00

126 lines
4.7 KiB
C++

/// \file Slices.cpp
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <unordered_map>
#include "llvm/ADT/BreadthFirstIterator.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "revng/Yield/CallGraphs/CallGraphSlices.h"
using Graph = yield::calls::PreLayoutGraph;
using Node = yield::calls::PreLayoutNode;
static Node *copyNode(yield::calls::PreLayoutGraph &Graph, const Node *Source) {
return Graph.addNode(std::make_unique<Node>(Source->data()));
}
/// \tparam NV local `NodeView` specialization
/// \tparam INV inverted location `NodeView` specialization
template<typename NV, typename INV>
Graph makeTreeImpl(const Graph &Input, llvm::StringRef SlicePointLocation) {
auto SlicePointPredicate = [&SlicePointLocation](const Node *Node) {
return Node->getLocationString() == SlicePointLocation;
};
auto Entry = llvm::find_if(Input.nodes(), SlicePointPredicate);
revng_assert(Entry != Input.nodes().end());
// Find the rank of each node, such that for any node its rank is equal to
// the highest rank among its children plus one.
llvm::ReversePostOrderTraversal ReversePostOrder(NV{ *Entry });
std::unordered_map<const Node *, size_t> Ranks;
for (const Node *CurrentNode : ReversePostOrder) {
uint64_t &CurrentRank = Ranks[CurrentNode];
CurrentRank = 0;
for (auto Child : llvm::children<INV>(CurrentNode))
if (auto RankIterator = Ranks.find(Child); RankIterator != Ranks.end())
CurrentRank = std::max(RankIterator->second + 1, CurrentRank);
}
// For each node, select a single predecessor to keep connected to.
// The ranks calculated earlier are used to choose a specific one.
//
// TODO: We should consider a better selection algorithm.
std::unordered_map<const Node *, const Node *> RealEdges;
for (const Node *Current : ReversePostOrder) {
auto NodeIt = Ranks.find(Current);
revng_assert(NodeIt != Ranks.end());
// Select the neighbour with the highest possible rank that is still
// lower than the current node's rank.
const Node *SelectedNeighbour = nullptr;
size_t SelectedNeighbourRank = 0;
for (const Node *Neighbour : llvm::children<INV>(Current)) {
if (auto Iterator = Ranks.find(Neighbour); Iterator != Ranks.end()) {
// If an inverse neighbour is not present in the `Ranks` table, it's not
// a part of the desired slice, as such we can safely ignore it.
size_t Rank = Iterator->second;
if (Rank < NodeIt->second && Rank >= SelectedNeighbourRank) {
SelectedNeighbour = Neighbour;
SelectedNeighbourRank = Rank;
}
}
}
auto &&[_, Success] = RealEdges.try_emplace(Current, SelectedNeighbour);
revng_assert(Success);
}
Graph Result;
std::unordered_map<const Node *, Node *> Lookup;
// Returns the version of the node from the new graph if it exists,
// or adds a new one to if it does not.
auto FindOrAddHelper = [&Result, &Lookup](const Node *OldNode) {
if (auto NewNode = Lookup.find(OldNode); NewNode != Lookup.end())
return NewNode->second;
else
return Lookup.emplace(OldNode, copyNode(Result, OldNode)).first->second;
};
// Manually adding `Entry` to the result graphs guarantees that it's never
// empty. Since we only ever iterate on edges, this will guarantee that the
// produced graph is not empty even in the cases where `Entry` has no edges.
Result.setEntryNode(FindOrAddHelper(*Entry));
// Fill in the `Result` graph.
for (const Node *Node : llvm::breadth_first(NV{ *Entry })) {
for (auto Neighbour : llvm::children<INV>(Node)) {
if (Ranks.contains(Neighbour)) {
auto *NewNeighbour = FindOrAddHelper(Neighbour);
if (RealEdges.at(Node) == Neighbour) {
// Emit a real edge, if this is the neighbour selected earlier.
NewNeighbour->addSuccessor(FindOrAddHelper(Node));
} else {
// Emit a fake node otherwise.
auto NewNode = copyNode(Result, Node);
NewNode->IsShallow = true;
NewNeighbour->addSuccessor(NewNode);
}
}
}
}
return Result;
}
yield::calls::PreLayoutGraph
yield::calls::makeCalleeTree(const PreLayoutGraph &Input,
llvm::StringRef SlicePoint) {
// Forwards direction, makes sure no successor relation ever gets lost.
return makeTreeImpl<const PreLayoutNode *,
llvm::Inverse<const PreLayoutNode *>>(Input, SlicePoint);
}
yield::calls::PreLayoutGraph
yield::calls::makeCallerTree(const PreLayoutGraph &Input,
llvm::StringRef SlicePoint) {
// Backwards direction, makes sure no predecessor relation ever gets lost.
return makeTreeImpl<llvm::Inverse<const PreLayoutNode *>,
const PreLayoutNode *>(Input, SlicePoint);
}