mirror of
https://github.com/Z3Prover/z3
synced 2025-08-22 11:07:51 +00:00
tuning bit-vector operations
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
1eebab355c
commit
8e26c97782
9 changed files with 271 additions and 94 deletions
|
@ -121,8 +121,11 @@ public:
|
|||
void mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
|
||||
|
||||
void 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);
|
||||
void mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
|
||||
bool mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
|
||||
bool mk_const_case_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
|
||||
void mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer<expr, 128>& a_bits, ptr_buffer<expr, 128>& b_bits, expr_ref_vector & out_bits);
|
||||
|
||||
bool is_bool_const(expr* e) const { return m().is_true(e) || m().is_false(e); }
|
||||
void mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits);
|
||||
};
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ void bit_blaster_tpl<Cfg>::checkpoint() {
|
|||
template<typename Cfg>
|
||||
bool bit_blaster_tpl<Cfg>::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]))
|
||||
if (!is_bool_const(bits[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -158,30 +158,24 @@ void bit_blaster_tpl<Cfg>::mk_subtracter(unsigned sz, expr * const * a_bits, exp
|
|||
template<typename Cfg>
|
||||
void bit_blaster_tpl<Cfg>::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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 (is_numeral(sz, a_bits, n_a)) {
|
||||
n_a *= n_b;
|
||||
num2bits(n_a, sz, out_bits);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mk_const_multiplier(sz, a_bits, b_bits, out_bits)) {
|
||||
return;
|
||||
}
|
||||
if (mk_const_multiplier(sz, b_bits, a_bits, out_bits)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_use_wtm) {
|
||||
|
@ -1171,13 +1165,74 @@ void bit_blaster_tpl<Cfg>::mk_carry_save_adder(unsigned sz, expr * const * a_bit
|
|||
}
|
||||
|
||||
template<typename Cfg>
|
||||
void bit_blaster_tpl<Cfg>::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());
|
||||
});
|
||||
bool bit_blaster_tpl<Cfg>::mk_const_case_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) {
|
||||
unsigned nb = 0;
|
||||
unsigned case_size = 1;
|
||||
unsigned circuit_size = sz*sz*5;
|
||||
for (unsigned i = 0; case_size < circuit_size && i < sz; ++i) {
|
||||
if (!is_bool_const(a_bits[i])) {
|
||||
case_size *= 2;
|
||||
}
|
||||
if (!is_bool_const(b_bits[i])) {
|
||||
case_size *= 2;
|
||||
}
|
||||
}
|
||||
if (case_size >= circuit_size) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(out_bits.empty());
|
||||
|
||||
ptr_buffer<expr, 128> na_bits;
|
||||
na_bits.append(sz, a_bits);
|
||||
ptr_buffer<expr, 128> nb_bits;
|
||||
nb_bits.append(sz, b_bits);
|
||||
mk_const_case_multiplier(true, 0, sz, na_bits, nb_bits, out_bits);
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Cfg>
|
||||
void bit_blaster_tpl<Cfg>::mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer<expr, 128>& a_bits, ptr_buffer<expr, 128>& b_bits, expr_ref_vector & out_bits) {
|
||||
while (is_a && i < sz && is_bool_const(a_bits[i])) ++i;
|
||||
if (is_a && i == sz) { is_a = false; i = 0; }
|
||||
while (!is_a && i < sz && is_bool_const(b_bits[i])) ++i;
|
||||
if (i < sz) {
|
||||
expr_ref_vector out1(m()), out2(m());
|
||||
expr_ref x(m());
|
||||
x = is_a?a_bits[i]:b_bits[i];
|
||||
if (is_a) a_bits[i] = m().mk_true(); else b_bits[i] = m().mk_true();
|
||||
mk_const_case_multiplier(is_a, i+1, sz, a_bits, b_bits, out1);
|
||||
if (is_a) a_bits[i] = m().mk_false(); else b_bits[i] = m().mk_false();
|
||||
mk_const_case_multiplier(is_a, i+1, sz, a_bits, b_bits, out2);
|
||||
if (is_a) a_bits[i] = x; else b_bits[i] = x;
|
||||
SASSERT(out_bits.empty());
|
||||
for (unsigned j = 0; j < sz; ++j) {
|
||||
out_bits.push_back(m().mk_ite(x, out1[j].get(), out2[j].get()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
numeral n_a, n_b;
|
||||
SASSERT(i == sz && !is_a);
|
||||
VERIFY(is_numeral(sz, a_bits.c_ptr(), n_a));
|
||||
VERIFY(is_numeral(sz, b_bits.c_ptr(), n_b));
|
||||
n_a *= n_b;
|
||||
num2bits(n_a, sz, out_bits);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Cfg>
|
||||
bool bit_blaster_tpl<Cfg>::mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) {
|
||||
numeral n_a;
|
||||
if (!is_numeral(sz, a_bits, n_a)) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(out_bits.empty());
|
||||
|
||||
if (mk_const_case_multiplier(sz, a_bits, b_bits, out_bits)) {
|
||||
return true;
|
||||
}
|
||||
if (!m_use_bcm) {
|
||||
return false;
|
||||
}
|
||||
expr_ref_vector minus_b_bits(m()), tmp(m());
|
||||
mk_neg(sz, b_bits, minus_b_bits);
|
||||
|
||||
|
@ -1255,4 +1310,6 @@ void bit_blaster_tpl<Cfg>::mk_const_multiplier(unsigned sz, expr * const * a_bit
|
|||
|
||||
TRACE("bit_blaster_tpl_booth", for (unsigned i=0; i<out_bits.size(); i++)
|
||||
tout << "Booth encoding: " << mk_pp(out_bits[i].get(), m()) << "\n"; );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1968,7 +1968,63 @@ void bv_rewriter::mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & r
|
|||
result = m().mk_eq(t1, m_util.mk_bv_sub(c, t2));
|
||||
}
|
||||
|
||||
#include "ast_pp.h"
|
||||
|
||||
bool bv_rewriter::isolate_term(expr* lhs, expr* rhs, expr_ref& result) {
|
||||
if (!m_util.is_numeral(lhs) || !is_add(rhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
if (!m_util.is_numeral(lhs) || !is_add(rhs)) {
|
||||
return false;
|
||||
}
|
||||
unsigned sz = to_app(rhs)->get_num_args();
|
||||
expr_ref t1(m()), t2(m());
|
||||
t1 = to_app(rhs)->get_arg(0);
|
||||
if (sz > 2) {
|
||||
t2 = m().mk_app(get_fid(), OP_BADD, sz-1, to_app(rhs)->get_args()+1);
|
||||
}
|
||||
else {
|
||||
SASSERT(sz == 2);
|
||||
t2 = to_app(rhs)->get_arg(1);
|
||||
}
|
||||
mk_t1_add_t2_eq_c(t1, t2, lhs, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bv_rewriter::is_add_mul_const(expr* e) const {
|
||||
if (!m_util.is_bv_add(e)) {
|
||||
return false;
|
||||
}
|
||||
unsigned num = to_app(e)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * arg = to_app(e)->get_arg(i);
|
||||
expr * c2, * x2;
|
||||
if (m_util.is_numeral(arg))
|
||||
continue;
|
||||
if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2))
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bv_rewriter::is_concat_target(expr* lhs, expr* rhs) const {
|
||||
return
|
||||
m_util.is_concat(lhs) && (is_concat_split_target(rhs) || has_numeral(to_app(lhs))) ||
|
||||
m_util.is_concat(rhs) && (is_concat_split_target(lhs) || has_numeral(to_app(rhs)));
|
||||
}
|
||||
|
||||
bool bv_rewriter::has_numeral(app* a) const {
|
||||
for (unsigned i = 0; i < a->get_num_args(); ++i) {
|
||||
if (is_numeral(a->get_arg(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
|
||||
expr * c, * x;
|
||||
numeral c_val, c_inv_val;
|
||||
unsigned sz;
|
||||
|
@ -2001,24 +2057,30 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
|
||||
// c * x = t_1 + ... + t_n
|
||||
// and t_i's have non-unary coefficients (this condition is used to make sure we are actually reducing the number of multipliers).
|
||||
if (m_util.is_bv_add(rhs)) {
|
||||
if (is_add_mul_const(rhs)) {
|
||||
// Potential problem: this simplification may increase the number of adders by reducing the amount of sharing.
|
||||
unsigned num = to_app(rhs)->get_num_args();
|
||||
unsigned i;
|
||||
for (i = 0; i < num; i++) {
|
||||
expr * arg = to_app(rhs)->get_arg(i);
|
||||
expr * c2, * x2;
|
||||
if (m_util.is_numeral(arg))
|
||||
continue;
|
||||
if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (i == num) {
|
||||
result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs));
|
||||
return BR_REWRITE2;
|
||||
result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
if (m_util.is_numeral(lhs, c_val, sz) && is_add_mul_const(rhs)) {
|
||||
unsigned sz = to_app(rhs)->get_num_args();
|
||||
unsigned i = 0;
|
||||
expr* c2, *x2;
|
||||
numeral c2_val, c2_inv_val;
|
||||
bool found = false;
|
||||
for (; !found && i < sz; ++i) {
|
||||
expr* arg = to_app(rhs)->get_arg(i);
|
||||
if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2, c2_val, sz) &&
|
||||
m_util.mult_inverse(c2_val, sz, c2_inv_val)) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
result = m().mk_eq(m_util.mk_numeral(c2_inv_val*c_val, sz),
|
||||
m_util.mk_bv_mul(m_util.mk_numeral(c2_inv_val, sz), rhs));
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
@ -2065,9 +2127,10 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
return st;
|
||||
}
|
||||
|
||||
expr_ref new_lhs(m());
|
||||
expr_ref new_rhs(m());
|
||||
|
||||
if (m_util.is_bv_add(lhs) || m_util.is_bv_mul(lhs) || m_util.is_bv_add(rhs) || m_util.is_bv_mul(rhs)) {
|
||||
expr_ref new_lhs(m());
|
||||
expr_ref new_rhs(m());
|
||||
st = cancel_monomials(lhs, rhs, false, new_lhs, new_rhs);
|
||||
if (st != BR_FAILED) {
|
||||
if (is_numeral(new_lhs) && is_numeral(new_rhs)) {
|
||||
|
@ -2080,28 +2143,27 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
new_rhs = rhs;
|
||||
}
|
||||
|
||||
lhs = new_lhs;
|
||||
rhs = new_rhs;
|
||||
// Try to rewrite t1 + t2 = c --> t1 = c - t2
|
||||
// Reason: it is much cheaper to bit-blast.
|
||||
expr * t1, * t2;
|
||||
if (m_util.is_bv_add(new_lhs, t1, t2) && is_numeral(new_rhs)) {
|
||||
mk_t1_add_t2_eq_c(t1, t2, new_rhs, result);
|
||||
if (isolate_term(lhs, rhs, result)) {
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
if (m_util.is_bv_add(new_rhs, t1, t2) && is_numeral(new_lhs)) {
|
||||
mk_t1_add_t2_eq_c(t1, t2, new_lhs, result);
|
||||
return BR_REWRITE2;
|
||||
if (is_concat_target(lhs, rhs)) {
|
||||
return mk_eq_concat(lhs, rhs, result);
|
||||
}
|
||||
|
||||
|
||||
if (st != BR_FAILED) {
|
||||
result = m().mk_eq(new_lhs, new_rhs);
|
||||
result = m().mk_eq(lhs, rhs);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_util.is_concat(lhs) && is_concat_split_target(rhs)) ||
|
||||
(m_util.is_concat(rhs) && is_concat_split_target(lhs)))
|
||||
if (is_concat_target(lhs, rhs)) {
|
||||
return mk_eq_concat(lhs, rhs, result);
|
||||
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
result = m().mk_eq(lhs, rhs);
|
||||
return BR_DONE;
|
||||
|
|
|
@ -137,6 +137,10 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
|
|||
bool is_concat_split_target(expr * t) const;
|
||||
|
||||
br_status mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result);
|
||||
bool is_add_mul_const(expr* e) const;
|
||||
bool isolate_term(expr* lhs, expr* rhs, expr_ref & result);
|
||||
bool has_numeral(app* e) const;
|
||||
bool is_concat_target(expr* lhs, expr* rhs) const;
|
||||
|
||||
void updt_local_params(params_ref const & p);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue