mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
126 lines
4.7 KiB
C++
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);
|
|
}
|