z3-z3-4.13.0.src.muz.rel.dl_instruction.h Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
dl_instruction.h
Abstract:
Author:
Krystof Hoder (t-khoder) 2010-09-14.
Revision History:
--*/
#pragma once
#include
#include
#include
#include "ast/ast.h"
#include "util/vector.h"
#include "muz/rel/dl_base.h"
#include "muz/base/dl_costs.h"
#include "muz/base/dl_context.h"
namespace datalog {
class instruction_block;
class rel_context;
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_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 reg_annotations;
context & m_context;
reg_vector m_registers;
reg_annotations m_reg_annotation;
stopwatch * m_stopwatch;
unsigned m_timelimit_ms; //zero means no limit
public:
execution_context(context & context);
~execution_context();
void reset();
rel_context & get_rel_context();
rel_context const & get_rel_context() const;
void set_timelimit(unsigned time_in_ms);
void reset_timelimit();
bool should_terminate();
struct stats {
unsigned m_join;
unsigned m_project;
unsigned m_filter;
unsigned m_total;
unsigned m_unary_singleton;
unsigned m_filter_by_negation;
unsigned m_select_equal_project;
unsigned m_join_project;
unsigned m_project_rename;
unsigned m_union;
unsigned m_filter_interp_project;
unsigned m_filter_id;
unsigned m_filter_eq;
unsigned m_min;
stats() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
};
stats m_stats;
void collect_statistics(statistics& st) const;
/**
\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) const {
if (i >= m_registers.size()) {
return nullptr;
}
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);
}
if (m_registers[i]) {
m_registers[i]->deallocate();
}
m_registers[i] = val;
}
void make_empty(reg_idx i) {
if (reg(i)) {
set_reg(i, nullptr);
}
}
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, const std::string & str) {
m_reg_annotation.insert(reg, str);
}
void report_big_relations(unsigned threshold, std::ostream & out) const;
};
// -----------------------------------
//
// 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 fn_cache;
fn_cache m_fn_cache;
static const int rk_encode_base = 1024;
inline static unsigned encode_kind(family_id k)
{ SASSERT(k
bool find_fn(const relation_base & r, T* & result) const
{ return m_fn_cache.find(encode_kind(r.get_kind()), reinterpret_cast(result)); }
template
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(result)); }
template
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(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 std::ostream& display_head_impl(execution_context const & ctx, std::ostream & out) const {
return out << "";
}
/**
\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(execution_context const & ctx, std::ostream & out, const std::string & indentation) const {}
void log_verbose(execution_context& ctx);
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(execution_context const& ctx, std::ostream & out) const {
display_indented(ctx, out, "");
}
void display_indented(execution_context const & ctx, std::ostream & out, const 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_filter_interpreted_and_project(reg_idx src, app_ref & condition,
unsigned col_cnt, const unsigned * removed_cols, reg_idx result);
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_min(reg_idx source, reg_idx target, const unsigned_vector & group_by_cols,
unsigned min_col);
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);
void collect_statistics(statistics& st) const;
};
// -----------------------------------
//
// instruction_block
//
// -----------------------------------
class instruction_block {
public:
struct instruction_observer {
virtual ~instruction_observer() = default;
virtual void notify(instruction * i) {}
};
private:
typedef ptr_vector instr_seq_type;
instr_seq_type m_data;
instruction_observer* m_observer;
public:
instruction_block() : m_observer(nullptr) {}
~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;
}
void collect_statistics(statistics& st) const;
/**
\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(execution_context const & ctx, std::ostream & out) const {
display_indented(ctx, out, "");
}
void display_indented(execution_context const & ctx, std::ostream & out, const std::string & indentation) const;
unsigned num_instructions() const { return m_data.size(); }
};
};