mirror of
https://github.com/Z3Prover/z3
synced 2025-09-04 09:07:40 +00:00
* Introduce X-macro-based trace tag definition - Created trace_tags.def to centralize TRACE tag definitions - Each tag includes a symbolic name and description - Set up enum class TraceTag for type-safe usage in TRACE macros * Add script to generate Markdown documentation from trace_tags.def - Python script parses trace_tags.def and outputs trace_tags.md * Refactor TRACE_NEW to prepend TraceTag and pass enum to is_trace_enabled * trace: improve trace tag handling system with hierarchical tagging - Introduce hierarchical tag-class structure: enabling a tag class activates all child tags - Unify TRACE, STRACE, SCTRACE, and CTRACE under enum TraceTag - Implement initial version of trace_tag.def using X(tag, tag_class, description) (class names and descriptions to be refined in a future update) * trace: replace all string-based TRACE tags with enum TraceTag - Migrated all TRACE, STRACE, SCTRACE, and CTRACE macros to use enum TraceTag values instead of raw string literals * trace : add cstring header * trace : Add Markdown documentation generation from trace_tags.def via mk_api_doc.py * trace : rename macro parameter 'class' to 'tag_class' and remove Unicode comment in trace_tags.h. * trace : Add TODO comment for future implementation of tag_class activation * trace : Disable code related to tag_class until implementation is ready (#7663).
693 lines
21 KiB
C++
693 lines
21 KiB
C++
/*++
|
|
Copyright (c) 2020 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
bv2int_translator
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2024-10-27
|
|
|
|
--*/
|
|
|
|
#include "ast/ast.h"
|
|
#include "ast/arith_decl_plugin.h"
|
|
#include "ast/bv_decl_plugin.h"
|
|
#include "ast/rewriter/bv2int_translator.h"
|
|
#include "ast/ast_ll_pp.h"
|
|
#include "ast/ast_pp.h"
|
|
|
|
bv2int_translator::bv2int_translator(ast_manager& m, bv2int_translator_trail& ctx) :
|
|
m(m),
|
|
ctx(ctx),
|
|
bv(m),
|
|
a(m),
|
|
m_translate(m),
|
|
m_args(m),
|
|
m_pinned(m),
|
|
m_vars(m),
|
|
m_preds(m)
|
|
{}
|
|
|
|
void bv2int_translator::reset(bool is_plugin) {
|
|
m_vars.reset();
|
|
m_preds.reset();
|
|
for (unsigned i = m_translate.size(); i-- > 0; )
|
|
m_translate[i] = nullptr;
|
|
m_is_plugin = is_plugin;
|
|
}
|
|
|
|
|
|
void bv2int_translator::set_translated(expr* e, expr* r) {
|
|
SASSERT(r);
|
|
SASSERT(!is_translated(e));
|
|
m_translate.setx(e->get_id(), r);
|
|
ctx.push_idx(set_vector_idx_trail(m_translate, e->get_id()));
|
|
}
|
|
|
|
void bv2int_translator::internalize_bv(app* e) {
|
|
ensure_translated(e);
|
|
if (m.is_bool(e)) {
|
|
m_preds.push_back(e);
|
|
ctx.push(push_back_vector(m_preds));
|
|
}
|
|
}
|
|
|
|
void bv2int_translator::ensure_translated(expr* e) {
|
|
if (m_translate.get(e->get_id(), nullptr))
|
|
return;
|
|
ptr_vector<expr> todo;
|
|
ast_fast_mark1 visited;
|
|
todo.push_back(e);
|
|
visited.mark(e);
|
|
for (unsigned i = 0; i < todo.size(); ++i) {
|
|
expr* e = todo[i];
|
|
if (!is_app(e))
|
|
continue;
|
|
app* a = to_app(e);
|
|
if (m.is_bool(e) && a->get_family_id() != bv.get_family_id())
|
|
continue;
|
|
for (auto arg : *a)
|
|
if (!visited.is_marked(arg) && !m_translate.get(arg->get_id(), nullptr)) {
|
|
visited.mark(arg);
|
|
todo.push_back(arg);
|
|
}
|
|
}
|
|
std::stable_sort(todo.begin(), todo.end(), [&](expr* a, expr* b) { return get_depth(a) < get_depth(b); });
|
|
for (expr* e : todo)
|
|
translate_expr(e);
|
|
}
|
|
|
|
rational bv2int_translator::bv_size(expr* bv_expr) {
|
|
return rational::power_of_two(bv.get_bv_size(bv_expr->get_sort()));
|
|
}
|
|
|
|
void bv2int_translator::translate_expr(expr* e) {
|
|
if (is_quantifier(e))
|
|
translate_quantifier(to_quantifier(e));
|
|
else if (is_var(e))
|
|
translate_var(to_var(e));
|
|
else {
|
|
app* ap = to_app(e);
|
|
if (m_is_plugin && ap->get_family_id() == basic_family_id && m.is_bool(ap)) {
|
|
set_translated(e, e);
|
|
return;
|
|
}
|
|
m_args.reset();
|
|
for (auto arg : *ap)
|
|
m_args.push_back(translated(arg));
|
|
|
|
if (ap->get_family_id() == basic_family_id)
|
|
translate_basic(ap);
|
|
else if (ap->get_family_id() == bv.get_family_id())
|
|
translate_bv(ap);
|
|
else
|
|
translate_app(ap);
|
|
}
|
|
}
|
|
|
|
void bv2int_translator::translate_quantifier(quantifier* q) {
|
|
if (m_is_plugin) {
|
|
set_translated(q, q);
|
|
return;
|
|
}
|
|
if (is_lambda(q))
|
|
throw default_exception("lambdas are not supported in intblaster");
|
|
expr* b = q->get_expr();
|
|
unsigned nd = q->get_num_decls();
|
|
ptr_vector<sort> sorts;
|
|
for (unsigned i = 0; i < nd; ++i) {
|
|
auto s = q->get_decl_sort(i);
|
|
if (bv.is_bv_sort(s)) {
|
|
NOT_IMPLEMENTED_YET();
|
|
sorts.push_back(a.mk_int());
|
|
}
|
|
else
|
|
sorts.push_back(s);
|
|
}
|
|
b = translated(b);
|
|
// TODO if sorts contain integer, then created bounds variables.
|
|
set_translated(q, m.update_quantifier(q, b));
|
|
}
|
|
|
|
void bv2int_translator::translate_var(var* v) {
|
|
if (bv.is_bv_sort(v->get_sort()))
|
|
set_translated(v, m.mk_var(v->get_idx(), a.mk_int()));
|
|
else
|
|
set_translated(v, v);
|
|
}
|
|
|
|
// Translate functions that are not built-in or bit-vectors.
|
|
// Base method uses fresh functions.
|
|
// Other method could use bv2int, int2bv axioms and coercions.
|
|
// f(args) = bv2int(f(int2bv(args'))
|
|
//
|
|
|
|
void bv2int_translator::translate_app(app* e) {
|
|
|
|
if (m_is_plugin && m.is_bool(e)) {
|
|
set_translated(e, e);
|
|
return;
|
|
}
|
|
|
|
bool has_bv_sort = bv.is_bv(e);
|
|
func_decl* f = e->get_decl();
|
|
|
|
for (unsigned i = 0; i < m_args.size(); ++i)
|
|
if (bv.is_bv(e->get_arg(i)))
|
|
m_args[i] = bv.mk_int2bv(bv.get_bv_size(e->get_arg(i)), m_args.get(i));
|
|
|
|
if (has_bv_sort)
|
|
m_vars.push_back(e);
|
|
if (m_is_plugin) {
|
|
expr* r = m.mk_app(f, m_args);
|
|
if (has_bv_sort) {
|
|
ctx.push(push_back_vector(m_vars));
|
|
r = bv.mk_ubv2int(r);
|
|
}
|
|
set_translated(e, r);
|
|
return;
|
|
}
|
|
else if (has_bv_sort) {
|
|
if (f->get_family_id() != null_family_id)
|
|
throw default_exception("conversion for interpreted functions is not supported by intblast solver");
|
|
func_decl* g = nullptr;
|
|
if (!m_new_funs.find(f, g)) {
|
|
g = m.mk_fresh_func_decl(e->get_decl()->get_name(), symbol("bv"), f->get_arity(), f->get_domain(), a.mk_int());
|
|
m_new_funs.insert(f, g);
|
|
}
|
|
f = g;
|
|
m_pinned.push_back(f);
|
|
}
|
|
set_translated(e, m.mk_app(f, m_args));
|
|
}
|
|
|
|
expr_ref bv2int_translator::mk_le(expr* x, expr* y) {
|
|
if (a.is_numeral(y))
|
|
return expr_ref(a.mk_le(x, y), m);
|
|
if (a.is_numeral(x))
|
|
return expr_ref(a.mk_ge(y, x), m);
|
|
return expr_ref(a.mk_le(a.mk_sub(x, y), a.mk_numeral(rational(0), x->get_sort())), m);
|
|
}
|
|
|
|
expr_ref bv2int_translator::mk_lt(expr* x, expr* y) {
|
|
return expr_ref(m.mk_not(mk_le(y, x)), m);
|
|
}
|
|
|
|
|
|
|
|
void bv2int_translator::translate_bv(app* e) {
|
|
|
|
auto bnot = [&](expr* e) {
|
|
return a.mk_sub(a.mk_int(-1), e);
|
|
};
|
|
|
|
auto band = [&](expr_ref_vector const& args) {
|
|
expr* r = arg(0);
|
|
for (unsigned i = 1; i < args.size(); ++i)
|
|
r = a.mk_band(bv.get_bv_size(e), r, arg(i));
|
|
return r;
|
|
};
|
|
|
|
auto rotate_left = [&](unsigned n) {
|
|
auto sz = bv.get_bv_size(e);
|
|
n = n % sz;
|
|
expr* r = arg(0);
|
|
if (n != 0 && sz != 1) {
|
|
// r[sz - n - 1 : 0] ++ r[sz - 1 : sz - n]
|
|
// r * 2^(sz - n) + (r div 2^n) mod 2^(sz - n)???
|
|
// r * A + (r div B) mod A
|
|
auto N = bv_size(e);
|
|
auto A = rational::power_of_two(sz - n);
|
|
auto B = rational::power_of_two(n);
|
|
auto hi = mul(r, a.mk_int(A));
|
|
auto lo = amod(e, a.mk_idiv(umod(e, 0), a.mk_int(B)), A);
|
|
r = add(hi, lo);
|
|
}
|
|
return r;
|
|
};
|
|
|
|
expr* bv_expr = e;
|
|
expr_ref r(m);
|
|
auto const& args = m_args;
|
|
switch (e->get_decl_kind()) {
|
|
case OP_BADD:
|
|
r = a.mk_add(args);
|
|
break;
|
|
case OP_BSUB:
|
|
r = a.mk_sub(args.size(), args.data());
|
|
break;
|
|
case OP_BMUL:
|
|
r = a.mk_mul(args);
|
|
break;
|
|
case OP_ULEQ:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_le(umod(bv_expr, 0), umod(bv_expr, 1));
|
|
break;
|
|
case OP_UGEQ:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_ge(umod(bv_expr, 0), umod(bv_expr, 1));
|
|
break;
|
|
case OP_ULT:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_lt(umod(bv_expr, 0), umod(bv_expr, 1));
|
|
break;
|
|
case OP_UGT:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_gt(umod(bv_expr, 0), umod(bv_expr, 1));
|
|
break;
|
|
case OP_SLEQ:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_le(smod(bv_expr, 0), smod(bv_expr, 1));
|
|
break;
|
|
case OP_SGEQ:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_ge(smod(bv_expr, 0), smod(bv_expr, 1));
|
|
break;
|
|
case OP_SLT:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_lt(smod(bv_expr, 0), smod(bv_expr, 1));
|
|
break;
|
|
case OP_SGT:
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_gt(smod(bv_expr, 0), smod(bv_expr, 1));
|
|
break;
|
|
case OP_BNEG:
|
|
r = a.mk_uminus(arg(0));
|
|
break;
|
|
case OP_CONCAT: {
|
|
unsigned sz = 0;
|
|
expr_ref new_arg(m);
|
|
for (unsigned i = args.size(); i-- > 0;) {
|
|
expr* old_arg = e->get_arg(i);
|
|
new_arg = umod(old_arg, i);
|
|
if (sz > 0) {
|
|
new_arg = mul(new_arg, a.mk_int(rational::power_of_two(sz)));
|
|
r = add(r, new_arg);
|
|
}
|
|
else
|
|
r = new_arg;
|
|
sz += bv.get_bv_size(old_arg->get_sort());
|
|
}
|
|
break;
|
|
}
|
|
case OP_EXTRACT: {
|
|
unsigned lo, hi;
|
|
expr* old_arg;
|
|
VERIFY(bv.is_extract(e, lo, hi, old_arg));
|
|
r = arg(0);
|
|
if (lo > 0)
|
|
r = a.mk_idiv(r, a.mk_int(rational::power_of_two(lo)));
|
|
break;
|
|
}
|
|
case OP_BV_NUM: {
|
|
rational val;
|
|
unsigned sz;
|
|
VERIFY(bv.is_numeral(e, val, sz));
|
|
r = a.mk_int(val);
|
|
break;
|
|
}
|
|
case OP_BUREM:
|
|
case OP_BUREM_I: {
|
|
expr* x = umod(e, 0), * y = umod(e, 1);
|
|
r = if_eq(y, 0, x, a.mk_mod(x, y));
|
|
break;
|
|
}
|
|
case OP_BUDIV:
|
|
case OP_BUDIV_I: {
|
|
expr* x = umod(e, 0), * y = umod(e, 1);
|
|
r = if_eq(y, 0, a.mk_int(-1), a.mk_idiv(x, y));
|
|
break;
|
|
}
|
|
case OP_BUMUL_NO_OVFL: {
|
|
bv_expr = e->get_arg(0);
|
|
r = mk_lt(mul(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(bv_size(bv_expr)));
|
|
break;
|
|
}
|
|
case OP_BSHL: {
|
|
if (!a.is_numeral(arg(0)) && !a.is_numeral(arg(1)))
|
|
r = a.mk_shl(bv.get_bv_size(e), arg(0), arg(1));
|
|
else {
|
|
expr* x = arg(0), * y = umod(e, 1);
|
|
r = a.mk_int(0);
|
|
for (unsigned i = 0; i < bv.get_bv_size(e); ++i)
|
|
r = if_eq(y, i, mul(x, a.mk_int(rational::power_of_two(i))), r);
|
|
}
|
|
break;
|
|
}
|
|
case OP_BNOT:
|
|
r = bnot(arg(0));
|
|
break;
|
|
case OP_BLSHR:
|
|
if (!a.is_numeral(arg(0)) && !a.is_numeral(arg(1)))
|
|
r = a.mk_lshr(bv.get_bv_size(e), arg(0), arg(1));
|
|
else {
|
|
expr* x = arg(0), * y = umod(e, 1);
|
|
r = a.mk_int(0);
|
|
IF_VERBOSE(4, verbose_stream() << "lshr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
|
for (unsigned i = 0; i < bv.get_bv_size(e); ++i)
|
|
r = if_eq(y, i, a.mk_idiv(x, a.mk_int(rational::power_of_two(i))), r);
|
|
}
|
|
break;
|
|
case OP_BASHR:
|
|
if (!a.is_numeral(arg(1)))
|
|
r = a.mk_ashr(bv.get_bv_size(e), arg(0), arg(1));
|
|
else {
|
|
|
|
//
|
|
// ashr(x, y)
|
|
// if y = k & x >= 0 -> x / 2^k
|
|
// if y = k & x < 0 -> (x / 2^k) - 2^{N-k}
|
|
//
|
|
unsigned sz = bv.get_bv_size(e);
|
|
rational N = bv_size(e);
|
|
expr* x = umod(e, 0), * y = umod(e, 1);
|
|
expr* signx = a.mk_ge(x, a.mk_int(N / 2));
|
|
r = m.mk_ite(signx, a.mk_int(-1), a.mk_int(0));
|
|
IF_VERBOSE(4, verbose_stream() << "ashr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
expr* d = a.mk_idiv(x, a.mk_int(rational::power_of_two(i)));
|
|
r = if_eq(y, i,
|
|
m.mk_ite(signx, add(d, a.mk_int(-rational::power_of_two(sz - i))), d),
|
|
r);
|
|
}
|
|
}
|
|
break;
|
|
case OP_BOR:
|
|
// p | q := (p + q) - band(p, q)
|
|
IF_VERBOSE(4, verbose_stream() << "bor " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
|
r = arg(0);
|
|
for (unsigned i = 1; i < args.size(); ++i)
|
|
r = a.mk_sub(add(r, arg(i)), a.mk_band(bv.get_bv_size(e), r, arg(i)));
|
|
break;
|
|
case OP_BNAND:
|
|
r = bnot(band(args));
|
|
break;
|
|
case OP_BAND:
|
|
IF_VERBOSE(4, verbose_stream() << "band " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
|
r = band(args);
|
|
break;
|
|
case OP_BXNOR:
|
|
case OP_BXOR: {
|
|
// p ^ q := (p + q) - 2*band(p, q);
|
|
unsigned sz = bv.get_bv_size(e);
|
|
IF_VERBOSE(4, verbose_stream() << "bxor " << bv.get_bv_size(e) << "\n");
|
|
r = arg(0);
|
|
for (unsigned i = 1; i < args.size(); ++i) {
|
|
expr* q = arg(i);
|
|
r = a.mk_sub(add(r, q), mul(a.mk_int(2), a.mk_band(sz, r, q)));
|
|
}
|
|
if (e->get_decl_kind() == OP_BXNOR)
|
|
r = bnot(r);
|
|
break;
|
|
}
|
|
case OP_ZERO_EXT:
|
|
bv_expr = e->get_arg(0);
|
|
r = umod(bv_expr, 0);
|
|
SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
|
|
break;
|
|
case OP_SIGN_EXT: {
|
|
bv_expr = e->get_arg(0);
|
|
r = umod(bv_expr, 0);
|
|
SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
|
|
unsigned arg_sz = bv.get_bv_size(bv_expr);
|
|
//unsigned sz = bv.get_bv_size(e);
|
|
// rational N = rational::power_of_two(sz);
|
|
rational M = rational::power_of_two(arg_sz);
|
|
expr* signbit = a.mk_ge(r, a.mk_int(M / 2));
|
|
r = m.mk_ite(signbit, a.mk_sub(r, a.mk_int(M)), r);
|
|
break;
|
|
}
|
|
case OP_INT2BV:
|
|
m_int2bv.push_back(e);
|
|
ctx.push(push_back_vector(m_int2bv));
|
|
r = arg(0);
|
|
break;
|
|
case OP_UBV2INT:
|
|
m_bv2int.push_back(e);
|
|
ctx.push(push_back_vector(m_bv2int));
|
|
r = umod(e->get_arg(0), 0);
|
|
break;
|
|
case OP_BCOMP:
|
|
bv_expr = e->get_arg(0);
|
|
r = m.mk_ite(m.mk_eq(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(1), a.mk_int(0));
|
|
break;
|
|
case OP_BSMOD_I:
|
|
case OP_BSMOD: {
|
|
expr* x = umod(e, 0), * y = umod(e, 1);
|
|
rational N = bv_size(e);
|
|
expr* signx = a.mk_ge(x, a.mk_int(N / 2));
|
|
expr* signy = a.mk_ge(y, a.mk_int(N / 2));
|
|
expr* u = a.mk_mod(x, y);
|
|
// u = 0 -> 0
|
|
// y = 0 -> x
|
|
// x < 0, y < 0 -> -u
|
|
// x < 0, y >= 0 -> y - u
|
|
// x >= 0, y < 0 -> y + u
|
|
// x >= 0, y >= 0 -> u
|
|
r = a.mk_uminus(u);
|
|
r = m.mk_ite(m.mk_and(m.mk_not(signx), signy), add(u, y), r);
|
|
r = m.mk_ite(m.mk_and(signx, m.mk_not(signy)), a.mk_sub(y, u), r);
|
|
r = m.mk_ite(m.mk_and(m.mk_not(signx), m.mk_not(signy)), u, r);
|
|
r = if_eq(u, 0, a.mk_int(0), r);
|
|
r = if_eq(y, 0, x, r);
|
|
break;
|
|
}
|
|
case OP_BSDIV_I:
|
|
case OP_BSDIV: {
|
|
// d = udiv(abs(x), abs(y))
|
|
// y = 0, x > 0 -> 1
|
|
// y = 0, x <= 0 -> -1
|
|
// x = 0, y != 0 -> 0
|
|
// x > 0, y < 0 -> -d
|
|
// x < 0, y > 0 -> -d
|
|
// x > 0, y > 0 -> d
|
|
// x < 0, y < 0 -> d
|
|
expr* x = umod(e, 0), * y = umod(e, 1);
|
|
rational N = bv_size(e);
|
|
expr* signx = a.mk_ge(x, a.mk_int(N / 2));
|
|
expr* signy = a.mk_ge(y, a.mk_int(N / 2));
|
|
x = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
|
|
y = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
|
|
expr* d = a.mk_idiv(x, y);
|
|
r = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d));
|
|
r = if_eq(y, 0, m.mk_ite(signx, a.mk_int(1), a.mk_int(-1)), r);
|
|
break;
|
|
}
|
|
case OP_BSREM_I:
|
|
case OP_BSREM: {
|
|
// y = 0 -> x
|
|
// else x - sdiv(x, y) * y
|
|
expr* x = umod(e, 0), * y = umod(e, 1);
|
|
rational N = bv_size(e);
|
|
expr* signx = a.mk_ge(x, a.mk_int(N / 2));
|
|
expr* signy = a.mk_ge(y, a.mk_int(N / 2));
|
|
expr* absx = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
|
|
expr* absy = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
|
|
expr* d = a.mk_idiv(absx, absy);
|
|
d = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d));
|
|
r = a.mk_sub(x, mul(d, y));
|
|
r = if_eq(y, 0, x, r);
|
|
break;
|
|
}
|
|
case OP_ROTATE_LEFT: {
|
|
auto n = e->get_parameter(0).get_int();
|
|
r = rotate_left(n);
|
|
break;
|
|
}
|
|
case OP_ROTATE_RIGHT: {
|
|
unsigned sz = bv.get_bv_size(e);
|
|
auto n = e->get_parameter(0).get_int();
|
|
r = rotate_left(sz - n);
|
|
break;
|
|
}
|
|
case OP_EXT_ROTATE_LEFT: {
|
|
unsigned sz = bv.get_bv_size(e);
|
|
expr* y = umod(e, 1);
|
|
r = a.mk_int(0);
|
|
for (unsigned i = 0; i < sz; ++i)
|
|
r = if_eq(y, i, rotate_left(i), r);
|
|
break;
|
|
}
|
|
case OP_EXT_ROTATE_RIGHT: {
|
|
unsigned sz = bv.get_bv_size(e);
|
|
expr* y = umod(e, 1);
|
|
r = a.mk_int(0);
|
|
for (unsigned i = 0; i < sz; ++i)
|
|
r = if_eq(y, i, rotate_left(sz - i), r);
|
|
break;
|
|
}
|
|
case OP_REPEAT: {
|
|
unsigned n = e->get_parameter(0).get_int();
|
|
expr* x = umod(e->get_arg(0), 0);
|
|
r = x;
|
|
rational N = bv_size(e->get_arg(0));
|
|
rational N0 = N;
|
|
for (unsigned i = 1; i < n; ++i)
|
|
r = add(mul(a.mk_int(N), x), r), N *= N0;
|
|
break;
|
|
}
|
|
case OP_BREDOR: {
|
|
r = umod(e->get_arg(0), 0);
|
|
r = m.mk_not(m.mk_eq(r, a.mk_int(0)));
|
|
break;
|
|
}
|
|
case OP_BREDAND: {
|
|
rational N = bv_size(e->get_arg(0));
|
|
r = umod(e->get_arg(0), 0);
|
|
r = m.mk_not(m.mk_eq(r, a.mk_int(N - 1)));
|
|
break;
|
|
}
|
|
default:
|
|
verbose_stream() << mk_pp(e, m) << "\n";
|
|
NOT_IMPLEMENTED_YET();
|
|
}
|
|
set_translated(e, r);
|
|
}
|
|
|
|
expr_ref bv2int_translator::if_eq(expr* n, unsigned k, expr* th, expr* el) {
|
|
rational r;
|
|
expr_ref _th(th, m), _el(el, m);
|
|
if (bv.is_numeral(n, r)) {
|
|
if (r == k)
|
|
return expr_ref(th, m);
|
|
else
|
|
return expr_ref(el, m);
|
|
}
|
|
return expr_ref(m.mk_ite(m.mk_eq(n, a.mk_int(k)), th, el), m);
|
|
}
|
|
|
|
void bv2int_translator::translate_basic(app* e) {
|
|
if (m.is_eq(e)) {
|
|
bool has_bv_arg = any_of(*e, [&](expr* arg) { return bv.is_bv(arg); });
|
|
if (has_bv_arg) {
|
|
expr* bv_expr = e->get_arg(0);
|
|
rational N = rational::power_of_two(bv.get_bv_size(bv_expr));
|
|
if (a.is_numeral(arg(0)) || a.is_numeral(arg(1)) ||
|
|
is_bounded(arg(0), N) || is_bounded(arg(1), N)) {
|
|
set_translated(e, m.mk_eq(umod(bv_expr, 0), umod(bv_expr, 1)));
|
|
}
|
|
else {
|
|
m_args[0] = a.mk_sub(arg(0), arg(1));
|
|
set_translated(e, m.mk_eq(umod(bv_expr, 0), a.mk_int(0)));
|
|
}
|
|
}
|
|
else
|
|
set_translated(e, m.mk_eq(arg(0), arg(1)));
|
|
}
|
|
else if (m.is_ite(e))
|
|
set_translated(e, m.mk_ite(arg(0), arg(1), arg(2)));
|
|
else if (m_is_plugin)
|
|
set_translated(e, e);
|
|
else
|
|
set_translated(e, m.mk_app(e->get_decl(), m_args));
|
|
}
|
|
|
|
bool bv2int_translator::is_bounded(expr* x, rational const& N) {
|
|
return any_of(m_vars, [&](expr* v) {
|
|
return is_translated(v) && translated(v) == x && bv_size(v) <= N;
|
|
});
|
|
}
|
|
|
|
bool bv2int_translator::is_non_negative(expr* bv_expr, expr* e) {
|
|
auto N = rational::power_of_two(bv.get_bv_size(bv_expr));
|
|
rational r;
|
|
if (a.is_numeral(e, r))
|
|
return r >= 0;
|
|
if (is_bounded(e, N))
|
|
return true;
|
|
expr* x = nullptr, * y = nullptr;
|
|
if (a.is_mul(e, x, y))
|
|
return is_non_negative(bv_expr, x) && is_non_negative(bv_expr, y);
|
|
if (a.is_add(e, x, y))
|
|
return is_non_negative(bv_expr, x) && is_non_negative(bv_expr, y);
|
|
return false;
|
|
}
|
|
|
|
expr* bv2int_translator::umod(expr* bv_expr, unsigned i) {
|
|
expr* x = arg(i);
|
|
rational N = bv_size(bv_expr);
|
|
return amod(bv_expr, x, N);
|
|
}
|
|
|
|
expr* bv2int_translator::smod(expr* bv_expr, unsigned i) {
|
|
expr* x = arg(i);
|
|
auto N = bv_size(bv_expr);
|
|
auto shift = N / 2;
|
|
rational r;
|
|
if (a.is_numeral(x, r))
|
|
return a.mk_int(mod(r + shift, N));
|
|
return amod(bv_expr, add(x, a.mk_int(shift)), N);
|
|
}
|
|
|
|
expr_ref bv2int_translator::mul(expr* x, expr* y) {
|
|
expr_ref _x(x, m), _y(y, m);
|
|
if (a.is_zero(x))
|
|
return _x;
|
|
if (a.is_zero(y))
|
|
return _y;
|
|
if (a.is_one(x))
|
|
return _y;
|
|
if (a.is_one(y))
|
|
return _x;
|
|
rational v1, v2;
|
|
if (a.is_numeral(x, v1) && a.is_numeral(y, v2))
|
|
return expr_ref(a.mk_int(v1 * v2), m);
|
|
_x = a.mk_mul(x, y);
|
|
return _x;
|
|
}
|
|
|
|
expr_ref bv2int_translator::add(expr* x, expr* y) {
|
|
expr_ref _x(x, m), _y(y, m);
|
|
if (a.is_zero(x))
|
|
return _y;
|
|
if (a.is_zero(y))
|
|
return _x;
|
|
rational v1, v2;
|
|
if (a.is_numeral(x, v1) && a.is_numeral(y, v2))
|
|
return expr_ref(a.mk_int(v1 + v2), m);
|
|
_x = a.mk_add(x, y);
|
|
return _x;
|
|
}
|
|
|
|
/*
|
|
* Perform simplifications that are claimed sound when the bit-vector interpretations of
|
|
* mod/div always guard the mod and dividend to be non-zero.
|
|
* Potentially shady area is for arithmetic expressions created by int2bv.
|
|
* They will be guarded by a modulus which does not disappear.
|
|
*/
|
|
expr* bv2int_translator::amod(expr* bv_expr, expr* x, rational const& N) {
|
|
rational v;
|
|
expr* r = nullptr, * c = nullptr, * t = nullptr, * e = nullptr;
|
|
if (m.is_ite(x, c, t, e))
|
|
r = m.mk_ite(c, amod(bv_expr, t, N), amod(bv_expr, e, N));
|
|
else if (a.is_idiv(x, t, e) && a.is_numeral(t, v) && 0 <= v && v < N && is_non_negative(bv_expr, e))
|
|
r = x;
|
|
else if (a.is_mod(x, t, e) && a.is_numeral(t, v) && 0 <= v && v < N)
|
|
r = x;
|
|
else if (a.is_numeral(x, v))
|
|
r = a.mk_int(mod(v, N));
|
|
else if (is_bounded(x, N))
|
|
r = x;
|
|
else
|
|
r = a.mk_mod(x, a.mk_int(N));
|
|
return r;
|
|
}
|
|
|
|
void bv2int_translator::translate_eq(expr* e) {
|
|
expr* x = nullptr, * y = nullptr;
|
|
VERIFY(m.is_eq(e, x, y));
|
|
SASSERT(bv.is_bv(x));
|
|
if (!is_translated(e)) {
|
|
ensure_translated(x);
|
|
ensure_translated(y);
|
|
m_args.reset();
|
|
m_args.push_back(a.mk_sub(translated(x), translated(y)));
|
|
set_translated(e, m.mk_eq(umod(x, 0), a.mk_int(0)));
|
|
}
|
|
m_preds.push_back(e);
|
|
TRACE(bv, tout << mk_pp(e, m) << " " << mk_pp(translated(e), m) << "\n");
|
|
ctx.push(push_back_vector(m_preds));
|
|
|
|
}
|