diff --git a/include/revng-c/RestructureCFG/MetaRegion.h b/include/revng-c/RestructureCFG/MetaRegion.h index 5ae74cebb..e1525e3d5 100644 --- a/include/revng-c/RestructureCFG/MetaRegion.h +++ b/include/revng-c/RestructureCFG/MetaRegion.h @@ -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 *Parent) { ParentRegion = Parent; } diff --git a/include/revng-c/RestructureCFG/MetaRegionImpl.h b/include/revng-c/RestructureCFG/MetaRegionImpl.h index c4c1e07f9..0529c719a 100644 --- a/include/revng-c/RestructureCFG/MetaRegionImpl.h +++ b/include/revng-c/RestructureCFG/MetaRegionImpl.h @@ -25,8 +25,8 @@ template void MetaRegion::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::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 diff --git a/lib/RestructureCFG/RestructureCFG.cpp b/lib/RestructureCFG/RestructureCFG.cpp index b01409c63..7a72f01f9 100644 --- a/lib/RestructureCFG/RestructureCFG.cpp +++ b/lib/RestructureCFG/RestructureCFG.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #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 &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 PredecessorMetaRegions; + for (BasicBlockNodeBB *Predecessor : Node->predecessors()) { + + // Collect all the `MetaRegion`s containing the `Predecessor` + llvm::SmallSet 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 OutlinedNodes; + llvm::SmallSet OutlinedClonedNodes; + llvm::SmallSet OutlinedOriginalNodes; if (Entries.size() > 1) { - // Clone all the nodes of the SCS except for the head. std::map ClonedMap; - for (BasicBlockNodeBB *Node : Meta->nodes()) { - if (Node != Head) { + + llvm::df_iterator_default_set 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 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 BoundaryNodes; + BoundaryNodes.push_back(Head); + for (BasicBlockNodeBB *Successor : Successors) { + BoundaryNodes.push_back(Successor); + } + llvm::SmallSet 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 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 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(); }