#ifndef ROSE_BinaryAnalysis_SymbolicExpr_H #define ROSE_BinaryAnalysis_SymbolicExpr_H #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include "../util/Map.h" #include #include #include #include #include #include #include "../util/Attribute.h" #include "../util/BitVector.h" #include "../util/Set.h" #include "../util/SharedPointer.h" #include "../util/SmallObject.h" #include #include namespace rose { namespace BinaryAnalysis { using namespace Sawyer::Container; class SMTSolver; /** Namespace supplying types and functions for symbolic expressions. * * These are used by certain instruction semantics policies and satisfiability modulo theory (SMT) solvers. These expressions * are tailored to bit-vector and integer difference logics, whereas the expression nodes in other parts of ROSE have * different goals. */ namespace SymbolicExpr { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Basic Types //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Exceptions for symbolic expressions. */ class Exception : public std::runtime_error { public: explicit Exception(const std::string &mesg) : std::runtime_error(mesg) { } }; /** Operators for interior nodes of the expression tree. * * Commutative operators generally take one or more operands. Operators such as shifting, extending, and truncating have the * size operand appearing before the bit vector on which to operate (this makes the output more human-readable since the size * operand is often a constant). */ enum Operator { OP_ADD, /**< Addition. One or more operands, all the same width. */ OP_AND, /**< Boolean AND. Operands are all Boolean (1-bit) values. See also OP_BV_AND. */ OP_ASR, /**< Arithmetic shift right. Operand B shifted by A bits; 0 <= A < width(B). A is unsigned. */ OP_BV_AND, /**< Bitwise AND. One or more operands, all the same width. */ OP_BV_OR, /**< Bitwise OR. One or more operands, all the same width. */ OP_BV_XOR, /**< Bitwise exclusive OR. One or more operands, all the same width. */ OP_CONCAT, /**< Concatenation. Operand A becomes high-order bits. Any number of operands. */ OP_EQ, /**< Equality. Two operands, both the same width. */ OP_EXTRACT, /**< Extract subsequence of bits. Extract bits [A..B) of C. 0 <= A < B <= width(C). */ OP_INVERT, /**< Boolean inversion. One operand. */ OP_ITE, /**< If-then-else. A must be one bit. Returns B if A is set, C otherwise. */ OP_LSSB, /**< Least significant set bit or zero. One operand. */ OP_MSSB, /**< Most significant set bit or zero. One operand. */ OP_NE, /**< Inequality. Two operands, both the same width. */ OP_NEGATE, /**< Arithmetic negation. One operand. */ OP_NOOP, /**< No operation. Used only by the default constructor. */ OP_OR, /**< Boolean OR. Operands are all Boolean (1-bit) values. See also OP_BV_OR. */ OP_READ, /**< Read a value from memory. Arguments are the memory state and the address expression. */ OP_ROL, /**< Rotate left. Rotate bits of B left by A bits. 0 <= A < width(B). A is unsigned. */ OP_ROR, /**< Rotate right. Rotate bits of B right by A bits. 0 <= B < width(B). A is unsigned. */ OP_SDIV, /**< Signed division. Two operands, A/B. Result width is width(A). */ OP_SET, /**< Set of expressions. Any number of operands in any order. */ OP_SEXTEND, /**< Signed extension at msb. Extend B to A bits by replicating B's most significant bit. */ OP_SGE, /**< Signed greater-than-or-equal. Two operands of equal width. Result is Boolean. */ OP_SGT, /**< Signed greater-than. Two operands of equal width. Result is Boolean. */ OP_SHL0, /**< Shift left, introducing zeros at lsb. Bits of B are shifted by A, where 0 <=A < width(B). */ OP_SHL1, /**< Shift left, introducing ones at lsb. Bits of B are shifted by A, where 0 <=A < width(B). */ OP_SHR0, /**< Shift right, introducing zeros at msb. Bits of B are shifted by A, where 0 <=A Ptr; /** Shared-ownership pointer to an expression @ref Interior node. See @ref heap_object_shared_ownership. */ typedef Sawyer::SharedPointer InteriorPtr; /** Shared-ownership pointer to an expression @ref Leaf node. See @ref heap_object_shared_ownership. */ typedef Sawyer::SharedPointer LeafPtr; typedef std::vector Nodes; typedef Map RenameMap; /** Controls formatting of expression trees when printing. */ struct Formatter { enum ShowComments { CMT_SILENT, /**< Do not show comments. */ CMT_AFTER, /**< Show comments after the node. */ CMT_INSTEAD, /**< Like CMT_AFTER, but show comments instead of variable names. */ }; Formatter() : show_comments(CMT_INSTEAD), do_rename(false), add_renames(true), use_hexadecimal(true), max_depth(0), cur_depth(0), show_width(true), show_flags(true) { } ShowComments show_comments; /**< Show node comments when printing? */ bool do_rename; /**< Use the @p renames map to rename variables to shorter names? */ bool add_renames; /**< Add additional entries to the @p renames as variables are encountered? */ bool use_hexadecimal; /**< Show values in hexadecimal and decimal rather than just decimal. */ size_t max_depth; /**< If non-zero, then replace deep parts of expressions with "...". */ size_t cur_depth; /**< Depth in expression. */ RenameMap renames; /**< Map for renaming variables to use smaller integers. */ bool show_width; /**< Show width in bits inside square brackets. */ bool show_flags; /**< Show user-defined flags inside square brackets. */ }; /** Return type for visitors. */ enum VisitAction { CONTINUE, /**< Continue the traversal as normal. */ TRUNCATE, /**< For a pre-order depth-first visit, do not descend into children. */ TERMINATE, /**< Terminate the traversal. */ }; /** Maximum number of nodes that can be reported. * * If @ref nnodes returns this value then the size of the expressions could not be counted. This can happens when the * expression contains a large number of common subexpressions. */ extern const uint64_t MAX_NNODES; // defined in .C so we don't pollute user namespace with limit macros /** Base class for visiting nodes during expression traversal. The preVisit method is called before children are visited, and * the postVisit method is called after children are visited. If preVisit returns TRUNCATE, then the children are not * visited, but the postVisit method is still called. If either method returns TERMINATE then the traversal is immediately * terminated. */ class Visitor { public: virtual ~Visitor() { } virtual VisitAction preVisit(const Ptr &) = 0; virtual VisitAction postVisit(const Ptr &) = 0; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Base Node Type //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Base class for symbolic expression nodes. * * Every node has a specified number of significant bits that is constant over the life of the node. * * In order that subtrees can be freely assigned as children of other nodes (provided the structure as a whole remains a * lattice and not a graph with cycles), two things are required: First, tree nodes are always referenced through * shared-ownership pointers that collectively own the expression node (expressions are never explicitly deleted). Second, * expression nodes are immutable once they're instantiated. There are a handful of exceptions to the immutable rule: * comments and attributes are allowed to change since they're not significant to hashing or arithmetic operations. * * Each node has a bit flags property, the bits of which are defined by the user. New nodes are created having all bits * cleared unless the user specifies a value in the constructor. Bits are significant for hashing. Simplifiers produce * result expressions whose bits are set in a predictable manner with the following rules: * * @li Interior Node Rule: The flags for an interior node are the union of the flags of its subtrees. * * @li Simplification Discard Rule: If a simplification discards a subtree then that subtree does not contribute flags to the * result. E.g., cancellation of terms in an @c add operation. * * @li Simplification Create Rule: If a simplification creates a new leaf node that doesn't depend on the input expression * that new leaf node will have zero flags. E.g., XOR of an expression with itself; an add operation where all the terms * cancel each other resulting in zero. * * @li Simplification Folding Rule: If a simplification creates a new expression from some combination of incoming expressions * then the flags of the new expression are the union of the flags from the expressions on which it depends. E.g., * constant folding, which is therefore consistent with the Interior Node Rule. * * @li Hashing Rule: User-defined flags are significant for hashing. E.g., structural equivalence will return false if the * two expressions have different flags since structural equivalence uses hashes. * * @li Relational Operator Rule: Simplification of relational operators to produce a Boolean constant will act as if they are * performing constant folding even if the simplification is on variables. E.g., (ule v1 v1) results in true * with flags the same as @c v1. */ class Node : public Sawyer::SharedObject, public Sawyer::SharedFromThis, public Sawyer::SmallObject, public Sawyer::Attribute::Storage { // Attributes are not significant for hashing or arithmetic protected: size_t nBits_; /**< Number of significant bits. Constant over the life of the node. */ size_t domainWidth_; /**< Width of domain for unary functions. E.g., memory. */ unsigned flags_; /**< Bit flags. Meaning of flags is up to the user. Low-order 16 bits are reserved. */ std::string comment_; /**< Optional comment. Only for debugging; not significant for any calculation. */ uint64_t hashval_; /**< Optional hash used as a quick way to indicate that two expressions are different. */ dyncompat::any userData_; /**< Additional user-specified data. This is not part of the hash. */ public: // Bit flags /** These flags are reserved for use within ROSE. */ static const unsigned RESERVED_FLAGS = 0x0000ffff; /** Value is somehow indeterminate. E.g., read from writable memory. */ static const unsigned INDETERMINATE = 0x00000001; /** Value is somehow unspecified. A value that is intantiated as part of processing a machine instruction where the ISA * documentation is incomplete or says that some result is unspecified or undefined. Intel documentation for the x86 shift * and rotate instructions, for example, states that certain status bits have "undefined" values after the instruction * executes. */ static const unsigned UNSPECIFIED = 0x00000002; /** Value represents bottom in dataflow analysis. If this flag is used by ROSE's dataflow engine to represent a bottom * value in a lattice. */ static const unsigned BOTTOM = 0x00000004; protected: Node() : nBits_(0), domainWidth_(0), flags_(0), hashval_(0) { } explicit Node(const std::string &comment, unsigned flags = 0) : nBits_(0), domainWidth_(0), flags_(flags), comment_(comment), hashval_(0) { } public: /** Returns true if two expressions must be equal (cannot be unequal). * * If an SMT solver is specified then that solver is used to answer this question, otherwise equality is established by * looking only at the structure of the two expressions. Two expressions can be equal without being the same width (e.g., * a 32-bit constant zero is equal to a 16-bit constant zero). */ virtual bool mustEqual(const Ptr &other, SMTSolver *) = 0; // [Robb P. Matzke 2015-10-08]: deprecated bool must_equal(const Ptr &other, SMTSolver *solver) { return mustEqual(other, solver); } /** Returns true if two expressions might be equal, but not necessarily be equal. */ virtual bool mayEqual(const Ptr &other, SMTSolver *) = 0; // [Robb P. Matzke 2015-10-08]: deprecated bool may_equal(const Ptr &other, SMTSolver *solver) { return mayEqual(other, solver); } /** Tests two expressions for structural equivalence. * * Two leaf nodes are equivalent if they are the same width and have equal values or are the same variable. Two interior * nodes are equivalent if they are the same width, the same operation, have the same number of children, and those * children are all pairwise equivalent. */ virtual bool isEquivalentTo(const Ptr &other) = 0; // [Robb P. Matzke 2015-10-08]: deprecated bool equivalent_to(const Ptr &other) { return isEquivalentTo(other); } /** Compare two expressions structurally for sorting. * * Returns -1 if @p this is less than @p other, 0 if they are structurally equal, and 1 if @p this is greater than @p * other. This function returns zero when an only when @ref isEquivalentTo returns zero, but @ref isEquivalentTo can be * much faster since it uses hashing. */ virtual int compareStructure(const Ptr &other) = 0; // [Robb P. Matzke 2015-10-08]: deprecated int structural_compare(const Ptr &other) { return compareStructure(other); } /** Substitute one value for another. * * Finds all occurrances of @p from in this expression and replace them with @p to. If a substitution occurs, then a new * expression is returned. The matching of @p from to sub-parts of this expression uses structural equivalence, the * @ref isEquivalentTo predicate. The @p from and @p to expressions must have the same width. */ virtual Ptr substitute(const Ptr &from, const Ptr &to) = 0; /** Returns true if the expression is a known numeric value. * * The value itself is stored in the @ref number property. */ virtual bool isNumber() = 0; // [Robb P. Matzke 2015-10-08]: deprecated bool is_known() { return isNumber(); } /** Property: integer value of expression node. * * Returns the integer value of a node for which @ref isKnown returns true. The high-order bits, those beyond the number * of significant bits returned by the @ref nBits propert, are guaranteed to be zero. */ virtual uint64_t toInt() = 0; // [Robb P. Matzke 2015-10-08]: deprecated uint64_t get_value() { return toInt(); } /** Property: Comment. * * Comments can be changed after a node has been created since the comment is not intended to be used for anything but * annotation and/or debugging. If many expressions are sharing the same node, then the comment is changed in all those * expressions. Changing the comment property is allowed even though nodes are generally immutable because comments are * not considered significant for comparisons, computing hash values, etc. * * @{ */ const std::string &comment() { return comment_; } void comment(const std::string &s) { comment_ = s; } /** @} */ // [Robb P. Matzke 2015-10-08]: deprecated const std::string &get_comment() { return comment(); } // [Robb P. Matzke 2015-10-08]: deprecated void set_comment(const std::string &s) { comment(s); } /** Property: User-defined data. * * User defined data is always optional and does not contribute to the hash value of an expression. The user-defined data * can be changed at any time by the user even if the expression node to which it is attached is shared between many * expressions. * * @{ */ void userData(dyncompat::any &data) { userData_ = data; } const dyncompat::any &userData() { return userData_; } /** @} */ /** Property: Number of significant bits. * * An expression with a known value is guaranteed to have all higher-order bits cleared. */ size_t nBits() { return nBits_; } // [Robb P. Matzke 2015-10-08]: deprecated size_t get_nbits() { return nBits(); } /** Property: User-defined bit flags. * * This property is significant for hashing, comparisons, and possibly other operations, therefore it is immutable. To * change the flags one must create a new expression; see @ref newFlags. */ unsigned flags() { return flags_; } unsigned get_flags() { return flags(); } /** Sets flags. Since symbolic expressions are immutable it is not possible to change the flags directly. Therefore if the * desired flags are different than the current flags a new expression is created that is the same in every other * respect. If the flags are not changed then the original expression is returned. */ Ptr newFlags(unsigned flags); /** Property: Width for memory expressions. * * The return value is non-zero if and only if this tree node is a memory expression. */ size_t domainWidth() { return domainWidth_; } /** Check whether expression is scalar. * * Everything is scalar except for memory. */ bool isScalar() { return 0 == domainWidth_; } /** Traverse the expression. * * The expression is traversed in a depth-first visit. The final return value is the final return value of the last call * to the visitor. */ virtual VisitAction depthFirstTraversal(Visitor &) = 0; // [Robb P. Matzke 2015-10-08]: deprecated VisitAction depth_first_traversal(Visitor &v) { return depthFirstTraversal(v); } /** Computes the size of an expression by counting the number of nodes. * * Operates in constant time. Note that it is possible (even likely) for the 64-bit return value to overflow in * expressions when many nodes are shared. For instance, the following loop will create an expression that contains more * than 2^64 nodes: * * @code * SymbolicExpr expr = Leaf::createVariable(32); * for(size_t i=0; i<64; ++i) * expr = Interior::create(32, OP_ADD, expr, expr) * @endcode * * When an overflow occurs the result is meaningless. * * @sa nNodesUnique */ virtual uint64_t nNodes() = 0; uint64_t nnodes() { return nNodes(); } /** Number of unique nodes in expression. */ uint64_t nNodesUnique(); // [Robb P. Matzke 2015-10-08]: deprecated uint64_t nnodesUnique() { return nNodesUnique(); } /** Returns the variables appearing in the expression. */ std::set getVariables(); // [Robb P. Matzke 2015-10-08]: deprecated std::set get_variables() { return getVariables(); } /** Dynamic cast of this object to an interior node. * * Returns null if the cast is not valid. */ InteriorPtr isInteriorNode() { return sharedFromThis().dynamicCast(); } // [Robb P. Matzke 2015-10-09]: deprecated InteriorPtr isInternalNode() { return isInteriorNode(); } /** Dynamic cast of this object to a leaf node. * * Returns null if the cast is not valid. */ LeafPtr isLeafNode() { return sharedFromThis().dynamicCast(); } /** Returns true if this node has a hash value computed and cached. The hash value zero is reserved to indicate that no * hash has been computed; if a node happens to actually hash to zero, it will not be cached and will be recomputed for * every call to hash(). */ bool isHashed() { return hashval_ != 0; } // [Robb P. Matzke 2015-10-08]: deprecated bool is_hashed() { return isHashed(); } /** Returns (and caches) the hash value for this node. If a hash value is not cached in this node, then a new hash value * is computed and cached. */ uint64_t hash(); // used internally to set the hash value void hash(uint64_t); /** A node with formatter. See the with_format() method. */ class WithFormatter { private: Ptr node; Formatter &formatter; public: WithFormatter(const Ptr &node_, Formatter &formatter_) : node(node_), formatter(formatter_) { } void print(std::ostream &stream) const { node->print(stream, formatter); } }; /** Combines a node with a formatter for printing. This is used for convenient printing with the "<<" operator. For * instance: * * @code * Formatter fmt; * fmt.show_comments = Formatter::CMT_AFTER; //show comments after the variable * Ptr expression = ...; * std::cout <<"method 1: "; expression->print(std::cout, fmt); std::cout <<"\n"; * std::cout <<"method 2: " <withFormat(fmt) <<"\n"; * std::cout <<"method 3: " <<*expression+fmt <<"\n"; * * @endcode * @{ */ WithFormatter withFormat(Formatter &fmt) { return WithFormatter(sharedFromThis(), fmt); } WithFormatter operator+(Formatter &fmt) { return withFormat(fmt); } /** @} */ // [Robb P. Matzke 2015-10-08]: deprecated WithFormatter with_format(Formatter &fmt) { return withFormat(fmt); } /** Print the expression to a stream. The output is an S-expression with no line-feeds. The format of the output is * controlled by the mutable Formatter argument. * @{ */ virtual void print(std::ostream &, Formatter &) = 0; void print(std::ostream &o) { Formatter fmt; print(o, fmt); } /** @} */ /** Asserts that expressions are acyclic. This is intended only for debugging. */ void assertAcyclic(); // [Robb P. Matzke 2015-10-08]: deprecated void assert_acyclic() { return assertAcyclic(); } /** Find common subexpressions. * * Returns a vector of the largest common subexpressions. The list is computed by performing a depth-first search on this * expression and adding expressions to the return vector whenever a subtree is encountered a second time. Therefore the * if a common subexpression A contains another common subexpression B then B will appear earlier in the list than A. */ std::vector findCommonSubexpressions(); protected: void printFlags(std::ostream &o, unsigned flags, char &bracket); }; /** Operator-specific simplification methods. */ class Simplifier { public: virtual ~Simplifier() { } /** Constant folding. The range @p begin (inclusive) to @p end (exclusive) must contain at least two nodes and all of * the nodes must be leaf nodes with known values. This method returns a new folded node if folding is possible, or * the null pointer if folding is not possible. */ virtual Ptr fold(Nodes::const_iterator /*begin*/, Nodes::const_iterator /*end*/) const { return Ptr(); } /** Rewrite the entire expression to something simpler. Returns the new node if the node can be simplified, otherwise * returns null. */ virtual Ptr rewrite(Interior *) const { return Ptr(); } }; /** Compare two expressions for STL containers. */ class ExpressionLessp { public: bool operator()(const Ptr &a, const Ptr &b) const; }; /** Set of expressions. */ typedef Sawyer::Container::Set ExpressionSet; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Simplification //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct AddSimplifier : Simplifier { virtual Ptr fold(Nodes::const_iterator, Nodes::const_iterator) const; virtual Ptr rewrite(Interior *) const; }; struct AndSimplifier : Simplifier { virtual Ptr fold(Nodes::const_iterator, Nodes::const_iterator) const; virtual Ptr rewrite(Interior *) const; }; struct OrSimplifier : Simplifier { virtual Ptr fold(Nodes::const_iterator, Nodes::const_iterator) const; virtual Ptr rewrite(Interior *) const; }; struct XorSimplifier : Simplifier { virtual Ptr fold(Nodes::const_iterator, Nodes::const_iterator) const; virtual Ptr rewrite(Interior *) const; }; struct SmulSimplifier : Simplifier { virtual Ptr fold(Nodes::const_iterator, Nodes::const_iterator) const; }; struct UmulSimplifier : Simplifier { virtual Ptr fold(Nodes::const_iterator, Nodes::const_iterator) const; }; struct ConcatSimplifier : Simplifier { virtual Ptr fold(Nodes::const_iterator, Nodes::const_iterator) const; virtual Ptr rewrite(Interior *) const; }; struct ExtractSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct AsrSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct InvertSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct NegateSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct IteSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct NoopSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct RolSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct RorSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct UextendSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SextendSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct EqSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SgeSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SgtSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SleSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SltSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct UgeSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct UgtSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct UleSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct UltSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct ZeropSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SdivSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SmodSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct UdivSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct UmodSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct ShiftSimplifier : Simplifier { bool newbits; ShiftSimplifier(bool newbits_) : newbits(newbits_) { } Ptr combine_strengths(Ptr strength1, Ptr strength2, size_t value_width) const; }; struct ShlSimplifier : ShiftSimplifier { ShlSimplifier(bool newbits_) : ShiftSimplifier(newbits_) { } virtual Ptr rewrite(Interior *) const; }; struct ShrSimplifier : ShiftSimplifier { ShrSimplifier(bool newbits_) : ShiftSimplifier(newbits_) { } virtual Ptr rewrite(Interior *) const; }; struct LssbSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct MssbSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; struct SetSimplifier : Simplifier { virtual Ptr rewrite(Interior *) const; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Interior Nodes //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Interior node of an expression tree for instruction semantics. Each interior node has an operator (constant for the life of * the node and obtainable with get_operator()) and zero or more children. Children are added to the interior node during the * construction phase. Once construction is complete, the children should only change in ways that don't affect the value of * the node as a whole (since this node might be pointed to by any number of expressions). */ class Interior : public Node { private: Operator op_; Nodes children_; uint64_t nnodes_; // total number of nodes; self + children's nnodes // Constructors should not be called directly. Use the create() class method instead. This is to help prevent // accidently using pointers to these objects -- all access should be through shared-ownership pointers. Interior(size_t nbits, Operator op, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Interior(size_t nbits, Operator op, const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Interior(size_t nbits, Operator op, const Ptr &a, const Ptr &b, const Ptr &c, const std::string &comment = "", unsigned flags = 0); Interior(size_t nbits, Operator op, const Nodes &children, const std::string &comment = "", unsigned flags = 0); public: /** Create a new expression node. Although we're creating interior nodes, the simplification process might replace it with * a leaf node. Use these class methods instead of c'tors. * * Flags are normally initialized as the union of the flags of the operator arguments subject to various rules in the * expression simplifiers. Flags specified in the constructor are set in addition to those that would normally be set. * * @{ */ static Ptr create(size_t nbits, Operator op, const Ptr &a, const std::string &comment = "", unsigned flags = 0) { InteriorPtr retval(new Interior(nbits, op, a, comment, flags)); return retval->simplifyTop(); } static Ptr create(size_t nbits, Operator op, const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0) { InteriorPtr retval(new Interior(nbits, op, a, b, comment, flags)); return retval->simplifyTop(); } static Ptr create(size_t nbits, Operator op, const Ptr &a, const Ptr &b, const Ptr &c, const std::string &comment = "", unsigned flags = 0) { InteriorPtr retval(new Interior(nbits, op, a, b, c, comment, flags)); return retval->simplifyTop(); } static Ptr create(size_t nbits, Operator op, const Nodes &children, const std::string &comment = "", unsigned flags = 0) { InteriorPtr retval(new Interior(nbits, op, children, comment, flags)); return retval->simplifyTop(); } /** @} */ /* see superclass, where these are pure virtual */ virtual bool mustEqual(const Ptr &other, SMTSolver *); virtual bool mayEqual(const Ptr &other, SMTSolver *); virtual bool isEquivalentTo(const Ptr &other); virtual int compareStructure(const Ptr &other); virtual Ptr substitute(const Ptr &from, const Ptr &to); virtual bool isNumber() { return false; /*if it's known, then it would have been folded to a leaf*/ } virtual uint64_t toInt() { ASSERT_forbid2(true, "not a number"); return 0; } virtual VisitAction depthFirstTraversal(Visitor &); virtual uint64_t nNodes() { return nnodes_; } /** Returns the number of children. */ size_t nChildren() { return children_.size(); } // [Robb P. Matzke 2015-10-09]: deprecated size_t nchildren() { return nChildren(); } /** Returns the specified child. */ Ptr child(size_t idx) { ASSERT_require(idx < children_.size()); return children_[idx]; } /** Property: Children. * * The children are the operands for an operator expression. */ const Nodes &children() { return children_; } // [Robb P. Matzke 2015-10-09]: deprecated Nodes get_children() { return children(); } /** Returns the operator. */ Operator getOperator() { return op_; } // [Robb P. Matzke 2015-10-09]: deprecated Operator get_operator() { return getOperator(); } /** Simplifies the specified interior node. Returns a new node if necessary, otherwise returns this. */ Ptr simplifyTop(); /** Perform constant folding. This method returns either a new expression (if changes were mde) or the original * expression. The simplifier is specific to the kind of operation at the node being simplified. */ Ptr foldConstants(const Simplifier &); // [Robb P. Matzke 2015-10-09]: deprecated Ptr constant_folding(const Simplifier &simplifier) { return foldConstants(simplifier); } /** Simplifies non-associative operators by flattening the specified interior node with its children that are the same * interior node type. Call this only if the top node is a truly non-associative. A new node is returned only if * changed. When calling both nonassociative and commutative, it's usually more appropriate to call nonassociative * first. */ InteriorPtr associative(); // [Robb P. Matzke 2015-10-09]: deprecated InteriorPtr nonassociative() { return associative(); } /** Simplifies commutative operators by sorting arguments. The arguments are sorted so that all the interior nodes come * before the leaf nodes. Call this only if the top node is truly commutative. A new node is returned only if * changed. When calling both nonassociative and commutative, it's usually more appropriate to call nonassociative * first. */ InteriorPtr commutative(); /** Simplifies involutary operators. An involutary operator is one that is its own inverse. This method should only be * called if this node is an interior node whose operator has the involutary property (such as invert or negate). Returns * either a new expression that is simplified, or the original expression. */ Ptr involutary(); /** Simplifies nested shift-like operators. Simplifies (shift AMT1 (shift AMT2 X)) to (shift (add AMT1 AMT2) X). */ Ptr additiveNesting(); Ptr additive_nesting() { return additiveNesting(); } /** Removes identity arguments. Returns either a new expression or the original expression. */ Ptr identity(uint64_t ident); /** Replaces a binary operator with its only argument. Returns either a new expression or the original expression. */ Ptr unaryNoOp(); /** Simplify an interior node. Returns a new node if this node could be simplified, otherwise returns this node. When * the simplification could result in a leaf node, we return an OP_NOOP interior node instead. */ Ptr rewrite(const Simplifier &simplifier); virtual void print(std::ostream &, Formatter &); protected: /** Appends @p child as a new child of this node. This must only be called from constructors. */ void addChild(const Ptr &child); // [Robb P. Matzke 2015-10-09]: deprecated void add_child(const Ptr &child) { addChild(child); } /** Adjust width based on operands. This must only be called from constructors. */ void adjustWidth(); /** Adjust user-defined bit flags. This must only be called from constructors. Flags are the union of the operand flags * subject to simplification rules, unioned with the specified flags. */ void adjustBitFlags(unsigned extraFlags); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Leaf Nodes //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Leaf node of an expression tree for instruction semantics. * * A leaf node is either a known bit vector value, a free bit vector variable, or a memory state. */ class Leaf : public Node { private: enum LeafType { CONSTANT, BITVECTOR, MEMORY }; LeafType leafType_; Sawyer::Container::BitVector bits_; /**< Value when 'known' is true */ uint64_t name_; /**< Variable ID number when 'known' is false. */ // Private to help prevent creating pointers to leaf nodes. See create_* methods instead. Leaf() : Node(""), leafType_(CONSTANT), name_(0) { } explicit Leaf(const std::string &comment, unsigned flags = 0) : Node(comment, flags), leafType_(CONSTANT), name_(0) { } public: /** Construct a new free variable with a specified number of significant bits. */ static LeafPtr createVariable(size_t nbits, const std::string &comment = "", unsigned flags = 0); // [Robb P. Matzke 2015-10-09]: deprecated static LeafPtr create_variable(size_t nbits, std::string comment = "", unsigned flags = 0) { return createVariable(nbits, comment, flags); } /** Construct another reference to an existing variable. This method is used internally by the expression parsing * mechanism to produce a new instance of some previously existing variable -- both instances are the same variable and * therefore should be given the same size (although this consistency cannot be checked automatically). */ static LeafPtr createExistingVariable(size_t nbits, uint64_t id, const std::string &comment = "", unsigned flags = 0); // [Robb P. Matzke 2015-10-09]: deprecated static LeafPtr create_existing_variable(size_t nbits, uint64_t id, const std::string &comment = "", unsigned flags = 0) { return createExistingVariable(nbits, id, comment, flags); } /** Construct a new integer with the specified number of significant bits. Any high-order bits beyond the specified size * will be zeroed. */ static LeafPtr createInteger(size_t nbits, uint64_t n, const std::string &comment = "", unsigned flags = 0); // [Robb P. Matzke 2015-10-09]: deprecated static LeafPtr create_integer(size_t nbits, uint64_t n, std::string comment = "", unsigned flags = 0) { return createInteger(nbits, n, comment, flags); } /** Construct a new known value with the specified bits. */ static LeafPtr createConstant(const Sawyer::Container::BitVector &bits, const std::string &comment = "", unsigned flags = 0); // [Robb P. Matzke 2015-10-09]: deprecated static LeafPtr create_constant(const Sawyer::Container::BitVector &bits, std::string comment = "", unsigned flags = 0) { return createConstant(bits, comment, flags); } /** Create a new Boolean, a single-bit integer. */ static LeafPtr createBoolean(bool b, const std::string &comment = "", unsigned flags = 0) { return createInteger(1, (uint64_t)(b ? 1 : 0), comment, flags); } // [Robb P. Matzke 2015-10-09]: deprecated static LeafPtr create_boolean(bool b, std::string comment = "", unsigned flags = 0) { return createBoolean(b, comment, flags); } /** Construct a new memory state. A memory state is a function that maps addresses to values. */ static LeafPtr createMemory(size_t addressWidth, size_t valueWidth, const std::string &comment = "", unsigned flags = 0); /** Construct another reference to an existing variable. This method is used internally by the expression parsing * mechanism to produce a new instance of some previously existing memory state. Both instances are the same state and * therefore should be given the same domain and range size (although this consistency cannot be checked automatically.) */ static LeafPtr createExistingMemory(size_t addressWidth, size_t valueWidth, uint64_t id, const std::string &comment = "", unsigned flags = 0); // [Robb P. Matzke 2015-10-09]: deprecated static LeafPtr create_memory(size_t addressWidth, size_t valueWidth, std::string comment = "", unsigned flags = 0) { return createMemory(addressWidth, valueWidth, comment, flags); } // from base class virtual bool isNumber(); virtual uint64_t toInt(); virtual bool mustEqual(const Ptr &other, SMTSolver *); virtual bool mayEqual(const Ptr &other, SMTSolver *); virtual bool isEquivalentTo(const Ptr &other); virtual int compareStructure(const Ptr &other); virtual Ptr substitute(const Ptr &from, const Ptr &to); virtual VisitAction depthFirstTraversal(Visitor &); virtual uint64_t nNodes() { return 1; } /** Property: Bits stored for numeric values. */ const Sawyer::Container::BitVector &bits(); // [Robb P. Matzke 2015-10-09]: deprecated const Sawyer::Container::BitVector &get_bits() { return bits(); } /** Is the node a bitvector variable? */ virtual bool isVariable(); // [Robb P. Matzke 2015-10-09]: deprecated bool is_variable() { return isVariable(); } /** Does the node represent memory? */ virtual bool isMemory(); // [Robb P. Matzke 2015-10-09]: deprecated bool is_memory() { return isMemory(); } /** Returns the name ID of a free variable. * * The output functions print variables as "vN" where N is an integer. It is this N that this method returns. It should * only be invoked on leaf nodes for which @ref isNumber returns false. */ uint64_t nameId(); // [Robb P. Matzke 2015-10-09]: deprecated uint64_t get_name() { return nameId(); } /** Returns a string for the leaf. * * Variables are returned as "vN", memory is returned as "mN", and constants are returned as a hexadecimal string, where N * is a variable or memory number. */ std::string toString(); // documented in super class virtual void print(std::ostream &, Formatter &); /** Prints an integer interpreted as a signed value. */ void printAsSigned(std::ostream &, Formatter &, bool asSigned = true); // [Robb P. Matzke 2015-10-09]: deprecated void print_as_signed(std::ostream &stream, Formatter &formatter, bool as_signed = true) { printAsSigned(stream, formatter, as_signed); } /** Prints an integer interpreted as an unsigned value. */ void printAsUnsigned(std::ostream &o, Formatter &f) { printAsSigned(o, f, false); } // [Robb P. Matzke 2015-10-09]: deprecated void print_as_unsigned(std::ostream &o, Formatter &f) { printAsUnsigned(o, f); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Factories //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Leaf constructor. * * Constructs an expression leaf node. This is a wrapper around one of the "create" factory methods in @ref Leaf. * * @{ */ Ptr makeVariable(size_t nbits, const std::string &comment = "", unsigned flags = 0); Ptr makeExistingVariable(size_t nbits, uint64_t id, const std::string &comment = "", unsigned flags = 0); Ptr makeInteger(size_t nbits, uint64_t n, const std::string &comment = "", unsigned flags = 0); Ptr makeConstant(const Sawyer::Container::BitVector &, const std::string &comment = "", unsigned flags = 0); Ptr makeBoolean(bool, const std::string &comment = "", unsigned flags = 0); Ptr makeMemory(size_t addressWidth, size_t valueWidth, const std::string &comment = "", unsigned flags = 0); Ptr makeExistingMemory(size_t addressWidth, size_t valueWidth, uint64_t id, const std::string &comment = "", unsigned flags = 0); /** @} */ /** Interior node constructor. * * Constructs an interior node. This is a wrapper around one of the "create" factory methods in @ref Interior. It * interprets its operands as unsigned values unless the method has "Signed" in its name. * * @{ */ Ptr makeAdd(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeBooleanAnd(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeAsr(const Ptr &sa, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeAnd(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeOr(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeXor(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeConcat(const Ptr &hi, const Ptr &lo, const std::string &comment = "", unsigned flags = 0); Ptr makeEq(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeExtract(const Ptr &begin, const Ptr &end, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeInvert(const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeIte(const Ptr &cond, const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeLssb(const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeMssb(const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeNe(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeNegate(const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeBooleanOr(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeRead(const Ptr &mem, const Ptr &addr, const std::string &comment = "", unsigned flags = 0); Ptr makeRol(const Ptr &sa, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeRor(const Ptr &sa, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeSet(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeSet(const Ptr &a, const Ptr &b, const Ptr &c, const std::string &comment = "", unsigned flags = 0); Ptr makeSignedDiv(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeSignExtend(const Ptr &newSize, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeSignedGe(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeSignedGt(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeShl0(const Ptr &sa, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeShl1(const Ptr &sa, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeShr0(const Ptr &sa, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeShr1(const Ptr &sa, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeSignedLe(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeSignedLt(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeSignedMod(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeSignedMul(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeDiv(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeExtend(const Ptr &newSize, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeGe(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeGt(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeLe(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeLt(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeMod(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeMul(const Ptr &a, const Ptr &b, const std::string &comment = "", unsigned flags = 0); Ptr makeWrite(const Ptr &mem, const Ptr &addr, const Ptr &a, const std::string &comment = "", unsigned flags = 0); Ptr makeZerop(const Ptr &a, const std::string &comment = "", unsigned flags = 0); /** @} */ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Miscellaneous functions //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::ostream &operator<<(std::ostream &o, Node &); std::ostream &operator<<(std::ostream &o, const Node::WithFormatter &); /** Convert a set to an ite expression. */ Ptr setToIte(const Ptr &); /** Counts the number of nodes. * * Counts the total number of nodes in multiple expressions. The return value is a saturated sum, returning MAX_NNODES if an * overflow occurs. */ template uint64_t nNodes(InputIterator begin, InputIterator end) { uint64_t total = 0; for (InputIterator ii = begin; ii != end; ++ii) { uint64_t n = (*ii)->nnodes(); if (MAX_NNODES == n) return MAX_NNODES; if (total + n < total) return MAX_NNODES; total += n; } return total; } // [Robb P. Matzke 2015-10-08]: deprecated template uint64_t nnodes(InputIterator begin, InputIterator end); template uint64_t nnodes(InputIterator begin, InputIterator end) { return nNodes(begin, end); } /** Counts the number of unique nodes. * * Counts the number of unique nodes across a number of expressions. Nodes shared between two expressions are counted only * one time, whereas the Node::nnodes virtual method counts shared nodes multiple times. */ template uint64_t nNodesUnique(InputIterator begin, InputIterator end) { struct T1 : Visitor { typedef std::set SeenNodes; SeenNodes seen; // nodes that we've already seen, and the subtree size uint64_t nUnique; // number of unique nodes T1() : nUnique(0) { } VisitAction preVisit(const Ptr &node) { if (seen.insert(getRawPointer(node)).second) { ++nUnique; return CONTINUE; // this node has not been seen before; traverse into children } else { return TRUNCATE; // this node has been seen already; skip over the children } } VisitAction postVisit(const Ptr &/*node*/) { return CONTINUE; } } visitor; VisitAction status = CONTINUE; for (InputIterator ii = begin; ii != end && TERMINATE != status; ++ii) status = (*ii)->depthFirstTraversal(visitor); return visitor.nUnique; } // [Robb P. Matzke 2015-10-08]: deprecated template uint64_t nnodesUnique(InputIterator begin, InputIterator end); template uint64_t nnodesUnique(InputIterator begin, InputIterator end) { return nNodesUnique(begin, end); } /** Find common subexpressions. * * This is similar to @ref Node::findCommonSubexpressions except the analysis is over a collection of expressions * rather than a single expression. * * @{ */ std::vector findCommonSubexpressions(const std::vector &); template std::vector findCommonSubexpressions(InputIterator begin, InputIterator end) { typedef Sawyer::Container::Map NodeCounts; struct T1 : Visitor { NodeCounts nodeCounts; std::vector result; VisitAction preVisit(const Ptr &node) { size_t &nSeen = nodeCounts.insertMaybe(node, 0); if (2 == ++nSeen) result.push_back(node); return nSeen > 1 ? TRUNCATE : CONTINUE; } VisitAction postVisit(const Ptr &) { return CONTINUE; } } visitor; for (InputIterator ii = begin; ii != end; ++ii) (*ii)->depthFirstTraversal(visitor); return visitor.result; } /** @} */ } // namespace } // namespace } // namespace #endif