RestructureCFG: improve first iteration outline

Improve the logic of the first iteration outline:
1) The cloning of the nodes involved in the first iteration outlining,
   is now performed with a series of DFS visits from all the late entry
   nodes in the `MetaRegion`, instead of performing a blind clone of all
   the nodes and removing those not needed.
2) Restoring the edges between the cloned nodes has been subject to
   minor changes.
3) The outline nodes are now assigned to a `MetaRegion` using a new
   logic, instead of blindly assigning them to the parent `MetaRegion`.
This commit is contained in:
Andrea Gussoni
2023-09-18 17:55:51 +02:00
parent 496f0becd7
commit deee899ad0
3 changed files with 277 additions and 59 deletions
+1 -2
View File
@@ -58,8 +58,7 @@ public:
void updateNodes(const BasicBlockNodeTSet &Removal,
BasicBlockNodeT *Collapsed,
BasicBlockNodeT *ExitDispatcher,
const BasicBlockNodeTVect &DefaultEntrySet,
const BasicBlockNodeTVect &OutlinedNodes);
const BasicBlockNodeTVect &DefaultEntrySet);
void setParent(MetaRegion<NodeT> *Parent) { ParentRegion = Parent; }
@@ -25,8 +25,8 @@ template<class NodeT>
void MetaRegion<NodeT>::updateNodes(const BasicBlockNodeTSet &ToRemove,
BasicBlockNodeT *Collapsed,
BasicBlockNodeT *ExitDispatcher,
const BasicBlockNodeTVect &DefaultEntrySet,
const BasicBlockNodeTVect &OutlinedNodes) {
const BasicBlockNodeTVect
&DefaultEntrySet) {
// Remove the old SCS nodes
for (BasicBlockNodeT *Node : ToRemove)
Nodes.erase(Node);
@@ -44,12 +44,6 @@ void MetaRegion<NodeT>::updateNodes(const BasicBlockNodeTSet &ToRemove,
return this->containsNode(B);
}));
Nodes.insert(DefaultEntrySet.begin(), DefaultEntrySet.end());
// Add the nodes that were generated with first iteration outlining
revng_assert(not llvm::any_of(OutlinedNodes, [this](BasicBlockNodeT *B) {
return this->containsNode(B);
}));
Nodes.insert(OutlinedNodes.begin(), OutlinedNodes.end());
}
template<class NodeT>
+274 -49
View File
@@ -5,6 +5,7 @@
#include <iterator>
#include <limits>
#include <sstream>
#include <utility>
#include "llvm/ADT/BreadthFirstIterator.h"
#include "llvm/ADT/PostOrderIterator.h"
@@ -388,6 +389,75 @@ getCandidateEntries(MetaRegionBB *Meta) {
return Result;
}
// Function to compute the most nested regions between the ones passed in the
// parameter `SmallSet`. The assumption is that the candidate `MetaRegion`s
// passed as parameters all lie on a single nesting derivation line, i.e., the
// need to be all descendant of one another.
static MetaRegionBB *
mostNestedRegion(llvm::SmallSet<MetaRegionBB *, 4> &MetaRegions) {
// Select the most nested `MetaRegion`
MetaRegionBB *MaxMetaRegion = nullptr;
size_t MaxLevel = 0;
for (MetaRegionBB *Meta : MetaRegions) {
// If we encounter the `root` `MetaRegion`, we do not proceed with the body
// of the loop, since the initialization value already represents the `root`
if (Meta == nullptr) {
continue;
}
// Compute the nesting level for each `MetaRegion`
MetaRegionBB *UpwardMeta = Meta;
size_t Level = 0;
while (UpwardMeta != nullptr) {
UpwardMeta = UpwardMeta->getParent();
Level++;
}
// Due to the initial assumption that all the input `MetaRegion`s lie on a
// single nesting tree in the `MetaRegion` containement tree, we should
// never encounter the same level twice
revng_assert(Level != MaxLevel);
// If the level reached at this iteration is greater than what found
// previously, we update the value
if (Level > MaxLevel) {
MaxMetaRegion = Meta;
MaxLevel = Level;
}
}
return MaxMetaRegion;
}
// Function that computes the most nested `MetaRegion` parent between the
// predecessors of the `Node` input block.
static MetaRegionBB *computePredecessorsParent(MetaRegionBBPtrVect &MetaRegions,
BasicBlockNodeBB *Node) {
// Elect the parent `MetaRegion` for each of the `Node` predecessor
llvm::SmallSet<MetaRegionBB *, 4> PredecessorMetaRegions;
for (BasicBlockNodeBB *Predecessor : Node->predecessors()) {
// Collect all the `MetaRegion`s containing the `Predecessor`
llvm::SmallSet<MetaRegionBB *, 4> ContainingMetaRegions;
for (MetaRegionBB *Meta : MetaRegions) {
if (Meta->containsNode(Predecessor)) {
ContainingMetaRegions.insert(Meta);
}
}
// Elect the most nested `MetaRegion` for each `Predecessor`
MetaRegionBB *Meta = mostNestedRegion(ContainingMetaRegions);
PredecessorMetaRegions.insert(Meta);
}
// Elect the most nested `MetaRegion` between each one selected from the input
// `MetaRegion
return mostNestedRegion(PredecessorMetaRegions);
}
bool restructureCFG(Function &F, ASTTree &AST) {
revng_log(CombLogger, "restructuring Function: " << F.getName());
revng_log(CombLogger, "Num basic blocks: " << F.size());
@@ -688,6 +758,9 @@ bool restructureCFG(Function &F, ASTTree &AST) {
}
}
// Verify that we found at least one backedge
revng_assert(ContinueBackedges.size() > 0);
revng_assert(Head != nullptr);
revng_log(CombLogger, "New head name is: " << Head->getNameStr());
@@ -776,73 +849,226 @@ bool restructureCFG(Function &F, ASTTree &AST) {
}
// First Iteration outlining.
std::vector<BasicBlockNodeBB *> OutlinedNodes;
llvm::SmallSet<BasicBlockNodeBB *, 4> OutlinedClonedNodes;
llvm::SmallSet<BasicBlockNodeBB *, 4> OutlinedOriginalNodes;
if (Entries.size() > 1) {
// Clone all the nodes of the SCS except for the head.
std::map<BasicBlockNodeBB *, BasicBlockNodeBB *> ClonedMap;
for (BasicBlockNodeBB *Node : Meta->nodes()) {
if (Node != Head) {
llvm::df_iterator_default_set<BasicBlockNodeBB *> VisitedForOutlining;
VisitedForOutlining.insert(Head);
Entries.erase(Head);
// We perform the cloning of the nodes interested by the first iteration
// outlining, performing a DFS starting from all the `Entries` nodes, and
// not proceeding towards node that are not in the `MetaRegion` under
// restructuring
for (const auto &[LateEntry, Value] : Entries) {
auto ItBegin = llvm::df_ext_begin(LateEntry, VisitedForOutlining);
auto ItEnd = llvm::df_ext_end(LateEntry, VisitedForOutlining);
while (ItBegin != ItEnd) {
// Extract the currently visited node
BasicBlockNodeBB *Node = *ItBegin;
// If the node is not in the `MetaRegion`, we do not want to proceed
// in this direction
if (not Meta->containsNode(Node)) {
// Skip over the children of `Node`
ItBegin.skipChildren();
// Skip the cloning process for the current `Node`, since it is not
// part of the outlined iteration
continue;
}
// If we reach this point, we are inspecting a node part of the first
// outlined iteration, therefore we proceed with the cloning
BasicBlockNodeBB *Clone = RootCFG.cloneNode(*Node);
// In case we are cloning nodes that may become entry candidates of
// regions, we need to assign to them a value in the
// `ShortestPathFromEntry` map.
// `ShortestPathFromEntry` map
if (Node->isCollapsed() or Node->isCode()) {
ShortestPathFromEntry[Clone] = ShortestPathFromEntry.at(Node);
}
Clone->setName(Node->getName().str() + " outlined");
ClonedMap[Node] = Clone;
// Add the nodes to the additional vector
OutlinedNodes.push_back(Clone);
// Add the nodes to two additional vectors used later in the
// postprocessing that assigns each node to the correct `MetaRegion`
OutlinedClonedNodes.insert(Clone);
OutlinedOriginalNodes.insert(Node);
// Increment the `df_iterator`
ItBegin++;
}
}
// Restore edges between cloned nodes.
for (BasicBlockNodeBB *Node : Meta->nodes()) {
if (Node != Head) {
// Restore the edges between the node cloned during the first step of the
// outlining
for (BasicBlockNodeBB *Node : OutlinedOriginalNodes) {
revng_assert(Node != Head);
// Handle outgoing edges from SCS nodes.
for (const auto &[Successor, Labels] : Node->labeled_successors()) {
revng_assert(not Backedges.contains(EdgeDescriptor(Node,
Successor)));
using ED = EdgeDescriptor;
auto *NewEdgeSrc = ClonedMap.at(Node);
auto *NewEdgeTgt = Successor;
if (Meta->containsNode(Successor)) {
// Handle edges pointing inside the SCS.
if (Successor == Head) {
// Retreating edges should point to the new head.
NewEdgeTgt = Head;
} else {
// Other edges should be restored between cloned nodes.
NewEdgeTgt = ClonedMap.at(Successor);
}
// Handle the successors of each node
for (const auto &[Successor, Labels] : Node->labeled_successors()) {
revng_assert(not Backedges.contains(EdgeDescriptor(Node, Successor)));
BasicBlockNodeBB *NewEdgeSrc = ClonedMap.at(Node);
BasicBlockNodeBB *NewEdgeTgt = nullptr;
if (Meta->containsNode(Successor)) {
if (OutlinedOriginalNodes.contains(Successor)) {
// The successor may be another outlined node
NewEdgeTgt = ClonedMap.at(Successor);
} else if (Successor == Head) {
// The successor is the `Head`, so we should reconnect it
NewEdgeTgt = Head;
} else {
// We should not encounter another type of successor
revng_abort();
}
addEdge(ED(NewEdgeSrc, NewEdgeTgt), Labels);
} else {
// If the successor is not part of the `MetaRegion`, we expect it to
// be part of the loop successors previously identified
revng_assert(Successors.contains(Successor));
NewEdgeTgt = Successor;
}
addEdge(EdgeDescriptor(NewEdgeSrc, NewEdgeTgt), Labels);
}
// Handle the predecessors. Note that we are interested in handling here
// only predecessors not belonging to the `MetaRegion`, since the
// predecessors of each node that lies in the outlined iteration, should
// have been already handled (or will be) as successors of other nodes
// in the outlined iteration.
// We do not iterate directly on the predecessors to avoid iterator
// invalidation
llvm::SmallVector<BasicBlockNodeBB *> Predecessors;
for (BasicBlockNodeBB *Predecessor : Node->predecessors()) {
Predecessors.push_back(Predecessor);
}
for (BasicBlockNodeBB *Predecessor : Predecessors) {
if (not(Meta->containsNode(Predecessor))) {
// We handle edges incoming in nodes from outside the outlined
// iteration
// Are we moving a backedge with the first iteration outlinig?
revng_assert(not Backedges.contains({ Predecessor, Node }));
// If we are on the border of the outlined iteration, `Node` must be
// one of the late entries
revng_assert(Entries.contains(Node));
BasicBlockNodeBB *Clone = ClonedMap.at(Node);
moveEdgeTarget(EdgeDescriptor(Predecessor, Node), Clone);
} else {
// We should do nothing, we already took care of these edges while
// iterating over the successors of the group of nodes
}
}
}
// Postprocessing that encapsules each node that has been outlined in the
// correct `MetaRegion`. The process, at a macro level, proceeds as
// follows:
// 1) We need to process all the outlined nodes, with the guarantee that
// when we visit each node, all its predecessors must have been already
// processed (a requirement for point 3). To do that, we instantiate
// multiple `post order` visits, that starts from each successor of
// nodes in the outlined iteration. All the visits share the same `ext`
// set, which is pre-populated with the nodes that are the entries of
// the outlined iteration, so that we only visit nodes we are
// interested to postprocess (i.e., the nodes in the outlined
// iteration). post order over the `Inverse` graph, using an `ext` DFS
// visit that stops at predecessor of the outline iteration.
// 2) For each node encountered, we proceed at the election of the correct
// `MetaRegion` to which the node will be assigned, on the basis of the
// following criterion.
// 3) We collect the predecessors of each node, and collect all the
// `MetaRegion`s to which they do belong. After this, we select the
// most nested `MetaRegion` between them. This is done under the
// assumption that all the candidate `MetaRegion`s must fall on a
// single inheritance line on the `MetaRegion` inclusion tree.
// Collect the `Head` plus the successors, which are the point from where
// the DFSs for the `po_ext` should start. More in detail, the DFS used by
// the post order should not start from the `Head` and the `Successors`,
// but only from their predecessors contained inside the outlined
// iteration.
llvm::SmallVector<BasicBlockNodeBB *> BoundaryNodes;
BoundaryNodes.push_back(Head);
for (BasicBlockNodeBB *Successor : Successors) {
BoundaryNodes.push_back(Successor);
}
llvm::SmallSet<BasicBlockNodeBB *, 4> DFSOrigins;
for (BasicBlockNodeBB *BoundaryNode : BoundaryNodes) {
for (BasicBlockNodeBB *Predecessor : BoundaryNode->predecessors()) {
if (OutlinedClonedNodes.contains(Predecessor)) {
DFSOrigins.insert(Predecessor);
revng_assert(Predecessor != Head);
}
}
}
// This is the `ext` set used to stop all the subsequents `post order`
// visits
llvm::SmallSet<BasicBlockNodeBB *, 4> DFSExtSet;
// We prepopulate the `DFSExtSet` with all the predecessors of the `Head`
// that are not part of the outlined iteration
for (BasicBlockNodeBB *Predecessor : Head->predecessors()) {
if (not OutlinedClonedNodes.contains(Predecessor)) {
revng_assert(Predecessor != Head);
DFSExtSet.insert(Predecessor);
}
}
// We also insert all the predecessors of each `LateEntry`, which are not
// part of the `OutlinedClonedNodes`
for (const auto &[LateEntry, Value] : Entries) {
revng_assert(LateEntry != Head);
BasicBlockNodeBB *LateEntryCloned = ClonedMap.at(LateEntry);
for (BasicBlockNodeBB *Predecessor : LateEntryCloned->predecessors()) {
if (not OutlinedClonedNodes.contains(Predecessor)) {
revng_assert(Predecessor != Head);
DFSExtSet.insert(Predecessor);
}
}
}
// We need to instantiate a new `post order` over the Inverse graph, for
// each exit point from the outlined iteration. The relevant nodes, have
// been collected in `DFSOrigins`.
for (BasicBlockNodeBB *DFSEntry : DFSOrigins) {
for (BasicBlockNodeBB *OutlinedNode :
llvm::inverse_post_order_ext(DFSEntry, DFSExtSet)) {
// We should not encounter a node not part of the outlined iteration
// during the DFSs
if (not OutlinedClonedNodes.contains(OutlinedNode)) {
revng_abort();
}
// 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 (not Meta->containsNode(Predecessor)) {
// Find the region where each outlined node should be placed
MetaRegionBB
*CandidateParent = computePredecessorsParent(OrderedMetaRegions,
OutlinedNode);
if (CombLogger.isEnabled()) {
CombLogger << "Index region: " << Meta->getIndex() << "\n";
CombLogger << "Edge that now points to an outlined iteration: "
<< Predecessor->getNameStr() << " -> "
<< Node->getNameStr() << "\n";
}
// Are we moving a backedge with the first iteration outlining?
revng_assert(not Backedges.contains({ Predecessor, Node }));
moveEdgeTarget(EdgeDescriptor(Predecessor, Node),
ClonedMap.at(Node));
}
// The `CandidateParent` may be `nullptr`, if the `root` region is
// selected as `CandidateParent`, which is an admissible situation,
// and since the `root` `MetaRegion` is no more materialized, in such
// case we do not need to insert the nodes anywhere
if (CandidateParent != nullptr) {
CandidateParent->insertNode(OutlinedNode);
}
}
}
@@ -1067,8 +1293,7 @@ bool restructureCFG(Function &F, ASTTree &AST) {
ParentMetaRegion->updateNodes(Meta->getNodes(),
Collapsed,
ExitDispatcher,
DefaultEntrySet,
OutlinedNodes);
DefaultEntrySet);
ParentMetaRegion = ParentMetaRegion->getParent();
}