Files
revng-revng/lib/ValueManipulationAnalysis/ContractedGraph.cpp
T
Giacomo Vercesi ab125b35b0 Fix License headers
Change company name to "rev.ng Labs Srl" in all license headers
to reflect changed company name and legal status
Add missing license headers to files that didn't have one
2022-04-19 12:17:59 +02:00

267 lines
8.3 KiB
C++

//
// Copyright rev.ng Labs Srl. See LICENSE.md for details.
//
#include <memory>
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"
#include "revng/Support/Assert.h"
#include "revng/Support/Debug.h"
#include "revng/Support/IRHelpers.h"
#include "revng-c/ValueManipulationAnalysis/TypeColors.h"
#include "ContractedGraph.h"
#include "TypeFlowGraph.h"
#include "TypeFlowNode.h"
using namespace vma;
ContractedNode *
ContractedGraph::addNode(std::optional<TypeFlowNode *> InitialContent = {}) {
Nodes.push_back(std::make_unique<ContractedNode>());
const auto &NewNode = Nodes.back();
if (InitialContent) {
NewNode->InitialNodes.insert(*InitialContent);
ReverseMap[*InitialContent] = NewNode.get();
}
return NewNode.get();
}
void ContractedGraph::contract(size_t EdgeIndex) {
revng_assert(EdgeIndex < NActiveEdges);
EdgeT &E = EdgeList[EdgeIndex];
ContractedNode *CN1 = getMapEntry(E.first);
ContractedNode *CN2 = getMapEntry(E.second);
// Verify that the nodes belongs to the graph (expensive)
if (VerifyLog.isEnabled()) {
const auto IsCN1 = [CN1](std::unique_ptr<ContractedNode> &N) {
return N.get() == CN1;
};
revng_assert(llvm::find_if(Nodes, IsCN1));
const auto IsCN2 = [CN2](std::unique_ptr<ContractedNode> &N) {
return N.get() == CN2;
};
revng_assert(llvm::find_if(Nodes, IsCN2));
}
auto IsSpecialNode = [this](ContractedNode *CN) {
return (CN == this->NodesToColor or CN == this->NodesToUncolor);
};
bool IsCN1Special = IsSpecialNode(CN1);
bool IsCN2Special = IsSpecialNode(CN2);
bool BothAreSpecial = IsCN1Special and IsCN2Special;
bool NoneIsSpecial = not IsCN1Special and not IsCN2Special;
// Never merge the two special nodes together
if (BothAreSpecial or (CN1 == CN2)) {
// Move the contracted edge after the active part of the list
NActiveEdges--;
std::swap(E, EdgeList[NActiveEdges]);
return;
}
ContractedNode *NodeToKeep, *NodeToRemove;
// Never remove a special node. If both are not special, remove the one with
// less nodes.
if (IsCN1Special or (NoneIsSpecial and CN1->totalSize() > CN2->totalSize())) {
NodeToKeep = CN1;
NodeToRemove = CN2;
} else {
NodeToKeep = CN2;
NodeToRemove = CN1;
}
// Move content
NodeToKeep->AdditionalNodes.set_union(NodeToRemove->InitialNodes);
NodeToKeep->AdditionalNodes.set_union(NodeToRemove->AdditionalNodes);
// Update Map
for (TypeFlowNode *TFGNode : NodeToRemove->InitialNodes)
getMapEntry(TFGNode) = NodeToKeep;
for (TypeFlowNode *TFGNode : NodeToRemove->AdditionalNodes)
getMapEntry(TFGNode) = NodeToKeep;
// Move the contracted edge after the active part of the list
NActiveEdges--;
revng_assert(NActiveEdges < EdgeList.size());
std::swap(E, EdgeList[NActiveEdges]);
}
void ContractedGraph::reset() {
for (auto &CN : Nodes) {
CN->reset();
for (auto *TFGNode : CN->InitialNodes)
ReverseMap[TFGNode] = CN.get();
}
NActiveEdges = EdgeList.size();
}
void ContractedGraph::check() {
// Check edges
for (auto &E : EdgeList) {
revng_assert(E.first != nullptr);
revng_assert(E.second != nullptr);
// The edge must exist in the TypeFlowGraph
revng_assert(llvm::is_contained(E.first->successors(), E.second));
// Only one edge between these two nodes must exist
revng_assert(1 == llvm::count(EdgeList, E));
// Both nodes of the edge must be in the map
revng_assert(ReverseMap.count(E.first));
revng_assert(ReverseMap.count(E.second));
}
const auto IsCentralNode = [Col(this->Color)](const TypeFlowNode *TFGNode) {
return TFGNode->isUndecided() and TFGNode->Candidates.contains(Col);
};
// Check map
for (const auto &[CurNode, Contracted] : ReverseMap) {
{
revng_assert(CurNode != nullptr);
revng_assert(Contracted != nullptr);
// The map entries must be nodes of the graph
revng_assert(1 == llvm::count_if(Nodes, [C(Contracted)](const auto &N) {
return N.get() == C;
}));
// The map entries must be coherent
const auto &Initial = Contracted->InitialNodes;
const auto &Additional = Contracted->AdditionalNodes;
revng_assert(1 == (Initial.count(CurNode) + Additional.count(CurNode)));
}
// The neighbors of the node should also be in the map
for (TypeFlowNode *Succ : CurNode->successors()) {
unsigned CNEdges = llvm::count(EdgeList, EdgeT{ CurNode, Succ });
unsigned TFEdges = llvm::count(CurNode->successors(), Succ);
if (IsCentralNode(CurNode) or IsCentralNode(Succ)) {
revng_assert(ReverseMap.count(Succ));
revng_assert(CNEdges == TFEdges);
} else if (not IsCentralNode(CurNode) and not IsCentralNode(Succ)) {
revng_assert(CNEdges == 0);
}
}
// Check that the right nodes belong to the special nodes
if (not IsCentralNode(CurNode)) {
if (CurNode->Candidates.contains(Color))
revng_assert(Contracted == NodesToColor);
else
revng_assert(Contracted == NodesToUncolor);
}
}
// Check nodes
for (auto &CN : Nodes) {
revng_assert(CN != nullptr);
for (auto *TFGNode : CN->InitialNodes) {
revng_assert(TFGNode != nullptr);
// Initial nodes must be unique
revng_assert(1 == llvm::count_if(Nodes, [TFGNode](auto &CN2) {
return CN2.get()->InitialNodes.count(TFGNode);
}));
revng_assert(ReverseMap.count(TFGNode));
// Check that the right nodes belong to the special nodes
if (not IsCentralNode(TFGNode)) {
if (TFGNode->Candidates.contains(Color))
revng_assert(CN.get() == NodesToColor);
else
revng_assert(CN.get() == NodesToUncolor);
}
}
for (auto *TFGNode : CN->AdditionalNodes) {
revng_assert(TFGNode != nullptr);
revng_assert(ReverseMap.count(TFGNode));
}
}
}
/// Insert a TypeFlowNode in the right ContractedNode
///
/// If the TypeFlowNode is a "border" node (i.e. it's decided or it doesn't
/// have the right color) it will be inserted in the corresponding "special"
/// node (NodesToColor or NodesToUncolor).
/// Otherwise, a new ContractedNode is created.
static void addInitialNode(ContractedGraph &G, TypeFlowNode *TFGNode) {
ContractedNode *Node;
if (G.ReverseMap.find(TFGNode) != G.ReverseMap.end())
return;
bool HasWrongColor = not TFGNode->Candidates.contains(G.Color);
if (HasWrongColor) {
// Wrong color => uncolor
Node = G.NodesToUncolor;
} else if (TFGNode->isDecided()) {
// Right color and decided => color
Node = G.NodesToColor;
} else if (TFGNode->isUndecided()) {
// Right color and undecided => create a new node
G.Nodes.push_back(std::make_unique<ContractedNode>());
Node = G.Nodes.back().get();
} else {
// Unreachable
revng_abort();
}
Node->InitialNodes.insert(TFGNode);
G.ReverseMap[TFGNode] = Node;
++G.NTypeFlowNodes;
}
void vma::makeContractedGraph(ContractedGraph &G,
TypeFlowNode *Entry,
const ColorSet CurColor) {
// Check entrypoint properties
revng_assert(Entry->isDecided() and Entry->Candidates.contains(CurColor));
// Create special nodes
G.NodesToColor = G.addNode(Entry);
G.NodesToUncolor = G.addNode();
G.NTypeFlowNodes = 1;
const auto IsBorderNode = [CurColor](const TypeFlowNode *TFNode) {
return TFNode->isDecided() or TFNode->isUncolored()
or (not TFNode->Candidates.contains(CurColor));
};
// Visit the TypeFlowGraph depth-first. Stop when you find a decided node of
// any color or an undecided node that doesn't have CurColor as a candidate.
llvm::df_iterator_default_set<TypeFlowNode *> DfsExtSet;
for (TypeFlowNode *Reachable : llvm::depth_first_ext(Entry, DfsExtSet)) {
// Add current node
addInitialNode(G, Reachable);
for (TypeFlowNode *Succ : Reachable->successors()) {
if (IsBorderNode(Reachable) and IsBorderNode(Succ)) {
// Mark the node as "not to visit" by putting it in the visited set
if (llvm::all_of(Succ->successors(), IsBorderNode))
DfsExtSet.insert(Succ);
} else {
// Add successor
addInitialNode(G, Succ);
// Add edge
G.EdgeList.push_back({ Reachable, Succ });
}
}
}
}