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:
parent
9e299b88c4
commit
0a4446ae26
255 changed files with 17 additions and 17 deletions
53
src/ast/substitution/expr_offset.h
Normal file
53
src/ast/substitution/expr_offset.h
Normal 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_ */
|
94
src/ast/substitution/expr_offset_map.h
Normal file
94
src/ast/substitution/expr_offset_map.h
Normal 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_ */
|
81
src/ast/substitution/matcher.cpp
Normal file
81
src/ast/substitution/matcher.cpp
Normal 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();
|
||||
}
|
64
src/ast/substitution/matcher.h
Normal file
64
src/ast/substitution/matcher.h
Normal 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_ */
|
||||
|
286
src/ast/substitution/substitution.cpp
Normal file
286
src/ast/substitution/substitution.cpp
Normal 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";
|
||||
}
|
||||
}
|
||||
|
202
src/ast/substitution/substitution.h
Normal file
202
src/ast/substitution/substitution.h
Normal 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
|
899
src/ast/substitution/substitution_tree.cpp
Normal file
899
src/ast/substitution/substitution_tree.cpp
Normal 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();
|
||||
}
|
148
src/ast/substitution/substitution_tree.h
Normal file
148
src/ast/substitution/substitution_tree.h
Normal 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_ */
|
||||
|
183
src/ast/substitution/unifier.cpp
Normal file
183
src/ast/substitution/unifier.cpp
Normal 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);
|
||||
}
|
||||
|
70
src/ast/substitution/unifier.h
Normal file
70
src/ast/substitution/unifier.h
Normal 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_ */
|
||||
|
114
src/ast/substitution/var_offset_map.h
Normal file
114
src/ast/substitution/var_offset_map.h
Normal 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_ */
|
Loading…
Add table
Add a link
Reference in a new issue