include/FixpointIterator.h (49 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include "AbstractDomain.h" namespace sparta { /* * This class defines the interface to a chaotic fixpoint iterator. A detailed * exposition of chaotic fixpoint iteration and its use in Abstract * Interpretation can be found in the following paper: * * Patrick Cousot & Radhia Cousot. Abstract interpretation and application to * logic programs. Journal of Logic Programming, 13(2—3):103—179, 1992. * * A chaotic fixpoint iterator takes a control-flow graph (CFG) and an abstract * domain as inputs. The notion of CFG used here is very broad and includes call * graphs, dependency graphs of systems of semantic equations, etc. * * The interface to the CFG is specified by a structure that should have the * following layout: * * class CFG { * using Graph = ...; * using NodeId = ...; * using EdgeId = ...; * * static const NodeId entry(const Graph& graph) { ... } * static const NodeId source(const Graph& graph, const EdgeId& e) { ... } * static const NodeId target(const Graph& graph, const EdgeId& e) { ... } * * // Edges is an arbitrary type representing a collection of edges. The only * // requirement is that it must define a standard iterator interface. * static Edges predecessors(const Graph& graph, const NodeId& m) { ... } * static Edges successors(const Graph& graph, const NodeId& m) { ... } * } * */ template <typename GraphInterface, typename Domain> class FixpointIterator { public: using Graph = typename GraphInterface::Graph; using NodeId = typename GraphInterface::NodeId; using EdgeId = typename GraphInterface::EdgeId; virtual ~FixpointIterator() { static_assert(std::is_base_of<AbstractDomain<Domain>, Domain>::value, "Domain does not inherit from AbstractDomain"); // Check that GraphInterface has the necessary methods. // We specify it here instead of putting the static asserts in the // destructor of a CRTP-style base class because the destructor may not be // instantiated when we don't create any instances of the GraphInterface // class. // The graph is specified by its root node together with the successors, // predecessors, and edge source/target functions. static_assert( std::is_same<decltype(GraphInterface::entry(std::declval<Graph>())), NodeId>::value, "No implementation of entry()"); static_assert( !std::is_same< typename std::iterator_traits<typename std::remove_reference< decltype(GraphInterface::predecessors( std::declval<Graph>(), std::declval<NodeId>()))>::type::iterator>::value_type, void>::value, "No implementation of predecessors() that returns an iterable type"); static_assert( !std::is_same< typename std::iterator_traits<typename std::remove_reference< decltype(GraphInterface::successors( std::declval<Graph>(), std::declval<NodeId>()))>::type::iterator>::value_type, void>::value, "No implementation of successors() that returns an iterable type"); static_assert( std::is_same<decltype(GraphInterface::source(std::declval<Graph>(), std::declval<EdgeId>())), NodeId>::value, "No implementation of source()"); static_assert( std::is_same<decltype(GraphInterface::target(std::declval<Graph>(), std::declval<EdgeId>())), NodeId>::value, "No implementation of target()"); } /* * This method implements the semantic transformer for each node in the * control-flow graph. For better performance, the transformer operates by * modifying the current state via side effects (hence the pointer to an * abstract value). The method is invoked with an abstract value describing * the state of the program upon entering the node. When the method returns, * the abstract value 'current_state' should contain the state of the program * after the node has been processed. If a node represents a basic block, the * same abstract value can be used in sequence to analyze all instructions in * the block, thus avoiding costly copies between instructions. * * Node transformers are required to be monotonic. * */ virtual void analyze_node(const NodeId& node, Domain* current_state) const = 0; /* * Edges in the control-flow graph may be associated with different behaviors * that have distinct semantics (conditional branch, exception, etc.). This * method describes the effect of traversing an outgoing edge on the state of * the program, when the source node is exited and control is transferred over * to the target node. * * Edge transformers are required to be monotonic. * */ virtual Domain analyze_edge(const EdgeId& edge, const Domain& exit_state_at_source) const = 0; }; } // namespace sparta