Files
revng-revng/lib/RestructureCFGPass/RestructureCFG.cpp
T
Andrea Gussoni 4777c81800 Add dummy node as source of retreating edges
Add a new dummy node as the source of each backedge found in the graph,
during the preprocessing.

This help in the semplification phase of the SCS, since the new dummy
node helps triggering merging of metaregions with have a non empty
intersection.
2019-05-24 18:35:52 +02:00

1321 lines
46 KiB
C++

/// \file Restructure.cpp
/// \brief FunctionPass that applies the comb to the RegionCFG of a function
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Standard includes
#include <sstream>
#include <stdlib.h>
// LLVM includes
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/GenericDomTreeConstruction.h"
#include "llvm/Support/raw_os_ostream.h"
// revng includes
#include "revng/Support/Debug.h"
#include "revng/Support/IRHelpers.h"
// Local libraries includes
#include "revng-c/RestructureCFGPass/Flattening.h"
#include "revng-c/RestructureCFGPass/MetaRegion.h"
#include "revng-c/RestructureCFGPass/RegionCFGTree.h"
#include "revng-c/RestructureCFGPass/RestructureCFG.h"
#include "revng-c/RestructureCFGPass/Utils.h"
using namespace llvm;
using std::make_pair;
using std::pair;
using std::string;
using std::to_string;
// TODO: Move the initialization of the logger here from "Utils.h"
// Debug logger.
Logger<> CombLogger("restructure");
// EdgeDescriptor is a handy way to create and manipulate edges on the
// RegionCFG.
using BasicBlockNodeBB = BasicBlockNode<BasicBlock *>;
using EdgeDescriptor = std::pair<BasicBlockNodeBB *, BasicBlockNodeBB *>;
// Explicit instantation of template classes `Metaregion`.
template class MetaRegion<BasicBlock *>;
using MetaRegionBB = MetaRegion<BasicBlock *>;
using MetaRegionBBVect = std::vector<MetaRegionBB>;
using MetaRegionBBPtrVect = std::vector<MetaRegionBB *>;
using BackedgeMetaRegionMap = std::map<EdgeDescriptor, MetaRegionBB *>;
static std::set<EdgeDescriptor> getBackedges(RegionCFG<BasicBlock *> &Graph) {
// Some helper data structures.
int Time = 0;
std::map<BasicBlockNodeBB *, int> StartTime;
std::map<BasicBlockNodeBB *, int> FinishTime;
std::vector<std::pair<BasicBlockNodeBB *, size_t>> Stack;
// Set of backedges.
std::set<EdgeDescriptor> Backedges;
// Push the entry node in the exploration stack.
BasicBlockNodeBB &EntryNode = Graph.getEntryNode();
Stack.push_back(make_pair(&EntryNode, 0));
// Go through the exploration stack.
while (!Stack.empty()) {
auto StackElem = Stack.back();
Stack.pop_back();
BasicBlockNodeBB *Vertex = StackElem.first;
Time++;
// Check if we are inspecting a vertex for the first time, and in case mark
// the start time of the visit.
if (StartTime.count(Vertex) == 0) {
StartTime[Vertex] = Time;
}
// Successor exploraition
size_t Index = StackElem.second;
// If we are still successors to explore.
if (Index < StackElem.first->successor_size()) {
BasicBlockNodeBB *Successor = Vertex->getSuccessorI(Index);
Index++;
Stack.push_back(make_pair(Vertex, Index));
// We are in presence of a backedge.
if (StartTime.count(Successor) != 0
and FinishTime.count(Successor) == 0) {
Backedges.insert(make_pair(Vertex, Successor));
}
// Enqueue the successor for the visit.
if (StartTime.count(Successor) == 0) {
Stack.push_back(make_pair(Successor, 0));
}
} else {
// Mark the finish of the visit of a vertex.
FinishTime[Vertex] = Time;
}
}
return Backedges;
}
static bool mergeSCSStep(MetaRegionBBVect &MetaRegions) {
for (auto RegionIt1 = MetaRegions.begin(); RegionIt1 != MetaRegions.end();
RegionIt1++) {
for (auto RegionIt2 = std::next(RegionIt1); RegionIt2 != MetaRegions.end();
RegionIt2++) {
bool Intersects = (*RegionIt1).intersectsWith(*RegionIt2);
bool IsIncluded = (*RegionIt1).isSubSet(*RegionIt2);
bool IsIncludedReverse = (*RegionIt2).isSubSet(*RegionIt1);
bool AreEquivalent = (*RegionIt1).nodesEquality(*RegionIt2);
if (Intersects
and (((!IsIncluded) and (!IsIncludedReverse)) or AreEquivalent)) {
(*RegionIt1).mergeWith(*RegionIt2);
MetaRegions.erase(RegionIt2);
return true;
}
}
}
return false;
}
static void simplifySCS(MetaRegionBBVect &MetaRegions) {
bool Changes = true;
while (Changes) {
Changes = mergeSCSStep(MetaRegions);
}
}
static bool
mergeSCSAbnormalRetreating(MetaRegionBBVect &MetaRegions,
const std::set<EdgeDescriptor> &Backedges,
BackedgeMetaRegionMap &BackedgeMetaRegionMap,
std::set<MetaRegionBB *> &BlacklistedMetaregions) {
for (auto RegionIt = MetaRegions.begin(); RegionIt != MetaRegions.end();
RegionIt++) {
MetaRegionBB &Region = *RegionIt;
// Do not re-analyze blacklisted metaregions.
if (BlacklistedMetaregions.count(&Region) == 0) {
// Iterate over all the backedges present in the graph, if the current
// region contains the source of a backedge, it should contain also the
// the target of that backedge. If not, merge the two SCSs.
for (EdgeDescriptor Backedge : Backedges) {
bool AbnormalIncoming = Region.containsNode(Backedge.first)
and !Region.containsNode(Backedge.second);
bool AbnormalOutgoing = Region.containsNode(Backedge.second)
and !Region.containsNode(Backedge.first);
if (AbnormalIncoming or AbnormalOutgoing) {
// Retrieve the Metaregion identified by the backedge with goes
// goes outside the scope of the current Metaregion.
MetaRegionBB *OtherRegion = BackedgeMetaRegionMap.at(Backedge);
Region.mergeWith(*OtherRegion);
// Find the iterator to the `OtherRegion`
for (auto OtherRegionIt = MetaRegions.begin();
OtherRegionIt != MetaRegions.end();
OtherRegionIt++) {
if (&*OtherRegionIt == OtherRegion) {
// Blacklist the region which we have merged.
BackedgeMetaRegionMap[Backedge] = &Region;
BlacklistedMetaregions.insert(OtherRegion);
return true;
}
}
// Abort if we didn't find the metaregion to remove.
revng_abort("Not found the region to merge with.");
}
}
}
}
return false;
}
static void
simplifySCSAbnormalRetreating(MetaRegionBBVect &MetaRegions,
const std::set<EdgeDescriptor> &Backedges,
BackedgeMetaRegionMap &BackedgeMetaRegionMap) {
std::set<MetaRegionBB *> BlacklistedMetaregions;
bool Changes = true;
while (Changes) {
Changes = mergeSCSAbnormalRetreating(MetaRegions,
Backedges,
BackedgeMetaRegionMap,
BlacklistedMetaregions);
}
// Remove all the metaregion that have been merged with others, using the
// erase/remove idiom.
MetaRegions.erase(remove_if(MetaRegions.begin(),
MetaRegions.end(),
[&BlacklistedMetaregions](MetaRegionBB &M) {
return BlacklistedMetaregions.count(&M) == 1;
}),
MetaRegions.end());
}
static void sortMetaRegions(MetaRegionBBVect &MetaRegions) {
std::sort(MetaRegions.begin(),
MetaRegions.end(),
[](MetaRegionBB &First, MetaRegionBB &Second) {
return First.getNodes().size() < Second.getNodes().size();
});
}
static bool
checkMetaregionConsistency(const MetaRegionBBVect &MetaRegions,
const std::set<EdgeDescriptor> &Backedges) {
bool ComparisonState = true;
for (const MetaRegionBB &MetaRegion : MetaRegions) {
for (EdgeDescriptor Backedge : Backedges) {
BasicBlockNodeBB *Source = Backedge.first;
BasicBlockNodeBB *Target = Backedge.second;
if (MetaRegion.containsNode(Source)) {
if ((not MetaRegion.containsNode(Source))
or (not MetaRegion.containsNode(Source))) {
ComparisonState = false;
}
revng_assert(MetaRegion.containsNode(Source));
revng_assert(MetaRegion.containsNode(Target));
}
}
}
return ComparisonState;
}
static void
computeParents(MetaRegionBBVect &MetaRegions, MetaRegionBB *RootMetaRegion) {
for (MetaRegionBB &MetaRegion1 : MetaRegions) {
bool ParentFound = false;
for (MetaRegionBB &MetaRegion2 : MetaRegions) {
if (&MetaRegion1 != &MetaRegion2) {
if (MetaRegion1.isSubSet(MetaRegion2)) {
if (CombLogger.isEnabled()) {
CombLogger << "For metaregion: " << &MetaRegion1 << "\n";
CombLogger << "parent found\n";
CombLogger << &MetaRegion2 << "\n";
}
MetaRegion1.setParent(&MetaRegion2);
ParentFound = true;
break;
}
}
}
if (!ParentFound) {
if (CombLogger.isEnabled()) {
CombLogger << "For metaregion: " << &MetaRegion1 << "\n";
CombLogger << "no parent found\n";
}
MetaRegion1.setParent(RootMetaRegion);
}
}
}
static MetaRegionBBPtrVect applyPartialOrder(MetaRegionBBVect &V) {
MetaRegionBBPtrVect OrderedVector;
std::set<MetaRegionBB *> Processed;
while (V.size() != Processed.size()) {
for (auto RegionIt1 = V.begin(); RegionIt1 != V.end(); RegionIt1++) {
if (Processed.count(&*RegionIt1) == 0) {
bool FoundParent = false;
for (auto RegionIt2 = V.begin(); RegionIt2 != V.end(); RegionIt2++) {
if ((RegionIt1 != RegionIt2) and Processed.count(&*RegionIt2) == 0) {
if ((*RegionIt1).getParent() == &*RegionIt2) {
FoundParent = true;
break;
}
}
}
if (FoundParent == false) {
OrderedVector.push_back(&*RegionIt1);
Processed.insert(&*RegionIt1);
break;
}
}
}
}
std::reverse(OrderedVector.begin(), OrderedVector.end());
return OrderedVector;
}
static bool alreadyInMetaregion(MetaRegionBBVect &V, BasicBlockNodeBB *N) {
// Scan all the metaregions and check if a node is already contained in one of
// them
for (MetaRegionBB &Region : V) {
if (Region.containsNode(N)) {
return true;
}
}
return false;
}
static MetaRegionBBVect
createMetaRegions(const std::vector<EdgeDescriptor> &Backedges) {
std::map<BasicBlockNodeBB *, std::set<BasicBlockNodeBB *>> AdditionalSCSNodes;
std::vector<std::pair<BasicBlockNodeBB *, std::set<BasicBlockNodeBB *>>>
Regions;
for (auto &Backedge : Backedges) {
auto SCSNodes = findReachableNodes(*Backedge.second, *Backedge.first);
AdditionalSCSNodes[Backedge.second].insert(SCSNodes.begin(),
SCSNodes.end());
if (CombLogger.isEnabled()) {
CombLogger << "SCS identified by: ";
CombLogger << Backedge.first->getNameStr() << " -> "
<< Backedge.second->getNameStr() << "\n";
CombLogger << "Is composed of nodes:\n";
for (auto Node : SCSNodes) {
CombLogger << Node->getNameStr() << "\n";
}
}
Regions.push_back(std::make_pair(Backedge.second, SCSNodes));
}
// Include in the regions found before other possible sub-regions, if an edge
// which is the target of a backedge is included in an outer region.
for (auto &Region : Regions) {
BasicBlockNodeBB *Head = Region.first;
std::set<BasicBlockNodeBB *> &Nodes = Region.second;
std::set<BasicBlockNodeBB *> AdditionalNodes;
std::set<BasicBlockNodeBB *> OldNodes;
do {
OldNodes = Nodes;
for (BasicBlockNodeBB *Node : Nodes) {
if ((Node != Head) and (AdditionalSCSNodes.count(Node) != 0)) {
CombLogger << "Adding additional nodes for region with head: ";
CombLogger << Head->getNameStr();
CombLogger << " and relative to node: ";
CombLogger << Node->getNameStr() << "\n";
AdditionalNodes.insert(AdditionalSCSNodes[Node].begin(),
AdditionalSCSNodes[Node].end());
}
}
Nodes.insert(AdditionalNodes.begin(), AdditionalNodes.end());
} while (Nodes != OldNodes);
}
MetaRegionBBVect MetaRegions;
int SCSIndex = 1;
for (size_t I = 0; I < Regions.size(); ++I) {
auto &SCS = Regions[I].second;
MetaRegions.push_back(MetaRegionBB(SCSIndex, SCS, true));
SCSIndex++;
}
return MetaRegions;
}
static BasicBlockNodeBB *getCheckPredecessor(BasicBlockNodeBB *Node) {
bool CheckFound = false;
BasicBlockNodeBB *CheckPredecessor = nullptr;
for (BasicBlockNodeBB *Predecessor : Node->predecessors()) {
if (Predecessor->isCheck()) {
revng_assert(not CheckFound);
CheckPredecessor = Predecessor;
CheckFound = true;
}
}
revng_assert(CheckPredecessor != nullptr);
return CheckPredecessor;
}
static void removeFromRPOT(std::vector<BasicBlockNodeBB *> &RPOT,
BasicBlockNodeBB *Node) {
RPOT.erase(std::remove_if(RPOT.begin(),
RPOT.end(),
[Node](BasicBlockNodeBB *N) {
if (N == Node) {
return true;
}
return false;
}),
RPOT.end());
}
char RestructureCFG::ID = 0;
static RegisterPass<RestructureCFG> X("restructure-cfg",
"Apply RegionCFG restructuring "
"transformation",
true,
true);
bool RestructureCFG::runOnFunction(Function &F) {
// Analyze only isolated functions.
if (!F.getName().startswith("bb.")
or F.getName().startswith("bb.quotearg_buffer_restyled")
or F.getName().startswith("bb.printf_parse")
or F.getName().startswith("bb.printf_core")
or F.getName().startswith("bb._Unwind_VRS_Pop")
or F.getName().startswith("bb.vasnprintf")) {
return false;
}
// Clear graph object from the previous pass.
RootCFG = RegionCFG<BasicBlock *>();
// Set names of the CFG region
RootCFG.setFunctionName(F.getName());
RootCFG.setRegionName("root");
// Initialize the RegionCFG object
RootCFG.initialize(&F);
// Dump the function name.
if (CombLogger.isEnabled()) {
CombLogger << "Analyzing function: " << F.getName() << "\n";
}
// Dump the object in .dot format if debug mode is activated.
if (CombLogger.isEnabled()) {
RootCFG.dumpDotOnFile("dots", F.getName(), "begin");
}
// Identify SCS regions.
std::set<EdgeDescriptor> Backedges = getBackedges(RootCFG);
if (CombLogger.isEnabled()) {
CombLogger << "Backedges in the graph:\n";
for (auto &Backedge : Backedges) {
CombLogger << Backedge.first->getNameStr() << " -> "
<< Backedge.second->getNameStr() << "\n";
}
}
// Insert a dummy node for each retrating node.
for (EdgeDescriptor Backedge : Backedges) {
BasicBlockNodeBB *OriginalTarget = Backedge.second;
BasicBlockNodeBB *Dummy = RootCFG.addArtificialNode();
moveEdgeTarget(Backedge, Dummy);
addEdge(EdgeDescriptor(Dummy, OriginalTarget));
}
Backedges = getBackedges(RootCFG);
// Check that the source node of each retreating edge is a dummy node.
for (EdgeDescriptor Backedge : Backedges) {
revng_assert(Backedge.first->isEmpty());
}
// Fill a vector with the backedges, to ensure order of inspection.
std::vector<EdgeDescriptor> BackedgesVect;
for (EdgeDescriptor Backedge : Backedges) {
BackedgesVect.push_back(Backedge);
}
// Create meta regions
MetaRegionBBVect MetaRegions = createMetaRegions(BackedgesVect);
// Temporary map where to store the corrispondence between the backedge and
// the SCS it gives origin to.
// HACK: this should be done at the same time of the metaregion creation.
unsigned MetaRegionIndex = 0;
std::map<EdgeDescriptor, MetaRegionBB *> BackedgeMetaRegionMap;
for (EdgeDescriptor Backedge : Backedges) {
BackedgeMetaRegionMap[Backedge] = &MetaRegions.at(MetaRegionIndex);
MetaRegionIndex++;
}
// Print gross metaregions.
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Metaregions after nothing:\n";
for (auto &Meta : MetaRegions) {
CombLogger << "\n";
CombLogger << &Meta << "\n";
CombLogger << "With index " << Meta.getIndex() << "\n";
CombLogger << "With size " << Meta.nodes_size() << "\n";
CombLogger << "Is composed of nodes:\n";
auto &Nodes = Meta.getNodes();
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
}
}
// Simplify SCS if they contain an edge which goes outside the scope of the
// current region.
simplifySCSAbnormalRetreating(MetaRegions, Backedges, BackedgeMetaRegionMap);
// Check consitency of metaregions simplified above.
revng_assert(checkMetaregionConsistency(MetaRegions, Backedges));
// Print SCS after first simplification.
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Metaregions after first simplification:\n";
for (auto &Meta : MetaRegions) {
CombLogger << "\n";
CombLogger << &Meta << "\n";
CombLogger << "With index " << Meta.getIndex() << "\n";
CombLogger << "With size " << Meta.nodes_size() << "\n";
CombLogger << "Is composed of nodes:\n";
auto &Nodes = Meta.getNodes();
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
}
}
// Simplify SCS in a fixed-point fashion.
simplifySCS(MetaRegions);
// Check consitency of metaregions simplified above
revng_assert(checkMetaregionConsistency(MetaRegions, Backedges));
// Print SCS after second simplification.
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Metaregions after second simplification:\n";
for (auto &Meta : MetaRegions) {
CombLogger << "\n";
CombLogger << &Meta << "\n";
CombLogger << "With index " << Meta.getIndex() << "\n";
CombLogger << "With size " << Meta.nodes_size() << "\n";
CombLogger << "Is composed of nodes:\n";
auto &Nodes = Meta.getNodes();
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
}
}
// Sort the Metaregions in increasing number of composing nodes order.
sortMetaRegions(MetaRegions);
// Print SCS after ordering.
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Metaregions after ordering:\n";
for (auto &Meta : MetaRegions) {
CombLogger << "\n";
CombLogger << &Meta << "\n";
CombLogger << "With index " << Meta.getIndex() << "\n";
CombLogger << "With size " << Meta.nodes_size() << "\n";
CombLogger << "Is composed of nodes:\n";
auto &Nodes = Meta.getNodes();
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
}
}
// Compute parent relations for the identified SCSs.
std::set<BasicBlockNodeBB *> Empty;
MetaRegionBB RootMetaRegion(0, Empty);
computeParents(MetaRegions, &RootMetaRegion);
// Print metaregions after ordering.
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Metaregions parent relationship:\n";
for (auto &Meta : MetaRegions) {
CombLogger << "\n";
CombLogger << &Meta << "\n";
CombLogger << "With index " << Meta.getIndex() << "\n";
CombLogger << "With size " << Meta.nodes_size() << "\n";
CombLogger << "Is composed of nodes:\n";
auto &Nodes = Meta.getNodes();
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
CombLogger << "Has parent: " << Meta.getParent() << "\n";
}
}
// Find an ordering for the metaregions that satisfies the inclusion
// relationship. We create a new "shadow" vector containing only pointers to
// the "real" metaregions.
MetaRegionBBPtrVect OrderedMetaRegions = applyPartialOrder(MetaRegions);
// Print metaregions after ordering.
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Metaregions after partial ordering:\n";
for (auto *Meta : OrderedMetaRegions) {
CombLogger << "\n";
CombLogger << Meta << "\n";
CombLogger << "With index " << Meta->getIndex() << "\n";
CombLogger << "With size " << Meta->nodes_size() << "\n";
CombLogger << "Is composed of nodes:\n";
auto &Nodes = Meta->getNodes();
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
CombLogger << "Has parent: " << Meta->getParent() << "\n";
CombLogger << "Is SCS: " << Meta->isSCS() << "\n";
}
}
ReversePostOrderTraversal<BasicBlockNodeBB *> ORPOT(&RootCFG.getEntryNode());
// Create a std::vector from the reverse post order (we will later need
// the removal operation)
std::vector<BasicBlockNodeBB *> RPOT;
for (BasicBlockNodeBB *BN : ORPOT) {
RPOT.push_back(BN);
}
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Reverse post order is:\n";
for (BasicBlockNodeBB *BN : RPOT) {
CombLogger << BN->getNameStr() << "\n";
}
CombLogger << "Reverse post order end\n";
}
CombLogger << "Debugged function"
<< "\n";
CombLogger << F.getName().equals("bb._start_c") << "\n";
DominatorTreeBase<BasicBlockNodeBB, false> DT;
DT.recalculate(RootCFG);
DominatorTreeBase<BasicBlockNodeBB, true> PDT;
PDT.recalculate(RootCFG);
// Reserve enough space for all the OrderedMetaRegions.
// The following algorithms stores pointers to the elements of this vector, so
// we need to make sure that no reallocation happens.
std::vector<RegionCFG<BasicBlock *>> Regions(OrderedMetaRegions.size());
for (MetaRegionBB *Meta : OrderedMetaRegions) {
if (CombLogger.isEnabled()) {
CombLogger << "\nAnalyzing region: " << Meta->getIndex() << "\n";
}
if (CombLogger.isEnabled()) {
auto &Nodes = Meta->getNodes();
CombLogger << "Which is composed of nodes:\n";
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
CombLogger << "Dumping main graph snapshot before restructuring\n";
RootCFG.dumpDotOnFile("dots",
F.getName(),
"Out-pre-" + std::to_string(Meta->getIndex()));
}
#if 0
std::map<BasicBlockNodeBB *, int> IncomingDegree;
for (BasicBlockNodeBB *Node : Meta->nodes()) {
int IncomingCounter = 0;
for (BasicBlockNodeBB *Predecessor : Node->predecessors()) {
EdgeDescriptor Edge = make_pair(Predecessor, Node);
if ((Meta->containsNode(Predecessor)) and (Backedges.count(Edge))) {
IncomingCounter++;
}
}
IncomingDegree[Node] = IncomingCounter;
}
// Print information about incoming edge degrees.
if (CombLogger.isEnabled()) {
CombLogger << "Incoming degree:\n";
for (auto &it : IncomingDegree) {
CombLogger << it.first->getNameStr() << " " << it.second << "\n";
}
}
auto MaxDegreeIt = max_element(IncomingDegree.begin(),
IncomingDegree.end(),
[](const pair<BasicBlockNodeBB *, int> &p1,
const pair<BasicBlockNodeBB *, int> &p2) {
return p1.second < p2.second;
});
int MaxDegree = (*MaxDegreeIt).second;
if (CombLogger.isEnabled()) {
CombLogger << "Maximum incoming degree found: ";
CombLogger << MaxDegree << "\n";
}
std::set<BasicBlockNodeBB *> MaximuxEdgesNodes;
copy_if(Meta->begin(),
Meta->end(),
std::inserter(MaximuxEdgesNodes, MaximuxEdgesNodes.begin()),
[&IncomingDegree, &MaxDegree](BasicBlockNodeBB *Node) {
return IncomingDegree[Node] == MaxDegree;
});
revng_assert(MaxDegree > 0);
BasicBlockNodeBB *FirstCandidate;
if (MaximuxEdgesNodes.size() > 1) {
for (BasicBlockNodeBB *BN : RPOT) {
if (MaximuxEdgesNodes.count(BN) != 0) {
FirstCandidate = BN;
break;
}
}
} else {
FirstCandidate = *MaximuxEdgesNodes.begin();
}
#endif
#if 1
BasicBlockNodeBB *FirstCandidate = nullptr;
for (BasicBlockNodeBB *BN : RPOT) {
if (Meta->containsNode(BN) == true) {
FirstCandidate = BN;
break;
}
}
#endif
revng_assert(FirstCandidate != nullptr);
// Print out the name of the node that has been selected as head of the
// region
if (CombLogger.isEnabled()) {
CombLogger << "Elected head is: " << FirstCandidate->getNameStr() << "\n";
}
// Identify all the abnormal retreating edges in a SCS.
std::set<EdgeDescriptor> Retreatings;
std::set<BasicBlockNodeBB *> RetreatingTargets;
for (EdgeDescriptor Backedge : Backedges) {
if (Meta->containsNode(Backedge.first)) {
// Check that the target of the retreating edge falls inside the current
// SCS.
revng_assert(Meta->containsNode(Backedge.second));
Retreatings.insert(Backedge);
RetreatingTargets.insert(Backedge.second);
}
}
if (CombLogger.isEnabled()) {
CombLogger << "Retreatings found:\n";
for (EdgeDescriptor Retreating : Retreatings) {
CombLogger << Retreating.first->getNameStr() << " -> ";
CombLogger << Retreating.second->getNameStr() << "\n";
}
}
// We need to update the backedges list removing the edges which have been
// considered as retreatings of the SCS under analysis.
for (EdgeDescriptor Retreating : Retreatings) {
revng_assert(Backedges.count(Retreating) == 1);
Backedges.erase(Retreating);
}
bool NewHeadNeeded = false;
for (BasicBlockNodeBB *Node : RetreatingTargets) {
if (Node != FirstCandidate) {
NewHeadNeeded = true;
}
}
if (CombLogger.isEnabled()) {
CombLogger << "New head needed: " << NewHeadNeeded << "\n";
}
BasicBlockNodeBB *Head;
if (NewHeadNeeded) {
revng_assert(RetreatingTargets.size() > 1);
std::map<BasicBlockNodeBB *, int> RetreatingIdxMap;
BasicBlockNodeBB *const False = *RetreatingTargets.begin();
RetreatingIdxMap[False] = 0;
BasicBlockNodeBB *const True = *std::next(RetreatingTargets.begin());
RetreatingIdxMap[True] = 1;
unsigned Idx = 1;
Head = RootCFG.addDispatcher(Idx, True, False);
Meta->insertNode(Head);
Idx = 2;
using TargetIterator = std::set<BasicBlockNodeBB *>::iterator;
TargetIterator TgtIt = std::next(std::next(RetreatingTargets.begin()));
TargetIterator TgtEnd = RetreatingTargets.end();
for (; TgtIt != TgtEnd; ++TgtIt) {
BasicBlockNodeBB *New = RootCFG.addDispatcher(Idx, *TgtIt, Head);
Meta->insertNode(New);
RetreatingIdxMap[*TgtIt] = Idx;
Idx++;
Head = New;
}
revng_assert(Idx == RetreatingTargets.size());
for (EdgeDescriptor R : Retreatings) {
BasicBlockNodeBB *OriginalSource = R.first;
// If the original source is a set node, move it after the entry
// dispatcher.
if (OriginalSource->isSet()) {
BasicBlockNodeBB *OldSetNode = OriginalSource;
BasicBlockNodeBB *OldTarget = R.second;
Idx = RetreatingIdxMap[R.second];
revng_assert(OldSetNode->predecessor_size() == 1);
BasicBlockNodeBB *Predecessor = OldSetNode->getPredecessorI(0);
auto *SetNode = RootCFG.addSetStateNode(Idx, OldSetNode->getName());
Meta->insertNode(SetNode);
moveEdgeTarget(EdgeDescriptor(Predecessor, OldSetNode), Head);
// Search for the corresponding check node and move it.
BasicBlockNodeBB *CheckNode = getCheckPredecessor(OldTarget);
revng_assert(CheckNode->isCheck());
moveEdgeTarget(EdgeDescriptor(CheckNode, OldTarget), OldSetNode);
} else {
Idx = RetreatingIdxMap[R.second];
auto *SetNode = RootCFG.addSetStateNode(Idx, R.second->getName());
Meta->insertNode(SetNode);
moveEdgeTarget(EdgeDescriptor(R.first, R.second), SetNode);
addEdge(EdgeDescriptor(SetNode, Head));
}
}
// Move the incoming edge from the old head to new one.
std::vector<BasicBlockNodeBB *> Predecessors;
for (BasicBlockNodeBB *Predecessor : FirstCandidate->predecessors())
Predecessors.push_back(Predecessor);
for (BasicBlockNodeBB *Predecessor : Predecessors) {
if (!Meta->containsNode(Predecessor)) {
moveEdgeTarget(EdgeDescriptor(Predecessor, FirstCandidate), Head);
}
}
} else {
Head = FirstCandidate;
}
revng_assert(Head != nullptr);
if (CombLogger.isEnabled()) {
CombLogger << "New head name is: " << Head->getNameStr() << "\n";
}
// Successor refinement step.
std::set<BasicBlockNodeBB *> Successors = Meta->getSuccessors();
if (CombLogger.isEnabled()) {
CombLogger << "Region successors are:\n";
for (BasicBlockNodeBB *Node : Successors) {
CombLogger << Node->getNameStr() << "\n";
}
}
bool AnotherIteration = true;
while (AnotherIteration and Successors.size() > 1) {
AnotherIteration = false;
std::set<EdgeDescriptor> OutgoingEdges = Meta->getOutEdges();
std::vector<BasicBlockNodeBB *> Frontiers;
std::map<BasicBlockNodeBB *, pair<BasicBlockNodeBB *, BasicBlockNodeBB *>>
EdgeExtremal;
for (EdgeDescriptor Edge : OutgoingEdges) {
BasicBlockNodeBB *Frontier = RootCFG.addArtificialNode();
BasicBlockNodeBB *OldSource = Edge.first;
BasicBlockNodeBB *OldTarget = Edge.second;
EdgeExtremal[Frontier] = make_pair(OldSource, OldTarget);
moveEdgeTarget(Edge, Frontier);
addEdge(EdgeDescriptor(Frontier, OldTarget));
Meta->insertNode(Frontier);
Frontiers.push_back(Frontier);
}
DT.recalculate(RootCFG);
for (BasicBlockNodeBB *Frontier : Frontiers) {
for (BasicBlockNodeBB *Successor : Successors) {
if ((DT.dominates(Head, Successor))
and (DT.dominates(Frontier, Successor))
and !alreadyInMetaregion(MetaRegions, Successor)) {
Meta->insertNode(Successor);
AnotherIteration = true;
if (CombLogger.isEnabled()) {
CombLogger << "Identified new candidate for successor "
"refinement:";
CombLogger << Successor->getNameStr() << "\n";
}
}
}
}
// Remove the frontier nodes since we do not need them anymore.
for (BasicBlockNodeBB *Frontier : Frontiers) {
EdgeDescriptor Extremal = EdgeExtremal[Frontier];
BasicBlockNodeBB *OriginalSource = EdgeExtremal[Frontier].first;
BasicBlockNodeBB *OriginalTarget = EdgeExtremal[Frontier].second;
moveEdgeTarget({ OriginalSource, Frontier }, OriginalTarget);
RootCFG.removeNode(Frontier);
Meta->removeNode(Frontier);
}
Successors = Meta->getSuccessors();
}
// First Iteration outlining.
// Clone all the nodes of the SCS except for the head.
std::map<BasicBlockNodeBB *, BasicBlockNodeBB *> ClonedMap;
std::vector<BasicBlockNodeBB *> OutlinedNodes;
for (BasicBlockNodeBB *Node : Meta->nodes()) {
if (Node != Head) {
BasicBlockNodeBB *Clone = RootCFG.cloneNode(*Node);
Clone->setName(Node->getName().str() + " outlined");
ClonedMap[Node] = Clone;
// Add the nodes to the additional vector
OutlinedNodes.push_back(Clone);
}
}
// Restore edges between cloned nodes.
for (BasicBlockNodeBB *Node : Meta->nodes()) {
if (Node != Head) {
// Handle outgoing edges from SCS nodes.
if (Node->isCheck()) {
BasicBlockNodeBB *TrueSucc = Node->getTrue();
revng_assert(!Backedges.count(EdgeDescriptor(Node, TrueSucc)));
if (Meta->containsNode(TrueSucc)) {
if (TrueSucc == Head) {
ClonedMap.at(Node)->setTrue(Head);
} else {
ClonedMap.at(Node)->setTrue(ClonedMap.at(TrueSucc));
}
} else {
ClonedMap.at(Node)->setTrue(TrueSucc);
}
BasicBlockNodeBB *FalseSucc = Node->getFalse();
revng_assert(!Backedges.count(EdgeDescriptor(Node, FalseSucc)));
if (Meta->containsNode(FalseSucc)) {
if (FalseSucc == Head) {
ClonedMap.at(Node)->setFalse(Head);
} else {
ClonedMap.at(Node)->setFalse(ClonedMap.at(FalseSucc));
}
} else {
ClonedMap.at(Node)->setFalse(FalseSucc);
}
} else {
for (BasicBlockNodeBB *Successor : Node->successors()) {
revng_assert(!Backedges.count(EdgeDescriptor(Node, Successor)));
if (Meta->containsNode(Successor)) {
// Handle edges pointing inside the SCS.
if (Successor == Head) {
// Retreating edges should point to the new head.
addEdge(EdgeDescriptor(ClonedMap.at(Node), Head));
} else {
// Other edges should be restored between cloned nodes.
addEdge(EdgeDescriptor(ClonedMap.at(Node),
ClonedMap.at(Successor)));
}
} else {
// Edges exiting from the SCS should go to the right target.
addEdge(EdgeDescriptor(ClonedMap.at(Node), Successor));
}
}
}
// We need this temporary vector to avoid invalidating iterators.
std::vector<BasicBlockNodeBB *> Predecessors;
for (BasicBlockNodeBB *Predecessor : Node->predecessors()) {
Predecessors.push_back(Predecessor);
}
for (BasicBlockNodeBB *Predecessor : Predecessors) {
if (!Meta->containsNode(Predecessor)) {
// Is the edge we are moving a backedge ?.
if (CombLogger.isEnabled()) {
CombLogger << "Index region: " << Meta->getIndex() << "\n";
CombLogger << "Backedge that we would insert: "
<< Predecessor->getNameStr() << " -> "
<< Node->getNameStr() << "\n";
}
// Are we moving a backedge with the first iteration outlining?
revng_assert(!Backedges.count(EdgeDescriptor(Predecessor, Node)));
moveEdgeTarget(EdgeDescriptor(Predecessor, Node),
ClonedMap.at(Node));
}
}
}
}
// Vector which contains the additional set nodes that set the default value
// for the entry dispatcher.
std::vector<BasicBlockNodeBB *> DefaultEntrySet;
// Default set node for entry dispatcher.
if (NewHeadNeeded) {
revng_assert(Head->isCheck());
std::set<BasicBlockNodeBB *> SetCandidates;
for (BasicBlockNodeBB *Pred : Head->predecessors()) {
if (not Pred->isSet()) {
SetCandidates.insert(Pred);
}
}
unsigned Value = RetreatingTargets.size() - 1;
for (BasicBlockNodeBB *Pred : SetCandidates) {
BasicBlockNodeBB *Set = RootCFG.addSetStateNode(Value, Head->getName());
DefaultEntrySet.push_back(Set);
moveEdgeTarget(EdgeDescriptor(Pred, Head), Set);
addEdge(EdgeDescriptor(Set, Head));
// HACK: Consider using a multimap.
//
// Update the backedges set. Basically, when we place the default set
// node in case of an entry dispatcher, we need to take care to verify
// if the edge we are "moving" (inserting the set node before it) is a
// backedge, and in case update the information regarding the backedges
// present in the graph accordingly (the backedge becomes the edge
// departing from the set node).
bool UpdatedBackedges = true;
while (UpdatedBackedges) {
UpdatedBackedges = false;
for (EdgeDescriptor Backedge : Backedges) {
BasicBlockNodeBB *Source = Backedge.first;
BasicBlockNodeBB *Target = Backedge.second;
if (Source == Pred) {
Backedges.erase(Backedge);
Backedges.insert(EdgeDescriptor(Set, Head));
UpdatedBackedges = true;
break;
}
}
}
}
}
// Exit dispatcher creation.
// TODO: Factorize this out together with the head dispatcher creation.
bool NewExitNeeded = false;
BasicBlockNodeBB *Exit;
std::vector<BasicBlockNodeBB *> ExitDispatcherNodes;
if (Successors.size() > 1) {
NewExitNeeded = true;
}
if (CombLogger.isEnabled()) {
CombLogger << "New exit needed: " << NewExitNeeded << "\n";
}
if (NewExitNeeded) {
revng_assert(Successors.size() > 1);
std::map<BasicBlockNodeBB *, int> SuccessorsIdxMap;
BasicBlockNodeBB *const False = *Successors.begin();
SuccessorsIdxMap[False] = 0;
BasicBlockNodeBB *const True = *std::next(Successors.begin());
SuccessorsIdxMap[True] = 1;
unsigned Idx = 1;
Exit = RootCFG.addDispatcher(Idx, True, False);
ExitDispatcherNodes.push_back(Exit);
Idx = 2;
using SuccessorIterator = std::set<BasicBlockNodeBB *>::iterator;
SuccessorIterator SuccIt = std::next(std::next(Successors.begin()));
SuccessorIterator SuccEnd = Successors.end();
for (; SuccIt != SuccEnd; ++SuccIt) {
BasicBlockNodeBB *New = RootCFG.addDispatcher(Idx, *SuccIt, Exit);
ExitDispatcherNodes.push_back(New);
SuccessorsIdxMap[*SuccIt] = Idx;
Idx++;
Exit = New;
}
revng_assert(Idx == Successors.size());
std::set<EdgeDescriptor> OutEdges = Meta->getOutEdges();
for (EdgeDescriptor Edge : OutEdges) {
Idx = SuccessorsIdxMap.at(Edge.second);
auto *IdxSetNode = RootCFG.addSetStateNode(Idx, Edge.second->getName());
Meta->insertNode(IdxSetNode);
moveEdgeTarget(EdgeDescriptor(Edge.first, Edge.second), IdxSetNode);
addEdge(EdgeDescriptor(IdxSetNode, Edge.second));
// We should not be adding new backedges.
revng_assert(Backedges.count(Edge) == 0);
}
if (CombLogger.isEnabled()) {
CombLogger << "New exit name is: " << Exit->getNameStr() << "\n";
}
}
// Collapse Region.
// Create a new RegionCFG object for representing the collapsed region and
// populate it with the internal nodes.
Regions.push_back(RegionCFG<BasicBlock *>());
RegionCFG<BasicBlock *> &CollapsedGraph = Regions.back();
RegionCFG<BasicBlock *>::BBNodeMap SubstitutionMap{};
CollapsedGraph.setFunctionName(F.getName());
CollapsedGraph.setRegionName(std::to_string(Meta->getIndex()));
revng_assert(Head != nullptr);
// Create the collapsed node in the outer region.
BasicBlockNodeBB *Collapsed = RootCFG.createCollapsedNode(&CollapsedGraph);
// Hack: we should use a std::multimap here, so that we can update the
// target of the edgedescriptor in place without having to remove and insert
// from the set and invalidating iterators.
//
// Update the backedges set, checking that if a backedge of an outer region
// pointed to a node that now has been collapsed, now should point to the
// collapsed node, and that does not exists at this point a backedge which
// has as source a node that will be collapsed.
bool UpdatedBackedges = true;
while (UpdatedBackedges) {
UpdatedBackedges = false;
for (EdgeDescriptor Backedge : Backedges) {
BasicBlockNodeBB *Source = Backedge.first;
BasicBlockNodeBB *Target = Backedge.second;
revng_assert(!Meta->containsNode(Source));
if (Meta->containsNode(Target)) {
revng_assert(Target == Head);
Backedges.erase(Backedge);
Backedges.insert(EdgeDescriptor(Source, Collapsed));
UpdatedBackedges = true;
break;
}
}
}
CollapsedGraph.insertBulkNodes(Meta->getNodes(), Head, SubstitutionMap);
// Connect the break and continue nodes with the necessary edges (we create
// a new break/continue node for each outgoing or retreating edge).
CollapsedGraph.connectContinueNode();
std::set<EdgeDescriptor> OutgoingEdges = Meta->getOutEdges();
CollapsedGraph.connectBreakNode(OutgoingEdges, SubstitutionMap);
// Connect the old incoming edges to the collapsed node.
std::set<EdgeDescriptor> IncomingEdges = Meta->getInEdges();
for (EdgeDescriptor Edge : IncomingEdges) {
BasicBlockNodeBB *OldSource = Edge.first;
revng_assert(Edge.second == Head);
// Check if the old edge was a backedge edge, and in case update the
// information about backedges accordingly.
if (Backedges.count(Edge) == 1) {
Backedges.erase(Edge);
Backedges.insert(EdgeDescriptor(OldSource, Collapsed));
}
moveEdgeTarget(Edge, Collapsed);
}
// Connect the outgoing edges to the collapsed node.
if (NewExitNeeded) {
revng_assert(Exit != nullptr);
addEdge(EdgeDescriptor(Collapsed, Exit));
} else {
// Double check that we have at most a single successor
revng_assert(Successors.size() <= 1);
if (Successors.size() == 1) {
// Connect the collapsed node to the unique successor
BasicBlockNodeBB *Successor = *Successors.begin();
addEdge(EdgeDescriptor(Collapsed, Successor));
}
}
// Remove collapsed nodes from the outer region.
for (BasicBlockNodeBB *Node : Meta->nodes()) {
if (CombLogger.isEnabled()) {
CombLogger << "Removing from main graph node :" << Node->getNameStr()
<< "\n";
}
RootCFG.removeNode(Node);
removeFromRPOT(RPOT, Node);
}
// Substitute in the other SCSs the nodes of the current SCS with the
// collapsed node and the exit dispatcher structure.
for (MetaRegionBB *OtherMeta : OrderedMetaRegions) {
if (OtherMeta != Meta) {
OtherMeta->updateNodes(Meta->getNodes(),
Collapsed,
ExitDispatcherNodes,
DefaultEntrySet,
OutlinedNodes);
}
}
// Replace the pointers inside SCS.
Meta->replaceNodes(CollapsedGraph.getNodes());
// Remove useless nodes inside the SCS (like dandling break/continue)
CollapsedGraph.removeNotReachables(OrderedMetaRegions);
// Serialize the newly collapsed SCS region.
if (CombLogger.isEnabled()) {
CombLogger << "Dumping CFG of metaregion " << Meta->getIndex() << "\n";
CollapsedGraph.dumpDotOnFile("dots",
F.getName(),
"In-" + std::to_string(Meta->getIndex()));
CombLogger << "Dumping main graph snapshot post restructuring\n";
RootCFG.dumpDotOnFile("dots",
F.getName(),
"Out-post-" + std::to_string(Meta->getIndex()));
}
// Remove not reachables nodes from the graph at each iteration.
RootCFG.removeNotReachables(OrderedMetaRegions);
// Check that the newly created collapsed region is acyclic.
revng_assert(CollapsedGraph.isDAG());
}
// Serialize the newly collapsed SCS region.
if (CombLogger.isEnabled()) {
CombLogger << "Dumping main graph before final purge\n";
RootCFG.dumpDotOnFile("dots", F.getName(), "Final-before-purge");
}
// Remove not reachables nodes from the main final graph.
RootCFG.removeNotReachables(OrderedMetaRegions);
// Serialize the newly collapsed SCS region.
if (CombLogger.isEnabled()) {
CombLogger << "Dumping main graph after final purge\n";
RootCFG.dumpDotOnFile("dots", F.getName(), "Final-after-purge");
}
// Print metaregions after ordering.
if (CombLogger.isEnabled()) {
CombLogger << "\n";
CombLogger << "Metaregions after collapse:\n";
for (auto *Meta : OrderedMetaRegions) {
CombLogger << "\n";
CombLogger << Meta << "\n";
CombLogger << "With index " << Meta->getIndex() << "\n";
CombLogger << "With size " << Meta->nodes_size() << "\n";
auto &Nodes = Meta->getNodes();
CombLogger << "Is composed of nodes:\n";
for (auto *Node : Nodes) {
CombLogger << Node->getNameStr() << "\n";
}
CombLogger << "Has parent: " << Meta->getParent() << "\n";
CombLogger << "Is SCS: " << Meta->isSCS() << "\n";
}
}
// Check that the root region is acyclic at this point.
revng_assert(RootCFG.isDAG());
// Invoke the AST generation for the root region.
RootCFG.generateAst();
// Serialize final AST on file
if (CombLogger.isEnabled()) {
RootCFG.getAST().dumpOnFile("ast", F.getName(), "Final");
}
// Early exit if the AST generation produced a version of the AST which is
// identical to the cached version.
// In that case there's no need to flatten the RegionCFG.
// TODO: figure out how to decide when we're done
if (Done)
return false;
if (CombLogger.isEnabled()) {
CombLogger << "Dumping main graph after Flattening\n";
RootCFG.dumpDotOnFile("dots", F.getName(), "final-before-flattening");
}
flattenRegionCFGTree(RootCFG);
// Collect the number of cloned nodes introduced by the comb for a single
// `llvm::BasicBlock`, information which is needed later in the
// `MarkForSerialization` pass.
for (BasicBlockNodeBB *BBNode : RootCFG.nodes()) {
BasicBlock *BB = BBNode->getOriginalNode();
if (BBNode->isCode()) {
revng_assert(BB != nullptr);
NDuplicates[BB] += 1;
} else {
revng_assert(BB == nullptr);
}
}
// Serialize final AST after flattening on file
if (CombLogger.isEnabled()) {
RootCFG.getAST().dumpOnFile("ast", F.getName(), "Final-after-flattening");
}
// Serialize the newly collapsed SCS region.
if (CombLogger.isEnabled()) {
CombLogger << "Dumping main graph after Flattening\n";
RootCFG.dumpDotOnFile("dots", F.getName(), "final-after-flattening");
}
return false;
}