#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include "llvm/ADT/Optional.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SmallVector.h" #include "revng/ADT/Queue.h" #include "revng/Support/Debug.h" /// \brief Backport of std::map::insert_or_assign template inline void insert_or_assign(std::map &Map, K Key, V &&Value) { auto It = Map.find(Key); if (It != Map.end()) It->second = std::forward(Value); else Map.emplace(Key, std::forward(Value)); } enum VisitType { /// Breadth first visit, useful if the function body is unknown BreadthFirst, /// Post order visit, for backward analyses PostOrder, /// Reverse post order visit, for forward analyses ReversePostOrder }; /// \brief Work list for the monotone framework supporting various visit /// strategies template class MonotoneFrameworkWorkList {}; // Breadth first implementation template class MonotoneFrameworkWorkList { private: UniquedQueue Queue; public: MonotoneFrameworkWorkList(Iterated) {} void clear() { Queue.clear(); } void insert(Iterated Entry) { Queue.insert(Entry); } bool empty() const { return Queue.empty(); } Iterated head() const { return Queue.head(); } Iterated pop() { return Queue.pop(); } size_t size() const { return Queue.size(); } }; template concept IsPostOrderLike = V == PostOrder or V == ReversePostOrder; // (Reverse) post order implementation template requires IsPostOrderLike class MonotoneFrameworkWorkList { private: /// \brief Class for an entry in the work list /// /// All the basic blocks are always in the list in (reverse) post order. When /// an entry is popped it is simply disabled. class PostOrderEntry { private: Iterated Entry; bool Enabled; public: PostOrderEntry(Iterated Entry) : Entry(Entry), Enabled(true) {} void enable() { Enabled = true; } void disable() { Enabled = false; } bool isEnabled() const { return Enabled; } Iterated entry() const { return Entry; } }; private: /// List of all basic blocks in the appropriate order std::vector PostOrderList; /// Map to quickly find the index of an entry in PostOrderList std::map PostOrderListIndex; /// The next index to consume. This should always point to the lowest enabled /// entry in PostOrderList size_t Next; /// Special value for Next to indicate that the work list is empty const static size_t InvalidIndex = std::numeric_limits::max(); public: MonotoneFrameworkWorkList(const std::vector &RPOT) { for (Iterated Entry : RPOT) PostOrderList.push_back(PostOrderEntry(Entry)); initialize(); } MonotoneFrameworkWorkList(const llvm::SmallVectorImpl &RPOT) { for (Iterated Entry : RPOT) PostOrderList.push_back(PostOrderEntry(Entry)); initialize(); } MonotoneFrameworkWorkList(Iterated Entry) : MonotoneFrameworkWorkList(buildRPOT(Entry)) {} size_t size() const { revng_assert(verify()); if (empty()) return 0; size_t Result = 0; for (size_t I = Next; I < PostOrderList.size(); I++) if (PostOrderList[I].isEnabled()) Result++; return Result; } void clear() { for (PostOrderEntry &Entry : PostOrderList) Entry.disable(); Next = InvalidIndex; } void insert(Iterated Entry) { // Find the entry auto It = PostOrderListIndex.find(Entry); revng_assert(It != PostOrderListIndex.end()); // Enable it PostOrderList[It->second].enable(); // Reset next to the lowest enabled index, if necessary Next = std::min(Next, It->second); } bool empty() const { return Next == InvalidIndex; } Iterated head() const { revng_assert(Next != InvalidIndex); return PostOrderList[Next].entry(); } Iterated pop() { revng_assert(not empty()); revng_assert(verify()); // Start from the next element and look for an enabled element size_t I = Next + 1; for (; I < PostOrderList.size(); I++) if (PostOrderList[I].isEnabled()) break; // Update Next size_t OldNext = Next; Next = (I >= PostOrderList.size()) ? InvalidIndex : I; // Consume the previous next PostOrderList[OldNext].disable(); // Return the consumed entry return PostOrderList[OldNext].entry(); } private: static std::vector buildRPOT(Iterated Entry) { // Populate PostOrderList std::vector RPOT; for (Iterated I : llvm::ReversePostOrderTraversal(Entry)) RPOT.push_back(I); return RPOT; } private: void initialize() { // Reverse the list in case we don't want the reverse post order if (Visit == PostOrder) std::reverse(PostOrderList.begin(), PostOrderList.end()); // Populate the index, used for faster lookups for (unsigned I = 0; I < PostOrderList.size(); I++) PostOrderListIndex[PostOrderList[I].entry()] = I; // Initialize the next index Next = (PostOrderList.size() > 0) ? 0 : InvalidIndex; } bool verify() const { if (PostOrderList.size() == 0 and not empty()) return false; if (empty()) { // If the worklist is empty, no elements should be enabled for (const PostOrderEntry &Entry : PostOrderList) if (Entry.isEnabled()) return false; } else { // Otherwise, all the elements up to next should be disabled for (size_t I = 0; I != Next; I++) if (PostOrderList[I].isEnabled()) return false; if (not PostOrderList[Next].isEnabled()) return false; } return true; } }; /// \brief CRTP base class for an element of the lattice /// /// \note This class is more for reference. It's unused. /// /// \tparam D the derived class. template class ElementBase { public: /// \brief The partial ordering relation bool lowerThanOrEqual(const ElementBase &RHS) const { const D &This = *static_cast(this); const D &Other = static_cast(RHS); return This.lowerThanOrEqual(Other); } /// \brief The combination operator // TODO: assert monotonicity ElementBase &combine(const ElementBase &RHS) { return static_cast(this)->combine(static_cast(RHS)); } }; /// \brief Default class to represent a simple Interrupt for a MonotoneFramework /// /// This class provides the simplest possible implementation for an Interrupt /// for a Monotone Framework. /// /// In particular this interrupt is suitable for MonotoneFrameworks that are NOT /// interprocedural, and that DO NOT need to combine all the results on the /// terminal labels at the end of the analysis in a single FinalResult. /// /// With these assumptions, the resulting Interrupt is pretty simple and it just /// forwards the results of the transfer function. template class DefaultInterrupt { private: explicit DefaultInterrupt(const LatticeElement &Element) : Result(Element.copy()) {} explicit DefaultInterrupt(LatticeElement &&Element) : Result(Element) {} public: explicit DefaultInterrupt() = default; static DefaultInterrupt createInterrupt(const LatticeElement &Element) { return DefaultInterrupt(Element); } static DefaultInterrupt createInterrupt(LatticeElement &&Element) { return DefaultInterrupt(Element); } public: static bool requiresInterproceduralHandling() { return false; } LatticeElement &&extractResult() { return std::move(Result); } static bool isPartOfFinalResults() { return false; } private: LatticeElement Result; }; /// \brief Helper struct for creation of Interrupts for MonotoneFramework /// /// This is for creating generic Interrupts. /// In this case we delegate the construction of the Interrupts to the /// derived class in the CRTP. /// There is a full specialization for DefaultInterrupt. /// /// \tparam D the CRTP derived class of MonotoneFramework /// \tparam LatticeElement the type representing an element of the lattice of /// the MonotoneFramework /// \tparam InterruptTy the type representing an Interrupt of the /// MonotoneFramework template struct InterruptCreator { InterruptTy createSummaryInterrupt(D &I) { return I.createSummaryInterrupt(); } InterruptTy createNoReturnInterrupt(D &I) { return I.createNoReturnInterrupt(); } }; /// \brief Specialization of InterruptCreator for DefaultInterrupt /// /// This is for creating DefaultInterrupt. /// In case of DefaultInterrupt the Summary Interrupt is never created, /// because there is never a Final State to compute the Summary. /// /// \tparam D the CRTP derived class of MonotoneFramework /// \tparam LatticeElement the type representing an element of the lattice of /// the MonotoneFramework template struct InterruptCreator> { DefaultInterrupt createSummaryInterrupt(D &) { revng_abort(); return DefaultInterrupt(); } DefaultInterrupt createNoReturnInterrupt(D &) { return DefaultInterrupt(); } }; /// \brief CRTP base class for implementing a monotone framework /// /// This class provides the base structure to implement an analysis based on a /// monotone framework. It also provides an implementation of the MFP solution. /// /// For further information about monotone frameworks see "Principles of Program /// Analysis" (by Nielson, Flemming), Chapter 2. /// /// To use this class you need to define a Label (typically the basic block of /// the IR you're working on), a class representing an element of the lattice /// (LatticeElement, see ElementBase) and a class representing an Interrupt /// reason of the analysis. It is suggested to create a namespace for these /// classes and keep their names simple: Analysis for the class inherting from /// MonotoneFramework, Element for LatticeElement and Interrupt for Interrupt. /// /// \tparam Label the type identifying a "label" in the monotone framework, /// typically an instruction or a basic block. /// \tparam LatticeElement the type representing an element of the lattice. /// \tparam Interrupt the type describing why the analysis has been interrupted. /// \tparam D the derived class. /// \tparam SuccessorsRange the return type of D::successors. /// \tparam Visit type of visit to perform. // TODO: static_assert features of these classes (Interrupt in particular) template, bool DynamicGraph = false> class MonotoneFramework { static_assert(DynamicGraph ? Visit == BreadthFirst : true, "Cannot compute (reverse) post order for dynamic graphs"); protected: /// Lattice element where the results on return points of the function are /// accumulated LatticeElement FinalResult; /// Have we already met at least return label? This is used to ensure that the /// first final result we get is assigned to FinalResult and we're not /// combining with an uninitialized FinalResult. /// /// \note Unused if DynamicGraph == true bool FirstFinalResult; MonotoneFrameworkWorkList WorkList; /// State of the monotone framework, maps a label to a lattice element std::map State; /// List of basic blocks we want to be sure to visit again before the end of /// the analysis std::set