mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 18:05:21 +00:00
1636 lines
69 KiB
C++
1636 lines
69 KiB
C++
/*++
|
|
Copyright (c) 2006 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
theory_bv.cpp
|
|
|
|
Abstract:
|
|
|
|
<abstract>
|
|
|
|
Author:
|
|
|
|
Leonardo de Moura (leonardo) 2008-06-06.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include"smt_context.h"
|
|
#include"theory_bv.h"
|
|
#include"ast_ll_pp.h"
|
|
#include"ast_pp.h"
|
|
#include"smt_model_generator.h"
|
|
#include"stats.h"
|
|
|
|
|
|
namespace smt {
|
|
|
|
void theory_bv::init(context * ctx) {
|
|
theory::init(ctx);
|
|
m_simplifier = &(ctx->get_simplifier());
|
|
}
|
|
|
|
theory_var theory_bv::mk_var(enode * n) {
|
|
theory_var r = theory::mk_var(n);
|
|
m_find.mk_var();
|
|
m_bits.push_back(literal_vector());
|
|
m_wpos.push_back(0);
|
|
m_zero_one_bits.push_back(zero_one_bits());
|
|
get_context().attach_th_var(n, this, r);
|
|
return r;
|
|
}
|
|
|
|
app * theory_bv::mk_bit2bool(app * bv, unsigned idx) {
|
|
parameter p(idx);
|
|
expr * args[1] = {bv};
|
|
return get_manager().mk_app(get_id(), OP_BIT2BOOL, 1, &p, 1, args);
|
|
}
|
|
|
|
void theory_bv::mk_bits(theory_var v) {
|
|
enode * n = get_enode(v);
|
|
app * owner = n->get_owner();
|
|
unsigned bv_size = get_bv_size(n);
|
|
context & ctx = get_context();
|
|
literal_vector & bits = m_bits[v];
|
|
for (unsigned i = 0; i < bv_size; i++) {
|
|
app * bit = mk_bit2bool(owner, i);
|
|
ctx.internalize(bit, true);
|
|
bool_var b = ctx.get_bool_var(bit);
|
|
bits.push_back(literal(b));
|
|
}
|
|
}
|
|
|
|
class mk_atom_trail : public trail<theory_bv> {
|
|
bool_var m_var;
|
|
public:
|
|
mk_atom_trail(bool_var v):m_var(v) {}
|
|
virtual void undo(theory_bv & th) {
|
|
theory_bv::atom * a = th.get_bv2a(m_var);
|
|
a->~atom();
|
|
th.erase_bv2a(m_var);
|
|
}
|
|
};
|
|
|
|
void theory_bv::mk_bit2bool(app * n) {
|
|
context & ctx = get_context();
|
|
SASSERT(!ctx.b_internalized(n));
|
|
if (!ctx.e_internalized(n->get_arg(0))) {
|
|
// This may happen if bit2bool(x) is in a conflict
|
|
// clause that is being reinitialized, and x was not reinitialized
|
|
// yet.
|
|
// So, we internalize x (i.e., n->get_arg(0))
|
|
expr * first_arg = n->get_arg(0);
|
|
ctx.internalize(first_arg, false);
|
|
SASSERT(ctx.e_internalized(first_arg));
|
|
// In most cases, when x is internalized, its bits are created.
|
|
// They are created because x is a bit-vector operation or apply_sort_cnstr is invoked.
|
|
// However, there is an exception. The method apply_sort_cnstr is not invoked for ite-terms.
|
|
// So, I execute get_var on the enode attached to first_arg.
|
|
// This will force a theory variable to be created if it does not already exist.
|
|
// This will also force the creation of all bits for x.
|
|
enode * first_arg_enode = ctx.get_enode(first_arg);
|
|
get_var(first_arg_enode);
|
|
SASSERT(ctx.b_internalized(n));
|
|
}
|
|
else {
|
|
enode * arg = ctx.get_enode(n->get_arg(0));
|
|
// The argument was already internalized, but it may not have a theory variable associated with it.
|
|
// For example, for ite-terms the method apply_sort_cnstr is not invoked.
|
|
// See comment in the then-branch.
|
|
theory_var v_arg = arg->get_th_var(get_id());
|
|
if (v_arg == null_theory_var) {
|
|
// The method get_var will create a theory variable for arg.
|
|
// As a side-effect the bits for arg will also be created.
|
|
get_var(arg);
|
|
SASSERT(ctx.b_internalized(n));
|
|
}
|
|
else {
|
|
SASSERT(v_arg != null_theory_var);
|
|
bool_var bv = ctx.mk_bool_var(n);
|
|
ctx.set_var_theory(bv, get_id());
|
|
bit_atom * a = new (get_region()) bit_atom();
|
|
insert_bv2a(bv, a);
|
|
m_trail_stack.push(mk_atom_trail(bv));
|
|
unsigned idx = n->get_decl()->get_parameter(0).get_int();
|
|
SASSERT(a->m_occs == 0);
|
|
a->m_occs = new (get_region()) var_pos_occ(v_arg, idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
void theory_bv::process_args(app * n) {
|
|
context & ctx = get_context();
|
|
unsigned num_args = n->get_num_args();
|
|
for (unsigned i = 0; i < num_args; i++)
|
|
ctx.internalize(n->get_arg(i), false);
|
|
}
|
|
|
|
enode * theory_bv::mk_enode(app * n) {
|
|
context & ctx = get_context();
|
|
enode * e;
|
|
if (ctx.e_internalized(n)) {
|
|
e = ctx.get_enode(n);
|
|
}
|
|
else {
|
|
e = ctx.mk_enode(n, !m_params.m_bv_reflect, false, m_params.m_bv_cc);
|
|
mk_var(e);
|
|
}
|
|
SASSERT(e->get_th_var(get_id()) != null_theory_var);
|
|
return e;
|
|
}
|
|
|
|
theory_var theory_bv::get_var(enode * n) {
|
|
theory_var v = n->get_th_var(get_id());
|
|
if (v == null_theory_var) {
|
|
v = mk_var(n);
|
|
mk_bits(v);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
enode * theory_bv::get_arg(enode * n, unsigned idx) {
|
|
if (m_params.m_bv_reflect) {
|
|
return n->get_arg(idx);
|
|
}
|
|
else {
|
|
context & ctx = get_context();
|
|
app * arg = to_app(n->get_owner()->get_arg(idx));
|
|
SASSERT(ctx.e_internalized(arg));
|
|
return ctx.get_enode(arg);
|
|
}
|
|
}
|
|
|
|
inline theory_var theory_bv::get_arg_var(enode * n, unsigned idx) {
|
|
return get_var(get_arg(n, idx));
|
|
}
|
|
|
|
void theory_bv::get_bits(theory_var v, expr_ref_vector & r) {
|
|
context & ctx = get_context();
|
|
literal_vector & bits = m_bits[v];
|
|
literal_vector::const_iterator it = bits.begin();
|
|
literal_vector::const_iterator end = bits.end();
|
|
for (; it != end; ++it) {
|
|
expr_ref l(get_manager());
|
|
ctx.literal2expr(*it, l);
|
|
r.push_back(l);
|
|
}
|
|
}
|
|
|
|
inline void theory_bv::get_bits(enode * n, expr_ref_vector & r) {
|
|
get_bits(get_var(n), r);
|
|
}
|
|
|
|
inline void theory_bv::get_arg_bits(enode * n, unsigned idx, expr_ref_vector & r) {
|
|
get_bits(get_arg_var(n, idx), r);
|
|
}
|
|
|
|
inline void theory_bv::get_arg_bits(app * n, unsigned idx, expr_ref_vector & r) {
|
|
context & ctx = get_context();
|
|
app * arg = to_app(n->get_arg(idx));
|
|
SASSERT(ctx.e_internalized(arg));
|
|
get_bits(ctx.get_enode(arg), r);
|
|
}
|
|
|
|
class add_var_pos_trail : public trail<theory_bv> {
|
|
theory_bv::bit_atom * m_atom;
|
|
public:
|
|
add_var_pos_trail(theory_bv::bit_atom * a):m_atom(a) {}
|
|
virtual void undo(theory_bv & th) {
|
|
SASSERT(m_atom->m_occs);
|
|
m_atom->m_occs = m_atom->m_occs->m_next;
|
|
}
|
|
};
|
|
|
|
/**
|
|
\brief v1[idx] = ~v2[idx], then v1 /= v2 is a theory axiom.
|
|
*/
|
|
void theory_bv::mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx) {
|
|
SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]);
|
|
TRACE("bv_diseq_axiom", tout << "found new diseq axiom\n"; display_var(tout, v1); display_var(tout, v2););
|
|
// found new disequality
|
|
m_stats.m_num_diseq_static++;
|
|
enode * e1 = get_enode(v1);
|
|
enode * e2 = get_enode(v2);
|
|
literal l = ~(mk_eq(e1->get_owner(), e2->get_owner(), true));
|
|
context & ctx = get_context();
|
|
ctx.mk_th_axiom(get_id(), 1, &l);
|
|
if (ctx.relevancy()) {
|
|
expr * eq = ctx.bool_var2expr(l.var());
|
|
relevancy_eh * eh = ctx.mk_relevancy_eh(pair_relevancy_eh(e1->get_owner(), e2->get_owner(), eq));
|
|
ctx.add_relevancy_eh(e1->get_owner(), eh);
|
|
ctx.add_relevancy_eh(e2->get_owner(), eh);
|
|
}
|
|
}
|
|
|
|
void theory_bv::register_true_false_bit(theory_var v, unsigned idx) {
|
|
SASSERT(m_bits[v][idx] == true_literal || m_bits[v][idx] == false_literal);
|
|
bool is_true = (m_bits[v][idx] == true_literal);
|
|
zero_one_bits & bits = m_zero_one_bits[v];
|
|
bits.push_back(zero_one_bit(v, idx, is_true));
|
|
}
|
|
|
|
/**
|
|
\brief v[idx] = ~v'[idx], then v /= v' is a theory axiom.
|
|
*/
|
|
void theory_bv::find_new_diseq_axioms(var_pos_occ * occs, theory_var v, unsigned idx) {
|
|
literal l = m_bits[v][idx];
|
|
l.neg();
|
|
while (occs) {
|
|
theory_var v2 = occs->m_var;
|
|
unsigned idx2 = occs->m_idx;
|
|
if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v))
|
|
mk_new_diseq_axiom(v, v2, idx);
|
|
occs = occs->m_next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
\brief Add bit l to the given variable.
|
|
*/
|
|
void theory_bv::add_bit(theory_var v, literal l) {
|
|
context & ctx = get_context();
|
|
literal_vector & bits = m_bits[v];
|
|
unsigned idx = bits.size();
|
|
bits.push_back(l);
|
|
if (l.var() == true_bool_var) {
|
|
register_true_false_bit(v, idx);
|
|
}
|
|
else {
|
|
theory_id th_id = ctx.get_var_theory(l.var());
|
|
if (th_id == get_id()) {
|
|
atom * a = get_bv2a(l.var());
|
|
SASSERT(a && a->is_bit());
|
|
bit_atom * b = static_cast<bit_atom*>(a);
|
|
find_new_diseq_axioms(b->m_occs, v, idx);
|
|
m_trail_stack.push(add_var_pos_trail(b));
|
|
b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs);
|
|
}
|
|
else {
|
|
SASSERT(th_id == null_theory_id);
|
|
ctx.set_var_theory(l.var(), get_id());
|
|
SASSERT(ctx.get_var_theory(l.var()) == get_id());
|
|
bit_atom * b = new (get_region()) bit_atom();
|
|
insert_bv2a(l.var(), b);
|
|
m_trail_stack.push(mk_atom_trail(l.var()));
|
|
SASSERT(b->m_occs == 0);
|
|
b->m_occs = new (get_region()) var_pos_occ(v, idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
void theory_bv::simplify_bit(expr * s, expr_ref & r) {
|
|
// proof_ref p(get_manager());
|
|
// if (get_context().at_base_level())
|
|
// m_simplifier->operator()(s, r, p);
|
|
// else
|
|
r = s;
|
|
}
|
|
|
|
void theory_bv::init_bits(enode * n, expr_ref_vector const & bits) {
|
|
context & ctx = get_context();
|
|
ast_manager & m = get_manager();
|
|
theory_var v = n->get_th_var(get_id());
|
|
SASSERT(v != null_theory_var);
|
|
unsigned sz = bits.size();
|
|
SASSERT(get_bv_size(n) == sz);
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
expr * bit = bits.get(i);
|
|
expr_ref s_bit(m);
|
|
simplify_bit(bit, s_bit);
|
|
ctx.internalize(s_bit, true);
|
|
literal l = ctx.get_literal(s_bit.get());
|
|
TRACE("init_bits", tout << "bit " << i << " of #" << n->get_owner_id() << "\n" << mk_ll_pp(s_bit, m) << "\n";);
|
|
add_bit(v, l);
|
|
}
|
|
find_wpos(v);
|
|
}
|
|
|
|
/**
|
|
\brief Find an unassigned bit for m_wpos[v], if such bit cannot be found invoke fixed_var_eh
|
|
*/
|
|
void theory_bv::find_wpos(theory_var v) {
|
|
context & ctx = get_context();
|
|
literal_vector const & bits = m_bits[v];
|
|
unsigned sz = bits.size();
|
|
unsigned & wpos = m_wpos[v];
|
|
unsigned init = wpos;
|
|
for (; wpos < sz; wpos++) {
|
|
TRACE("find_wpos", tout << "curr bit: " << bits[wpos] << "\n";);
|
|
if (ctx.get_assignment(bits[wpos]) == l_undef) {
|
|
TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";);
|
|
return;
|
|
}
|
|
}
|
|
wpos = 0;
|
|
for (; wpos < init; wpos++) {
|
|
if (ctx.get_assignment(bits[wpos]) == l_undef) {
|
|
TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";);
|
|
return;
|
|
}
|
|
}
|
|
TRACE("find_wpos", tout << "v" << v << " is a fixed variable.\n";);
|
|
fixed_var_eh(v);
|
|
}
|
|
|
|
class fixed_eq_justification : public justification {
|
|
theory_bv & m_th;
|
|
theory_var m_var1;
|
|
theory_var m_var2;
|
|
|
|
void mark_bits(conflict_resolution & cr, literal_vector const & bits) {
|
|
context & ctx = cr.get_context();
|
|
literal_vector::const_iterator it = bits.begin();
|
|
literal_vector::const_iterator end = bits.end();
|
|
for (; it != end; ++it) {
|
|
if (it->var() != true_bool_var) {
|
|
if (ctx.get_assignment(*it) == l_true)
|
|
cr.mark_literal(*it);
|
|
else
|
|
cr.mark_literal(~(*it));
|
|
}
|
|
}
|
|
}
|
|
|
|
void get_proof(conflict_resolution & cr, literal l, ptr_buffer<proof> & prs, bool & visited) {
|
|
if (l.var() == true_bool_var)
|
|
return;
|
|
proof * pr = 0;
|
|
if (cr.get_context().get_assignment(l) == l_true)
|
|
pr = cr.get_proof(l);
|
|
else
|
|
pr = cr.get_proof(~l);
|
|
if (pr)
|
|
prs.push_back(pr);
|
|
else
|
|
visited = false;
|
|
}
|
|
|
|
public:
|
|
fixed_eq_justification(theory_bv & th, theory_var v1, theory_var v2):
|
|
m_th(th), m_var1(v1), m_var2(v2) {
|
|
}
|
|
|
|
virtual void get_antecedents(conflict_resolution & cr) {
|
|
mark_bits(cr, m_th.m_bits[m_var1]);
|
|
mark_bits(cr, m_th.m_bits[m_var2]);
|
|
}
|
|
|
|
virtual proof * mk_proof(conflict_resolution & cr) {
|
|
ptr_buffer<proof> prs;
|
|
context & ctx = cr.get_context();
|
|
bool visited = true;
|
|
literal_vector const & bits1 = m_th.m_bits[m_var1];
|
|
literal_vector const & bits2 = m_th.m_bits[m_var2];
|
|
literal_vector::const_iterator it1 = bits1.begin();
|
|
literal_vector::const_iterator it2 = bits2.begin();
|
|
literal_vector::const_iterator end1 = bits1.end();
|
|
for (; it1 != end1; ++it1, ++it2) {
|
|
get_proof(cr, *it1, prs, visited);
|
|
get_proof(cr, *it2, prs, visited);
|
|
}
|
|
if (!visited)
|
|
return 0;
|
|
expr * fact = ctx.mk_eq_atom(m_th.get_enode(m_var1)->get_owner(), m_th.get_enode(m_var2)->get_owner());
|
|
ast_manager & m = ctx.get_manager();
|
|
return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr());
|
|
}
|
|
|
|
virtual theory_id get_from_theory() const {
|
|
return m_th.get_id();
|
|
}
|
|
|
|
virtual char const * get_name() const { return "bv-fixed-eq"; }
|
|
|
|
};
|
|
|
|
void theory_bv::fixed_var_eh(theory_var v) {
|
|
numeral val;
|
|
bool r = get_fixed_value(v, val);
|
|
SASSERT(r);
|
|
unsigned sz = get_bv_size(v);
|
|
value_sort_pair key(val, sz);
|
|
theory_var v2;
|
|
if (m_fixed_var_table.find(key, v2)) {
|
|
numeral val2;
|
|
if (v2 < static_cast<int>(get_num_vars()) && is_bv(v2) &&
|
|
get_bv_size(v2) == sz && get_fixed_value(v2, val2) && val == val2) {
|
|
if (get_enode(v)->get_root() != get_enode(v2)->get_root()) {
|
|
SASSERT(get_bv_size(v) == get_bv_size(v2));
|
|
context & ctx = get_context();
|
|
justification * js = ctx.mk_justification(fixed_eq_justification(*this, v, v2));
|
|
TRACE("fixed_var_eh", tout << "detected equality: v" << v << " = v" << v2 << "\n";
|
|
display_var(tout, v);
|
|
display_var(tout, v2););
|
|
m_stats.m_num_th2core_eq++;
|
|
ctx.assign_eq(get_enode(v), get_enode(v2), eq_justification(js));
|
|
}
|
|
}
|
|
else {
|
|
// the original fixed variable v2 was deleted or it is not fixed anymore.
|
|
m_fixed_var_table.erase(key);
|
|
m_fixed_var_table.insert(key, v);
|
|
}
|
|
}
|
|
else {
|
|
m_fixed_var_table.insert(key, v);
|
|
}
|
|
}
|
|
|
|
bool theory_bv::get_fixed_value(theory_var v, numeral & result) const {
|
|
context & ctx = get_context();
|
|
result.reset();
|
|
literal_vector const & bits = m_bits[v];
|
|
literal_vector::const_iterator it = bits.begin();
|
|
literal_vector::const_iterator end = bits.end();
|
|
for (unsigned i = 0; it != end; ++it, ++i) {
|
|
switch (ctx.get_assignment(*it)) {
|
|
case l_false: break;
|
|
case l_undef: return false;
|
|
case l_true: result += m_bb.power(i); break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool theory_bv::get_fixed_value(app* x, numeral & result) const {
|
|
context& ctx = get_context();
|
|
if (!ctx.e_internalized(x)) return false;
|
|
enode * e = ctx.get_enode(x);
|
|
theory_var v = e->get_th_var(get_id());
|
|
return get_fixed_value(v, result);
|
|
}
|
|
|
|
|
|
void theory_bv::internalize_num(app * n) {
|
|
SASSERT(!get_context().e_internalized(n));
|
|
ast_manager & m = get_manager();
|
|
numeral val;
|
|
unsigned sz;
|
|
m_util.is_numeral(n, val, sz);
|
|
enode * e = mk_enode(n);
|
|
// internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant.
|
|
// e->mark_as_interpreted();
|
|
theory_var v = e->get_th_var(get_id());
|
|
expr_ref_vector bits(m);
|
|
m_bb.num2bits(val, sz, bits);
|
|
SASSERT(bits.size() == sz);
|
|
literal_vector & c_bits = m_bits[v];
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
expr * l = bits.get(i);
|
|
if (m.is_true(l)) {
|
|
c_bits.push_back(true_literal);
|
|
}
|
|
else {
|
|
SASSERT(m.is_false(l));
|
|
c_bits.push_back(false_literal);
|
|
}
|
|
register_true_false_bit(v, i);
|
|
}
|
|
fixed_var_eh(v);
|
|
}
|
|
|
|
void theory_bv::internalize_mkbv(app* n) {
|
|
ast_manager& m = get_manager();
|
|
expr_ref_vector bits(m);
|
|
process_args(n);
|
|
enode * e = mk_enode(n);
|
|
bits.append(n->get_num_args(), n->get_args());
|
|
init_bits(e, bits);
|
|
}
|
|
|
|
void theory_bv::internalize_bv2int(app* n) {
|
|
SASSERT(!get_context().e_internalized(n));
|
|
ast_manager & m = get_manager();
|
|
context& ctx = get_context();
|
|
TRACE("bv", tout << mk_bounded_pp(n, m) << "\n";);
|
|
process_args(n);
|
|
mk_enode(n);
|
|
if (!ctx.relevancy()) {
|
|
assert_bv2int_axiom(n);
|
|
}
|
|
}
|
|
|
|
|
|
void theory_bv::assert_bv2int_axiom(app * n) {
|
|
//
|
|
// create the axiom:
|
|
// n = bv2int(k) = ite(bit2bool(k[sz-1],2^{sz-1},0) + ... + ite(bit2bool(k[0],1,0))
|
|
//
|
|
SASSERT(get_context().e_internalized(n));
|
|
SASSERT(m_util.is_bv2int(n));
|
|
ast_manager & m = get_manager();
|
|
TRACE("bv2int_bug", tout << "bv2int:\n" << mk_pp(n, m) << "\n";);
|
|
context & ctx = get_context();
|
|
sort * int_sort = m.get_sort(n);
|
|
app * k = to_app(n->get_arg(0));
|
|
SASSERT(m_util.is_bv_sort(m.get_sort(k)));
|
|
expr_ref_vector k_bits(m);
|
|
enode * k_enode = mk_enode(k);
|
|
get_bits(k_enode, k_bits);
|
|
unsigned sz = m_util.get_bv_size(k);
|
|
expr_ref_vector args(m);
|
|
expr_ref zero(m_autil.mk_numeral(numeral(0), int_sort), m);
|
|
numeral num(1);
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
// Remark: A previous version of this method was using
|
|
//
|
|
// expr* b = mk_bit2bool(k,i);
|
|
//
|
|
// This is not correct. The predicate bit2bool is an
|
|
// internal construct, and it was not meant for building
|
|
// axioms directly. It is used to represent the bits of a
|
|
// constant, and in some cases the bits of a complicated
|
|
// bit-vector expression. In most cases, the bits of a
|
|
// composite bit-vector expression T are just boolean
|
|
// combinations of bit2bool atoms of the bit-vector
|
|
// constants contained in T. So, instead of using
|
|
// mk_bit2bool to access a particular bit of T, we should
|
|
// use the method get_bits.
|
|
//
|
|
expr * b = k_bits.get(i);
|
|
expr_ref n(m_autil.mk_numeral(num, int_sort), m);
|
|
args.push_back(m.mk_ite(b, n, zero));
|
|
num *= numeral(2);
|
|
}
|
|
expr_ref sum(m);
|
|
arith_simp().mk_add(sz, args.c_ptr(), sum);
|
|
TRACE("bv",
|
|
tout << mk_pp(n, m) << "\n";
|
|
tout << mk_pp(sum, m) << "\n";
|
|
);
|
|
|
|
literal l(mk_eq(n, sum, false));
|
|
|
|
ctx.mark_as_relevant(l);
|
|
ctx.mk_th_axiom(get_id(), 1, &l);
|
|
}
|
|
|
|
void theory_bv::internalize_int2bv(app* n) {
|
|
SASSERT(!get_context().e_internalized(n));
|
|
SASSERT(n->get_num_args() == 1);
|
|
context& ctx = get_context();
|
|
process_args(n);
|
|
mk_enode(n);
|
|
mk_bits(ctx.get_enode(n)->get_th_var(get_id()));
|
|
if (!ctx.relevancy()) {
|
|
assert_int2bv_axiom(n);
|
|
}
|
|
}
|
|
|
|
void theory_bv::assert_int2bv_axiom(app* n) {
|
|
//
|
|
// create the axiom:
|
|
// bv2int(n) = e mod 2^bit_width
|
|
//
|
|
// where n = int2bv(e)
|
|
//
|
|
SASSERT(get_context().e_internalized(n));
|
|
SASSERT(m_util.is_int2bv(n));
|
|
ast_manager & m = get_manager();
|
|
context& ctx = get_context();
|
|
|
|
parameter param(m_autil.mk_int());
|
|
expr* n_expr = n;
|
|
expr* lhs = m.mk_app(get_id(), OP_BV2INT, 1, ¶m, 1, &n_expr);
|
|
unsigned sz = m_util.get_bv_size(n);
|
|
numeral mod = power(numeral(2), sz);
|
|
expr* rhs = m_autil.mk_mod(n->get_arg(0), m_autil.mk_numeral(mod, true));
|
|
|
|
literal l(mk_eq(lhs, rhs, false));
|
|
ctx.mark_as_relevant(l);
|
|
ctx.mk_th_axiom(get_id(), 1, &l);
|
|
|
|
TRACE("bv",
|
|
tout << mk_pp(lhs, m) << " == \n";
|
|
tout << mk_pp(rhs, m) << "\n";
|
|
);
|
|
}
|
|
|
|
|
|
#define MK_UNARY(NAME, BLAST_OP) \
|
|
void theory_bv::NAME(app * n) { \
|
|
SASSERT(!get_context().e_internalized(n)); \
|
|
SASSERT(n->get_num_args() == 1); \
|
|
process_args(n); \
|
|
ast_manager & m = get_manager(); \
|
|
enode * e = mk_enode(n); \
|
|
expr_ref_vector arg1_bits(m), bits(m); \
|
|
get_arg_bits(e, 0, arg1_bits); \
|
|
m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), bits); \
|
|
init_bits(e, bits); \
|
|
}
|
|
|
|
#define MK_BINARY(NAME, BLAST_OP) \
|
|
void theory_bv::NAME(app * n) { \
|
|
SASSERT(!get_context().e_internalized(n)); \
|
|
SASSERT(n->get_num_args() == 2); \
|
|
process_args(n); \
|
|
ast_manager & m = get_manager(); \
|
|
enode * e = mk_enode(n); \
|
|
expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); \
|
|
get_arg_bits(e, 0, arg1_bits); \
|
|
get_arg_bits(e, 1, arg2_bits); \
|
|
SASSERT(arg1_bits.size() == arg2_bits.size()); \
|
|
m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits); \
|
|
init_bits(e, bits); \
|
|
}
|
|
|
|
|
|
#define MK_AC_BINARY(NAME, BLAST_OP) \
|
|
void theory_bv::NAME(app * n) { \
|
|
SASSERT(!get_context().e_internalized(n)); \
|
|
SASSERT(n->get_num_args() >= 2); \
|
|
process_args(n); \
|
|
ast_manager & m = get_manager(); \
|
|
enode * e = mk_enode(n); \
|
|
expr_ref_vector arg_bits(m); \
|
|
expr_ref_vector bits(m); \
|
|
expr_ref_vector new_bits(m); \
|
|
unsigned i = n->get_num_args(); \
|
|
--i; \
|
|
get_arg_bits(e, i, bits); \
|
|
while (i > 0) { \
|
|
--i; \
|
|
arg_bits.reset(); \
|
|
get_arg_bits(e, i, arg_bits); \
|
|
SASSERT(arg_bits.size() == bits.size()); \
|
|
new_bits.reset(); \
|
|
m_bb.BLAST_OP(arg_bits.size(), arg_bits.c_ptr(), bits.c_ptr(), new_bits); \
|
|
bits.swap(new_bits); \
|
|
} \
|
|
init_bits(e, bits); \
|
|
}
|
|
|
|
|
|
#define MK_BINARY_COND(NAME, BLAST_OP) \
|
|
void theory_bv::NAME(app * n) { \
|
|
SASSERT(!get_context().e_internalized(n)); \
|
|
SASSERT(n->get_num_args() == 2); \
|
|
process_args(n); \
|
|
ast_manager & m = get_manager(); \
|
|
context& ctx = get_context(); \
|
|
enode * e = mk_enode(n); \
|
|
expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); \
|
|
expr_ref cond(m), s_cond(m); \
|
|
get_arg_bits(e, 0, arg1_bits); \
|
|
get_arg_bits(e, 1, arg2_bits); \
|
|
SASSERT(arg1_bits.size() == arg2_bits.size()); \
|
|
m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits, cond); \
|
|
init_bits(e, bits); \
|
|
simplify_bit(cond, s_cond); \
|
|
ctx.internalize(s_cond, true); \
|
|
literal l(ctx.get_literal(s_cond)); \
|
|
ctx.mark_as_relevant(l); \
|
|
ctx.mk_th_axiom(get_id(), 1, &l); \
|
|
TRACE("bv", tout << mk_pp(cond, get_manager()) << "\n"; tout << l << "\n";); \
|
|
}
|
|
|
|
MK_UNARY(internalize_not, mk_not);
|
|
MK_UNARY(internalize_redand, mk_redand);
|
|
MK_UNARY(internalize_redor, mk_redor);
|
|
|
|
MK_AC_BINARY(internalize_add, mk_adder);
|
|
MK_AC_BINARY(internalize_mul, mk_multiplier);
|
|
MK_BINARY(internalize_udiv, mk_udiv);
|
|
MK_BINARY(internalize_sdiv, mk_sdiv);
|
|
MK_BINARY(internalize_urem, mk_urem);
|
|
MK_BINARY(internalize_srem, mk_srem);
|
|
MK_BINARY(internalize_smod, mk_smod);
|
|
MK_BINARY(internalize_shl, mk_shl);
|
|
MK_BINARY(internalize_lshr, mk_lshr);
|
|
MK_BINARY(internalize_ashr, mk_ashr);
|
|
MK_BINARY(internalize_ext_rotate_left, mk_ext_rotate_left);
|
|
MK_BINARY(internalize_ext_rotate_right, mk_ext_rotate_right);
|
|
MK_AC_BINARY(internalize_and, mk_and);
|
|
MK_AC_BINARY(internalize_or, mk_or);
|
|
MK_AC_BINARY(internalize_xor, mk_xor);
|
|
MK_AC_BINARY(internalize_nand, mk_nand);
|
|
MK_AC_BINARY(internalize_nor, mk_nor);
|
|
MK_AC_BINARY(internalize_xnor, mk_xnor);
|
|
MK_BINARY(internalize_comp, mk_comp);
|
|
|
|
#define MK_PARAMETRIC_UNARY(NAME, BLAST_OP) \
|
|
void theory_bv::NAME(app * n) { \
|
|
SASSERT(!get_context().e_internalized(n)); \
|
|
SASSERT(n->get_num_args() == 1); \
|
|
process_args(n); \
|
|
ast_manager & m = get_manager(); \
|
|
enode * e = mk_enode(n); \
|
|
expr_ref_vector arg1_bits(m), bits(m); \
|
|
get_arg_bits(e, 0, arg1_bits); \
|
|
unsigned param = n->get_decl()->get_parameter(0).get_int(); \
|
|
m_bb.BLAST_OP(arg1_bits.size(), arg1_bits.c_ptr(), param, bits); \
|
|
init_bits(e, bits); \
|
|
}
|
|
|
|
MK_PARAMETRIC_UNARY(internalize_sign_extend, mk_sign_extend);
|
|
MK_PARAMETRIC_UNARY(internalize_zero_extend, mk_zero_extend);
|
|
MK_PARAMETRIC_UNARY(internalize_rotate_left, mk_rotate_left);
|
|
MK_PARAMETRIC_UNARY(internalize_rotate_right, mk_rotate_right);
|
|
|
|
void theory_bv::internalize_concat(app * n) {
|
|
process_args(n);
|
|
enode * e = mk_enode(n);
|
|
theory_var v = e->get_th_var(get_id());
|
|
unsigned num_args = n->get_num_args();
|
|
unsigned i = num_args;
|
|
while (i > 0) {
|
|
i--;
|
|
theory_var arg = get_arg_var(e, i);
|
|
literal_vector::const_iterator it = m_bits[arg].begin();
|
|
literal_vector::const_iterator end = m_bits[arg].end();
|
|
for (; it != end; ++it)
|
|
add_bit(v, *it);
|
|
}
|
|
find_wpos(v);
|
|
}
|
|
|
|
void theory_bv::internalize_extract(app * n) {
|
|
SASSERT(n->get_num_args() == 1);
|
|
process_args(n);
|
|
enode * e = mk_enode(n);
|
|
theory_var v = e->get_th_var(get_id());
|
|
theory_var arg = get_arg_var(e, 0);
|
|
unsigned start = n->get_decl()->get_parameter(1).get_int();
|
|
unsigned end = n->get_decl()->get_parameter(0).get_int();
|
|
SASSERT(start <= end);
|
|
literal_vector & arg_bits = m_bits[arg];
|
|
for (unsigned i = start; i <= end; ++i)
|
|
add_bit(v, arg_bits[i]);
|
|
find_wpos(v);
|
|
}
|
|
|
|
bool theory_bv::internalize_term(app * term) {
|
|
SASSERT(term->get_family_id() == get_family_id());
|
|
TRACE("bv", tout << "internalizing term: " << mk_bounded_pp(term, get_manager()) << "\n";);
|
|
if (approximate_term(term)) {
|
|
return false;
|
|
}
|
|
switch (term->get_decl_kind()) {
|
|
case OP_BV_NUM: internalize_num(term); return true;
|
|
case OP_BADD: internalize_add(term); return true;
|
|
case OP_BMUL: internalize_mul(term); return true;
|
|
case OP_BSDIV_I: internalize_sdiv(term); return true;
|
|
case OP_BUDIV_I: internalize_udiv(term); return true;
|
|
case OP_BSREM_I: internalize_srem(term); return true;
|
|
case OP_BUREM_I: internalize_urem(term); return true;
|
|
case OP_BSMOD_I: internalize_smod(term); return true;
|
|
case OP_BAND: internalize_and(term); return true;
|
|
case OP_BOR: internalize_or(term); return true;
|
|
case OP_BNOT: internalize_not(term); return true;
|
|
case OP_BXOR: internalize_xor(term); return true;
|
|
case OP_BNAND: internalize_nand(term); return true;
|
|
case OP_BNOR: internalize_nor(term); return true;
|
|
case OP_BXNOR: internalize_xnor(term); return true;
|
|
case OP_CONCAT: internalize_concat(term); return true;
|
|
case OP_SIGN_EXT: internalize_sign_extend(term); return true;
|
|
case OP_ZERO_EXT: internalize_zero_extend(term); return true;
|
|
case OP_EXTRACT: internalize_extract(term); return true;
|
|
case OP_BREDOR: internalize_redor(term); return true;
|
|
case OP_BREDAND: internalize_redand(term); return true;
|
|
case OP_BCOMP: internalize_comp(term); return true;
|
|
case OP_BSHL: internalize_shl(term); return true;
|
|
case OP_BLSHR: internalize_lshr(term); return true;
|
|
case OP_BASHR: internalize_ashr(term); return true;
|
|
case OP_ROTATE_LEFT: internalize_rotate_left(term); return true;
|
|
case OP_ROTATE_RIGHT: internalize_rotate_right(term); return true;
|
|
case OP_EXT_ROTATE_LEFT: internalize_ext_rotate_left(term); return true;
|
|
case OP_EXT_ROTATE_RIGHT: internalize_ext_rotate_right(term); return true;
|
|
case OP_BSDIV0: return false;
|
|
case OP_BUDIV0: return false;
|
|
case OP_BSREM0: return false;
|
|
case OP_BUREM0: return false;
|
|
case OP_BSMOD0: return false;
|
|
case OP_MKBV: internalize_mkbv(term); return true;
|
|
case OP_INT2BV:
|
|
if (m_params.m_bv_enable_int2bv2int) {
|
|
internalize_int2bv(term);
|
|
}
|
|
return m_params.m_bv_enable_int2bv2int;
|
|
case OP_BV2INT:
|
|
if (m_params.m_bv_enable_int2bv2int) {
|
|
internalize_bv2int(term);
|
|
}
|
|
return m_params.m_bv_enable_int2bv2int;
|
|
default:
|
|
TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, get_manager()) << "\n";);
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#define MK_NO_OVFL(NAME, OP) \
|
|
void theory_bv::NAME(app *n) { \
|
|
SASSERT(n->get_num_args() == 2); \
|
|
process_args(n); \
|
|
ast_manager & m = get_manager(); \
|
|
context & ctx = get_context(); \
|
|
expr_ref_vector arg1_bits(m), arg2_bits(m); \
|
|
get_arg_bits(n, 0, arg1_bits); \
|
|
get_arg_bits(n, 1, arg2_bits); \
|
|
expr_ref out(m); \
|
|
m_bb.OP(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), out); \
|
|
expr_ref s_out(m); \
|
|
simplify_bit(out, s_out); \
|
|
ctx.internalize(s_out, true); \
|
|
literal def = ctx.get_literal(s_out); \
|
|
literal l(ctx.mk_bool_var(n)); \
|
|
ctx.set_var_theory(l.var(), get_id()); \
|
|
le_atom * a = new (get_region()) le_atom(l, def); /* abuse le_atom */ \
|
|
insert_bv2a(l.var(), a); \
|
|
m_trail_stack.push(mk_atom_trail(l.var())); \
|
|
/* smul_no_overflow and umul_no_overflow are using the le_atom (THIS IS A BIG HACK)... */ \
|
|
/* the connection between the l and def was never realized when */ \
|
|
/* relevancy() is true and m_bv_lazy_le is false (the default configuration). */ \
|
|
/* So, we need to check also the m_bv_lazy_le flag here. */ \
|
|
/* Maybe, we should rename the le_atom to bridge_atom, and m_bv_lazy_le option to m_bv_lazy_bridge. */ \
|
|
if (!ctx.relevancy() || !m_params.m_bv_lazy_le) { \
|
|
ctx.mk_th_axiom(get_id(), l, ~def); \
|
|
ctx.mk_th_axiom(get_id(), ~l, def); \
|
|
} \
|
|
}
|
|
|
|
MK_NO_OVFL(internalize_umul_no_overflow, mk_umul_no_overflow);
|
|
MK_NO_OVFL(internalize_smul_no_overflow, mk_smul_no_overflow);
|
|
MK_NO_OVFL(internalize_smul_no_underflow, mk_smul_no_underflow);
|
|
|
|
template<bool Signed>
|
|
void theory_bv::internalize_le(app * n) {
|
|
SASSERT(n->get_num_args() == 2);
|
|
process_args(n);
|
|
ast_manager & m = get_manager();
|
|
context & ctx = get_context();
|
|
expr_ref_vector arg1_bits(m), arg2_bits(m);
|
|
get_arg_bits(n, 0, arg1_bits);
|
|
get_arg_bits(n, 1, arg2_bits);
|
|
expr_ref le(m);
|
|
if (Signed)
|
|
m_bb.mk_sle(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
|
|
else
|
|
m_bb.mk_ule(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le);
|
|
expr_ref s_le(m);
|
|
simplify_bit(le, s_le);
|
|
ctx.internalize(s_le, true);
|
|
literal def = ctx.get_literal(s_le);
|
|
literal l(ctx.mk_bool_var(n));
|
|
ctx.set_var_theory(l.var(), get_id());
|
|
le_atom * a = new (get_region()) le_atom(l, def);
|
|
insert_bv2a(l.var(), a);
|
|
m_trail_stack.push(mk_atom_trail(l.var()));
|
|
if (!ctx.relevancy() || !m_params.m_bv_lazy_le) {
|
|
ctx.mk_th_axiom(get_id(), l, ~def);
|
|
ctx.mk_th_axiom(get_id(), ~l, def);
|
|
}
|
|
}
|
|
|
|
bool theory_bv::internalize_carry(app * n, bool gate_ctx) {
|
|
context & ctx = get_context();
|
|
ctx.internalize(n->get_arg(0), true);
|
|
ctx.internalize(n->get_arg(1), true);
|
|
ctx.internalize(n->get_arg(2), true);
|
|
bool is_new_var = false;
|
|
bool_var v;
|
|
if (!ctx.b_internalized(n)) {
|
|
is_new_var = true;
|
|
v = ctx.mk_bool_var(n);
|
|
literal r(v);
|
|
literal l1 = ctx.get_literal(n->get_arg(0));
|
|
literal l2 = ctx.get_literal(n->get_arg(1));
|
|
literal l3 = ctx.get_literal(n->get_arg(2));
|
|
ctx.mk_gate_clause(~r, l1, l2);
|
|
ctx.mk_gate_clause(~r, l1, l3);
|
|
ctx.mk_gate_clause(~r, l2, l3);
|
|
ctx.mk_gate_clause( r, ~l1, ~l2);
|
|
ctx.mk_gate_clause( r, ~l1, ~l3);
|
|
ctx.mk_gate_clause( r, ~l2, ~l3);
|
|
}
|
|
else {
|
|
v = ctx.get_bool_var(n);
|
|
}
|
|
|
|
if (!ctx.e_internalized(n) && !gate_ctx) {
|
|
bool suppress_args = true;
|
|
bool merge_tf = !gate_ctx;
|
|
ctx.mk_enode(n, suppress_args, merge_tf, true);
|
|
ctx.set_enode_flag(v, is_new_var);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool theory_bv::internalize_xor3(app * n, bool gate_ctx) {
|
|
context & ctx = get_context();
|
|
ctx.internalize(n->get_arg(0), true);
|
|
ctx.internalize(n->get_arg(1), true);
|
|
ctx.internalize(n->get_arg(2), true);
|
|
bool is_new_var = false;
|
|
bool_var v;
|
|
if (!ctx.b_internalized(n)) {
|
|
is_new_var = true;
|
|
v = ctx.mk_bool_var(n);
|
|
literal r(v);
|
|
literal l1 = ctx.get_literal(n->get_arg(0));
|
|
literal l2 = ctx.get_literal(n->get_arg(1));
|
|
literal l3 = ctx.get_literal(n->get_arg(2));
|
|
ctx.mk_gate_clause(~r, l1, l2, l3);
|
|
ctx.mk_gate_clause(~r, ~l1, ~l2, l3);
|
|
ctx.mk_gate_clause(~r, ~l1, l2, ~l3);
|
|
ctx.mk_gate_clause(~r, l1, ~l2, ~l3);
|
|
ctx.mk_gate_clause( r, ~l1, l2, l3);
|
|
ctx.mk_gate_clause( r, l1, ~l2, l3);
|
|
ctx.mk_gate_clause( r, l1, l2, ~l3);
|
|
ctx.mk_gate_clause( r, ~l1, ~l2, ~l3);
|
|
}
|
|
else {
|
|
v = ctx.get_bool_var(n);
|
|
}
|
|
|
|
if (!ctx.e_internalized(n) && !gate_ctx) {
|
|
bool suppress_args = true;
|
|
bool merge_tf = !gate_ctx;
|
|
ctx.mk_enode(n, suppress_args, merge_tf, true);
|
|
ctx.set_enode_flag(v, is_new_var);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool theory_bv::internalize_atom(app * atom, bool gate_ctx) {
|
|
TRACE("bv", tout << "internalizing atom: " << mk_bounded_pp(atom, get_manager()) << "\n";);
|
|
SASSERT(atom->get_family_id() == get_family_id());
|
|
if (approximate_term(atom)) {
|
|
return false;
|
|
}
|
|
switch (atom->get_decl_kind()) {
|
|
case OP_BIT2BOOL: mk_bit2bool(atom); return true;
|
|
case OP_ULEQ: internalize_le<false>(atom); return true;
|
|
case OP_SLEQ: internalize_le<true>(atom); return true;
|
|
case OP_XOR3: return internalize_xor3(atom, gate_ctx);
|
|
case OP_CARRY: return internalize_carry(atom, gate_ctx);
|
|
case OP_BUMUL_NO_OVFL: internalize_umul_no_overflow(atom); return true;
|
|
case OP_BSMUL_NO_OVFL: internalize_smul_no_overflow(atom); return true;
|
|
case OP_BSMUL_NO_UDFL: internalize_smul_no_underflow(atom); return true;
|
|
default:
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine whether bit-vector expression should be approximated
|
|
// based on the number of bits used by the arguments.
|
|
//
|
|
bool theory_bv::approximate_term(app* n) {
|
|
if (m_params.m_bv_blast_max_size == INT_MAX) {
|
|
return false;
|
|
}
|
|
unsigned num_args = n->get_num_args();
|
|
for (unsigned i = 0; i <= num_args; i++) {
|
|
expr* arg = (i == num_args)?n:n->get_arg(i);
|
|
sort* s = get_manager().get_sort(arg);
|
|
s = get_manager().get_sort(arg);
|
|
if (m_util.is_bv_sort(s) && m_util.get_bv_size(arg) > m_params.m_bv_blast_max_size) {
|
|
if (!m_approximates_large_bvs) {
|
|
TRACE("bv", tout << "found large size bit-vector:\n" << mk_pp(n, get_manager()) << "\n";);
|
|
get_context().push_trail(value_trail<context, bool>(m_approximates_large_bvs));
|
|
m_approximates_large_bvs = true;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
void theory_bv::apply_sort_cnstr(enode * n, sort * s) {
|
|
if (!is_attached_to_var(n) && !approximate_term(n->get_owner())) {
|
|
theory_var v = mk_var(n);
|
|
mk_bits(v);
|
|
}
|
|
}
|
|
|
|
void theory_bv::new_eq_eh(theory_var v1, theory_var v2) {
|
|
TRACE("bv_eq", tout << "new_eq: " << mk_pp(get_enode(v1)->get_owner(), get_manager()) << " = " << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";);
|
|
TRACE("bv", tout << "new_eq_eh v" << v1 << " = v" << v2 <<
|
|
" relevant1: " << get_context().is_relevant(get_enode(v1)) <<
|
|
" relevant2: " << get_context().is_relevant(get_enode(v2)) << "\n";);
|
|
m_find.merge(v1, v2);
|
|
}
|
|
|
|
void theory_bv::new_diseq_eh(theory_var v1, theory_var v2) {
|
|
if (is_bv(v1)) {
|
|
expand_diseq(v1, v2);
|
|
}
|
|
}
|
|
|
|
void theory_bv::expand_diseq(theory_var v1, theory_var v2) {
|
|
SASSERT(get_bv_size(v1) == get_bv_size(v2));
|
|
context & ctx = get_context();
|
|
ast_manager & m = get_manager();
|
|
#ifdef _TRACE
|
|
unsigned num_bool_vars = ctx.get_num_bool_vars();
|
|
#endif
|
|
literal_vector & lits = m_tmp_literals;
|
|
lits.reset();
|
|
lits.push_back(mk_eq(get_enode(v1)->get_owner(), get_enode(v2)->get_owner(), true));
|
|
literal_vector const & bits1 = m_bits[v1];
|
|
literal_vector::const_iterator it1 = bits1.begin();
|
|
literal_vector::const_iterator end1 = bits1.end();
|
|
literal_vector const & bits2 = m_bits[v2];
|
|
literal_vector::const_iterator it2 = bits2.begin();
|
|
for (; it1 != end1; ++it1, ++it2) {
|
|
if (*it1 == ~(*it2))
|
|
return; // static diseq
|
|
}
|
|
it1 = bits1.begin();
|
|
it2 = bits2.begin();
|
|
for (; it1 != end1; ++it1, ++it2) {
|
|
expr_ref l1(m), l2(m), diff(m);
|
|
ctx.literal2expr(*it1, l1);
|
|
ctx.literal2expr(*it2, l2);
|
|
m_bb.mk_xor(l1, l2, diff);
|
|
ctx.internalize(diff, true);
|
|
literal arg = ctx.get_literal(diff);
|
|
lits.push_back(arg);
|
|
}
|
|
m_stats.m_num_diseq_dynamic++;
|
|
ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
|
|
TRACE_CODE({
|
|
static unsigned num = 0;
|
|
static unsigned new_bool_vars = 0;
|
|
new_bool_vars += (ctx.get_num_bool_vars() - num_bool_vars);
|
|
if (num % 1000 == 0)
|
|
TRACE("expand_diseq", tout << "num: " << num << " " << new_bool_vars << "\n";);
|
|
num++;
|
|
});
|
|
}
|
|
|
|
void theory_bv::assign_eh(bool_var v, bool is_true) {
|
|
context & ctx = get_context();
|
|
atom * a = get_bv2a(v);
|
|
TRACE("bv", tout << "assert: v" << v << " #" << ctx.bool_var2expr(v)->get_id() << " is_true: " << is_true << "\n";);
|
|
if (a->is_bit()) {
|
|
// The following optimization is not correct.
|
|
// Boolean variables created for performing bit-blasting are reused.
|
|
// See regression\trevor6.smt for example.
|
|
//
|
|
// if (ctx.has_th_justification(v, get_id())) {
|
|
// TRACE("bv", tout << "has th_justification\n";);
|
|
// return;
|
|
// }
|
|
m_prop_queue.reset();
|
|
bit_atom * b = static_cast<bit_atom*>(a);
|
|
var_pos_occ * curr = b->m_occs;
|
|
while (curr) {
|
|
m_prop_queue.push_back(var_pos(curr->m_var, curr->m_idx));
|
|
curr = curr->m_next;
|
|
}
|
|
TRACE("bv", tout << m_prop_queue.size() << "\n";);
|
|
propagate_bits();
|
|
}
|
|
}
|
|
|
|
void theory_bv::propagate_bits() {
|
|
context & ctx = get_context();
|
|
for (unsigned i = 0; i < m_prop_queue.size(); i++) {
|
|
var_pos const & entry = m_prop_queue[i];
|
|
theory_var v = entry.first;
|
|
unsigned idx = entry.second;
|
|
|
|
if (m_wpos[v] == idx)
|
|
find_wpos(v);
|
|
|
|
|
|
literal_vector & bits = m_bits[v];
|
|
literal bit = bits[idx];
|
|
lbool val = ctx.get_assignment(bit);
|
|
theory_var v2 = next(v);
|
|
TRACE("bv_bit_prop", tout << "propagating #" << get_enode(v)->get_owner_id() << "[" << idx << "] = " << val << "\n";);
|
|
while (v2 != v) {
|
|
literal_vector & bits2 = m_bits[v2];
|
|
literal bit2 = bits2[idx];
|
|
SASSERT(bit != ~bit2);
|
|
lbool val2 = ctx.get_assignment(bit2);
|
|
TRACE("bv_bit_prop", tout << "propagating #" << get_enode(v2)->get_owner_id() << "[" << idx << "] = " << val2 << "\n";);
|
|
if (val != val2) {
|
|
literal antecedent = bit;
|
|
literal consequent = bit2;
|
|
if (val == l_false) {
|
|
antecedent.neg();
|
|
consequent.neg();
|
|
}
|
|
SASSERT(ctx.get_assignment(antecedent) == l_true);
|
|
assign_bit(consequent, v, v2, idx, antecedent, false);
|
|
if (ctx.inconsistent()) {
|
|
TRACE("bv_bit_prop", tout << "inconsistent " << bit << " " << bit2 << "\n";);
|
|
return;
|
|
}
|
|
}
|
|
v2 = next(v2);
|
|
}
|
|
}
|
|
m_prop_queue.reset();
|
|
TRACE("bv_bit_prop", tout << "done propagating\n";);
|
|
}
|
|
|
|
void theory_bv::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) {
|
|
m_stats.m_num_bit2core++;
|
|
context & ctx = get_context();
|
|
SASSERT(ctx.get_assignment(antecedent) == l_true);
|
|
SASSERT(m_bits[v2][idx].var() == consequent.var());
|
|
SASSERT(consequent.var() != antecedent.var());
|
|
TRACE("bv_bit_prop", tout << "assigning: "; ctx.display_literal(tout, consequent);
|
|
tout << " using "; ctx.display_literal(tout, antecedent);
|
|
tout << " #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << " idx: " << idx << "\n";
|
|
tout << "propagate_eqc: " << propagate_eqc << "\n";);
|
|
if (consequent == false_literal) {
|
|
m_stats.m_num_conflicts++;
|
|
ctx.set_conflict(mk_bit_eq_justification(v1, v2, consequent, antecedent));
|
|
}
|
|
else {
|
|
ctx.assign(consequent, mk_bit_eq_justification(v1, v2, consequent, antecedent));
|
|
if (m_wpos[v2] == idx)
|
|
find_wpos(v2);
|
|
// REMARK: bit_eq_justification is marked as a theory_bv justification.
|
|
// Thus, the assignment to consequent will not be notified back to the theory.
|
|
// So, we need to propagate the assignment to other bits.
|
|
bool_var bv = consequent.var();
|
|
atom * a = get_bv2a(bv);
|
|
SASSERT(a->is_bit());
|
|
bit_atom * b = static_cast<bit_atom*>(a);
|
|
var_pos_occ * curr = b->m_occs;
|
|
while (curr) {
|
|
TRACE("assign_bit_bug", tout << "curr->m_var: v" << curr->m_var << ", curr->m_idx: " << curr->m_idx << ", v2: v" << v2 << ", idx: " << idx << "\n";
|
|
tout << "find(curr->m_var): v" << find(curr->m_var) << ", find(v2): v" << find(v2) << "\n";
|
|
tout << "is bit of #" << get_enode(curr->m_var)->get_owner_id() << "\n";
|
|
);
|
|
// If find(curr->m_var) == find(v2) && curr->m_idx == idx and propagate_eqc == false, then
|
|
// this bit will be propagated to the equivalence class of v2 by assign_bit caller.
|
|
if (propagate_eqc || find(curr->m_var) != find(v2) || curr->m_idx != idx)
|
|
m_prop_queue.push_back(var_pos(curr->m_var, curr->m_idx));
|
|
curr = curr->m_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void theory_bv::relevant_eh(app * n) {
|
|
ast_manager & m = get_manager();
|
|
context & ctx = get_context();
|
|
if (m.is_bool(n)) {
|
|
bool_var v = ctx.get_bool_var(n);
|
|
atom * a = get_bv2a(v);
|
|
if (a && !a->is_bit()) {
|
|
le_atom * le = static_cast<le_atom*>(a);
|
|
ctx.mark_as_relevant(le->m_def);
|
|
if (m_params.m_bv_lazy_le) {
|
|
ctx.mk_th_axiom(get_id(), le->m_var, ~le->m_def);
|
|
ctx.mk_th_axiom(get_id(), ~le->m_var, le->m_def);
|
|
}
|
|
}
|
|
}
|
|
else if (m_params.m_bv_enable_int2bv2int && m_util.is_bv2int(n)) {
|
|
ctx.mark_as_relevant(n->get_arg(0));
|
|
assert_bv2int_axiom(n);
|
|
}
|
|
else if (m_params.m_bv_enable_int2bv2int && m_util.is_int2bv(n)) {
|
|
ctx.mark_as_relevant(n->get_arg(0));
|
|
assert_int2bv_axiom(n);
|
|
}
|
|
else if (ctx.e_internalized(n)) {
|
|
enode * e = ctx.get_enode(n);
|
|
theory_var v = e->get_th_var(get_id());
|
|
if (v != null_theory_var) {
|
|
literal_vector & bits = m_bits[v];
|
|
literal_vector::iterator it = bits.begin();
|
|
literal_vector::iterator end = bits.end();
|
|
for (; it != end; ++it)
|
|
ctx.mark_as_relevant(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
void theory_bv::push_scope_eh() {
|
|
theory::push_scope_eh();
|
|
m_trail_stack.push_scope();
|
|
}
|
|
|
|
void theory_bv::pop_scope_eh(unsigned num_scopes) {
|
|
TRACE("bv",tout << num_scopes << "\n";);
|
|
m_trail_stack.pop_scope(num_scopes);
|
|
unsigned num_old_vars = get_old_num_vars(num_scopes);
|
|
m_bits.shrink(num_old_vars);
|
|
m_wpos.shrink(num_old_vars);
|
|
m_zero_one_bits.shrink(num_old_vars);
|
|
theory::pop_scope_eh(num_scopes);
|
|
}
|
|
|
|
final_check_status theory_bv::final_check_eh() {
|
|
SASSERT(check_invariant());
|
|
if (m_approximates_large_bvs) {
|
|
return FC_GIVEUP;
|
|
}
|
|
return FC_DONE;
|
|
}
|
|
|
|
void theory_bv::reset_eh() {
|
|
pop_scope_eh(m_trail_stack.get_num_scopes());
|
|
m_bool_var2atom.reset();
|
|
m_fixed_var_table.reset();
|
|
theory::reset_eh();
|
|
}
|
|
|
|
theory_bv::theory_bv(ast_manager & m, theory_bv_params const & params, bit_blaster_params const & bb_params):
|
|
theory(m.get_family_id("bv")),
|
|
m_params(params),
|
|
m_util(m),
|
|
m_autil(m),
|
|
m_simplifier(0),
|
|
m_bb(m, bb_params),
|
|
m_trail_stack(*this),
|
|
m_find(*this),
|
|
m_approximates_large_bvs(false) {
|
|
}
|
|
|
|
theory_bv::~theory_bv() {
|
|
}
|
|
|
|
void theory_bv::merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {
|
|
TRACE("bv", tout << "merging: #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << "\n";);
|
|
TRACE("bv_bit_prop", tout << "merging: #" << get_enode(v1)->get_owner_id() << " #" << get_enode(v2)->get_owner_id() << "\n";);
|
|
if (!merge_zero_one_bits(r1, r2)) {
|
|
TRACE("bv", tout << "conflict detected\n";);
|
|
return; // conflict was detected
|
|
}
|
|
m_prop_queue.reset();
|
|
context & ctx = get_context();
|
|
literal_vector & bits1 = m_bits[v1];
|
|
literal_vector & bits2 = m_bits[v2];
|
|
SASSERT(bits1.size() == bits2.size());
|
|
unsigned sz = bits1.size();
|
|
bool changed;
|
|
TRACE("bv", tout << "bits size: " << sz << "\n";);
|
|
do {
|
|
// This outerloop is necessary to avoid missing propagation steps.
|
|
// For example, let's assume that bits1 and bits2 contains the following
|
|
// sequence of bits:
|
|
// b4 b3 b2 b1
|
|
// b5 b4 b3 b2
|
|
// Let's also assume that b1 is assigned, and b2, b3, b4, and b5 are not.
|
|
// Only the propagation from b1 to b2 is performed by the first iteration of this
|
|
// loop.
|
|
//
|
|
// In the worst case, we need to execute this loop bits1.size() times.
|
|
//
|
|
// Remark: the assignment to b2 is marked as a bv theory propagation,
|
|
// then it is not notified to the bv theory.
|
|
changed = false;
|
|
for (unsigned idx = 0; idx < sz; idx++) {
|
|
literal bit1 = bits1[idx];
|
|
literal bit2 = bits2[idx];
|
|
CTRACE("bv_bug", bit1 == ~bit2, display_var(tout, v1); display_var(tout, v2); tout << "idx: " << idx << "\n";);
|
|
SASSERT(bit1 != ~bit2);
|
|
lbool val1 = ctx.get_assignment(bit1);
|
|
lbool val2 = ctx.get_assignment(bit2);
|
|
if (val1 == val2)
|
|
continue;
|
|
changed = true;
|
|
if (val1 != l_undef && val2 != l_undef) {
|
|
TRACE("bv", tout << "inconsistent "; display_var(tout, v1); display_var(tout, v2); tout << "idx: " << idx << "\n";);
|
|
}
|
|
if (val1 != l_undef) {
|
|
literal antecedent = bit1;
|
|
literal consequent = bit2;
|
|
if (val1 == l_false) {
|
|
consequent.neg();
|
|
antecedent.neg();
|
|
}
|
|
assign_bit(consequent, v1, v2, idx, antecedent, true);
|
|
}
|
|
else if (val2 != l_undef) {
|
|
literal antecedent = bit2;
|
|
literal consequent = bit1;
|
|
if (val2 == l_false) {
|
|
consequent.neg();
|
|
antecedent.neg();
|
|
}
|
|
assign_bit(consequent, v2, v1, idx, antecedent, true);
|
|
}
|
|
if (ctx.inconsistent())
|
|
return;
|
|
if (val1 != l_undef && val2 != l_undef && val1 != val2) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
}
|
|
}
|
|
while(changed);
|
|
|
|
propagate_bits();
|
|
}
|
|
|
|
bool theory_bv::merge_zero_one_bits(theory_var r1, theory_var r2) {
|
|
zero_one_bits & bits2 = m_zero_one_bits[r2];
|
|
if (bits2.empty())
|
|
return true;
|
|
zero_one_bits & bits1 = m_zero_one_bits[r1];
|
|
unsigned bv_size = get_bv_size(r1);
|
|
SASSERT(bv_size == get_bv_size(r2));
|
|
m_merge_aux[0].reserve(bv_size+1, null_theory_var);
|
|
m_merge_aux[1].reserve(bv_size+1, null_theory_var);
|
|
#define RESET_MERGET_AUX() { \
|
|
zero_one_bits::iterator it = bits1.begin(); \
|
|
zero_one_bits::iterator end = bits1.end(); \
|
|
for (; it != end; ++it) \
|
|
m_merge_aux[it->m_is_true][it->m_idx] = null_theory_var; \
|
|
}
|
|
DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == null_theory_var || m_merge_aux[1][i] == null_theory_var); });
|
|
// save info about bits1
|
|
zero_one_bits::iterator it = bits1.begin();
|
|
zero_one_bits::iterator end = bits1.end();
|
|
for (; it != end; ++it)
|
|
m_merge_aux[it->m_is_true][it->m_idx] = it->m_owner;
|
|
// check if bits2 is consistent with bits1, and copy new bits to bits1
|
|
it = bits2.begin();
|
|
end = bits2.end();
|
|
for (; it != end; ++it) {
|
|
theory_var v2 = it->m_owner;
|
|
theory_var v1 = m_merge_aux[!it->m_is_true][it->m_idx];
|
|
if (v1 != null_theory_var) {
|
|
// conflict was detected ... v1 and v2 have complementary bits
|
|
SASSERT(m_bits[v1][it->m_idx] == ~(m_bits[v2][it->m_idx]));
|
|
mk_new_diseq_axiom(v1, v2, it->m_idx);
|
|
RESET_MERGET_AUX();
|
|
return false;
|
|
}
|
|
if (m_merge_aux[it->m_is_true][it->m_idx] == null_theory_var) {
|
|
// copy missing variable to bits1
|
|
bits1.push_back(*it);
|
|
}
|
|
}
|
|
// reset m_merge_aux vector
|
|
RESET_MERGET_AUX();
|
|
DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == null_theory_var || m_merge_aux[1][i] == null_theory_var); });
|
|
return true;
|
|
}
|
|
|
|
class bit_eq_justification : public justification {
|
|
enode * m_v1;
|
|
enode * m_v2;
|
|
theory_id m_th_id; // TODO: steal 4 bits from each one of the following literas and use them to represent the th_id.
|
|
literal m_consequent;
|
|
literal m_antecedent;
|
|
public:
|
|
bit_eq_justification(theory_id th_id, enode * v1, enode * v2, literal c, literal a):
|
|
m_v1(v1), m_v2(v2), m_th_id(th_id), m_consequent(c), m_antecedent(a) {}
|
|
|
|
virtual void get_antecedents(conflict_resolution & cr) {
|
|
cr.mark_eq(m_v1, m_v2);
|
|
if (m_antecedent.var() != true_bool_var)
|
|
cr.mark_literal(m_antecedent);
|
|
}
|
|
|
|
virtual proof * mk_proof(conflict_resolution & cr) {
|
|
bool visited = true;
|
|
ptr_buffer<proof> prs;
|
|
proof * pr = cr.get_proof(m_v1, m_v2);
|
|
if (pr)
|
|
prs.push_back(pr);
|
|
else
|
|
visited = false;
|
|
if (m_antecedent.var() != true_bool_var) {
|
|
proof * pr = cr.get_proof(m_antecedent);
|
|
if (pr)
|
|
prs.push_back(pr);
|
|
else
|
|
visited = false;
|
|
}
|
|
if (!visited)
|
|
return 0;
|
|
context & ctx = cr.get_context();
|
|
ast_manager & m = cr.get_manager();
|
|
expr_ref fact(m);
|
|
ctx.literal2expr(m_consequent, fact);
|
|
return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr());
|
|
}
|
|
|
|
virtual theory_id get_from_theory() const {
|
|
return m_th_id;
|
|
}
|
|
|
|
virtual char const * get_name() const { return "bv-bit-eq"; }
|
|
};
|
|
|
|
inline justification * theory_bv::mk_bit_eq_justification(theory_var v1, theory_var v2, literal consequent, literal antecedent) {
|
|
return get_context().mk_justification(bit_eq_justification(get_id(), get_enode(v1), get_enode(v2), consequent, antecedent));
|
|
}
|
|
|
|
void theory_bv::unmerge_eh(theory_var v1, theory_var v2) {
|
|
// v1 was the root of the equivalence class
|
|
// I must remove the zero_one_bits that are from v2.
|
|
|
|
// REMARK: it is unsafe to invoke check_zero_one_bits, since
|
|
// the enode associated with v1 and v2 may have already been
|
|
// deleted.
|
|
//
|
|
// The logical context trail_stack is popped before
|
|
// the theories pop_scope_eh is invoked.
|
|
|
|
zero_one_bits & bits = m_zero_one_bits[v1];
|
|
if (bits.empty()) {
|
|
// SASSERT(check_zero_one_bits(v1));
|
|
// SASSERT(check_zero_one_bits(v2));
|
|
return;
|
|
}
|
|
unsigned j = bits.size();
|
|
while (j > 0) {
|
|
--j;
|
|
zero_one_bit & bit = bits[j];
|
|
if (find(bit.m_owner) == v1) {
|
|
bits.shrink(j+1);
|
|
// SASSERT(check_zero_one_bits(v1));
|
|
// SASSERT(check_zero_one_bits(v2));
|
|
return;
|
|
}
|
|
}
|
|
bits.shrink(0);
|
|
// SASSERT(check_zero_one_bits(v1));
|
|
// SASSERT(check_zero_one_bits(v2));
|
|
}
|
|
|
|
void theory_bv::init_model(model_generator & m) {
|
|
m_factory = alloc(bv_factory, get_manager());
|
|
m.register_factory(m_factory);
|
|
}
|
|
|
|
model_value_proc * theory_bv::mk_value(enode * n, model_generator & mg) {
|
|
numeral val;
|
|
theory_var v = n->get_th_var(get_id());
|
|
SASSERT(v != null_theory_var);
|
|
#ifdef Z3DEBUG
|
|
bool r =
|
|
#endif
|
|
get_fixed_value(v, val);
|
|
SASSERT(r);
|
|
return alloc(expr_wrapper_proc, m_factory->mk_value(val, get_bv_size(v)));
|
|
}
|
|
|
|
void theory_bv::display_var(std::ostream & out, theory_var v) const {
|
|
out << "v";
|
|
out.width(4);
|
|
out << std::left << v;
|
|
out << " #";
|
|
out.width(4);
|
|
out << get_enode(v)->get_owner_id() << " -> #";
|
|
out.width(4);
|
|
out << get_enode(find(v))->get_owner_id();
|
|
out << std::right << ", bits:";
|
|
context & ctx = get_context();
|
|
literal_vector const & bits = m_bits[v];
|
|
literal_vector::const_iterator it = bits.begin();
|
|
literal_vector::const_iterator end = bits.end();
|
|
for (; it != end; ++it) {
|
|
out << " ";
|
|
ctx.display_literal(out, *it);
|
|
}
|
|
numeral val;
|
|
if (get_fixed_value(v, val))
|
|
out << ", value: " << val;
|
|
out << "\n";
|
|
}
|
|
|
|
void theory_bv::display_bit_atom(std::ostream & out, bool_var v, bit_atom const * a) const {
|
|
context & ctx = get_context();
|
|
out << "#" << ctx.bool_var2expr(v)->get_id() << " ->";
|
|
var_pos_occ * curr = a->m_occs;
|
|
while (curr) {
|
|
out << " #" << get_enode(curr->m_var)->get_owner_id() << "[" << curr->m_idx << "]";
|
|
curr = curr->m_next;
|
|
}
|
|
out << "\n";
|
|
}
|
|
|
|
void theory_bv::display_atoms(std::ostream & out) const {
|
|
out << "atoms:\n";
|
|
context & ctx = get_context();
|
|
unsigned num = ctx.get_num_bool_vars();
|
|
for (unsigned v = 0; v < num; v++) {
|
|
atom * a = get_bv2a(v);
|
|
if (a && a->is_bit())
|
|
display_bit_atom(out, v, static_cast<bit_atom*>(a));
|
|
}
|
|
}
|
|
|
|
void theory_bv::display(std::ostream & out) const {
|
|
out << "Theory bv:\n";
|
|
unsigned num_vars = get_num_vars();
|
|
for (unsigned v = 0; v < num_vars; v++) {
|
|
display_var(out, v);
|
|
}
|
|
display_atoms(out);
|
|
}
|
|
|
|
void theory_bv::collect_statistics(::statistics & st) const {
|
|
st.update("bv conflicts", m_stats.m_num_conflicts);
|
|
st.update("bv diseqs", m_stats.m_num_diseq_static);
|
|
st.update("bv dynamic diseqs", m_stats.m_num_diseq_dynamic);
|
|
st.update("bv bit2core", m_stats.m_num_bit2core);
|
|
st.update("bv->core eq", m_stats.m_num_th2core_eq);
|
|
}
|
|
|
|
#ifdef Z3DEBUG
|
|
bool theory_bv::check_assignment(theory_var v) const {
|
|
context & ctx = get_context();
|
|
if (!is_root(v))
|
|
return true;
|
|
if (!ctx.is_relevant(get_enode(v))) {
|
|
return true;
|
|
}
|
|
|
|
theory_var v2 = v;
|
|
literal_vector const & bits2 = m_bits[v2];
|
|
theory_var v1 = v2;
|
|
do {
|
|
literal_vector const & bits1 = m_bits[v1];
|
|
SASSERT(bits1.size() == bits2.size());
|
|
unsigned sz = bits1.size();
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
literal bit1 = bits1[i];
|
|
literal bit2 = bits2[i];
|
|
lbool val1 = ctx.get_assignment(bit1);
|
|
lbool val2 = ctx.get_assignment(bit2);
|
|
CTRACE("bv_bug", val1 != val2,
|
|
tout << "equivalence class is inconsistent, i: " << i << "\n";
|
|
display_var(tout, v1);
|
|
display_var(tout, v2);
|
|
tout << "val1: " << val1 << " lvl: " << ctx.get_assign_level(bit1.var()) << " bit " << bit1 << "\n";
|
|
tout << "val2: " << val2 << " lvl: " << ctx.get_assign_level(bit2.var()) << " bit " << bit2 << "\n";);
|
|
SASSERT(val1 == val2);
|
|
}
|
|
SASSERT(ctx.is_relevant(get_enode(v1)));
|
|
v1 = next(v1);
|
|
}
|
|
while (v1 != v);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
\brief Check whether m_zero_one_bits is an accurate summary of the bits in the
|
|
equivalence class rooted by v.
|
|
|
|
\remark The method does nothing if v is not the root of the equivalence class.
|
|
*/
|
|
bool theory_bv::check_zero_one_bits(theory_var v) const {
|
|
if (get_context().inconsistent())
|
|
return true; // property is only valid if the context is not in a conflict.
|
|
if (is_root(v) && is_bv(v)) {
|
|
svector<bool> bits[2];
|
|
unsigned num_bits = 0;
|
|
unsigned bv_sz = get_bv_size(v);
|
|
bits[0].resize(bv_sz, false);
|
|
bits[1].resize(bv_sz, false);
|
|
theory_var curr = v;
|
|
do {
|
|
literal_vector const & lits = m_bits[curr];
|
|
for (unsigned i = 0; i < lits.size(); i++) {
|
|
literal l = lits[i];
|
|
if (l.var() == true_bool_var) {
|
|
unsigned is_true = (l == true_literal);
|
|
SASSERT(!bits[!is_true][i]); // no complementary bits
|
|
if (!bits[is_true][i]) {
|
|
bits[is_true][i] = true;
|
|
num_bits++;
|
|
}
|
|
}
|
|
}
|
|
curr = next(curr);
|
|
}
|
|
while (curr != v);
|
|
|
|
zero_one_bits const & _bits = m_zero_one_bits[v];
|
|
SASSERT(_bits.size() == num_bits);
|
|
svector<bool> already_found;
|
|
already_found.resize(bv_sz, false);
|
|
zero_one_bits::const_iterator it = _bits.begin();
|
|
zero_one_bits::const_iterator end = _bits.end();
|
|
for (; it != end; ++it) {
|
|
SASSERT(find(it->m_owner) == v);
|
|
SASSERT(bits[it->m_is_true][it->m_idx]);
|
|
SASSERT(!already_found[it->m_idx]);
|
|
already_found[it->m_idx] = true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool theory_bv::check_invariant() const {
|
|
unsigned num = get_num_vars();
|
|
for (unsigned v = 0; v < num; v++) {
|
|
check_assignment(v);
|
|
check_zero_one_bits(v);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
};
|