Files
revng-revng/lib/ValueManipulationAnalysis/Mincut.cpp
T
Alvise de Faveri 105edb69a3 DLA: Decouple DLATypeSystem from LLVM Values
- Remove `CreateInterProceduralTypes` and `CreateIntraProceduralTypes`
  from the StepManager and put them in a separate
  `DLATypeSystemLLVMBuilder` object that is in charge of initializing
  the DLATypeSystem graph.
- Remove `MakeLayouts` from the StepManager and split into two free
  functions: `makeLayouts` and `makeLayoutMap()`
- Remove all LLVM-related stuff (Module, LayoutTypePtrs and mappings
  between these and DLATypeSystemNodes) from DLATypeSystem
- Add an IntEqClasses member to DLATypeSystem, to use to map between
  LayoutTypePtrs and Layouts
- Add a TSDebugPrinter object inside the DLATypeSystem, which by default
  prints only IDs
- Define an LLVMTSDebugPrinter inside DLATypeSystemBuilder which
  overrides the default printer's behavior, printing LLVM-related info.
- Add the possibility to dump into a .csv file the bindings between
  Nodes in the TypeSystem and `llvm::Value`s
2021-06-18 18:12:14 +02:00

307 lines
10 KiB
C++

//
// Copyright rev.ng Srls. See LICENSE.md for details.
//
#include <algorithm>
#include <functional>
#include <limits>
#include <random>
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/GraphWriter.h"
#include "revng/ADT/GenericGraph.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 "Mincut.h"
#include "TypeFlowNode.h"
using namespace vma;
static Logger<> MincutLog("vma-mincut");
static llvm::cl::opt<unsigned> MincutIterOpt("vma-mincut-iter",
llvm ::cl::desc("Specify the "
"number of karger "
"iterations for "
"the mincut "
"algorithm"));
static unsigned calcCost(ContractedGraph &G) {
auto ContractedSize = G.NodesToColor->totalSize()
+ G.NodesToUncolor->totalSize();
revng_assert(G.NTypeFlowNodes == ContractedSize);
unsigned Cost = 0;
llvm::SmallSet<TypeFlowNode *, 16> Visited;
// Cost of a node in the NodesToColor set
auto CostOfNodeToColor = [&Visited, &G](TypeFlowNode *TFGNode) {
// Pay the cost only for the nodes that are being decided by the mincut
if (not TFGNode->isUndecided())
return 0U;
unsigned AdditionalCost = 0;
// If the node is undecided and belongs to NodesToColor, it means that all
// of its successors that have the right color are also in NodesToColor.
// This means that successors that do not belong to NodesToColor have
// automatically the wrong color.
for (auto *Succ : TFGNode->successors())
if (not G.NodesToColor->contains(Succ))
AdditionalCost++;
Visited.insert(TFGNode);
return AdditionalCost;
};
for (auto *TFGNode : G.NodesToColor->InitialNodes)
Cost += CostOfNodeToColor(TFGNode);
for (auto *TFGNode : G.NodesToColor->AdditionalNodes)
Cost += CostOfNodeToColor(TFGNode);
// Cost of a node in the NodesToUncolor set
auto CostOfNodeToUncolor = [&Visited, &G](TypeFlowNode *TFGNode) {
// Pay the cost only for the nodes that are being decided by the mincut
if (not TFGNode->isUndecided())
return 0U;
unsigned AdditionalCost = 0;
// If the node belongs to NodesToUncolor, remove G.Color from the candidates
ColorSet NodeColor = TFGNode->Candidates;
NodeColor.Bits.reset(G.Color.firstSetBit());
for (auto *Succ : TFGNode->successors()) {
if (Visited.count(Succ))
continue;
ColorSet CommonColors;
CommonColors.Bits = Succ->Candidates.Bits & NodeColor.Bits;
// If the node and its successor have no common candidates, pay a cost
if (CommonColors.countValid() == 0)
AdditionalCost++;
}
Visited.insert(TFGNode);
return AdditionalCost;
};
for (auto *TFGNode : G.NodesToUncolor->InitialNodes)
Cost += CostOfNodeToUncolor(TFGNode);
for (auto *TFGNode : G.NodesToUncolor->AdditionalNodes)
Cost += CostOfNodeToUncolor(TFGNode);
return Cost;
}
void vma::karger(ContractedGraph &G,
unsigned &BestCost,
ContractedNode &BestNodesToColor,
ContractedNode &BestNodesToUncolor) {
// Fixed seed generated with /dev/urandom
static const unsigned RandSeed = 320464148;
// Seed the random generator for repeatability
srand(RandSeed);
// TODO: find a sane default, e.g. 10 * log2 (G.size())
const unsigned int DefaultNIter = 50U;
const unsigned NIter = (MincutIterOpt ? MincutIterOpt : DefaultNIter);
// Execute many times (Monte-carlo)
for (size_t Iter = 0; Iter < NIter; Iter++) {
G.reset();
auto SpecialNodesDimension = [&G]() {
return G.NodesToColor->totalSize() + G.NodesToUncolor->totalSize();
};
// Execute Karger until all nodes have been collapsed in a special supernode
while (G.NTypeFlowNodes > SpecialNodesDimension()) {
unsigned RandIdx = rand() % G.NActiveEdges;
G.contract(RandIdx);
}
if (VerifyLog.isEnabled())
G.check();
unsigned Cost = calcCost(G);
// Update best solution
if (Cost < BestCost) {
BestCost = Cost;
std::swap(BestNodesToColor.AdditionalNodes,
G.NodesToColor->AdditionalNodes);
std::swap(BestNodesToUncolor.AdditionalNodes,
G.NodesToUncolor->AdditionalNodes);
revng_log(MincutLog,
"Karger new best cost: " << BestCost << " [iteration: " << Iter
<< "]");
revng_log(MincutLog, "Best choice: divided");
}
if (BestCost == 0)
break;
}
}
///\brief Generate the solution in which all nodes of \a G are colored
static void generateColorAllSolution(ContractedGraph &G) {
for (auto &CN : G.Nodes) {
if (CN.get() == G.NodesToColor or CN.get() == G.NodesToUncolor)
continue;
for (TypeFlowNode *TFGNode : CN->InitialNodes) {
revng_assert(not TFGNode->isDecided()
or not TFGNode->Candidates.contains(G.Color));
G.NodesToColor->AdditionalNodes.insert(TFGNode);
G.getMapEntry(TFGNode) = G.NodesToColor;
}
}
}
///\brief Generate the solution in which all nodes of \a G are uncolored
static void moveAllColoredToUncolored(ContractedGraph &G) {
std::swap(G.NodesToUncolor->AdditionalNodes, G.NodesToColor->AdditionalNodes);
for (TypeFlowNode *TFGNode : G.NodesToUncolor->AdditionalNodes) {
revng_assert(not TFGNode->isDecided()
or not TFGNode->Candidates.contains(G.Color));
G.getMapEntry(TFGNode) = G.NodesToUncolor;
}
}
///\brief Generate the two simplest cuts (color all and uncolor all)
static void generateNaiveSolutions(ContractedGraph &G,
unsigned &BestCost,
ContractedNode &BestNodesToColor,
ContractedNode &BestNodesToUncolor) {
generateColorAllSolution(G);
unsigned ColorAllCost = calcCost(G);
moveAllColoredToUncolored(G);
unsigned RemoveAllCost = calcCost(G);
revng_log(MincutLog,
"cost of coloring all: "
<< ColorAllCost << " cost of removing all: " << RemoveAllCost);
if (RemoveAllCost < ColorAllCost) {
BestCost = RemoveAllCost;
BestNodesToColor.AdditionalNodes.clear();
std::swap(BestNodesToUncolor.AdditionalNodes,
G.NodesToUncolor->AdditionalNodes);
revng_log(MincutLog, "Best choice: remove all");
} else {
BestCost = ColorAllCost;
std::swap(BestNodesToColor.AdditionalNodes,
G.NodesToUncolor->AdditionalNodes);
BestNodesToUncolor.AdditionalNodes.clear();
revng_log(MincutLog, "Best choice: color all");
}
}
void vma::minCut(TypeFlowGraph &TG) {
// Apply karger one color at a time, using the color index in the bitset as
// ordering criterion.
for (unsigned I = 0; I < MAX_COLORS; I++) {
ColorSet CurColor(1 << I);
revng_log(MincutLog, "------ Color: " << dumpToString(CurColor));
for (TypeFlowNode *N : TG.nodes()) {
// Check if we can start building a ContractedGraph from the current node
auto HasUndecidedNeighbors = [CurColor](TypeFlowNode *TFGNodeode) {
return llvm::any_of(TFGNodeode->successors(),
[CurColor](TypeFlowNode *Succ) {
return Succ->isUndecided()
and Succ->Candidates.contains(CurColor);
});
};
if (not(N->isDecided() and N->Candidates.contains(CurColor)
and HasUndecidedNeighbors(N)))
continue;
// Build Contracted graph
ContractedGraph G{ CurColor };
makeContractedGraph(G, N, CurColor);
if (VerifyLog.isEnabled())
G.check();
revng_log(MincutLog, "------ New karger: " << G.NTypeFlowNodes);
revng_log(MincutLog,
"Karger with "
<< G.NTypeFlowNodes
<< " nodes, NodesToColor: " << G.NodesToColor->totalSize()
<< " NodesToUncolor: " << G.NodesToUncolor->totalSize());
// Keep track of the best solution
unsigned BestCost = std::numeric_limits<unsigned>::max();
ContractedNode BestNodesToColor = *G.NodesToColor;
ContractedNode BestNodesToUncolor = *G.NodesToUncolor;
// Try to color all and uncolor all
generateNaiveSolutions(G, BestCost, BestNodesToColor, BestNodesToUncolor);
// If NodesToUncolor is empty there's no point in trying karger
if (G.NodesToUncolor->InitialNodes.size() > 0)
karger(G, BestCost, BestNodesToColor, BestNodesToUncolor);
revng_log(MincutLog,
"Final solution "
<< G.NTypeFlowNodes
<< " nodes, NodesToColor: " << BestNodesToColor.totalSize()
<< " NodesToUncolor: " << BestNodesToUncolor.totalSize());
// Color all nodes that belong to NodesToColor
for (TypeFlowNode *TFGNode : BestNodesToColor.InitialNodes) {
revng_assert(TFGNode->Candidates.contains(G.Color));
TFGNode->Candidates = G.Color;
}
for (TypeFlowNode *TFGNode : BestNodesToColor.AdditionalNodes) {
revng_assert(TFGNode->Candidates.contains(G.Color));
TFGNode->Candidates = G.Color;
}
// Uncolor all nodes that belong to NodesToColor
for (TypeFlowNode *TFGNode : BestNodesToUncolor.InitialNodes) {
revng_assert(not TFGNode->isDecided()
or not TFGNode->Candidates.contains(G.Color));
TFGNode->Candidates.Bits.reset(I);
}
for (TypeFlowNode *TFGNode : BestNodesToUncolor.AdditionalNodes) {
revng_assert(not TFGNode->isDecided()
or not TFGNode->Candidates.contains(G.Color));
TFGNode->Candidates.Bits.reset(I);
}
revng_log(MincutLog, "CurCost after applying mincut " << countCasts(TG));
}
// Remove CurColor from the candidates of any remaining grey node before
// going to another color
for (TypeFlowNode *N : TG.nodes())
if (N->isUndecided() and N->Candidates.contains(CurColor))
N->Candidates.Bits.reset(I);
revng_log(MincutLog, "CurCost after resetting color " << countCasts(TG));
applyMajorityVoting(TG);
revng_log(MincutLog,
"CurCost after applying majority voting " << countCasts(TG));
}
}