#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Instructions.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/GenericDomTreeConstruction.h" #include "revng/ADT/FilteredGraphTraits.h" #include "revng/ADT/SmallMap.h" #include "revng/RestructureCFG/ASTTree.h" #include "revng/RestructureCFG/BasicBlockNodeBB.h" #include "revng/RestructureCFG/Utils.h" template class MetaRegion; template inline bool InlineFilter(const typename llvm::GraphTraits::EdgeRef &E) { return !E.second.Inlined; } inline bool isASwitch(BasicBlockNode *Node) { // TODO: remove this workaround for searching for switch nodes. if (Node->isCode() and Node->getOriginalNode()) { llvm::BasicBlock *OriginalBB = Node->getOriginalNode(); llvm::Instruction *TerminatorBB = OriginalBB->getTerminator(); return llvm::isa(TerminatorBB); } // The node may be an artificial node, therefore not an original switch. return false; } /// The RegionCFG, a container for BasicBlockNodes template class RegionCFG { using BBNodeT = BasicBlockNode; using BBNodeTUniquePtr = typename std::unique_ptr; using getPointerT = BBNodeT *(*) (BBNodeTUniquePtr &); using getConstPointerT = const BBNodeT *(*) (const BBNodeTUniquePtr &); static BBNodeT *getPointer(BBNodeTUniquePtr &Original) { return Original.get(); } static_assert(std::is_same_v); static const BBNodeT *getConstPointer(const BBNodeTUniquePtr &Original) { return Original.get(); } static_assert(std::is_same_v); public: using BasicBlockNodeT = typename BBNodeT::BasicBlockNodeT; using BasicBlockNodeType = typename BasicBlockNodeT::Type; using BasicBlockNodeTSet = std::set; using BasicBlockNodeTVect = std::vector; using BasicBlockNodeTUPVect = std::vector>; using BBNodeMap = typename BBNodeT::BBNodeMap; using RegionCFGT = typename BBNodeT::RegionCFGT; using EdgeDescriptor = typename BBNodeT::EdgeDescriptor; using links_container = std::vector; using internal_iterator = typename links_container::iterator; using internal_const_iterator = typename links_container::const_iterator; using links_iterator = llvm::mapped_iterator; using links_const_iterator = llvm::mapped_iterator; using links_range = llvm::iterator_range; using links_const_range = llvm::iterator_range; using ExprNodeMap = std::map; // Template type for the `NodePairFilteredGraph`. This must be template // because `llvm::DominatorTreeOnView` expects a template type as first // template parameter, so it must not be resolved beforehand. template using EFGT = EdgeFilteredGraph>; using FDomTree = llvm::DominatorTreeOnView; using FPostDomTree = llvm::DominatorTreeOnView; static constexpr size_t WeightNotComputed = std::numeric_limits::max(); private: /// Storage for basic block nodes, associated to their original counterpart links_container BlockNodes; /// Quarantine for dead nodes. // When nodes are removed from RegionCFG, they are not really freed, but they // are held here until the RegionCFG itself goes out of scope. // This is unfortunately necessary now, since the CFG restructuring algorithm // uses maps and sets (e.g. Backedges.) that are indexed using a // BasicBlockNodeT *. // If we don't hold the removed nodes in quarantine, the system allocator can // reuse the blocks, allocating new nodes at the same address, and causing // false-positive hits in some of the mentioned maps. This was the most // straightforward solution for now. // // Other solutions we have considered: // - use a special monotonic allocator for BasicBlockNodes // - this should work, but in principle it gives the same results as the // current solution, with more boilerplate. Also, at the moment // std::unique_ptr is not allocator aware, so we would need to change // BlockNodes to not use them, and this would require even more // boilerplate. // - change the API for RegionCFG::removeNode, to take as arguments the // reference to the data structure and maps that must be updated, so that // when we remove the node from RegionCFG we also clear it from the maps. // However, this is very invasive, it requires changing the public facing // API, it requirese coupling the RegionCFG API with internal details, // and in the future it would need to be updated for every new map that // must be updated on removal of a node. links_container DeadNodesQuarantine; /// Pointer to the entry basic block of this function BasicBlockNodeT *EntryNode = nullptr; unsigned IDCounter = 0; std::string FunctionName; std::string RegionName; bool ToInflate = true; size_t UntangleWeight = WeightNotComputed; llvm::DominatorTreeBase DT; FPostDomTree IFPDT; private: template void addSuccessorEdges(GraphNodeT N, const std::map &NodeMap) { BBNodeT *BBNode = NodeMap.at(N); using llvm::make_range; using GT = typename llvm::GraphTraits; for (typename GT::NodeRef OriginalSucc : make_range(GT::child_begin(N), GT::child_end(N))) { BBNodeT *Successor = NodeMap.at(OriginalSucc); addPlainEdge({ BBNode, Successor }); } } using CBBMap = const std::map; template<> void addSuccessorEdges(llvm::BasicBlock *BB, CBBMap &NodeMap) { BBNodeT *BBNode = NodeMap.at(BB); if (auto *Switch = dyn_cast(BB->getTerminator())) { revng_assert(BBNode->isCode() and isASwitch(BBNode)); using EdgeInfo = typename BasicBlockNodeT::EdgeInfo; SmallMap LabeledEdges; for (const llvm::SwitchInst::CaseHandle &Case : Switch->cases()) { llvm::BasicBlock *CaseBB = Case.getCaseSuccessor(); llvm::ConstantInt *CaseValue = Case.getCaseValue(); uint64_t LabelValue = CaseValue->getZExtValue(); LabeledEdges[CaseBB].Labels.insert(LabelValue); } auto *DefaultBB = Switch->getDefaultDest(); revng_assert(DefaultBB); LabeledEdges[DefaultBB].Labels.clear(); for (const auto &[Succ, Label] : LabeledEdges) addEdge({ BBNode, NodeMap.at(Succ) }, Label); } else { using GT = llvm::GraphTraits; for (llvm::BasicBlock *OriginalSuccBB : make_range(GT::child_begin(BB), GT::child_end(BB))) { BBNodeT *Successor = NodeMap.at(OriginalSuccBB); addPlainEdge({ BBNode, Successor }); } } } public: RegionCFG() = default; RegionCFG(const RegionCFG &) = default; RegionCFG(RegionCFG &&) = default; RegionCFG &operator=(const RegionCFG &) = default; RegionCFG &operator=(RegionCFG &&) = default; template void initialize(GraphT Graph) { using GT = llvm::GraphTraits; using NodeRef = typename GT::NodeRef; // Map to keep the link between the original nodes and the BBNode created // from it. std::map NodeToBBNodeMap; // Create a new node for each node in Graph. using llvm::make_range; for (NodeRef N : make_range(GT::nodes_begin(Graph), GT::nodes_end(Graph))) { BBNodeT *BBNode = addNode(N); NodeToBBNodeMap[N] = BBNode; } // Do another iteration over all the nodes in the graph to create the edges // in the graph. for (NodeRef N : make_range(GT::nodes_begin(Graph), GT::nodes_end(Graph))) { addSuccessorEdges(N, NodeToBBNodeMap); } // Set the `EntryNode` BasicBlockNode reference. EntryNode = NodeToBBNodeMap.at(GT::getEntryNode(Graph)); } unsigned getNewID() { return IDCounter++; } links_range nodes() { return llvm::make_range(begin(), end()); } links_const_range nodes() const { return llvm::make_range(begin(), end()); } void setFunctionName(std::string Name); void setRegionName(std::string Name); std::string getFunctionName() const; std::string getRegionName() const; links_iterator begin() { return llvm::map_iterator(BlockNodes.begin(), getPointer); } links_const_iterator begin() const { return llvm::map_iterator(BlockNodes.begin(), getConstPointer); } links_iterator end() { return llvm::map_iterator(BlockNodes.end(), getPointer); } links_const_iterator end() const { return llvm::map_iterator(BlockNodes.end(), getConstPointer); } size_t size() const { return BlockNodes.size(); } void setSize(size_t Size) { BlockNodes.reserve(Size); } BBNodeT *addNode(NodeT Node, llvm::StringRef Name); BBNodeT *addNode(NodeT Node) { return addNode(Node, Node->getName()); } BBNodeT *createCollapsedNode(RegionCFG *Collapsed) { BlockNodes.emplace_back(std::make_unique(this, Collapsed)); return BlockNodes.back().get(); } BBNodeT *addArtificialNode(llvm::StringRef Name = "dummy", BasicBlockNodeType T = BasicBlockNodeType::Empty) { revng_assert(T == BasicBlockNodeType::Empty or T == BasicBlockNodeType::Break or T == BasicBlockNodeType::Continue); BlockNodes.emplace_back(std::make_unique(this, Name, T)); return BlockNodes.back().get(); } BBNodeT *addContinue() { return addArtificialNode("continue", BasicBlockNodeT::Type::Continue); } BBNodeT *addBreak() { return addArtificialNode("break", BasicBlockNodeT::Type::Break); } BBNodeT *addDispatcher(llvm::StringRef Name, BasicBlockNodeT::Type T) { using BBNodeT = BasicBlockNodeT; auto D = std::make_unique(this, Name, T); return BlockNodes.emplace_back(std::move(D)).get(); } BBNodeT *addEntryDispatcher() { using Type = typename BasicBlockNodeT::Type; return addDispatcher("entry dispatcher", Type::EntryDispatcher); } BBNodeT *addExitDispatcher() { using Type = typename BasicBlockNodeT::Type; return addDispatcher("exit dispatcher", Type::ExitDispatcher); } BBNodeT *addSetStateNode(unsigned StateVariableValue, llvm::StringRef TargetName, BasicBlockNodeT::Type T) { using BBNodeT = BasicBlockNodeT; std::string IdStr = std::to_string(StateVariableValue); std::string Name = "set idx " + IdStr + " (desired target) " + TargetName.str(); auto Tmp = std::make_unique(this, Name, T, StateVariableValue); BlockNodes.emplace_back(std::move(Tmp)); return BlockNodes.back().get(); } BBNodeT *addEntrySetStateNode(unsigned StateVariableValue, llvm::StringRef TargetName) { using Type = typename BasicBlockNodeT::Type; return addSetStateNode(StateVariableValue, TargetName, Type::EntrySet); } BBNodeT *addExitSetStateNode(unsigned StateVariableValue, llvm::StringRef TargetName) { using Type = typename BasicBlockNodeT::Type; return addSetStateNode(StateVariableValue, TargetName, Type::ExitSet); } BBNodeT *addTile() { using Type = typename BasicBlockNodeT::Type; auto Tmp = std::make_unique(this, "tile", Type::Tile); BlockNodes.emplace_back(std::move(Tmp)); return BlockNodes.back().get(); } BBNodeT *cloneNode(BasicBlockNodeT &OriginalNode); void removeNode(BasicBlockNodeT *Node); void insertBulkNodes(BasicBlockNodeTSet &Nodes, BasicBlockNodeT *Head, BBNodeMap &SubstitutionMap, std::set &Out, llvm::SmallVector &ContinueBackedges); BBNodeT &getEntryNode() const { return *EntryNode; } BBNodeT &front() const { return *EntryNode; } std::vector &getNodes() { return BlockNodes; } public: /// Dump a GraphViz representing this function on any stream template void dumpDot(StreamT &) const; /// Dump a GraphViz file on a file using an absolute path void dumpCFGOnFile(const std::string &FileName) const; /// Dump a GraphViz file on a file using an absolute path void dumpCFGOnFile(const char *FName) const { return dumpCFGOnFile(std::string(FName)); } /// Dump a GraphViz file on a file representing this function void dumpCFGOnFile(const std::string &FunctionName, const std::string &FolderName, const std::string &FileName) const; bool purgeTrivialDummies(); bool purgeIfTrivialDummy(BBNodeT *Dummy); void purgeVirtualSink(BBNodeT *Sink); BBNodeT *cloneUntilExit(BBNodeT *Node, BBNodeT *Sink); /// Apply the untangle preprocessing pass. void untangle(); /// Apply comb to the region. void inflate(); void removeNotReachables(); void removeNotReachables(std::vector *> &MS); bool isDAG(); bool isTopologicallyEquivalent(RegionCFG &Other) const; void weave(); void markUnreachableAsInlined(); void computeUntangleWeight() { if (UntangleWeight == WeightNotComputed) { UntangleWeight = 0; for (BasicBlockNode *Node : nodes()) { UntangleWeight += Node->getWeight(); } } } size_t getUntangleWeight() { if (UntangleWeight == WeightNotComputed) { computeUntangleWeight(); } revng_assert(UntangleWeight != 0); return UntangleWeight; } protected: template void streamNode(StreamT &S, const BasicBlockNodeT *) const; }; // Provide graph traits for usage with, e.g., llvm::ReversePostOrderTraversal namespace llvm { template struct GraphTraits *> : public GraphTraits *> { using nodes_iterator = typename RegionCFG::links_iterator; using NodeRef = BasicBlockNode *; static NodeRef getEntryNode(RegionCFG *F) { return &F->getEntryNode(); } static nodes_iterator nodes_begin(RegionCFG *F) { return F->begin(); } static nodes_iterator nodes_end(RegionCFG *F) { return F->end(); } static size_t size(RegionCFG *F) { return F->size(); } }; template struct GraphTraits *>> : public GraphTraits *>> { using NodeRef = BasicBlockNode *; static NodeRef getEntryNode(Inverse *> G) { return &G.Graph->getEntryNode(); } }; } // namespace llvm extern unsigned DuplicationCounter; extern unsigned UntangleTentativeCounter; extern unsigned UntanglePerformedCounter;