mirror of
https://github.com/Z3Prover/z3
synced 2025-04-25 10:05:32 +00:00
Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
3f9edad676
commit
e9eab22e5c
1186 changed files with 381859 additions and 0 deletions
224
lib/act_cache.cpp
Normal file
224
lib/act_cache.cpp
Normal file
|
@ -0,0 +1,224 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
act_cache.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
expr -> expr activity cache
|
||||
It maintains at most N unused entries
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-04-12
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"act_cache.h"
|
||||
|
||||
#define MIN_MAX_UNUSED 1024
|
||||
#define INITIAL_CAPACITY 128
|
||||
|
||||
/*
|
||||
This cache is a mapping from expr -> tagged expressions
|
||||
A tagged expression is essentially a pair (expr, flag)
|
||||
Thus, an entry
|
||||
t -> (s, 0)
|
||||
maps the key t to value s, and says that key t was never accessed.
|
||||
That is, client code never executed find(t)
|
||||
Similarly, an entry
|
||||
t -> (s, 1)
|
||||
also maps the key t to value s, but signs that key t was already accessed
|
||||
by client code.
|
||||
|
||||
When a new key/value pair is inserted the flag is 0.
|
||||
The flag is set to 1 after the key is accessed.
|
||||
|
||||
The number of unused entries (m_unused) is equal to the number of entries
|
||||
of the form
|
||||
t -> (s, 0)
|
||||
That is, it is the number of keys that were never accessed by cliend code.
|
||||
|
||||
The cache maintains at most m_max_unused entries.
|
||||
When the maximum number of unused entries exceeds m_max_unused, then
|
||||
the cache will delete the oldest unused entry.
|
||||
*/
|
||||
|
||||
/**
|
||||
m_queue stores the recently added keys.
|
||||
The queue is implemented as pair: m_queue (vector), m_qhead (unsigned).
|
||||
The "active" part of m_queue is the range [m_qhead, m_queue.size())
|
||||
The "inactive" part [0, m_qhead) contains keys that were already used by client code.
|
||||
This procedure, deletes the inactive part, and makes m_qhead == 0.
|
||||
*/
|
||||
void act_cache::compress_queue() {
|
||||
SASSERT(m_qhead > 0);
|
||||
unsigned sz = m_queue.size();
|
||||
unsigned j = 0;
|
||||
for (unsigned i = m_qhead; i < sz; i++, j++) {
|
||||
m_queue[j] = m_queue[i];
|
||||
}
|
||||
m_queue.shrink(j);
|
||||
m_qhead = 0;
|
||||
}
|
||||
|
||||
void act_cache::init() {
|
||||
if (m_max_unused < MIN_MAX_UNUSED)
|
||||
m_max_unused = MIN_MAX_UNUSED;
|
||||
m_unused = 0;
|
||||
m_qhead = 0;
|
||||
}
|
||||
|
||||
void act_cache::dec_refs() {
|
||||
map::iterator it = m_table.begin();
|
||||
map::iterator end = m_table.end();
|
||||
for (; it != end; ++it) {
|
||||
m_manager.dec_ref((*it).m_key);
|
||||
m_manager.dec_ref(UNTAG(expr*, (*it).m_value));
|
||||
}
|
||||
}
|
||||
|
||||
act_cache::act_cache(ast_manager & m):
|
||||
m_manager(m),
|
||||
m_max_unused(m.get_num_asts()) {
|
||||
init();
|
||||
}
|
||||
|
||||
act_cache::act_cache(ast_manager & m, unsigned max_unused):
|
||||
m_manager(m),
|
||||
m_max_unused(max_unused) {
|
||||
init();
|
||||
}
|
||||
|
||||
act_cache::~act_cache() {
|
||||
dec_refs();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Search m_queue from [m_qhead, m_queue.size()) until it finds
|
||||
an unused key. That is a key associated with an entry
|
||||
key -> (value, 0)
|
||||
*/
|
||||
void act_cache::del_unused() {
|
||||
unsigned sz = m_queue.size();
|
||||
while (m_qhead < sz) {
|
||||
expr * k = m_queue[m_qhead];
|
||||
m_qhead++;
|
||||
SASSERT(m_table.contains(k));
|
||||
map::key_value * entry = m_table.find_core(k);
|
||||
SASSERT(entry);
|
||||
if (GET_TAG(entry->m_value) == 0) {
|
||||
// Key k was never accessed by client code.
|
||||
// That is, find(k) was never executed by client code.
|
||||
m_unused--;
|
||||
expr * v = entry->m_value;
|
||||
m_table.erase(k);
|
||||
m_manager.dec_ref(k);
|
||||
m_manager.dec_ref(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_qhead == sz) {
|
||||
// The "active" part of the queue is empty.
|
||||
// So, we perform a "cheap" compress.
|
||||
m_queue.reset();
|
||||
m_qhead = 0;
|
||||
}
|
||||
else if (m_qhead > m_max_unused) {
|
||||
compress_queue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Insert a new entry k -> v into the cache.
|
||||
*/
|
||||
void act_cache::insert(expr * k, expr * v) {
|
||||
SASSERT(k);
|
||||
if (m_unused >= m_max_unused)
|
||||
del_unused();
|
||||
expr * dummy = reinterpret_cast<expr*>(1);
|
||||
map::key_value & entry = m_table.insert_if_not_there(k, dummy);
|
||||
#if 0
|
||||
unsigned static counter = 0;
|
||||
counter++;
|
||||
if (counter % 100000 == 0)
|
||||
verbose_stream() << "[act-cache] counter: " << counter << " used_slots: " << m_table.used_slots() << " capacity: " << m_table.capacity() << " size: " << m_table.size() << " collisions: " << m_table.collisions() << "\n";
|
||||
#endif
|
||||
|
||||
#ifdef Z3DEBUG
|
||||
unsigned expected_tag;
|
||||
#endif
|
||||
if (entry.m_value == dummy) {
|
||||
// new entry;
|
||||
m_manager.inc_ref(k);
|
||||
m_manager.inc_ref(v);
|
||||
entry.m_value = v;
|
||||
m_queue.push_back(k);
|
||||
m_unused++;
|
||||
DEBUG_CODE(expected_tag = 0;); // new entry
|
||||
}
|
||||
else if (UNTAG(expr*, entry.m_value) == v) {
|
||||
// already there
|
||||
DEBUG_CODE(expected_tag = GET_TAG(entry.m_value););
|
||||
}
|
||||
else {
|
||||
// replacing old entry
|
||||
m_manager.inc_ref(v);
|
||||
m_manager.dec_ref(UNTAG(expr*, entry.m_value));
|
||||
entry.m_value = v;
|
||||
SASSERT(GET_TAG(entry.m_value) == 0);
|
||||
// replaced old entry, and reset the tag.
|
||||
DEBUG_CODE(expected_tag = 0;);
|
||||
}
|
||||
DEBUG_CODE({
|
||||
expr * v2;
|
||||
SASSERT(m_table.find(k, v2));
|
||||
SASSERT(v == UNTAG(expr*, v2));
|
||||
SASSERT(expected_tag == GET_TAG(v2));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Search for key k in the cache.
|
||||
If entry k -> (v, tag) is found, we set tag to 1.
|
||||
*/
|
||||
expr * act_cache::find(expr * k) {
|
||||
map::key_value * entry = m_table.find_core(k);
|
||||
if (entry == 0)
|
||||
return 0;
|
||||
if (GET_TAG(entry->m_value) == 0) {
|
||||
entry->m_value = TAG(expr*, entry->m_value, 1);
|
||||
SASSERT(GET_TAG(entry->m_value) == 1);
|
||||
SASSERT(m_unused > 0);
|
||||
m_unused--;
|
||||
DEBUG_CODE({
|
||||
expr * v;
|
||||
SASSERT(m_table.find(k, v));
|
||||
SASSERT(GET_TAG(v) == 1);
|
||||
});
|
||||
}
|
||||
return UNTAG(expr*, entry->m_value);
|
||||
}
|
||||
|
||||
void act_cache::reset() {
|
||||
dec_refs();
|
||||
m_table.reset();
|
||||
m_queue.reset();
|
||||
m_unused = 0;
|
||||
m_qhead = 0;
|
||||
}
|
||||
|
||||
void act_cache::cleanup() {
|
||||
dec_refs();
|
||||
m_table.finalize();
|
||||
m_queue.finalize();
|
||||
m_unused = 0;
|
||||
m_qhead = 0;
|
||||
}
|
||||
|
||||
bool act_cache::check_invariant() const {
|
||||
return true;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue