Files
revng-revng/include/revng/RestructureCFG/RegionCFGTreeImpl.h
2025-02-13 13:10:51 +02:00

1445 lines
51 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iterator>
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/GenericDomTreeConstruction.h"
#include "llvm/Support/raw_os_ostream.h"
#include "revng/ADT/ReversePostOrderTraversal.h"
#include "revng/MFP/MFP.h"
#include "revng/RestructureCFG/ASTTree.h"
#include "revng/RestructureCFG/BasicBlockNodeBB.h"
#include "revng/RestructureCFG/MetaRegionBB.h"
#include "revng/RestructureCFG/RegionCFGTree.h"
#include "revng/RestructureCFG/Utils.h"
#include "revng/Support/GraphAlgorithms.h"
#include "revng/Support/IRHelpers.h"
template<typename IterT>
bool intersects(IterT I1, IterT E1, IterT I2, IterT E2) {
while ((I1 != E1) and (I2 != E2)) {
if (*I1 < *I2)
++I1;
else if (*I2 < *I1)
++I2;
else
return true;
}
return false;
}
template<typename IterT>
bool isDisjoint(IterT I1, IterT E1, IterT I2, IterT E2) {
return not intersects(I1, E1, I2, E2);
}
template<typename RangeT>
bool intersects(const RangeT &R1, const RangeT &R2) {
return intersects(R1.begin(), R1.end(), R2.begin(), R2.end());
}
template<typename RangeT>
bool isDisjoint(const RangeT &R1, const RangeT &R2) {
return not intersects(R1, R2);
}
unsigned const SmallSetSize = 16;
// llvm::SmallPtrSet is a handy way to store set of BasicBlockNode pointers.
template<class NodeT>
using SmallPtrSet = llvm::SmallPtrSet<BasicBlockNode<NodeT> *, SmallSetSize>;
template<class NodeT>
inline void RegionCFG<NodeT>::setFunctionName(std::string Name) {
FunctionName = Name;
}
template<class NodeT>
inline void RegionCFG<NodeT>::setRegionName(std::string Name) {
RegionName = Name;
}
template<class NodeT>
inline std::string RegionCFG<NodeT>::getFunctionName() const {
return FunctionName;
}
template<class NodeT>
inline std::string RegionCFG<NodeT>::getRegionName() const {
return RegionName;
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
RegionCFG<NodeT>::addNode(NodeT Node, llvm::StringRef Name) {
using BBNodeT = BasicBlockNodeT;
BlockNodes.emplace_back(std::make_unique<BBNodeT>(this, Node, Name));
BasicBlockNodeT *Result = BlockNodes.back().get();
revng_log(CombLogger,
"Building " << Name << " at address: " << Result << "\n");
return Result;
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
RegionCFG<NodeT>::cloneNode(BasicBlockNodeT &OriginalNode) {
using BBNodeT = BasicBlockNodeT;
BlockNodes.emplace_back(std::make_unique<BBNodeT>(OriginalNode, this));
BasicBlockNodeT *New = BlockNodes.back().get();
New->setName(OriginalNode.getName().str() + " cloned");
New->setWeaved(OriginalNode.isWeaved());
return New;
}
template<class NodeT>
inline void RegionCFG<NodeT>::removeNode(BasicBlockNodeT *Node) {
revng_log(CombLogger, "Removing node named: " << Node->getNameStr() << "\n");
for (BasicBlockNodeT *Predecessor : Node->predecessors())
Predecessor->removeSuccessor(Node);
for (BasicBlockNodeT *Successor : Node->successors())
Successor->removePredecessor(Node);
for (auto It = BlockNodes.begin(); It != BlockNodes.end(); It++) {
if ((*It).get() == Node) {
DeadNodesQuarantine.push_back(std::move(*It));
BlockNodes.erase(It);
break;
}
}
}
template<class NodeT>
using BBNodeT = typename RegionCFG<NodeT>::BasicBlockNodeT;
template<class NodeT>
inline void copyNeighbors(BBNodeT<NodeT> *Dst, BBNodeT<NodeT> *Src) {
for (const auto &P : Src->labeled_successors())
Dst->addLabeledSuccessor(P);
for (const auto &P : Src->labeled_predecessors())
Dst->addLabeledPredecessor(P);
}
template<class NodeT>
inline void RegionCFG<NodeT>::insertBulkNodes(BasicBlockNodeTSet &Nodes,
BasicBlockNodeT *Head,
BBNodeMap &SubMap,
std::set<EdgeDescriptor> &Out,
llvm::SmallVector<EdgeDescriptor>
&ContinueBackedges) {
revng_assert(BlockNodes.empty());
for (BasicBlockNodeT *Node : Nodes) {
BlockNodes.emplace_back(std::make_unique<BasicBlockNodeT>(*Node, this));
BasicBlockNodeT *New = BlockNodes.back().get();
SubMap[Node] = New;
// The copy constructor used above does not bring along the successors and
// the predecessors, neither adjusts the parent.
// The following lines are a hack to fix this problem, but they momentarily
// build a broken data structure where the predecessors and the successors
// of the New BasicBlockNodes in *this still refer to the BasicBlockNodes in
// the Parent CFGRegion of Nodes. This will be fixed later by updatePointers
copyNeighbors<NodeT>(New, Node);
}
// We now create the break nodes, and put in the `SubMap` the correspondence
// between each target of the outgoing edges, and the newly created break
// nodes. The adjustment of the break target must be handled now and not
// postponed in a later stage, in order to avoid losing the ordering of
// successors (e.g., then and else, if then goes to a break).
// In addition, since multiple break can go to the same successors, we keep a
// mapping of successor -> corresponding break, so that we can reuse it.
BBNodeMap BreakMap;
for (EdgeDescriptor Edge : Out) {
// Check if we already have a break for each outgoing edge, or create it.
BasicBlockNodeT *Break = nullptr;
auto It = BreakMap.find(Edge.second);
if (It != BreakMap.end()) {
Break = It->second;
} else {
Break = addBreak();
BreakMap[Edge.second] = Break;
}
// Extract from the old predecessor edge the corresponding labels.
auto OldPredEdgeWithLabels = Edge.second->getPredecessorEdge(Edge.first);
auto &OldEdgeInfo = OldPredEdgeWithLabels.second;
// We add the old predecessor, so that when `updatePointers` is called it
// will adjust the predecessor to the correspondent one found in the
// `SubMap`.
Break->addLabeledPredecessor(std::make_pair(Edge.first, OldEdgeInfo));
// We leave to the `updatePointers` helper the task of adding the break as
// new target for the exiting node, by inserting specific information in the
// `SubMap`.
SubMap[Edge.second] = Break;
}
revng_assert(Head != nullptr);
EntryNode = SubMap[Head];
revng_assert(EntryNode != nullptr);
// Fix the hack above
for (BBNodeTUniquePtr &Node : BlockNodes)
Node->updatePointers(SubMap);
// Connect all the `ContinueBackedges` to `continue` nodes
for (EdgeDescriptor &Backedge : ContinueBackedges) {
// Confirm that the retreating edge points to the `Head` (the previous entry
// node)
revng_assert(Backedge.second == Head);
// Create a new continue node for each retreating edge.
BasicBlockNodeT *Continue = addContinue();
BasicBlockNodeT *RetreatingSource = SubMap[Backedge.first];
revng_assert(RetreatingSource != nullptr);
moveEdgeTarget(EdgeDescriptor(RetreatingSource, EntryNode), Continue);
}
// After the processing, confirm that the `EntryNode` has no more predecessor
revng_assert(EntryNode->predecessor_size() == 0);
}
template<class NodeT>
template<typename StreamT>
inline void
RegionCFG<NodeT>::streamNode(StreamT &S, const BasicBlockNodeT *BB) const {
unsigned NodeID = BB->getID();
S << "\"" << NodeID << "\"";
S << " ["
<< "label=\"ID: " << NodeID << " Name: " << BB->getNameStr();
if (BB->isCollapsed())
S << " Idx: " << BB->getCollapsedRegionName();
S << "\"";
if (BB == EntryNode)
S << ",fillcolor=green,style=filled";
S << "];\n";
}
/// Dump a GraphViz file on stdout representing this function
template<class NodeT>
template<typename StreamT>
inline void RegionCFG<NodeT>::dumpDot(StreamT &S) const {
S << "digraph CFGFunction {\n";
for (const std::unique_ptr<BasicBlockNode<NodeT>> &BB : BlockNodes) {
streamNode(S, BB.get());
unsigned Counter = 0;
for (const auto &[Successor, EdgeInfo] : BB->labeled_successors()) {
unsigned PredID = BB->getID();
unsigned SuccID = Successor->getID();
S << "\"" << PredID << "\""
<< " -> \"" << SuccID << "\"";
if (EdgeInfo.Inlined)
S << " [color=purple, label=" << Counter << "];\n";
else
S << " [color=green, label=" << Counter << "];\n";
Counter++;
}
}
S << "}\n";
}
template<class NodeT>
inline void RegionCFG<NodeT>::dumpCFGOnFile(const std::string &FileName) const {
std::error_code EC;
llvm::raw_fd_ostream DotFile(FileName, EC);
revng_check(not EC, "Could not open file for printing RegionCFG dot");
dumpDot(DotFile);
}
template<class NodeT>
inline void RegionCFG<NodeT>::dumpCFGOnFile(const std::string &FuncName,
const std::string &FolderName,
const std::string &FileName) const {
const std::string GraphDir = "debug-graphs";
std::error_code EC = llvm::sys::fs::create_directory(GraphDir);
revng_check(not EC, "Could not create directory to print RegionCFG dot");
EC = llvm::sys::fs::create_directory(GraphDir + "/" + FuncName);
revng_check(not EC, "Could not create directory to print RegionCFG dot");
const std::string PathName = GraphDir + "/" + FuncName + "/" + FolderName;
EC = llvm::sys::fs::create_directory(PathName);
revng_check(not EC, "Could not create directory to print RegionCFG dot");
dumpCFGOnFile(PathName + "/" + FileName);
}
template<class NodeT>
inline bool RegionCFG<NodeT>::purgeIfTrivialDummy(BBNodeT *Dummy) {
RegionCFG<NodeT> &Graph = *this;
revng_assert(not Dummy->isEmpty() or Dummy->predecessor_size() != 0);
if ((Dummy->isEmpty()) and (Dummy->predecessor_size() == 1)
and (Dummy->successor_size() == 1)) {
revng_log(CombLogger, "Purging dummy node " << Dummy->getNameStr());
BasicBlockNode<NodeT> *Predecessor = *Dummy->predecessors().begin();
BasicBlockNode<NodeT> *Successor = *Dummy->successors().begin();
// Connect directly predecessor and successor, and remove the dummy node
// under analysis
moveEdgeTarget({ Predecessor, Dummy }, Successor);
Graph.removeNode(Dummy);
return true;
}
return false;
}
template<class NodeT>
inline bool RegionCFG<NodeT>::purgeTrivialDummies() {
RegionCFG<NodeT> &Graph = *this;
bool RemovedNow = true;
bool Removed = false;
while (RemovedNow) {
RemovedNow = false;
for (auto *Node : Graph) {
RemovedNow = purgeIfTrivialDummy(Node);
if (RemovedNow) {
Removed = true;
break;
}
}
}
return Removed;
}
template<class NodeT>
inline void RegionCFG<NodeT>::purgeVirtualSink(BasicBlockNode<NodeT> *Sink) {
RegionCFG<NodeT> &Graph = *this;
BasicBlockNodeTVect WorkList;
BasicBlockNodeTVect PurgeList;
WorkList.push_back(Sink);
while (!WorkList.empty()) {
BasicBlockNode<NodeT> *CurrentNode = WorkList.back();
WorkList.pop_back();
if (CurrentNode->isEmpty()) {
PurgeList.push_back(CurrentNode);
for (BasicBlockNode<NodeT> *Predecessor : CurrentNode->predecessors()) {
WorkList.push_back(Predecessor);
}
}
}
for (BasicBlockNode<NodeT> *Purge : PurgeList) {
Graph.removeNode(Purge);
}
}
inline bool isGreater(unsigned Op1, unsigned Op2) {
unsigned MultiplicativeFactor = 1;
if (Op1 > (MultiplicativeFactor * Op2)) {
return true;
} else {
return false;
}
}
template<class NodeT>
inline BasicBlockNode<NodeT> *
RegionCFG<NodeT>::cloneUntilExit(BasicBlockNode<NodeT> *Node,
BasicBlockNode<NodeT> *Sink) {
// Clone the postdominator node.
BBNodeMap CloneMap;
BasicBlockNode<NodeT> *Clone = cloneNode(*Node);
// Insert the postdominator clone in the map.
CloneMap[Node] = Clone;
BasicBlockNodeTVect WorkList;
WorkList.push_back(Node);
// Set of nodes which have been already processed.
BasicBlockNodeTSet AlreadyProcessed;
while (!WorkList.empty()) {
BasicBlockNode<NodeT> *CurrentNode = WorkList.back();
WorkList.pop_back();
// Ensure that we are not processing the sink node.
revng_assert(CurrentNode != Sink);
auto &&[_, Inserted] = AlreadyProcessed.insert(CurrentNode);
if (!Inserted)
continue;
// Get the clone of the `CurrentNode`.
BasicBlockNode<NodeT> *CurrentClone = CloneMap.at(CurrentNode);
for (const auto &[Succ, Labels] : CurrentNode->labeled_successors()) {
// If the successor is not the sink, create and edge that directly
// connects it.
if (Succ != Sink) {
BasicBlockNode<NodeT> *SuccessorClone = nullptr;
// The clone of the successor node already exists.
auto CloneIt = CloneMap.find(Succ);
if (CloneIt != CloneMap.end()) {
SuccessorClone = CloneIt->second;
} else {
// The clone of the successor does not exist, create it in place.
SuccessorClone = cloneNode(*Succ);
CloneMap[Succ] = SuccessorClone;
}
// Create the edge to the clone of the successor.
revng_assert(SuccessorClone != nullptr);
addEdge(EdgeDescriptor(CurrentClone, SuccessorClone), Labels);
// Add the successor to the worklist.
WorkList.push_back(Succ);
}
}
}
return Clone;
}
template<class NodeT>
inline void RegionCFG<NodeT>::untangle() {
// TODO: Here we handle only conditional nodes with two successors. We should
// consider extending the untangle procedure also to conditional nodes
// with more than two successors (switch nodes).
revng_assert(isDAG());
RegionCFG<NodeT> &Graph = *this;
// Add a new virtual sink node to computer the postdominator.
BasicBlockNode<NodeT> *Sink = Graph.addArtificialNode("Sink");
for (auto *Node : Graph)
if (Node != Sink and Node->successor_size() == 0)
addPlainEdge(EdgeDescriptor(Node, Sink));
if (CombLogger.isEnabled()) {
Graph.dumpCFGOnFile(FunctionName,
"untangle",
"region-" + RegionName + "-before-untangle");
}
// Map which contains the precomputed weight for each node in the graph. In
// case of a code node the weight will be equal to the number of instruction
// in the original basic block; in case of a collapsed node the weight will be
// the sum of the weights of all the nodes contained in the collapsed graph.
std::map<BasicBlockNode<NodeT> *, size_t> WeightMap;
for (BasicBlockNode<NodeT> *Node : Graph.nodes()) {
WeightMap[Node] = Node->getWeight();
}
// Collect all the conditional nodes in the graph into a vector sorted in
// Reverse Post-Order.
BasicBlockNodeTVect ConditionalNodes;
{
BasicBlockNodeTSet ConditionalNodesSet;
for (auto *Node : Graph)
if (Node->successor_size() == 2)
ConditionalNodesSet.insert(Node);
llvm::ReversePostOrderTraversal<BasicBlockNode<NodeT> *> RPOT(EntryNode);
for (BasicBlockNode<NodeT> *RPOTBB : RPOT) {
if (ConditionalNodesSet.contains(RPOTBB)) {
ConditionalNodes.push_back(RPOTBB);
}
}
}
while (not ConditionalNodes.empty()) {
BasicBlockNode<NodeT> *Conditional = ConditionalNodes.back();
ConditionalNodes.pop_back();
// Update the information of the dominator and postdominator trees.
DT.recalculate(Graph);
IFPDT.recalculate(Graph);
// Update the postdominator
BasicBlockNodeT *PostDominator = IFPDT[Conditional]->getIDom()->getBlock();
// Ensure that we have both the successors.
revng_assert(Conditional->successor_size() == 2);
// Get the first node of the then and else branches respectively.
// TODO: Check that this is the right way to do this. At this point we
// cannot assume that we have the `getThen()` and `getFalse()`
// methods.
BasicBlockNode<NodeT> *ThenChild = Conditional->getSuccessorI(0);
BasicBlockNode<NodeT> *ElseChild = Conditional->getSuccessorI(1);
// Collect all the nodes laying between the branches
llvm::SmallSetVector<BasicBlockNode<NodeT> *, 4> ThenNodes;
llvm::SmallSetVector<BasicBlockNode<NodeT> *, 4> ElseNodes;
// If the `PostDominator` is present, we use the `nodesBetween` primitive to
// stop at the `PostDominator`, otherwise we collect all the reachable nodes
if (PostDominator != nullptr) {
ThenNodes = nodesBetween(ThenChild, PostDominator);
ElseNodes = nodesBetween(ElseChild, PostDominator);
} else {
ThenNodes = findReachableNodes(ThenChild);
ElseNodes = findReachableNodes(ElseChild);
}
// Remove the postdominator from both the sets.
ThenNodes.remove(PostDominator);
ElseNodes.remove(PostDominator);
const auto EdgeDominates = [DT = &DT](const EdgeDescriptor &E,
BasicBlockNodeT *N) {
const auto &[Src, Dst] = E;
if (not DT->dominates(Dst, N))
return false;
if (Dst->predecessor_size() < 2)
return true;
bool DuplicateEdge = false;
for (BasicBlockNodeT *Pred : Dst->predecessors()) {
if (Pred == Src) {
if (DuplicateEdge)
return false;
DuplicateEdge = true;
continue;
}
if (not DT->dominates(Dst, Pred))
return false;
}
return true;
};
// New implementation of the dominance criterion which uses the then and
// else edges to compute the dominance.
if (EdgeDominates({ Conditional, ElseChild }, ElseChild)) {
const auto DominatedByElse = [DT = &DT, ElseChild](auto *Node) {
return DT->dominates(ElseChild, Node);
};
ElseNodes.remove_if(DominatedByElse);
}
if (EdgeDominates({ Conditional, ThenChild }, ThenChild)) {
const auto DominatedByThen = [DT = &DT, ThenChild](auto *Node) {
return DT->dominates(ThenChild, Node);
};
ThenNodes.remove_if(DominatedByThen);
}
// Compute the weight of the `then` and `else` branches.
unsigned ThenWeight = 0;
unsigned ElseWeight = 0;
for (BasicBlockNode<NodeT> *Node : ThenNodes) {
ThenWeight += WeightMap[Node];
}
for (BasicBlockNode<NodeT> *Node : ElseNodes) {
ElseWeight += WeightMap[Node];
}
// The weight of the nodes placed after the immediate postdominator is the
// sum of all the weights of the nodes which are reachable starting from the
// immediate post dominator and the sink node (to which all the exits have
// been connected).
// If the post dominator is `nullptr` (meaning that it is the `VirtualRoot`
// node on the filtered post dominator tree), we can skip the computation of
// this weight.
unsigned PostDominatorWeight = 0;
if (PostDominator != nullptr) {
llvm::SmallSetVector<BasicBlockNode<NodeT> *, 4>
PostDominatorToExit = nodesBetween(PostDominator, Sink);
for (BasicBlockNode<NodeT> *Node : PostDominatorToExit) {
PostDominatorWeight += WeightMap[Node];
}
}
// Criterion which decides if we can apply the untangle optimization to the
// conditional under analysis.
// We define 3 weights:
// - 1) weight(then) + weight(else)
// - 2) weight(then) + weight(postdom)
// - 3) weight(else) + weight(postdom)
//
// We need to operate the split if:
// 2 >> 3
// 1 >> 3
// and specifically we need to split the `else` branch.
//
// We need to operate the split if:
// 3 >> 2
// 1 >> 2
// and specifically we need to split the `then` branch.
//
// We can also define in a dynamic way the >> operator, so we can change the
// threshold that triggers the split.
unsigned CombingCost = ThenWeight + ElseWeight;
unsigned UntangleThenCost = ThenWeight + PostDominatorWeight;
unsigned UntangleElseCost = ElseWeight + PostDominatorWeight;
unsigned UntanglingCost = std::min(UntangleThenCost, UntangleElseCost);
if (isGreater(CombingCost, UntanglingCost)) {
revng_log(CombLogger, FunctionName << ":");
revng_log(CombLogger, RegionName << ":");
revng_log(CombLogger,
"Found untangle candidate " << Conditional->getNameStr());
revng_log(CombLogger, "CombingCost:" << CombingCost);
revng_log(CombLogger, "UntangleThenCost:" << UntangleThenCost);
revng_log(CombLogger, "UntangleElseCost:" << UntangleElseCost);
// Register a tentative untangle in the dedicated counter.
UntangleTentativeCounter++;
// Register an actual untangle in the dedicated counter.
UntanglePerformedCounter++;
revng_log(CombLogger, "Actually splitting node");
auto *ToUntangle = (UntangleThenCost > UntangleElseCost) ? ElseChild :
ThenChild;
// Perform the split from the first node of the then/else branches.
// We fully inline all the nodes belonging to the branch we are untangling
// till the exit node.
BasicBlockNode<NodeT> *UntangledChild = cloneUntilExit(ToUntangle, Sink);
// Move the edge coming out of the conditional node to the new clone of
// the node.
moveEdgeTarget(EdgeDescriptor(Conditional, ToUntangle), UntangledChild);
// We mark the edge going into the `UntangleChild` as an inlined edge.
// In this way, in all the next phases, these edges will be ignored by the
// dominator and postdominator trees.
markEdgeInlined(EdgeDescriptor(Conditional, UntangledChild));
// Remove nodes that have no predecessors (nodes that are the result of
// node cloning and that remains dandling around).
// While doing this, update InlineEdges.
bool Removed = true;
while (Removed) {
Removed = false;
BasicBlockNode<NodeT> *Entry = &getEntryNode();
for (auto It = begin(); It != end(); ++It) {
if ((Entry != *It and (*It)->predecessor_size() == 0)) {
removeNode(*It);
Removed = true;
break;
}
}
}
}
}
if (CombLogger.isEnabled()) {
Graph.dumpCFGOnFile(FunctionName,
"untangle",
"region-" + RegionName + "-after-untangle");
}
// Remove the sink node.
purgeVirtualSink(Sink);
if (CombLogger.isEnabled()) {
Graph.dumpCFGOnFile(FunctionName,
"untangle",
"region-" + RegionName
+ "-after-untangle-after-sink-removal");
}
}
template<class NodeT>
struct ReachableExitsAnalysis
: public SetUnionLattice<std::set<BasicBlockNode<NodeT> *>> {
using Label = BasicBlockNode<NodeT> *;
using GraphType = RegionCFG<NodeT> *;
using LatticeElement = typename SetUnionLattice<
std::set<BasicBlockNode<NodeT> *>>::LatticeElement;
static LatticeElement applyTransferFunction(const Label &L,
const LatticeElement E) {
const auto IsInlined = [](const auto &NodeLabelPair) {
return NodeLabelPair.second.Inlined;
};
if (bool IsExit = llvm::all_of(L->labeled_successors(), IsInlined); IsExit)
return { L };
return E;
}
};
template<class NodeT>
inline void RegionCFG<NodeT>::inflate() {
// Call the untangle preprocessing.
untangle();
revng_assert(isDAG());
// Apply the comb to a RegionCFG object.
RegionCFG<NodeT> &Graph = *this;
BasicBlockNode<NodeT> *Entry = &Graph.getEntryNode();
if (CombLogger.isEnabled()) {
revng_log(CombLogger, "Entry node is: " << Entry->getNameStr());
Graph.dumpCFGOnFile(FunctionName,
"inflate",
"region-" + RegionName + "-before-inflate");
}
// Collect the sets of reachable exits from each node that is a successor of a
// node that induces duplication.
std::vector<BasicBlockNode<NodeT> *> Exits;
for (auto *Exit : Graph)
if (llvm::all_of(Exit->labeled_successors(),
[](const auto &Pair) { return Pair.second.Inlined; }))
Exits.push_back(Exit);
revng_log(CombLogger, "Num exits: " << Exits.size());
revng_log(CombLogger, "Region Size: " << Graph.size());
using REA = ReachableExitsAnalysis<NodeT>;
using Inverse = llvm::Inverse<typename REA::GraphType>;
auto ReachableExits = MFP::getMaximalFixedPoint<
REA,
llvm::GraphTraits<Inverse>,
llvm::Inverse<BasicBlockNode<NodeT> *>>({}, &Graph, {}, {}, {}, Exits);
// Refresh information of dominator and postdominator trees.
DT.recalculate(Graph);
IFPDT.recalculate(Graph);
// Map to hold, for each conditional node that initiates combing, the node
// that will be used to detect the point where combing needs to stop
// duplicating node. This is the immediate post dominator for most nodes, but
// we have a special case for the case nodes of switches.
BBNodeMap ConditionalToCombEnd;
// Collect all the conditional nodes in the graph.
// This is the working list of conditional nodes on which we will operate and
// will contain only the filtered conditionals.
BasicBlockNodeTSet ConditionalNodesSet;
std::vector<BasicBlockNode<NodeT> *> Switches;
for (BBNodeT *Node : Graph) {
switch (Node->successor_size()) {
case 0:
case 1:
// We don't need to add it to the conditional nodes vector.
break;
case 2: {
BasicBlockNodeTSet ThenExits = ReachableExits.at(Node->getSuccessorI(0))
.OutValue;
BasicBlockNodeTSet ElseExits = ReachableExits.at(Node->getSuccessorI(1))
.OutValue;
// Add the conditional node to the set of nodes processed by the inflate.
ConditionalNodesSet.insert(Node);
BasicBlockNode<NodeT> *PostDom = IFPDT[Node]->getIDom()->getBlock();
bool New = ConditionalToCombEnd.insert({ Node, PostDom }).second;
revng_assert(New);
// If the exit nodes reachable from the Then and from the Else are not
// disjoint, then the conditional node is not eligible for having its
// successor nodes marked as inlined.
if (not isDisjoint(ThenExits, ElseExits))
break;
// Check that we do not dominate at maximum on of the two sets of
// reachable exits.
bool ThenIsDominated = true;
bool ElseIsDominated = true;
for (BasicBlockNode<NodeT> *Exit : ThenExits) {
if (not DT.dominates(Node, Exit)) {
ThenIsDominated = false;
break;
}
}
for (BasicBlockNode<NodeT> *Exit : ElseExits) {
if (not DT.dominates(Node, Exit)) {
ElseIsDominated = false;
break;
}
}
// If there is one set of exits that Node entirely dominates, we can
// blacklist it because it will never cause duplication.
// The reason is that the set of exits that we dominate can be completely
// inlined and absorbed either into the then or into the else.
if (ThenIsDominated or ElseIsDominated) {
revng_log(CombLogger,
"Blacklisted conditional: " << Node->getNameStr());
// Mark then or else edges as inlined during the conditional
// blacklisting. In case both the `then` and `else` branches are
// completely dominated, we mark both as inlineable.
BasicBlockNodeT *Then = Node->getSuccessorI(0);
BasicBlockNodeT *Else = Node->getSuccessorI(1);
// TODO: Verify that, for nodes where the inling is applied to both the
// branches, the immediate post-dominator on the filtered postdom
// is the conditional node itself. Verify also that, if this is
// the situation, the inflating procedure does not go in a loop
// (it should stop immediately if conditional == postdom).
if (ThenIsDominated and ElseIsDominated) {
markEdgeInlined(EdgeDescriptor(Node, Then));
markEdgeInlined(EdgeDescriptor(Node, Else));
} else if (ThenIsDominated) {
markEdgeInlined(EdgeDescriptor(Node, Then));
} else if (ElseIsDominated) {
markEdgeInlined(EdgeDescriptor(Node, Else));
} else {
revng_abort();
}
}
} break;
default: {
Switches.push_back(Node);
} break;
}
}
for (auto *Switch : Switches) {
llvm::SmallPtrSet<BasicBlockNode<NodeT> *, 8> CaseNodes;
for (auto *SwitchCase : Switch->successors())
CaseNodes.insert(SwitchCase);
for (auto *Case : CaseNodes) {
auto *DummyCase = addArtificialNode("dummy case");
moveEdgeTarget(EdgeDescriptor(Switch, Case), DummyCase);
addPlainEdge(EdgeDescriptor(DummyCase, Case));
ConditionalNodesSet.insert(DummyCase);
BasicBlockNode<NodeT> *PostDom = IFPDT[Switch]->getIDom()->getBlock();
// Combing of switch cases continues until the post dominator of the
// switch, not until the post dominator of the case.
bool New = ConditionalToCombEnd.insert({ DummyCase, PostDom }).second;
revng_assert(New);
}
}
if (CombLogger.isEnabled()) {
revng_log(CombLogger, "Conditional nodes present in the graph are:");
for (BasicBlockNode<NodeT> *Node : ConditionalNodesSet)
revng_log(CombLogger, Node->getNameStr());
}
// Equivalence-class like set to keep track of all the cloned nodes created
// starting from an original node.
std::map<BasicBlockNode<NodeT> *, SmallPtrSet<NodeT>> NodesEquivalenceClass;
// Map to keep track of the cloning relationship.
BBNodeMap CloneToOriginalMap;
// Initialize a list containing the reverse post order of the nodes of the
// graph.
std::list<BasicBlockNode<NodeT> *> RevPostOrderList;
// Vector of conditional nodes, to be filled in reverse post order.
BasicBlockNodeTVect ConditionalNodes;
llvm::ReversePostOrderTraversal<BasicBlockNode<NodeT> *> RPOT(Entry);
for (BasicBlockNode<NodeT> *RPOTBB : RPOT) {
RevPostOrderList.push_back(RPOTBB);
NodesEquivalenceClass[RPOTBB].insert(RPOTBB);
CloneToOriginalMap[RPOTBB] = RPOTBB;
if (ConditionalNodesSet.contains(RPOTBB))
ConditionalNodes.push_back(RPOTBB);
}
NodesEquivalenceClass[nullptr] = {};
// CFGDumper used to incrementally print the combing evolution
CFGDumper Dumper(Graph, FunctionName, RegionName, "inflate");
// Iterate on ConditionalNodes from the back. Given that they are inserted
// into ConditionalNodes in RPOT, this iteration is in post-order.
while (not ConditionalNodes.empty()) {
// Process each conditional node after ordering it.
BasicBlockNode<NodeT> *Conditional = ConditionalNodes.back();
ConditionalNodes.pop_back();
// Retrieve a reference to the set of postdominators.
auto CombEndIt = ConditionalToCombEnd.find(Conditional);
revng_assert(CombEndIt != ConditionalToCombEnd.end());
auto CombEndSetIt = NodesEquivalenceClass.find(CombEndIt->second);
revng_assert(CombEndSetIt != NodesEquivalenceClass.end());
if (CombLogger.isEnabled()) {
revng_log(CombLogger,
"Analyzing conditional node " << Conditional->getNameStr());
Dumper.log("-conditional-" + Conditional->getNameStr()
+ "-initial-state");
}
// List to keep track of the nodes that we still need to analyze.
SmallPtrSet<NodeT> WorkList;
// Enqueue in the worklist the successors of the contional node.
for (auto &[Successor, EdgeLabel] : Conditional->labeled_successors())
if (not EdgeLabel.Inlined)
WorkList.insert(Successor);
// Keep a set of the visited nodes for the current conditional node.
SmallPtrSet<NodeT> Visited = { Conditional };
// Get an iterator from the reverse post order list in the position of the
// conditional node.
auto ListIt = std::find(RevPostOrderList.begin(),
RevPostOrderList.end(),
Conditional);
revng_assert(ListIt != RevPostOrderList.end());
int Iteration = 0;
while (++ListIt != RevPostOrderList.end() and not WorkList.empty()) {
if (not WorkList.contains(*ListIt))
continue; // Go to the next node in reverse postorder.
// Otherwise this node is in the worklist, and we have to analyze it.
BasicBlockNode<NodeT> *Candidate = *ListIt;
revng_assert(Candidate != nullptr);
revng_log(CombLogger, "Analyzing candidate " << Candidate->getNameStr());
bool AllPredAreVisited = std::all_of(Candidate->predecessors().begin(),
Candidate->predecessors().end(),
[&Visited](auto *Pred) {
return Visited.contains(Pred);
});
WorkList.erase(Candidate);
Visited.insert(Candidate);
// Comb end flag, which is useful to understand if the dummies we will
// insert will need to substitute the current postdominator.
bool IsCombEnd = CombEndSetIt->second.contains(Candidate);
if (not IsCombEnd) {
for (auto &[Successor, EdgeLabel] : Candidate->labeled_successors()) {
WorkList.insert(Successor);
}
} else {
revng_log(CombLogger,
Candidate->getNameStr()
<< " is Post-Dominator of " << Conditional->getNameStr());
}
if (AllPredAreVisited)
continue; // Go to the next node in reverse postorder.
if (IsCombEnd) {
revng_assert(Candidate->predecessor_size() > 1);
llvm::SmallVector<BasicBlockNode<NodeT> *, 8> NewDummyPredecessors;
revng_log(CombLogger, "Current predecessors are:");
for (BasicBlockNode<NodeT> *Predecessor : Candidate->predecessors()) {
revng_log(CombLogger, Predecessor->getNameStr());
if (Visited.contains(Predecessor))
NewDummyPredecessors.push_back(Predecessor);
}
// We don't insert the dummy, because it would be a dummy with a
// single predecessor and a single successor, which is pointless.
if (NewDummyPredecessors.size() < 2)
continue;
revng_log(CombLogger,
"Inserting a dummy node for " << Candidate->getNameStr());
// Insert a dummy node. Notice, this is guaranteed not to be trivial
// because it will have more than one predecessor.
BasicBlockNode<NodeT> *Dummy = Graph.addArtificialNode();
for (BasicBlockNode<NodeT> *Predecessor : NewDummyPredecessors) {
revng_log(CombLogger,
"Moving edge from predecessor " << Predecessor->getNameStr()
<< " to dummy");
moveEdgeTarget(EdgeDescriptor(Predecessor, Candidate), Dummy);
}
addPlainEdge(EdgeDescriptor(Dummy, Candidate));
// Remove from the visited set the node which triggered the creation
// of the dummy nodes, because we're not really analyzing it now,
// since we're just inserting the dummy. For the same reason we
// re-insert it in the WorkList, otherwise it will be skipped at the
// next iteration.
Visited.erase(Candidate);
// The new dummy node does not lead back to any original node, for
// this reason we need to insert a new entry in the
// `CloneToOriginalMap`.
CloneToOriginalMap[Dummy] = Dummy;
revng_log(CombLogger,
"Update conditional post-dominator. Old: "
<< CombEndIt->second->getNameStr()
<< " New: " << Dummy->getNameStr());
// The dummy is now the node that ends the combing for Conditional.
CombEndIt->second = Dummy;
CombEndSetIt = NodesEquivalenceClass.insert({ Dummy, { Dummy } }).first;
// Mark the dummy to explore.
WorkList.insert(Dummy);
// Insert the dummy nodes in the reverse post order list. The
// insertion order is particularly relevant, because we have added a
// dummy that now post-dominates the region starting from Conditional,
// while Candidate (which is the post-dominator of Conditional here),
// is a successor of Dummy. Hence Dummy must come first in reverse
// post order, otherwise future RPOT visits based on RevPostOrderList
// might be disrupted.
auto PrevListIt = std::prev(ListIt);
RevPostOrderList.insert(ListIt, Dummy);
ListIt = PrevListIt;
} else {
// Duplicate node.
DuplicationCounter++;
revng_log(CombLogger, "Duplicating node " << Candidate->getNameStr());
BasicBlockNode<NodeT> *Duplicated = Graph.cloneNode(*Candidate);
revng_assert(Duplicated != nullptr);
// Initialize the successors of the Duplicated node with the same
// successors of Candidate node
for (const auto &[Succ, Label] : Candidate->labeled_successors())
addEdge(EdgeDescriptor(Duplicated, Succ), Label);
bool Same = Candidate->successor_size() == Duplicated->successor_size();
revng_assert(Same);
// Move Candidate's predecessors that have not been visited yet, so that
// they become predecessors of Duplicated
BasicBlockNodeTVect NotVisitedPredecessors;
for (BasicBlockNode<NodeT> *Predecessor : Candidate->predecessors())
if (not Visited.contains(Predecessor))
NotVisitedPredecessors.push_back(Predecessor);
for (BasicBlockNode<NodeT> *Predecessor : NotVisitedPredecessors) {
moveEdgeTarget(EdgeDescriptor(Predecessor, Candidate), Duplicated);
revng_log(CombLogger,
"Moving edge from predecessor "
<< Predecessor->getNameStr() << " to "
<< Duplicated->getNameStr());
}
if (CombLogger.isEnabled()) {
Dumper.log("-conditional-" + Conditional->getNameStr()
+ "-before-purge-dummies-iteration-"
+ std::to_string(Iteration));
}
BasicBlockNode<NodeT> *OriginalNode = CloneToOriginalMap.at(Candidate);
bool AreDummies = Candidate->isEmpty();
revng_assert(AreDummies == Duplicated->isEmpty());
if (AreDummies) {
revng_log(CombLogger, "Duplicated is dummy");
unsigned CandidateSuccSize = Candidate->successor_size();
unsigned DuplicatedSuccSize = Duplicated->successor_size();
revng_assert(CandidateSuccSize < 2 and DuplicatedSuccSize < 2);
revng_assert(CandidateSuccSize == DuplicatedSuccSize);
bool CInl = Candidate->labeled_successors().begin()->second.Inlined;
bool DInl = Duplicated->labeled_successors().begin()->second.Inlined;
revng_assert(CandidateSuccSize == 0 or CInl == false);
revng_assert(DuplicatedSuccSize == 0 or DInl == false);
// Notice: after this call Duplicated is invalid if the call returns
// true, meaning that dereferencing it is bad. You can still use it as
// a key or value into maps though.
if (not purgeIfTrivialDummy(Duplicated)) {
// Add the cloned node in the equivalence class of the original
// node.
CloneToOriginalMap[Duplicated] = OriginalNode;
NodesEquivalenceClass.at(OriginalNode).insert(Duplicated);
// If it wasn't purged, insert the cloned node in the reverse post
// order list. Here the order is not strictly relevant, because
// there is no strict relationship between Candidate and Duplicated.
RevPostOrderList.insert(ListIt, Duplicated);
} else {
revng_log(CombLogger, "Duplicated is trivial");
}
// The duplication process divides the edges incoming to Candidate,
// and it moves some of them to Duplicated. If Candidate is a dummy
// node, this process may make it trivial. In that case we want to
// remove it.
// Notice: after this call Candidate is invalid if the call returns
// true, meaning that dereferencing it is bad. You can still use it as
// a key or value into maps though.
if (purgeIfTrivialDummy(Candidate)) {
revng_log(CombLogger, "Candidate is now trivial");
CloneToOriginalMap.erase(Candidate);
NodesEquivalenceClass.at(OriginalNode).erase(Candidate);
Visited.erase(Candidate);
// Erase Candidate from the post order list, but update ListIt so
// that after the removal it points to the element that was
// previously before Candidate. In this way, at the next iteration
// of the loop on RevPostOrderList we go on with the element that
// was right after Candidate before its removal.
auto PrevListIt = std::prev(ListIt);
RevPostOrderList.erase(ListIt);
ListIt = PrevListIt;
}
} else {
revng_log(CombLogger, "Duplicated is not dummy");
// Add the cloned node in the equivalence class of the original node.
CloneToOriginalMap[Duplicated] = OriginalNode;
NodesEquivalenceClass.at(OriginalNode).insert(Duplicated);
// Insert the cloned node in the reverse post order list, right before
// the Candidate. This is not important right now, because we don't
// add it to the WorkList. It will become important if whenever
// Duplicated is reached with a traversal based on RevPostOrderList
// starting from a different Conditional.
// In this sense, it's not really important to insert Duplicated
// before or after Candidate, since they have no strict relationship
// in the reverse post order.
RevPostOrderList.insert(ListIt, Duplicated);
}
}
if (CombLogger.isEnabled()) {
Dumper.log("-conditional-" + Conditional->getNameStr()
+ "-after-processing-iteration-"
+ std::to_string(Iteration));
}
Iteration++;
}
revng_log(CombLogger,
"Finished looking at conditional: " << Conditional->getNameStr());
Dumper.log("-conditional-" + Conditional->getNameStr() + "-final-state");
}
if (CombLogger.isEnabled()) {
Graph.dumpCFGOnFile(FunctionName,
"inflate",
"region-" + RegionName
+ "-after-inflate-before-cleanup");
}
// Purge extra dummy nodes introduced.
purgeTrivialDummies();
if (CombLogger.isEnabled()) {
Graph.dumpCFGOnFile(FunctionName,
"inflate",
"region-" + RegionName + "-after-inflate");
}
revng_log(CombLogger, "Region Final Size: " << Graph.size());
}
template<class NodeT>
inline void RegionCFG<NodeT>::removeNotReachables() {
// Remove nodes that have no predecessors (nodes that are the result of node
// cloning and that remains dandling around).
bool Difference = true;
while (Difference) {
Difference = false;
BasicBlockNode<NodeT> *Entry = &getEntryNode();
for (auto It = begin(); It != end(); It++) {
if ((Entry != *It and (*It)->predecessor_size() == 0)) {
removeNode(*It);
Difference = true;
break;
}
}
}
}
template<class NodeT>
inline void
RegionCFG<NodeT>::removeNotReachables(std::vector<MetaRegion<NodeT> *> &MS) {
// Remove nodes that have no predecessors (nodes that are the result of node
// cloning and that remains dandling around).
bool Difference = true;
while (Difference) {
Difference = false;
BasicBlockNode<NodeT> *Entry = &getEntryNode();
for (auto It = begin(); It != end(); It++) {
if ((Entry != *It and (*It)->predecessor_size() == 0)) {
for (MetaRegion<NodeT> *M : MS) {
M->removeNode(*It);
}
removeNode(*It);
Difference = true;
break;
}
}
}
}
template<class NodeT>
inline bool RegionCFG<NodeT>::isDAG() {
for (llvm::scc_iterator<RegionCFG<NodeT> *> I = llvm::scc_begin(this),
IE = llvm::scc_end(this);
I != IE;
++I)
if (I.hasCycle())
return false;
return true;
}
template<class NodeT>
inline bool
RegionCFG<NodeT>::isTopologicallyEquivalent(RegionCFG &Other) const {
// The algorithm inspects in a depth first fashion the two graphs, and check
// that they are topologically equivalent. Take care that this function may
// return true if there are nodes not reachable from the entry node.
// Early failure if the number of nodes composing the two CFG is different.
if (size() != Other.size()) {
return false;
}
// Retrieve the entry nodes of the two `RegionCFG` under analysis.
BasicBlockNode<NodeT> &Entry = getEntryNode();
BasicBlockNode<NodeT> &OtherEntry = Other.getEntryNode();
// Check that the only node without predecessors is the entry node.
for (const BasicBlockNode<NodeT> *Node : nodes()) {
if (Node != &Entry and Node->predecessor_size() == 0) {
return false;
}
}
// Check that the only node without predecessors is the entry node.
for (const BasicBlockNode<NodeT> *Node : Other.nodes()) {
if (Node != &OtherEntry and Node->predecessor_size() == 0) {
return false;
}
}
// Call to a `BasicBlockNode` method which does a deep and recursive
// comparison of a node and its successors.
return Entry.isEquivalentTo(&OtherEntry);
}
template<class NodeT>
inline void RegionCFG<NodeT>::weave() {
// Check that we are in a valid state of the graph.
revng_assert(isDAG());
// Collect useful objects.
RegionCFG<NodeT> &Graph = *this;
IFPDT.recalculate(Graph);
if (CombLogger.isEnabled()) {
Graph.dumpCFGOnFile(FunctionName,
"weave",
"region-" + RegionName + "-before-weave");
}
// Iterate over all the nodes in post order.
BBNodeT *Entry = &getEntryNode();
for (BBNodeT *Switch : post_order(Entry)) {
if (not Switch->isDispatcher() and not isASwitch(Switch))
continue;
// If we find a switch node we can start the weaving analysis.
if (Switch->successor_size() > 2) {
revng_log(CombLogger,
"Looking at switch node: " << Switch->getName() << "\n");
// Collect the case nodes of the switch.
BasicBlockNodeTSet CaseSet;
for (BBNodeT *Successor : Switch->successors())
CaseSet.insert(Successor);
// Find the postdominator of the switch.
BBNodeT *PostDom = IFPDT[Switch]->getIDom()->getBlock();
// Iterate over all the nodes "in the body" of the switch in reverse post
// order.
llvm::SmallPtrSet<BBNodeT *, 1> PostDomSet;
if (nullptr != PostDom)
PostDomSet.insert(PostDom);
ReversePostOrderTraversalExt RPOT(EFGT<BBNodeT *>(Switch), PostDomSet);
revng_log(CombLogger,
"Dumping the candidates that may initiate weaving:");
for (BBNodeT *RPOTBB : RPOT) {
// We expect to never reach the PostDom
revng_assert(RPOTBB != PostDom);
// Skip the switch
if (RPOTBB == Switch)
continue;
revng_log(CombLogger, RPOTBB->getName());
BasicBlockNodeTVect PostDominatedCases;
for (BBNodeT *Case : CaseSet)
if (IFPDT.dominates(RPOTBB, Case))
PostDominatedCases.push_back(Case);
// Criterion to check if we need to perform the weaving. Specifically,
// we need to perform a weaving if we find a node (between the switch
// and its postdominator) that postdominates more than 1 of the cases.
// Note: it cannot postdominate not all of them, otherwise it would be
// the immediate postdominator of the switch, that we have explicitly
// excluded.
auto NumPostDominatedCases = PostDominatedCases.size();
revng_assert(NumPostDominatedCases != CaseSet.size());
if (NumPostDominatedCases > 1U) {
// Create the new sub-switch node.
BasicBlockNodeT *NewSwitch = nullptr;
if (Switch->isDispatcher()) {
NewSwitch = addDispatcher(Switch->getNameStr() + " weaved",
Switch->getDispatcherType());
} else if (Switch->isCode()) {
NewSwitch = addNode(Switch->getOriginalNode(),
Switch->getNameStr() + " weaved");
} else {
revng_unreachable("unexpected switch");
}
revng_assert(nullptr != NewSwitch);
revng_assert(not NewSwitch->successor_size());
NewSwitch->setWeaved(true);
using edge_label_t = typename BasicBlockNodeT::edge_label_t;
edge_label_t Labels;
bool WeavingDefault = false;
// Iterate over all the case nodes that we found, moving all the
// necessary edges and updating the IFPDT.
// Also, collect all the case labels of the cases we're weaving.
for (BasicBlockNodeT *Case : PostDominatedCases) {
auto LabeledEdge = extractLabeledEdge(EdgeDescriptor(Switch, Case));
IFPDT.deleteEdge(Switch, Case);
auto &EdgeInfo = LabeledEdge.second;
// If we find an edge without case labels, that's the default.
if (EdgeInfo.Labels.empty()) {
revng_assert(WeavingDefault == false);
WeavingDefault = true;
Labels = {};
}
// If we're weaving the default, we don't care about the exact case
// labels, because the weaved switch will become the default of the
// original switch.
if (not WeavingDefault)
Labels.insert(EdgeInfo.Labels.begin(), EdgeInfo.Labels.end());
addEdge(EdgeDescriptor(NewSwitch, Case), EdgeInfo);
IFPDT.insertEdge(NewSwitch, Case);
CaseSet.erase(Case);
}
CaseSet.insert(NewSwitch);
// Connect the old switch to the new one and update the IFPDT.
// Use the collected labels to mark the new edge from the original
// switch to the weaved switch.
using EdgeInfo = typename BasicBlockNodeT::EdgeInfo;
EdgeInfo EI = { Labels, false };
addEdge(EdgeDescriptor(Switch, NewSwitch), EI);
IFPDT.insertEdge(Switch, NewSwitch);
}
}
}
}
DT.recalculate(Graph);
if (CombLogger.isEnabled()) {
Graph.dumpCFGOnFile(FunctionName,
"weave",
"region-" + RegionName + "-after-weave");
}
}
template<class NodeT>
inline void RegionCFG<NodeT>::markUnreachableAsInlined() {
llvm::SmallPtrSet<BBNodeT *, 8> UnreachableBlocks;
for (BBNodeT *BBNode : *this) {
if (not BBNode->isCode())
continue;
llvm::BasicBlock *BB = BBNode->getOriginalNode();
if (llvm::isa<llvm::UnreachableInst>(BB->getTerminator()))
UnreachableBlocks.insert(BBNode);
}
for (BBNodeT *Unreachable : UnreachableBlocks) {
BasicBlockNodeTVect Predecessors;
for (BBNodeT *Pred : Unreachable->predecessors()) {
Predecessors.push_back(Pred);
markEdgeInlined(EdgeDescriptor(Pred, Unreachable));
}
if (Predecessors.size() > 1) {
for (BBNodeT *Pred : llvm::drop_begin(Predecessors, 1)) {
BBNodeT *UnreachableClone = cloneNode(*Unreachable);
moveEdgeTarget({ Pred, Unreachable }, UnreachableClone);
}
}
}
}