mirror of
https://github.com/Z3Prover/z3
synced 2025-08-20 18:20:22 +00:00
Merge branch 'upstream-master' into release-1.0
Conflicts: src/cmd_context/check_logic.cpp src/cmd_context/cmd_context.cpp src/cmd_context/cmd_context.h src/smt/params/smt_params_helper.pyg src/smt/smt_context.cpp
This commit is contained in:
commit
235ea79043
588 changed files with 21784 additions and 15202 deletions
|
@ -162,8 +162,8 @@ bool arith_rewriter::div_polynomial(expr * t, numeral const & g, const_treatment
|
|||
}
|
||||
|
||||
bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) {
|
||||
numeral c;
|
||||
if (!is_add(arg1) && is_numeral(arg2, c)) {
|
||||
numeral b, c;
|
||||
if (!is_add(arg1) && !m_util.is_mod(arg1) && is_numeral(arg2, c)) {
|
||||
numeral a;
|
||||
bool r = false;
|
||||
expr * pp = get_power_product(arg1, a);
|
||||
|
@ -193,6 +193,45 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref &
|
|||
case EQ: result = m_util.mk_eq(pp, k); return true;
|
||||
}
|
||||
}
|
||||
expr* t1, *t2;
|
||||
bool is_int;
|
||||
if (m_util.is_mod(arg2)) {
|
||||
std::swap(arg1, arg2);
|
||||
switch (kind) {
|
||||
case LE: kind = GE; break;
|
||||
case GE: kind = LE; break;
|
||||
case EQ: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_util.is_numeral(arg2, c, is_int) && is_int &&
|
||||
m_util.is_mod(arg1, t1, t2) && m_util.is_numeral(t2, b, is_int) && !b.is_zero()) {
|
||||
// mod x b <= c = false if c < 0, b != 0, true if c >= b, b != 0
|
||||
if (c.is_neg()) {
|
||||
switch (kind) {
|
||||
case EQ:
|
||||
case LE: result = m().mk_false(); return true;
|
||||
case GE: result = m().mk_true(); return true;
|
||||
}
|
||||
}
|
||||
if (c.is_zero() && kind == GE) {
|
||||
result = m().mk_true();
|
||||
return true;
|
||||
}
|
||||
if (c.is_pos() && c >= abs(b)) {
|
||||
switch (kind) {
|
||||
case LE: result = m().mk_true(); return true;
|
||||
case EQ:
|
||||
case GE: result = m().mk_false(); return true;
|
||||
}
|
||||
}
|
||||
// mod x b <= b - 1
|
||||
if (c + rational::one() == abs(b) && kind == LE) {
|
||||
result = m().mk_true();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -946,12 +985,13 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res
|
|||
|
||||
br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) {
|
||||
numeral a;
|
||||
expr* x;
|
||||
if (m_util.is_numeral(arg, a)) {
|
||||
result = m_util.mk_numeral(floor(a), true);
|
||||
return BR_DONE;
|
||||
}
|
||||
else if (m_util.is_to_real(arg)) {
|
||||
result = to_app(arg)->get_arg(0);
|
||||
else if (m_util.is_to_real(arg, x)) {
|
||||
result = x;
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
|
@ -982,8 +1022,8 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) {
|
|||
new_args.push_back(m_util.mk_numeral(a, true));
|
||||
}
|
||||
else {
|
||||
SASSERT(m_util.is_to_real(c));
|
||||
new_args.push_back(to_app(c)->get_arg(0));
|
||||
VERIFY (m_util.is_to_real(c, x));
|
||||
new_args.push_back(x);
|
||||
}
|
||||
}
|
||||
SASSERT(num_args == new_args.size());
|
||||
|
@ -1196,11 +1236,17 @@ expr * arith_rewriter::mk_sin_value(rational const & k) {
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) {
|
||||
if (is_app_of(arg, get_fid(), OP_ASIN)) {
|
||||
expr * m, *x;
|
||||
if (m_util.is_asin(arg, x)) {
|
||||
// sin(asin(x)) == x
|
||||
result = to_app(arg)->get_arg(0);
|
||||
result = x;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_acos(arg, x)) {
|
||||
// sin(acos(x)) == sqrt(1 - x^2)
|
||||
result = m_util.mk_power(m_util.mk_sub(m_util.mk_real(1), m_util.mk_mul(x,x)), m_util.mk_numeral(rational(1,2), false));
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
rational k;
|
||||
if (is_numeral(arg, k) && k.is_zero()) {
|
||||
// sin(0) == 0
|
||||
|
@ -1214,7 +1260,6 @@ br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) {
|
|||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
||||
expr * m;
|
||||
if (is_pi_offset(arg, k, m)) {
|
||||
rational k_prime = mod(floor(k), rational(2)) + k - floor(k);
|
||||
SASSERT(k_prime >= rational(0) && k_prime < rational(2));
|
||||
|
@ -1250,11 +1295,15 @@ br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) {
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_cos_core(expr * arg, expr_ref & result) {
|
||||
if (is_app_of(arg, get_fid(), OP_ACOS)) {
|
||||
expr* x;
|
||||
if (m_util.is_acos(arg, x)) {
|
||||
// cos(acos(x)) == x
|
||||
result = to_app(arg)->get_arg(0);
|
||||
result = x;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.is_asin(arg, x)) {
|
||||
// cos(asin(x)) == ...
|
||||
}
|
||||
|
||||
rational k;
|
||||
if (is_numeral(arg, k) && k.is_zero()) {
|
||||
|
@ -1306,9 +1355,10 @@ br_status arith_rewriter::mk_cos_core(expr * arg, expr_ref & result) {
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) {
|
||||
if (is_app_of(arg, get_fid(), OP_ATAN)) {
|
||||
expr* x;
|
||||
if (m_util.is_atan(arg, x)) {
|
||||
// tan(atan(x)) == x
|
||||
result = to_app(arg)->get_arg(0);
|
||||
result = x;
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
@ -1488,9 +1538,10 @@ br_status arith_rewriter::mk_atan_core(expr * arg, expr_ref & result) {
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_sinh_core(expr * arg, expr_ref & result) {
|
||||
if (is_app_of(arg, get_fid(), OP_ASINH)) {
|
||||
expr* x;
|
||||
if (m_util.is_asinh(arg, x)) {
|
||||
// sinh(asinh(x)) == x
|
||||
result = to_app(arg)->get_arg(0);
|
||||
result = x;
|
||||
return BR_DONE;
|
||||
}
|
||||
expr * t;
|
||||
|
@ -1503,12 +1554,12 @@ br_status arith_rewriter::mk_sinh_core(expr * arg, expr_ref & result) {
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_cosh_core(expr * arg, expr_ref & result) {
|
||||
if (is_app_of(arg, get_fid(), OP_ACOSH)) {
|
||||
// cosh(acosh(x)) == x
|
||||
result = to_app(arg)->get_arg(0);
|
||||
expr* t;
|
||||
if (m_util.is_acosh(arg, t)) {
|
||||
// cosh(acosh(t)) == t
|
||||
result = t;
|
||||
return BR_DONE;
|
||||
}
|
||||
expr * t;
|
||||
if (m_util.is_times_minus_one(arg, t)) {
|
||||
// cosh(-t) == cosh
|
||||
result = m_util.mk_cosh(t);
|
||||
|
@ -1518,12 +1569,12 @@ br_status arith_rewriter::mk_cosh_core(expr * arg, expr_ref & result) {
|
|||
}
|
||||
|
||||
br_status arith_rewriter::mk_tanh_core(expr * arg, expr_ref & result) {
|
||||
if (is_app_of(arg, get_fid(), OP_ATANH)) {
|
||||
// tanh(atanh(x)) == x
|
||||
result = to_app(arg)->get_arg(0);
|
||||
expr * t;
|
||||
if (m_util.is_atanh(arg, t)) {
|
||||
// tanh(atanh(t)) == t
|
||||
result = t;
|
||||
return BR_DONE;
|
||||
}
|
||||
expr * t;
|
||||
if (m_util.is_times_minus_one(arg, t)) {
|
||||
// tanh(-t) == -tanh(t)
|
||||
result = m_util.mk_uminus(m_util.mk_tanh(t));
|
||||
|
|
|
@ -26,6 +26,7 @@ void array_rewriter::updt_params(params_ref const & _p) {
|
|||
m_sort_store = p.sort_store();
|
||||
m_expand_select_store = p.expand_select_store();
|
||||
m_expand_store_eq = p.expand_store_eq();
|
||||
m_expand_select_ite = false;
|
||||
}
|
||||
|
||||
void array_rewriter::get_param_descrs(param_descrs & r) {
|
||||
|
@ -201,6 +202,17 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args,
|
|||
result = m().mk_app(f, num_args - 1, args + 1);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
expr* c, *th, *el;
|
||||
if (m_expand_select_ite && m().is_ite(args[0], c, th, el)) {
|
||||
ptr_vector<expr> args1, args2;
|
||||
args1.push_back(th);
|
||||
args1.append(num_args-1, args + 1);
|
||||
args2.push_back(el);
|
||||
args2.append(num_args-1, args + 1);
|
||||
result = m().mk_ite(c, m_util.mk_select(num_args, args1.c_ptr()), m_util.mk_select(num_args, args2.c_ptr()));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ class array_rewriter {
|
|||
bool m_sort_store;
|
||||
bool m_expand_select_store;
|
||||
bool m_expand_store_eq;
|
||||
bool m_expand_select_ite;
|
||||
template<bool CHECK_DISEQ>
|
||||
lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2);
|
||||
public:
|
||||
|
@ -43,6 +44,8 @@ public:
|
|||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
|
||||
void set_expand_select_store(bool f) { m_expand_select_store = f; }
|
||||
void set_expand_select_ite(bool f) { m_expand_select_ite = f; }
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ Revision History:
|
|||
struct bit_blaster_params {
|
||||
bool m_bb_ext_gates;
|
||||
bool m_bb_quantifiers;
|
||||
bit_blaster_params():
|
||||
bit_blaster_params() :
|
||||
m_bb_ext_gates(false),
|
||||
m_bb_quantifiers(false) {
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ struct bit_blaster_params {
|
|||
p.register_bool_param("bb_quantifiers", m_bb_quantifiers, "convert bit-vectors to Booleans in quantifiers");
|
||||
}
|
||||
#endif
|
||||
|
||||
void display(std::ostream & out) const {
|
||||
out << "m_bb_ext_gates=" << m_bb_ext_gates << std::endl;
|
||||
out << "m_bb_quantifiers=" << m_bb_quantifiers << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* BIT_BLASTER_PARAMS_H_ */
|
||||
|
|
|
@ -66,9 +66,6 @@ struct blaster_cfg {
|
|||
void mk_nor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nor(a, b, r); }
|
||||
};
|
||||
|
||||
// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o
|
||||
// template class bit_blaster_tpl<blaster_cfg>;
|
||||
|
||||
class blaster : public bit_blaster_tpl<blaster_cfg> {
|
||||
bool_rewriter m_rewriter;
|
||||
bv_util m_util;
|
||||
|
@ -165,6 +162,10 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg {
|
|||
m_keyval_lim.push_back(m_keys.size());
|
||||
}
|
||||
|
||||
unsigned get_num_scopes() const {
|
||||
return m_keyval_lim.size();
|
||||
}
|
||||
|
||||
void pop(unsigned num_scopes) {
|
||||
if (num_scopes > 0) {
|
||||
SASSERT(num_scopes <= m_keyval_lim.size());
|
||||
|
@ -621,9 +622,6 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
|
|||
}
|
||||
};
|
||||
|
||||
// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o
|
||||
// template class rewriter_tpl<blaster_rewriter_cfg>;
|
||||
|
||||
struct bit_blaster_rewriter::imp : public rewriter_tpl<blaster_rewriter_cfg> {
|
||||
blaster m_blaster;
|
||||
blaster_rewriter_cfg m_cfg;
|
||||
|
@ -637,6 +635,7 @@ struct bit_blaster_rewriter::imp : public rewriter_tpl<blaster_rewriter_cfg> {
|
|||
}
|
||||
void push() { m_cfg.push(); }
|
||||
void pop(unsigned s) { m_cfg.pop(s); }
|
||||
unsigned get_num_scopes() const { return m_cfg.get_num_scopes(); }
|
||||
};
|
||||
|
||||
bit_blaster_rewriter::bit_blaster_rewriter(ast_manager & m, params_ref const & p):
|
||||
|
@ -680,3 +679,7 @@ void bit_blaster_rewriter::operator()(expr * e, expr_ref & result, proof_ref & r
|
|||
m_imp->operator()(e, result, result_proof);
|
||||
}
|
||||
|
||||
unsigned bit_blaster_rewriter::get_num_scopes() const {
|
||||
return m_imp->get_num_scopes();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
void operator()(expr * e, expr_ref & result, proof_ref & result_proof);
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
unsigned get_num_scopes() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -59,9 +59,12 @@ br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * co
|
|||
mk_implies(args[0], args[1], result);
|
||||
return BR_DONE;
|
||||
case OP_XOR:
|
||||
SASSERT(num_args == 2);
|
||||
mk_xor(args[0], args[1], result);
|
||||
return BR_DONE;
|
||||
switch (num_args) {
|
||||
case 0: return BR_FAILED;
|
||||
case 1: result = args[0]; return BR_DONE;
|
||||
case 2: mk_xor(args[0], args[1], result); return BR_DONE;
|
||||
default: UNREACHABLE(); return BR_FAILED;
|
||||
}
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
@ -456,6 +459,22 @@ bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, exp
|
|||
return false;
|
||||
}
|
||||
|
||||
void bool_rewriter::push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits) {
|
||||
expr* narg;
|
||||
if (m().is_not(arg, narg)) {
|
||||
if (!neg_lits.is_marked(narg)) {
|
||||
neg_lits.mark(narg);
|
||||
new_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!pos_lits.is_marked(arg)) {
|
||||
pos_lits.mark(arg);
|
||||
new_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Apply local context simplification at (OR args[0] ... args[num_args-1])
|
||||
Basic idea:
|
||||
|
@ -473,6 +492,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_
|
|||
bool modified = false;
|
||||
bool forward = true;
|
||||
unsigned rounds = 0;
|
||||
expr* narg;
|
||||
|
||||
while (true) {
|
||||
rounds++;
|
||||
|
@ -481,20 +501,13 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_
|
|||
verbose_stream() << "rounds: " << rounds << "\n";
|
||||
#endif
|
||||
|
||||
#define PUSH_NEW_ARG(ARG) { \
|
||||
new_args.push_back(ARG); \
|
||||
if (m().is_not(ARG)) \
|
||||
neg_lits.mark(to_app(ARG)->get_arg(0)); \
|
||||
else \
|
||||
pos_lits.mark(ARG); \
|
||||
}
|
||||
|
||||
#define PROCESS_ARG() \
|
||||
{ \
|
||||
expr * arg = args[i]; \
|
||||
if (m().is_not(arg) && m().is_or(to_app(arg)->get_arg(0)) && \
|
||||
simp_nested_not_or(to_app(to_app(arg)->get_arg(0))->get_num_args(), \
|
||||
to_app(to_app(arg)->get_arg(0))->get_args(), \
|
||||
if (m().is_not(arg, narg) && m().is_or(narg) && \
|
||||
simp_nested_not_or(to_app(narg)->get_num_args(), \
|
||||
to_app(narg)->get_args(), \
|
||||
neg_lits, \
|
||||
pos_lits, \
|
||||
new_arg)) { \
|
||||
|
@ -515,11 +528,11 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_
|
|||
unsigned sz = to_app(arg)->get_num_args(); \
|
||||
for (unsigned j = 0; j < sz; j++) { \
|
||||
expr * arg_arg = to_app(arg)->get_arg(j); \
|
||||
PUSH_NEW_ARG(arg_arg); \
|
||||
push_new_arg(arg_arg, new_args, neg_lits, pos_lits); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
PUSH_NEW_ARG(arg); \
|
||||
push_new_arg(arg, new_args, neg_lits, pos_lits); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
@ -528,7 +541,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_
|
|||
static unsigned counter = 0;
|
||||
counter++;
|
||||
if (counter % 10000 == 0)
|
||||
verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << "\n";
|
||||
verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << " " << num_args << "\n";
|
||||
#endif
|
||||
|
||||
if (forward) {
|
||||
|
@ -572,7 +585,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_
|
|||
|
||||
*/
|
||||
br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) {
|
||||
expr* cond, *t, *e;
|
||||
expr* cond = 0, *t = 0, *e = 0;
|
||||
VERIFY(m().is_ite(ite, cond, t, e));
|
||||
SASSERT(m().is_value(val));
|
||||
|
||||
|
@ -581,38 +594,40 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result)
|
|||
TRACE("try_ite_value", tout << mk_ismt2_pp(t, m()) << " " << mk_ismt2_pp(e, m()) << " " << mk_ismt2_pp(val, m()) << "\n";
|
||||
tout << t << " " << e << " " << val << "\n";);
|
||||
result = m().mk_false();
|
||||
}
|
||||
}
|
||||
else if (t == val && e == val) {
|
||||
result = m().mk_true();
|
||||
}
|
||||
}
|
||||
else if (t == val) {
|
||||
result = cond;
|
||||
}
|
||||
else {
|
||||
SASSERT(e == val);
|
||||
SASSERT(e == val);
|
||||
mk_not(cond, result);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m().is_value(t)) {
|
||||
if (val == t) {
|
||||
result = m().mk_or(cond, m().mk_eq(val, e));
|
||||
if (m_ite_extra_rules) {
|
||||
if (m().is_value(t)) {
|
||||
if (val == t) {
|
||||
result = m().mk_or(cond, m().mk_eq(val, e));
|
||||
}
|
||||
else {
|
||||
mk_not(cond, result);
|
||||
result = m().mk_and(result, m().mk_eq(val, e));
|
||||
}
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
else {
|
||||
mk_not(cond, result);
|
||||
result = m().mk_and(result, m().mk_eq(val, e));
|
||||
if (m().is_value(e)) {
|
||||
if (val == e) {
|
||||
mk_not(cond, result);
|
||||
result = m().mk_or(result, m().mk_eq(val, t));
|
||||
}
|
||||
else {
|
||||
result = m().mk_and(cond, m().mk_eq(val, t));
|
||||
}
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
if (m().is_value(e)) {
|
||||
if (val == e) {
|
||||
mk_not(cond, result);
|
||||
result = m().mk_or(result, m().mk_eq(val, t));
|
||||
}
|
||||
else {
|
||||
result = m().mk_and(cond, m().mk_eq(val, t));
|
||||
}
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
expr* cond2, *t2, *e2;
|
||||
if (m().is_ite(t, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) {
|
||||
|
|
|
@ -75,6 +75,8 @@ class bool_rewriter {
|
|||
bool local_ctx_simp(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status try_ite_value(app * ite, app * val, expr_ref & result);
|
||||
|
||||
void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits);
|
||||
|
||||
public:
|
||||
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); }
|
||||
ast_manager & m() const { return m_manager; }
|
||||
|
|
664
src/ast/rewriter/bv_bounds.cpp
Normal file
664
src/ast/rewriter/bv_bounds.cpp
Normal file
|
@ -0,0 +1,664 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv_bounds.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Mikolas Janota (MikolasJanota)
|
||||
|
||||
Revision History:
|
||||
--*/
|
||||
#include"bv_bounds.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
bv_bounds::~bv_bounds() {
|
||||
reset();
|
||||
}
|
||||
|
||||
bv_bounds::conv_res bv_bounds::record(app * v, numeral lo, numeral hi, bool negated, vector<ninterval>& nis) {
|
||||
TRACE("bv_bounds", tout << "record0 " << mk_ismt2_pp(v, m_m) << ":" << (negated ? "~[" : "[") << lo << ";" << hi << "]" << std::endl;);
|
||||
const unsigned bv_sz = m_bv_util.get_bv_size(v);
|
||||
const numeral& one = numeral::one();
|
||||
SASSERT(numeral::zero() <= lo);
|
||||
SASSERT(lo <= hi);
|
||||
SASSERT(hi < numeral::power_of_two(bv_sz));
|
||||
numeral vmax, vmin;
|
||||
const bool has_upper = m_unsigned_uppers.find(v, vmax);
|
||||
const bool has_lower = m_unsigned_lowers.find(v, vmin);
|
||||
if (!has_lower) vmin = numeral::zero();
|
||||
if (!has_upper) vmax = (numeral::power_of_two(bv_sz) - one);
|
||||
bool lo_min = lo <= vmin;
|
||||
bool hi_max = hi >= vmax;
|
||||
if (negated) {
|
||||
if (lo_min && hi_max) return UNSAT;
|
||||
if (lo > vmax) return CONVERTED;
|
||||
if (hi < vmin) return CONVERTED;
|
||||
if (lo_min) {
|
||||
negated = false; lo = hi + one; hi = vmax;
|
||||
lo_min = lo <= vmin;
|
||||
hi_max = true;
|
||||
} else if (hi_max) {
|
||||
negated = false; hi = lo - one; lo = vmin;
|
||||
hi_max = hi >= vmax;
|
||||
lo_min = true;
|
||||
}
|
||||
SASSERT(lo.is_nonneg());
|
||||
SASSERT(lo <= hi);
|
||||
SASSERT(hi < numeral::power_of_two(bv_sz));
|
||||
}
|
||||
if (lo_min) lo = vmin;
|
||||
if (hi_max) hi = vmax;
|
||||
TRACE("bv_bounds", tout << "record1 " << mk_ismt2_pp(v, m_m) << ":" << (negated ? "~[" : "[") << lo << ";" << hi << "]" << std::endl;);
|
||||
if (lo > hi) return negated ? CONVERTED : UNSAT;
|
||||
if (lo_min && hi_max) return negated ? UNSAT : CONVERTED;
|
||||
nis.resize(nis.size() + 1);
|
||||
nis.back().v = v;
|
||||
nis.back().lo = lo;
|
||||
nis.back().hi = hi;
|
||||
nis.back().negated = negated;
|
||||
return CONVERTED;
|
||||
}
|
||||
|
||||
bool bv_bounds::is_uleq(expr * e, expr * & v, numeral & c) {
|
||||
// To detect the following rewrite from bv_rewriter:
|
||||
// m().mk_and(
|
||||
// m().mk_eq(m_mk_extract(bv_sz - 1, first_non_zero + 1, a), m_util.mk_numeral(numeral(0), bv_sz - first_non_zero - 1)),
|
||||
// m_util.mk_ule(m_mk_extract(first_non_zero, 0, a), m_mk_extract(first_non_zero, 0, b)));
|
||||
expr * eq;
|
||||
expr * eql;
|
||||
expr * eqr;
|
||||
expr * ule;
|
||||
expr * ulel;
|
||||
expr * uler;
|
||||
numeral eqr_val, uleqr_val;
|
||||
unsigned eqr_sz, uleqr_sz;
|
||||
if (!m_m.is_and(e, eq, ule)) return false;
|
||||
if (!m_m.is_eq(eq, eql, eqr)) return false;
|
||||
if (!m_bv_util.is_bv_ule(ule, ulel, uler)) return false;
|
||||
if (!m_bv_util.is_extract(eql)) return false;
|
||||
expr * const eql0 = to_app(eql)->get_arg(0);
|
||||
const unsigned eql0_sz = m_bv_util.get_bv_size(eql0);
|
||||
if (m_bv_util.get_extract_high(eql) != (eql0_sz - 1)) return false;
|
||||
if (!m_bv_util.is_numeral(eqr, eqr_val, eqr_sz)) return false;
|
||||
if (!eqr_val.is_zero()) return false;
|
||||
if (!m_bv_util.is_extract(ulel)) return false;
|
||||
expr * const ulel0 = to_app(ulel)->get_arg(0);
|
||||
if (ulel0 != eql0) return false;
|
||||
if ((m_bv_util.get_extract_high(ulel) + 1) != m_bv_util.get_extract_low(eql)) return false;
|
||||
if (m_bv_util.get_extract_low(ulel) != 0) return false;
|
||||
if (!m_bv_util.is_numeral(uler, uleqr_val, uleqr_sz)) return false;
|
||||
SASSERT(m_bv_util.get_bv_size(ulel0) == uleqr_sz + eqr_sz);
|
||||
v = ulel0;
|
||||
c = uleqr_val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bv_bounds::conv_res bv_bounds::convert(expr * e, vector<ninterval>& nis, bool negated) {
|
||||
TRACE("bv_bounds", tout << "new constraint: " << (negated ? "~" : "" ) << mk_ismt2_pp(e, m_m) << std::endl;);
|
||||
|
||||
if (m_m.is_not(e)) {
|
||||
negated = !negated;
|
||||
e = to_app(e)->get_arg(0);
|
||||
}
|
||||
|
||||
expr *lhs, *rhs;
|
||||
numeral val, val1;
|
||||
unsigned bv_sz1;
|
||||
|
||||
if (0) {
|
||||
if (m_m.is_eq(e, lhs, rhs) && to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz1)) {
|
||||
return record(to_app(lhs), val, val, negated, nis);
|
||||
}
|
||||
|
||||
if (m_m.is_eq(e, lhs, rhs) && to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz1)) {
|
||||
return record(to_app(rhs), val, val, negated, nis);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_uleq(e, lhs, val) && to_bound(lhs)) {
|
||||
return record(to_app(lhs), numeral::zero(), val, negated, nis);
|
||||
}
|
||||
|
||||
if (1) {
|
||||
numeral rhs_val;
|
||||
unsigned rhs_sz;
|
||||
if (m_m.is_eq(e, lhs, rhs)
|
||||
&& m_bv_util.is_numeral(rhs, rhs_val, rhs_sz)
|
||||
&& rhs_val.is_zero()
|
||||
&& m_bv_util.is_extract(lhs)) {
|
||||
expr * const lhs0 = to_app(lhs)->get_arg(0);
|
||||
const unsigned lhs0_sz = m_bv_util.get_bv_size(lhs0);
|
||||
if (m_bv_util.get_extract_high(lhs)+1 == lhs0_sz) {
|
||||
const numeral u = numeral::power_of_two(m_bv_util.get_extract_low(lhs)) - numeral::one();
|
||||
return record(to_app(lhs0), numeral::zero(), u, negated, nis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bv_util.is_bv_ule(e, lhs, rhs)) {
|
||||
unsigned bv_sz = m_bv_util.get_bv_size(lhs);
|
||||
// unsigned inequality with one variable and a constant
|
||||
if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) // v <= val
|
||||
return record(to_app(lhs), numeral::zero(), val, negated, nis);
|
||||
if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) // val <= v
|
||||
return record(to_app(rhs), val, numeral::power_of_two(bv_sz) - numeral::one(), negated, nis);
|
||||
|
||||
// unsigned inequality with one variable, constant, and addition
|
||||
expr *t1, *t2;
|
||||
if (m_bv_util.is_bv_add(lhs, t1, t2)
|
||||
&& m_bv_util.is_numeral(t1, val, bv_sz)
|
||||
&& to_bound(t2)
|
||||
&& t2 == rhs) { // val + v <= v
|
||||
if (val.is_zero()) return negated ? UNSAT : CONVERTED;
|
||||
SASSERT(val.is_pos());
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
return record(to_app(rhs), mod - val, mod - numeral::one(), negated, nis);
|
||||
}
|
||||
|
||||
if (m_bv_util.is_bv_add(rhs, t1, t2)
|
||||
&& m_bv_util.is_numeral(t1, val, bv_sz)
|
||||
&& to_bound(t2)
|
||||
&& m_bv_util.is_numeral(lhs, val1, bv_sz1)) { // val1 <= val + v
|
||||
SASSERT(bv_sz1 == bv_sz);
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (val1.is_zero()) return negated ? UNSAT : CONVERTED;
|
||||
if (val1 < val) {
|
||||
const numeral nl = mod - val;
|
||||
const numeral nh = mod + val1 - val - numeral::one();
|
||||
return nl <= nh ? record(to_app(t2), nl, nh, !negated, nis) : (negated ? UNSAT : CONVERTED);
|
||||
}
|
||||
else {
|
||||
const numeral l = val1 - val;
|
||||
const numeral h = mod - val - numeral::one();
|
||||
return l <= h ? record(to_app(t2), l, h, negated, nis) : (negated ? CONVERTED : UNSAT);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bv_util.is_bv_add(lhs, t1, t2)
|
||||
&& m_bv_util.is_numeral(t1, val, bv_sz)
|
||||
&& to_bound(t2)
|
||||
&& m_bv_util.is_numeral(rhs, val1, bv_sz1)) { // val + v <= val1
|
||||
SASSERT(bv_sz1 == bv_sz);
|
||||
if (!val.is_pos() || !val1.is_pos()) return UNDEF;
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (val <= val1) {
|
||||
const numeral nl = val1 - val + numeral::one();
|
||||
const numeral nh = mod - val - numeral::one();
|
||||
return nl <= nh ? record(to_app(t2), nl, nh, !negated, nis) : (negated ? UNSAT : CONVERTED);
|
||||
}
|
||||
else {
|
||||
const numeral l = mod - val;
|
||||
const numeral h = l + val1;
|
||||
return record(to_app(t2), l, h, negated, nis);
|
||||
}
|
||||
}
|
||||
|
||||
// v + c1 <= v + c2
|
||||
app * v1(NULL), *v2(NULL);
|
||||
numeral val1, val2;
|
||||
if (is_constant_add(bv_sz, lhs, v1, val1)
|
||||
&& is_constant_add(bv_sz, rhs, v2, val2)
|
||||
&& v1 == v2) {
|
||||
if (val1 == val2) return negated ? UNSAT : CONVERTED;
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (val1 < val2) {
|
||||
SASSERT(val1 < (mod - numeral::one()));
|
||||
SASSERT(val2 > numeral::zero());
|
||||
return record(v1, mod - val2, mod - val1 - numeral::one(), !negated, nis);
|
||||
}
|
||||
else {
|
||||
SASSERT(val1 > val2);
|
||||
SASSERT(val2 < (mod - numeral::one()));
|
||||
SASSERT(val1 > numeral::zero());
|
||||
return record(v1, mod - val1, mod - val2 - numeral::one(), negated, nis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bv_util.is_bv_sle(e, lhs, rhs)) {
|
||||
unsigned bv_sz = m_bv_util.get_bv_size(lhs);
|
||||
// signed inequality with one variable and a constant
|
||||
if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) { // v <= val
|
||||
val = m_bv_util.norm(val, bv_sz, true);
|
||||
return convert_signed(to_app(lhs), -numeral::power_of_two(bv_sz - 1), val, negated, nis);
|
||||
}
|
||||
if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) { // val <= v
|
||||
val = m_bv_util.norm(val, bv_sz, true);
|
||||
return convert_signed(to_app(rhs), val, numeral::power_of_two(bv_sz - 1) - numeral::one(), negated, nis);
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEF;
|
||||
}
|
||||
|
||||
void bv_bounds::reset() {
|
||||
intervals_map::iterator it = m_negative_intervals.begin();
|
||||
const intervals_map::iterator end = m_negative_intervals.end();
|
||||
for (; it != end; ++it) dealloc(it->m_value);
|
||||
}
|
||||
|
||||
br_status bv_bounds::rewrite(unsigned limit, func_decl * f, unsigned num, expr * const * args, expr_ref& result) {
|
||||
if (!m_m.is_bool(f->get_range())) return BR_FAILED;
|
||||
const decl_kind k = f->get_decl_kind();
|
||||
if ((k != OP_OR && k != OP_AND) || num > limit) return BR_FAILED;
|
||||
const bool negated = k == OP_OR;
|
||||
vector<ninterval> nis;
|
||||
vector<unsigned> lengths;
|
||||
vector<bool> ignore;
|
||||
unsigned nis_head = 0;
|
||||
for (unsigned i = 0; i < num && m_okay; ++i) {
|
||||
expr * const curr = args[i];
|
||||
const conv_res cr = convert(curr, nis, negated);
|
||||
ignore.push_back(cr == UNDEF);
|
||||
switch (cr) {
|
||||
case UNDEF: continue;
|
||||
case UNSAT: m_okay = false; break;
|
||||
case CONVERTED:
|
||||
{
|
||||
for (unsigned i = nis_head; i < nis.size(); ++i) {
|
||||
const ninterval& ni = nis[i];
|
||||
m_okay = m_okay && add_bound_unsigned(ni.v, ni.lo, ni.hi, ni.negated);
|
||||
}
|
||||
lengths.push_back(nis.size());
|
||||
nis_head = nis.size();
|
||||
break;
|
||||
}
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (!m_okay || !is_sat()) {
|
||||
result = negated ? m_m.mk_true() : m_m.mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
nis_head = 0;
|
||||
unsigned count = 0;
|
||||
expr_ref_vector nargs(m_m);
|
||||
bool has_singls = false;
|
||||
for (unsigned i = 0; i < num && m_okay; ++i) {
|
||||
TRACE("bv_bounds", tout << "check red: " << mk_ismt2_pp(args[i], m_m) << std::endl;);
|
||||
if (ignore[i]) {
|
||||
TRACE("bv_bounds", tout << "unprocessed" << std::endl;);
|
||||
nargs.push_back(args[i]);
|
||||
continue;
|
||||
}
|
||||
SASSERT(nis_head <= lengths[count]);
|
||||
const bool redundant = nis_head == lengths[count];
|
||||
bool is_singl = false;
|
||||
if (nis_head < lengths[count]) {
|
||||
app * const v = nis[nis_head].v;
|
||||
numeral th, tl;
|
||||
const unsigned bv_sz = m_bv_util.get_bv_size(v);
|
||||
const bool has_upper = m_unsigned_uppers.find(v, th);
|
||||
const bool has_lower = m_unsigned_lowers.find(v, tl);
|
||||
const numeral& one = numeral::one();
|
||||
if (!has_lower) tl = numeral::zero();
|
||||
if (!has_upper) th = (numeral::power_of_two(bv_sz) - one);
|
||||
TRACE("bv_bounds", tout << "bounds: " << mk_ismt2_pp(v, m_m) << "[" << tl << "-" << th << "]" << std::endl;);
|
||||
is_singl = tl == th;
|
||||
nis_head = lengths[count];
|
||||
}
|
||||
if (!redundant && !is_singl) nargs.push_back(args[i]);
|
||||
has_singls |= is_singl;
|
||||
CTRACE("bv_bounds", redundant, tout << "redundant: " << mk_ismt2_pp(args[i], m_m) << std::endl;);
|
||||
++count;
|
||||
}
|
||||
|
||||
if (nargs.size() == num && !has_singls) return BR_FAILED;
|
||||
|
||||
expr_ref eq(m_m);
|
||||
for (bv_bounds::bound_map::iterator i = m_singletons.begin(); i != m_singletons.end(); ++i) {
|
||||
app * const v = i->m_key;
|
||||
const rational val = i->m_value;
|
||||
eq = m_m.mk_eq(v, bvu().mk_numeral(val, v->get_decl()->get_range()));
|
||||
if (negated) eq = m_m.mk_not(eq);
|
||||
nargs.push_back(eq);
|
||||
}
|
||||
|
||||
switch (nargs.size()) {
|
||||
case 0: result = negated ? m_m.mk_false() : m_m.mk_true(); return BR_DONE;
|
||||
case 1: result = nargs.get(0); return BR_DONE;
|
||||
default: result = negated ? m_m.mk_or(nargs.size(), nargs.c_ptr())
|
||||
: m_m.mk_and(nargs.size(), nargs.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
bool bv_bounds::add_constraint(expr* e) {
|
||||
TRACE("bv_bounds", tout << "new constraint" << mk_ismt2_pp(e, m_m) << std::endl;);
|
||||
if (!m_okay) return false;
|
||||
|
||||
bool negated = false;
|
||||
if (m_m.is_not(e)) {
|
||||
negated = true;
|
||||
e = to_app(e)->get_arg(0);
|
||||
}
|
||||
|
||||
expr *lhs, *rhs;
|
||||
numeral val, val1;
|
||||
unsigned bv_sz1;
|
||||
|
||||
if (0) {
|
||||
if (m_m.is_eq(e, lhs, rhs) && to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz1)) {
|
||||
return add_bound_unsigned(to_app(lhs), val, val, negated);
|
||||
}
|
||||
|
||||
if (m_m.is_eq(e, lhs, rhs) && to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz1)) {
|
||||
return add_bound_unsigned(to_app(rhs), val, val, negated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_bv_util.is_bv_ule(e, lhs, rhs)) {
|
||||
unsigned bv_sz = m_bv_util.get_bv_size(lhs);
|
||||
// unsigned inequality with one variable and a constant
|
||||
if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) // v <= val
|
||||
return add_bound_unsigned(to_app(lhs), numeral::zero(), val, negated);
|
||||
if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) // val <= v
|
||||
return add_bound_unsigned(to_app(rhs), val, numeral::power_of_two(bv_sz) - numeral::one(), negated);
|
||||
|
||||
// unsigned inequality with one variable, constant, and addition
|
||||
expr *t1, *t2;
|
||||
if (m_bv_util.is_bv_add(lhs, t1, t2)
|
||||
&& m_bv_util.is_numeral(t1, val, bv_sz)
|
||||
&& to_bound(t2)
|
||||
&& t2 == rhs) { // val + v <= v
|
||||
if (!val.is_pos()) return m_okay;
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
return add_bound_unsigned(to_app(rhs), mod - val, mod - numeral::one(), negated);
|
||||
}
|
||||
|
||||
if (m_bv_util.is_bv_add(rhs, t1, t2)
|
||||
&& m_bv_util.is_numeral(t1, val, bv_sz)
|
||||
&& to_bound(t2)
|
||||
&& m_bv_util.is_numeral(lhs, val1, bv_sz1)) { // val1 <= val + v
|
||||
SASSERT(bv_sz1 == bv_sz);
|
||||
if (!val.is_pos() || !val1.is_pos()) return m_okay;
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (val1 < val) {
|
||||
const numeral nl = mod - val;
|
||||
const numeral nh = mod + val1 - val - numeral::one();
|
||||
return nl <= nh ? add_bound_unsigned(to_app(t2), nl, nh, !negated) : m_okay;
|
||||
}
|
||||
else {
|
||||
const numeral l = val1 - val;
|
||||
const numeral h = mod - val - numeral::one();
|
||||
return l <= h ? add_bound_unsigned(to_app(t2), l, h, negated) : m_okay;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bv_util.is_bv_add(lhs, t1, t2)
|
||||
&& m_bv_util.is_numeral(t1, val, bv_sz)
|
||||
&& to_bound(t2)
|
||||
&& m_bv_util.is_numeral(rhs, val1, bv_sz1)) { // val + v <= val1
|
||||
SASSERT(bv_sz1 == bv_sz);
|
||||
if (!val.is_pos() || !val1.is_pos()) return m_okay;
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (val <= val1) {
|
||||
const numeral nl = val1 - val + numeral::one();
|
||||
const numeral nh = mod - val - numeral::one();
|
||||
return nl <= nh ? add_bound_unsigned(to_app(t2), nl, nh, !negated) : m_okay;
|
||||
}
|
||||
else {
|
||||
const numeral l = mod - val;
|
||||
const numeral h = l + val1;
|
||||
return add_bound_unsigned(to_app(t2), l, h, negated);
|
||||
}
|
||||
}
|
||||
|
||||
// v + c1 <= v + c2
|
||||
app * v1(NULL), *v2(NULL);
|
||||
numeral val1, val2;
|
||||
if (is_constant_add(bv_sz, lhs, v1, val1)
|
||||
&& is_constant_add(bv_sz, rhs, v2, val2)
|
||||
&& v1 == v2) {
|
||||
if (val1 == val2) return m_okay;
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (val1 < val2) {
|
||||
SASSERT(val1 < (mod - numeral::one()));
|
||||
SASSERT(val2 > numeral::zero());
|
||||
return add_bound_unsigned(v1, mod - val2, mod - val1 - numeral::one(), !negated);
|
||||
}
|
||||
else {
|
||||
SASSERT(val1 > val2);
|
||||
SASSERT(val2 < (mod - numeral::one()));
|
||||
SASSERT(val1 > numeral::zero());
|
||||
return add_bound_unsigned(v1, mod - val1, mod - val2 - numeral::one(), negated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bv_util.is_bv_sle(e, lhs, rhs)) {
|
||||
unsigned bv_sz = m_bv_util.get_bv_size(lhs);
|
||||
// signed inequality with one variable and a constant
|
||||
if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) { // v <= val
|
||||
val = m_bv_util.norm(val, bv_sz, true);
|
||||
return add_bound_signed(to_app(lhs), -numeral::power_of_two(bv_sz - 1), val, negated);
|
||||
}
|
||||
if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) { // val <= v
|
||||
val = m_bv_util.norm(val, bv_sz, true);
|
||||
return add_bound_signed(to_app(rhs), val, numeral::power_of_two(bv_sz - 1) - numeral::one(), negated);
|
||||
}
|
||||
}
|
||||
|
||||
return m_okay;
|
||||
}
|
||||
|
||||
bool bv_bounds::add_bound_unsigned(app * v, numeral a, numeral b, bool negate) {
|
||||
TRACE("bv_bounds", tout << "bound_unsigned " << mk_ismt2_pp(v, m_m) << ": " << (negate ? "~[" : "[") << a << ";" << b << "]" << std::endl;);
|
||||
const unsigned bv_sz = m_bv_util.get_bv_size(v);
|
||||
const numeral& zero = numeral::zero();
|
||||
const numeral& one = numeral::one();
|
||||
SASSERT(zero <= a);
|
||||
SASSERT(a <= b);
|
||||
SASSERT(b < numeral::power_of_two(bv_sz));
|
||||
const bool a_min = a == zero;
|
||||
const bool b_max = b == (numeral::power_of_two(bv_sz) - one);
|
||||
if (negate) {
|
||||
if (a_min && b_max) return m_okay = false;
|
||||
if (a_min) return bound_lo(v, b + one);
|
||||
if (b_max) return bound_up(v, a - one);
|
||||
return add_neg_bound(v, a, b);
|
||||
}
|
||||
else {
|
||||
if (!a_min) m_okay &= bound_lo(v, a);
|
||||
if (!b_max) m_okay &= bound_up(v, b);
|
||||
return m_okay;
|
||||
}
|
||||
}
|
||||
|
||||
bv_bounds::conv_res bv_bounds::convert_signed(app * v, numeral a, numeral b, bool negate, vector<ninterval>& nis) {
|
||||
TRACE("bv_bounds", tout << "convert_signed " << mk_ismt2_pp(v, m_m) << ":" << (negate ? "~[" : "[") << a << ";" << b << "]" << std::endl;);
|
||||
const unsigned bv_sz = m_bv_util.get_bv_size(v);
|
||||
SASSERT(a <= b);
|
||||
const numeral& zero = numeral::zero();
|
||||
const numeral& one = numeral::one();
|
||||
const bool a_neg = a < zero;
|
||||
const bool b_neg = b < zero;
|
||||
if (!a_neg && !b_neg) return record(v, a, b, negate, nis);
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (a_neg && b_neg) return record(v, mod + a, mod + b, negate, nis);
|
||||
SASSERT(a_neg && !b_neg);
|
||||
if (negate) {
|
||||
const conv_res r1 = record(v, mod + a, mod - one, true, nis);
|
||||
const conv_res r2 = record(v, zero, b, true, nis);
|
||||
return r1 == UNSAT || r2 == UNSAT ? UNSAT : CONVERTED;
|
||||
}
|
||||
else {
|
||||
const numeral l = b + one;
|
||||
const numeral u = mod + a - one;
|
||||
return l <= u ? record(v, l, u, true, nis) : CONVERTED;
|
||||
}
|
||||
}
|
||||
|
||||
bool bv_bounds::add_bound_signed(app * v, numeral a, numeral b, bool negate) {
|
||||
TRACE("bv_bounds", tout << "bound_signed " << mk_ismt2_pp(v, m_m) << ":" << (negate ? "~" : " ") << a << ";" << b << std::endl;);
|
||||
const unsigned bv_sz = m_bv_util.get_bv_size(v);
|
||||
SASSERT(a <= b);
|
||||
const numeral& zero = numeral::zero();
|
||||
const numeral& one = numeral::one();
|
||||
const bool a_neg = a < zero;
|
||||
const bool b_neg = b < zero;
|
||||
if (!a_neg && !b_neg) return add_bound_unsigned(v, a, b, negate);
|
||||
const numeral mod = numeral::power_of_two(bv_sz);
|
||||
if (a_neg && b_neg) return add_bound_unsigned(v, mod + a, mod + b, negate);
|
||||
SASSERT(a_neg && !b_neg);
|
||||
if (negate) {
|
||||
return add_bound_unsigned(v, mod + a, mod - one, true)
|
||||
&& add_bound_unsigned(v, zero, b, true);
|
||||
}
|
||||
else {
|
||||
const numeral l = b + one;
|
||||
const numeral u = mod + a - one;
|
||||
return (l <= u) ? add_bound_unsigned(v, l, u, true) : m_okay;
|
||||
}
|
||||
}
|
||||
|
||||
bool bv_bounds::bound_lo(app * v, numeral l) {
|
||||
SASSERT(in_range(v, l));
|
||||
TRACE("bv_bounds", tout << "lower " << mk_ismt2_pp(v, m_m) << ":" << l << std::endl;);
|
||||
// l <= v
|
||||
bound_map::obj_map_entry * const entry = m_unsigned_lowers.insert_if_not_there2(v, l);
|
||||
if (!(entry->get_data().m_value < l)) return m_okay;
|
||||
// improve bound
|
||||
entry->get_data().m_value = l;
|
||||
return m_okay;
|
||||
}
|
||||
|
||||
bool bv_bounds::bound_up(app * v, numeral u) {
|
||||
SASSERT(in_range(v, u));
|
||||
TRACE("bv_bounds", tout << "upper " << mk_ismt2_pp(v, m_m) << ":" << u << std::endl;);
|
||||
// v <= u
|
||||
bound_map::obj_map_entry * const entry = m_unsigned_uppers.insert_if_not_there2(v, u);
|
||||
if (!(u < entry->get_data().m_value)) return m_okay;
|
||||
// improve bound
|
||||
entry->get_data().m_value = u;
|
||||
return m_okay;
|
||||
}
|
||||
|
||||
bool bv_bounds::add_neg_bound(app * v, numeral a, numeral b) {
|
||||
TRACE("bv_bounds", tout << "negative bound " << mk_ismt2_pp(v, m_m) << ":" << a << ";" << b << std::endl;);
|
||||
bv_bounds::interval negative_interval(a, b);
|
||||
SASSERT(m_bv_util.is_bv(v));
|
||||
SASSERT(a >= numeral::zero());
|
||||
SASSERT(b < numeral::power_of_two(m_bv_util.get_bv_size(v)));
|
||||
SASSERT(a <= b);
|
||||
|
||||
intervals_map::obj_map_entry * const e = m_negative_intervals.find_core(v);
|
||||
intervals * ivs(NULL);
|
||||
if (e == 0) {
|
||||
ivs = alloc(intervals);
|
||||
m_negative_intervals.insert(v, ivs);
|
||||
}
|
||||
else {
|
||||
ivs = e->get_data().get_value();
|
||||
}
|
||||
ivs->push_back(negative_interval);
|
||||
return m_okay;
|
||||
}
|
||||
|
||||
|
||||
bool bv_bounds::is_sat() {
|
||||
if (!m_okay) return false;
|
||||
obj_hashtable<app> seen;
|
||||
obj_hashtable<app>::entry *dummy;
|
||||
|
||||
for (bound_map::iterator i = m_unsigned_lowers.begin(); i != m_unsigned_lowers.end(); ++i) {
|
||||
app * const v = i->m_key;
|
||||
if (!seen.insert_if_not_there_core(v, dummy)) continue;
|
||||
if (!is_sat(v)) return false;
|
||||
}
|
||||
|
||||
for (bound_map::iterator i = m_unsigned_uppers.begin(); i != m_unsigned_uppers.end(); ++i) {
|
||||
app * const v = i->m_key;
|
||||
if (!seen.insert_if_not_there_core(v, dummy)) continue;
|
||||
if (!is_sat(v)) return false;
|
||||
}
|
||||
|
||||
for (intervals_map::iterator i = m_negative_intervals.begin(); i != m_negative_intervals.end(); ++i) {
|
||||
app * const v = i->m_key;
|
||||
if (!seen.insert_if_not_there_core(v, dummy)) continue;
|
||||
if (!is_sat(v)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct interval_comp_t {
|
||||
bool operator() (bv_bounds::interval i, bv_bounds::interval j) {
|
||||
return (i.first < j.first);
|
||||
}
|
||||
} interval_comp;
|
||||
|
||||
|
||||
void bv_bounds::record_singleton(app * v, numeral& singleton_value) {
|
||||
TRACE("bv_bounds", tout << "singleton:" << mk_ismt2_pp(v, m_m) << ":" << singleton_value << std::endl;);
|
||||
SASSERT(!m_singletons.find(v, singleton_value));
|
||||
m_singletons.insert(v, singleton_value);
|
||||
}
|
||||
|
||||
bool bv_bounds::is_sat(app * v) {
|
||||
TRACE("bv_bounds", tout << "is_sat " << mk_ismt2_pp(v, m_m) << std::endl;);
|
||||
const bool rv = is_sat_core(v);
|
||||
TRACE("bv_bounds", tout << "is_sat " << mk_ismt2_pp(v, m_m) << "\nres: " << rv << std::endl;);
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool bv_bounds::is_sat_core(app * v) {
|
||||
SASSERT(m_bv_util.is_bv(v));
|
||||
if (!m_okay) return false;
|
||||
unsigned const bv_sz = m_bv_util.get_bv_size(v);
|
||||
numeral lower, upper;
|
||||
const bool has_upper = m_unsigned_uppers.find(v, upper);
|
||||
const bool has_lower = m_unsigned_lowers.find(v, lower);
|
||||
if (has_upper && has_lower && lower > upper) return false;
|
||||
const numeral& one = numeral::one();
|
||||
if (!has_lower) lower = numeral::zero();
|
||||
if (!has_upper) upper = (numeral::power_of_two(bv_sz) - one);
|
||||
TRACE("bv_bounds", tout << "is_sat bound:" << lower << "-" << upper << std::endl;);
|
||||
intervals * negative_intervals(NULL);
|
||||
const bool has_neg_intervals = m_negative_intervals.find(v, negative_intervals);
|
||||
bool is_sat(false);
|
||||
numeral new_lo = lower;
|
||||
numeral new_hi = lower - one;
|
||||
numeral ptr = lower;
|
||||
if (has_neg_intervals) {
|
||||
SASSERT(negative_intervals != NULL);
|
||||
std::sort(negative_intervals->begin(), negative_intervals->end(), interval_comp);
|
||||
intervals::const_iterator e = negative_intervals->end();
|
||||
for (intervals::const_iterator i = negative_intervals->begin(); i != e; ++i) {
|
||||
const numeral negative_lower = i->first;
|
||||
const numeral negative_upper = i->second;
|
||||
if (ptr > negative_upper) continue;
|
||||
if (ptr < negative_lower) {
|
||||
if (!is_sat) new_lo = ptr;
|
||||
new_hi = negative_lower - one;
|
||||
if (new_hi > upper) new_hi = upper;
|
||||
is_sat = true;
|
||||
}
|
||||
TRACE("bv_bounds", tout << "is_sat new_lo, new_hi:" << new_lo << "-" << new_hi << std::endl;);
|
||||
ptr = negative_upper + one;
|
||||
TRACE("bv_bounds", tout << "is_sat ptr, new_hi:" << ptr << "-" << new_hi << std::endl;);
|
||||
if (ptr > upper) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptr <= upper) {
|
||||
if (!is_sat) new_lo = ptr;
|
||||
new_hi = upper;
|
||||
is_sat = true;
|
||||
}
|
||||
if (new_hi < upper) bound_up(v, new_hi);
|
||||
if (new_lo > lower) bound_lo(v, new_lo);
|
||||
TRACE("bv_bounds", tout << "is_sat new_lo, new_hi:" << new_lo << "-" << new_hi << std::endl;);
|
||||
|
||||
const bool is_singleton = is_sat && new_hi == new_lo;
|
||||
if (is_singleton) record_singleton(v, new_lo);
|
||||
|
||||
return is_sat;
|
||||
}
|
130
src/ast/rewriter/bv_bounds.h
Normal file
130
src/ast/rewriter/bv_bounds.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv_bounds.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A class used to determine bounds on bit-vector variables.
|
||||
The satisfiability procedure is polynomial.
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Mikolas Janota (MikolasJanota)
|
||||
|
||||
Revision History:
|
||||
--*/
|
||||
#ifndef BV_BOUNDS_H_23754
|
||||
#define BV_BOUNDS_H_23754
|
||||
#include"ast.h"
|
||||
#include"bv_decl_plugin.h"
|
||||
#include"rewriter_types.h"
|
||||
|
||||
/* \brief A class to analyze constraints on bit vectors.
|
||||
|
||||
The objective is to identify inconsistencies in polynomial time.
|
||||
All bounds/intervals are closed. Methods that add new constraints
|
||||
return false if inconsistency has already been reached.
|
||||
Typical usage is to call repeatedly add_constraint(e) and call is_sat() in the end.
|
||||
*/
|
||||
class bv_bounds {
|
||||
public:
|
||||
typedef rational numeral;
|
||||
typedef std::pair<numeral, numeral> interval;
|
||||
typedef obj_map<app, numeral> bound_map;
|
||||
bv_bounds(ast_manager& m) : m_m(m), m_bv_util(m), m_okay(true) {};
|
||||
~bv_bounds();
|
||||
public: // bounds addition methods
|
||||
br_status rewrite(unsigned limit, func_decl * f, unsigned num, expr * const * args, expr_ref& result);
|
||||
|
||||
/** \brief Add a constraint to the system.
|
||||
|
||||
The added constraints are to be considered by is_sat.
|
||||
Currently, only special types of inequalities are supported, e.g. v <= v+1.
|
||||
Other constraints are ignored.
|
||||
Returns false if the system became trivially unsatisfiable
|
||||
**/
|
||||
bool add_constraint(expr* e);
|
||||
|
||||
bool bound_up(app * v, numeral u); // v <= u
|
||||
bool bound_lo(app * v, numeral l); // l <= v
|
||||
inline bool add_neg_bound(app * v, numeral a, numeral b); // not (a<=v<=b)
|
||||
bool add_bound_signed(app * v, numeral a, numeral b, bool negate);
|
||||
bool add_bound_unsigned(app * v, numeral a, numeral b, bool negate);
|
||||
public:
|
||||
bool is_sat(); ///< Determine if the set of considered constraints is satisfiable.
|
||||
bool is_okay();
|
||||
const bound_map& singletons() { return m_singletons; }
|
||||
bv_util& bvu() { return m_bv_util; }
|
||||
void reset();
|
||||
protected:
|
||||
struct ninterval {
|
||||
//ninterval(app * v, numeral lo, numeral hi, bool negated) : v(v), lo(lo), hi(hi), negated(negated) {}
|
||||
app * v;
|
||||
numeral lo, hi;
|
||||
bool negated;
|
||||
};
|
||||
enum conv_res { CONVERTED, UNSAT, UNDEF };
|
||||
conv_res convert(expr * e, vector<ninterval>& nis, bool negated);
|
||||
conv_res record(app * v, numeral lo, numeral hi, bool negated, vector<ninterval>& nis);
|
||||
conv_res convert_signed(app * v, numeral a, numeral b, bool negate, vector<ninterval>& nis);
|
||||
|
||||
typedef vector<interval> intervals;
|
||||
typedef obj_map<app, intervals*> intervals_map;
|
||||
ast_manager& m_m;
|
||||
bound_map m_unsigned_lowers;
|
||||
bound_map m_unsigned_uppers;
|
||||
intervals_map m_negative_intervals;
|
||||
bound_map m_singletons;
|
||||
bv_util m_bv_util;
|
||||
bool m_okay;
|
||||
bool is_sat(app * v);
|
||||
bool is_sat_core(app * v);
|
||||
inline bool in_range(app *v, numeral l);
|
||||
inline bool is_constant_add(unsigned bv_sz, expr * e, app*& v, numeral& val);
|
||||
void record_singleton(app * v, numeral& singleton_value);
|
||||
inline bool to_bound(const expr * e) const;
|
||||
bool is_uleq(expr * e, expr * & v, numeral & c);
|
||||
};
|
||||
|
||||
|
||||
inline bool bv_bounds::is_okay() { return m_okay; }
|
||||
|
||||
inline bool bv_bounds::to_bound(const expr * e) const {
|
||||
return is_app(e) && m_bv_util.is_bv(e)
|
||||
&& !m_bv_util.is_bv_add(e)
|
||||
&& !m_bv_util.is_numeral(e);
|
||||
}
|
||||
|
||||
inline bool bv_bounds::in_range(app *v, bv_bounds::numeral n) {
|
||||
const unsigned bv_sz = m_bv_util.get_bv_size(v);
|
||||
const bv_bounds::numeral zero(0);
|
||||
const bv_bounds::numeral mod(rational::power_of_two(bv_sz));
|
||||
return (zero <= n) && (n < mod);
|
||||
}
|
||||
|
||||
inline bool bv_bounds::is_constant_add(unsigned bv_sz, expr * e, app*& v, numeral& val) {
|
||||
SASSERT(e && !v);
|
||||
SASSERT(m_bv_util.get_bv_size(e) == bv_sz);
|
||||
expr *lhs(NULL), *rhs(NULL);
|
||||
if (!m_bv_util.is_bv_add(e, lhs, rhs)) {
|
||||
v = to_app(e);
|
||||
val = rational(0);
|
||||
return true;
|
||||
}
|
||||
if (to_bound(lhs) && m_bv_util.is_numeral(rhs, val, bv_sz)) {
|
||||
v = to_app(lhs);
|
||||
return true;
|
||||
}
|
||||
if (to_bound(rhs) && m_bv_util.is_numeral(lhs, val, bv_sz)) {
|
||||
v = to_app(rhs);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif /* BV_BOUNDS_H_23754 */
|
|
@ -29,12 +29,18 @@ void bv_rewriter::updt_local_params(params_ref const & _p) {
|
|||
m_mul2concat = p.mul2concat();
|
||||
m_bit2bool = p.bit2bool();
|
||||
m_trailing = p.bv_trailing();
|
||||
m_urem_simpl = p.bv_urem_simpl();
|
||||
m_blast_eq_value = p.blast_eq_value();
|
||||
m_split_concat_eq = p.split_concat_eq();
|
||||
m_udiv2mul = p.udiv2mul();
|
||||
m_bvnot2arith = p.bvnot2arith();
|
||||
m_bvnot_simpl = p.bv_not_simpl();
|
||||
m_bv_sort_ac = p.bv_sort_ac();
|
||||
m_mkbv2num = _p.get_bool("mkbv2num", false);
|
||||
m_extract_prop = p.bv_extract_prop();
|
||||
m_ite2id = p.bv_ite2id();
|
||||
m_le_extra = p.bv_le_extra();
|
||||
set_sort_sums(p.bv_sort_ac());
|
||||
}
|
||||
|
||||
void bv_rewriter::updt_params(params_ref const & p) {
|
||||
|
@ -189,6 +195,12 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
|
|||
return mk_bv_comp(args[0], args[1], result);
|
||||
case OP_MKBV:
|
||||
return mk_mkbv(num_args, args, result);
|
||||
case OP_BSMUL_NO_OVFL:
|
||||
return mk_bvsmul_no_overflow(num_args, args, result);
|
||||
case OP_BUMUL_NO_OVFL:
|
||||
return mk_bvumul_no_overflow(num_args, args, result);
|
||||
case OP_BSMUL_NO_UDFL:
|
||||
return mk_bvsmul_no_underflow(num_args, args, result);
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
@ -228,6 +240,198 @@ br_status bv_rewriter::mk_slt(expr * a, expr * b, expr_ref & result) {
|
|||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
// short-circuited concat
|
||||
expr * bv_rewriter::concat(unsigned num_args, expr * const * args) {
|
||||
SASSERT(num_args);
|
||||
switch (num_args) {
|
||||
case 0: return m_util.mk_concat(num_args, args);
|
||||
case 1: return args[0];
|
||||
default: return m_util.mk_concat(num_args, args);
|
||||
}
|
||||
}
|
||||
|
||||
// finds a commonality in sums, e.g. 2 + x + y and 5 + x + y
|
||||
bool bv_rewriter::are_eq_upto_num(expr * _a, expr * _b,
|
||||
expr_ref& common,
|
||||
numeral& a0_val, numeral& b0_val) {
|
||||
const bool aadd = m_util.is_bv_add(_a);
|
||||
const bool badd = m_util.is_bv_add(_b);
|
||||
const bool has_num_a = aadd && to_app(_a)->get_num_args() && is_numeral(to_app(_a)->get_arg(0));
|
||||
const bool has_num_b = badd && to_app(_b)->get_num_args() && is_numeral(to_app(_b)->get_arg(0));
|
||||
a0_val = numeral::zero();
|
||||
b0_val = numeral::zero();
|
||||
if (!aadd && !badd) {
|
||||
if (_a == _b) {
|
||||
common = _a;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!aadd && badd) {
|
||||
if (to_app(_a)->get_num_args() != 2 || !has_num_a || to_app(_a)->get_arg(0) != _b)
|
||||
return false;
|
||||
common = _b;
|
||||
return true;
|
||||
}
|
||||
if (aadd && !badd) {
|
||||
if (to_app(_b)->get_num_args() != 2 || !has_num_b || to_app(_b)->get_arg(0) != _a)
|
||||
return false;
|
||||
common = _a;
|
||||
return true;
|
||||
}
|
||||
SASSERT(aadd && badd);
|
||||
app * const a = to_app(_a);
|
||||
app * const b = to_app(_b);
|
||||
const unsigned numa = a->get_num_args();
|
||||
const unsigned numb = b->get_num_args();
|
||||
if (!numa || !numb) return false;
|
||||
if ((numa - (has_num_a ? 1 : 0)) != (numb - (has_num_b ? 1 : 0))) return false;
|
||||
unsigned ai = has_num_a ? 1 : 0;
|
||||
unsigned bi = has_num_b ? 1 : 0;
|
||||
while (ai < numa) {
|
||||
if (a->get_arg(ai) != b->get_arg(bi)) return false;
|
||||
++ai;
|
||||
++bi;
|
||||
}
|
||||
a0_val = numeral::zero();
|
||||
b0_val = numeral::zero();
|
||||
const unsigned sz = m_util.get_bv_size(a);
|
||||
unsigned a0_sz(sz), b0_sz(sz);
|
||||
if (has_num_a) is_numeral(a->get_arg(0), a0_val, a0_sz);
|
||||
if (has_num_b) is_numeral(b->get_arg(0), b0_val, b0_sz);
|
||||
SASSERT(a0_sz == m_util.get_bv_size(a) && b0_sz == m_util.get_bv_size(a));
|
||||
if (has_num_a && numa > 2) {
|
||||
common = m().mk_app(m_util.get_fid(), add_decl_kind(), numa - 1, a->get_args() + 1);
|
||||
}
|
||||
else {
|
||||
common = has_num_a ? a->get_arg(1) : a;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// simplifies expressions as (bvuleq (X + c1) (X + c2)) for some common expression X and numerals c1, c2
|
||||
br_status bv_rewriter::rw_leq_overflow(bool is_signed, expr * a, expr * b, expr_ref & result) {
|
||||
if (is_signed) return BR_FAILED;
|
||||
expr_ref common(m());
|
||||
numeral a0_val, b0_val;
|
||||
if (!are_eq_upto_num(a, b, common, a0_val, b0_val)) return BR_FAILED;
|
||||
SASSERT(a0_val.is_nonneg() && b0_val.is_nonneg());
|
||||
const unsigned sz = m_util.get_bv_size(a);
|
||||
if (a0_val == b0_val) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (a0_val < b0_val) {
|
||||
result = m_util.mk_ule(m_util.mk_numeral(b0_val - a0_val, sz), b);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
SASSERT(a0_val > b0_val);
|
||||
SASSERT(!a0_val.is_zero());
|
||||
const numeral lower = rational::power_of_two(sz) - a0_val;
|
||||
const numeral upper = rational::power_of_two(sz) - b0_val - numeral::one();
|
||||
if (lower == upper) {
|
||||
result = m().mk_eq(common, mk_numeral(lower, sz));
|
||||
}
|
||||
else if (b0_val.is_zero()) {
|
||||
result = m_util.mk_ule(mk_numeral(lower, sz), common);
|
||||
}
|
||||
else {
|
||||
SASSERT(lower.is_pos());
|
||||
result = m().mk_and(m_util.mk_ule(mk_numeral(lower, sz), common),
|
||||
m_util.mk_ule(common, mk_numeral(upper, sz)));
|
||||
}
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
// simplification for leq comparison between two concatenations
|
||||
br_status bv_rewriter::rw_leq_concats(bool is_signed, expr * _a, expr * _b, expr_ref & result) {
|
||||
if (!m_util.is_concat(_a) || !m_util.is_concat(_b))
|
||||
return BR_FAILED;
|
||||
const app * const a = to_app(_a);
|
||||
const app * const b = to_app(_b);
|
||||
const unsigned numa = a->get_num_args();
|
||||
const unsigned numb = b->get_num_args();
|
||||
const unsigned num_min = std::min(numa, numb);
|
||||
|
||||
if (numa && numb) { // first arg numeral
|
||||
numeral af, bf;
|
||||
unsigned af_sz, bf_sz;
|
||||
if ( is_numeral(a->get_arg(0), af, af_sz)
|
||||
&& is_numeral(b->get_arg(0), bf, bf_sz) ) {
|
||||
const unsigned sz_min = std::min(af_sz, bf_sz);
|
||||
const numeral hi_af = m_util.norm(af_sz > sz_min ? div(af, rational::power_of_two(af_sz - sz_min)) : af,
|
||||
sz_min, is_signed);
|
||||
const numeral hi_bf = m_util.norm(bf_sz > sz_min ? div(bf, rational::power_of_two(bf_sz - sz_min)) : bf,
|
||||
sz_min, is_signed);
|
||||
if (hi_af != hi_bf) {
|
||||
result = hi_af < hi_bf ? m().mk_true() : m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
expr_ref new_a(m());
|
||||
expr_ref new_b(m());
|
||||
if (af_sz > sz_min) {
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(mk_numeral(af, af_sz - sz_min));
|
||||
for (unsigned i = 1; i < numa; ++i) new_args.push_back(a->get_arg(i));
|
||||
new_a = concat(new_args.size(), new_args.c_ptr());
|
||||
} else {
|
||||
new_a = concat(numa - 1, a->get_args() + 1);
|
||||
}
|
||||
if (bf_sz > sz_min) {
|
||||
ptr_buffer<expr> new_args;
|
||||
new_args.push_back(mk_numeral(bf, bf_sz - sz_min));
|
||||
for (unsigned i = 1; i < numb; ++i) new_args.push_back(b->get_arg(i));
|
||||
new_b = concat(new_args.size(), new_args.c_ptr());
|
||||
} else {
|
||||
new_b = concat(numb - 1, b->get_args() + 1);
|
||||
}
|
||||
result = m_util.mk_ule(new_a, new_b);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
||||
{ // common prefix
|
||||
unsigned common = 0;
|
||||
while (common < num_min && m().are_equal(a->get_arg(common), b->get_arg(common))) ++common;
|
||||
SASSERT((common == numa) == (common == numb));
|
||||
if (common == numa) {
|
||||
SASSERT(0); // shouldn't get here as both sides are equal
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (common > 0) {
|
||||
result = m_util.mk_ule(concat(numa - common, a->get_args() + common),
|
||||
concat(numb - common, b->get_args() + common));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
||||
{ // common postfix
|
||||
unsigned new_numa = a->get_num_args();
|
||||
unsigned new_numb = b->get_num_args();
|
||||
while (new_numa && new_numb) {
|
||||
expr * const last_a = a->get_arg(new_numa - 1);
|
||||
expr * const last_b = b->get_arg(new_numb - 1);
|
||||
if (!m().are_equal(last_a, last_b)) break;
|
||||
new_numa--;
|
||||
new_numb--;
|
||||
}
|
||||
if (new_numa == 0) {
|
||||
SASSERT(0); // shouldn't get here as both sides are equal
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (new_numa != numa) {
|
||||
result = is_signed ? m_util.mk_sle(concat(new_numa, a->get_args()), concat(new_numb, b->get_args()))
|
||||
: m_util.mk_ule(concat(new_numa, a->get_args()), concat(new_numb, b->get_args()));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result) {
|
||||
|
||||
numeral r1, r2, r3;
|
||||
|
@ -246,7 +450,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref
|
|||
r2 = m_util.norm(r2, sz, is_signed);
|
||||
|
||||
if (is_num1 && is_num2) {
|
||||
result = r1 <= r2 ? m().mk_true() : m().mk_false();
|
||||
result = m().mk_bool_val(r1 <= r2);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
@ -303,6 +507,24 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref
|
|||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
if (m_le_extra) {
|
||||
const br_status cst = rw_leq_concats(is_signed, a, b, result);
|
||||
if (cst != BR_FAILED) {
|
||||
TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n")
|
||||
<< mk_ismt2_pp(a, m(), 2) << "\n" << mk_ismt2_pp(b, m(), 2) << "\n--->\n"<< mk_ismt2_pp(result, m(), 2) << "\n";);
|
||||
return cst;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_le_extra) {
|
||||
const br_status cst = rw_leq_overflow(is_signed, a, b, result);
|
||||
if (cst != BR_FAILED) {
|
||||
TRACE("le_extra", tout << (is_signed ? "bv_sle\n" : "bv_ule\n")
|
||||
<< mk_ismt2_pp(a, m(), 2) << "\n" << mk_ismt2_pp(b, m(), 2) << "\n--->\n"<< mk_ismt2_pp(result, m(), 2) << "\n";);
|
||||
return cst;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!is_signed && m_util.is_concat(b) && to_app(b)->get_num_args() == 2 && m_util.is_zero(to_app(b)->get_arg(0))) {
|
||||
//
|
||||
|
@ -360,6 +582,88 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
// attempt to chop off bits that are above the position high for bv_mul and bv_add,
|
||||
// returns how many bits were chopped off
|
||||
// e.g. (bvadd(concat #b11 p) #x1)) with high=1, returns 2 and sets result = p + #b01
|
||||
// the sz of results is the sz of arg minus the return value
|
||||
unsigned bv_rewriter::propagate_extract(unsigned high, expr * arg, expr_ref & result) {
|
||||
if (!m_util.is_bv_add(arg) && !m_util.is_bv_mul(arg))
|
||||
return 0;
|
||||
const unsigned sz = m_util.get_bv_size(arg);
|
||||
const unsigned to_remove = high + 1 < sz ? sz - high - 1 : 0;
|
||||
if (to_remove == 0)
|
||||
return 0; // high goes to the top, nothing to do
|
||||
const app * const a = to_app(arg);
|
||||
const unsigned num = a->get_num_args();
|
||||
bool all_numerals = true;
|
||||
unsigned removable = to_remove;
|
||||
numeral val;
|
||||
unsigned curr_first_sz = -1;
|
||||
// calculate how much can be removed
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * const curr = a->get_arg(i);
|
||||
const bool curr_is_conc = m_util.is_concat(curr);
|
||||
if (curr_is_conc && to_app(curr)->get_num_args() == 0) continue;
|
||||
expr * const curr_first = curr_is_conc ? to_app(curr)->get_arg(0) : curr;
|
||||
if (!all_numerals) {
|
||||
if (removable != m_util.get_bv_size(curr_first))
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
if (is_numeral(curr_first, val, curr_first_sz)) {
|
||||
removable = std::min(removable, curr_first_sz);
|
||||
} else {
|
||||
all_numerals = false;
|
||||
curr_first_sz = m_util.get_bv_size(curr_first);
|
||||
if (curr_first_sz > removable) return 0;
|
||||
removable = curr_first_sz;
|
||||
}
|
||||
if (removable == 0) return 0;
|
||||
}
|
||||
// perform removal
|
||||
SASSERT(removable <= to_remove);
|
||||
ptr_buffer<expr> new_args;
|
||||
ptr_buffer<expr> new_concat_args;
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * const curr = a->get_arg(i);
|
||||
const bool curr_is_conc = m_util.is_concat(curr);
|
||||
if (curr_is_conc && to_app(curr)->get_num_args() == 0) continue;
|
||||
expr * const curr_first = curr_is_conc ? to_app(curr)->get_arg(0) : curr;
|
||||
expr * new_first = NULL;
|
||||
if (is_numeral(curr_first, val, curr_first_sz)) {
|
||||
SASSERT(curr_first_sz >= removable);
|
||||
const unsigned new_num_sz = curr_first_sz - removable;
|
||||
new_first = new_num_sz ? mk_numeral(val, new_num_sz) : NULL;
|
||||
}
|
||||
expr * new_arg = NULL;
|
||||
if (curr_is_conc) {
|
||||
const unsigned conc_num = to_app(curr)->get_num_args();
|
||||
if (new_first) {
|
||||
new_concat_args.reset();
|
||||
new_concat_args.push_back(new_first);
|
||||
for (unsigned j = 1; j < conc_num; ++j)
|
||||
new_concat_args.push_back(to_app(curr)->get_arg(j));
|
||||
new_arg = m_util.mk_concat(new_concat_args.size(), new_concat_args.c_ptr());
|
||||
} else {
|
||||
// remove first element of concat
|
||||
expr * const * const old_conc_args = to_app(curr)->get_args();
|
||||
switch (conc_num) {
|
||||
case 0: UNREACHABLE(); break;
|
||||
case 1: new_arg = NULL; break;
|
||||
case 2: new_arg = to_app(curr)->get_arg(1); break;
|
||||
default: new_arg = m_util.mk_concat(conc_num - 1, old_conc_args + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
new_arg = new_first;
|
||||
}
|
||||
if (new_arg) new_args.push_back(new_arg);
|
||||
}
|
||||
result = m().mk_app(get_fid(), a->get_decl()->get_decl_kind(), new_args.size(), new_args.c_ptr());
|
||||
SASSERT(m_util.is_bv(result));
|
||||
return removable;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result) {
|
||||
unsigned sz = get_bv_size(arg);
|
||||
SASSERT(sz > 0);
|
||||
|
@ -463,6 +767,17 @@ br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_
|
|||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
if (m_extract_prop && (high >= low)) {
|
||||
expr_ref ep_res(m());
|
||||
const unsigned ep_rm = propagate_extract(high, arg, ep_res);
|
||||
if (ep_rm != 0) {
|
||||
result = m_mk_extract(high, low, ep_res);
|
||||
TRACE("extract_prop", tout << mk_ismt2_pp(arg, m()) << "\n[" << high <<"," << low << "]\n" << ep_rm << "---->\n"
|
||||
<< mk_ismt2_pp(result.get(), m()) << "\n";);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
||||
if (m().is_ite(arg)) {
|
||||
result = m().mk_ite(to_app(arg)->get_arg(0),
|
||||
m_mk_extract(high, low, to_app(arg)->get_arg(1)),
|
||||
|
@ -836,6 +1151,22 @@ bool bv_rewriter::is_minus_one_core(expr * arg) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool bv_rewriter::is_negatable(expr * arg, expr_ref& x) {
|
||||
numeral r;
|
||||
unsigned bv_size;
|
||||
if (is_numeral(arg, r, bv_size)) {
|
||||
r = bitwise_not(bv_size, r);
|
||||
x = mk_numeral(r, bv_size);
|
||||
return true;
|
||||
}
|
||||
if (m_util.is_bv_not(arg)) {
|
||||
SASSERT(to_app(arg)->get_num_args() == 1);
|
||||
x = to_app(arg)->get_arg(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bv_rewriter::is_x_minus_one(expr * arg, expr * & x) {
|
||||
if (is_add(arg) && to_app(arg)->get_num_args() == 2) {
|
||||
if (is_minus_one_core(to_app(arg)->get_arg(0))) {
|
||||
|
@ -1040,6 +1371,7 @@ br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) {
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
expr_ref_buffer new_args(m());
|
||||
numeral v1;
|
||||
|
@ -1100,6 +1432,8 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re
|
|||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
br_status bv_rewriter::mk_zero_extend(unsigned n, expr * arg, expr_ref & result) {
|
||||
if (n == 0) {
|
||||
result = arg;
|
||||
|
@ -1526,6 +1860,35 @@ br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) {
|
|||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
if (m_bvnot_simpl) {
|
||||
expr *s(0), *t(0);
|
||||
if (m_util.is_bv_mul(arg, s, t)) {
|
||||
// ~(-1 * x) --> (x - 1)
|
||||
bv_size = m_util.get_bv_size(s);
|
||||
if (m_util.is_allone(s)) {
|
||||
rational minus_one = (rational::power_of_two(bv_size) - rational::one());
|
||||
result = m_util.mk_bv_add(m_util.mk_numeral(minus_one, bv_size), t);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
if (m_util.is_allone(t)) {
|
||||
rational minus_one = (rational::power_of_two(bv_size) - rational::one());
|
||||
result = m_util.mk_bv_add(m_util.mk_numeral(minus_one, bv_size), s);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
if (m_util.is_bv_add(arg, s, t)) {
|
||||
expr_ref ns(m());
|
||||
expr_ref nt(m());
|
||||
// ~(x + y) --> (~x + ~y + 1) when x and y are easy to negate
|
||||
if (is_negatable(t, nt) && is_negatable(s, ns)) {
|
||||
bv_size = m_util.get_bv_size(s);
|
||||
expr * nargs[3] = { m_util.mk_numeral(rational::one(), bv_size), ns.get(), nt.get() };
|
||||
result = m().mk_app(m_util.get_fid(), OP_BADD, 3, nargs);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
@ -1777,7 +2140,7 @@ br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
|
||||
if (is_numeral(lhs)) {
|
||||
SASSERT(is_numeral(rhs));
|
||||
result = lhs == rhs ? m().mk_true() : m().mk_false();
|
||||
result = m().mk_bool_val(lhs == rhs);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
@ -2068,6 +2431,16 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool bv_rewriter::is_urem_any(expr * e, expr * & dividend, expr * & divisor) {
|
||||
if (!m_util.is_bv_urem(e) && !m_util.is_bv_uremi(e))
|
||||
return false;
|
||||
const app * const a = to_app(e);
|
||||
SASSERT(a->get_num_args() == 2);
|
||||
dividend = a->get_arg(0);
|
||||
divisor = a->get_arg(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
||||
if (lhs == rhs) {
|
||||
result = m().mk_true();
|
||||
|
@ -2119,6 +2492,25 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
return st;
|
||||
}
|
||||
|
||||
if (m_urem_simpl) {
|
||||
expr * dividend;
|
||||
expr * divisor;
|
||||
numeral divisor_val, rhs_val;
|
||||
unsigned divisor_sz, rhs_sz;
|
||||
if (is_urem_any(lhs, dividend, divisor)
|
||||
&& is_numeral(rhs, rhs_val, rhs_sz)
|
||||
&& is_numeral(divisor, divisor_val, divisor_sz)) {
|
||||
if (rhs_val >= divisor_val) {//(= (bvurem x c1) c2) where c2 >= c1
|
||||
result = m().mk_false();
|
||||
return BR_DONE;
|
||||
}
|
||||
if ((divisor_val + rhs_val) >= rational::power_of_two(divisor_sz)) {//(= (bvurem x c1) c2) where c1+c2 >= 2^width
|
||||
result = m().mk_eq(dividend, rhs);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref new_lhs(m());
|
||||
expr_ref new_rhs(m());
|
||||
|
||||
|
@ -2126,7 +2518,7 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
st = cancel_monomials(lhs, rhs, false, new_lhs, new_rhs);
|
||||
if (st != BR_FAILED) {
|
||||
if (is_numeral(new_lhs) && is_numeral(new_rhs)) {
|
||||
result = new_lhs == new_rhs ? m().mk_true() : m().mk_false();
|
||||
result = m().mk_bool_val(new_lhs == new_rhs);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
|
@ -2186,6 +2578,137 @@ br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & res
|
|||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_ite_core(expr * c, expr * t, expr * e, expr_ref & result) {
|
||||
TRACE("bv_ite", tout << "mk_ite_core:\n" << mk_ismt2_pp(c, m()) << "?\n"
|
||||
<< mk_ismt2_pp(t, m()) << "\n:" << mk_ismt2_pp(e, m()) << "\n";);
|
||||
if (m().are_equal(t, e)) {
|
||||
result = e;
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
if (m().is_not(c)) {
|
||||
result = m().mk_ite(to_app(c)->get_arg(0), e, t);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
if (m_ite2id && m().is_eq(c) && is_bv(t) && is_bv(e)) {
|
||||
// detect when ite is actually some simple function based on the pattern (lhs=rhs) ? t : e
|
||||
expr * lhs = to_app(c)->get_arg(0);
|
||||
expr * rhs = to_app(c)->get_arg(1);
|
||||
|
||||
if (is_bv(rhs)) {
|
||||
if (is_numeral(lhs))
|
||||
std::swap(lhs, rhs);
|
||||
|
||||
if ( (m().are_equal(lhs, t) && m().are_equal(rhs, e))
|
||||
|| (m().are_equal(lhs, e) && m().are_equal(rhs, t))) {
|
||||
// (a = b ? a : b) is b. (a = b ? b : a) is a
|
||||
result = e;
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
const unsigned sz = m_util.get_bv_size(rhs);
|
||||
if (sz == 1) { // detect (lhs = N) ? C : D, where N, C, D are 1 bit numberals
|
||||
numeral rhs_n, e_n, t_n;
|
||||
unsigned rhs_sz, e_sz, t_sz;
|
||||
if (is_numeral(rhs, rhs_n, rhs_sz)
|
||||
&& is_numeral(t, t_n, t_sz) && is_numeral(e, e_n, e_sz)) {
|
||||
if (t_sz == 1) {
|
||||
SASSERT(rhs_sz == sz && e_sz == sz && t_sz == sz);
|
||||
SASSERT(!m().are_equal(t, e));
|
||||
result = m().are_equal(rhs, t) ? lhs : m_util.mk_bv_not(lhs);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num == 2);
|
||||
unsigned bv_sz;
|
||||
rational a0_val, a1_val;
|
||||
|
||||
bool is_num1 = is_numeral(args[0], a0_val, bv_sz);
|
||||
bool is_num2 = is_numeral(args[1], a1_val, bv_sz);
|
||||
if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (is_num1 && is_num2) {
|
||||
rational mr = a0_val * a1_val;
|
||||
rational lim = rational::power_of_two(bv_sz-1);
|
||||
result = m().mk_bool_val(mr < lim);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num == 2);
|
||||
unsigned bv_sz;
|
||||
rational a0_val, a1_val;
|
||||
|
||||
bool is_num1 = is_numeral(args[0], a0_val, bv_sz);
|
||||
bool is_num2 = is_numeral(args[1], a1_val, bv_sz);
|
||||
if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (is_num1 && is_num2) {
|
||||
rational mr = a0_val * a1_val;
|
||||
rational lim = rational::power_of_two(bv_sz);
|
||||
result = m().mk_bool_val(mr < lim);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status bv_rewriter::mk_bvsmul_no_underflow(unsigned num, expr * const * args, expr_ref & result) {
|
||||
SASSERT(num == 2);
|
||||
unsigned bv_sz;
|
||||
rational a0_val, a1_val;
|
||||
|
||||
bool is_num1 = is_numeral(args[0], a0_val, bv_sz);
|
||||
bool is_num2 = is_numeral(args[1], a1_val, bv_sz);
|
||||
if (is_num1 && (a0_val.is_zero() || a0_val.is_one())) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
if (is_num2 && (a1_val.is_zero() || a1_val.is_one())) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
if (is_num1 && is_num2) {
|
||||
rational ul = rational::power_of_two(bv_sz);
|
||||
rational lim = rational::power_of_two(bv_sz-1);
|
||||
if (a0_val >= lim) a0_val -= ul;
|
||||
if (a1_val >= lim) a1_val -= ul;
|
||||
rational mr = a0_val * a1_val;
|
||||
rational neg_lim = -lim;
|
||||
TRACE("bv_rewriter_bvsmul_no_underflow", tout << "a0:" << a0_val << " a1:" << a1_val << " mr:" << mr << " neg_lim:" << neg_lim << std::endl;);
|
||||
result = m().mk_bool_val(mr >= neg_lim);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
||||
template class poly_rewriter<bv_rewriter_core>;
|
||||
|
||||
|
|
|
@ -56,11 +56,16 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
|
|||
bool m_bit2bool;
|
||||
bool m_blast_eq_value;
|
||||
bool m_mkbv2num;
|
||||
bool m_ite2id;
|
||||
bool m_split_concat_eq;
|
||||
bool m_udiv2mul;
|
||||
bool m_bvnot2arith;
|
||||
bool m_bv_sort_ac;
|
||||
bool m_trailing;
|
||||
bool m_extract_prop;
|
||||
bool m_bvnot_simpl;
|
||||
bool m_le_extra;
|
||||
bool m_urem_simpl;
|
||||
|
||||
bool is_zero_bit(expr * x, unsigned idx);
|
||||
|
||||
|
@ -70,13 +75,18 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
|
|||
br_status mk_sle(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_sge(expr * a, expr * b, expr_ref & result);
|
||||
br_status mk_slt(expr * a, expr * b, expr_ref & result);
|
||||
br_status rw_leq_concats(bool is_signed, expr * a, expr * b, expr_ref & result);
|
||||
bool are_eq_upto_num(expr * a, expr * b, expr_ref& common, numeral& a0_val, numeral& b0_val);
|
||||
br_status rw_leq_overflow(bool is_signed, expr * _a, expr * _b, expr_ref & result);
|
||||
br_status mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref & result);
|
||||
|
||||
br_status mk_concat(unsigned num_args, expr * const * args, expr_ref & result);
|
||||
unsigned propagate_extract(unsigned high, expr * arg, expr_ref & result);
|
||||
br_status mk_extract(unsigned high, unsigned low, expr * arg, expr_ref & result);
|
||||
br_status mk_repeat(unsigned n, expr * arg, expr_ref & result);
|
||||
br_status mk_zero_extend(unsigned n, expr * arg, expr_ref & result);
|
||||
br_status mk_sign_extend(unsigned n, expr * arg, expr_ref & result);
|
||||
bool is_negatable(expr * arg, expr_ref& x);
|
||||
br_status mk_bv_not(expr * arg, expr_ref & result);
|
||||
br_status mk_bv_or(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bv_xor(unsigned num, expr * const * args, expr_ref & result);
|
||||
|
@ -123,6 +133,9 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
|
|||
br_status mk_blast_eq_value(expr * lhs, expr * rhs, expr_ref & result);
|
||||
br_status mk_eq_concat(expr * lhs, expr * rhs, expr_ref & result);
|
||||
br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bvsmul_no_overflow(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result);
|
||||
br_status mk_bvsmul_no_underflow(unsigned num, expr * const * args, expr_ref & result);
|
||||
bool is_minus_one_times_t(expr * arg);
|
||||
void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result);
|
||||
|
||||
|
@ -136,6 +149,7 @@ class bv_rewriter : public poly_rewriter<bv_rewriter_core> {
|
|||
|
||||
void updt_local_params(params_ref const & p);
|
||||
|
||||
expr * concat(unsigned num_args, expr * const * args);
|
||||
public:
|
||||
bv_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
poly_rewriter<bv_rewriter_core>(m, p),
|
||||
|
@ -164,7 +178,9 @@ public:
|
|||
result = m().mk_app(f, num_args, args);
|
||||
}
|
||||
|
||||
bool is_urem_any(expr * e, expr * & dividend, expr * & divisor);
|
||||
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
|
||||
br_status mk_ite_core(expr * c, expr * t, expr * e, expr_ref & resul);
|
||||
|
||||
bool hi_div0() const { return m_hi_div0; }
|
||||
|
||||
|
|
|
@ -10,5 +10,10 @@ def_module_params(module_name='rewriter',
|
|||
("mul2concat", BOOL, False, "replace multiplication by a power of two into a concatenation"),
|
||||
("bvnot2arith", BOOL, False, "replace (bvnot x) with (bvsub -1 x)"),
|
||||
("bv_sort_ac", BOOL, False, "sort the arguments of all AC operators"),
|
||||
("bv_trailing", BOOL, False, "lean removal of trailing zeros")
|
||||
("bv_trailing", BOOL, False, "lean removal of trailing zeros"),
|
||||
("bv_extract_prop", BOOL, False, "attempt to partially propagate extraction inwards"),
|
||||
("bv_not_simpl", BOOL, False, "apply simplifications for bvnot"),
|
||||
("bv_ite2id", BOOL, False, "rewrite ite that can be simplified to identity"),
|
||||
("bv_le_extra", BOOL, False, "additional bu_(u/s)le simplifications"),
|
||||
("bv_urem_simpl", BOOL, False, "additional simplification for bvurem")
|
||||
))
|
||||
|
|
|
@ -36,11 +36,14 @@ struct bv_trailing::imp {
|
|||
: m_mk_extract(mk_extract)
|
||||
, m_util(mk_extract.bvutil())
|
||||
, m(mk_extract.m()) {
|
||||
TRACE("bv-trailing", tout << "ctor\n";);
|
||||
|
||||
for (unsigned i = 0; i <= TRAILING_DEPTH; ++i)
|
||||
m_count_cache[i] = NULL;
|
||||
}
|
||||
|
||||
virtual ~imp() {
|
||||
TRACE("bv-trailing", tout << "dtor\n";);
|
||||
reset_cache(0);
|
||||
}
|
||||
|
||||
|
@ -67,10 +70,8 @@ struct bv_trailing::imp {
|
|||
}
|
||||
expr_ref out1(m);
|
||||
expr_ref out2(m);
|
||||
DEBUG_CODE(
|
||||
const unsigned rm1 = remove_trailing(e1, min, out1, TRAILING_DEPTH);
|
||||
const unsigned rm2 = remove_trailing(e2, min, out2, TRAILING_DEPTH);
|
||||
SASSERT(rm1 == min && rm2 == min););
|
||||
VERIFY(min == remove_trailing(e1, min, out1, TRAILING_DEPTH));
|
||||
VERIFY(min == remove_trailing(e2, min, out2, TRAILING_DEPTH));
|
||||
const bool are_eq = m.are_equal(out1, out2);
|
||||
result = are_eq ? m.mk_true() : m.mk_eq(out1, out2);
|
||||
return are_eq ? BR_DONE : BR_REWRITE2;
|
||||
|
@ -339,6 +340,7 @@ struct bv_trailing::imp {
|
|||
if (depth == 0) return;
|
||||
if (m_count_cache[depth] == NULL)
|
||||
m_count_cache[depth] = alloc(map);
|
||||
SASSERT(!m_count_cache[depth]->contains(e));
|
||||
m.inc_ref(e);
|
||||
m_count_cache[depth]->insert(e, std::make_pair(min, max));
|
||||
TRACE("bv-trailing", tout << "caching@" << depth <<": " << mk_ismt2_pp(e, m) << '[' << m_util.get_bv_size(e) << "]\n: " << min << '-' << max << "\n";);
|
||||
|
@ -361,11 +363,13 @@ struct bv_trailing::imp {
|
|||
return true;
|
||||
}
|
||||
|
||||
void reset_cache(unsigned condition) {
|
||||
void reset_cache(const unsigned condition) {
|
||||
SASSERT(m_count_cache[0] == NULL);
|
||||
for (unsigned i = 1; i <= TRAILING_DEPTH; ++i) {
|
||||
if (m_count_cache[i] == NULL) continue;
|
||||
if (m_count_cache[i]->size() < condition) continue;
|
||||
TRACE("bv-trailing", tout << "may reset cache " << i << " " << condition << "\n";);
|
||||
if (condition && m_count_cache[i]->size() < condition) continue;
|
||||
TRACE("bv-trailing", tout << "reset cache " << i << "\n";);
|
||||
map::iterator it = m_count_cache[i]->begin();
|
||||
map::iterator end = m_count_cache[i]->end();
|
||||
for (; it != end; ++it) m.dec_ref(it->m_key);
|
||||
|
|
293
src/ast/rewriter/enum2bv_rewriter.cpp
Normal file
293
src/ast/rewriter/enum2bv_rewriter.cpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
enum2bv_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Conversion from enumeration types to bit-vectors.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2016-10-18
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#include"rewriter.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"enum2bv_rewriter.h"
|
||||
#include"ast_util.h"
|
||||
#include"ast_pp.h"
|
||||
|
||||
struct enum2bv_rewriter::imp {
|
||||
ast_manager& m;
|
||||
params_ref m_params;
|
||||
obj_map<func_decl, func_decl*> m_enum2bv;
|
||||
obj_map<func_decl, func_decl*> m_bv2enum;
|
||||
obj_map<func_decl, expr*> m_enum2def;
|
||||
expr_ref_vector m_bounds;
|
||||
datatype_util m_dt;
|
||||
func_decl_ref_vector m_enum_consts;
|
||||
func_decl_ref_vector m_enum_bvs;
|
||||
expr_ref_vector m_enum_defs;
|
||||
unsigned_vector m_enum_consts_lim;
|
||||
unsigned m_num_translated;
|
||||
i_sort_pred* m_sort_pred;
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
imp& m_imp;
|
||||
ast_manager& m;
|
||||
datatype_util m_dt;
|
||||
bv_util m_bv;
|
||||
|
||||
rw_cfg(imp& i, ast_manager & m) :
|
||||
m_imp(i),
|
||||
m(m),
|
||||
m_dt(m),
|
||||
m_bv(m)
|
||||
{}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
expr_ref a0(m), a1(m);
|
||||
expr_ref_vector _args(m);
|
||||
if (m.is_eq(f) && reduce_arg(args[0], a0) && reduce_arg(args[1], a1)) {
|
||||
result = m.mk_eq(a0, a1);
|
||||
return BR_DONE;
|
||||
}
|
||||
else if (m.is_distinct(f) && reduce_args(num, args, _args)) {
|
||||
result = m.mk_distinct(_args.size(), _args.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
else if (m_dt.is_recognizer(f) && reduce_arg(args[0], a0)) {
|
||||
unsigned idx = m_dt.get_recognizer_constructor_idx(f);
|
||||
a1 = m_bv.mk_numeral(rational(idx), get_sort(a0));
|
||||
result = m.mk_eq(a0, a1);
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
check_for_fd(num, args);
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
bool reduce_args(unsigned sz, expr*const* as, expr_ref_vector& result) {
|
||||
expr_ref tmp(m);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (!reduce_arg(as[i], tmp)) return false;
|
||||
result.push_back(tmp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void throw_non_fd(expr* e) {
|
||||
std::stringstream strm;
|
||||
strm << "unabled nested data-type expression " << mk_pp(e, m);
|
||||
throw rewriter_exception(strm.str().c_str());
|
||||
}
|
||||
|
||||
void check_for_fd(unsigned n, expr* const* args) {
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
if (m_imp.is_fd(get_sort(args[i]))) {
|
||||
throw_non_fd(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool reduce_arg(expr* a, expr_ref& result) {
|
||||
|
||||
sort* s = get_sort(a);
|
||||
if (!m_imp.is_fd(s)) {
|
||||
return false;
|
||||
}
|
||||
unsigned bv_size = get_bv_size(s);
|
||||
|
||||
if (is_var(a)) {
|
||||
result = m.mk_var(to_var(a)->get_idx(), m_bv.mk_sort(bv_size));
|
||||
return true;
|
||||
}
|
||||
SASSERT(is_app(a));
|
||||
func_decl* f = to_app(a)->get_decl();
|
||||
if (m_dt.is_constructor(f)) {
|
||||
unsigned idx = m_dt.get_constructor_idx(f);
|
||||
result = m_bv.mk_numeral(idx, bv_size);
|
||||
}
|
||||
else if (is_uninterp_const(a)) {
|
||||
func_decl* f_fresh;
|
||||
if (m_imp.m_enum2bv.find(f, f_fresh)) {
|
||||
result = m.mk_const(f_fresh);
|
||||
return true;
|
||||
}
|
||||
|
||||
// create a fresh variable, add bounds constraints for it.
|
||||
unsigned nc = m_dt.get_datatype_num_constructors(s);
|
||||
result = m.mk_fresh_const(f->get_name().str().c_str(), m_bv.mk_sort(bv_size));
|
||||
f_fresh = to_app(result)->get_decl();
|
||||
if (!is_power_of_two(nc) || nc == 1) {
|
||||
m_imp.m_bounds.push_back(m_bv.mk_ule(result, m_bv.mk_numeral(nc-1, bv_size)));
|
||||
}
|
||||
expr_ref f_def(m);
|
||||
ptr_vector<func_decl> const& cs = *m_dt.get_datatype_constructors(s);
|
||||
f_def = m.mk_const(cs[nc-1]);
|
||||
for (unsigned i = nc - 1; i > 0; ) {
|
||||
--i;
|
||||
f_def = m.mk_ite(m.mk_eq(result, m_bv.mk_numeral(i,bv_size)), m.mk_const(cs[i]), f_def);
|
||||
}
|
||||
m_imp.m_enum2def.insert(f, f_def);
|
||||
m_imp.m_enum2bv.insert(f, f_fresh);
|
||||
m_imp.m_bv2enum.insert(f_fresh, f);
|
||||
m_imp.m_enum_consts.push_back(f);
|
||||
m_imp.m_enum_bvs.push_back(f_fresh);
|
||||
m_imp.m_enum_defs.push_back(f_def);
|
||||
}
|
||||
else {
|
||||
throw_non_fd(a);
|
||||
}
|
||||
++m_imp.m_num_translated;
|
||||
return true;
|
||||
}
|
||||
|
||||
ptr_buffer<sort> m_sorts;
|
||||
|
||||
bool reduce_quantifier(
|
||||
quantifier * q,
|
||||
expr * old_body,
|
||||
expr * const * new_patterns,
|
||||
expr * const * new_no_patterns,
|
||||
expr_ref & result,
|
||||
proof_ref & result_pr) {
|
||||
m_sorts.reset();
|
||||
expr_ref_vector bounds(m);
|
||||
bool found = false;
|
||||
for (unsigned i = 0; i < q->get_num_decls(); ++i) {
|
||||
sort* s = q->get_decl_sort(i);
|
||||
if (m_imp.is_fd(s)) {
|
||||
unsigned bv_size = get_bv_size(s);
|
||||
m_sorts.push_back(m_bv.mk_sort(bv_size));
|
||||
unsigned nc = m_dt.get_datatype_num_constructors(s);
|
||||
if (!is_power_of_two(nc) || nc == 1) {
|
||||
bounds.push_back(m_bv.mk_ule(m.mk_var(q->get_num_decls()-i-1, m_sorts[i]), m_bv.mk_numeral(nc-1, bv_size)));
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
m_sorts.push_back(s);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
expr_ref new_body_ref(old_body, m), tmp(m);
|
||||
if (!bounds.empty()) {
|
||||
if (q->is_forall()) {
|
||||
new_body_ref = m.mk_implies(mk_and(bounds), new_body_ref);
|
||||
}
|
||||
else {
|
||||
bounds.push_back(new_body_ref);
|
||||
new_body_ref = mk_and(bounds);
|
||||
}
|
||||
}
|
||||
result = m.mk_quantifier(q->is_forall(), q->get_num_decls(), m_sorts.c_ptr(), q->get_decl_names(), new_body_ref,
|
||||
q->get_weight(), q->get_qid(), q->get_skid(),
|
||||
q->get_num_patterns(), new_patterns,
|
||||
q->get_num_no_patterns(), new_no_patterns);
|
||||
result_pr = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned get_bv_size(sort* s) {
|
||||
unsigned nc = m_dt.get_datatype_num_constructors(s);
|
||||
unsigned bv_size = 1;
|
||||
while ((unsigned)(1 << bv_size) < nc) {
|
||||
++bv_size;
|
||||
}
|
||||
return bv_size;
|
||||
}
|
||||
};
|
||||
|
||||
struct rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
|
||||
rw(imp& t, ast_manager & m, params_ref const & p) :
|
||||
rewriter_tpl<rw_cfg>(m, m.proofs_enabled(), m_cfg),
|
||||
m_cfg(t, m) {
|
||||
}
|
||||
};
|
||||
|
||||
rw m_rw;
|
||||
|
||||
imp(ast_manager& m, params_ref const& p):
|
||||
m(m), m_params(p), m_bounds(m),
|
||||
m_dt(m),
|
||||
m_enum_consts(m),
|
||||
m_enum_bvs(m),
|
||||
m_enum_defs(m),
|
||||
m_num_translated(0),
|
||||
m_sort_pred(0),
|
||||
m_rw(*this, m, p) {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {}
|
||||
unsigned get_num_steps() const { return m_rw.get_num_steps(); }
|
||||
void cleanup() { m_rw.cleanup(); }
|
||||
void operator()(expr * e, expr_ref & result, proof_ref & result_proof) {
|
||||
m_rw(e, result, result_proof);
|
||||
}
|
||||
void push() {
|
||||
m_enum_consts_lim.push_back(m_enum_consts.size());
|
||||
}
|
||||
void pop(unsigned num_scopes) {
|
||||
SASSERT(m_bounds.empty()); // bounds must be flushed before pop.
|
||||
if (num_scopes > 0) {
|
||||
SASSERT(num_scopes <= m_enum_consts_lim.size());
|
||||
unsigned new_sz = m_enum_consts_lim.size() - num_scopes;
|
||||
unsigned lim = m_enum_consts_lim[new_sz];
|
||||
for (unsigned i = m_enum_consts.size(); i > lim; ) {
|
||||
--i;
|
||||
func_decl* f = m_enum_consts[i].get();
|
||||
func_decl* f_fresh = m_enum2bv.find(f);
|
||||
m_bv2enum.erase(f_fresh);
|
||||
m_enum2bv.erase(f);
|
||||
m_enum2def.erase(f);
|
||||
}
|
||||
m_enum_consts_lim.resize(new_sz);
|
||||
m_enum_consts.resize(lim);
|
||||
m_enum_defs.resize(lim);
|
||||
m_enum_bvs.resize(lim);
|
||||
}
|
||||
m_rw.reset();
|
||||
}
|
||||
|
||||
void flush_side_constraints(expr_ref_vector& side_constraints) {
|
||||
side_constraints.append(m_bounds);
|
||||
m_bounds.reset();
|
||||
}
|
||||
|
||||
bool is_fd(sort* s) {
|
||||
return m_dt.is_enum_sort(s) && (!m_sort_pred || (*m_sort_pred)(s));
|
||||
}
|
||||
|
||||
void set_is_fd(i_sort_pred* sp) {
|
||||
m_sort_pred = sp;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum2bv_rewriter::enum2bv_rewriter(ast_manager & m, params_ref const& p) { m_imp = alloc(imp, m, p); }
|
||||
enum2bv_rewriter::~enum2bv_rewriter() { dealloc(m_imp); }
|
||||
void enum2bv_rewriter::updt_params(params_ref const & p) { m_imp->updt_params(p); }
|
||||
ast_manager & enum2bv_rewriter::m() const { return m_imp->m; }
|
||||
unsigned enum2bv_rewriter::get_num_steps() const { return m_imp->get_num_steps(); }
|
||||
void enum2bv_rewriter::cleanup() { ast_manager& mgr = m(); params_ref p = m_imp->m_params; dealloc(m_imp); m_imp = alloc(imp, mgr, p); }
|
||||
obj_map<func_decl, func_decl*> const& enum2bv_rewriter::enum2bv() const { return m_imp->m_enum2bv; }
|
||||
obj_map<func_decl, func_decl*> const& enum2bv_rewriter::bv2enum() const { return m_imp->m_bv2enum; }
|
||||
obj_map<func_decl, expr*> const& enum2bv_rewriter::enum2def() const { return m_imp->m_enum2def; }
|
||||
void enum2bv_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(e, result, result_proof); }
|
||||
void enum2bv_rewriter::push() { m_imp->push(); }
|
||||
void enum2bv_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); }
|
||||
void enum2bv_rewriter::flush_side_constraints(expr_ref_vector& side_constraints) { m_imp->flush_side_constraints(side_constraints); }
|
||||
unsigned enum2bv_rewriter::num_translated() const { return m_imp->m_num_translated; }
|
||||
void enum2bv_rewriter::set_is_fd(i_sort_pred* sp) const { m_imp->set_is_fd(sp); }
|
48
src/ast/rewriter/enum2bv_rewriter.h
Normal file
48
src/ast/rewriter/enum2bv_rewriter.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
enum2bv_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Conversion from enumeration types to bit-vectors.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2016-10-18
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef ENUM_REWRITER_H_
|
||||
#define ENUM_REWRITER_H_
|
||||
|
||||
#include"datatype_decl_plugin.h"
|
||||
#include"rewriter_types.h"
|
||||
#include"expr_functors.h"
|
||||
|
||||
class enum2bv_rewriter {
|
||||
struct imp;
|
||||
imp* m_imp;
|
||||
public:
|
||||
enum2bv_rewriter(ast_manager & m, params_ref const& p);
|
||||
~enum2bv_rewriter();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
ast_manager & m() const;
|
||||
unsigned get_num_steps() const;
|
||||
void cleanup();
|
||||
obj_map<func_decl, func_decl*> const& enum2bv() const;
|
||||
obj_map<func_decl, func_decl*> const& bv2enum() const;
|
||||
obj_map<func_decl, expr*> const& enum2def() const;
|
||||
void operator()(expr * e, expr_ref & result, proof_ref & result_proof);
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void flush_side_constraints(expr_ref_vector& side_constraints);
|
||||
unsigned num_translated() const;
|
||||
void set_is_fd(i_sort_pred* sp) const;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -18,11 +18,12 @@ Notes:
|
|||
--*/
|
||||
#include"fpa_rewriter.h"
|
||||
#include"fpa_rewriter_params.hpp"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
||||
fpa_rewriter::fpa_rewriter(ast_manager & m, params_ref const & p) :
|
||||
m_util(m),
|
||||
m_fm(m_util.fm()),
|
||||
m_hi_fp_unspecified(true) {
|
||||
m_hi_fp_unspecified(false) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
|
@ -98,7 +99,7 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
|
|||
case OP_FPA_INTERNAL_MIN_UNSPECIFIED:
|
||||
case OP_FPA_INTERNAL_MAX_UNSPECIFIED:
|
||||
SASSERT(num_args == 2); st = BR_FAILED; break;
|
||||
|
||||
|
||||
case OP_FPA_INTERNAL_BVWRAP: SASSERT(num_args == 1); st = mk_bvwrap(args[0], result); break;
|
||||
case OP_FPA_INTERNAL_BV2RM: SASSERT(num_args == 1); st = mk_bv2rm(args[0], result); break;
|
||||
|
||||
|
@ -117,34 +118,40 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
|
|||
|
||||
br_status fpa_rewriter::mk_to_ubv_unspecified(unsigned ebits, unsigned sbits, unsigned width, expr_ref & result) {
|
||||
bv_util bu(m());
|
||||
if (m_hi_fp_unspecified)
|
||||
if (m_hi_fp_unspecified) {
|
||||
// The "hardware interpretation" is 0.
|
||||
result = bu.mk_numeral(0, width);
|
||||
else
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
result = m_util.mk_internal_to_ubv_unspecified(ebits, sbits, width);
|
||||
|
||||
return BR_DONE;
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
br_status fpa_rewriter::mk_to_sbv_unspecified(unsigned ebits, unsigned sbits, unsigned width, expr_ref & result) {
|
||||
bv_util bu(m());
|
||||
if (m_hi_fp_unspecified)
|
||||
if (m_hi_fp_unspecified) {
|
||||
// The "hardware interpretation" is 0.
|
||||
result = bu.mk_numeral(0, width);
|
||||
else
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
result = m_util.mk_internal_to_sbv_unspecified(ebits, sbits, width);
|
||||
|
||||
return BR_DONE;
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
br_status fpa_rewriter::mk_to_real_unspecified(unsigned ebits, unsigned sbits, expr_ref & result) {
|
||||
if (m_hi_fp_unspecified)
|
||||
if (m_hi_fp_unspecified) {
|
||||
// The "hardware interpretation" is 0.
|
||||
result = m_util.au().mk_numeral(rational(0), false);
|
||||
else
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
result = m_util.mk_internal_to_real_unspecified(ebits, sbits);
|
||||
|
||||
return BR_DONE;
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
br_status fpa_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
|
@ -776,10 +783,8 @@ br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_
|
|||
m_util.is_numeral(arg2, v)) {
|
||||
const mpf & x = v.get();
|
||||
|
||||
if (m_fm.is_nan(v) || m_fm.is_inf(v) || m_fm.is_neg(v)) {
|
||||
mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
if (m_fm.is_nan(v) || m_fm.is_inf(v) || m_fm.is_neg(v))
|
||||
return mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
|
||||
bv_util bu(m());
|
||||
scoped_mpq q(m_fm.mpq_manager());
|
||||
|
@ -789,11 +794,13 @@ br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_
|
|||
rational ul, ll;
|
||||
ul = m_fm.m_powers2.m1(bv_sz);
|
||||
ll = rational(0);
|
||||
if (r >= ll && r <= ul)
|
||||
if (r >= ll && r <= ul) {
|
||||
result = bu.mk_numeral(r, bv_sz);
|
||||
return BR_DONE;
|
||||
}
|
||||
else
|
||||
mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
return BR_DONE;
|
||||
return mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
|
@ -810,10 +817,8 @@ br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_
|
|||
m_util.is_numeral(arg2, v)) {
|
||||
const mpf & x = v.get();
|
||||
|
||||
if (m_fm.is_nan(v) || m_fm.is_inf(v)) {
|
||||
mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
if (m_fm.is_nan(v) || m_fm.is_inf(v))
|
||||
return mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
|
||||
bv_util bu(m());
|
||||
scoped_mpq q(m_fm.mpq_manager());
|
||||
|
@ -823,46 +828,42 @@ br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_
|
|||
rational ul, ll;
|
||||
ul = m_fm.m_powers2.m1(bv_sz - 1);
|
||||
ll = - m_fm.m_powers2(bv_sz - 1);
|
||||
if (r >= ll && r <= ul)
|
||||
if (r >= ll && r <= ul) {
|
||||
result = bu.mk_numeral(r, bv_sz);
|
||||
return BR_DONE;
|
||||
}
|
||||
else
|
||||
mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
return BR_DONE;
|
||||
return mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result);
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
br_status fpa_rewriter::mk_to_ieee_bv(func_decl * f, expr * arg, expr_ref & result) {
|
||||
TRACE("fp_rewriter", tout << "to_ieee_bv of " << mk_ismt2_pp(arg, m()) << std::endl;);
|
||||
scoped_mpf v(m_fm);
|
||||
|
||||
if (m_util.is_numeral(arg, v)) {
|
||||
TRACE("fp_rewriter", tout << "to_ieee_bv numeral: " << m_fm.to_string(v) << std::endl;);
|
||||
bv_util bu(m());
|
||||
const mpf & x = v.get();
|
||||
|
||||
if (m_fm.is_nan(v)) {
|
||||
if (m_hi_fp_unspecified) {
|
||||
result = bu.mk_concat(bu.mk_numeral(0, 1),
|
||||
bu.mk_concat(bu.mk_numeral(-1, x.get_ebits()),
|
||||
bu.mk_concat(bu.mk_numeral(0, x.get_sbits() - 2),
|
||||
bu.mk_numeral(1, 1))));
|
||||
expr * args[4] = { bu.mk_numeral(0, 1),
|
||||
bu.mk_numeral(-1, x.get_ebits()),
|
||||
bu.mk_numeral(0, x.get_sbits() - 2),
|
||||
bu.mk_numeral(1, 1) };
|
||||
result = bu.mk_concat(4, args);
|
||||
}
|
||||
else {
|
||||
app_ref unspec(m()), mask(m()), extra(m());
|
||||
unspec = m_util.mk_internal_to_ieee_bv_unspecified(x.get_ebits(), x.get_sbits());
|
||||
mask = bu.mk_concat(bu.mk_numeral(0, 1),
|
||||
bu.mk_concat(bu.mk_numeral(-1, x.get_ebits()),
|
||||
bu.mk_concat(bu.mk_numeral(0, x.get_sbits() - 2),
|
||||
bu.mk_numeral(1, 1))));
|
||||
expr * args[2] = { unspec, mask };
|
||||
result = m().mk_app(bu.get_fid(), OP_BOR, 2, args);
|
||||
}
|
||||
return BR_REWRITE_FULL;
|
||||
else
|
||||
result = m_util.mk_internal_to_ieee_bv_unspecified(x.get_ebits(), x.get_sbits());
|
||||
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
else {
|
||||
scoped_mpz rz(m_fm.mpq_manager());
|
||||
m_fm.to_ieee_bv_mpz(v, rz);
|
||||
|
||||
result = bu.mk_numeral(rational(rz), x.get_ebits() + x.get_sbits());
|
||||
return BR_DONE;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
def_module_params(module_name='rewriter',
|
||||
class_name='fpa_rewriter_params',
|
||||
export=True,
|
||||
params=(("hi_fp_unspecified", BOOL, True, "use the 'hardware interpretation' for unspecified values in fp.to_ubv, fp.to_sbv, and fp.to_real"),
|
||||
params=(("hi_fp_unspecified", BOOL, False, "use the 'hardware interpretation' for unspecified values in fp.to_ubv, fp.to_sbv, fp.to_real, and fp.to_ieee_bv"),
|
||||
))
|
||||
|
|
477
src/ast/rewriter/pb2bv_rewriter.cpp
Normal file
477
src/ast/rewriter/pb2bv_rewriter.cpp
Normal file
|
@ -0,0 +1,477 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Conversion from pseudo-booleans to bit-vectors.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2016-10-23
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#include"rewriter.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"statistics.h"
|
||||
#include"pb2bv_rewriter.h"
|
||||
#include"sorting_network.h"
|
||||
#include"ast_util.h"
|
||||
#include"ast_pp.h"
|
||||
#include"lbool.h"
|
||||
|
||||
|
||||
struct pb2bv_rewriter::imp {
|
||||
|
||||
ast_manager& m;
|
||||
params_ref m_params;
|
||||
expr_ref_vector m_lemmas;
|
||||
func_decl_ref_vector m_fresh; // all fresh variables
|
||||
unsigned_vector m_fresh_lim;
|
||||
unsigned m_num_translated;
|
||||
|
||||
struct card2bv_rewriter {
|
||||
typedef expr* literal;
|
||||
typedef ptr_vector<expr> literal_vector;
|
||||
psort_nw<card2bv_rewriter> m_sort;
|
||||
ast_manager& m;
|
||||
imp& m_imp;
|
||||
arith_util au;
|
||||
pb_util pb;
|
||||
bv_util bv;
|
||||
expr_ref_vector m_trail;
|
||||
expr_ref_vector m_args;
|
||||
rational m_k;
|
||||
vector<rational> m_coeffs;
|
||||
|
||||
template<lbool is_le>
|
||||
expr_ref mk_le_ge(expr_ref_vector& fmls, expr* a, expr* b, expr* bound) {
|
||||
expr_ref x(m), y(m), result(m);
|
||||
unsigned nb = bv.get_bv_size(a);
|
||||
x = bv.mk_zero_extend(1, a);
|
||||
y = bv.mk_zero_extend(1, b);
|
||||
result = bv.mk_bv_add(x, y);
|
||||
x = bv.mk_extract(nb, nb, result);
|
||||
result = bv.mk_extract(nb-1, 0, result);
|
||||
if (is_le != l_false) {
|
||||
fmls.push_back(m.mk_eq(x, bv.mk_numeral(rational::zero(), 1)));
|
||||
fmls.push_back(bv.mk_ule(result, bound));
|
||||
}
|
||||
else {
|
||||
fmls.push_back(m.mk_eq(x, bv.mk_numeral(rational::one(), 1)));
|
||||
fmls.push_back(bv.mk_ule(bound, result));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// create a circuit of size sz*log(k)
|
||||
// by forming a binary tree adding pairs of values that are assumed <= k,
|
||||
// and in each step we check that the result is <= k by checking the overflow
|
||||
// bit and that the non-overflow bits are <= k.
|
||||
// The procedure for checking >= k is symmetric and checking for = k is
|
||||
// achieved by checking <= k on intermediary addends and the resulting sum is = k.
|
||||
//
|
||||
// is_le = l_true - <=
|
||||
// is_le = l_undef - =
|
||||
// is_le = l_false - >=
|
||||
//
|
||||
template<lbool is_le>
|
||||
expr_ref mk_le_ge(unsigned sz, expr * const* args, rational const & k) {
|
||||
TRACE("pb",
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
tout << m_coeffs[i] << "*" << mk_pp(args[i], m) << " ";
|
||||
if (i + 1 < sz && !m_coeffs[i+1].is_neg()) tout << "+ ";
|
||||
}
|
||||
switch (is_le) {
|
||||
case l_true: tout << "<= "; break;
|
||||
case l_undef: tout << "= "; break;
|
||||
case l_false: tout << ">= "; break;
|
||||
}
|
||||
tout << m_k << "\n";);
|
||||
if (k.is_zero()) {
|
||||
if (is_le != l_false) {
|
||||
return expr_ref(m.mk_not(mk_or(m, sz, args)), m);
|
||||
}
|
||||
else {
|
||||
return expr_ref(m.mk_true(), m);
|
||||
}
|
||||
}
|
||||
if (k.is_neg()) {
|
||||
return expr_ref((is_le == l_false)?m.mk_true():m.mk_false(), m);
|
||||
}
|
||||
SASSERT(k.is_pos());
|
||||
expr_ref zero(m), bound(m);
|
||||
expr_ref_vector es(m), fmls(m);
|
||||
unsigned nb = k.get_num_bits();
|
||||
zero = bv.mk_numeral(rational(0), nb);
|
||||
bound = bv.mk_numeral(k, nb);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
SASSERT(!m_coeffs[i].is_neg());
|
||||
if (m_coeffs[i] > k) {
|
||||
if (is_le != l_false) {
|
||||
fmls.push_back(m.mk_not(args[i]));
|
||||
}
|
||||
else {
|
||||
fmls.push_back(args[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
es.push_back(mk_ite(args[i], bv.mk_numeral(m_coeffs[i], nb), zero));
|
||||
}
|
||||
}
|
||||
while (es.size() > 1) {
|
||||
for (unsigned i = 0; i + 1 < es.size(); i += 2) {
|
||||
es[i/2] = mk_le_ge<is_le>(fmls, es[i].get(), es[i+1].get(), bound);
|
||||
}
|
||||
if ((es.size() % 2) == 1) {
|
||||
es[es.size()/2] = es.back();
|
||||
}
|
||||
es.shrink((1 + es.size())/2);
|
||||
}
|
||||
switch (is_le) {
|
||||
case l_true:
|
||||
return mk_and(fmls);
|
||||
case l_false:
|
||||
if (!es.empty()) {
|
||||
fmls.push_back(bv.mk_ule(bound, es.back()));
|
||||
}
|
||||
return mk_or(fmls);
|
||||
case l_undef:
|
||||
if (es.empty()) {
|
||||
fmls.push_back(m.mk_bool_val(k.is_zero()));
|
||||
}
|
||||
else {
|
||||
fmls.push_back(m.mk_eq(bound, es.back()));
|
||||
}
|
||||
return mk_and(fmls);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return expr_ref(m.mk_true(), m);
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref mk_bv(func_decl * f, unsigned sz, expr * const* args) {
|
||||
decl_kind kind = f->get_decl_kind();
|
||||
rational k = pb.get_k(f);
|
||||
m_coeffs.reset();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
m_coeffs.push_back(pb.get_coeff(f, i));
|
||||
}
|
||||
SASSERT(!k.is_neg());
|
||||
switch (kind) {
|
||||
case OP_PB_GE:
|
||||
case OP_AT_LEAST_K: {
|
||||
expr_ref_vector nargs(m);
|
||||
nargs.append(sz, args);
|
||||
dualize(f, nargs, k);
|
||||
SASSERT(!k.is_neg());
|
||||
return mk_le_ge<l_true>(sz, nargs.c_ptr(), k);
|
||||
}
|
||||
case OP_PB_LE:
|
||||
case OP_AT_MOST_K:
|
||||
return mk_le_ge<l_true>(sz, args, k);
|
||||
case OP_PB_EQ:
|
||||
return mk_le_ge<l_undef>(sz, args, k);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return expr_ref(m.mk_true(), m);
|
||||
}
|
||||
}
|
||||
|
||||
void dualize(func_decl* f, expr_ref_vector & args, rational & k) {
|
||||
k.neg();
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
k += pb.get_coeff(f, i);
|
||||
args[i] = ::mk_not(m, args[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
expr* negate(expr* e) {
|
||||
if (m.is_not(e, e)) return e;
|
||||
return m.mk_not(e);
|
||||
}
|
||||
expr* mk_ite(expr* c, expr* hi, expr* lo) {
|
||||
while (m.is_not(c, c)) {
|
||||
std::swap(hi, lo);
|
||||
}
|
||||
if (hi == lo) return hi;
|
||||
if (m.is_true(hi) && m.is_false(lo)) return c;
|
||||
if (m.is_false(hi) && m.is_true(lo)) return negate(c);
|
||||
if (m.is_true(hi)) return m.mk_or(c, lo);
|
||||
if (m.is_false(lo)) return m.mk_and(c, hi);
|
||||
if (m.is_false(hi)) return m.mk_and(negate(c), lo);
|
||||
if (m.is_true(lo)) return m.mk_implies(c, hi);
|
||||
return m.mk_ite(c, hi, lo);
|
||||
}
|
||||
|
||||
bool is_or(func_decl* f) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_AT_MOST_K:
|
||||
case OP_PB_LE:
|
||||
return false;
|
||||
case OP_AT_LEAST_K:
|
||||
case OP_PB_GE:
|
||||
return pb.get_k(f).is_one();
|
||||
case OP_PB_EQ:
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
card2bv_rewriter(imp& i, ast_manager& m):
|
||||
m_sort(*this),
|
||||
m(m),
|
||||
m_imp(i),
|
||||
au(m),
|
||||
pb(m),
|
||||
bv(m),
|
||||
m_trail(m),
|
||||
m_args(m)
|
||||
{}
|
||||
|
||||
bool mk_app(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) {
|
||||
if (f->get_family_id() == pb.get_family_id()) {
|
||||
mk_pb(full, f, sz, args, result);
|
||||
}
|
||||
else if (au.is_le(f) && is_pb(args[0], args[1])) {
|
||||
result = mk_le_ge<l_true>(m_args.size(), m_args.c_ptr(), m_k);
|
||||
}
|
||||
else if (au.is_lt(f) && is_pb(args[0], args[1])) {
|
||||
++m_k;
|
||||
result = mk_le_ge<l_true>(m_args.size(), m_args.c_ptr(), m_k);
|
||||
}
|
||||
else if (au.is_ge(f) && is_pb(args[1], args[0])) {
|
||||
result = mk_le_ge<l_true>(m_args.size(), m_args.c_ptr(), m_k);
|
||||
}
|
||||
else if (au.is_gt(f) && is_pb(args[1], args[0])) {
|
||||
++m_k;
|
||||
result = mk_le_ge<l_true>(m_args.size(), m_args.c_ptr(), m_k);
|
||||
}
|
||||
else if (m.is_eq(f) && is_pb(args[0], args[1])) {
|
||||
result = mk_le_ge<l_undef>(m_args.size(), m_args.c_ptr(), m_k);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
++m_imp.m_num_translated;
|
||||
return true;
|
||||
}
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) {
|
||||
if (mk_app(true, f, sz, args, result)) {
|
||||
return BR_DONE;
|
||||
}
|
||||
else {
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_pb(expr* x, expr* y) {
|
||||
m_args.reset();
|
||||
m_coeffs.reset();
|
||||
m_k.reset();
|
||||
return is_pb(x, rational::one()) && is_pb(y, rational::minus_one());
|
||||
}
|
||||
|
||||
bool is_pb(expr* e, rational const& mul) {
|
||||
if (!is_app(e)) {
|
||||
return false;
|
||||
}
|
||||
app* a = to_app(e);
|
||||
rational r, r1, r2;
|
||||
expr* c, *th, *el;
|
||||
unsigned sz = a->get_num_args();
|
||||
if (a->get_family_id() == au.get_family_id()) {
|
||||
switch (a->get_decl_kind()) {
|
||||
case OP_ADD:
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (!is_pb(a->get_arg(i), mul)) return false;
|
||||
}
|
||||
return true;
|
||||
case OP_SUB: {
|
||||
if (!is_pb(a->get_arg(0), mul)) return false;
|
||||
r = -mul;
|
||||
for (unsigned i = 1; i < sz; ++i) {
|
||||
if (!is_pb(a->get_arg(1), r)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case OP_UMINUS:
|
||||
return is_pb(a->get_arg(0), -mul);
|
||||
case OP_NUM:
|
||||
VERIFY(au.is_numeral(a, r));
|
||||
m_k -= mul * r;
|
||||
return true;
|
||||
case OP_MUL:
|
||||
if (sz != 2) {
|
||||
return false;
|
||||
}
|
||||
if (au.is_numeral(a->get_arg(0), r)) {
|
||||
r *= mul;
|
||||
return is_pb(a->get_arg(1), r);
|
||||
}
|
||||
if (au.is_numeral(a->get_arg(1), r)) {
|
||||
r *= mul;
|
||||
return is_pb(a->get_arg(0), r);
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (m.is_ite(a, c, th, el) && au.is_numeral(th, r1) && au.is_numeral(el, r2)) {
|
||||
r1 *= mul;
|
||||
r2 *= mul;
|
||||
if (r1 < r2) {
|
||||
m_args.push_back(::mk_not(m, c));
|
||||
m_coeffs.push_back(r2-r1);
|
||||
m_k -= r1;
|
||||
}
|
||||
else {
|
||||
m_args.push_back(c);
|
||||
m_coeffs.push_back(r1-r2);
|
||||
m_k -= r2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mk_pb(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) {
|
||||
SASSERT(f->get_family_id() == pb.get_family_id());
|
||||
if (is_or(f)) {
|
||||
result = m.mk_or(sz, args);
|
||||
}
|
||||
else if (pb.is_at_most_k(f) && pb.get_k(f).is_unsigned()) {
|
||||
result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args);
|
||||
}
|
||||
else if (pb.is_at_least_k(f) && pb.get_k(f).is_unsigned()) {
|
||||
result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args);
|
||||
}
|
||||
else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) {
|
||||
result = m_sort.eq(full, pb.get_k(f).get_unsigned(), sz, args);
|
||||
}
|
||||
else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) {
|
||||
result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args);
|
||||
}
|
||||
else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) {
|
||||
result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args);
|
||||
}
|
||||
else {
|
||||
result = mk_bv(f, sz, args);
|
||||
}
|
||||
}
|
||||
|
||||
// definitions used for sorting network
|
||||
literal mk_false() { return m.mk_false(); }
|
||||
literal mk_true() { return m.mk_true(); }
|
||||
literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); }
|
||||
literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); }
|
||||
literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); }
|
||||
|
||||
std::ostream& pp(std::ostream& out, literal lit) { return out << mk_ismt2_pp(lit, m); }
|
||||
|
||||
literal trail(literal l) {
|
||||
m_trail.push_back(l);
|
||||
return l;
|
||||
}
|
||||
literal fresh() {
|
||||
expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m);
|
||||
m_imp.m_fresh.push_back(to_app(fr)->get_decl());
|
||||
return trail(fr);
|
||||
}
|
||||
|
||||
void mk_clause(unsigned n, literal const* lits) {
|
||||
m_imp.m_lemmas.push_back(mk_or(m, n, lits));
|
||||
}
|
||||
};
|
||||
|
||||
struct card2bv_rewriter_cfg : public default_rewriter_cfg {
|
||||
card2bv_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = 0;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
card2bv_rewriter_cfg(imp& i, ast_manager & m):m_r(i, m) {}
|
||||
};
|
||||
|
||||
class card_pb_rewriter : public rewriter_tpl<card2bv_rewriter_cfg> {
|
||||
public:
|
||||
card2bv_rewriter_cfg m_cfg;
|
||||
card_pb_rewriter(imp& i, ast_manager & m):
|
||||
rewriter_tpl<card2bv_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(i, m) {}
|
||||
};
|
||||
|
||||
card_pb_rewriter m_rw;
|
||||
|
||||
imp(ast_manager& m, params_ref const& p):
|
||||
m(m), m_params(p), m_lemmas(m),
|
||||
m_fresh(m),
|
||||
m_num_translated(0),
|
||||
m_rw(*this, m) {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {}
|
||||
unsigned get_num_steps() const { return m_rw.get_num_steps(); }
|
||||
void cleanup() { m_rw.cleanup(); }
|
||||
void operator()(expr * e, expr_ref & result, proof_ref & result_proof) {
|
||||
m_rw(e, result, result_proof);
|
||||
}
|
||||
void push() {
|
||||
m_fresh_lim.push_back(m_fresh.size());
|
||||
}
|
||||
void pop(unsigned num_scopes) {
|
||||
SASSERT(m_lemmas.empty()); // lemmas must be flushed before pop.
|
||||
if (num_scopes > 0) {
|
||||
SASSERT(num_scopes <= m_fresh_lim.size());
|
||||
unsigned new_sz = m_fresh_lim.size() - num_scopes;
|
||||
unsigned lim = m_fresh_lim[new_sz];
|
||||
m_fresh.resize(lim);
|
||||
m_fresh_lim.resize(new_sz);
|
||||
}
|
||||
m_rw.reset();
|
||||
}
|
||||
|
||||
void flush_side_constraints(expr_ref_vector& side_constraints) {
|
||||
side_constraints.append(m_lemmas);
|
||||
m_lemmas.reset();
|
||||
}
|
||||
|
||||
void collect_statistics(statistics & st) const {
|
||||
st.update("pb-aux-variables", m_fresh.size());
|
||||
st.update("pb-aux-clauses", m_rw.m_cfg.m_r.m_sort.m_stats.m_num_compiled_clauses);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
pb2bv_rewriter::pb2bv_rewriter(ast_manager & m, params_ref const& p) { m_imp = alloc(imp, m, p); }
|
||||
pb2bv_rewriter::~pb2bv_rewriter() { dealloc(m_imp); }
|
||||
void pb2bv_rewriter::updt_params(params_ref const & p) { m_imp->updt_params(p); }
|
||||
ast_manager & pb2bv_rewriter::m() const { return m_imp->m; }
|
||||
unsigned pb2bv_rewriter::get_num_steps() const { return m_imp->get_num_steps(); }
|
||||
void pb2bv_rewriter::cleanup() { ast_manager& mgr = m(); params_ref p = m_imp->m_params; dealloc(m_imp); m_imp = alloc(imp, mgr, p); }
|
||||
func_decl_ref_vector const& pb2bv_rewriter::fresh_constants() const { return m_imp->m_fresh; }
|
||||
void pb2bv_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(e, result, result_proof); }
|
||||
void pb2bv_rewriter::push() { m_imp->push(); }
|
||||
void pb2bv_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); }
|
||||
void pb2bv_rewriter::flush_side_constraints(expr_ref_vector& side_constraints) { m_imp->flush_side_constraints(side_constraints); }
|
||||
unsigned pb2bv_rewriter::num_translated() const { return m_imp->m_num_translated; }
|
||||
|
||||
|
||||
void pb2bv_rewriter::collect_statistics(statistics & st) const { m_imp->collect_statistics(st); }
|
46
src/ast/rewriter/pb2bv_rewriter.h
Normal file
46
src/ast/rewriter/pb2bv_rewriter.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb2bv_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Conversion from pseudo-booleans to bit-vectors.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2016-10-23
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef PB2BV_REWRITER_H_
|
||||
#define PB2BV_REWRITER_H_
|
||||
|
||||
#include"pb_decl_plugin.h"
|
||||
#include"rewriter_types.h"
|
||||
#include"expr_functors.h"
|
||||
|
||||
class pb2bv_rewriter {
|
||||
struct imp;
|
||||
imp* m_imp;
|
||||
public:
|
||||
pb2bv_rewriter(ast_manager & m, params_ref const& p);
|
||||
~pb2bv_rewriter();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
ast_manager & m() const;
|
||||
unsigned get_num_steps() const;
|
||||
void cleanup();
|
||||
func_decl_ref_vector const& fresh_constants() const;
|
||||
void operator()(expr * e, expr_ref & result, proof_ref & result_proof);
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void flush_side_constraints(expr_ref_vector& side_constraints);
|
||||
unsigned num_translated() const;
|
||||
void collect_statistics(statistics & st) const;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -257,7 +257,12 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
|
|||
all_unit &= m_coeffs.back().is_one();
|
||||
}
|
||||
if (is_eq) {
|
||||
result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k);
|
||||
if (sz == 0) {
|
||||
result = k.is_zero()?m.mk_true():m.mk_false();
|
||||
}
|
||||
else {
|
||||
result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k);
|
||||
}
|
||||
}
|
||||
else if (all_unit && k.is_one()) {
|
||||
result = mk_or(m, sz, m_args.c_ptr());
|
||||
|
@ -277,6 +282,7 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
|
|||
tout << tmp << "\n";
|
||||
tout << result << "\n";
|
||||
);
|
||||
|
||||
TRACE("pb_validate",
|
||||
validate_rewrite(f, num_args, args, result););
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ protected:
|
|||
void elim_reflex_prs(unsigned spos);
|
||||
public:
|
||||
rewriter_core(ast_manager & m, bool proof_gen);
|
||||
~rewriter_core();
|
||||
virtual ~rewriter_core();
|
||||
ast_manager & m() const { return m_manager; }
|
||||
void reset();
|
||||
void cleanup();
|
||||
|
|
|
@ -27,7 +27,7 @@ void rewriter_tpl<Config>::process_var(var * v) {
|
|||
SASSERT(v->get_sort() == m().get_sort(m_r));
|
||||
if (ProofGen) {
|
||||
result_pr_stack().push_back(m_pr);
|
||||
m_pr = 0;
|
||||
m_pr = 0;
|
||||
}
|
||||
set_new_child_flag(v);
|
||||
TRACE("rewriter", tout << mk_ismt2_pp(v, m()) << " -> " << m_r << "\n";);
|
||||
|
@ -39,11 +39,11 @@ void rewriter_tpl<Config>::process_var(var * v) {
|
|||
unsigned idx = v->get_idx();
|
||||
if (idx < m_bindings.size()) {
|
||||
unsigned index = m_bindings.size() - idx - 1;
|
||||
expr * r = m_bindings[index];
|
||||
var * r = (var*)(m_bindings[index]);
|
||||
if (r != 0) {
|
||||
SASSERT(v->get_sort() == m().get_sort(r));
|
||||
if (!is_ground(r) && m_shifts[index] != m_bindings.size()) {
|
||||
|
||||
|
||||
unsigned shift_amount = m_bindings.size() - m_shifts[index];
|
||||
expr_ref tmp(m());
|
||||
m_shifter(r, shift_amount, tmp);
|
||||
|
@ -103,8 +103,8 @@ template<typename Config>
|
|||
template<bool ProofGen>
|
||||
bool rewriter_tpl<Config>::visit(expr * t, unsigned max_depth) {
|
||||
TRACE("rewriter_visit", tout << "visiting\n" << mk_ismt2_pp(t, m()) << "\n";);
|
||||
expr * new_t;
|
||||
proof * new_t_pr;
|
||||
expr * new_t = 0;
|
||||
proof * new_t_pr = 0;
|
||||
if (m_cfg.get_subst(t, new_t, new_t_pr)) {
|
||||
TRACE("rewriter_subst", tout << "subst\n" << mk_ismt2_pp(t, m()) << "\n---->\n" << mk_ismt2_pp(new_t, m()) << "\n";);
|
||||
SASSERT(m().get_sort(t) == m().get_sort(new_t));
|
||||
|
@ -195,9 +195,9 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
|
|||
// this optimization is only used when Proof generation is disabled.
|
||||
if (f->is_associative() && t->get_ref_count() <= 1 && frame_stack().size() > 1) {
|
||||
frame & prev_fr = frame_stack()[frame_stack().size() - 2];
|
||||
if (is_app(prev_fr.m_curr) &&
|
||||
to_app(prev_fr.m_curr)->get_decl() == f &&
|
||||
prev_fr.m_state == PROCESS_CHILDREN &&
|
||||
if (is_app(prev_fr.m_curr) &&
|
||||
to_app(prev_fr.m_curr)->get_decl() == f &&
|
||||
prev_fr.m_state == PROCESS_CHILDREN &&
|
||||
flat_assoc(f)) {
|
||||
frame_stack().pop_back();
|
||||
set_new_child_flag(t);
|
||||
|
@ -223,7 +223,7 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
|
|||
}
|
||||
br_status st = m_cfg.reduce_app(f, new_num_args, new_args, m_r, m_pr2);
|
||||
SASSERT(st != BR_DONE || m().get_sort(m_r) == m().get_sort(t));
|
||||
TRACE("reduce_app",
|
||||
TRACE("reduce_app",
|
||||
tout << mk_ismt2_pp(t, m()) << "\n";
|
||||
tout << "st: " << st;
|
||||
if (m_r) tout << " --->\n" << mk_ismt2_pp(m_r, m());
|
||||
|
@ -296,11 +296,11 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
|
|||
expr * def;
|
||||
proof * def_pr;
|
||||
quantifier * def_q;
|
||||
// When get_macro succeeds, then
|
||||
// When get_macro succeeds, then
|
||||
// we know that:
|
||||
// forall X. f(X) = def[X]
|
||||
// and def_pr is a proof for this quantifier.
|
||||
//
|
||||
//
|
||||
// Remark: def_q is only used for proof generation.
|
||||
// It is the quantifier forall X. f(X) = def[X]
|
||||
if (get_macro(f, def, def_q, def_pr)) {
|
||||
|
@ -318,7 +318,7 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
|
|||
if (ProofGen) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
// We do not support the use of bindings in proof generation mode.
|
||||
// Thus we have to apply the subsitution here, and
|
||||
// Thus we have to apply the subsitution here, and
|
||||
// beta_reducer subst(m());
|
||||
// subst.set_bindings(new_num_args, new_args);
|
||||
// expr_ref r2(m());
|
||||
|
@ -333,7 +333,7 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) {
|
|||
unsigned i = num_args;
|
||||
while (i > 0) {
|
||||
--i;
|
||||
m_bindings.push_back(new_args[i]); // num_args - i - 1]);
|
||||
m_bindings.push_back(new_args[i]); // num_args - i - 1]);
|
||||
m_shifts.push_back(sz);
|
||||
}
|
||||
result_stack().push_back(def);
|
||||
|
@ -465,7 +465,7 @@ void rewriter_tpl<Config>::process_quantifier(quantifier * q, frame & fr) {
|
|||
}
|
||||
else {
|
||||
new_pats = q->get_patterns();
|
||||
new_no_pats = q->get_no_patterns();
|
||||
new_no_pats = q->get_no_patterns();
|
||||
}
|
||||
if (ProofGen) {
|
||||
quantifier * new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body);
|
||||
|
@ -559,7 +559,7 @@ template<typename Config>
|
|||
void rewriter_tpl<Config>::display_bindings(std::ostream& out) {
|
||||
out << "bindings:\n";
|
||||
for (unsigned i = 0; i < m_bindings.size(); i++) {
|
||||
if (m_bindings[i])
|
||||
if (m_bindings[i])
|
||||
out << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n";
|
||||
}
|
||||
}
|
||||
|
@ -596,6 +596,7 @@ template<typename Config>
|
|||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) {
|
||||
if (m_cancel_check && m().canceled()) {
|
||||
reset();
|
||||
throw rewriter_exception(m().limit().get_cancel_msg());
|
||||
}
|
||||
SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size());
|
||||
|
@ -630,6 +631,7 @@ void rewriter_tpl<Config>::resume_core(expr_ref & result, proof_ref & result_pr)
|
|||
SASSERT(!frame_stack().empty());
|
||||
while (!frame_stack().empty()) {
|
||||
if (m_cancel_check && m().canceled()) {
|
||||
reset();
|
||||
throw rewriter_exception(m().limit().get_cancel_msg());
|
||||
}
|
||||
SASSERT(!ProofGen || result_stack().size() == result_pr_stack().size());
|
||||
|
|
|
@ -7,5 +7,6 @@ def_module_params('rewriter',
|
|||
("push_ite_arith", BOOL, False, "push if-then-else over arithmetic terms."),
|
||||
("push_ite_bv", BOOL, False, "push if-then-else over bit-vector terms."),
|
||||
("pull_cheap_ite", BOOL, False, "pull if-then-else terms when cheap."),
|
||||
("bv_ineq_consistency_test_max", UINT, 0, "max size of conjunctions on which to perform consistency test based on inequalities on bitvectors."),
|
||||
("cache_all", BOOL, False, "cache all intermediate results.")))
|
||||
|
||||
|
|
|
@ -86,20 +86,25 @@ public:
|
|||
expr_ref fml(m.mk_true(), m);
|
||||
return sym_expr::mk_pred(fml, m.mk_bool_sort());
|
||||
}
|
||||
virtual T mk_and(T x, T y) {
|
||||
if (x->is_char() && y->is_char()) {
|
||||
if (x->get_char() == y->get_char()) {
|
||||
return x;
|
||||
}
|
||||
if (m.are_distinct(x->get_char(), y->get_char())) {
|
||||
expr_ref fml(m.mk_false(), m);
|
||||
return sym_expr::mk_pred(fml, x->get_sort());
|
||||
}
|
||||
}
|
||||
var_ref v(m.mk_var(0, x->get_sort()), m);
|
||||
expr_ref fml1 = x->accept(v);
|
||||
expr_ref fml2 = y->accept(v);
|
||||
if (m.is_true(fml1)) return y;
|
||||
virtual T mk_and(T x, T y) {
|
||||
if (x->is_char() && y->is_char()) {
|
||||
if (x->get_char() == y->get_char()) {
|
||||
return x;
|
||||
}
|
||||
if (m.are_distinct(x->get_char(), y->get_char())) {
|
||||
expr_ref fml(m.mk_false(), m);
|
||||
return sym_expr::mk_pred(fml, x->get_sort());
|
||||
}
|
||||
}
|
||||
|
||||
sort* s = x->get_sort();
|
||||
if (m.is_bool(s)) s = y->get_sort();
|
||||
var_ref v(m.mk_var(0, s), m);
|
||||
expr_ref fml1 = x->accept(v);
|
||||
expr_ref fml2 = y->accept(v);
|
||||
if (m.is_true(fml1)) {
|
||||
return y;
|
||||
}
|
||||
if (m.is_true(fml2)) return x;
|
||||
expr_ref fml(m.mk_and(fml1, fml2), m);
|
||||
return sym_expr::mk_pred(fml, x->get_sort());
|
||||
|
@ -166,6 +171,11 @@ public:
|
|||
expr_ref fml(m.mk_not(x->accept(v)), m);
|
||||
return sym_expr::mk_pred(fml, x->get_sort());
|
||||
}
|
||||
|
||||
/*virtual vector<std::pair<vector<bool>, T>> generate_min_terms(vector<T> constraints){
|
||||
|
||||
return 0;
|
||||
}*/
|
||||
};
|
||||
|
||||
re2automaton::re2automaton(ast_manager& m): m(m), u(m), bv(m), m_ba(0), m_sa(0) {}
|
||||
|
@ -233,46 +243,8 @@ eautomaton* re2automaton::re2aut(expr* e) {
|
|||
TRACE("seq", tout << "Range expression is not handled: " << mk_pp(e, m) << "\n";);
|
||||
}
|
||||
}
|
||||
else if (u.re.is_complement(e, e0)) {
|
||||
// TBD non-standard semantics of complementation.
|
||||
if (u.re.is_range(e0, e1, e2) && u.str.is_string(e1, s1) && u.str.is_string(e2, s2) &&
|
||||
s1.length() == 1 && s2.length() == 1) {
|
||||
unsigned start = s1[0];
|
||||
unsigned stop = s2[0];
|
||||
unsigned nb = s1.num_bits();
|
||||
sort_ref s(bv.mk_sort(nb), m);
|
||||
expr_ref v(m.mk_var(0, s), m);
|
||||
expr_ref _start(bv.mk_numeral(start, nb), m);
|
||||
expr_ref _stop(bv.mk_numeral(stop, nb), m);
|
||||
expr_ref _pred(m.mk_not(m.mk_and(bv.mk_ule(_start, v), bv.mk_ule(v, _stop))), m);
|
||||
a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s));
|
||||
display_expr1 disp(m);
|
||||
TRACE("seq", tout << mk_pp(e, m) << "\n"; a->display(tout, disp););
|
||||
return a.detach();
|
||||
}
|
||||
else if (u.re.is_to_re(e0, e1) && u.str.is_string(e1, s1) && s1.length() == 1) {
|
||||
unsigned nb = s1.num_bits();
|
||||
sort_ref s(bv.mk_sort(nb), m);
|
||||
expr_ref v(m.mk_var(0, s), m);
|
||||
expr_ref _ch(bv.mk_numeral(s1[0], nb), m);
|
||||
expr_ref _pred(m.mk_not(m.mk_eq(v, _ch)), m);
|
||||
a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s));
|
||||
display_expr1 disp(m);
|
||||
TRACE("seq", tout << mk_pp(e, m) << "\n"; a->display(tout, disp););
|
||||
return a.detach();
|
||||
}
|
||||
else if (u.re.is_to_re(e0, e1) && u.str.is_unit(e1, e2)) {
|
||||
sort* s = m.get_sort(e2);
|
||||
expr_ref v(m.mk_var(0, s), m);
|
||||
expr_ref _pred(m.mk_not(m.mk_eq(v, e2)), m);
|
||||
a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s));
|
||||
display_expr1 disp(m);
|
||||
TRACE("seq", tout << mk_pp(e, m) << "\n"; a->display(tout, disp););
|
||||
return a.detach();
|
||||
}
|
||||
else {
|
||||
TRACE("seq", tout << "Complement expression is not handled: " << mk_pp(e, m) << "\n";);
|
||||
}
|
||||
else if (u.re.is_complement(e, e0) && (a = re2aut(e0)) && m_sa) {
|
||||
return m_sa->mk_complement(*a);
|
||||
}
|
||||
else if (u.re.is_loop(e, e1, lo, hi) && (a = re2aut(e1))) {
|
||||
scoped_ptr<eautomaton> eps = eautomaton::mk_epsilon(sm);
|
||||
|
@ -303,7 +275,7 @@ eautomaton* re2automaton::re2aut(expr* e) {
|
|||
}
|
||||
else if (u.re.is_full(e)) {
|
||||
expr_ref tt(m.mk_true(), m);
|
||||
sort* seq_s, *char_s;
|
||||
sort *seq_s = 0, *char_s = 0;
|
||||
VERIFY (u.is_re(m.get_sort(e), seq_s));
|
||||
VERIFY (u.is_seq(seq_s, char_s));
|
||||
sym_expr* _true = sym_expr::mk_pred(tt, char_s);
|
||||
|
@ -363,6 +335,9 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
|
|||
SASSERT(num_args == 1);
|
||||
return mk_re_opt(args[0], result);
|
||||
case OP_RE_CONCAT:
|
||||
if (num_args == 1) {
|
||||
result = args[0]; return BR_DONE;
|
||||
}
|
||||
SASSERT(num_args == 2);
|
||||
return mk_re_concat(args[0], args[1], result);
|
||||
case OP_RE_UNION:
|
||||
|
@ -549,7 +524,7 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) {
|
|||
result = m().mk_bool_val(c.contains(d));
|
||||
return BR_DONE;
|
||||
}
|
||||
// check if subsequence of b is in a.
|
||||
// check if subsequence of a is in b.
|
||||
expr_ref_vector as(m()), bs(m());
|
||||
m_util.str.get_concat(a, as);
|
||||
m_util.str.get_concat(b, bs);
|
||||
|
@ -612,6 +587,12 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) {
|
|||
SASSERT(sz > offs);
|
||||
result = m_util.str.mk_contains(m_util.str.mk_concat(sz-offs, as.c_ptr()+offs), b);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
expr* x, *y, *z;
|
||||
if (m_util.str.is_extract(b, x, y, z) && x == a) {
|
||||
result = m().mk_true();
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
|
@ -665,6 +646,14 @@ br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& resu
|
|||
result = a;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (m_util.str.is_string(b, s2) && s2.length() == 0) {
|
||||
result = m_util.str.mk_concat(a, c);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
if (m_util.str.is_string(a, s1) && s1.length() == 0) {
|
||||
result = a;
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
@ -794,7 +783,7 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) {
|
|||
|
||||
bool isc1 = false;
|
||||
bool isc2 = false;
|
||||
expr* a1, *a2, *b1, *b2;
|
||||
expr *a1 = 0, *a2 = 0, *b1 = 0, *b2 = 0;
|
||||
if (m_util.str.is_concat(a, a1, a2) && m_util.str.is_string(a2, s1)) {
|
||||
isc1 = true;
|
||||
}
|
||||
|
@ -910,6 +899,11 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
|
|||
result = m_autil.mk_numeral(r, true);
|
||||
return BR_DONE;
|
||||
}
|
||||
expr* b;
|
||||
if (m_util.str.is_itos(a, b)) {
|
||||
result = b;
|
||||
return BR_DONE;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
@ -1321,7 +1315,7 @@ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) {
|
|||
}
|
||||
|
||||
br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) {
|
||||
sort* s;
|
||||
sort* s = 0;
|
||||
VERIFY(m_util.is_re(a, s));
|
||||
result = m_util.re.mk_union(m_util.re.mk_to_re(m_util.str.mk_empty(s)), a);
|
||||
return BR_REWRITE1;
|
||||
|
@ -1511,10 +1505,15 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_
|
|||
lchange = true;
|
||||
}
|
||||
|
||||
bool is_sat;
|
||||
bool is_sat = true;
|
||||
unsigned szl = ls.size() - head1, szr = rs.size() - head2;
|
||||
expr* const* _ls = ls.c_ptr() + head1, * const* _rs = rs.c_ptr() + head2;
|
||||
|
||||
if (solve_itos(szl, _ls, szr, _rs, lhs, rhs, is_sat)) {
|
||||
ls.reset(); rs.reset();
|
||||
change = true;
|
||||
return is_sat;
|
||||
}
|
||||
|
||||
if (length_constrained(szl, _ls, szr, _rs, lhs, rhs, is_sat)) {
|
||||
ls.reset(); rs.reset();
|
||||
|
@ -1679,6 +1678,56 @@ bool seq_rewriter::min_length(unsigned n, expr* const* es, unsigned& len) {
|
|||
return bounded;
|
||||
}
|
||||
|
||||
bool seq_rewriter::is_string(unsigned n, expr* const* es, zstring& s) const {
|
||||
zstring s1;
|
||||
expr* e;
|
||||
bv_util bv(m());
|
||||
rational val;
|
||||
unsigned sz;
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
if (m_util.str.is_string(es[i], s1)) {
|
||||
s = s + s1;
|
||||
}
|
||||
else if (m_util.str.is_unit(es[i], e) && bv.is_numeral(e, val, sz)) {
|
||||
s = s + zstring(val.get_unsigned());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool seq_rewriter::solve_itos(unsigned szl, expr* const* ls, unsigned szr, expr* const* rs,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) {
|
||||
|
||||
expr* l, *r;
|
||||
is_sat = true;
|
||||
if (szl == 1 && m_util.str.is_itos(ls[0], l)) {
|
||||
if (szr == 1 && m_util.str.is_itos(rs[0], r)) {
|
||||
lhs.push_back(l);
|
||||
rhs.push_back(r);
|
||||
return true;
|
||||
}
|
||||
zstring s;
|
||||
if (is_string(szr, rs, s)) {
|
||||
std::string s1 = s.encode();
|
||||
rational r(s1.c_str());
|
||||
if (s1 == r.to_string()) {
|
||||
lhs.push_back(l);
|
||||
rhs.push_back(m_autil.mk_numeral(r, true));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (szr == 1 && m_util.str.is_itos(rs[0], r) && !m_util.str.is_itos(ls[0])) {
|
||||
return solve_itos(szr, rs, szl, ls, rhs, lhs, is_sat);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool seq_rewriter::length_constrained(unsigned szl, expr* const* l, unsigned szr, expr* const* r,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) {
|
||||
is_sat = true;
|
||||
|
|
|
@ -125,15 +125,20 @@ class seq_rewriter {
|
|||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat);
|
||||
bool length_constrained(unsigned n, expr* const* l, unsigned m, expr* const* r,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat);
|
||||
bool solve_itos(unsigned n, expr* const* l, unsigned m, expr* const* r,
|
||||
expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat);
|
||||
bool min_length(unsigned n, expr* const* es, unsigned& len);
|
||||
expr* concat_non_empty(unsigned n, expr* const* es);
|
||||
|
||||
bool is_string(unsigned n, expr* const* es, zstring& s) const;
|
||||
|
||||
void add_next(u_map<expr*>& next, expr_ref_vector& trail, unsigned idx, expr* cond);
|
||||
bool is_sequence(expr* e, expr_ref_vector& seq);
|
||||
bool is_sequence(eautomaton& aut, expr_ref_vector& seq);
|
||||
bool is_epsilon(expr* e) const;
|
||||
void split_units(expr_ref_vector& lhs, expr_ref_vector& rhs);
|
||||
|
||||
|
||||
public:
|
||||
seq_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
m_util(m), m_autil(m), m_re2aut(m), m_es(m), m_lhs(m), m_rhs(m) {
|
||||
|
@ -144,6 +149,9 @@ public:
|
|||
void updt_params(params_ref const & p) {}
|
||||
static void get_param_descrs(param_descrs & r) {}
|
||||
|
||||
void set_solver(expr_solver* solver) { m_re2aut.set_solver(solver); }
|
||||
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
br_status mk_eq_core(expr * lhs, expr * rhs, expr_ref & result);
|
||||
|
||||
|
|
|
@ -194,6 +194,14 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
|
|||
if (st != BR_FAILED)
|
||||
return st;
|
||||
}
|
||||
if (k == OP_ITE) {
|
||||
SASSERT(num == 3);
|
||||
family_id s_fid = m().get_sort(args[1])->get_family_id();
|
||||
if (s_fid == m_bv_rw.get_fid())
|
||||
st = m_bv_rw.mk_ite_core(args[0], args[1], args[2], result);
|
||||
if (st != BR_FAILED)
|
||||
return st;
|
||||
}
|
||||
return m_b_rw.mk_app_core(f, num, args, result);
|
||||
}
|
||||
if (fid == m_a_rw.get_fid())
|
||||
|
@ -700,6 +708,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
template class rewriter_tpl<th_rewriter_cfg>;
|
||||
|
@ -713,6 +722,10 @@ struct th_rewriter::imp : public rewriter_tpl<th_rewriter_cfg> {
|
|||
expr_ref mk_app(func_decl* f, unsigned sz, expr* const* args) {
|
||||
return m_cfg.mk_app(f, sz, args);
|
||||
}
|
||||
|
||||
void set_solver(expr_solver* solver) {
|
||||
m_cfg.m_seq_rw.set_solver(solver);
|
||||
}
|
||||
};
|
||||
|
||||
th_rewriter::th_rewriter(ast_manager & m, params_ref const & p):
|
||||
|
@ -798,3 +811,7 @@ void th_rewriter::reset_used_dependencies() {
|
|||
expr_ref th_rewriter::mk_app(func_decl* f, unsigned num_args, expr* const* args) {
|
||||
return m_imp->mk_app(f, num_args, args);
|
||||
}
|
||||
|
||||
void th_rewriter::set_solver(expr_solver* solver) {
|
||||
m_imp->set_solver(solver);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ Notes:
|
|||
|
||||
class expr_substitution;
|
||||
|
||||
class expr_solver;
|
||||
|
||||
class th_rewriter {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
|
@ -58,6 +60,9 @@ public:
|
|||
// Remark: reset_used_dependecies will reset the internal cache if get_used_dependencies() != 0
|
||||
expr_dependency * get_used_dependencies();
|
||||
void reset_used_dependencies();
|
||||
|
||||
void set_solver(expr_solver* solver);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue