3
0
Fork 0
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:
Murphy Berzish 2017-02-18 15:04:44 -05:00
commit 235ea79043
588 changed files with 21784 additions and 15202 deletions

View file

@ -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));

View file

@ -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;
}

View file

@ -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);

View file

@ -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_ */

View file

@ -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();
}

View file

@ -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

View file

@ -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)) {

View file

@ -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; }

View 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;
}

View 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 */

View file

@ -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>;

View file

@ -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; }

View file

@ -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")
))

View file

@ -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);

View 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); }

View 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

View file

@ -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;
}

View file

@ -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"),
))

View 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); }

View 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

View file

@ -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););

View file

@ -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();

View file

@ -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());

View file

@ -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.")))

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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