3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-11 13:40:52 +00:00

reorganizing the code

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-23 22:14:35 -07:00
parent 9e299b88c4
commit 0a4446ae26
255 changed files with 17 additions and 17 deletions

View file

@ -0,0 +1,53 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_offset.h
Abstract:
Expressions + Offsets.
In order to avoid creating variants of terms, we use a pair (expression, offset),
where offset is just a small integer.
Non ground terms with different offsets are always considered
disequal. For example, (f x):1 is different from (f x):2, and
(f x):1 is unifiable with x:2.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#ifndef _EXPR_OFFSET_H_
#define _EXPR_OFFSET_H_
#include"ast.h"
class expr_offset {
expr * m_expr;
unsigned m_offset;
public:
expr_offset():m_expr(0), m_offset(0) {}
expr_offset(expr * e, unsigned o):m_expr(e), m_offset(o) {}
expr * get_expr() const { return m_expr; }
unsigned get_offset() const { return m_offset; }
bool operator==(expr_offset const & other) const { return m_expr == other.m_expr && m_offset == other.m_offset; }
bool operator!=(expr_offset const & other) const { return !operator==(other); }
unsigned hash() const {
unsigned a = m_expr->get_id();
unsigned b = m_offset;
unsigned c = 17;
mix(a, b, c);
return c;
}
};
typedef std::pair<expr_offset, expr_offset> expr_offset_pair;
typedef pair_hash<obj_hash<expr_offset>, obj_hash<expr_offset> > expr_offset_pair_hash;
#endif /* _EXPR_OFFSET_H_ */

View file

@ -0,0 +1,94 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
expr_offset_map.h
Abstract:
A generic mapping from (expression, offset) to a value T.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _EXPR_OFFSET_MAP_H_
#define _EXPR_OFFSET_MAP_H_
#include"expr_offset.h"
#include"vector.h"
/**
\brief A mapping from expr_offset to some value of type T.
*/
template<typename T>
class expr_offset_map {
struct data {
T m_data;
unsigned m_timestamp;
data():m_timestamp(0) {}
};
vector<svector<data> > m_map;
unsigned m_timestamp;
public:
expr_offset_map():
m_timestamp(1) {}
bool contains(expr_offset const & n) const {
unsigned off = n.get_offset();
if (off < m_map.size()) {
svector<data> const & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id < v.size())
return v[id].m_timestamp == m_timestamp;
}
return false;
}
bool find(expr_offset const & n, T & r) const {
unsigned off = n.get_offset();
if (off < m_map.size()) {
svector<data> const & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id < v.size() && v[id].m_timestamp == m_timestamp) {
r = v[id].m_data;
return true;
}
}
return false;
}
void insert(expr_offset const & n, T const & r) {
unsigned off = n.get_offset();
if (off >= m_map.size())
m_map.resize(off+1, svector<data>());
svector<data> & v = m_map[off];
unsigned id = n.get_expr()->get_id();
if (id >= v.size())
v.resize(id+1);
v[id].m_data = r;
v[id].m_timestamp = m_timestamp;
}
void reset() {
m_timestamp++;
if (m_timestamp == UINT_MAX) {
typename vector<svector<data> >::iterator it = m_map.begin();
typename vector<svector<data> >::iterator end = m_map.end();
for (; it != end; ++it) {
svector<data> & v = *it;
typename svector<data>::iterator it2 = v.begin();
typename svector<data>::iterator end2 = v.end();
for (; it2 != end2; ++it2)
it2->m_timestamp = 0;
}
m_timestamp = 1;
}
}
};
#endif /* _EXPR_OFFSET_MAP_H_ */

View file

@ -0,0 +1,81 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
matcher.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#include"matcher.h"
matcher::matcher(ast_manager & m):
m_manager(m) {
}
bool matcher::operator()(expr * e1, expr * e2, substitution & s) {
reset();
m_subst = &s;
m_todo.push_back(expr_pair(e1, e2));
while (!m_todo.empty()) {
expr_pair const & p = m_todo.back();
// if (m_cache.contains(p)) {
// m_todo.pop_back();
// continue;
// }
if (is_var(p.first)) {
expr_offset r;
if (m_subst->find(to_var(p.first), 0, r)) {
if (r.get_expr() != p.second)
return false;
}
else {
m_subst->insert(to_var(p.first), 0, expr_offset(p.second, 1));
}
m_todo.pop_back();
continue;
}
if (is_var(p.second))
return false;
app * n1 = to_app(p.first);
app * n2 = to_app(p.second);
if (n1->get_decl() != n2->get_decl())
return false;
unsigned num_args1 = n1->get_num_args();
if (num_args1 != n2->get_num_args())
return false;
m_todo.pop_back();
if (num_args1 == 0)
continue;
// m_cache.insert(p);
unsigned j = num_args1;
while (j > 0) {
--j;
m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j)));
}
}
return true;
}
void matcher::reset() {
// m_cache.reset();
m_todo.reset();
}

View file

@ -0,0 +1,64 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
matcher.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-02.
Revision History:
--*/
#ifndef _MATCHER_H_
#define _MATCHER_H_
#include"substitution.h"
#include"hashtable.h"
/**
\brief Functor for matching expressions.
*/
class matcher {
typedef std::pair<expr *, expr *> expr_pair;
typedef pair_hash<obj_ptr_hash<expr>, obj_ptr_hash<expr> > expr_pair_hash;
typedef hashtable<expr_pair, expr_pair_hash, default_eq<expr_pair> > cache;
ast_manager & m_manager;
substitution * m_subst;
cache m_cache;
svector<expr_pair> m_todo;
void reset();
public:
matcher(ast_manager & m);
/**
\brief Return true if e2 is an instance of e1.
In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s.
For example:
1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a))
The result is true, and s will contain x -> h(a)
2) e1 = f(a, x) e2 = f(x, a)
The result is false.
3) e1 = f(x, x) e2 = f(y, a)
The result is false
4) e1 = f(x, y) e2 = f(h(z), a)
The result is true, and s contains x->h(z) and y->a
*/
bool operator()(expr * e1, expr * e2, substitution & s);
};
#endif /* _MATCHER_H_ */

View file

@ -0,0 +1,286 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution.cpp
Abstract:
A substitution, that is, a mapping from (variable, offset) to (expr, offset).
We use offsets in order to avoid creating variants of terms.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#include"substitution.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
substitution::substitution(ast_manager & m):
m_manager(m),
m_new_exprs(m) {
}
void substitution::reset() {
reset_subst();
reset_cache();
}
void substitution::reset_subst() {
m_subst.reset();
m_vars.reset();
m_scopes.reset();
}
void substitution::reset_cache() {
TRACE("subst_bug", tout << "substitution::reset_cache\n";
for (unsigned i = 0; i < m_new_exprs.size(); i++) { tout << mk_pp(m_new_exprs.get(i), m_manager) << "\nref_count: " << m_new_exprs.get(i)->get_ref_count() << "\n"; });
m_apply_cache.reset();
m_new_exprs.reset();
}
void substitution::pop_scope(unsigned num_scopes) {
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
unsigned old_sz = m_scopes[new_lvl];
unsigned curr_sz = m_vars.size();
SASSERT(old_sz <= curr_sz);
for (unsigned i = old_sz; i < curr_sz; i++) {
var_offset & curr = m_vars[i];
m_subst.erase(curr.first, curr.second);
}
m_vars.shrink(old_sz);
m_scopes.shrink(new_lvl);
}
inline void substitution::apply_visit(expr_offset const & n, bool & visited) {
if (!m_apply_cache.contains(n)) {
m_todo.push_back(n);
visited = false;
}
}
void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n,
expr_offset const & s, expr_offset const & t, expr_ref & result) {
TRACE("subst_bug", tout << "BEGIN substitution::apply\n";);
// It is incorrect to cache results between different calls if we are applying a substitution
// modulo a substitution s -> t.
if (s != expr_offset(0,0))
reset_cache();
unsigned j;
expr * e;
unsigned off;
expr_offset n1;
bool visited;
unsigned num_args;
ptr_buffer<expr> new_args;
m_todo.push_back(n);
while (!m_todo.empty()) {
expr_offset n = m_todo.back();
TRACE("subst_bug", tout << "n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset() << "\n";);
if (m_apply_cache.contains(n)) {
m_todo.pop_back();
continue;
}
expr_offset n_prime = n == s ? t : n;
TRACE("subst_bug", tout << "n_prime: " << mk_pp(n_prime.get_expr(), m_manager) << " : " << n_prime.get_offset() << "\n";);
visited = true;
e = n_prime.get_expr();
off = n_prime.get_offset();
switch (e->get_kind()) {
case AST_VAR:
if (find(to_var(e)->get_idx(), off, n1)) {
apply_visit(n1, visited);
TRACE("subst_bug", tout << "visited: " << visited << ", n1: " << mk_pp(n1.get_expr(), m_manager) << " : " << n1.get_offset() << "\n";);
if (visited) {
m_todo.pop_back();
expr * new_expr;
m_apply_cache.find(n1, new_expr);
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "1. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
}
else {
m_todo.pop_back();
SASSERT(off < num_actual_offsets);
unsigned delta = deltas[off];
expr * new_expr = e;
if (delta > 0) {
new_expr = m_manager.mk_var(to_var(e)->get_idx() + delta, to_var(e)->get_sort());
m_new_exprs.push_back(new_expr);
}
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "2. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
break;
case AST_APP:
num_args = to_app(e)->get_num_args();
j = num_args;
while (j > 0) {
--j;
apply_visit(expr_offset(to_app(e)->get_arg(j), off), visited);
}
if (visited) {
m_todo.pop_back();
new_args.reset();
bool has_new_args = false;
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(e)->get_arg(i);
expr * new_arg;
m_apply_cache.find(expr_offset(arg, off), new_arg);
new_args.push_back(new_arg);
if (arg != new_arg)
has_new_args = true;
}
if (!has_new_args) {
m_apply_cache.insert(n, e);
TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(e, m_manager) << "\n";);
}
else {
expr * new_expr = m_manager.mk_app(to_app(e)->get_decl(), new_args.size(), new_args.c_ptr());
m_new_exprs.push_back(new_expr);
m_apply_cache.insert(n, new_expr);
TRACE("subst_bug", tout << "3. insert n: " << mk_pp(n.get_expr(), m_manager) << " : " << n.get_offset()
<< " --> " << mk_pp(new_expr, m_manager) << "\n";);
}
}
break;
default:
UNREACHABLE();
}
}
SASSERT(m_apply_cache.contains(n));
m_apply_cache.find(n, e);
m_new_exprs.push_back(e);
result = e;
if (s != expr_offset(0,0))
reset_cache();
TRACE("subst_bug", tout << "END substitution::apply\nresult:\n" << mk_pp(e, m_manager) << "\nref_count: " << e->get_ref_count() << "\n";);
}
inline substitution::color substitution::get_color(expr_offset const & p) const {
color c;
if (m_color.find(p, c))
return c;
return White;
}
inline void substitution::set_color(expr_offset const & p, color c) {
m_color.insert(p, c);
}
inline void substitution::visit(expr_offset const & p, bool & visited) {
if (get_color(p) != Black) {
m_todo.push_back(p);
visited = false;
}
}
bool substitution::visit_children(expr_offset const & p) {
bool visited = true;
expr * n = p.get_expr();
unsigned off;
expr_offset p1;
unsigned j;
switch (n->get_kind()) {
case AST_VAR:
if (find(p, p1) && p != p1)
visit(p1, visited);
break;
case AST_APP:
off = p.get_offset();
j = to_app(n)->get_num_args();
while (j > 0) {
--j;
visit(expr_offset(to_app(n)->get_arg(j), off), visited);
}
break;
default:
UNREACHABLE();
}
return visited;
}
bool substitution::acyclic(expr_offset p) {
if (get_color(p) == Black)
return true;
m_todo.reset();
m_todo.push_back(p);
while (!m_todo.empty()) {
expr_offset p = m_todo.back();
switch (get_color(p)) {
case Black:
m_todo.pop_back();
break;
case White:
set_color(p, Grey);
if (visit_children(p)) {
set_color(p, Black);
SASSERT(m_todo.back() == p);
m_todo.pop_back();
}
break;
case Grey:
if (!visit_children(p))
return false;
set_color(p, Black);
SASSERT(m_todo.back() == p);
m_todo.pop_back();
break;
}
}
return true;
}
bool substitution::acyclic() {
m_color.reset();
expr_offset r;
svector<var_offset>::iterator it = m_vars.begin();
svector<var_offset>::iterator end = m_vars.end();
for (; it != end; ++it) {
m_subst.find(it->first, it->second, r);
if (!acyclic(r))
return false;
}
return true;
}
void substitution::display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas) {
reset_cache();
for (unsigned i = 0; i < num_actual_offsets; i++)
for (unsigned j = 0; j < m_subst.vars_capacity(); j++) {
expr_offset r;
if (find(j, i, r)) {
expr_ref tmp(m_manager);
apply(num_actual_offsets, deltas, r, tmp);
out << "VAR " << j << ":" << i << " -->\n" << mk_pp(tmp, m_manager) << "\n";
}
}
}
void substitution::display(std::ostream & out) {
for (unsigned i = 0; i < m_subst.offsets_capacity(); i++)
for (unsigned j = 0; j < m_subst.vars_capacity(); j++) {
expr_offset r;
if (find(j, i, r))
out << "VAR " << j << ":" << i << " --> " << r.get_offset() << "\n" << mk_pp(r.get_expr(), m_manager) << "\n";
}
}

View file

@ -0,0 +1,202 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution.h
Abstract:
A substitution, that is, a mapping from (variable, offset) to (expr, offset).
We use offsets in order to avoid creating variants of terms.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _SUBSTITUTION_H_
#define _SUBSTITUTION_H_
#include"expr_offset_map.h"
#include"var_offset_map.h"
#include"ast_pp.h"
/**
\brief A mapping from (variable,offset) to expr_offset.
*/
class substitution {
ast_manager & m_manager;
var_offset_map<expr_offset> m_subst;
// field for backtracking
typedef std::pair<unsigned, unsigned> var_offset;
svector<var_offset> m_vars;
unsigned_vector m_scopes;
// fields for applying substitutions
svector<expr_offset> m_todo;
expr_offset_map<expr *> m_apply_cache;
expr_ref_vector m_new_exprs;
// fields for checking for cycles
enum color { White, Grey, Black };
expr_offset_map<color> m_color;
#ifdef Z3DEBUG
unsigned m_max_offset_since_reset;
#endif
void apply_visit(expr_offset const & n, bool & visited);
color get_color(expr_offset const & p) const;
void set_color(expr_offset const & p, color c);
void visit(expr_offset const & p, bool & visited);
bool visit_children(expr_offset const & p);
bool acyclic(expr_offset p);
public:
substitution(ast_manager & m);
ast_manager & get_manager() const { return m_manager; }
// -----------------------------------
//
// Reserve memory for the given number of
// offsets and variables.
//
// -----------------------------------
void reserve(unsigned num_offsets, unsigned num_vars) { m_subst.reserve(num_offsets, num_vars); }
void reserve_offsets(unsigned num_offsets) { m_subst.reserve_offsets(num_offsets); }
void reserve_vars(unsigned num_vars) { m_subst.reserve_vars(num_vars); }
// -----------------------------------
//
// Reset functions
//
// -----------------------------------
// reset everything
void reset();
// reset only the mapping from variables to expressions
void reset_subst();
// reset only the substitution application cache
void reset_cache();
// -----------------------------------
//
// Backtracking
//
// -----------------------------------
void push_scope() { m_scopes.push_back(m_vars.size()); }
void pop_scope(unsigned num_scopes = 1);
unsigned get_scope_lvl() { return m_scopes.size(); }
bool top_scope_has_bindings() const { return m_scopes.empty() ? !m_vars.empty() : m_scopes.back() < m_vars.size(); }
unsigned get_num_bindings() const { return m_vars.size(); }
// -----------------------------------
//
// Cycle detection
//
// -----------------------------------
bool acyclic();
// -----------------------------------
//
// Insertion & Lookup
//
// get_binding supplies a way to inspect the substitution.
//
// -----------------------------------
void insert(unsigned v_idx, unsigned offset, expr_offset const & t) {
TRACE("subst_insert", tout << "inserting: #" << v_idx << ":" << offset << " --> " << mk_pp(t.get_expr(), m_manager)
<< ":" << t.get_offset() << "\n";);
m_vars.push_back(var_offset(v_idx, offset));
m_subst.insert(v_idx, offset, t);
}
void insert(var * v, unsigned offset, expr_offset const & t) { insert(v->get_idx(), offset, t); }
void insert(expr_offset v, expr_offset const & t) {
SASSERT(is_var(v.get_expr()));
insert(to_var(v.get_expr()), v.get_offset(), t);
}
bool find(unsigned v_idx, unsigned offset, expr_offset & r) const { return m_subst.find(v_idx, offset, r); }
bool find(var * v, unsigned offset, expr_offset & r) const { return find(v->get_idx(), offset, r); }
bool find(expr_offset v, expr_offset & r) const {
SASSERT(is_var(v.get_expr()));
return find(to_var(v.get_expr()), v.get_offset(), r);
}
void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) {
var = m_vars[binding_num];
VERIFY(m_subst.find(var.first, var.second, r));
}
bool contains(var * v, unsigned offset) { expr_offset r; return find(v, offset, r); }
// -----------------------------------
//
// Application
//
// -----------------------------------
/**
\brief Apply the current substitution to the given
expression+offset. The result is an expression.
The argument num_actual_offsets is the maximum offset used in a
insert method since the last reset.
The argument deltas is an array of size num_actual_offsets. It contains
the variable delta for each offset. A free variable x:i in an expression offset t:j is mapped
to the variable x+delta[i].
*/
void apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_ref & result) {
apply(num_actual_offsets, deltas, n, expr_offset(0, 0), expr_offset(0, 0), result);
}
/**
\brief Similar to the previous method, but occurrences of s in n are substituted by t.
If s != expr_offset(0,0), then the cache is reset before and after the execution of this procedure.
*/
void apply(unsigned num_actual_offsets, unsigned const* deltas, expr_offset const & n, expr_offset const & s, expr_offset const & t, expr_ref & result);
void apply(expr * n, expr_ref & result) {
unsigned deltas[1] = { 0 };
apply(1, deltas, expr_offset(n, 0), result);
}
// -----------------------------------
//
// Debugging
//
// -----------------------------------
/**
\brief Dump the current substitution (for debugging purposes).
*/
void display(std::ostream & out, unsigned num_actual_offsets, unsigned const * deltas);
/**
\brief Dump the current substitution without normalizing expressions (for debugging purposes).
*/
void display(std::ostream & out);
// -----------------------------------
//
// Compare terms modulo a substitution
//
// -----------------------------------
bool compare(expr_offset t1, expr_offset t2);
};
#endif

View file

@ -0,0 +1,899 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution_tree.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-02-04.
Revision History:
--*/
#include"substitution_tree.h"
#include"ast_pp.h"
#include"ast_smt2_pp.h"
/**
\brief Return the next available register.
*/
unsigned substitution_tree::next_reg() {
while(true) {
unsigned curr = m_next_reg;
if (curr > m_max_reg)
m_max_reg = curr;
m_next_reg++;
if (curr >= m_used_regs.size() || !m_used_regs.get(curr))
return curr;
}
}
inline void substitution_tree::push(svector<subst> & sv, subst const & s) {
sv.push_back(s);
m_manager.inc_ref(s.first);
m_manager.inc_ref(s.second);
}
inline expr * substitution_tree::get_reg_value(unsigned ridx) {
return m_registers.get(ridx, 0);
}
inline void substitution_tree::set_reg_value(unsigned ridx, expr * e) {
m_registers.setx(ridx, e, 0);
}
inline void substitution_tree::erase_reg_from_todo(unsigned ridx) {
SASSERT(m_registers[ridx]);
m_registers[ridx] = 0;
SASSERT(m_todo.contains(ridx));
m_todo.erase(ridx);
}
/**
\brief Linearize the expressions in the registers stored in m_todo.
Store the result in \c result.
Example:
m_todo = { 3, 4 }
m_registers[3] = (f (g a))
m_registers[4] = b
next_regs are 5 6 7
result:
#3 -> (f #5); #4 -> b; #5 -> (g #6); #6 -> a
*/
void substitution_tree::linearize(svector<subst> & result) {
ptr_buffer<expr> new_args;
for (unsigned i = 0; i < m_todo.size(); i++) {
unsigned ireg_idx = m_todo[i];
expr * n = get_reg_value(ireg_idx);
var * ireg = m_manager.mk_var(ireg_idx, m_manager.get_sort(n));
if (is_var(n))
push(result, subst(ireg, n));
else {
SASSERT(is_app(n));
app * new_app;
unsigned num = to_app(n)->get_num_args();
if (num == 0)
new_app = to_app(n);
else {
for (unsigned j = 0; j < num; j++) {
unsigned oreg = next_reg();
set_reg_value(oreg, to_app(n)->get_arg(j));
m_todo.push_back(oreg);
sort * s = m_manager.get_sort(get_reg_value(oreg));
new_args.push_back(m_manager.mk_var(oreg, s));
}
new_app = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr());
new_args.reset();
}
push(result, subst(ireg, new_app));
}
}
}
/**
\brief Process the pair in := (f t_1 ... t_n) and out := (f r_1 ... r_n),
where r_i's are variables (register ids), and t_i's are arbitrary expressions.
The r_i's are added to the m_todo list, and m_registers[r_i] is assigned to t_i.
If save_set_registers == true, then r_i's are stored in m_to_reset.
*/
void substitution_tree::process_args(app * in, app * out) {
CTRACE("subst_tree_bug", in->get_num_args() != out->get_num_args(), tout << mk_ismt2_pp(in, m_manager) << "\n"
<< mk_ismt2_pp(out, m_manager) << "\n";);
unsigned num = out->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * in_arg = in->get_arg(i);
expr * out_arg = out->get_arg(i);
SASSERT(is_var(out_arg));
unsigned oreg = to_var(out_arg)->get_idx();
set_reg_value(oreg, in_arg);
m_todo.push_back(oreg);
}
}
/**
\brief Reset registers in m_todo at [old_size, m_todo.size())
*/
void substitution_tree::reset_registers(unsigned old_size) {
SASSERT(m_todo.size() >= old_size);
unsigned_vector::iterator it2 = m_todo.begin() + old_size;
unsigned_vector::iterator end2 = m_todo.end();
for (; it2 != end2; ++it2)
m_registers[*it2] = 0;
m_todo.shrink(old_size);
}
/**
\brief Return a measure on how compatible sv and the expressions to be processed are.
*/
unsigned substitution_tree::get_compatibility_measure(svector<subst> const & sv) {
unsigned old_size = m_todo.size();
unsigned measure = 0;
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
if (is_var(out)) {
if (out == in)
measure += 1;
}
else {
SASSERT(is_app(out));
if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) {
measure += 2;
process_args(to_app(in), to_app(out));
}
}
}
reset_registers(old_size);
return measure;
}
/**
\brief Find the child of r that is most compatible with the expressions stored
in the registers in m_todo.
Return 0 if none of the children has any compatible substitution entry.
*/
substitution_tree::node * substitution_tree::find_best_child(node * r) {
SASSERT(!r->m_leaf);
#ifdef Z3DEBUG
unsigned todo_size = m_todo.size();
#endif
node * best_child = 0;
unsigned max_measure = 0;
node * curr_child = r->m_first_child;
while (curr_child) {
unsigned measure = get_compatibility_measure(curr_child->m_subst);
if (measure > max_measure) {
best_child = curr_child;
max_measure = measure;
}
curr_child = curr_child->m_next_sibling;
}
SASSERT(todo_size == m_todo.size());
return best_child;
}
/**
\brief Reset datastructures used to insert/erase elements from the substitution tree.
*/
void substitution_tree::reset_compiler() {
m_todo.reset();
m_used_regs.reset();
m_next_reg = 1; // register 0 is reserved for input.
DEBUG_CODE({
ptr_vector<expr>::iterator it = m_registers.begin();
ptr_vector<expr>::iterator end = m_registers.end();
for (; it != end; ++it) {
SASSERT(*it == 0);
}
});
}
/**
\brief Create a node with the linearization for all registers in todo.
Attach new_expr to it.
*/
substitution_tree::node * substitution_tree::mk_node_for(expr * new_expr) {
node * n = alloc(node, true);
linearize(n->m_subst);
n->m_expr = new_expr;
m_manager.inc_ref(new_expr);
return n;
}
/**
\brief Mark register ridx as used.
*/
void substitution_tree::mark_used_reg(unsigned ridx) {
if (ridx >= m_used_regs.size())
m_used_regs.resize(ridx+1);
m_used_regs.set(ridx);
}
/**
\brief Mark (m_used_regs) all registers used in \c sv.
*/
void substitution_tree::mark_used_regs(svector<subst> const & sv) {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
mark_used_reg(s.first->get_idx());
if (is_app(s.second)) {
unsigned num_args = to_app(s.second)->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = to_app(s.second)->get_arg(i);
SASSERT(is_var(arg));
mark_used_reg(to_var(arg)->get_idx());
}
}
}
}
/**
\brief Insert a new expression in the substitution tree.
*/
void substitution_tree::insert(expr * new_expr) {
if (is_app(new_expr)) {
insert(to_app(new_expr));
}
else {
SASSERT(is_var(new_expr));
sort * s = to_var(new_expr)->get_sort();
unsigned id = s->get_decl_id();
if (id >= m_vars.size())
m_vars.resize(id+1, 0);
if (m_vars[id] == 0)
m_vars[id] = alloc(var_ref_vector, m_manager);
var_ref_vector * v = m_vars[id];
if (!v->contains(to_var(new_expr)))
v->push_back(to_var(new_expr));
}
}
/**
\brief Insert a new application in the substitution tree.
*/
void substitution_tree::insert(app * new_expr) {
reset_compiler();
set_reg_value(0, new_expr);
m_todo.push_back(0);
func_decl * d = new_expr->get_decl();
unsigned id = d->get_decl_id();
if (id >= m_roots.size())
m_roots.resize(id+1, 0);
if (!m_roots[id]) {
// there is no tree for the function symbol heading new_expr
m_roots[id] = mk_node_for(new_expr);
reset_registers(0);
m_size++;
return;
}
node * r = m_roots[id];
while (true) {
m_compatible.reset();
m_incompatible.reset();
svector<subst> & sv = r->m_subst;
// separate sv in the set of compatible & incompatible instructions
svector<subst>::iterator it = sv.begin();
svector<subst>::iterator end = sv.end();
for (; it != end; ++it) {
subst & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
SASSERT(is_var(out) || is_app(out));
if (is_var(out)) {
if (out == in) {
erase_reg_from_todo(ireg);
m_compatible.push_back(s);
}
else {
m_incompatible.push_back(s);
}
}
else {
if (in && is_app(in) && to_app(out)->get_decl() == to_app(in)->get_decl()) {
erase_reg_from_todo(ireg);
m_compatible.push_back(s);
process_args(to_app(in), to_app(out));
}
else {
m_incompatible.push_back(s);
}
}
}
// process m_compatible & m_incompatible
if (m_incompatible.empty()) {
if (m_todo.empty()) {
// nothing else to process
// new_expr is already in the substitution tree
SASSERT(r->m_leaf && r->m_expr == new_expr);
reset_registers(0);
return;
}
else {
mark_used_regs(r->m_subst);
node * best_child = find_best_child(r);
if (best_child == 0) {
// there is no compatible child
node * n = mk_node_for(new_expr);
n->m_next_sibling = r->m_first_child;
r->m_first_child = n;
reset_registers(0);
m_size++;
return;
}
else {
// continue with best_child
r = best_child;
}
}
}
else {
SASSERT(!m_compatible.empty());
SASSERT(!m_incompatible.empty());
mark_used_regs(m_compatible);
r->m_subst.swap(m_compatible);
node * n = mk_node_for(new_expr);
node * incomp = alloc(node, r->m_leaf);
incomp->m_subst.swap(m_incompatible);
if (r->m_leaf) {
incomp->m_expr = r->m_expr;
r->m_leaf = false;
}
else
incomp->m_first_child = r->m_first_child;
incomp->m_next_sibling = n;
SASSERT(!r->m_leaf);
r->m_first_child = incomp;
reset_registers(0);
m_size++;
return;
}
}
}
/**
\brief Return true if sv is fully compatible with the expressions in the registers in m_todo.
*/
bool substitution_tree::is_fully_compatible(svector<subst> const & sv) {
unsigned old_size = m_todo.size();
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
if (is_var(out)) {
if (out != in) {
reset_registers(old_size);
return false;
}
}
else {
if (!in || !is_app(in) || to_app(in)->get_decl() != to_app(out)->get_decl()) {
reset_registers(old_size);
return false;
}
process_args(to_app(in), to_app(out));
}
}
reset_registers(old_size);
return true;
}
/**
\brief Return a child of r that is fully compatible with the expressions in the registers in m_todo.
*/
bool substitution_tree::find_fully_compatible_child(node * r, node * & prev, node * & child) {
SASSERT(!r->m_leaf);
prev = 0;
child = r->m_first_child;
while (child) {
if (is_fully_compatible(child->m_subst))
return true;
prev = child;
child = child->m_next_sibling;
}
return false;
}
inline bool substitution_tree::at_least_3_children(node * r) {
return !r->m_leaf && r->m_first_child->m_next_sibling && r->m_first_child->m_next_sibling->m_next_sibling;
}
/**
\brief Remove expression from the substitution tree.
Do nothing, if n is not in the tree.
*/
void substitution_tree::erase(expr * e) {
if (is_app(e))
erase(to_app(e));
else {
SASSERT(is_var(e));
sort * s = to_var(e)->get_sort();
unsigned id = s->get_decl_id();
if (id >= m_vars.size() || m_vars[id] == 0)
return;
var_ref_vector * v = m_vars[id];
v->erase(to_var(e));
}
}
/**
\brief Remove application from the substitution tree.
Do nothing, if n is not in the tree.
*/
void substitution_tree::erase(app * e) {
func_decl * d = e->get_decl();
unsigned id = d->get_decl_id();
if (id >= m_roots.size() || !m_roots[id])
return;
reset_compiler();
set_reg_value(0, e);
m_todo.push_back(0);
node * r = m_roots[id];
node * parent = 0;
node * prev = 0;
while (true) {
svector<subst> & sv = r->m_subst;
svector<subst>::iterator it = sv.begin();
svector<subst>::iterator end = sv.end();
for (; it != end; ++it) {
subst & s = *it;
unsigned ireg = s.first->get_idx();
expr * out = s.second;
expr * in = get_reg_value(ireg);
SASSERT(is_var(out) || is_app(out));
if (is_var(out)) {
if (out != in) {
reset_registers(0);
return; // node is not in the substitution tree
}
erase_reg_from_todo(ireg);
}
else {
if (!in || !is_app(in) || to_app(out)->get_decl() != to_app(in)->get_decl()) {
reset_registers(0);
return; // node is not in the substitution tree
}
erase_reg_from_todo(ireg);
process_args(to_app(in), to_app(out));
}
}
if (m_todo.empty()) {
reset_registers(0);
SASSERT(r->m_expr == e);
if (parent == 0) {
delete_node(r);
m_roots[id] = 0;
}
else if (at_least_3_children(parent)) {
if (prev == 0)
parent->m_first_child = r->m_next_sibling;
else
prev->m_next_sibling = r->m_next_sibling;
delete_node(r);
}
else {
SASSERT(parent->m_first_child && parent->m_first_child->m_next_sibling && !parent->m_first_child->m_next_sibling->m_next_sibling);
node * other_child = prev ? prev : r->m_next_sibling;
SASSERT(other_child);
parent->m_subst.append(other_child->m_subst);
parent->m_leaf = other_child->m_leaf;
if (other_child->m_leaf)
parent->m_expr = other_child->m_expr;
else
parent->m_first_child = other_child->m_first_child;
delete_node(r);
dealloc(other_child); // Remark: I didn't use delete_node since its resources were sent to parent.
}
m_size --;
return;
}
else {
parent = r;
if (!find_fully_compatible_child(r, prev, r)) {
// node is not in the substitution tree
reset_registers(0);
return;
}
// continue with fully compatible child
}
}
}
void substitution_tree::delete_node(node * n) {
ptr_buffer<node> todo;
SASSERT(todo.empty());
todo.push_back(n);
while (!todo.empty()) {
node * n = todo.back();
todo.pop_back();
svector<subst>::iterator it2 = n->m_subst.begin();
svector<subst>::iterator end2 = n->m_subst.end();
for (; it2 != end2; ++it2) {
m_manager.dec_ref(it2->first);
m_manager.dec_ref(it2->second);
}
if (n->m_leaf)
m_manager.dec_ref(n->m_expr);
else {
node * c = n->m_first_child;
while (c) {
todo.push_back(c);
c = c->m_next_sibling;
}
}
dealloc(n);
}
}
void substitution_tree::reset() {
ptr_vector<node>::iterator it = m_roots.begin();
ptr_vector<node>::iterator end = m_roots.end();
for (; it != end; ++it) {
if (*it)
delete_node(*it);
}
m_roots.reset();
std::for_each(m_vars.begin(), m_vars.end(), delete_proc<var_ref_vector>());
m_vars.reset();
m_size = 0;
}
void substitution_tree::display(std::ostream & out, subst const & s) const {
out << "r!" << s.first->get_idx() << " -> ";
if (is_app(s.second)) {
unsigned num = to_app(s.second)->get_num_args();
if (num == 0)
out << to_app(s.second)->get_decl()->get_name();
else {
out << "(" << to_app(s.second)->get_decl()->get_name();
for (unsigned i = 0; i < num; i++)
out << " r!" << to_var(to_app(s.second)->get_arg(i))->get_idx();
out << ")";
}
}
else {
out << mk_pp(s.second, m_manager);
}
}
void substitution_tree::display(std::ostream & out, svector<subst> const & sv) const {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (bool first = true; it != end; ++it, first = false) {
subst const & s = *it;
if (!first)
out << "; ";
display(out, s);
}
}
void substitution_tree::display(std::ostream & out, node * n, unsigned delta) const {
for (unsigned i = 0; i < delta; i++)
out << " ";
display(out, n->m_subst);
if (n->m_leaf) {
pp_params p;
p.m_pp_single_line = true;
out << " ==> ";
ast_pp(out, n->m_expr, m_manager, p);
out << "\n";
}
else {
out << "\n";
node * c = n->m_first_child;
while (c) {
display(out, c, delta+1);
c = c->m_next_sibling;
}
}
}
bool substitution_tree::backtrack() {
while (!m_bstack.empty()) {
TRACE("st", tout << "backtracking...\n";);
m_subst->pop_scope();
node * n = m_bstack.back();
if (n->m_next_sibling) {
m_bstack.back() = n->m_next_sibling;
return true;
}
m_bstack.pop_back();
}
return false;
}
inline expr_offset substitution_tree::find(expr_offset p) {
TRACE("substitution_tree_bug", tout << "find...\n";);
while (is_var(p.get_expr())) {
TRACE("substitution_tree_bug", tout << mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";);
if (!m_subst->find(to_var(p.get_expr()), p.get_offset(), p))
return p;
}
return p;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::bind_var(var * v, unsigned offset, expr_offset const & p) {
TRACE("st", tout << "bind_var: " << mk_pp(v, m_manager) << " " << offset << "\n" <<
mk_pp(p.get_expr(), m_manager) << " " << p.get_offset() << "\n";);
if (Mode == STV_INST && offset == m_st_offset) {
SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset);
if (is_var(p.get_expr()) && p.get_offset() == m_in_offset) {
m_subst->insert(p, expr_offset(v, offset));
return true;
}
return false;
}
if (Mode == STV_GEN && offset == m_in_offset) {
SASSERT(!is_var(p.get_expr()) || p.get_offset() != m_reg_offset);
if (is_var(p.get_expr()) && p.get_offset() == m_st_offset) {
m_subst->insert(p, expr_offset(v, offset));
return true;
}
return false;
}
m_subst->insert(v, offset, p);
TRACE("st_bug", tout << "substitution updated\n"; m_subst->display(tout););
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::unify_match(expr_offset p1, expr_offset p2) {
svector<entry> & todo = m_visit_todo;
todo.reset();
todo.push_back(entry(p1, p2));
while (!todo.empty()) {
entry const & e = todo.back();
p1 = find(e.first);
p2 = find(e.second);
todo.pop_back();
if (p1 != p2) {
expr * n1 = p1.get_expr();
expr * n2 = p2.get_expr();
SASSERT(!is_quantifier(n1));
SASSERT(!is_quantifier(n2));
bool v1 = is_var(n1);
bool v2 = is_var(n2);
TRACE("st",
tout << "n1: " << mk_pp(n1, m_manager) << " " << p1.get_offset() << "\n";
tout << "n2: " << mk_pp(n2, m_manager) << " " << p2.get_offset() << "\n";);
if (v1 && v2) {
if (p2.get_offset() == m_reg_offset)
std::swap(p1, p2);
if (!bind_var<Mode>(to_var(p1.get_expr()), p1.get_offset(), p2))
return false;
}
else if (v1) {
if (!bind_var<Mode>(to_var(n1), p1.get_offset(), p2))
return false;
}
else if (v2) {
if (!bind_var<Mode>(to_var(n2), p2.get_offset(), p1))
return false;
}
else {
app * a1 = to_app(n1);
app * a2 = to_app(n2);
unsigned off1 = p1.get_offset();
unsigned off2 = p2.get_offset();
if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args())
return false;
unsigned j = a1->get_num_args();
while (j > 0) {
--j;
entry new_e(expr_offset(a1->get_arg(j), off1),
expr_offset(a2->get_arg(j), off2));
todo.push_back(new_e);
}
}
}
}
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit_vars(expr * e, st_visitor & st) {
if (m_vars.empty())
return true; // continue
sort * s = m_manager.get_sort(e);
unsigned s_id = s->get_decl_id();
if (s_id < m_vars.size()) {
var_ref_vector * v = m_vars[s_id];
if (v && !v->empty()) {
unsigned sz = v->size();
for (unsigned i = 0; i < sz; i++) {
var * curr = v->get(i);
m_subst->push_scope();
if (unify_match<Mode>(expr_offset(curr, m_st_offset), expr_offset(e, m_in_offset))) {
if (Mode != STV_UNIF || m_subst->acyclic()) {
if (!st(curr)) {
m_subst->pop_scope();
return false; // stop
}
}
}
m_subst->pop_scope();
}
}
}
return true; // continue
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit(svector<subst> const & sv) {
svector<subst>::const_iterator it = sv.begin();
svector<subst>::const_iterator end = sv.end();
for (; it != end; ++it) {
subst const & s = *it;
TRACE("st", tout << "processing subst:\n"; display(tout, s); tout << "\n";);
var * rin = s.first;
expr * out = s.second;
expr_offset p1(rin, m_reg_offset);
expr_offset p2(out, is_var(out) ? m_st_offset : m_reg_offset);
if (!unify_match<Mode>(p1, p2))
return false;
}
return true;
}
template<substitution_tree::st_visit_mode Mode>
bool substitution_tree::visit(expr * e, st_visitor & st, node * r) {
m_bstack.reset();
m_bstack.push_back(r);
m_subst->push_scope();
m_subst->insert(static_cast<unsigned>(0), m_reg_offset, expr_offset(e, m_in_offset));
while (true) {
node * n = m_bstack.back();
TRACE("st", tout << "push scope...\n";);
m_subst->push_scope();
TRACE("st", tout << "processing node:\n"; display(tout, n->m_subst); tout << "\n";);
if (visit<Mode>(n->m_subst)) {
if (n->m_leaf) {
// if searching for unifiers and the substitution is cyclic, then backtrack.
if (Mode == STV_UNIF && !m_subst->acyclic()) {
if (!backtrack())
break;
}
else {
TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";);
if (!st(n->m_expr))
return false;
if (!backtrack())
break;
}
}
else {
m_bstack.push_back(n->m_first_child);
}
}
else if (!backtrack())
break;
}
while (!m_bstack.empty()) {
m_subst->pop_scope();
m_bstack.pop_back();
}
m_subst->pop_scope();
return true;
}
template<substitution_tree::st_visit_mode Mode>
void substitution_tree::visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
m_in_offset = in_offset;
m_st_offset = st_offset;
m_reg_offset = reg_offset;
m_subst = &(st.get_substitution());
m_subst->reserve_vars(get_approx_num_regs());
if (visit_vars<Mode>(e, st)) {
if (is_app(e)) {
func_decl * d = to_app(e)->get_decl();
unsigned id = d->get_decl_id();
node * r = m_roots.get(id, 0);
if (r)
visit<Mode>(e, st, r);
}
else {
SASSERT(is_var(e));
ptr_vector<node>::iterator it = m_roots.begin();
ptr_vector<node>::iterator end = m_roots.end();
for (; it != end; ++it) {
node * r = *it;
if (r != 0) {
var * v = r->m_subst[0].first;
if (v->get_sort() == to_var(e)->get_sort())
if (!visit<Mode>(e, st, r))
break;
}
}
}
}
}
void substitution_tree::unify(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_UNIF>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::inst(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_INST>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::gen(expr * e, st_visitor & v, unsigned in_offset, unsigned st_offset, unsigned reg_offset) {
visit<STV_GEN>(e, v, in_offset, st_offset, reg_offset);
}
void substitution_tree::display(std::ostream & out) const {
out << "substitution tree:\n";
ptr_vector<node>::const_iterator it = m_roots.begin();
ptr_vector<node>::const_iterator end = m_roots.end();
for (; it != end; ++it)
if (*it)
display(out, *it, 0);
bool found_var = false;
ptr_vector<var_ref_vector>::const_iterator it2 = m_vars.begin();
ptr_vector<var_ref_vector>::const_iterator end2 = m_vars.end();
for (; it2 != end2; ++it2) {
var_ref_vector * v = *it2;
if (v == 0)
continue; // m_vars may contain null pointers. See substitution_tree::insert.
unsigned num = v->size();
for (unsigned i = 0; i < num; i++) {
if (!found_var) {
found_var = true;
out << "vars: ";
}
out << mk_pp(v->get(i), m_manager) << " ";
}
}
if (found_var)
out << "\n";
}
substitution_tree::substitution_tree(ast_manager & m):
m_manager(m),
m_max_reg(0),
m_size(0) {
}
substitution_tree::~substitution_tree() {
reset();
}

View file

@ -0,0 +1,148 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
substitution_tree.h
Abstract:
Substitution Trees
Author:
Leonardo de Moura (leonardo) 2008-02-03.
Revision History:
--*/
#ifndef _SUBSTITUTION_TREE_H_
#define _SUBSTITUTION_TREE_H_
#include"ast.h"
#include"substitution.h"
/**
\brief Substitution tree visitor.
*/
class st_visitor {
protected:
substitution & m_subst;
public:
st_visitor(substitution & s):m_subst(s) {}
virtual ~st_visitor() {}
substitution & get_substitution() { return m_subst; }
virtual bool operator()(expr * e) { return true; }
};
/**
\brief Substitution tree term index.
*/
class substitution_tree {
typedef std::pair<var *, expr *> subst;
struct node {
bool m_leaf;
svector<subst> m_subst;
node * m_next_sibling;
union {
node * m_first_child;
expr * m_expr;
};
node(bool leaf):m_leaf(leaf), m_next_sibling(0), m_first_child(0) {}
};
ast_manager & m_manager;
ptr_vector<node> m_roots;
unsigned m_max_reg;
ptr_vector<expr> m_registers;
unsigned m_size;
ptr_vector<var_ref_vector> m_vars; // mapping from decl_id to var_ref_vector
// Compilation time fields
unsigned m_next_reg;
bit_vector m_used_regs;
unsigned_vector m_todo;
svector<subst> m_compatible;
svector<subst> m_incompatible;
// Execution time fields
substitution * m_subst;
ptr_vector<node> m_bstack;
unsigned m_in_offset;
unsigned m_st_offset;
unsigned m_reg_offset;
typedef std::pair<expr_offset, expr_offset> entry;
svector<entry> m_visit_todo;
unsigned next_reg();
void push(svector<subst> & sv, subst const & s);
expr * get_reg_value(unsigned ridx);
void set_reg_value(unsigned ridx, expr * e);
void erase_reg_from_todo(unsigned ridx);
void linearize(svector<subst> & result);
void process_args(app * in, app * out);
void reset_registers(unsigned old_size);
unsigned get_compatibility_measure(svector<subst> const & sv);
node * find_best_child(node * r);
void reset_compiler();
node * mk_node_for(expr * new_expr);
void mark_used_reg(unsigned ridx);
void mark_used_regs(svector<subst> const & sv);
bool is_fully_compatible(svector<subst> const & sv);
bool find_fully_compatible_child(node * r, node * & prev, node * & child);
static bool at_least_3_children(node * r);
void delete_node(node * n);
void display(std::ostream & out, subst const & s) const;
void display(std::ostream & out, svector<subst> const & sv) const;
void display(std::ostream & out, node * n, unsigned delta) const;
enum st_visit_mode {
STV_UNIF,
STV_INST,
STV_GEN
};
expr_offset find(expr_offset p);
bool backtrack();
template<st_visit_mode Mode>
bool bind_var(var * v, unsigned offset, expr_offset const & p);
template<st_visit_mode Mode>
bool unify_match(expr_offset p1, expr_offset p2);
template<substitution_tree::st_visit_mode Mode>
bool visit_vars(expr * e, st_visitor & st);
template<st_visit_mode Mode>
bool visit(svector<subst> const & s);
template<st_visit_mode Mode>
bool visit(expr * e, st_visitor & st, node * r);
template<st_visit_mode Mode>
void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset);
public:
substitution_tree(ast_manager & m);
~substitution_tree();
void insert(app * n);
void insert(expr * n);
void erase(app * n);
void erase(expr * n);
void reset();
bool empty() const { return m_size == 0; }
void unify(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
void inst(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
void gen(expr * e, st_visitor & v, unsigned in_offset = 0, unsigned st_offset = 1, unsigned reg_offset = 2);
unsigned get_approx_num_regs() const { return m_max_reg + 1; }
void display(std::ostream & out) const;
};
#endif /* _SUBSTITUTION_TREE_H_ */

View file

@ -0,0 +1,183 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
unifier.cpp
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#include"unifier.h"
#include"ast_pp.h"
void unifier::reset(unsigned num_offsets) {
m_todo.reset();
m_find.reset();
m_size.reset();
}
/**
\brief Find with path compression.
*/
expr_offset unifier::find(expr_offset p) {
buffer<expr_offset> path;
expr_offset next;
while (m_find.find(p, next)) {
path.push_back(p);
p = next;
}
buffer<expr_offset>::iterator it = path.begin();
buffer<expr_offset>::iterator end = path.end();
for (; it != end; ++it) {
expr_offset & prev = *it;
m_find.insert(prev, p);
}
return p;
}
void unifier::save_var(expr_offset const & p, expr_offset const & t) {
expr * n = p.get_expr();
if (is_var(n)) {
unsigned off = p.get_offset();
m_subst->insert(to_var(n)->get_idx(), off, t);
}
}
/**
\brief Merge the equivalence classes of n1 and n2. n2 will be the
root of the resultant equivalence class.
*/
void unifier::union1(expr_offset const & n1, expr_offset const & n2) {
DEBUG_CODE({
expr_offset f;
SASSERT(!m_find.find(n1, f));
SASSERT(!m_find.find(n2, f));
});
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(n1, sz1);
m_size.find(n2, sz2);
m_find.insert(n1, n2);
m_size.insert(n2, sz1 + sz2);
save_var(n1, n2);
}
/**
\brief Merge the equivalence classes of n1 and n2. The root of the
resultant equivalence class is the one with more elements.
*/
void unifier::union2(expr_offset n1, expr_offset n2) {
DEBUG_CODE({
expr_offset f;
SASSERT(!m_find.find(n1, f));
SASSERT(!m_find.find(n2, f));
});
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(n1, sz1);
m_size.find(n2, sz2);
if (sz1 > sz2)
std::swap(n1, n2);
m_find.insert(n1, n2);
m_size.insert(n2, sz1 + sz2);
save_var(n1, n2);
}
bool unifier::unify_core(expr_offset p1, expr_offset p2) {
entry e(p1, p2);
m_todo.push_back(e);
while (!m_todo.empty()) {
entry const & e = m_todo.back();
p1 = find(e.first);
p2 = find(e.second);
m_todo.pop_back();
if (p1 != p2) {
expr * n1 = p1.get_expr();
expr * n2 = p2.get_expr();
SASSERT(!is_quantifier(n1));
SASSERT(!is_quantifier(n2));
bool v1 = is_var(n1);
bool v2 = is_var(n2);
if (v1 && v2) {
union2(p1, p2);
}
else if (v1) {
union1(p1, p2);
}
else if (v2) {
union1(p2, p1);
}
else {
app * a1 = to_app(n1);
app * a2 = to_app(n2);
unsigned off1 = p1.get_offset();
unsigned off2 = p2.get_offset();
if (a1->get_decl() != a2->get_decl() || a1->get_num_args() != a2->get_num_args())
return false;
union2(p1, p2);
unsigned j = a1->get_num_args();
while (j > 0) {
--j;
entry new_e(expr_offset(a1->get_arg(j), off1),
expr_offset(a2->get_arg(j), off2));
m_todo.push_back(new_e);
}
}
}
}
return true;
}
bool unifier::operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets) {
SASSERT(num_exprs > 0);
unsigned num_offsets = use_offsets ? num_exprs : 1;
reset(num_offsets);
m_subst = &s;
#if 1
TRACE("unifier", for (unsigned i = 0; i < num_exprs; ++i) tout << mk_pp(es[i], m_manager) << "\n";);
for (unsigned i = s.get_num_bindings(); i > 0; ) {
--i;
std::pair<unsigned,unsigned> bound;
expr_offset root, child;
s.get_binding(i, bound, root);
TRACE("unifier", tout << bound.first << " |-> " << mk_pp(root.get_expr(), m_manager) << "\n";);
if (is_var(root.get_expr())) {
var* v = m_manager.mk_var(bound.first,to_var(root.get_expr())->get_sort());
child = expr_offset(v, bound.second);
unsigned sz1 = 1;
unsigned sz2 = 1;
m_size.find(child, sz1);
m_size.find(root, sz2);
m_find.insert(child, root);
m_size.insert(root, sz1 + sz2);
}
}
#endif
for (unsigned i = 0; i < num_exprs - 1; i++) {
if (!unify_core(expr_offset(es[i], use_offsets ? i : 0),
expr_offset(es[i+1], use_offsets ? i + 1 : 0))) {
m_last_call_succeeded = false;
return m_last_call_succeeded;
}
}
m_last_call_succeeded = m_subst->acyclic();
return m_last_call_succeeded;
}
bool unifier::operator()(expr * e1, expr * e2, substitution & s, bool use_offsets) {
expr * es[2] = { e1, e2 };
return operator()(2, es, s, use_offsets);
}

View file

@ -0,0 +1,70 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
unifier.h
Abstract:
Quasi-linear unification.
Author:
Leonardo de Moura (leonardo) 2008-01-28.
Revision History:
--*/
#ifndef _UNIFIER_H_
#define _UNIFIER_H_
#include"ast.h"
#include"substitution.h"
/**
\brief Functor for unifying expressions.
It implements a quasi-linear unification algorithm.
It has support for two different variable banks: left and right.
That is, variable i in left bank is considered different from
variable i in the right bank. This feature allows us to avoid
unnecessary variable renaming.
*/
class unifier {
typedef std::pair<expr_offset, expr_offset> entry;
ast_manager & m_manager;
substitution * m_subst;
svector<entry> m_todo;
expr_offset_map<expr_offset> m_find;
expr_offset_map<unsigned> m_size;
bool m_last_call_succeeded;
expr_offset find(expr_offset n);
void save_var(expr_offset const & p, expr_offset const & t);
void union1(expr_offset const & n1, expr_offset const & n2);
void union2(expr_offset n1, expr_offset n2);
void reset(unsigned num_offsets);
bool unify_core(expr_offset p1, expr_offset p2);
public:
unifier(ast_manager & m):m_manager(m), m_last_call_succeeded(false) {}
/**
\brief Unify the given expressions. Return true if succeeded,
and store the result in the given substitution.
If use_offsets is true, then the variables in the given expressions are assumed to be
in different banks.
*/
bool operator()(unsigned num_exprs, expr ** es, substitution & s, bool use_offsets = true);
bool operator()(expr * e1, expr * e2, substitution & s, bool use_offsets = true);
};
#endif /* _UNIFIER_H_ */

View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
var_offset_map.h
Abstract:
A generic mapping from (var, offset) to a value T.
Author:
Leonardo de Moura (leonardo) 2008-02-01.
Revision History:
--*/
#ifndef _VAR_OFFSET_MAP_H_
#define _VAR_OFFSET_MAP_H_
#include"ast.h"
#include"vector.h"
/**
\brief A mapping from variable-id + offset to some value of type T.
*/
template<typename T>
class var_offset_map {
protected:
struct data {
T m_data;
unsigned m_timestamp;
data():m_timestamp(0) {}
};
svector<data> m_map;
unsigned m_num_offsets;
unsigned m_num_vars;
unsigned m_timestamp;
public:
var_offset_map():
m_num_offsets(0),
m_num_vars(0),
m_timestamp(1) {
}
void reset() {
m_timestamp++;
if (m_timestamp == UINT_MAX) {
typename svector<data>::iterator it = m_map.begin();
typename svector<data>::iterator end = m_map.end();
for (; it != end; ++it)
it->m_timestamp = 0;
m_timestamp = 1;
}
}
unsigned offsets_capacity() const { return m_num_offsets; }
unsigned vars_capacity() const { return m_num_vars; }
void reserve(unsigned num_offsets, unsigned num_vars) {
if (num_offsets > m_num_offsets || num_vars > m_num_vars) {
unsigned sz = num_offsets * num_vars;
m_map.resize(sz);
m_num_vars = num_vars;
m_num_offsets = num_offsets;
}
reset();
}
void reserve_offsets(unsigned num_offsets) { reserve(num_offsets, m_num_vars); }
void reserve_vars(unsigned num_vars) { reserve(m_num_offsets, num_vars); }
void insert(unsigned v_idx, unsigned offset, T const & t) {
SASSERT(v_idx < m_num_vars);
SASSERT(offset < m_num_offsets);
unsigned idx = v_idx + offset * m_num_vars;
SASSERT(idx < m_map.size());
data & d = m_map[idx];
d.m_data = t;
d.m_timestamp = m_timestamp;
}
void insert(var * v, unsigned offset, T const & t) { insert(v->get_idx(), offset, t); }
bool find(unsigned v_idx, unsigned offset, T & r) const {
SASSERT(v_idx < m_num_vars);
SASSERT(offset < m_num_offsets);
unsigned idx = v_idx + offset * m_num_vars;
data const & d = m_map[idx];
SASSERT(d.m_timestamp <= m_timestamp);
if (d.m_timestamp == m_timestamp) {
r = d.m_data;
return true;
}
return false;
}
bool find(var * v, unsigned offset, T & r) const { return find(v->get_idx(), offset, r); }
void erase(unsigned v_idx, unsigned offset) {
SASSERT(v_idx < m_num_vars);
SASSERT(offset < m_num_offsets);
unsigned idx = v_idx + offset * m_num_vars;
m_map[idx].m_timestamp = 0;
}
};
#endif /* _VAR_OFFSET_MAP_H_ */