mirror of
https://github.com/Z3Prover/z3
synced 2025-05-09 00:35:47 +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
342
lib/dl_instruction.h
Normal file
342
lib/dl_instruction.h
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dl_instruction.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2010-09-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _DL_INSTRUCTION_H_
|
||||
#define _DL_INSTRUCTION_H_
|
||||
|
||||
#include<iostream>
|
||||
#include<string>
|
||||
#include<utility>
|
||||
#include "ast.h"
|
||||
#include "vector.h"
|
||||
#include "dl_base.h"
|
||||
#include "dl_costs.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
class execution_context;
|
||||
class instruction_block;
|
||||
|
||||
inline void check_overflow(unsigned i) {
|
||||
if (i == UINT_MAX) {
|
||||
throw out_of_memory_error();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// execution_context
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class execution_context {
|
||||
public:
|
||||
typedef relation_base * reg_type;
|
||||
typedef vector<reg_type> reg_vector;
|
||||
typedef unsigned reg_idx;
|
||||
|
||||
/**
|
||||
\brief A register number that should never be referenced to. Can stand e.g. for a tail
|
||||
table in a rule with no tail.
|
||||
*/
|
||||
static const reg_idx void_register = UINT_MAX;
|
||||
private:
|
||||
typedef u_map<std::string> reg_annotations;
|
||||
|
||||
context & m_datalog_context;
|
||||
reg_vector m_registers;
|
||||
|
||||
reg_annotations m_reg_annotation;
|
||||
stopwatch * m_stopwatch;
|
||||
unsigned m_timelimit_ms; //zero means no limit
|
||||
/**
|
||||
\brief If true, after every operation that may result in an empty relation, a check
|
||||
for emptiness will be performed, and if a relation is empty, it will be deleted
|
||||
and replaced by zero. This allows us to avoid performing operations that would have
|
||||
no effect due to relation emptiness, but if the check for emptiness is expensive, its
|
||||
cost may overcome the gains.
|
||||
*/
|
||||
bool m_eager_emptiness_checking;
|
||||
public:
|
||||
execution_context(context & datalog_context);
|
||||
~execution_context();
|
||||
|
||||
void reset();
|
||||
|
||||
context & get_datalog_context() { return m_datalog_context; };
|
||||
|
||||
void set_timelimit(unsigned time_in_ms);
|
||||
void reset_timelimit();
|
||||
bool should_terminate();
|
||||
|
||||
bool eager_emptiness_checking() const { return m_eager_emptiness_checking; }
|
||||
|
||||
/**
|
||||
\brief Return reference to \c i -th register that contains pointer to a relation.
|
||||
|
||||
If register contains zero, it should be treated as if it contains an empty relation.
|
||||
*/
|
||||
reg_type reg(reg_idx i) {
|
||||
if (i>=m_registers.size()) {
|
||||
check_overflow(i);
|
||||
m_registers.resize(i+1,0);
|
||||
}
|
||||
return m_registers[i];
|
||||
}
|
||||
/**
|
||||
\brief Return value of the register and assign zero into it place.
|
||||
*/
|
||||
reg_type release_reg(reg_idx i) {
|
||||
SASSERT(i<m_registers.size());
|
||||
SASSERT(m_registers[i]);
|
||||
reg_type res = m_registers[i];
|
||||
m_registers[i] = 0;
|
||||
return res;
|
||||
}
|
||||
/**
|
||||
\brief Assign value to a register. If it was non-empty, deallocate its content first.
|
||||
*/
|
||||
void set_reg(reg_idx i, reg_type val) {
|
||||
if(i>=m_registers.size()) {
|
||||
check_overflow(i);
|
||||
m_registers.resize(i+1,0);
|
||||
}
|
||||
if(m_registers[i]) {
|
||||
m_registers[i]->deallocate();
|
||||
}
|
||||
m_registers[i]=val;
|
||||
}
|
||||
void make_empty(reg_idx i) {
|
||||
if(reg(i)) {
|
||||
set_reg(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned register_count() const {
|
||||
return m_registers.size();
|
||||
}
|
||||
bool get_register_annotation(reg_idx reg, std::string & res) const {
|
||||
return m_reg_annotation.find(reg, res);
|
||||
}
|
||||
void set_register_annotation(reg_idx reg, std::string str) {
|
||||
m_reg_annotation.insert(reg, str);
|
||||
}
|
||||
|
||||
void report_big_relations(unsigned threshold, std::ostream & out);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// instruction
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
|
||||
/**
|
||||
\brief Base class for instructions used in datalog saturation.
|
||||
|
||||
A relation in a register is owned by that register and is not referenced from anywhere else.
|
||||
|
||||
Instructions that move context of one register to another leave the source register empty
|
||||
and deallocate the previous content of the target register.
|
||||
*/
|
||||
class instruction : public accounted_object {
|
||||
typedef u_map<base_relation_fn *> fn_cache;
|
||||
|
||||
fn_cache m_fn_cache;
|
||||
|
||||
|
||||
static const int rk_encode_base = 1024;
|
||||
|
||||
inline static unsigned encode_kind(family_id k)
|
||||
{ SASSERT(k<rk_encode_base); return k; }
|
||||
inline static unsigned encode_kinds(family_id k1, family_id k2)
|
||||
{ SASSERT(k1<rk_encode_base && k2<rk_encode_base); return (k1+1)*rk_encode_base + k2; }
|
||||
inline static unsigned encode_kinds(family_id k1, family_id k2, family_id k3) {
|
||||
SASSERT(k1<rk_encode_base && k2<rk_encode_base && k3<rk_encode_base);
|
||||
return ((k1+1)*rk_encode_base + k2)*rk_encode_base + k3;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend class instruction_block;
|
||||
|
||||
template<typename T>
|
||||
bool find_fn(const relation_base & r, T* & result) const
|
||||
{ return m_fn_cache.find(encode_kind(r.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
|
||||
|
||||
template<typename T>
|
||||
bool find_fn(const relation_base & r1, const relation_base & r2, T*& result) const
|
||||
{ return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
|
||||
|
||||
template<typename T>
|
||||
bool find_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3, T * & result) const
|
||||
{ return m_fn_cache.find(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), reinterpret_cast<base_relation_fn*&>(result)); }
|
||||
|
||||
void store_fn(const relation_base & r, base_relation_fn * fn)
|
||||
{ m_fn_cache.insert(encode_kind(r.get_kind()), fn); }
|
||||
void store_fn(const relation_base & r1, const relation_base & r2, base_relation_fn * fn)
|
||||
{ m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind()), fn); }
|
||||
void store_fn(const relation_base & r1, const relation_base & r2, const relation_base & r3,
|
||||
base_relation_fn * fn)
|
||||
{ m_fn_cache.insert(encode_kinds(r1.get_kind(), r2.get_kind(), r3.get_kind()), fn); }
|
||||
|
||||
/**
|
||||
Process not only costs associated with the current instruction, but in case of
|
||||
block instructions, process also costs associated with its child instructions.
|
||||
*/
|
||||
virtual void process_all_costs();
|
||||
|
||||
/**
|
||||
\brief Output one line header of the current instruction.
|
||||
|
||||
The newline character at the end should not be printed.
|
||||
*/
|
||||
virtual void display_head_impl(context & ctx, std::ostream & out) const {
|
||||
out << "<instruction>";
|
||||
}
|
||||
/**
|
||||
\brief If relevant, output the body of the current instruction.
|
||||
|
||||
Each line must be prepended by \c indentation and ended by a newline character.
|
||||
*/
|
||||
virtual void display_body_impl(context & ctx, std::ostream & out, std::string indentation) const {}
|
||||
public:
|
||||
typedef execution_context::reg_type reg_type;
|
||||
typedef execution_context::reg_idx reg_idx;
|
||||
|
||||
virtual ~instruction();
|
||||
|
||||
virtual bool perform(execution_context & ctx) = 0;
|
||||
|
||||
virtual void make_annotations(execution_context & ctx) = 0;
|
||||
|
||||
void display(context & ctx, std::ostream & out) const {
|
||||
display_indented(ctx, out, "");
|
||||
}
|
||||
void display_indented(context & ctx, std::ostream & out, std::string indentation) const;
|
||||
|
||||
static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt);
|
||||
/**
|
||||
\brief The store operation moves the relation from a register into the context. The register
|
||||
is set to zero after the operation.
|
||||
*/
|
||||
static instruction * mk_store(ast_manager & m, func_decl * pred, reg_idx src);
|
||||
static instruction * mk_dealloc(reg_idx reg); //maybe not necessary
|
||||
static instruction * mk_clone(reg_idx from, reg_idx to);
|
||||
static instruction * mk_move(reg_idx from, reg_idx to);
|
||||
|
||||
/**
|
||||
\brief Return instruction that performs \c body as long as at least one register
|
||||
in \c control_regs contains non-empty relation.
|
||||
|
||||
The instruction object takes over the ownership of the \c body object.
|
||||
*/
|
||||
static instruction * mk_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs,
|
||||
instruction_block * body);
|
||||
|
||||
static instruction * mk_join(reg_idx rel1, reg_idx rel2, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2, reg_idx result);
|
||||
static instruction * mk_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col);
|
||||
static instruction * mk_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols);
|
||||
static instruction * mk_filter_interpreted(reg_idx reg, app_ref & condition);
|
||||
static instruction * mk_union(reg_idx src, reg_idx tgt, reg_idx delta);
|
||||
static instruction * mk_widen(reg_idx src, reg_idx tgt, reg_idx delta);
|
||||
static instruction * mk_projection(reg_idx src, unsigned col_cnt, const unsigned * removed_cols,
|
||||
reg_idx tgt);
|
||||
static instruction * mk_join_project(reg_idx rel1, reg_idx rel2, unsigned joined_col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt,
|
||||
const unsigned * removed_cols, reg_idx result);
|
||||
static instruction * mk_rename(reg_idx src, unsigned cycle_len, const unsigned * permutation_cycle,
|
||||
reg_idx tgt);
|
||||
static instruction * mk_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt,
|
||||
const unsigned * cols1, const unsigned * cols2);
|
||||
static instruction * mk_select_equal_and_project(ast_manager & m, reg_idx src,
|
||||
const relation_element & value, unsigned col, reg_idx result);
|
||||
|
||||
static instruction * mk_unary_singleton(ast_manager & m, func_decl* pred, const relation_sort & s, const relation_element & val, reg_idx tgt);
|
||||
static instruction * mk_total(const relation_signature & sig, func_decl* pred, reg_idx tgt);
|
||||
|
||||
/**
|
||||
\brief The mark_saturated instruction marks a relation as saturated, so that after
|
||||
next restart it does not have to be part of the saturation again.
|
||||
*/
|
||||
static instruction * mk_mark_saturated(ast_manager & m, func_decl * pred);
|
||||
|
||||
static instruction * mk_assert_signature(const relation_signature & s, reg_idx tgt);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// instruction_block
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
class instruction_block {
|
||||
public:
|
||||
struct instruction_observer {
|
||||
virtual ~instruction_observer() {}
|
||||
virtual void notify(instruction * i) {}
|
||||
};
|
||||
private:
|
||||
typedef ptr_vector<instruction> instr_seq_type;
|
||||
instr_seq_type m_data;
|
||||
instruction_observer* m_observer;
|
||||
public:
|
||||
instruction_block() : m_observer(0) {}
|
||||
~instruction_block();
|
||||
void reset();
|
||||
|
||||
void push_back(instruction * i) {
|
||||
m_data.push_back(i);
|
||||
if(m_observer) {
|
||||
m_observer->notify(i);
|
||||
}
|
||||
}
|
||||
void set_observer(instruction_observer * o) {
|
||||
SASSERT(o==0 || m_observer==0);
|
||||
m_observer = o;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Perform instructions in the block. If the run was interrupted before completion,
|
||||
return false; otherwise return true.
|
||||
|
||||
The execution can terminate before completion if the function
|
||||
\c execution_context::should_terminate() returns true.
|
||||
*/
|
||||
bool perform(execution_context & ctx) const;
|
||||
|
||||
void process_all_costs();
|
||||
|
||||
void make_annotations(execution_context & ctx);
|
||||
|
||||
void display(context & ctx, std::ostream & out) const {
|
||||
display_indented(ctx, out, "");
|
||||
}
|
||||
void display_indented(context & ctx, std::ostream & out, std::string indentation) const;
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* _DL_INSTRUCTION_H_ */
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue