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
2779 lines
99 KiB
C++
2779 lines
99 KiB
C++
#define __STDC_LIMIT_MACROS
|
|
|
|
//#include "sage3basic.h"
|
|
#include <algorithm>
|
|
#include "../util/StringUtility.h"
|
|
|
|
#include "BinarySymbolicExpr.h"
|
|
#include "SMTSolver.h"
|
|
//#include "stringify.h"
|
|
#include "../integerOps.h"
|
|
#include "../util/Combinatorics.h"
|
|
|
|
#include <dyncompat/foreach.hpp>
|
|
#include <dyncompat/thread/locks.hpp>
|
|
#include <dyncompat/thread/mutex.hpp>
|
|
|
|
namespace rose {
|
|
namespace BinaryAnalysis {
|
|
namespace SymbolicExpr {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Supporting functions
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// A mutex that's used by various methods in this namespace
|
|
static dyncompat::mutex symbolicExprMutex;
|
|
|
|
// Returns the next name counter. If @p useThis is specified then return that value and make sure the next value to be
|
|
// returned is larger.
|
|
static uint64_t
|
|
nextNameCounter(uint64_t useThis = (uint64_t)(-1)) {
|
|
static dyncompat::mutex mutex;
|
|
static uint64_t counter = 0;
|
|
dyncompat::lock_guard<dyncompat::mutex> lock(mutex);
|
|
if (useThis == (uint64_t)(-1))
|
|
return ++counter;
|
|
counter = std::max(counter, useThis);
|
|
return useThis;
|
|
}
|
|
|
|
const uint64_t
|
|
MAX_NNODES = UINT64_MAX;
|
|
|
|
std::string
|
|
toStr(Operator o) {
|
|
char buf[64];
|
|
//FIXME
|
|
//std::string s = 1fyBinaryAnalysisSymbolicExprOperator(o, "OP_");
|
|
std::string s = "";
|
|
ASSERT_require(s.size()<sizeof buf);
|
|
strcpy(buf, s.c_str());
|
|
for (char *s=buf; *s; s++) {
|
|
if ('_'==*s) {
|
|
*s = '-';
|
|
} else {
|
|
*s = tolower(*s);
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// Escape control and non-printing characters
|
|
static std::string
|
|
escapeCharacter(char ch) {
|
|
switch (ch) {
|
|
case '\a': return "\\a";
|
|
case '\b': return "\\b";
|
|
case '\t': return "\\t";
|
|
case '\n': return "\\n";
|
|
case '\v': return "\\v";
|
|
case '\f': return "\\f";
|
|
case '\r': return "\\r";
|
|
default:
|
|
if (!isprint(ch)) {
|
|
char buf[16];
|
|
sprintf(buf, "\\%03o", (unsigned)ch);
|
|
return buf;
|
|
}
|
|
return std::string(1, ch);
|
|
}
|
|
}
|
|
|
|
// Escape a string when it appears where a symbol name could appear.
|
|
static std::string
|
|
nameEscape(const std::string &s) {
|
|
std::string retval;
|
|
DYN_FOREACH (char ch, s) {
|
|
switch (ch) {
|
|
case '(':
|
|
case ')':
|
|
case '[':
|
|
case ']':
|
|
case '<':
|
|
case '>':
|
|
retval += std::string("\\") + ch;
|
|
break;
|
|
default:
|
|
retval += escapeCharacter(ch);
|
|
break;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
// Escape text that appears inside a "<...>" style comment.
|
|
static std::string
|
|
commentEscape(const std::string &s) {
|
|
std::string retval;
|
|
|
|
// Escape angle brackets if they're not balanced. We could be smarter and escape only the unbalanced angle brackets. We'll
|
|
// leave that as an exercise for the reader. ;-)
|
|
bool escapeAngleBrackets = false;
|
|
int angleBracketDepth = 0;
|
|
DYN_FOREACH (char ch, s) {
|
|
if ('<' == ch) {
|
|
++angleBracketDepth;
|
|
} else if ('>' == ch && --angleBracketDepth < 0) {
|
|
escapeAngleBrackets = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DYN_FOREACH (char ch, s) {
|
|
switch (ch) {
|
|
case '<':
|
|
case '>':
|
|
if (escapeAngleBrackets) {
|
|
retval += std::string("\\") + ch;
|
|
} else {
|
|
retval += ch;
|
|
}
|
|
break;
|
|
default:
|
|
retval += ch;
|
|
break;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool
|
|
ExpressionLessp::operator()(const Ptr &a, const Ptr &b) const {
|
|
if (a == NULL || b == NULL)
|
|
return a == NULL && b != NULL;
|
|
return a->hash() < b->hash();
|
|
}
|
|
|
|
Ptr
|
|
setToIte(const Ptr &set) {
|
|
ASSERT_not_null(set);
|
|
InteriorPtr iset = set->isInteriorNode();
|
|
if (!iset || iset->getOperator() != OP_SET)
|
|
return set;
|
|
ASSERT_require(iset->nChildren() >= 1);
|
|
Ptr condVar = makeVariable(32);
|
|
Ptr retval;
|
|
for (size_t i=iset->nChildren(); i>0; --i) {
|
|
Ptr member = iset->child(i-1);
|
|
if (!retval) {
|
|
retval = member;
|
|
} else {
|
|
Ptr cond = makeEq(condVar, makeInteger(32, i));
|
|
retval = makeIte(cond, member, retval);
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Base node
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Ptr
|
|
Node::newFlags(unsigned newFlags) {
|
|
if (newFlags == flags_)
|
|
return sharedFromThis();
|
|
if (InteriorPtr inode = isInteriorNode())
|
|
return Interior::create(0, inode->getOperator(), inode->children(), comment(), newFlags);
|
|
LeafPtr lnode = isLeafNode();
|
|
ASSERT_not_null(lnode);
|
|
if (lnode->isNumber())
|
|
return makeConstant(lnode->bits(), comment(), newFlags);
|
|
if (lnode->isVariable())
|
|
return makeVariable(nBits(), comment(), newFlags);
|
|
if (lnode->isMemory())
|
|
return makeMemory(domainWidth(), nBits(), comment(), newFlags);
|
|
ASSERT_not_reachable("invalid leaf node type");
|
|
}
|
|
|
|
std::set<LeafPtr>
|
|
Node::getVariables() {
|
|
struct T1: public Visitor {
|
|
std::set<LeafPtr> vars;
|
|
VisitAction preVisit(const Ptr&) {
|
|
return CONTINUE;
|
|
}
|
|
VisitAction postVisit(const Ptr &node) {
|
|
LeafPtr l_node = node->isLeafNode();
|
|
if (l_node && !l_node->isNumber())
|
|
vars.insert(l_node);
|
|
return CONTINUE;
|
|
}
|
|
} t1;
|
|
depthFirstTraversal(t1);
|
|
return t1.vars;
|
|
}
|
|
|
|
struct Hasher: Visitor {
|
|
virtual VisitAction preVisit(const Ptr &node) {
|
|
return 0 == node->isHashed() ? CONTINUE : TRUNCATE;
|
|
}
|
|
|
|
virtual VisitAction postVisit(const Ptr &node) {
|
|
if (!node->isHashed()) { // probably true, but some other thread may have beaten us here.
|
|
uint64_t h = hash(hash(node->domainWidth(), node->nBits()), node->flags());
|
|
if (LeafPtr leaf = node->isLeafNode()) {
|
|
if (leaf->isNumber()) {
|
|
if (leaf->nBits() <= 64) {
|
|
h = hash(h, leaf->toInt());
|
|
} else {
|
|
for (size_t i=0; i<leaf->nBits(); i+=64) {
|
|
typedef Sawyer::Container::BitVector::BitRange BitRange;
|
|
size_t j = std::min(i+64, leaf->nBits());
|
|
h = hash(h, leaf->bits().toInteger(BitRange::hull(i, j-1)));
|
|
}
|
|
}
|
|
} else {
|
|
// It's okay to not hash whether the leaf is a variable or memory because variables always have a zero
|
|
// domain width and memory has a non-zero width, which is already incorporated into the hash from above.
|
|
h = hash(h, leaf->nameId());
|
|
}
|
|
} else {
|
|
InteriorPtr inode = node->isInteriorNode();
|
|
ASSERT_not_null(inode);
|
|
h = hash(h, inode->getOperator());
|
|
DYN_FOREACH (const Ptr &child, inode->children()) {
|
|
ASSERT_require(child->isHashed());
|
|
h = hash(h, child->hash());
|
|
}
|
|
}
|
|
node->hash(h);
|
|
}
|
|
return CONTINUE;
|
|
}
|
|
|
|
// Incorporates data into the existing hash, h, and returns a new hash. This is no particular well-known algorithm, but
|
|
// testing showed that it gives pretty well-distributed results for close values, particularly when called on two or more
|
|
// pieces of data.
|
|
uint64_t hash(uint64_t h, uint64_t data) {
|
|
for (size_t i=0; i<64-6; i += 6) {
|
|
unsigned sa = ((data >> i) ^ h ^ i) & 0x3f;
|
|
h = (h >> (64-sa)) | (h << sa);
|
|
h ^= data;
|
|
}
|
|
return h;
|
|
}
|
|
};
|
|
|
|
uint64_t
|
|
Node::hash() {
|
|
if (0==hashval_) {
|
|
Hasher hasher;
|
|
depthFirstTraversal(hasher);
|
|
}
|
|
dyncompat::unique_lock<dyncompat::mutex> lock(symbolicExprMutex);
|
|
return hashval_;
|
|
}
|
|
|
|
void
|
|
Node::hash(uint64_t h) {
|
|
dyncompat::unique_lock<dyncompat::mutex> lock(symbolicExprMutex);
|
|
hashval_ = h;
|
|
}
|
|
|
|
void
|
|
Node::assertAcyclic() {
|
|
#ifndef NDEBUG
|
|
struct T1: Visitor {
|
|
std::vector<const Node*> ancestors;
|
|
VisitAction preVisit(const Ptr &node) {
|
|
ASSERT_require(std::find(ancestors.begin(), ancestors.end(), getRawPointer(node))==ancestors.end());
|
|
ancestors.push_back(getRawPointer(node));
|
|
return CONTINUE;
|
|
}
|
|
VisitAction postVisit(const Ptr &node) {
|
|
ASSERT_require(!ancestors.empty() && ancestors.back()==getRawPointer(node));
|
|
ancestors.pop_back();
|
|
return CONTINUE;
|
|
}
|
|
} t1;
|
|
depthFirstTraversal(t1);
|
|
#endif
|
|
}
|
|
|
|
uint64_t
|
|
Node::nNodesUnique() {
|
|
std::vector<Ptr> exprs(1, sharedFromThis());
|
|
return SymbolicExpr::nNodesUnique(exprs.begin(), exprs.end());
|
|
}
|
|
|
|
std::vector<Ptr>
|
|
Node::findCommonSubexpressions() {
|
|
return SymbolicExpr::findCommonSubexpressions(std::vector<Ptr>(1, sharedFromThis()));
|
|
}
|
|
|
|
void
|
|
Node::printFlags(std::ostream &o, unsigned flags, char &bracket) {
|
|
if ((flags & INDETERMINATE) != 0) {
|
|
o <<bracket <<"indet";
|
|
bracket = ',';
|
|
flags &= ~INDETERMINATE;
|
|
}
|
|
if ((flags & UNSPECIFIED) != 0) {
|
|
o <<bracket <<"unspec";
|
|
bracket = ',';
|
|
flags &= ~UNSPECIFIED;
|
|
}
|
|
if ((flags & BOTTOM) != 0) {
|
|
o <<bracket <<"bottom";
|
|
bracket = ',';
|
|
flags &= ~BOTTOM;
|
|
}
|
|
if (flags != 0) {
|
|
o <<bracket <<"f=" <<std::hex <<flags <<std::dec;
|
|
bracket = ',';
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Interior node
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Interior::Interior(size_t nbits, Operator op, const Ptr &a, const std::string &comment, unsigned flags)
|
|
: Node(comment), op_(op), nnodes_(1) {
|
|
addChild(a);
|
|
adjustWidth();
|
|
adjustBitFlags(flags);
|
|
if (nbits != 0 && nbits != nBits()) {
|
|
throw Exception("operator size mismatch (specified=" + StringUtility::numberToString(nbits) +
|
|
", actual=" + StringUtility::numberToString(nBits()) + ")");
|
|
}
|
|
}
|
|
|
|
Interior::Interior(size_t nbits, Operator op, const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags)
|
|
: Node(comment), op_(op), nnodes_(1) {
|
|
addChild(a);
|
|
addChild(b);
|
|
adjustWidth();
|
|
adjustBitFlags(flags);
|
|
if (nbits != 0 && nbits != nBits()) {
|
|
throw Exception("operator size mismatch (specified=" + StringUtility::numberToString(nbits) +
|
|
", actual=" + StringUtility::numberToString(nBits()) + ")");
|
|
}
|
|
}
|
|
|
|
Interior::Interior(size_t nbits, Operator op, const Ptr &a, const Ptr &b, const Ptr &c, const std::string &comment,
|
|
unsigned flags)
|
|
: Node(comment), op_(op), nnodes_(1) {
|
|
addChild(a);
|
|
addChild(b);
|
|
addChild(c);
|
|
adjustWidth();
|
|
adjustBitFlags(flags);
|
|
if (nbits != 0 && nbits != nBits()) {
|
|
throw Exception("operator size mismatch (specified=" + StringUtility::numberToString(nbits) +
|
|
", actual=" + StringUtility::numberToString(nBits()) + ")");
|
|
}
|
|
}
|
|
|
|
Interior::Interior(size_t nbits, Operator op, const Nodes &children, const std::string &comment, unsigned flags)
|
|
: Node(comment), op_(op), nnodes_(1) {
|
|
for (size_t i=0; i<children.size(); ++i)
|
|
addChild(children[i]);
|
|
adjustWidth();
|
|
adjustBitFlags(flags);
|
|
if (nbits != 0 && nbits != nBits()) {
|
|
throw Exception("operator size mismatch (specified=" + StringUtility::numberToString(nbits) +
|
|
", actual=" + StringUtility::numberToString(nBits()) + ")");
|
|
}
|
|
}
|
|
|
|
void
|
|
Interior::addChild(const Ptr &child)
|
|
{
|
|
ASSERT_not_null(child);
|
|
children_.push_back(child);
|
|
if (nnodes_ != MAX_NNODES) {
|
|
if (nnodes_ + child->nNodes() < nnodes_) {
|
|
nnodes_ = MAX_NNODES; // overflow
|
|
} else {
|
|
nnodes_ += child->nNodes();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Interior::adjustWidth() {
|
|
if (children_.empty())
|
|
throw Exception(toStr(op_) + " operator requires argument(s)");
|
|
switch (op_) {
|
|
case OP_ASR:
|
|
case OP_ROL:
|
|
case OP_ROR:
|
|
case OP_SHL0:
|
|
case OP_SHL1:
|
|
case OP_SHR0:
|
|
case OP_SHR1: {
|
|
if (nChildren() != 2)
|
|
throw Exception(toStr(op_) + " operator expects two arguments");
|
|
if (!child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's first argument (shift amount) must be scalar");
|
|
if (!child(1)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's second argument (value to shift) must be scalar");
|
|
nBits_ = child(1)->nBits();
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_CONCAT: {
|
|
size_t totalWidth = 0;
|
|
DYN_FOREACH (const Ptr &child, children_) {
|
|
if (!child->isScalar())
|
|
throw Exception(toStr(op_) + " operator's arguments must be scalar");
|
|
totalWidth += child->nBits();
|
|
}
|
|
nBits_ = totalWidth;
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_EQ:
|
|
case OP_NE:
|
|
case OP_SGE:
|
|
case OP_SGT:
|
|
case OP_SLE:
|
|
case OP_SLT:
|
|
case OP_UGE:
|
|
case OP_UGT:
|
|
case OP_ULE:
|
|
case OP_ULT: {
|
|
if (nChildren() != 2)
|
|
throw Exception(toStr(op_) + " operator expects two arguments");
|
|
if (child(0)->nBits() != child(1)->nBits())
|
|
throw Exception(toStr(op_) + " operator's arguments must both be the same width");
|
|
nBits_ = 1;
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_EXTRACT: {
|
|
if (nChildren() != 3)
|
|
throw Exception(toStr(op_) + " operator expects three arguments");
|
|
if (!child(0)->isNumber())
|
|
throw Exception(toStr(op_) + " operator's first argument (begin bit) must be an integer constant");
|
|
if (!child(1)->isNumber())
|
|
throw Exception(toStr(op_) + " operator's second argument (end bit) must be an integer constant");
|
|
if (!child(2)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's third argument must be scalar");
|
|
if (child(0)->toInt() >= child(1)->toInt())
|
|
throw Exception(toStr(op_) + " operator's first argument must be less than the second");
|
|
size_t totalSize = child(1)->toInt() - child(0)->toInt();
|
|
nBits_ = totalSize;
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_ITE: {
|
|
if (nChildren() != 3)
|
|
throw Exception(toStr(op_) + " operator expects three arguments");
|
|
if (!child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's first argument (condition) must be scalar");
|
|
if (child(0)->nBits() != 1)
|
|
throw Exception(toStr(op_) + " operator's first argument (condition) must be Boolean");
|
|
if (child(1)->nBits() != child(2)->nBits() || child(1)->domainWidth() != child(2)->domainWidth())
|
|
throw Exception(toStr(op_) + " operator's second and third arguments must have equal width");
|
|
nBits_ = child(1)->nBits();
|
|
domainWidth_ = child(1)->domainWidth();
|
|
break;
|
|
}
|
|
case OP_LSSB:
|
|
case OP_MSSB:
|
|
case OP_NEGATE: {
|
|
if (nChildren() != 1)
|
|
throw Exception(toStr(op_) + " operator expects one argument");
|
|
if (!child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " argument must be scalar");
|
|
nBits_ = child(0)->nBits();
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_READ: {
|
|
if (nChildren() != 2)
|
|
throw Exception(toStr(op_) + " operator expects two arguments");
|
|
if (child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's first argument must be a memory state");
|
|
if (!child(1)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's second argument (address) must be scalar");
|
|
if (child(0)->domainWidth() != child(1)->nBits())
|
|
throw Exception(toStr(op_) + " operator's arguments have mismatched sizes");
|
|
nBits_ = child(0)->nBits(); // size of values stored in memory
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_SDIV:
|
|
case OP_UDIV: {
|
|
if (nChildren() != 2)
|
|
throw Exception(toStr(op_) + " operator expects two arguments");
|
|
if (!child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's first argument must be scalar");
|
|
if (!child(1)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's second argument must be scalar");
|
|
nBits_ = child(0)->nBits();
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_SEXTEND:
|
|
case OP_UEXTEND: {
|
|
if (nChildren() != 2)
|
|
throw Exception(toStr(op_) + " operator expects two arguments");
|
|
if (!child(0)->isNumber())
|
|
throw Exception(toStr(op_) + " operator's first argument (size) must be a numeric constant");
|
|
if (!child(1)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's second argument must be scalar");
|
|
nBits_ = child(0)->toInt();
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_SMOD:
|
|
case OP_UMOD: {
|
|
if (nChildren() != 2)
|
|
throw Exception(toStr(op_) + " operator expects two arguments");
|
|
if (!child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's first argument must be scalar");
|
|
if (!child(1)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's second argument must be scalar");
|
|
nBits_ = child(1)->nBits();
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_SMUL:
|
|
case OP_UMUL: {
|
|
if (nChildren() < 1)
|
|
throw Exception(toStr(op_) + " operator expects at least one argument");
|
|
size_t totalWidth = 0;
|
|
for (size_t i=0; i<nChildren(); ++i) {
|
|
if (!child(i)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's arguments must all be scalar");
|
|
totalWidth += child(i)->nBits();
|
|
}
|
|
nBits_ = totalWidth;
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
case OP_WRITE: {
|
|
if (nChildren() != 3)
|
|
throw Exception(toStr(op_) + " operator expects three arguments");
|
|
if (child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's first operand must be a memory state");
|
|
if (!child(1)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's second argument (address) must be scalar");
|
|
if (!child(2)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's third argument (value to write) must be scalar");
|
|
if (child(1)->nBits() != child(0)->domainWidth())
|
|
throw Exception(toStr(op_) + " operator's second argument (address) has incorrect width");
|
|
if (child(2)->nBits() != child(0)->nBits())
|
|
throw Exception(toStr(op_) + " operator's third argument (value to write) has incorrect width");
|
|
nBits_ = child(0)->nBits();
|
|
domainWidth_ = child(0)->domainWidth();
|
|
break;
|
|
}
|
|
case OP_ZEROP: {
|
|
if (nChildren() != 1)
|
|
throw Exception(toStr(op_) + " operator expects one argument");
|
|
if (!child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's argument must be scalar");
|
|
nBits_ = 1;
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
default: {
|
|
// All children must have the same width, which is the width of this expression. This is suitable for things like
|
|
// bitwise operators, add, etc.
|
|
if (!child(0)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's arguments must all be scalar");
|
|
for (size_t i=1; i<nChildren(); ++i) {
|
|
if (!child(i)->isScalar())
|
|
throw Exception(toStr(op_) + " operator's arguments must all be scalar");
|
|
if (child(i)->nBits() != child(0)->nBits())
|
|
throw Exception(toStr(op_) + " operator's arguments must all have the same width");
|
|
}
|
|
nBits_ = child(0)->nBits();
|
|
domainWidth_ = 0;
|
|
break;
|
|
}
|
|
}
|
|
ASSERT_require(nBits_ != 0);
|
|
}
|
|
|
|
void
|
|
Interior::adjustBitFlags(unsigned flags) {
|
|
flags_ = flags;
|
|
DYN_FOREACH (const Ptr &child, children_)
|
|
flags_ |= child->flags();
|
|
}
|
|
|
|
void
|
|
Interior::print(std::ostream &o, Formatter &fmt) {
|
|
struct FormatGuard {
|
|
Formatter &fmt;
|
|
FormatGuard(Formatter &fmt): fmt(fmt) {
|
|
++fmt.cur_depth;
|
|
}
|
|
~FormatGuard() {
|
|
--fmt.cur_depth;
|
|
}
|
|
} formatGuard(fmt);
|
|
|
|
o <<"(" <<toStr(op_);
|
|
|
|
// The width of an operator is not normally too useful since it can also be inferred from the width of its operands, but we
|
|
// print it anyway for the benefit of mere humans.
|
|
char bracket = '[';
|
|
if (fmt.show_width) {
|
|
o <<bracket <<nBits_;
|
|
bracket = ',';
|
|
}
|
|
if (fmt.show_flags)
|
|
printFlags(o, flags(), bracket /*in,out*/);
|
|
if (fmt.show_comments!=Formatter::CMT_SILENT && !comment_.empty()) {
|
|
o <<bracket <<comment_;
|
|
bracket = ',';
|
|
}
|
|
if (bracket != '[')
|
|
o <<"]";
|
|
|
|
// Print the operand list.
|
|
if (fmt.max_depth!=0 && fmt.cur_depth>=fmt.max_depth && 0!=nChildren()) {
|
|
o <<" ...";
|
|
} else {
|
|
for (size_t i=0; i<children_.size(); i++) {
|
|
bool printed = false;
|
|
LeafPtr child_leaf = children_[i]->isLeafNode();
|
|
o <<" ";
|
|
switch (op_) {
|
|
case OP_ASR:
|
|
case OP_ROL:
|
|
case OP_ROR:
|
|
case OP_UEXTEND:
|
|
if (0==i && child_leaf) {
|
|
child_leaf->printAsUnsigned(o, fmt);
|
|
printed = true;
|
|
}
|
|
break;
|
|
|
|
case OP_EXTRACT:
|
|
if ((0==i || 1==i) && child_leaf) {
|
|
child_leaf->printAsUnsigned(o, fmt);
|
|
printed = true;
|
|
}
|
|
break;
|
|
|
|
case OP_BV_AND:
|
|
case OP_BV_OR:
|
|
case OP_BV_XOR:
|
|
case OP_CONCAT:
|
|
case OP_UDIV:
|
|
case OP_UGE:
|
|
case OP_UGT:
|
|
case OP_ULE:
|
|
case OP_ULT:
|
|
case OP_UMOD:
|
|
case OP_UMUL:
|
|
if (child_leaf) {
|
|
child_leaf->printAsUnsigned(o, fmt);
|
|
printed = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!printed)
|
|
children_[i]->print(o, fmt);
|
|
}
|
|
}
|
|
o <<")";
|
|
|
|
if (!comment().empty())
|
|
o <<"<" <<commentEscape(comment()) <<">";
|
|
}
|
|
|
|
bool
|
|
Interior::mustEqual(const Ptr &other_, SMTSolver *solver/*NULL*/) {
|
|
bool retval = false;
|
|
if (this==getRawPointer(other_)) {
|
|
retval = true;
|
|
} else if (isEquivalentTo(other_)) {
|
|
// This is probably faster than using an SMT solver. It also serves as the naive approach when an SMT solver
|
|
// is not available.
|
|
retval = true;
|
|
} else if (solver) {
|
|
Ptr assertion = makeNe(sharedFromThis(), other_);
|
|
retval = SMTSolver::SAT_NO==solver->satisfiable(assertion); /*equal if there is no solution for inequality*/
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool
|
|
Interior::mayEqual(const Ptr &other, SMTSolver *solver/*NULL*/) {
|
|
bool retval = false;
|
|
if (this==getRawPointer(other)) {
|
|
return true;
|
|
} else if (isEquivalentTo(other)) {
|
|
// This is probably faster than using an SMT solver. It also serves as the naive approach when an SMT solver
|
|
// is not available.
|
|
retval = true;
|
|
} else if (solver) {
|
|
Ptr assertion = makeEq(sharedFromThis(), other);
|
|
retval = SMTSolver::SAT_YES==solver->satisfiable(assertion);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
Interior::compareStructure(const Ptr &other_) {
|
|
InteriorPtr other = other_->isInteriorNode();
|
|
if (this==getRawPointer(other)) {
|
|
return 0;
|
|
} else if (other==NULL) {
|
|
return 1; // leaf nodes < internal nodes
|
|
} else if (op_ != other->op_) {
|
|
return op_ < other->op_ ? -1 : 1;
|
|
} else if (nBits() != other->nBits()) {
|
|
return nBits() < other->nBits() ? -1 : 1;
|
|
} else if (children_.size() != other->children_.size()) {
|
|
return children_.size() < other->children_.size() ? -1 : 1;
|
|
} else if (flags() != other->flags()) {
|
|
return flags() < other->flags() ? -1 : 1;
|
|
} else {
|
|
// compare children
|
|
ASSERT_require(children_.size()==other->children_.size());
|
|
for (size_t i=0; i<children_.size(); ++i) {
|
|
if (int cmp = children_[i]->compareStructure(other->children_[i]))
|
|
return cmp;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
Interior::isEquivalentTo(const Ptr &other_) {
|
|
bool retval = false;
|
|
InteriorPtr other = other_->isInteriorNode();
|
|
if (this==getRawPointer(other)) {
|
|
retval = true;
|
|
} else if (other==NULL || nBits()!=other->nBits() || flags()!=other->flags()) {
|
|
retval = false;
|
|
} else if (hashval_!=0 && other->hashval_!=0 && hashval_!=other->hashval_) {
|
|
// Unequal hashvals imply non-equivalent expressions. The converse is not necessarily true due to possible
|
|
// collisions.
|
|
retval = false;
|
|
} else if (op_==other->op_ && children_.size()==other->children_.size()) {
|
|
retval = true;
|
|
for (size_t i=0; i<children_.size() && retval; ++i)
|
|
retval = children_[i]->isEquivalentTo(other->children_[i]);
|
|
// Cache hash values. There's no need to compute a hash value if we've determined that the two expressions are
|
|
// equivalent because it wouldn't save us any work--two equal hash values doesn't necessarily mean that two expressions
|
|
// are equivalent. However, if we already know one of the hash values then we can cache that hash value in the other
|
|
// expression too.
|
|
if (retval) {
|
|
if (hashval_!=0 && other->hashval_==0) {
|
|
other->hashval_ = hashval_;
|
|
} else if (hashval_==0 && other->hashval_!=0) {
|
|
hashval_ = other->hashval_;
|
|
} else {
|
|
ASSERT_require(hashval_==other->hashval_);
|
|
}
|
|
} else {
|
|
#ifdef InsnInstructionExpr_USE_HASHES
|
|
hashval_ = hash();
|
|
other->hashval_ = other->hash();
|
|
#endif
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
Ptr
|
|
Interior::substitute(const Ptr &from, const Ptr &to) {
|
|
ASSERT_require(from!=NULL && to!=NULL && from->nBits()==to->nBits());
|
|
if (isEquivalentTo(from))
|
|
return to;
|
|
bool substituted = false;
|
|
Nodes newnodes;
|
|
for (size_t i=0; i<children_.size(); ++i) {
|
|
if (children_[i]->isEquivalentTo(from)) {
|
|
newnodes.push_back(to);
|
|
substituted = true;
|
|
} else {
|
|
newnodes.push_back(children_[i]->substitute(from, to));
|
|
if (newnodes.back()!=children_[i])
|
|
substituted = true;
|
|
}
|
|
}
|
|
if (!substituted)
|
|
return sharedFromThis();
|
|
return Interior::create(0, getOperator(), newnodes, comment());
|
|
}
|
|
|
|
VisitAction
|
|
Interior::depthFirstTraversal(Visitor &v) {
|
|
Ptr self = sharedFromThis();
|
|
VisitAction action = v.preVisit(self);
|
|
if (CONTINUE==action) {
|
|
for (std::vector<Ptr>::const_iterator ci=children_.begin(); ci!=children_.end(); ++ci) {
|
|
action = (*ci)->depthFirstTraversal(v);
|
|
if (TERMINATE==action)
|
|
break;
|
|
}
|
|
}
|
|
if (TERMINATE!=action)
|
|
action = v.postVisit(self);
|
|
return action;
|
|
}
|
|
|
|
InteriorPtr
|
|
Interior::associative() {
|
|
Nodes newOperands;
|
|
std::list<Ptr> worklist(children_.begin(), children_.end());
|
|
bool modified = false;
|
|
while (!worklist.empty()) {
|
|
Ptr child = worklist.front();
|
|
worklist.pop_front();
|
|
InteriorPtr ichild = child->isInteriorNode();
|
|
if (ichild && ichild->op_ == op_) {
|
|
worklist.insert(worklist.begin(), ichild->children_.begin(), ichild->children_.end());
|
|
modified = true;
|
|
} else {
|
|
newOperands.push_back(child);
|
|
}
|
|
}
|
|
if (!modified)
|
|
return isInteriorNode();
|
|
|
|
// Return the new expression without simplifying it again.
|
|
return InteriorPtr(new Interior(nBits(), op_, newOperands, comment()));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Simplification
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// compare expressions for sorting operands of commutative operators. Returns -1, 0, 1
|
|
static int
|
|
expr_cmp(const Ptr &a, const Ptr &b)
|
|
{
|
|
ASSERT_not_null(a);
|
|
ASSERT_not_null(b);
|
|
InteriorPtr ai = a->isInteriorNode();
|
|
InteriorPtr bi = b->isInteriorNode();
|
|
LeafPtr al = a->isLeafNode();
|
|
LeafPtr bl = b->isLeafNode();
|
|
ASSERT_require((ai!=NULL) ^ (al!=NULL));
|
|
ASSERT_require((bi!=NULL) ^ (bl!=NULL));
|
|
|
|
if (a == b) {
|
|
return 0;
|
|
} else if ((ai==NULL) != (bi==NULL)) {
|
|
// internal nodes are less than leaf nodes
|
|
return ai!=NULL ? -1 : 1;
|
|
} else if (al!=NULL) {
|
|
// both are leaf nodes
|
|
ASSERT_not_null(bl);
|
|
if (al->isNumber() != bl->isNumber()) {
|
|
// constants are greater than variables
|
|
return al->isNumber() ? 1 : -1;
|
|
} else if (al->isNumber()) {
|
|
// both are constants, sort by unsigned value
|
|
ASSERT_require(bl->isNumber());
|
|
return al->bits().compare(bl->bits());
|
|
} else if (al->isVariable() != bl->isVariable()) {
|
|
// variables are less than memory
|
|
return al->isVariable() ? -1 : 1;
|
|
} else {
|
|
// both are variables or both are memory; sort by variable name
|
|
ASSERT_require((al->isVariable() && bl->isVariable()) || (al->isMemory() && bl->isMemory()));
|
|
if (al->nameId() != bl->nameId())
|
|
return al->nameId() < bl->nameId() ? -1 : 1;
|
|
return 0;
|
|
}
|
|
} else {
|
|
// both are internal nodes
|
|
ASSERT_not_null(ai);
|
|
ASSERT_not_null(bi);
|
|
if (ai->getOperator() != bi->getOperator())
|
|
return ai->getOperator() < bi->getOperator() ? -1 : 1;
|
|
for (size_t i=0; i<std::min(ai->nChildren(), bi->nChildren()); ++i) {
|
|
if (int cmp = expr_cmp(ai->child(i), bi->child(i)))
|
|
return cmp;
|
|
}
|
|
if (ai->nChildren() != bi->nChildren())
|
|
return ai->nChildren() < bi->nChildren() ? -1 : 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
commutative_order(const Ptr &a, const Ptr &b)
|
|
{
|
|
if (int cmp = expr_cmp(a, b))
|
|
return cmp<0;
|
|
return getRawPointer(a) < getRawPointer(b); // make it a strict ordering
|
|
}
|
|
|
|
InteriorPtr
|
|
Interior::commutative() {
|
|
const Nodes &orig_operands = children();
|
|
Nodes sorted_operands = orig_operands;
|
|
std::sort(sorted_operands.begin(), sorted_operands.end(), commutative_order);
|
|
if (std::equal(sorted_operands.begin(), sorted_operands.end(), orig_operands.begin()))
|
|
return isInteriorNode();
|
|
|
|
// construct the new node but don't simplify it yet (i.e., don't use Interior::create())
|
|
Interior *inode = new Interior(nBits(), getOperator(), sorted_operands, comment());
|
|
return InteriorPtr(inode);
|
|
}
|
|
|
|
Ptr
|
|
Interior::involutary() {
|
|
if (InteriorPtr inode = isInteriorNode()) {
|
|
if (1==inode->nChildren()) {
|
|
if (InteriorPtr sub1 = inode->child(0)->isInteriorNode()) {
|
|
if (sub1->getOperator() == inode->getOperator() && 1==sub1->nChildren()) {
|
|
return sub1->child(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return sharedFromThis();
|
|
}
|
|
|
|
// simplifies things like:
|
|
// (shift a (shift b x)) ==> (shift (add a b) x)
|
|
// making sure a and b are extended to the same width
|
|
Ptr
|
|
Interior::additiveNesting() {
|
|
InteriorPtr nested = child(1)->isInteriorNode();
|
|
if (nested!=NULL && nested->getOperator()==getOperator()) {
|
|
ASSERT_require(nested->nChildren()==nChildren());
|
|
ASSERT_require(nested->nBits()==nBits());
|
|
size_t additive_nbits = std::max(child(0)->nBits(), nested->child(0)->nBits());
|
|
|
|
// The two addends must be the same width, so zero-extend them if necessary (or should we sign extend?)
|
|
// Note that the first argument (new width) of the UEXTEND operator is not actually used.
|
|
Ptr a = child(0)->nBits()==additive_nbits ? child(0) :
|
|
makeExtend(makeInteger(8, additive_nbits), child(0));
|
|
Ptr b = nested->child(0)->nBits()==additive_nbits ? nested->child(0) :
|
|
makeExtend(makeInteger(8, additive_nbits), nested->child(0));
|
|
|
|
// construct the new node but don't simplify it yet (i.e., don't use Interior::create())
|
|
Interior *inode = new Interior(nBits(), getOperator(), makeAdd(a, b), nested->child(1), comment());
|
|
return InteriorPtr(inode);
|
|
}
|
|
return isInteriorNode();
|
|
}
|
|
|
|
Ptr
|
|
Interior::identity(uint64_t ident) {
|
|
Nodes args;
|
|
bool modified = false;
|
|
for (Nodes::const_iterator ci=children_.begin(); ci!=children_.end(); ++ci) {
|
|
LeafPtr leaf = (*ci)->isLeafNode();
|
|
if (leaf && leaf->isNumber()) {
|
|
Sawyer::Container::BitVector identBv = Sawyer::Container::BitVector(leaf->nBits()).fromInteger(ident);
|
|
if (0==leaf->bits().compare(identBv)) {
|
|
// skip this arg
|
|
modified = true;
|
|
} else {
|
|
args.push_back(*ci);
|
|
}
|
|
} else {
|
|
args.push_back(*ci);
|
|
}
|
|
}
|
|
if (!modified)
|
|
return sharedFromThis();
|
|
if (args.empty())
|
|
return makeInteger(nBits(), ident, comment());
|
|
if (1==args.size()) {
|
|
if (args.front()->nBits()!=nBits())
|
|
return makeExtend(makeInteger(8, nBits()), args.front());
|
|
return args.front();
|
|
}
|
|
|
|
// Don't simplify the return value recursively
|
|
Interior *inode = new Interior(0, getOperator(), args, comment());
|
|
if (inode->nBits() != nBits())
|
|
return sharedFromThis(); // don't simplify if width changed.
|
|
return InteriorPtr(inode);
|
|
}
|
|
|
|
Ptr
|
|
Interior::unaryNoOp() {
|
|
return 1==nChildren() ? child(0) : sharedFromThis();
|
|
}
|
|
|
|
Ptr
|
|
Interior::rewrite(const Simplifier &simplifier) {
|
|
if (Ptr simplified = simplifier.rewrite(this))
|
|
return simplified;
|
|
return sharedFromThis();
|
|
}
|
|
|
|
Ptr
|
|
Interior::foldConstants(const Simplifier &simplifier) {
|
|
Nodes newOperands;
|
|
bool modified = false;
|
|
Nodes::const_iterator ci1 = children_.begin();
|
|
while (ci1!=children_.end()) {
|
|
Nodes::const_iterator ci2 = ci1;
|
|
LeafPtr leaf;
|
|
while (ci2!=children_.end() && (leaf=(*ci2)->isLeafNode()) && leaf->isNumber()) ++ci2;
|
|
if (ci1==ci2 || ci1+1==ci2) { // arg is not a constant, or we had only one constant by itself
|
|
newOperands.push_back(*ci1);
|
|
++ci1;
|
|
} else if (Ptr folded = simplifier.fold(ci1, ci2)) { // able to fold all these constants into a new node
|
|
newOperands.push_back(folded);
|
|
modified = true;
|
|
ci1 = ci2;
|
|
} else { // multiple constants, but unable to fold
|
|
newOperands.insert(newOperands.end(), ci1, ci2);
|
|
ci1 = ci2;
|
|
}
|
|
}
|
|
if (!modified)
|
|
return isInteriorNode();
|
|
if (1==newOperands.size())
|
|
return newOperands.front();
|
|
|
|
// Do not simplify again (i.e., don't use Interior::create())
|
|
return InteriorPtr(new Interior(nBits(), op_, newOperands, comment()));
|
|
}
|
|
|
|
Ptr
|
|
AddSimplifier::fold(Nodes::const_iterator begin, Nodes::const_iterator end) const {
|
|
Sawyer::Container::BitVector accumulator((*begin)->nBits());
|
|
unsigned flags = 0;
|
|
for (/*void*/; begin!=end; ++begin) {
|
|
accumulator.add((*begin)->isLeafNode()->bits());
|
|
flags |= (*begin)->flags();
|
|
}
|
|
return makeConstant(accumulator, "", flags);
|
|
}
|
|
|
|
Ptr
|
|
AddSimplifier::rewrite(Interior *inode) const {
|
|
// A and B are duals if they have one of the following forms:
|
|
// (1) A = x AND B = (negate x)
|
|
// (2) A = x AND B = (invert x) [adjust constant]
|
|
// (3) A = (negate x) AND B = x
|
|
// (4) A = (invert x) AND B = x [adjust constant]
|
|
//
|
|
// This makes use of the relationship:
|
|
// (add (negate x) -1) == (invert x)
|
|
// by decrementing adjustment. The adjustment, whose width is the same as A and B, is allowed to overflow. For example,
|
|
// consider the expression, where all values are two bits wide:
|
|
// (add v1 (invert v1) v2 (invert v2) v3 (invert v3)) by substitution for invert:
|
|
// (add v1 (negate v1) -1 v2 (negate v2) -1 v3 (negate v3) -1) canceling duals gives:
|
|
// (add -1 -1 -1) rewriting as 2's complement (2 bits wide):
|
|
// (add 3 3 3) constant folding modulo 4:
|
|
// 1
|
|
// compare with v1=0, v2=1, v3=2 (i.e., -2 in two's complement):
|
|
// (add 0 3 1 2 2 1) == 1 mod 4
|
|
struct are_duals {
|
|
bool operator()(Ptr a, Ptr b, Sawyer::Container::BitVector &adjustment/*in,out*/) {
|
|
ASSERT_not_null(a);
|
|
ASSERT_not_null(b);
|
|
ASSERT_require(a->nBits()==b->nBits());
|
|
|
|
// swap A and B if necessary so we have form (1) or (2).
|
|
if (b->isInteriorNode()==NULL)
|
|
std::swap(a, b);
|
|
if (b->isInteriorNode()==NULL)
|
|
return false;
|
|
|
|
InteriorPtr bi = b->isInteriorNode();
|
|
if (bi->getOperator()==OP_NEGATE) {
|
|
// form (3)
|
|
ASSERT_require(1==bi->nChildren());
|
|
return a->isEquivalentTo(bi->child(0));
|
|
} else if (bi->getOperator()==OP_INVERT) {
|
|
// form (4) and ninverts is small enough
|
|
if (a->isEquivalentTo(bi->child(0))) {
|
|
adjustment.decrement();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Rewrite (add ... (negate (add a b)) ...) => (add ... (negate a) (negate b) ...)
|
|
struct distributeNegations {
|
|
Ptr operator()(Interior *add) {
|
|
Nodes children;
|
|
bool distributed = false;
|
|
for (size_t i=0; i<add->nChildren(); ++i) {
|
|
bool pushed = false;
|
|
InteriorPtr addArg = add->child(i)->isInteriorNode();
|
|
if (addArg && addArg->getOperator()==OP_NEGATE && addArg->nChildren()==1) {
|
|
if (InteriorPtr negateArg = addArg->child(0)->isInteriorNode()) {
|
|
if (negateArg && negateArg->getOperator()==OP_ADD && negateArg->nChildren()>0) {
|
|
for (size_t j=0; j<negateArg->nChildren(); ++j)
|
|
children.push_back(makeNegate(negateArg->child(j)));
|
|
pushed = distributed = true;
|
|
}
|
|
}
|
|
}
|
|
if (!pushed)
|
|
children.push_back(add->child(i));
|
|
}
|
|
if (!distributed)
|
|
return Ptr();
|
|
return Interior::create(0, OP_ADD, children, add->comment());
|
|
}
|
|
};
|
|
|
|
// Arguments that are negated cancel out similar arguments that are not negated
|
|
bool had_duals = false;
|
|
Sawyer::Container::BitVector adjustment(inode->nBits());
|
|
Nodes children = inode->children();
|
|
for (size_t i=0; i<children.size(); ++i) {
|
|
if (children[i]!=NULL) {
|
|
for (size_t j=i+1; j<children.size() && children[j]!=NULL; ++j) {
|
|
if (children[j]!=NULL && are_duals()(children[i], children[j], adjustment/*in,out*/)) {
|
|
children[i] = Sawyer::Nothing();
|
|
children[j] = Sawyer::Nothing();
|
|
had_duals = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Otherwise distribute negations across adds:
|
|
// (add ... (negate (add a b)) ...) => (add ... (negate a) (negate b) ...)
|
|
if (!had_duals) {
|
|
if (Ptr distributed = distributeNegations()(inode))
|
|
return distributed;
|
|
return Ptr();
|
|
}
|
|
|
|
// Build the new expression
|
|
children.erase(std::remove(children.begin(), children.end(), Ptr()), children.end());
|
|
if (!adjustment.isEqualToZero())
|
|
children.push_back(makeConstant(adjustment));
|
|
if (children.empty())
|
|
return makeInteger(inode->nBits(), 0, inode->comment());
|
|
if (children.size()==1)
|
|
return children[0];
|
|
return Interior::create(0, OP_ADD, children, inode->comment());
|
|
}
|
|
|
|
Ptr
|
|
AndSimplifier::fold(Nodes::const_iterator begin, Nodes::const_iterator end) const {
|
|
Sawyer::Container::BitVector accumulator((*begin)->nBits(), true);
|
|
unsigned flags = 0;
|
|
for (/*void*/; begin!=end; ++begin) {
|
|
accumulator.bitwiseAnd((*begin)->isLeafNode()->bits());
|
|
flags |= (*begin)->flags();
|
|
}
|
|
return makeConstant(accumulator, "", flags);
|
|
}
|
|
|
|
Ptr
|
|
AndSimplifier::rewrite(Interior *inode) const {
|
|
// Result is zero if any argument is zero
|
|
for (size_t i=0; i<inode->nChildren(); ++i) {
|
|
LeafPtr child = inode->child(i)->isLeafNode();
|
|
if (child && child->isNumber() && child->bits().isEqualToZero())
|
|
return makeInteger(inode->nBits(), 0, inode->comment(), child->flags());
|
|
}
|
|
|
|
// (and X X) => X (for any number of arguments that are all the same)
|
|
bool allSameArgs = true;
|
|
for (size_t i=1; i<inode->nChildren() && allSameArgs; ++i) {
|
|
if (!inode->child(0)->isEquivalentTo(inode->child(i)))
|
|
allSameArgs = false;
|
|
}
|
|
if (allSameArgs)
|
|
return inode->child(0);
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
OrSimplifier::fold(Nodes::const_iterator begin, Nodes::const_iterator end) const {
|
|
Sawyer::Container::BitVector accumulator((*begin)->nBits());
|
|
unsigned flags = 0;
|
|
for (/*void*/; begin!=end; ++begin) {
|
|
accumulator.bitwiseOr((*begin)->isLeafNode()->bits());
|
|
flags |= (*begin)->flags();
|
|
}
|
|
return makeConstant(accumulator, "", flags);
|
|
}
|
|
|
|
Ptr
|
|
OrSimplifier::rewrite(Interior *inode) const {
|
|
// Result has all bits set if any argument has all bits set
|
|
for (size_t i=0; i<inode->nChildren(); ++i) {
|
|
LeafPtr child = inode->child(i)->isLeafNode();
|
|
if (child && child->isNumber() && child->bits().isAllSet())
|
|
return makeConstant(child->bits(), inode->comment(), child->flags());
|
|
}
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
XorSimplifier::fold(Nodes::const_iterator begin, Nodes::const_iterator end) const {
|
|
Sawyer::Container::BitVector accumulator((*begin)->nBits());
|
|
unsigned flags = 0;
|
|
for (++begin; begin!=end; ++begin) {
|
|
accumulator.bitwiseXor((*begin)->isLeafNode()->bits());
|
|
flags |= (*begin)->flags();
|
|
}
|
|
return makeConstant(accumulator, "", flags);
|
|
}
|
|
|
|
Ptr
|
|
XorSimplifier::rewrite(Interior *inode) const {
|
|
SMTSolver *solver = NULL; // FIXME
|
|
|
|
// If any pairs of arguments are equal, then they don't contribute to the final answer.
|
|
std::vector<bool> removed(inode->nChildren(), false);
|
|
bool modified = false;
|
|
for (size_t i=0; i<inode->nChildren(); ++i) {
|
|
if (removed[i])
|
|
continue;
|
|
for (size_t j=i+1; j<inode->nChildren(); ++j) {
|
|
if (!removed[j] && inode->child(i)->mustEqual(inode->child(j), solver)) {
|
|
removed[i] = removed[j] = modified = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!modified)
|
|
return Ptr();
|
|
Nodes newargs;
|
|
for (size_t i=0; i<inode->nChildren(); ++i) {
|
|
if (!removed[i])
|
|
newargs.push_back(inode->child(i));
|
|
}
|
|
if (newargs.empty())
|
|
return makeInteger(inode->nBits(), 0, inode->comment());
|
|
return Interior::create(0, inode->getOperator(), newargs, inode->comment());
|
|
}
|
|
|
|
Ptr
|
|
SmulSimplifier::fold(Nodes::const_iterator begin, Nodes::const_iterator end) const {
|
|
// FIXME[Robb P. Matzke 2014-05-05]: Constant folding is not currently possible when the operands are wider than 64 bits
|
|
// because Sawyer::Container::BitVector does not provide a multiplication method.
|
|
size_t totalWidth = 0;
|
|
int64_t product = 1;
|
|
unsigned flags = 0;
|
|
for (/*void*/; begin!=end; ++begin) {
|
|
size_t nbits = (*begin)->nBits();
|
|
totalWidth += nbits;
|
|
if (totalWidth > 8*sizeof(product))
|
|
return Ptr();
|
|
LeafPtr leaf = (*begin)->isLeafNode();
|
|
product *= (int64_t)leaf->toInt();
|
|
flags |= (*begin)->flags();
|
|
}
|
|
return makeInteger(totalWidth, product, "", flags);
|
|
}
|
|
|
|
Ptr
|
|
UmulSimplifier::fold(Nodes::const_iterator begin, Nodes::const_iterator end) const {
|
|
// FIXME[Robb P. Matzke 2014-05-05]: Constant folding is not currently possible when the operands are wider than 64 bits
|
|
// because Sawyer::Container::BitVector does not provide a multiplication method.
|
|
size_t totalWidth = 0;
|
|
uint64_t product = 1;
|
|
unsigned flags = 0;
|
|
for (/*void*/; begin!=end; ++begin) {
|
|
size_t nbits = (*begin)->nBits();
|
|
totalWidth += nbits;
|
|
if (totalWidth > 8*sizeof(product))
|
|
return Ptr();
|
|
LeafPtr leaf = (*begin)->isLeafNode();
|
|
product *= (uint64_t)leaf->toInt();
|
|
flags |= (*begin)->flags();
|
|
}
|
|
return makeInteger(totalWidth, product, "", flags);
|
|
}
|
|
|
|
Ptr
|
|
ConcatSimplifier::fold(Nodes::const_iterator begin, Nodes::const_iterator end) const {
|
|
// first arg is high-order bits. Although this is nice to look at, it makes the operation a bit more difficult.
|
|
size_t resultSize = 0;
|
|
for (Nodes::const_iterator ti=begin; ti!=end; ++ti)
|
|
resultSize += (*ti)->nBits();
|
|
Sawyer::Container::BitVector accumulator(resultSize);
|
|
|
|
// Copy bits into wherever they belong in the accumulator
|
|
unsigned flags = 0;
|
|
for (size_t sa=resultSize; begin!=end; ++begin) {
|
|
LeafPtr leaf = (*begin)->isLeafNode();
|
|
sa -= leaf->nBits();
|
|
typedef Sawyer::Container::BitVector::BitRange BitRange;
|
|
BitRange destination = BitRange::baseSize(sa, leaf->nBits());
|
|
accumulator.copy(destination, leaf->bits(), leaf->bits().hull());
|
|
flags |= (*begin)->flags();
|
|
}
|
|
return makeConstant(accumulator, "", flags);
|
|
}
|
|
|
|
Ptr
|
|
ConcatSimplifier::rewrite(Interior *inode) const {
|
|
SMTSolver *solver = NULL; // FIXME
|
|
|
|
// If all the concatenated expressions are extract expressions, all extracting bits from the same expression and
|
|
// in the correct order, then we can simplify this to that expression. For instance:
|
|
// (concat[32]
|
|
// (extract[8] 24[32] 32[32] v2[32])
|
|
// (extract[8] 16[32] 24[32] v2[32])
|
|
// (extract[8] 8[32] 16[32] v2[32])
|
|
// (extract[8] 0[32] 8[32] v2[32]))
|
|
// can be simplified to
|
|
// v2
|
|
Ptr retval;
|
|
size_t offset = 0;
|
|
for (size_t i=inode->nChildren(); i>0; --i) { // process args in little endian order
|
|
InteriorPtr extract = inode->child(i-1)->isInteriorNode();
|
|
if (!extract || OP_EXTRACT!=extract->getOperator())
|
|
break;
|
|
LeafPtr from_node = extract->child(0)->isLeafNode();
|
|
ASSERT_require(from_node->nBits() <= 8*sizeof offset);
|
|
if (!from_node || !from_node->isNumber() || from_node->toInt()!=offset ||
|
|
extract->child(2)->nBits()!=inode->nBits())
|
|
break;
|
|
if (inode->nChildren()==i) {
|
|
retval = extract->child(2);
|
|
} else if (!extract->child(2)->mustEqual(retval, solver)) {
|
|
break;
|
|
}
|
|
offset += extract->nBits();
|
|
}
|
|
if (offset==inode->nBits())
|
|
return retval;
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
ExtractSimplifier::rewrite(Interior *inode) const {
|
|
LeafPtr from_node = inode->child(0)->isLeafNode();
|
|
LeafPtr to_node = inode->child(1)->isLeafNode();
|
|
Ptr operand = inode->child(2);
|
|
ASSERT_require(!from_node->isNumber() || from_node->nBits() <= 8*sizeof(size_t));
|
|
ASSERT_require(!to_node->isNumber() || to_node->nBits() <= 8*sizeof(size_t));
|
|
size_t from = from_node && from_node->isNumber() ? from_node->toInt() : 0;
|
|
size_t to = to_node && to_node->isNumber() ? to_node->toInt() : 0;
|
|
|
|
// If limits are backward or extend beyond the operand size, don't simplify
|
|
if (from_node && to_node && from_node->isNumber() && to_node->isNumber() && (from>=to || to>operand->nBits()))
|
|
return Ptr();
|
|
|
|
// Constant folding
|
|
if (from_node && to_node && from_node->isNumber() && to_node->isNumber() &&
|
|
operand->isLeafNode() && operand->isLeafNode()->isNumber()) {
|
|
Sawyer::Container::BitVector result(to-from);
|
|
typedef Sawyer::Container::BitVector::BitRange BitRange;
|
|
BitRange source = BitRange::hull(from, to-1);
|
|
result.copy(result.hull(), operand->isLeafNode()->bits(), source);
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// Extracting the whole thing is a no-op
|
|
if (from_node && to_node && from_node->isNumber() && from==0 && to==operand->nBits())
|
|
return operand;
|
|
|
|
// Hoist concat operations to the outside of the extract
|
|
// If the operand is a concat operation then take only the parts we need. Some examples:
|
|
// (extract 0 24 (concat X[24] Y[8])) ==> (concat (extract 0 16 X) Y)
|
|
Nodes newChildren;
|
|
InteriorPtr ioperand = operand->isInteriorNode();
|
|
if (from_node && to_node && from_node->isNumber() && to_node->isNumber() &&
|
|
ioperand && OP_CONCAT==ioperand->getOperator()) {
|
|
size_t partAt = 0; // starting bit number in child
|
|
for (auto it = ioperand->children().rbegin(); it != ioperand->children().rend(); ++it) { // concatenated parts
|
|
const Ptr part = *it;
|
|
size_t partEnd = partAt + part->nBits();
|
|
if (partEnd <= from) {
|
|
// Part is entirely left of what we need
|
|
partAt = partEnd;
|
|
} else if (partAt >= to) {
|
|
// Part is entirely right of what we need
|
|
break;
|
|
} else if (partAt < from && partEnd > to) {
|
|
// We need the middle of this part, and then we're done
|
|
size_t need = to-from; // number of bits we need
|
|
newChildren.push_back(makeExtract(makeInteger(32, from-partAt), makeInteger(32, to-partAt), part));
|
|
partAt = partEnd;
|
|
from += need;
|
|
} else if (partAt < from) {
|
|
// We need the end of the part
|
|
ASSERT_require(partEnd <= to);
|
|
size_t need = partEnd - from;
|
|
newChildren.push_back(makeExtract(makeInteger(32, from-partAt), makeInteger(32, part->nBits()), part));
|
|
partAt = partEnd;
|
|
from += need;
|
|
} else if (partEnd > to) {
|
|
// We need the beginning of the part
|
|
ASSERT_require(partAt == from);
|
|
size_t need = to-from;
|
|
newChildren.push_back(makeExtract(makeInteger(32, 0), makeInteger(32, need), part));
|
|
break;
|
|
} else {
|
|
// We need the whole part
|
|
ASSERT_require(partAt >= from);
|
|
ASSERT_require(partEnd <= to);
|
|
newChildren.push_back(part);
|
|
partAt = from = partEnd;
|
|
}
|
|
}
|
|
|
|
// Concatenate all the parts.
|
|
if (newChildren.size() > 1) {
|
|
std::reverse(newChildren.begin(), newChildren.end());// high bits must be first
|
|
return Interior::create(0, OP_CONCAT, newChildren, inode->comment());
|
|
}
|
|
newChildren[0]->comment(inode->comment());
|
|
return newChildren[0];
|
|
}
|
|
|
|
// If the operand is another extract operation and we know all the limits then they can be replaced with a single extract.
|
|
if (from_node && to_node && from_node->isNumber() && to_node->isNumber() &&
|
|
ioperand && OP_EXTRACT==ioperand->getOperator()) {
|
|
LeafPtr from2_node = ioperand->child(0)->isLeafNode();
|
|
LeafPtr to2_node = ioperand->child(1)->isLeafNode();
|
|
if (from2_node && to2_node && from2_node->isNumber() && to2_node->isNumber()) {
|
|
size_t from2 = from2_node->toInt();
|
|
return makeExtract(makeInteger(32, from2+from), makeInteger(32, from2+to), ioperand->child(2), inode->comment());
|
|
}
|
|
}
|
|
|
|
// Simplifications for (extract 0 a (uextend b c))
|
|
if (from_node && to_node && from_node->isNumber() && 0==from && to_node->isNumber()) {
|
|
size_t a=to, b=operand->nBits();
|
|
// (extract[a] 0 a (uextend[b] b c[a])) => c when b>=a
|
|
if (ioperand && OP_UEXTEND==ioperand->getOperator() && b>=a && ioperand->child(1)->nBits()==a)
|
|
return ioperand->child(1);
|
|
}
|
|
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
AsrSimplifier::rewrite(Interior *inode) const {
|
|
ASSERT_require(2==inode->nChildren());
|
|
|
|
// Constant folding
|
|
LeafPtr shift_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr operand_leaf = inode->child(1)->isLeafNode();
|
|
if (shift_leaf!=NULL && operand_leaf!=NULL && shift_leaf->isNumber() && operand_leaf->isNumber()) {
|
|
size_t sa = shift_leaf->toInt();
|
|
Sawyer::Container::BitVector result = operand_leaf->bits();
|
|
result.shiftRightArithmetic(sa);
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
InvertSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr operand_node = inode->child(0)->isLeafNode();
|
|
if (operand_node==NULL || !operand_node->isNumber())
|
|
return Ptr();
|
|
Sawyer::Container::BitVector result = operand_node->bits();
|
|
result.invert();
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
Ptr
|
|
NegateSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr operand_node = inode->child(0)->isLeafNode();
|
|
if (operand_node==NULL || !operand_node->isNumber())
|
|
return Ptr();
|
|
Sawyer::Container::BitVector result = operand_node->bits();
|
|
result.negate();
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
Ptr
|
|
IteSimplifier::rewrite(Interior *inode) const {
|
|
// Is the condition known?
|
|
LeafPtr cond_node = inode->child(0)->isLeafNode();
|
|
if (cond_node!=NULL && cond_node->isNumber()) {
|
|
if (cond_node->nBits() != 1)
|
|
throw Exception(toStr(inode->getOperator()) + " operator's first argument (condition) should be one bit wide");
|
|
return cond_node->toInt() ? inode->child(1) : inode->child(2);
|
|
}
|
|
|
|
// Are both operands the same? Then the condition doesn't matter
|
|
if (inode->child(1)->isEquivalentTo(inode->child(2)))
|
|
return inode->child(1);
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
NoopSimplifier::rewrite(Interior *inode) const {
|
|
if (1==inode->nChildren())
|
|
return inode->child(0);
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
RolSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr sa_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr val_leaf = inode->child(1)->isLeafNode();
|
|
if (sa_leaf && val_leaf && sa_leaf->isNumber() && val_leaf->isNumber()) {
|
|
Sawyer::Container::BitVector result = val_leaf->bits();
|
|
result.rotateLeft(sa_leaf->toInt());
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// If the shift amount is known and is a multiple of the operand size, then this is a no-op
|
|
if (sa_leaf && sa_leaf->isNumber() && 0==sa_leaf->toInt() % inode->nBits())
|
|
return inode->child(1);
|
|
|
|
return Ptr();
|
|
}
|
|
Ptr
|
|
RorSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr sa_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr val_leaf = inode->child(1)->isLeafNode();
|
|
if (sa_leaf && val_leaf && sa_leaf->isNumber() && val_leaf->isNumber()) {
|
|
Sawyer::Container::BitVector result = val_leaf->bits();
|
|
result.rotateRight(sa_leaf->toInt());
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// If the shift amount is known and is a multiple of the operand size, then this is a no-op
|
|
if (sa_leaf && sa_leaf->isNumber() && 0==sa_leaf->toInt() % inode->nBits())
|
|
return inode->child(1);
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
UextendSimplifier::rewrite(Interior *inode) const {
|
|
// Noop case
|
|
size_t oldsize = inode->child(1)->nBits();
|
|
size_t newsize = inode->nBits();
|
|
if (oldsize==newsize)
|
|
return inode->child(1);
|
|
|
|
// Constant folding
|
|
LeafPtr val_leaf = inode->child(1)->isLeafNode();
|
|
if (val_leaf && val_leaf->isNumber()) {
|
|
Sawyer::Container::BitVector result = val_leaf->bits();
|
|
result.resize(newsize);
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// If the new size is smaller than the old size, use OP_EXTRACT instead.
|
|
if (newsize<oldsize) {
|
|
return makeExtract(makeInteger(32, 0), makeInteger(32, newsize), inode->child(1), inode->comment());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SextendSimplifier::rewrite(Interior *inode) const {
|
|
// Noop case
|
|
size_t oldsize = inode->child(1)->nBits();
|
|
size_t newsize = inode->nBits();
|
|
if (oldsize==newsize)
|
|
return inode->child(1);
|
|
|
|
// Constant folding
|
|
LeafPtr val_leaf = inode->child(1)->isLeafNode();
|
|
if (val_leaf && val_leaf->isNumber()) {
|
|
Sawyer::Container::BitVector result(inode->nBits());
|
|
result.signExtend(val_leaf->bits());
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// Downsizing should be represented as an extract operation
|
|
if (newsize < oldsize) {
|
|
return makeExtract(makeInteger(32, 0), makeInteger(32, newsize), inode->child(1), inode->comment());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
EqSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compare(b_leaf->bits());
|
|
return makeBoolean(0==cmp, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// (eq x x) => 1
|
|
if (inode->child(0)->mustEqual(inode->child(1), NULL))
|
|
return makeBoolean(true, inode->comment(), inode->flags());
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SgeSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compareSigned(b_leaf->bits());
|
|
return makeBoolean(cmp>=0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// (sge x x) => 1
|
|
if (inode->child(0)->mustEqual(inode->child(1), NULL))
|
|
return makeBoolean(true, inode->comment(), inode->flags());
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SgtSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compareSigned(b_leaf->bits());
|
|
return makeBoolean(cmp>0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SleSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compareSigned(b_leaf->bits());
|
|
return makeBoolean(cmp<=0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// (sle x x) => 1
|
|
if (inode->child(0)->mustEqual(inode->child(1), NULL))
|
|
return makeBoolean(true, inode->comment(), inode->flags());
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SltSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compareSigned(b_leaf->bits());
|
|
return makeBoolean(cmp<0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
UgeSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compare(b_leaf->bits());
|
|
return makeBoolean(cmp>=0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// (uge x x) => 1
|
|
if (inode->child(0)->mustEqual(inode->child(1), NULL))
|
|
return makeBoolean(true, inode->comment(), inode->flags());
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
UgtSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compare(b_leaf->bits());
|
|
return makeBoolean(cmp>0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
UleSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compare(b_leaf->bits());
|
|
return makeBoolean(cmp<=0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// (ule x x) => 1
|
|
if (inode->child(0)->mustEqual(inode->child(1), NULL))
|
|
return makeBoolean(true, inode->comment(), inode->flags());
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
UltSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber()) {
|
|
int cmp = a_leaf->bits().compare(b_leaf->bits());
|
|
return makeBoolean(cmp<0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
ZeropSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
if (a_leaf && a_leaf->isNumber())
|
|
return makeBoolean(a_leaf->bits().isEqualToZero(), inode->comment(), inode->flags());
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SdivSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber() && b_leaf->toInt()!=0) {
|
|
if (a_leaf->nBits() <= 64 && b_leaf->nBits() <= 64) {
|
|
int64_t a = IntegerOps::signExtend2(a_leaf->toInt(), a_leaf->nBits(), 8*sizeof(int8_t));
|
|
int64_t b = IntegerOps::signExtend2(b_leaf->toInt(), b_leaf->nBits(), 8*sizeof(int8_t));
|
|
return makeInteger(a_leaf->nBits(), a/b, inode->comment(), inode->flags());
|
|
} else {
|
|
// FIXME[Robb P. Matzke 2014-05-05]: not folding constants larger than 64 bits because Sawyer::Container::BitVector
|
|
// does not currently define division.
|
|
}
|
|
}
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SmodSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber() && b_leaf->toInt()!=0) {
|
|
if (a_leaf->nBits() <= 64 && b_leaf->nBits() <= 64) {
|
|
int64_t a = IntegerOps::signExtend2(a_leaf->toInt(), a_leaf->nBits(), 8*sizeof(int8_t));
|
|
int64_t b = IntegerOps::signExtend2(b_leaf->toInt(), b_leaf->nBits(), 8*sizeof(int8_t));
|
|
return makeInteger(b_leaf->nBits(), a%b, inode->comment(), inode->flags());
|
|
} else {
|
|
// FIXME[Robb P. Matzke 2014-05-05]: not folding constants larger than 64 bits because Sawyer::Container::BitVector
|
|
// does not currently define division.
|
|
}
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
UdivSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber() && b_leaf->toInt()!=0) {
|
|
if (a_leaf->nBits() <= 64 && b_leaf->nBits() <= 64) {
|
|
uint64_t a = a_leaf->toInt();
|
|
uint64_t b = b_leaf->toInt();
|
|
return makeInteger(a_leaf->nBits(), a/b, inode->comment(), inode->flags());
|
|
} else {
|
|
// FIXME[Robb P. Matzke 2014-05-05]: not folding constants larger than 64 bits because Sawyer::Container::BitVector
|
|
// does not currently define division.
|
|
}
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
UmodSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr b_leaf = inode->child(1)->isLeafNode();
|
|
if (a_leaf && b_leaf && a_leaf->isNumber() && b_leaf->isNumber() && b_leaf->toInt()!=0) {
|
|
if (a_leaf->nBits() <= 64 && b_leaf->nBits() <= 64) {
|
|
uint64_t a = a_leaf->toInt();
|
|
uint64_t b = b_leaf->toInt();
|
|
return makeInteger(b_leaf->nBits(), a%b, inode->comment(), inode->flags());
|
|
} else {
|
|
// FIXME[Robb P. Matzke 2014-05-05]: not folding constants larger than 64 bits because Sawyer::Container::BitVector
|
|
// does not currently define division.
|
|
}
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
ShiftSimplifier::combine_strengths(Ptr strength1, Ptr strength2, size_t value_width) const {
|
|
if (!strength1 || !strength2)
|
|
return Ptr();
|
|
|
|
// Calculate the width for the sum of the strengths. If the width of the value being shifted isn't a power of two then we
|
|
// need to avoid overflow in the sum, otherwise overflow doesn't matter. The sum should be wide enough to hold a shift
|
|
// amount that's the same as the width of the value, otherwise we wouldn't be able to distinguish between the case where
|
|
// modulo addition produced a shift amount that's large enough to decimate the value, as opposed to a shift count of zero
|
|
// which is a no-op.
|
|
size_t sum_width = std::max(strength1->nBits(), strength2->nBits());
|
|
if (IntegerOps::isPowerOfTwo(value_width)) {
|
|
sum_width = std::max(sum_width, IntegerOps::log2max(value_width)+1);
|
|
} else {
|
|
sum_width = std::max(sum_width+1, IntegerOps::log2max(value_width)+1);
|
|
}
|
|
if (sum_width > 64)
|
|
return Ptr();
|
|
|
|
// Zero-extend the strengths if they're not as wide as the sum. This is because the ADD operator requires that its
|
|
// operands are the same width, and the result will also be that width.
|
|
if (strength1->nBits() < sum_width)
|
|
strength1 = makeExtend(makeInteger(32, sum_width), strength1);
|
|
if (strength2->nBits() < sum_width)
|
|
strength2 = makeExtend(makeInteger(32, sum_width), strength2);
|
|
|
|
return makeAdd(strength1, strength2);
|
|
}
|
|
|
|
Ptr
|
|
ShlSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr sa_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr val_leaf = inode->child(1)->isLeafNode();
|
|
if (sa_leaf && val_leaf && sa_leaf->isNumber() && val_leaf->isNumber()) {
|
|
uint64_t sa = sa_leaf->toInt();
|
|
sa = std::min((uint64_t)inode->nBits(), sa);
|
|
Sawyer::Container::BitVector result = val_leaf->bits();
|
|
result.shiftLeft(sa, newbits);
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// If the shifted operand is itself a shift of the same kind, then simplify by combining the strengths:
|
|
// (shl AMT1 (shl AMT2 X)) ==> (shl (add AMT1 AMT2) X)
|
|
InteriorPtr val_inode = inode->child(1)->isInteriorNode();
|
|
if (val_inode && val_inode->getOperator()==inode->getOperator()) {
|
|
if (Ptr strength = combine_strengths(inode->child(0), val_inode->child(0), inode->child(1)->nBits())) {
|
|
return Interior::create(0, inode->getOperator(), strength, val_inode->child(1));
|
|
}
|
|
}
|
|
|
|
// If the shift amount is known to be at least as large as the value, then replace the value with a constant.
|
|
if (sa_leaf && sa_leaf->isNumber() && sa_leaf->toInt() >= inode->nBits()) {
|
|
Sawyer::Container::BitVector result(inode->nBits(), newbits);
|
|
return makeConstant(result, inode->comment());
|
|
}
|
|
|
|
// If the shift amount is zero then this is a no-op
|
|
if (sa_leaf && sa_leaf->isNumber() && sa_leaf->toInt()==0)
|
|
return inode->child(1);
|
|
|
|
// If the shift amount is a constant, then:
|
|
// (shl0[N] AMT X) ==> (concat (extract 0 N-AMT X)<hiBits> 0[AMT]<loBits>)
|
|
// (shl1[N] AMT X) ==> (concat (extract 0 N-AMT X)<hiBits> -1[AMT]<loBits>)
|
|
if (sa_leaf && sa_leaf->isNumber()) {
|
|
ASSERT_require(sa_leaf->toInt()>0 && sa_leaf->toInt()<inode->nBits());// handled above
|
|
size_t nHiBits = inode->nBits() - sa_leaf->toInt();
|
|
Ptr hiBits = makeExtract(makeInteger(32, 0), makeInteger(32, nHiBits), inode->child(1));
|
|
Ptr loBits = makeInteger(sa_leaf->toInt(), newbits?uint64_t(-1):uint64_t(0));
|
|
return makeConcat(hiBits, loBits);
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
ShrSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr sa_leaf = inode->child(0)->isLeafNode();
|
|
LeafPtr val_leaf = inode->child(1)->isLeafNode();
|
|
if (sa_leaf && val_leaf && sa_leaf->isNumber() && val_leaf->isNumber()) {
|
|
uint64_t sa = sa_leaf->toInt();
|
|
sa = std::min((uint64_t)inode->nBits(), sa);
|
|
Sawyer::Container::BitVector result = val_leaf->bits();
|
|
result.shiftRight(sa, newbits);
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// If the shifted operand is itself a shift of the same kind, then simplify by combining the strengths:
|
|
// (shr0 AMT1 (shr0 AMT2 X)) ==> (shr0 (add AMT1 AMT2) X)
|
|
InteriorPtr val_inode = inode->child(1)->isInteriorNode();
|
|
if (val_inode && val_inode->getOperator()==inode->getOperator()) {
|
|
if (Ptr strength = combine_strengths(inode->child(0), val_inode->child(0), inode->child(1)->nBits())) {
|
|
return Interior::create(0, inode->getOperator(), strength, val_inode->child(1));
|
|
}
|
|
}
|
|
|
|
// If the shift amount is known to be at least as large as the value, then replace the value with a constant.
|
|
if (sa_leaf && sa_leaf->isNumber() && sa_leaf->toInt() >= inode->nBits()) {
|
|
Sawyer::Container::BitVector result(inode->nBits(), newbits);
|
|
return makeConstant(result, inode->comment(), inode->flags());
|
|
}
|
|
|
|
// If the shift amount is zero then this is a no-op
|
|
if (sa_leaf && sa_leaf->isNumber() && sa_leaf->toInt()==0)
|
|
return inode->child(1);
|
|
|
|
// If the shift amount is a constant, then:
|
|
// (shr0[N] AMT X) ==> (concat 0[AMT] (extract AMT N X))
|
|
// (shr1[N] AMT X) ==> (concat -1[AMT] (extract AMT N X))
|
|
if (sa_leaf && sa_leaf->isNumber()) {
|
|
ASSERT_require(sa_leaf->toInt()>0 && sa_leaf->toInt()<inode->nBits());// handled above
|
|
Ptr loBits = makeExtract(makeInteger(32, sa_leaf->toInt()), makeInteger(32, inode->nBits()), inode->child(1));
|
|
Ptr hiBits = makeInteger(sa_leaf->toInt(), newbits?uint64_t(-1):uint64_t(0));
|
|
return makeConcat(hiBits, loBits);
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
LssbSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
if (a_leaf && a_leaf->isNumber()) {
|
|
if (Sawyer::Optional<size_t> idx = a_leaf->bits().leastSignificantSetBit())
|
|
return makeInteger(inode->nBits(), *idx, inode->comment());
|
|
return makeInteger(inode->nBits(), 0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
MssbSimplifier::rewrite(Interior *inode) const {
|
|
// Constant folding
|
|
LeafPtr a_leaf = inode->child(0)->isLeafNode();
|
|
if (a_leaf && a_leaf->isNumber()) {
|
|
if (Sawyer::Optional<size_t> idx = a_leaf->bits().mostSignificantSetBit())
|
|
return makeInteger(inode->nBits(), *idx, inode->comment());
|
|
return makeInteger(inode->nBits(), 0, inode->comment(), inode->flags());
|
|
}
|
|
|
|
return Ptr();
|
|
}
|
|
|
|
Ptr
|
|
SetSimplifier::rewrite(Interior *inode) const {
|
|
SMTSolver *solver = NULL; // FIXME[Robb Matzke 2015-11-03]
|
|
|
|
// (set x) => x
|
|
if (1 == inode->nChildren())
|
|
return inode->child(0);
|
|
|
|
// Remove duplicate arguments
|
|
bool removedDuplicate = false;
|
|
Nodes elements;
|
|
DYN_FOREACH (const Ptr &elmt1, inode->children()) {
|
|
bool isDuplicate = false;
|
|
DYN_FOREACH (const Ptr &elmt2, elements) {
|
|
if (elmt1->mustEqual(elmt2, solver)) {
|
|
isDuplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!isDuplicate) {
|
|
elements.push_back(elmt1);
|
|
} else {
|
|
removedDuplicate = true;
|
|
}
|
|
}
|
|
if (!removedDuplicate)
|
|
return Ptr();
|
|
if (1==elements.size())
|
|
return elements[0];
|
|
return Interior::create(0, inode->getOperator(), elements, inode->comment());
|
|
}
|
|
|
|
Ptr
|
|
Interior::simplifyTop() {
|
|
Ptr node = sharedFromThis();
|
|
while (InteriorPtr inode = node->isInteriorNode()) {
|
|
Ptr newnode = node;
|
|
switch (inode->getOperator()) {
|
|
case OP_ADD:
|
|
newnode = inode->rewrite(AddSimplifier());
|
|
if (newnode==node)
|
|
newnode = inode->associative()->commutative()->identity(0);
|
|
if (newnode==node)
|
|
newnode = inode->foldConstants(AddSimplifier());
|
|
break;
|
|
case OP_AND:
|
|
case OP_BV_AND:
|
|
newnode = inode->associative()->commutative()->identity((uint64_t)-1);
|
|
if (newnode==node)
|
|
newnode = inode->foldConstants(AndSimplifier());
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(AndSimplifier());
|
|
break;
|
|
case OP_ASR:
|
|
newnode = inode->additiveNesting();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(AsrSimplifier());
|
|
break;
|
|
case OP_BV_XOR:
|
|
newnode = inode->associative()->commutative()->foldConstants(XorSimplifier());
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(XorSimplifier());
|
|
break;
|
|
case OP_CONCAT:
|
|
newnode = inode->associative()->foldConstants(ConcatSimplifier());
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(ConcatSimplifier());
|
|
break;
|
|
case OP_EQ:
|
|
newnode = inode->commutative();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(EqSimplifier());
|
|
break;
|
|
case OP_EXTRACT:
|
|
newnode = inode->rewrite(ExtractSimplifier());
|
|
break;
|
|
case OP_INVERT:
|
|
newnode = inode->involutary();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(InvertSimplifier());
|
|
break;
|
|
case OP_ITE:
|
|
newnode = inode->rewrite(IteSimplifier());
|
|
break;
|
|
case OP_LSSB:
|
|
newnode = inode->rewrite(LssbSimplifier());
|
|
break;
|
|
case OP_MSSB:
|
|
newnode = inode->rewrite(MssbSimplifier());
|
|
break;
|
|
case OP_NE:
|
|
newnode = inode->commutative();
|
|
break;
|
|
case OP_NEGATE:
|
|
newnode = inode->involutary();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(NegateSimplifier());
|
|
break;
|
|
case OP_NOOP:
|
|
newnode = inode->rewrite(NoopSimplifier());
|
|
break;
|
|
case OP_OR:
|
|
case OP_BV_OR:
|
|
newnode = inode->associative()->commutative()->identity(0);
|
|
if (newnode==node)
|
|
newnode = inode->foldConstants(OrSimplifier());
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(OrSimplifier());
|
|
break;
|
|
case OP_READ:
|
|
// no simplifications
|
|
break;
|
|
case OP_ROL:
|
|
newnode = inode->rewrite(RolSimplifier());
|
|
break;
|
|
case OP_ROR:
|
|
newnode = inode->rewrite(RorSimplifier());
|
|
break;
|
|
case OP_SDIV:
|
|
newnode = inode->rewrite(SdivSimplifier());
|
|
break;
|
|
case OP_SET:
|
|
newnode = inode->associative()->commutative();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(SetSimplifier());
|
|
break;
|
|
case OP_SEXTEND:
|
|
newnode = inode->rewrite(SextendSimplifier());
|
|
break;
|
|
case OP_SGE:
|
|
newnode = inode->rewrite(SgeSimplifier());
|
|
break;
|
|
case OP_SGT:
|
|
newnode = inode->rewrite(SgtSimplifier());
|
|
break;
|
|
case OP_SHL0:
|
|
newnode = inode->additiveNesting();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(ShlSimplifier(false));
|
|
break;
|
|
case OP_SHL1:
|
|
newnode = inode->additiveNesting();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(ShlSimplifier(true));
|
|
break;
|
|
case OP_SHR0:
|
|
newnode = inode->additiveNesting();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(ShrSimplifier(false));
|
|
break;
|
|
case OP_SHR1:
|
|
newnode = inode->additiveNesting();
|
|
if (newnode==node)
|
|
newnode = inode->rewrite(ShrSimplifier(true));
|
|
break;
|
|
case OP_SLE:
|
|
newnode = inode->rewrite(SleSimplifier());
|
|
break;
|
|
case OP_SLT:
|
|
newnode = inode->rewrite(SltSimplifier());
|
|
break;
|
|
case OP_SMOD:
|
|
newnode = inode->rewrite(SmodSimplifier());
|
|
break;
|
|
case OP_SMUL:
|
|
newnode = inode->associative()->commutative()->foldConstants(SmulSimplifier());
|
|
break;
|
|
case OP_UDIV:
|
|
newnode = inode->rewrite(UdivSimplifier());
|
|
break;
|
|
case OP_UEXTEND:
|
|
newnode = inode->rewrite(UextendSimplifier());
|
|
break;
|
|
case OP_UGE:
|
|
newnode = inode->rewrite(UgeSimplifier());
|
|
break;
|
|
case OP_UGT:
|
|
newnode = inode->rewrite(UgtSimplifier());
|
|
break;
|
|
case OP_ULE:
|
|
newnode = inode->rewrite(UleSimplifier());
|
|
break;
|
|
case OP_ULT:
|
|
newnode = inode->rewrite(UltSimplifier());
|
|
break;
|
|
case OP_UMOD:
|
|
newnode = inode->rewrite(UmodSimplifier());
|
|
break;
|
|
case OP_UMUL:
|
|
newnode = inode->associative()->commutative()->identity(1);
|
|
if (newnode==node)
|
|
newnode = inode->foldConstants(UmulSimplifier());
|
|
break;
|
|
case OP_WRITE:
|
|
// no simplifications
|
|
break;
|
|
case OP_ZEROP:
|
|
newnode = inode->rewrite(ZeropSimplifier());
|
|
break;
|
|
}
|
|
if (newnode==node)
|
|
break;
|
|
node = newnode;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Leaf nodes
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// class method
|
|
LeafPtr
|
|
Leaf::createVariable(size_t nbits, const std::string &comment, unsigned flags) {
|
|
if (0 == nbits)
|
|
throw Exception("variables must have positive width");
|
|
Leaf *node = new Leaf(comment, flags);
|
|
node->nBits_ = nbits;
|
|
node->leafType_ = BITVECTOR;
|
|
node->name_ = nextNameCounter();
|
|
LeafPtr retval(node);
|
|
return retval;
|
|
}
|
|
|
|
// class method
|
|
LeafPtr
|
|
Leaf::createExistingVariable(size_t nbits, uint64_t id, const std::string &comment, unsigned flags) {
|
|
if (0 == nbits)
|
|
throw Exception("variables must have positive width");
|
|
Leaf *node = new Leaf(comment, flags);
|
|
node->nBits_ = nbits;
|
|
node->leafType_ = BITVECTOR;
|
|
node->name_ = nextNameCounter(id);
|
|
LeafPtr retval(node);
|
|
return retval;
|
|
}
|
|
|
|
// class method
|
|
LeafPtr
|
|
Leaf::createInteger(size_t nbits, uint64_t n, const std::string &comment, unsigned flags) {
|
|
if (0 == nbits)
|
|
throw Exception("integers must have positive width");
|
|
Leaf *node = new Leaf(comment, flags);
|
|
node->nBits_ = nbits;
|
|
node->leafType_ = CONSTANT;
|
|
node->bits_ = Sawyer::Container::BitVector(nbits).fromInteger(n);
|
|
LeafPtr retval(node);
|
|
return retval;
|
|
}
|
|
|
|
// class method
|
|
LeafPtr
|
|
Leaf::createConstant(const Sawyer::Container::BitVector &bits, const std::string &comment, unsigned flags) {
|
|
Leaf *node = new Leaf(comment, flags);
|
|
node->nBits_ = bits.size();
|
|
node->leafType_ = CONSTANT;
|
|
node->bits_ = bits;
|
|
LeafPtr retval(node);
|
|
return retval;
|
|
}
|
|
|
|
// class method
|
|
LeafPtr
|
|
Leaf::createMemory(size_t addressWidth, size_t valueWidth, const std::string &comment, unsigned flags) {
|
|
if (0 == addressWidth)
|
|
throw Exception("memory addresses must have positive width");
|
|
if (0 == valueWidth)
|
|
throw Exception("memory values must have positive width");
|
|
Leaf *node = new Leaf(comment, flags);
|
|
node->nBits_ = valueWidth;
|
|
node->domainWidth_ = addressWidth;
|
|
node->leafType_ = MEMORY;
|
|
node->name_ = nextNameCounter();
|
|
LeafPtr retval(node);
|
|
return retval;
|
|
}
|
|
|
|
// class method
|
|
LeafPtr
|
|
Leaf::createExistingMemory(size_t addressWidth, size_t valueWidth, uint64_t id, const std::string &comment, unsigned flags) {
|
|
if (0 == addressWidth)
|
|
throw Exception("memory addresses must have positive width");
|
|
if (0 == valueWidth)
|
|
throw Exception("memory values must have positive width");
|
|
Leaf *node = new Leaf(comment, flags);
|
|
node->nBits_ = valueWidth;
|
|
node->domainWidth_ = addressWidth;
|
|
node->leafType_ = MEMORY;
|
|
node->name_ = nextNameCounter(id);
|
|
LeafPtr retval(node);
|
|
return retval;
|
|
}
|
|
|
|
bool
|
|
Leaf::isNumber() {
|
|
return CONSTANT==leafType_;
|
|
}
|
|
|
|
uint64_t
|
|
Leaf::toInt() {
|
|
ASSERT_require(isNumber());
|
|
ASSERT_require(nBits() <= 64);
|
|
return bits_.toInteger();
|
|
}
|
|
|
|
const Sawyer::Container::BitVector&
|
|
Leaf::bits() {
|
|
ASSERT_require(isNumber());
|
|
return bits_;
|
|
}
|
|
|
|
bool
|
|
Leaf::isVariable() {
|
|
return BITVECTOR==leafType_;
|
|
}
|
|
|
|
bool
|
|
Leaf::isMemory() {
|
|
return MEMORY==leafType_;
|
|
}
|
|
|
|
uint64_t
|
|
Leaf::nameId() {
|
|
ASSERT_require(isVariable() || isMemory());
|
|
return name_;
|
|
}
|
|
|
|
std::string
|
|
Leaf::toString() {
|
|
if (isNumber())
|
|
return "0x" + bits().toHex();
|
|
if (isVariable())
|
|
return "v" + StringUtility::numberToString(nameId());
|
|
if (isMemory())
|
|
return "m" + StringUtility::numberToString(nameId());
|
|
ASSERT_not_reachable("invalid leaf type");
|
|
return "";
|
|
}
|
|
|
|
void
|
|
Leaf::print(std::ostream &o, Formatter &formatter) {
|
|
printAsSigned(o, formatter);
|
|
}
|
|
|
|
void
|
|
Leaf::printAsSigned(std::ostream &o, Formatter &formatter, bool as_signed) {
|
|
bool showed_comment = false;
|
|
if (isNumber()) {
|
|
if (bits_.size() == 1) {
|
|
// Boolean values
|
|
if (bits_.toInteger()) {
|
|
o <<"true";
|
|
} else {
|
|
o <<"false";
|
|
}
|
|
} else if (bits_.size() <= 64) {
|
|
// Integer values that are small enough to use the machine's native type.
|
|
uint64_t ival = bits_.toInteger();
|
|
if ((32==nBits_ || 64==nBits_) && 0!=(ival & 0xffff0000) && 0xffff0000!=(ival & 0xffff0000)) {
|
|
// The value is probably an address, so print it like one.
|
|
if (formatter.use_hexadecimal) {
|
|
o <<StringUtility::unsignedToHex2(ival, nBits_);
|
|
} else {
|
|
// The old behavior (which is enabled when formatter.use_hexadecimal is false) was to print only the
|
|
// hexadecimal format and not the decimal format, so we'll emulate that. [Robb P. Matzke 2013-12-26]
|
|
o <<StringUtility::addrToString(ival, nBits_);
|
|
}
|
|
} else if (as_signed) {
|
|
if (formatter.use_hexadecimal) {
|
|
o <<StringUtility::toHex2(ival, nBits_); // show as signed and unsigned
|
|
} else if (IntegerOps::signBit2(ival, nBits_)) {
|
|
o <<(int64_t)IntegerOps::signExtend2(ival, nBits_, 64);
|
|
} else {
|
|
o <<ival;
|
|
}
|
|
} else {
|
|
if (formatter.use_hexadecimal) {
|
|
o <<StringUtility::unsignedToHex2(ival, nBits_); // show only as unsigned
|
|
} else {
|
|
o <<ival;
|
|
}
|
|
}
|
|
} else {
|
|
// Integers that are too wide -- use bit vector support instead.
|
|
// FIXME[Robb P. Matzke 2014-05-05]: we should change StringUtility functions to handle BitVector arguments also.
|
|
o <<"0x" <<bits_.toHex();
|
|
}
|
|
} else if (formatter.show_comments==Formatter::CMT_INSTEAD && !comment_.empty()) {
|
|
// Use the comment as the variable name.
|
|
o <<nameEscape(comment_);
|
|
showed_comment = true;
|
|
} else {
|
|
// Show the variable name.
|
|
uint64_t renamed = name_;
|
|
if (formatter.do_rename) {
|
|
/*RenameMap::iterator found = formatter.renames.find(name_);
|
|
if (found==formatter.renames.end() && formatter.add_renames) {
|
|
renamed = formatter.renames.size();
|
|
formatter.renames.insert(std::make_pair(name_, renamed));
|
|
} else {
|
|
renamed = found->second;
|
|
}*/
|
|
}
|
|
switch (leafType_) {
|
|
case MEMORY:
|
|
o <<"m";
|
|
break;
|
|
case BITVECTOR:
|
|
o <<"v";
|
|
break;
|
|
case CONSTANT:
|
|
ASSERT_not_reachable("handled above");
|
|
}
|
|
o <<renamed;
|
|
}
|
|
|
|
// Bit width of variable. All variables have this otherwise there's no way for the parser to tell how wide a variable is
|
|
// when reading it back in.
|
|
if (formatter.show_width) {
|
|
o <<'[' <<nBits_ <<']';
|
|
}
|
|
|
|
// Comment stuff
|
|
char bracket='<';
|
|
if (formatter.show_flags)
|
|
printFlags(o, flags(), bracket /*in,out*/);
|
|
if (!showed_comment && formatter.show_comments!=Formatter::CMT_SILENT && !comment_.empty()) {
|
|
o <<bracket <<commentEscape(comment_);
|
|
bracket = ',';
|
|
}
|
|
if (bracket != '<')
|
|
o <<">";
|
|
}
|
|
|
|
bool
|
|
Leaf::mustEqual(const Ptr &other_, SMTSolver *solver) {
|
|
bool retval = false;
|
|
LeafPtr other = other_->isLeafNode();
|
|
if (this==getRawPointer(other)) {
|
|
retval = true;
|
|
} else if (flags() != other_->flags()) {
|
|
retval = false;
|
|
} else if (other==NULL) {
|
|
// We need an SMT solver to figure this out. This handles things like "x mustEqual (not (not x))" which is true.
|
|
if (solver) {
|
|
Ptr assertion = makeNe(sharedFromThis(), other_);
|
|
retval = SMTSolver::SAT_NO==solver->satisfiable(assertion); // must equal if there is no soln for inequality
|
|
}
|
|
} else if (isNumber()) {
|
|
retval = other->isNumber() && 0==bits_.compare(other->bits_);
|
|
} else {
|
|
retval = !other->isNumber() && name_==other->name_;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
bool
|
|
Leaf::mayEqual(const Ptr &other_, SMTSolver *solver) {
|
|
bool retval = false;
|
|
LeafPtr other = other_->isLeafNode();
|
|
if (this==getRawPointer(other)) {
|
|
retval = true;
|
|
} else if (other==NULL) {
|
|
// We need an SMT solver to figure out things like "x mayEqual (add y 1))", which is true.
|
|
if (solver) {
|
|
Ptr assertion = makeEq(sharedFromThis(), other_);
|
|
retval = SMTSolver::SAT_YES == solver->satisfiable(assertion);
|
|
}
|
|
} else if (!isNumber() || !other->isNumber() || 0==bits_.compare(other->bits_)) {
|
|
retval = true;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
Leaf::compareStructure(const Ptr &other_) {
|
|
LeafPtr other = other_->isLeafNode();
|
|
if (this==getRawPointer(other)) {
|
|
return 0;
|
|
} else if (other==NULL) {
|
|
return -1; // leaf nodes < internal nodes
|
|
} else if (nBits() != other->nBits()) {
|
|
return nBits() < other->nBits() ? -1 : 1;
|
|
} else if (flags() != other->flags()) {
|
|
return flags() < other->flags() ? -1 : 1;
|
|
} else if (isNumber() != other->isNumber()) {
|
|
return isNumber() ? -1 : 1; // concrete values < non-concrete
|
|
} else if (name_ != other->name_) {
|
|
return name_ < other->name_ ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
Leaf::isEquivalentTo(const Ptr &other_) {
|
|
bool retval = false;
|
|
LeafPtr other = other_->isLeafNode();
|
|
if (this==getRawPointer(other)) {
|
|
retval = true;
|
|
} else if (other && nBits()==other->nBits() && flags()==other->flags()) {
|
|
if (isNumber()) {
|
|
retval = other->isNumber() && 0==bits_.compare(other->bits_);
|
|
} else {
|
|
retval = !other->isNumber() && name_==other->name_;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
Ptr
|
|
Leaf::substitute(const Ptr &from, const Ptr &to) {
|
|
ASSERT_require(from!=NULL && to!=NULL && from->nBits()==to->nBits());
|
|
if (isEquivalentTo(from))
|
|
return to;
|
|
return sharedFromThis();
|
|
}
|
|
|
|
VisitAction
|
|
Leaf::depthFirstTraversal(Visitor &v) {
|
|
Ptr self = sharedFromThis();
|
|
VisitAction retval = v.preVisit(self);
|
|
if (TERMINATE!=retval)
|
|
retval = v.postVisit(self);
|
|
return retval;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Free functions of the API
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
std::ostream&
|
|
operator<<(std::ostream &o, Node &node) {
|
|
Formatter fmt;
|
|
node.print(o, fmt);
|
|
return o;
|
|
}
|
|
|
|
std::ostream&
|
|
operator<<(std::ostream &o, const Node::WithFormatter &w)
|
|
{
|
|
w.print(o);
|
|
return o;
|
|
}
|
|
|
|
std::vector<Ptr>
|
|
findCommonSubexpressions(const std::vector<Ptr> &exprs) {
|
|
return findCommonSubexpressions(exprs.begin(), exprs.end());
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Factory functions
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Ptr
|
|
makeVariable(size_t nbits, const std::string &comment, unsigned flags) {
|
|
return Leaf::createVariable(nbits, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeExistingVariable(size_t nbits, uint64_t id, const std::string &comment, unsigned flags) {
|
|
return Leaf::createExistingVariable(nbits, id, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeInteger(size_t nbits, uint64_t n, const std::string &comment, unsigned flags) {
|
|
return Leaf::createInteger(nbits, n, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeConstant(const Sawyer::Container::BitVector &bits, const std::string &comment, unsigned flags) {
|
|
return Leaf::createConstant(bits, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeBoolean(bool b, const std::string &comment, unsigned flags) {
|
|
return Leaf::createBoolean(b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeMemory(size_t addressWidth, size_t valueWidth, const std::string &comment, unsigned flags) {
|
|
return Leaf::createMemory(addressWidth, valueWidth, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeExistingMemory(size_t addressWidth, size_t valueWidth, uint64_t id, const std::string &comment, unsigned flags) {
|
|
return Leaf::createExistingMemory(addressWidth, valueWidth, id, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeAdd(const Ptr&a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ADD, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeBooleanAnd(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_AND, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeAsr(const Ptr &sa, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ASR, sa, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeAnd(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_BV_AND, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeOr(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_BV_OR, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeXor(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_BV_XOR, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeConcat(const Ptr &hi, const Ptr &lo, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_CONCAT, hi, lo, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeEq(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_EQ, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeExtract(const Ptr &begin, const Ptr &end, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_EXTRACT, begin, end, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeInvert(const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_INVERT, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeIte(const Ptr &cond, const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ITE, cond, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeLssb(const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_LSSB, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeMssb(const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_MSSB, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeNe(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_NE, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeNegate(const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_NEGATE, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeBooleanOr(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_OR, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeRead(const Ptr &mem, const Ptr &addr, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_READ, mem, addr, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeRol(const Ptr &sa, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ROL, sa, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeRor(const Ptr &sa, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ROR, sa, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSet(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SET, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSet(const Ptr &a, const Ptr &b, const Ptr &c, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SET, a, b, c, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignedDiv(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SDIV, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignExtend(const Ptr &newSize, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SEXTEND, newSize, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignedGe(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SGE, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignedGt(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SGT, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeShl0(const Ptr &sa, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SHL0, sa, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeShl1(const Ptr &sa, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SHL1, sa, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeShr0(const Ptr &sa, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SHR0, sa, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeShr1(const Ptr &sa, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SHR1, sa, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignedLe(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SLE, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignedLt(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SLT, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignedMod(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SMOD, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeSignedMul(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_SMUL, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeDiv(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_UDIV, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeExtend(const Ptr &newSize, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_UEXTEND, newSize, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeGe(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_UGE, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeGt(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_UGT, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeLe(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ULE, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeLt(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ULT, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeMod(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_UMOD, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeMul(const Ptr &a, const Ptr &b, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_UMUL, a, b, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeWrite(const Ptr &mem, const Ptr &addr, const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_WRITE, mem, addr, a, comment, flags);
|
|
}
|
|
|
|
Ptr
|
|
makeZerop(const Ptr &a, const std::string &comment, unsigned flags) {
|
|
return Interior::create(0, OP_ZEROP, a, comment, flags);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace
|
|
} // namespace
|