/*++ Copyright (c) 2006 Microsoft Corporation Module Name: bit_blaster_tpl_def.h Abstract: Template for bit-blaster operations Author: Leonardo de Moura (leonardo) 2011-05-02. Revision History: --*/ #include"bit_blaster_tpl.h" #include"rational.h" #include"ast_pp.h" #include"cooperate.h" template void bit_blaster_tpl::checkpoint() { if (memory::get_allocation_size() > m_max_memory) throw strategy_exception(STE_MAX_MEMORY_MSG); if (m_cancel) throw strategy_exception(STE_CANCELED_MSG); cooperate("bit-blaster"); } /** \brief Return true if all bits are true or false. */ template bool bit_blaster_tpl::is_numeral(unsigned sz, expr * const * bits) const { for (unsigned i = 0; i < sz; i++) if (!m().is_true(bits[i]) && !m().is_false(bits[i])) return false; return true; } /** \brief Return true if all bits are true or false, and store the number represent by these bits in r. */ template bool bit_blaster_tpl::is_numeral(unsigned sz, expr * const * bits, numeral & r) const { r.reset(); for (unsigned i = 0; i < sz; i++) { if (m().is_true(bits[i])) r += power(i); else if (!m().is_false(bits[i])) return false; } return true; } /** \brief Return true if all bits are true. */ template bool bit_blaster_tpl::is_minus_one(unsigned sz, expr * const * bits) const { for (unsigned i = 0; i < sz; i++) if (!m().is_true(bits[i])) return false; return true; } // hack to avoid GCC compilation error. static void _num2bits(ast_manager & m, rational const & v, unsigned sz, expr_ref_vector & out_bits) { SASSERT(v.is_nonneg()); rational aux = v; rational two(2); for (unsigned i = 0; i < sz; i++) { if ((aux % two).is_zero()) out_bits.push_back(m.mk_false()); else out_bits.push_back(m.mk_true()); aux = div(aux, two); } } template void bit_blaster_tpl::num2bits(numeral const & v, unsigned sz, expr_ref_vector & out_bits) const { _num2bits(m(), v, sz, out_bits); } template void bit_blaster_tpl::mk_half_adder(expr * a, expr * b, expr_ref & out, expr_ref & cout) { mk_xor(a, b, out); mk_and(a, b, cout); } template void bit_blaster_tpl::mk_full_adder(expr * a, expr * b, expr * cin, expr_ref & out, expr_ref & cout) { mk_xor3(a, b, cin, out); mk_carry(a, b, cin, cout); } template void bit_blaster_tpl::mk_neg(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); expr_ref cin(m()), cout(m()), out(m()); cin = m().mk_true(); for (unsigned idx = 0; idx < sz; idx++) { expr_ref not_a(m()); mk_not(a_bits[idx], not_a); if (idx < sz - 1) mk_half_adder(not_a, cin, out, cout); else mk_xor(not_a, cin, out); out_bits.push_back(out); cin = cout; } } template void bit_blaster_tpl::mk_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); expr_ref cin(m()), cout(m()), out(m()); cin = m().mk_false(); for (unsigned idx = 0; idx < sz; idx++) { if (idx < sz - 1) mk_full_adder(a_bits[idx], b_bits[idx], cin, out, cout); else mk_xor3(a_bits[idx], b_bits[idx], cin, out); out_bits.push_back(out); cin = cout; } #if 0 static unsigned counter = 0; counter++; verbose_stream() << "MK_ADDER: " << counter << std::endl; #endif } template void bit_blaster_tpl::mk_subtracter(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits, expr_ref & cout) { SASSERT(sz > 0); expr_ref cin(m()), out(m()); cin = m().mk_true(); for (unsigned j = 0; j < sz; j++) { expr_ref not_b(m()); mk_not(b_bits[j], not_b); mk_full_adder(a_bits[j], not_b, cin, out, cout); out_bits.push_back(out); cin = cout; } SASSERT(out_bits.size() == sz); } template void bit_blaster_tpl::mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); if (!m_use_bcm) { numeral n_a, n_b; if (is_numeral(sz, a_bits, n_b)) std::swap(a_bits, b_bits); if (is_minus_one(sz, b_bits)) { mk_neg(sz, a_bits, out_bits); return; } if (is_numeral(sz, a_bits, n_a)) { n_a *= n_b; num2bits(n_a, sz, out_bits); return; } } else { numeral n_a, n_b; if (is_numeral(sz, a_bits, n_a)) { mk_const_multiplier(sz, a_bits, b_bits, out_bits); return; } else if (is_numeral(sz, b_bits, n_b)) { mk_const_multiplier(sz, b_bits, a_bits, out_bits); return; } } if (!m_use_wtm) { #if 0 static unsigned counter = 0; counter++; verbose_stream() << "MK_MULTIPLIER: " << counter << std::endl; #endif expr_ref_vector cins(m()), couts(m()); expr_ref out(m()), cout(m()); mk_and(a_bits[0], b_bits[0], out); out_bits.push_back(out); /* out = a*b is encoded using the following circuit. a[0]&b[0] a[0]&b[1] a[0]&b[2] a[0]&b[3] ... | | | | | a[1]&b[0] - HA a[1]&b[1] - HA a[1]&b[2] - HA | | \ | \ | \ | | --------------- | -------------- | --- ... | | \| \ | | a[2]&b[0] - FA a[2]&b[1] - FA | | | \ | \ | | | -------------- | -- ... | | | \| | | | a[3]&b[0] - FA | | | | \ | | | | -- .... ... ... ... ... out[0] out[1] out[2] out[3] HA denotes a half-adder. FA denotes a full-adder. */ for (unsigned i = 1; i < sz; i++) { checkpoint(); couts.reset(); expr_ref i1(m()), i2(m()); mk_and(a_bits[0], b_bits[i], i1); mk_and(a_bits[1], b_bits[i-1], i2); if (i < sz - 1) { mk_half_adder(i1, i2, out, cout); couts.push_back(cout); for (unsigned j = 2; j <= i; j++) { expr_ref prev_out(m()); prev_out = out; expr_ref i3(m()); mk_and(a_bits[j], b_bits[i-j], i3); mk_full_adder(i3, prev_out, cins.get(j-2), out, cout); couts.push_back(cout); } out_bits.push_back(out); cins.swap(couts); } else { // last step --> I don't need to generate/store couts. mk_xor(i1, i2, out); for (unsigned j = 2; j <= i; j++) { expr_ref i3(m()); mk_and(a_bits[j], b_bits[i-j], i3); mk_xor3(i3, out, cins.get(j-2), out); } out_bits.push_back(out); } } } else { // WALLACE TREE MULTIPLIER if (sz == 1) { expr_ref t(m()); mk_and(a_bits[0], b_bits[0], t); out_bits.push_back(t); return; } // There are sz numbers to add and we use a Wallace tree to reduce that to two. // In this tree, we reduce as early as possible, as opposed to the Dada tree where some // additions may be delayed if they don't increase the propagation delay [which may be // a little bit more efficient, but it's tricky to find out which additions create // additional delays]. expr_ref zero(m()); zero = m().mk_false(); vector< expr_ref_vector > pps; pps.resize(sz, m()); for (unsigned i = 0; i < sz; i++) { checkpoint(); // The partial product is a_bits AND b_bits[i] // [or alternatively ITE(b_bits[i], a_bits, bv0[sz])] expr_ref_vector & pp = pps[i]; expr_ref t(m()); for (unsigned j = 0; j < i; j++) pp.push_back(zero); // left shift by i bits for (unsigned j = 0; j < (sz - i); j++) { mk_and(a_bits[j], b_bits[i], t); pp.push_back(t); } SASSERT(pps[i].size() == sz); } while (pps.size() != 2) { unsigned save_inx = 0; unsigned i = 0; unsigned end = pps.size() - 3; for ( ; i <= end; i += 3) { checkpoint(); expr_ref_vector pp1(m()), pp2(m()), pp3(m()); pp1.swap(pps[i]); pp2.swap(pps[i+1]); pp3.swap(pps[i+2]); expr_ref_vector & sum_bits = pps[save_inx]; expr_ref_vector & carry_bits = pps[save_inx+1]; SASSERT(sum_bits.empty() && carry_bits.empty()); carry_bits.push_back(zero); mk_carry_save_adder(pp1.size(), pp1.c_ptr(), pp2.c_ptr(), pp3.c_ptr(), sum_bits, carry_bits); carry_bits.pop_back(); save_inx += 2; } if (i == pps.size()-2) { pps[save_inx++].swap(pps[i++]); pps[save_inx++].swap(pps[i++]); } else if (i == pps.size()-1) { pps[save_inx++].swap(pps[i++]); } SASSERT (save_inx < pps.size() && i == pps.size()); pps.shrink(save_inx); } SASSERT(pps.size() == 2); // Now there are only two numbers to add, we can use a ripple carry adder here. mk_adder(sz, pps[0].c_ptr(), pps[1].c_ptr(), out_bits); } } template void bit_blaster_tpl::mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { SASSERT(sz > 0); expr_ref zero(m()); zero = m().mk_false(); ptr_buffer ext_a_bits; ptr_buffer ext_b_bits; ext_a_bits.append(sz, a_bits); ext_b_bits.append(sz, b_bits); ext_a_bits.push_back(zero); ext_b_bits.push_back(zero); SASSERT(ext_a_bits.size() == 1 + sz); SASSERT(ext_b_bits.size() == 1 + sz); expr_ref_vector mult_cout(m()); // // mk_multiplier will simplify output taking into account that // the most significant bits of ext_a_bits and ext_b_bits are zero. // mk_multiplier(1 + sz, ext_a_bits.c_ptr(), ext_b_bits.c_ptr(), mult_cout); expr_ref overflow1(m()), overflow2(m()), overflow(m()); // // ignore bits [0, sz-1] of mult_cout // overflow1 = mult_cout[sz].get(); expr_ref ovf(m()), v(m()), tmp(m()); ovf = m().mk_false(); v = m().mk_false(); for (unsigned i = 1; i < sz; ++i) { mk_or(ovf, a_bits[sz-i], ovf); mk_and(ovf, b_bits[i], tmp); mk_or(tmp, v, v); } overflow2 = v; mk_or(overflow1, overflow2, overflow); mk_not(overflow, result); } template void bit_blaster_tpl::mk_smul_no_overflow_core(unsigned sz, expr * const * a_bits, expr * const * b_bits, bool is_overflow, expr_ref & result) { SASSERT(sz > 0); expr_ref zero(m()); zero = m().mk_false(); ptr_buffer ext_a_bits; ptr_buffer ext_b_bits; ext_a_bits.append(sz, a_bits); ext_b_bits.append(sz, b_bits); ext_a_bits.push_back(a_bits[sz-1]); ext_b_bits.push_back(b_bits[sz-1]); SASSERT(ext_a_bits.size() == 1 + sz); SASSERT(ext_b_bits.size() == 1 + sz); expr_ref_vector mult_cout(m()); mk_multiplier(1 + sz, ext_a_bits.c_ptr(), ext_b_bits.c_ptr(), mult_cout); expr_ref overflow1(m()), overflow2(m()), overflow(m()); // // The two most significant bits are different. // mk_xor(mult_cout[sz].get(), mult_cout[sz-1].get(), overflow1); // // let // a_i = a[sz-1] xor a[i] // b_i = b[sz-1] xor b[i] // a_acc_i = a_{sz-2} or ... or a_{sz-1-i} // b = (a_acc_1 and b_1) or (a_acc_2 and b_2) or ... or (a_acc_{n-2} and b_{n-2}) // expr_ref v(m()), tmp(m()), a(m()), b(m()), a_acc(m()), sign(m()); a_acc = m().mk_false(); v = m().mk_false(); for (unsigned i = 1; i + 1 < sz; ++i) { mk_xor(b_bits[sz-1], b_bits[i], b); mk_xor(a_bits[sz-1], a_bits[sz-1-i], a); mk_or(a, a_acc, a_acc); mk_and(a_acc, b, tmp); mk_or(tmp, v, v); } overflow2 = v; mk_or(overflow1, overflow2, overflow); if (is_overflow) { // check for proper overflow // can only happen when the sign bits are the same. mk_iff(a_bits[sz-1], b_bits[sz-1], sign); } else { // check for proper underflow // can only happen when the sign bits are different. mk_xor(a_bits[sz-1], b_bits[sz-1], sign); } mk_and(sign, overflow, overflow); mk_not(overflow, result); } template void bit_blaster_tpl::mk_smul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { mk_smul_no_overflow_core(sz, a_bits, b_bits, true, result); } template void bit_blaster_tpl::mk_smul_no_underflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) { mk_smul_no_overflow_core(sz, a_bits, b_bits, false, result); } template void bit_blaster_tpl::mk_udiv_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits, expr_ref_vector & r_bits) { SASSERT(sz > 0); // p is the residual of each stage of the division. expr_ref_vector & p = r_bits; // t is an auxiliary vector used to store the result of a subtraction expr_ref_vector t(m()); // init p p.push_back(a_bits[sz-1]); for (unsigned i = 1; i < sz; i++) p.push_back(m().mk_false()); q_bits.resize(sz); for (unsigned i = 0; i < sz; i++) { checkpoint(); // generate p - b expr_ref q(m()); t.reset(); mk_subtracter(sz, p.c_ptr(), b_bits, t, q); q_bits.set(sz - i - 1, q); // update p if (i < sz - 1) { for (unsigned j = sz - 1; j > 0; j--) { expr_ref i(m()); mk_ite(q, t.get(j-1), p.get(j-1), i); p.set(j, i); } p.set(0, a_bits[sz - i - 2]); } else { // last step: p contains the remainder for (unsigned j = 0; j < sz; j++) { expr_ref i(m()); mk_ite(q, t.get(j), p.get(j), i); p.set(j, i); } } } DEBUG_CODE({ for (unsigned i = 0; i < sz; i++) { SASSERT(q_bits.get(i) != 0); }}); TRACE("bit_blaster", tout << "a: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(a_bits[i], m()) << " "; tout << "\nb: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(b_bits[i], m()) << " "; tout << "\nq: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(q_bits[i].get(), m()) << " "; tout << "\nr: "; for (unsigned i = 0; i < sz; ++i) tout << mk_pp(r_bits[i].get(), m()) << " "; tout << "\n"; ); } template void bit_blaster_tpl::mk_udiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & q_bits) { expr_ref_vector aux(m()); mk_udiv_urem(sz, a_bits, b_bits, q_bits, aux); } template void bit_blaster_tpl::mk_urem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & r_bits) { expr_ref_vector aux(m()); mk_udiv_urem(sz, a_bits, b_bits, aux, r_bits); } template void bit_blaster_tpl::mk_multiplexer(expr * c, unsigned sz, expr * const * t_bits, expr * const * e_bits, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) { expr_ref t(m()); mk_ite(c, t_bits[i], e_bits[i], t); out_bits.push_back(t); } } template void bit_blaster_tpl::mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; if (m().is_false(a_msb)) { out_bits.append(sz, a_bits); } else if (m().is_true(a_msb)) { mk_neg(sz, a_bits, out_bits); } else { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); mk_multiplexer(a_msb, sz, neg_a_bits.c_ptr(), a_bits, out_bits); } } #define SDIV 0 #define SREM 1 #define SMOD 2 template template void bit_blaster_tpl::mk_sdiv_srem_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { // This definition is only good when the most significant bits are set. // Otherwise, it will create 4 copies of the expensive sdiv/srem/smod SASSERT(k == SDIV || k == SREM || k == SMOD); expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; expr_ref_vector neg_a_bits(m()); expr_ref_vector neg_b_bits(m()); mk_neg(sz, a_bits, neg_a_bits); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector pp_q(m()), pp_r(m()), pn_q(m()), pn_r(m()), np_q(m()), np_r(m()), nn_q(m()), nn_r(m()); if (!m().is_true(a_msb) && !m().is_true(b_msb)) { mk_udiv_urem(sz, a_bits, b_bits, pp_q, pp_r); } else { pp_q.resize(sz, m().mk_false()); pp_r.resize(sz, m().mk_false()); } if (!m().is_false(a_msb) && !m().is_true(b_msb)) { mk_udiv_urem(sz, neg_a_bits.c_ptr(), b_bits, np_q, np_r); } else { np_q.resize(sz, m().mk_false()); np_r.resize(sz, m().mk_false()); } if (!m().is_true(a_msb) && !m().is_false(b_msb)) { mk_udiv_urem(sz, a_bits, neg_b_bits.c_ptr(), pn_q, pn_r); } else { pn_q.resize(sz, m().mk_false()); pn_r.resize(sz, m().mk_false()); } if (!m().is_false(a_msb) && !m().is_false(b_msb)) { mk_udiv_urem(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), nn_q, nn_r); } else { nn_q.resize(sz, m().mk_false()); nn_r.resize(sz, m().mk_false()); } expr_ref_vector ite1(m()), ite2(m()); if (k == SDIV) { expr_ref_vector & pp_out = pp_q; expr_ref_vector np_out(m()); expr_ref_vector pn_out(m()); expr_ref_vector & nn_out = nn_q; if (!m().is_false(a_msb) && !m().is_true(b_msb)) mk_neg(sz, np_q.c_ptr(), np_out); else np_out.resize(sz, m().mk_false()); if (!m().is_true(a_msb) && !m().is_false(b_msb)) mk_neg(sz, pn_q.c_ptr(), pn_out); else pn_out.resize(sz, m().mk_false()); #define MK_MULTIPLEXER() \ mk_multiplexer(b_msb, sz, nn_out.c_ptr(), np_out.c_ptr(), ite1); \ mk_multiplexer(b_msb, sz, pn_out.c_ptr(), pp_out.c_ptr(), ite2); \ mk_multiplexer(a_msb, sz, ite1.c_ptr(), ite2.c_ptr(), out_bits) MK_MULTIPLEXER(); } else if (k == SREM) { expr_ref_vector & pp_out = pp_r; expr_ref_vector np_out(m()); expr_ref_vector & pn_out = pn_r; expr_ref_vector nn_out(m()); if (!m().is_false(a_msb) && !m().is_true(b_msb)) mk_neg(sz, np_r.c_ptr(), np_out); else np_out.resize(sz, m().mk_false()); if (!m().is_false(a_msb) && !m().is_false(b_msb)) mk_neg(sz, nn_r.c_ptr(), nn_out); else nn_out.resize(sz, m().mk_false()); MK_MULTIPLEXER(); } else { SASSERT(k == SMOD); expr_ref_vector & pp_out = pp_r; expr_ref_vector np_out(m()); expr_ref_vector pn_out(m()); expr_ref_vector nn_out(m()); if (!m().is_false(a_msb) && !m().is_true(b_msb)) { expr_ref cout(m()); mk_subtracter(sz, b_bits, np_r.c_ptr(), np_out, cout); } else np_out.resize(sz, m().mk_false()); if (!m().is_true(a_msb) && !m().is_false(b_msb)) mk_adder(sz, b_bits, pn_r.c_ptr(), pn_out); else pn_out.resize(sz, m().mk_false()); if (!m().is_false(a_msb) && !m().is_false(b_msb)) mk_neg(sz, nn_r.c_ptr(), nn_out); else nn_out.resize(sz, m().mk_false()); MK_MULTIPLEXER(); } } template void bit_blaster_tpl::mk_sdiv(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; if (m().is_false(a_msb) && m().is_false(b_msb)) { mk_udiv(sz, a_bits, b_bits, out_bits); } else if (m().is_false(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector tmp(m()); mk_udiv(sz, a_bits, neg_b_bits.c_ptr(), tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_false(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector tmp(m()); mk_udiv(sz, neg_a_bits.c_ptr(), b_bits, tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); mk_udiv(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), out_bits); } else { #if 0 // creates 4 dividers mk_sdiv_srem_smod(sz, a_bits, b_bits, out_bits); #else // creates only 1 expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector udiv_bits(m()); mk_udiv(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), udiv_bits); expr_ref_vector neg_udiv_bits(m()); mk_neg(sz, udiv_bits.c_ptr(), neg_udiv_bits); expr_ref c(m()); mk_iff(a_msb, b_msb, c); mk_multiplexer(c, sz, udiv_bits.c_ptr(), neg_udiv_bits.c_ptr(), out_bits); #endif } } template void bit_blaster_tpl::mk_srem(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; if (m().is_false(a_msb) && m().is_false(b_msb)) { mk_urem(sz, a_bits, b_bits, out_bits); } else if (m().is_false(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); mk_urem(sz, a_bits, neg_b_bits.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_false(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector tmp(m()); mk_urem(sz, neg_a_bits.c_ptr(), b_bits, tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else if (m().is_true(a_msb) && m().is_true(b_msb)) { expr_ref_vector neg_a_bits(m()); mk_neg(sz, a_bits, neg_a_bits); expr_ref_vector neg_b_bits(m()); mk_neg(sz, b_bits, neg_b_bits); expr_ref_vector tmp(m()); mk_urem(sz, neg_a_bits.c_ptr(), neg_b_bits.c_ptr(), tmp); mk_neg(sz, tmp.c_ptr(), out_bits); } else { #if 0 // creates 4 urem mk_sdiv_srem_smod(sz, a_bits, b_bits, out_bits); #else // creates only 1 expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector urem_bits(m()); mk_urem(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), urem_bits); expr_ref_vector neg_urem_bits(m()); mk_neg(sz, urem_bits.c_ptr(), neg_urem_bits); mk_multiplexer(a_msb, sz, neg_urem_bits.c_ptr(), urem_bits.c_ptr(), out_bits); #endif } } /** \brief Generate circuit for signed mod. This function implements the semantics of bvsmod given below for two bits: (define-fun bvsmod_def ((s (_ BitVec 2)) (t (_ BitVec 2))) (_ BitVec 2) (let ((msb_s ((_ extract 1 1) s)) (msb_t ((_ extract 1 1) t))) (let ((abs_s (ite (= msb_s #b0) s (bvneg s))) (abs_t (ite (= msb_t #b0) t (bvneg t)))) (let ((u (bvurem abs_s abs_t))) (ite (= u (_ bv0 2)) u (ite (and (= msb_s #b0) (= msb_t #b0)) u (ite (and (= msb_s #b1) (= msb_t #b0)) (bvadd (bvneg u) t) (ite (and (= msb_s #b0) (= msb_t #b1)) (bvadd u t) (bvneg u))))))))) Note: The semantics is sensitive to the order of these tests. It is unsound to test first for whether the most significant bits of s and t are known and use the cases for those. If u is 0 then the result is 0. */ template void bit_blaster_tpl::mk_smod(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr * a_msb = a_bits[sz - 1]; expr * b_msb = b_bits[sz - 1]; expr_ref_vector abs_a_bits(m()); expr_ref_vector abs_b_bits(m()); mk_abs(sz, a_bits, abs_a_bits); mk_abs(sz, b_bits, abs_b_bits); expr_ref_vector u_bits(m()); mk_urem(sz, abs_a_bits.c_ptr(), abs_b_bits.c_ptr(), u_bits); expr_ref_vector neg_u_bits(m()); mk_neg(sz, u_bits.c_ptr(), neg_u_bits); expr_ref_vector neg_u_add_b(m()); mk_adder(sz, neg_u_bits.c_ptr(), b_bits, neg_u_add_b); expr_ref_vector u_add_b(m()); mk_adder(sz, u_bits.c_ptr(), b_bits, u_add_b); expr_ref_vector zero(m()); num2bits(numeral(0), sz, zero); expr_ref u_eq_0(m()); mk_eq(sz, u_bits.c_ptr(), zero.c_ptr(), u_eq_0); expr_ref_vector & pp_bits = u_bits; // pos & pos case expr_ref_vector & pn_bits = u_add_b; // pos & neg case expr_ref_vector & np_bits = neg_u_add_b; // neg & pos case expr_ref_vector & nn_bits = neg_u_bits; // neg & neg case expr_ref_vector ite1(m()); expr_ref_vector ite2(m()); expr_ref_vector body(m()); mk_multiplexer(b_msb, sz, nn_bits.c_ptr(), np_bits.c_ptr(), ite1); mk_multiplexer(b_msb, sz, pn_bits.c_ptr(), pp_bits.c_ptr(), ite2); mk_multiplexer(a_msb, sz, ite1.c_ptr(), ite2.c_ptr(), body); mk_multiplexer(u_eq_0, sz, u_bits.c_ptr(), body.c_ptr(), out_bits); } template void bit_blaster_tpl::mk_eq(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { expr_ref_vector out_bits(m()); for (unsigned i = 0; i < sz; i++) { mk_iff(a_bits[i], b_bits[i], out); out_bits.push_back(out); } mk_and(out_bits.size(), out_bits.c_ptr(), out); } template void bit_blaster_tpl::mk_rotate_left(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { n = n % sz; for (unsigned i = sz - n; i < sz; i++) out_bits.push_back(a_bits[i]); for (unsigned i = 0 ; i < sz - n; i++) out_bits.push_back(a_bits[i]); } template void bit_blaster_tpl::mk_rotate_right(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { n = n % sz; mk_rotate_left(sz, a_bits, sz - n, out_bits); } template void bit_blaster_tpl::mk_sign_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) out_bits.push_back(a_bits[i]); expr * high_bit = a_bits[sz - 1]; for (unsigned i = sz; i < sz + n; i++) out_bits.push_back(high_bit); } template void bit_blaster_tpl::mk_zero_extend(unsigned sz, expr * const * a_bits, unsigned n, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) out_bits.push_back(a_bits[i]); expr * high_bit = m().mk_false(); for (unsigned i = sz; i < sz + n; i++) out_bits.push_back(high_bit); } /** \brief Return an expression that is true iff a_bits represents the number n. */ template void bit_blaster_tpl::mk_is_eq(unsigned sz, expr * const * a_bits, unsigned n, expr_ref & out) { numeral two(2); expr_ref_vector out_bits(m()); for (unsigned i = 0; i < sz; i++) { if (n % 2 == 0) { expr_ref not_a(m()); mk_not(a_bits[i], not_a); out_bits.push_back(not_a); } else { out_bits.push_back(a_bits[i]); } n = n / 2; } mk_and(out_bits.size(), out_bits.c_ptr(), out); } /** \brief Store in eqs the equalities a_bits = 0, a_bits = 1, ..., a_bits = sz -1. */ template void bit_blaster_tpl::mk_eqs(unsigned sz, expr * const * a_bits, expr_ref_vector & eqs) { for (unsigned i = 0; i < sz; i++) { expr_ref eq(m()); mk_is_eq(sz, a_bits, i, eq); eqs.push_back(eq); } } template void bit_blaster_tpl::mk_shl(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { unsigned n = static_cast(k.get_int64()); if (n >= sz) n = sz; unsigned pos; for (pos = 0; pos < n; pos++) out_bits.push_back(m().mk_false()); for (unsigned i = 0; pos < sz; pos++, i++) out_bits.push_back(a_bits[i]); } else { expr_ref_vector eqs(m()); mk_eqs(sz, b_bits, eqs); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr_ref out(m()); mk_ite(eqs.get(i), a_bits[0], m().mk_false(), out); for (unsigned j = 1; j <= i; j++) { expr_ref new_out(m()); mk_ite(eqs.get(i - j), a_bits[j], out, new_out); out = new_out; } out_bits.push_back(out); } } } template void bit_blaster_tpl::mk_lshr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { unsigned n = static_cast(k.get_int64()); unsigned pos = 0; for (unsigned i = n; i < sz; pos++, i++) out_bits.push_back(a_bits[i]); for (; pos < sz; pos++) out_bits.push_back(m().mk_false()); } else { expr_ref_vector eqs(m()); mk_eqs(sz, b_bits, eqs); out_bits.resize(sz); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr_ref out(m()); mk_ite(eqs.get(i), a_bits[sz-1], m().mk_false(), out); for (unsigned j = 1; j <= i; j++) { expr_ref new_out(m()); mk_ite(eqs.get(i - j), a_bits[sz - j - 1], out, new_out); out = new_out; } out_bits.set(sz - i - 1, out); } } } template void bit_blaster_tpl::mk_ashr(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k)) { unsigned n = static_cast(k.get_int64()); unsigned pos = 0; for (unsigned i = n; i < sz; pos++, i++) out_bits.push_back(a_bits[i]); for (; pos < sz; pos++) out_bits.push_back(a_bits[sz-1]); } else { expr_ref_vector eqs(m()); mk_eqs(sz, b_bits, eqs); out_bits.resize(sz); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr_ref out(m()); out = a_bits[sz-1]; for (unsigned j = 1; j <= i; j++) { expr_ref new_out(m()); mk_ite(eqs.get(i - j), a_bits[sz - j - 1], out, new_out); out = new_out; } TRACE("bit_blaster_tpl", tout << (sz - i - 1) << " :\n" << mk_pp(out, m()) << "\n";); out_bits.set(sz - i - 1, out); } } } template template void bit_blaster_tpl::mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { numeral k; if (is_numeral(sz, b_bits, k) && k.is_unsigned()) { if (Left) mk_rotate_left(sz, a_bits, static_cast(k.get_uint64()), out_bits); else mk_rotate_right(sz, a_bits, static_cast(k.get_uint64()), out_bits); } else { expr_ref_vector sz_bits(m()); expr_ref_vector masked_b_bits(m()); expr_ref_vector eqs(m()); numeral sz_numeral(sz); num2bits(sz_numeral, sz, sz_bits); mk_urem(sz, b_bits, sz_bits.c_ptr(), masked_b_bits); mk_eqs(sz, masked_b_bits.c_ptr(), eqs); for (unsigned i = 0; i < sz; i++) { checkpoint(); expr_ref out(m()); out = a_bits[i]; for (unsigned j = 1; j < sz; j++) { expr_ref new_out(m()); unsigned src = (Left ? (i - j) : (i + j)) % sz; mk_ite(eqs.get(j), a_bits[src], out, new_out); out = new_out; } out_bits.push_back(out); } } } template void bit_blaster_tpl::mk_ext_rotate_left(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { mk_ext_rotate_left_right(sz, a_bits, b_bits, out_bits); } template void bit_blaster_tpl::mk_ext_rotate_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { mk_ext_rotate_left_right(sz, a_bits, b_bits, out_bits); } template template void bit_blaster_tpl::mk_le(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { SASSERT(sz > 0); expr_ref i1(m()), i2(m()), i3(m()), not_a(m()); mk_not(a_bits[0], not_a); mk_or(not_a, b_bits[0], out); for (unsigned idx = 1; idx < (Signed ? sz - 1 : sz); idx++) { mk_not(a_bits[idx], not_a); mk_and(not_a, b_bits[idx], i1); mk_and(not_a, out, i2); mk_and(b_bits[idx], out, i3); mk_or(i1, i2, i3, out); } if (Signed) { expr_ref not_b(m()); mk_not(b_bits[sz-1], not_b); mk_and(not_b, a_bits[sz-1], i1); mk_and(not_b, out, i2); mk_and(a_bits[sz-1], out, i3); mk_or(i1, i2, i3, out); } } template void bit_blaster_tpl::mk_sle(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { mk_le(sz, a_bits, b_bits, out); } template void bit_blaster_tpl::mk_ule(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & out) { mk_le(sz, a_bits, b_bits, out); } template void bit_blaster_tpl::mk_not(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { for (unsigned i = 0; i < sz; i++) { expr_ref t(m()); mk_not(a_bits[i], t); out_bits.push_back(t); } } #define MK_BINARY(NAME, OP) \ template \ void bit_blaster_tpl::NAME(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { \ for (unsigned i = 0; i < sz; i++) { \ expr_ref t(m()); \ OP(a_bits[i], b_bits[i], t); \ out_bits.push_back(t); \ } \ } MK_BINARY(mk_and, mk_and); MK_BINARY(mk_or, mk_or); MK_BINARY(mk_xor, mk_xor); MK_BINARY(mk_xnor, mk_iff); MK_BINARY(mk_nand, mk_nand); MK_BINARY(mk_nor, mk_nor); template void bit_blaster_tpl::mk_redand(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_and(sz, a_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_redor(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_or(sz, a_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { expr_ref tmp(m()); mk_eq(sz, a_bits, b_bits, tmp); out_bits.push_back(tmp); } template void bit_blaster_tpl::mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits) { expr_ref t(m()); for (unsigned i = 0; i < sz; i++) { mk_xor3(a_bits[i], b_bits[i], c_bits[i], t); sum_bits.push_back(t); mk_carry(a_bits[i], b_bits[i], c_bits[i], t); carry_bits.push_back(t); } } template void bit_blaster_tpl::mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { DEBUG_CODE({ numeral x; SASSERT(is_numeral(sz, a_bits, x)); SASSERT(out_bits.empty()); }); expr_ref_vector minus_b_bits(m()), tmp(m()); mk_neg(sz, b_bits, minus_b_bits); out_bits.resize(sz, m().mk_false()); #if 1 bool last=false, now; for (unsigned i = 0; i < sz; i++) { now = m().is_true(a_bits[i]); SASSERT(now || m().is_false(a_bits[i])); tmp.reset(); if (now && !last) { mk_adder(sz - i, out_bits.c_ptr() + i, minus_b_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux. } else if (!now && last) { mk_adder(sz - i, out_bits.c_ptr() + i, b_bits, tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux. } last = now; } #else // Radix 4 Booth encoder // B = b_bits, -B = minus_b_bits // 2B = b2_bits, -2B = minus_b2_bits expr_ref_vector b2_bits(m()); expr_ref_vector minus_b2_bits(m()); b2_bits.push_back(m().mk_false()); minus_b2_bits.push_back(m().mk_false()); for (unsigned i = 0; i < sz-1; i++) { b2_bits.push_back(b_bits[i]); minus_b2_bits.push_back(minus_b_bits.get(i)); } bool last=false, now1, now2; for (unsigned i = 0; i < sz; i += 2) { now1 = m().is_true(a_bits[i]); now2 = m().is_true(a_bits[i+1]); SASSERT(now1 || m().is_false(a_bits[i])); SASSERT(now2 || m().is_false(a_bits[i+1])); tmp.reset(); if ((!now2 && !now1 && last) || (!now2 && now1 && !last)) { // Add B mk_adder(sz - i, out_bits.c_ptr() + i, b_bits, tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if (!now2 && now1 && last) { // Add 2B mk_adder(sz - i, out_bits.c_ptr() + i, b2_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if (now2 && !now1 && !last) { // Add -2B mk_adder(sz - i, out_bits.c_ptr() + i, minus_b2_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } else if ((now2 && !now1 && last) || (now2 && now1 && !last)) { // Add -B mk_adder(sz - i, out_bits.c_ptr() + i, minus_b_bits.c_ptr(), tmp); for (unsigned j = 0; j < (sz - i); j++) out_bits.set(i+j, tmp.get(j)); } last = now2; } #endif TRACE("bit_blaster_tpl_booth", for (unsigned i=0; i