/*++ Copyright (c) 2011 Microsoft Corporation Module Name: bv2int_rewriter.cpp Abstract: Basic rewriting rules for bv2int propagation. Author: Nikolaj (nbjorner) 2011-05-05 Notes: --*/ #include "bv2int_rewriter.h" #include "rewriter_def.h" #include "ast_pp.h" void bv2int_rewriter_ctx::update_params(params_ref const& p) { m_max_size = p.get_uint(":max-bv-size", UINT_MAX); } struct lt_rational { bool operator()(rational const& a, rational const& b) const { return a < b; } }; void bv2int_rewriter_ctx::collect_power2(goal const& s) { ast_manager& m = m_trail.get_manager(); arith_util arith(m); bv_util bv(m); for (unsigned j = 0; j < s.size(); ++j) { expr* f = s.form(j); if (!m.is_or(f)) continue; unsigned sz = to_app(f)->get_num_args(); expr* x, *y, *v = 0; rational n; vector bounds; bool is_int, ok = true; for (unsigned i = 0; ok && i < sz; ++i) { expr* e = to_app(f)->get_arg(i); if (!m.is_eq(e, x, y)) { ok = false; break; } if (arith.is_numeral(y, n, is_int) && is_int && (x == v || v == 0)) { v = x; bounds.push_back(n); } else if (arith.is_numeral(x, n, is_int) && is_int && (y == v || v == 0)) { v = y; bounds.push_back(n); } else { ok = false; break; } } if (!ok || !v) continue; SASSERT(!bounds.empty()); lt_rational lt; // lt is a total order on rationals. std::sort(bounds.begin(), bounds.end(), lt); rational p(1); unsigned num_bits = 0; for (unsigned i = 0; ok && i < bounds.size(); ++i) { ok = (p == bounds[i]); p *= rational(2); ++num_bits; } if (!ok) continue; unsigned log2 = 0; for (unsigned i = 1; i <= num_bits; i *= 2) ++log2; if(log2 == 0) continue; expr* logx = m.mk_fresh_const("log2_v", bv.mk_sort(log2)); logx = bv.mk_zero_extend(num_bits - log2, logx); m_trail.push_back(logx); TRACE("bv2int_rewriter", tout << mk_pp(v, m) << " |-> " << mk_pp(logx, m) << "\n";); m_power2.insert(v, logx); } } bool bv2int_rewriter_ctx::is_power2(expr* x, expr*& log_x) { return m_power2.find(x, log_x); } bv2int_rewriter::bv2int_rewriter(ast_manager & m, bv2int_rewriter_ctx& ctx) :m_manager(m), m_ctx(ctx), m_bv(m), m_arith(m) { } br_status bv2int_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if(f->get_family_id() == m_arith.get_family_id()) { switch (f->get_decl_kind()) { case OP_NUM: return BR_FAILED; case OP_LE: SASSERT(num_args == 2); return mk_le(args[0], args[1], result); case OP_GE: SASSERT(num_args == 2); return mk_ge(args[0], args[1], result); case OP_LT: SASSERT(num_args == 2); return mk_lt(args[0], args[1], result); case OP_GT: SASSERT(num_args == 2); return mk_gt(args[0], args[1], result); case OP_ADD: return mk_add(num_args, args, result); case OP_MUL: return mk_mul(num_args, args, result); case OP_SUB: return mk_sub(num_args, args, result); case OP_DIV: return BR_FAILED; case OP_IDIV: SASSERT(num_args == 2); return mk_idiv(args[0], args[1], result); case OP_MOD: SASSERT(num_args == 2); return mk_mod(args[0], args[1], result); case OP_REM: SASSERT(num_args == 2); return mk_rem(args[0], args[1], result); case OP_UMINUS: SASSERT(num_args == 1); return mk_uminus(args[0], result); case OP_TO_REAL: return BR_FAILED; case OP_TO_INT: return BR_FAILED; case OP_IS_INT: return BR_FAILED; default: return BR_FAILED; } } if (f->get_family_id() == m().get_basic_family_id()) { switch (f->get_decl_kind()) { case OP_EQ: SASSERT(num_args == 2); return mk_eq(args[0], args[1], result); case OP_ITE: SASSERT(num_args == 3); return mk_ite(args[0], args[1], args[2], result); default: return BR_FAILED; } } return BR_FAILED; } br_status bv2int_rewriter::mk_le(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_ule(s1, t1); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 <= t1 - t2 // <=> // s1 + t2 <= t1 + s2 // s1 = mk_bv_add(s1, t2, false); t1 = mk_bv_add(t1, s2, false); align_sizes(s1, t1, false); result = m_bv.mk_ule(s1, t1); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = m_bv.mk_sle(s1, t1); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_lt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg2, arg1)); return BR_REWRITE2; } br_status bv2int_rewriter::mk_ge(expr * arg1, expr * arg2, expr_ref & result) { return mk_le(arg2, arg1, result); } br_status bv2int_rewriter::mk_gt(expr * arg1, expr * arg2, expr_ref & result) { result = m().mk_not(m_arith.mk_le(arg1, arg2)); return BR_REWRITE2; } br_status bv2int_rewriter::mk_ite(expr* c, expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m().mk_ite(c, s1, t1)); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = mk_sbv2int(m().mk_ite(c, s1, t1)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_eq(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m().mk_eq(s1, t1); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { s1 = mk_bv_add(s1, t2, false); t1 = mk_bv_add(s2, t1, false); align_sizes(s1, t1, false); result = m().mk_eq(s1, t1); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = m().mk_eq(s1, t1); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_idiv(expr * arg1, expr * arg2, expr_ref & result) { // TBD return BR_FAILED; } br_status bv2int_rewriter::mk_mod(expr * s, expr * t, expr_ref & result) { expr_ref s1(m()), s2(m()), t1(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { align_sizes(s1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(s1, t1)); return BR_DONE; } // // (s1 - s2) mod t1 = (s1 + (t1 - (s2 mod t1))) mod t1 // if (is_bv2int_diff(s, s1, s2) && is_bv2int(t, t1)) { expr_ref u1(m()); align_sizes(s1, t1, false); u1 = m_bv.mk_bv_urem(s1, t1); u1 = m_bv.mk_bv_sub(t1, u1); u1 = mk_bv_add(s1, u1, false); align_sizes(u1, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_urem(u1, t1)); return BR_DONE; } #if 0 // TBD: check semantics if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); result = mk_sbv2int(m_bv.mk_bv_srem(s1, t1)); return BR_DONE; } #endif return BR_FAILED; } br_status bv2int_rewriter::mk_rem(expr * arg1, expr * arg2, expr_ref & result) { // TBD return BR_FAILED; } br_status bv2int_rewriter::mk_uminus(expr * s, expr_ref & result) { expr_ref s1(m()), s2(m()); if (is_bv2int_diff(s, s1, s2)) { result = m_arith.mk_sub(m_bv.mk_bv2int(s2), m_bv.mk_bv2int(s1)); return BR_DONE; } if (is_sbv2int(s, s1)) { result = mk_sbv2int(m_bv.mk_bv_neg(s1)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_add(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_add(result, args[i], result); } return r; } void bv2int_rewriter::align_sizes(expr_ref& s, expr_ref& t, bool is_signed) { unsigned sz1 = m_bv.get_bv_size(s); unsigned sz2 = m_bv.get_bv_size(t); if (sz1 > sz2 && is_signed) { t = mk_extend(sz1-sz2, t, true); } if (sz1 > sz2 && !is_signed) { t = mk_extend(sz1-sz2, t, false); } if (sz1 < sz2 && is_signed) { s = mk_extend(sz2-sz1, s, true); } if (sz1 < sz2 && !is_signed) { s = mk_extend(sz2-sz1, s, false); } } bool bv2int_rewriter::is_zero(expr* n) { rational r; unsigned sz; return m_bv.is_numeral(n, r, sz) && r.is_zero(); } expr* bv2int_rewriter::mk_bv_add(expr* s, expr* t, bool is_signed) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return t; } if (is_zero(t)) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1, is_signed); s1 = mk_extend(1, s1, is_signed); t1 = mk_extend(1, t1, is_signed); return m_bv.mk_bv_add(s1, t1); } br_status bv2int_rewriter::mk_add(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int(s, s1) && is_bv2int(t, t1)) { result = m_bv.mk_bv2int(mk_bv_add(s1, t1, false)); return BR_DONE; } if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 + t1 - t2 // = // s1 + t1 - (s2 + t2) // t1 = m_bv.mk_bv2int(mk_bv_add(s1, t1, false)); t2 = m_bv.mk_bv2int(mk_bv_add(s2, t2, false)); result = m_arith.mk_sub(t1, t2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { result = mk_sbv2int(mk_bv_add(s1, t1, true)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_mul(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_mul(result, args[i], result); } return r; } expr* bv2int_rewriter::mk_bv_mul(expr* s, expr* t, bool is_signed) { SASSERT(m_bv.is_bv(s)); SASSERT(m_bv.is_bv(t)); if (is_zero(s)) { return s; } if (is_zero(t)) { return t; } rational r; unsigned sz; if (m_bv.is_numeral(s, r, sz) && r.is_one()) { return t; } if (m_bv.is_numeral(t, r, sz) && r.is_one()) { return s; } expr_ref s1(s, m()), t1(t, m()); align_sizes(s1, t1, is_signed); unsigned n = m_bv.get_bv_size(t1); unsigned max_bits = m_ctx.get_max_num_bits(); bool add_side_conds = 2*n > max_bits; if (n >= max_bits) { // } else if (2*n > max_bits) { s1 = mk_extend(max_bits-n, s1, is_signed); t1 = mk_extend(max_bits-n, t1, is_signed); } else { s1 = mk_extend(n, s1, is_signed); t1 = mk_extend(n, t1, is_signed); } if (add_side_conds) { if (is_signed) { m_ctx.add_side_condition(m_bv.mk_bvsmul_no_ovfl(s1, t1)); m_ctx.add_side_condition(m_bv.mk_bvsmul_no_udfl(s1, t1)); } else { m_ctx.add_side_condition(m_bv.mk_bvumul_no_ovfl(s1, t1)); } } return m_bv.mk_bv_mul(s1, t1); } br_status bv2int_rewriter::mk_mul(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), s2(m()), t1(m()), t2(m()); if ((is_shl1(s, s1) && is_bv2int(t, t1)) || (is_shl1(t, s1) && is_bv2int(s, t1))) { unsigned n = m_bv.get_bv_size(s1); unsigned m = m_bv.get_bv_size(t1); s1 = mk_extend(m, s1, false); t1 = mk_extend(n, t1, false); result = m_bv.mk_bv2int(m_bv.mk_bv_shl(t1, s1)); return BR_DONE; } if (is_bv2int(s, s1) && is_bv2int(t, t1)) { result = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false)); return BR_DONE; } if ((is_bv2int(s, s1) && is_bv2int_diff(t, t1, t2)) || (is_bv2int(t, s1) && is_bv2int_diff(s, t1, t2))) { t1 = m_bv.mk_bv2int(mk_bv_mul(s1, t1, false)); t2 = m_bv.mk_bv2int(mk_bv_mul(s1, t2, false)); result = m_arith.mk_sub(t1, t2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { result = mk_sbv2int(mk_bv_mul(s1, t1, true)); return BR_DONE; } return BR_FAILED; } br_status bv2int_rewriter::mk_sub(unsigned num_args, expr * const* args, expr_ref& result) { br_status r = BR_DONE; SASSERT(num_args > 0); result = args[0]; for (unsigned i = 1; r == BR_DONE && i < num_args; ++i) { r = mk_sub(result, args[i], result); } return r; } br_status bv2int_rewriter::mk_sub(expr* s, expr* t, expr_ref& result) { expr_ref s1(m()), t1(m()), s2(m()), t2(m()); if (is_bv2int_diff(s, s1, s2) && is_bv2int_diff(t, t1, t2)) { // s1 - s2 - (t1 - t2) // = // s1 + t2 - (t1 + s2) // s1 = m_bv.mk_bv2int(mk_bv_add(s1, t2, false)); s2 = m_bv.mk_bv2int(mk_bv_add(s2, t1, false)); result = m_arith.mk_sub(s1, s2); return BR_DONE; } if (is_sbv2int(s, s1) && is_sbv2int(t, t1)) { align_sizes(s1, t1, true); s1 = m_bv.mk_sign_extend(1, s1); t1 = m_bv.mk_sign_extend(1, t1); result = mk_sbv2int(m_bv.mk_bv_sub(s1, t1)); return BR_DONE; } return BR_FAILED; } bool bv2int_rewriter::is_bv2int(expr* n, expr_ref& s) { rational k; bool is_int; if (m_bv.is_bv2int(n)) { s = to_app(n)->get_arg(0); return true; } if (m_arith.is_numeral(n, k, is_int) && is_int && !k.is_neg()) { unsigned sz = k.get_num_bits(); s = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); return true; } return false; } bool bv2int_rewriter::is_shl1(expr* n, expr_ref& s) { expr* s1, *s2; rational r; unsigned bv_size; if(m_bv.is_bv2int(n, s2) && m_bv.is_bv_shl(s2, s1, s2) && m_bv.is_numeral(s1, r, bv_size) && r.is_one()) { s = s2; return true; } return false; } bool bv2int_rewriter::is_bv2int_diff(expr* n, expr_ref& s, expr_ref& t) { if (is_bv2int(n, s)) { t = m_bv.mk_numeral(0, 1); return true; } rational k; bool is_int; if (m_arith.is_numeral(n, k, is_int) && is_int) { SASSERT(k.is_neg()); k.neg(); unsigned sz = k.get_num_bits(); t = m_bv.mk_numeral(k, m_bv.mk_sort(sz)); s = m_bv.mk_numeral(0, 1); return true; } // // bv2int(a) - bv2int(b) // expr *e1, *e2; if (m_arith.is_sub(n, e1, e2) && is_bv2int(e1, s) && is_bv2int(e2, t)) { return true; } return false; } bool bv2int_rewriter::is_sbv2int(expr* n, expr_ref& s) { if (is_bv2int(n, s)) { s = m_bv.mk_zero_extend(1, s); return true; } expr_ref u1(m()), u2(m()); if (is_bv2int_diff(n, u1, u2)) { align_sizes(u1, u2, false); u1 = mk_extend(1, u1, false); u2 = mk_extend(1, u2, false); s = m_bv.mk_bv_sub(u1, u2); return true; } // ite(bv1 == b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2])) expr* c, *t, *e1, *c1, *c2, *c3, *t1, *t2, *e2, *e3; rational k; bool is_int; unsigned lo, hi, lo1, hi1, sz; if (m().is_ite(n, c, t, e1) && m().is_eq(c, c1, c2) && m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 && m_bv.is_extract(c2, lo, hi, c3) && lo == hi && lo == m_bv.get_bv_size(c3) - 1 && m_arith.is_sub(t, t1, t2) && e1 == t1 && m_bv.is_bv2int(e1, e2) && m_bv.is_extract(e2, lo1, hi1, e3) && lo1 == 0 && hi1 == hi-1 && m_arith.is_numeral(t2, k, is_int) && is_int && k == m_bv.power_of_two(hi) ) { s = e3; return true; } #if 0 // bv2int(b[0:n-2]) - ite(bv1 == b[n-1:n-1], 2^{n-1}, 0) if (m().is_sub(n, e1, e2) && m_bv.is_bv2int(e1, e3) && m_bv.is_extract(e3, lo, hi, e4) && lo == 0 && hi == m_bv.get_bv_size(e4) - 2 && m().is_ite(e2, t1, t2, t3) && m().is_eq(t1, c1, c2) && m_bv.is_numeral(c1, k, sz) && k.is_one() && sz == 1 && m_bv.is_extract(c2, lo1, hi1, c3) && lo1 == h1 + 1 && hi1 == lo1 && c3 == e4 && m_arith.is_numeral(t2, )) { } #endif return false; } expr* bv2int_rewriter::mk_sbv2int(expr* b) { // // ite(bit1 = b[n-1:n-1], bv2int(b[0:n-2]) - 2^{n-1}, bv2int(b[0:n-2])) // expr* bv1 = m_bv.mk_numeral(1, 1); unsigned n = m_bv.get_bv_size(b); expr* c = m().mk_eq(bv1, m_bv.mk_extract(n-1, n-1, b)); expr* e = m_bv.mk_bv2int(m_bv.mk_extract(n-2, 0, b)); expr* t = m_arith.mk_sub(e, m_arith.mk_numeral(power(rational(2), n-1), true)); return m().mk_ite(c, t, e); } expr* bv2int_rewriter::mk_extend(unsigned sz, expr* b, bool is_signed) { if (sz == 0) { return b; } rational r; unsigned bv_sz; if (is_signed) { return m_bv.mk_sign_extend(sz, b); } else if (m_bv.is_numeral(b, r, bv_sz)) { return m_bv.mk_numeral(r, bv_sz + sz); } else { return m_bv.mk_zero_extend(sz, b); } } template class rewriter_tpl;