Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

1410 lines
63 KiB
C++

#ifndef ROSE_BinaryAnalysis_SymbolicExpr_H
#define ROSE_BinaryAnalysis_SymbolicExpr_H
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include "../util/Map.h"
#include <iosfwd>
#include <stddef.h>
#include <string>
#include <cassert>
#include <dyncompat/any.hpp>
#include <inttypes.h>
#include "../util/Attribute.h"
#include "../util/BitVector.h"
#include "../util/Set.h"
#include "../util/SharedPointer.h"
#include "../util/SmallObject.h"
#include <set>
#include <vector>
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 <width(B). */
OP_SHR1,
/**< Shift right, introducing ones at msb. Bits of B are shifted by A, where 0 <=A <width(B). */
OP_SLE,
/**< Signed less-than-or-equal. Two operands of equal width. Result is Boolean. */
OP_SLT,
/**< Signed less-than. Two operands of equal width. Result is Boolean. */
OP_SMOD,
/**< Signed modulus. Two operands, A%B. Result width is width(B). */
OP_SMUL,
/**< Signed multiplication. Two operands A*B. Result width is width(A)+width(B). */
OP_UDIV,
/**< Signed division. Two operands, A/B. Result width is width(A). */
OP_UEXTEND,
/**< Unsigned extention at msb. Extend B to A bits by introducing zeros at the msb of B. */
OP_UGE,
/**< Unsigned greater-than-or-equal. Two operands of equal width. Boolean result. */
OP_UGT,
/**< Unsigned greater-than. Two operands of equal width. Result is Boolean. */
OP_ULE,
/**< Unsigned less-than-or-equal. Two operands of equal width. Result is Boolean. */
OP_ULT,
/**< Unsigned less-than. Two operands of equal width. Result is Boolean (1-bit vector). */
OP_UMOD,
/**< Unsigned modulus. Two operands, A%B. Result width is width(B). */
OP_UMUL,
/**< Unsigned multiplication. Two operands, A*B. Result width is width(A)+width(B). */
OP_WRITE,
/**< Write (update) memory with a new value. Arguments are memory, address and value. */
OP_ZEROP /**< Equal to zero. One operand. Result is a single bit, set iff A is equal to zero. */
};
std::string toStr(Operator);
class Node;
class Interior;
class Leaf;
/** Shared-ownership pointer to an expression @ref Node. See @ref heap_object_shared_ownership. */
typedef Sawyer::SharedPointer<Node> Ptr;
/** Shared-ownership pointer to an expression @ref Interior node. See @ref heap_object_shared_ownership. */
typedef Sawyer::SharedPointer<Interior> InteriorPtr;
/** Shared-ownership pointer to an expression @ref Leaf node. See @ref heap_object_shared_ownership. */
typedef Sawyer::SharedPointer<Leaf> LeafPtr;
typedef std::vector <Ptr> Nodes;
typedef Map <uint64_t, uint64_t> 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., <code>(ule v1 v1)</code> results in true
* with flags the same as @c v1. */
class Node
: public Sawyer::SharedObject,
public Sawyer::SharedFromThis<Node>,
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 <LeafPtr> getVariables();
// [Robb P. Matzke 2015-10-08]: deprecated
std::set <LeafPtr> 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<Interior>();
}
// [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<Leaf>();
}
/** 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: " <<expression->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 <Ptr> 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<Ptr, ExpressionLessp> 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<typename InputIterator>
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<typename InputIterator>
uint64_t
nnodes(InputIterator begin, InputIterator end);
template<typename InputIterator>
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<typename InputIterator>
uint64_t
nNodesUnique(InputIterator begin, InputIterator end) {
struct T1 : Visitor {
typedef std::set<const Node *> 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<typename InputIterator>
uint64_t
nnodesUnique(InputIterator begin, InputIterator end);
template<typename InputIterator>
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 <Ptr> findCommonSubexpressions(const std::vector <Ptr> &);
template<typename InputIterator>
std::vector <Ptr>
findCommonSubexpressions(InputIterator begin, InputIterator end) {
typedef Sawyer::Container::Map<Ptr, size_t> NodeCounts;
struct T1 : Visitor {
NodeCounts nodeCounts;
std::vector <Ptr> 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