mirror of
https://github.com/Z3Prover/z3
synced 2025-04-23 17:15:31 +00:00
Shared features from polysat branch (#6567)
* Allow setting default debug action * Fix dlist and add iterator * Add var_queue iterator * Add some helpers * rational: machine_div2k and pseudo_inverse * Basic support for non-copyable types in map * tbv helpers * pdd updates * Remove duplicate functions gcc doesn't like having both versions
This commit is contained in:
parent
be44ace995
commit
d69155b9e9
13 changed files with 456 additions and 62 deletions
|
@ -75,32 +75,62 @@ bool is_debug_enabled(const char * tag) {
|
|||
return g_enabled_debug_tags->contains(tag);
|
||||
}
|
||||
|
||||
atomic<debug_action> g_default_debug_action(debug_action::ask);
|
||||
|
||||
debug_action get_default_debug_action() {
|
||||
return g_default_debug_action;
|
||||
}
|
||||
|
||||
void set_default_debug_action(debug_action a) {
|
||||
g_default_debug_action = a;
|
||||
}
|
||||
|
||||
debug_action ask_debug_action(std::istream& in) {
|
||||
std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n";
|
||||
char result;
|
||||
bool ok = bool(in >> result);
|
||||
if (!ok)
|
||||
exit(ERR_INTERNAL_FATAL); // happens if std::cin is eof or unattached.
|
||||
switch(result) {
|
||||
case 'C':
|
||||
case 'c':
|
||||
return debug_action::cont;
|
||||
case 'A':
|
||||
case 'a':
|
||||
return debug_action::abort;
|
||||
case 'S':
|
||||
case 's':
|
||||
return debug_action::stop;
|
||||
case 't':
|
||||
case 'T':
|
||||
return debug_action::throw_exception;
|
||||
case 'G':
|
||||
case 'g':
|
||||
return debug_action::invoke_debugger;
|
||||
default:
|
||||
std::cerr << "INVALID COMMAND\n";
|
||||
return debug_action::ask;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(_WINDOWS) && !defined(NO_Z3_DEBUGGER)
|
||||
void invoke_gdb() {
|
||||
std::string buffer;
|
||||
int * x = nullptr;
|
||||
int *x = nullptr;
|
||||
debug_action a = get_default_debug_action();
|
||||
for (;;) {
|
||||
std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n";
|
||||
char result;
|
||||
bool ok = bool(std::cin >> result);
|
||||
if (!ok) exit(ERR_INTERNAL_FATAL); // happens if std::cin is eof or unattached.
|
||||
switch(result) {
|
||||
case 'C':
|
||||
case 'c':
|
||||
switch (a) {
|
||||
case debug_action::cont:
|
||||
return;
|
||||
case 'A':
|
||||
case 'a':
|
||||
case debug_action::abort:
|
||||
exit(1);
|
||||
case 'S':
|
||||
case 's':
|
||||
case debug_action::stop:
|
||||
// force seg fault...
|
||||
*x = 0;
|
||||
return;
|
||||
case 't':
|
||||
case 'T':
|
||||
case debug_action::throw_exception:
|
||||
throw default_exception("assertion violation");
|
||||
case 'G':
|
||||
case 'g':
|
||||
case debug_action::invoke_debugger:
|
||||
buffer = "gdb -nw /proc/" + std::to_string(getpid()) + "/exe " + std::to_string(getpid());
|
||||
std::cerr << "invoking GDB...\n";
|
||||
if (system(buffer.c_str()) == 0) {
|
||||
|
@ -109,12 +139,13 @@ void invoke_gdb() {
|
|||
else {
|
||||
std::cerr << "error starting GDB...\n";
|
||||
// forcing seg fault.
|
||||
int * x = nullptr;
|
||||
int *x = nullptr;
|
||||
*x = 0;
|
||||
}
|
||||
return;
|
||||
case debug_action::ask:
|
||||
default:
|
||||
std::cerr << "INVALID COMMAND\n";
|
||||
a = ask_debug_action(std::cin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,22 @@ Revision History:
|
|||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
|
||||
void enable_assertions(bool f);
|
||||
bool assertions_enabled();
|
||||
|
||||
enum class debug_action {
|
||||
ask,
|
||||
cont,
|
||||
abort,
|
||||
stop,
|
||||
throw_exception,
|
||||
invoke_debugger,
|
||||
};
|
||||
debug_action get_default_debug_action();
|
||||
void set_default_debug_action(debug_action a);
|
||||
|
||||
#include "util/error_codes.h"
|
||||
#include "util/warning.h"
|
||||
|
||||
|
|
147
src/util/dlist.h
147
src/util/dlist.h
|
@ -17,20 +17,38 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include "util/debug.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#define DLIST_EXTRA_ASSERTIONS 0
|
||||
|
||||
template<typename T>
|
||||
template <typename T> class dll_iterator;
|
||||
|
||||
template <typename T>
|
||||
class dll_base {
|
||||
T* m_next { nullptr };
|
||||
T* m_prev { nullptr };
|
||||
T* m_next = nullptr;
|
||||
T* m_prev = nullptr;
|
||||
|
||||
protected:
|
||||
dll_base() = default;
|
||||
~dll_base() = default;
|
||||
|
||||
public:
|
||||
dll_base(dll_base const&) = delete;
|
||||
dll_base(dll_base&&) = delete;
|
||||
dll_base& operator=(dll_base const&) = delete;
|
||||
dll_base& operator=(dll_base&&) = delete;
|
||||
|
||||
T* prev() { return m_prev; }
|
||||
T* next() { return m_next; }
|
||||
T const* prev() const { return m_prev; }
|
||||
T const* next() const { return m_next; }
|
||||
|
||||
void init(T* t) {
|
||||
m_next = t;
|
||||
m_prev = t;
|
||||
SASSERT(invariant());
|
||||
}
|
||||
|
||||
static T* pop(T*& list) {
|
||||
|
@ -41,23 +59,63 @@ public:
|
|||
return head;
|
||||
}
|
||||
|
||||
void insert_after(T* elem) {
|
||||
void insert_after(T* other) {
|
||||
#if DLIST_EXTRA_ASSERTIONS
|
||||
SASSERT(other);
|
||||
SASSERT(invariant());
|
||||
SASSERT(other->invariant());
|
||||
size_t const old_sz1 = count_if(*static_cast<T*>(this), [](T const&) { return true; });
|
||||
size_t const old_sz2 = count_if(*other, [](T const&) { return true; });
|
||||
#endif
|
||||
// have: this -> next -> ...
|
||||
// insert: other -> ... -> other_end
|
||||
// result: this -> other -> ... -> other_end -> next -> ...
|
||||
T* next = this->m_next;
|
||||
elem->m_prev = next->m_prev;
|
||||
elem->m_next = next;
|
||||
this->m_next = elem;
|
||||
next->m_prev = elem;
|
||||
T* other_end = other->m_prev;
|
||||
this->m_next = other;
|
||||
other->m_prev = static_cast<T*>(this);
|
||||
other_end->m_next = next;
|
||||
next->m_prev = other_end;
|
||||
#if DLIST_EXTRA_ASSERTIONS
|
||||
SASSERT(invariant());
|
||||
SASSERT(other->invariant());
|
||||
size_t const new_sz = count_if(*static_cast<T*>(this), [](T const&) { return true; });
|
||||
SASSERT_EQ(new_sz, old_sz1 + old_sz2);
|
||||
#endif
|
||||
}
|
||||
|
||||
void insert_before(T* elem) {
|
||||
void insert_before(T* other) {
|
||||
#if DLIST_EXTRA_ASSERTIONS
|
||||
SASSERT(other);
|
||||
SASSERT(invariant());
|
||||
SASSERT(other->invariant());
|
||||
size_t const old_sz1 = count_if(*static_cast<T*>(this), [](T const&) { return true; });
|
||||
size_t const old_sz2 = count_if(*other, [](T const&) { return true; });
|
||||
#endif
|
||||
// have: prev -> this -> ...
|
||||
// insert: other -> ... -> other_end
|
||||
// result: prev -> other -> ... -> other_end -> this -> ...
|
||||
T* prev = this->m_prev;
|
||||
elem->m_next = prev->m_next;
|
||||
elem->m_prev = prev;
|
||||
prev->m_next = elem;
|
||||
this->m_prev = elem;
|
||||
T* other_end = other->m_prev;
|
||||
prev->m_next = other;
|
||||
other->m_prev = prev;
|
||||
other_end->m_next = static_cast<T*>(this);
|
||||
this->m_prev = other_end;
|
||||
#if DLIST_EXTRA_ASSERTIONS
|
||||
SASSERT(invariant());
|
||||
SASSERT(other->invariant());
|
||||
size_t const new_sz = count_if(*static_cast<T*>(this), [](T const&) { return true; });
|
||||
SASSERT_EQ(new_sz, old_sz1 + old_sz2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void remove_from(T*& list, T* elem) {
|
||||
#if DLIST_EXTRA_ASSERTIONS
|
||||
SASSERT(list);
|
||||
SASSERT(elem);
|
||||
SASSERT(list->invariant());
|
||||
SASSERT(elem->invariant());
|
||||
#endif
|
||||
if (list->m_next == list) {
|
||||
SASSERT(elem == list);
|
||||
list = nullptr;
|
||||
|
@ -69,6 +127,9 @@ public:
|
|||
auto* prev = elem->m_prev;
|
||||
prev->m_next = next;
|
||||
next->m_prev = prev;
|
||||
#if DLIST_EXTRA_ASSERTIONS
|
||||
SASSERT(list->invariant());
|
||||
#endif
|
||||
}
|
||||
|
||||
static void push_to_front(T*& list, T* elem) {
|
||||
|
@ -105,11 +166,10 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool contains(T* list, T* elem) {
|
||||
static bool contains(T const* list, T const* elem) {
|
||||
if (!list)
|
||||
return false;
|
||||
T* first = list;
|
||||
T const* first = list;
|
||||
do {
|
||||
if (list == elem)
|
||||
return true;
|
||||
|
@ -120,5 +180,60 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class dll_iterator {
|
||||
T const* m_elem;
|
||||
bool m_first;
|
||||
|
||||
dll_iterator(T const* elem, bool first): m_elem(elem), m_first(first) { }
|
||||
|
||||
public:
|
||||
static dll_iterator mk_begin(T const* elem) {
|
||||
// Setting first==(bool)elem makes this also work for elem==nullptr;
|
||||
// but we can't implement top-level begin/end for pointers because it clashes with the definition for arrays.
|
||||
return {elem, (bool)elem};
|
||||
}
|
||||
|
||||
static dll_iterator mk_end(T const* elem) {
|
||||
return {elem, false};
|
||||
}
|
||||
|
||||
using value_type = T;
|
||||
using pointer = T const*;
|
||||
using reference = T const&;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
dll_iterator& operator++() {
|
||||
m_elem = m_elem->next();
|
||||
m_first = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T const& operator*() const {
|
||||
return *m_elem;
|
||||
}
|
||||
|
||||
bool operator==(dll_iterator const& other) const {
|
||||
return m_elem == other.m_elem && m_first == other.m_first;
|
||||
}
|
||||
|
||||
bool operator!=(dll_iterator const& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T
|
||||
, typename U = std::enable_if_t<std::is_base_of_v<dll_base<T>, T>> // should only match if T actually inherits from dll_base<T>
|
||||
>
|
||||
dll_iterator<T> begin(T const& elem) {
|
||||
return dll_iterator<T>::mk_begin(&elem);
|
||||
}
|
||||
|
||||
template < typename T
|
||||
, typename U = std::enable_if_t<std::is_base_of_v<dll_base<T>, T>> // should only match if T actually inherits from dll_base<T>
|
||||
>
|
||||
dll_iterator<T> end(T const& elem)
|
||||
{
|
||||
return dll_iterator<T>::mk_end(&elem);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,10 @@ struct _key_data {
|
|||
m_key(k),
|
||||
m_value(v) {
|
||||
}
|
||||
_key_data(Key const& k, Value&& v):
|
||||
m_key(k),
|
||||
m_value(std::move(v)) {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Entry, typename HashProc, typename EqProc>
|
||||
|
@ -106,6 +110,10 @@ public:
|
|||
void insert(key const & k, value const & v) {
|
||||
m_table.insert(key_data(k, v));
|
||||
}
|
||||
|
||||
void insert(key const& k, value&& v) {
|
||||
m_table.insert(key_data(k, std::move(v)));
|
||||
}
|
||||
|
||||
bool insert_if_not_there_core(key const & k, value const & v, entry *& et) {
|
||||
return m_table.insert_if_not_there_core(key_data(k,v), et);
|
||||
|
|
|
@ -487,6 +487,8 @@ public:
|
|||
|
||||
void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz & d) { mpz_manager<SYNCH>::machine_div_rem(a, b, c, d); }
|
||||
|
||||
void machine_div2k(mpz const & a, unsigned k, mpz & c) { mpz_manager<SYNCH>::machine_div2k(a, k, c); }
|
||||
|
||||
void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::div(a, b, c); }
|
||||
|
||||
void rat_div(mpz const & a, mpz const & b, mpq & c) {
|
||||
|
@ -513,6 +515,12 @@ public:
|
|||
machine_div(a.m_num, b.m_num, c);
|
||||
}
|
||||
|
||||
void machine_idiv2k(mpq const & a, unsigned k, mpq & c) {
|
||||
SASSERT(is_int(a));
|
||||
machine_div2k(a.m_num, k, c.m_num);
|
||||
reset_denominator(c);
|
||||
}
|
||||
|
||||
void idiv(mpq const & a, mpq const & b, mpq & c) {
|
||||
SASSERT(is_int(a) && is_int(b));
|
||||
div(a.m_num, b.m_num, c.m_num);
|
||||
|
|
|
@ -153,3 +153,21 @@ bool rational::mult_inverse(unsigned num_bits, rational & result) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the smallest multiplicative pseudo-inverse modulo 2^num_bits:
|
||||
*
|
||||
* mod(n * n.pseudo_inverse(bits), 2^bits) == 2^k,
|
||||
* where k is maximal such that 2^k divides n.
|
||||
*
|
||||
* Precondition: number is non-zero.
|
||||
*/
|
||||
rational rational::pseudo_inverse(unsigned num_bits) const {
|
||||
rational result;
|
||||
rational const& n = *this;
|
||||
SASSERT(!n.is_zero()); // TODO: or we define pseudo-inverse of 0 as 0.
|
||||
unsigned const k = n.trailing_zeros();
|
||||
rational const odd = machine_div2k(n, k);
|
||||
VERIFY(odd.mult_inverse(num_bits - k, result));
|
||||
SASSERT_EQ(mod(n * result, rational::power_of_two(num_bits)), rational::power_of_two(k));
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ public:
|
|||
|
||||
explicit rational(char const * v) { m().set(m_val, v); }
|
||||
|
||||
explicit rational(unsigned const * v, unsigned sz) { m().set(m_val, sz, v); }
|
||||
|
||||
struct i64 {};
|
||||
rational(int64_t i, i64) { m().set(m_val, i); }
|
||||
|
||||
|
@ -227,6 +229,12 @@ public:
|
|||
return r;
|
||||
}
|
||||
|
||||
friend inline rational machine_div2k(rational const & r1, unsigned k) {
|
||||
rational r;
|
||||
rational::m().machine_idiv2k(r1.m_val, k, r.m_val);
|
||||
return r;
|
||||
}
|
||||
|
||||
friend inline rational mod(rational const & r1, rational const & r2) {
|
||||
rational r;
|
||||
rational::m().mod(r1.m_val, r2.m_val, r.m_val);
|
||||
|
@ -353,6 +361,7 @@ public:
|
|||
}
|
||||
|
||||
bool mult_inverse(unsigned num_bits, rational & result) const;
|
||||
rational pseudo_inverse(unsigned num_bits) const;
|
||||
|
||||
static rational const & zero() {
|
||||
return m_zero;
|
||||
|
|
|
@ -27,10 +27,10 @@ Revision History:
|
|||
class tbv;
|
||||
|
||||
enum tbit {
|
||||
BIT_z = 0x0,
|
||||
BIT_0 = 0x1,
|
||||
BIT_1 = 0x2,
|
||||
BIT_x = 0x3
|
||||
BIT_z = 0x0, // unknown
|
||||
BIT_0 = 0x1, // for sure 0
|
||||
BIT_1 = 0x2, // for sure 1
|
||||
BIT_x = 0x3 // don't care
|
||||
};
|
||||
|
||||
inline tbit neg(tbit t) {
|
||||
|
@ -43,6 +43,7 @@ class tbv_manager {
|
|||
ptr_vector<tbv> allocated_tbvs;
|
||||
public:
|
||||
tbv_manager(unsigned n): m(2*n) {}
|
||||
tbv_manager(tbv_manager const& m) = delete;
|
||||
~tbv_manager();
|
||||
void reset();
|
||||
tbv* allocate();
|
||||
|
@ -132,8 +133,9 @@ class tbv_ref {
|
|||
tbv_manager& mgr;
|
||||
tbv* d;
|
||||
public:
|
||||
tbv_ref(tbv_manager& mgr):mgr(mgr),d(nullptr) {}
|
||||
tbv_ref(tbv_manager& mgr, tbv* d):mgr(mgr),d(d) {}
|
||||
tbv_ref(tbv_manager& mgr) : mgr(mgr), d(nullptr) {}
|
||||
tbv_ref(tbv_manager& mgr, tbv* d) : mgr(mgr), d(d) {}
|
||||
tbv_ref(tbv_ref&& d) : mgr(d.mgr), d(d.detach()) {}
|
||||
~tbv_ref() {
|
||||
if (d) mgr.deallocate(d);
|
||||
}
|
||||
|
@ -144,8 +146,17 @@ public:
|
|||
}
|
||||
tbv& operator*() { return *d; }
|
||||
tbv* operator->() { return d; }
|
||||
tbv* get() { return d; }
|
||||
tbit operator[](unsigned idx) const { return (*d)[idx]; }
|
||||
tbv* get() const { return d; }
|
||||
tbv* detach() { tbv* result = d; d = nullptr; return result; }
|
||||
tbv_manager& manager() const { return mgr; }
|
||||
unsigned num_tbits() const { return mgr.num_tbits(); }
|
||||
};
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, tbv_ref const& c) {
|
||||
char const* names[] = { "z", "0", "1", "x" };
|
||||
for (unsigned i = c.num_tbits(); i-- > 0; ) {
|
||||
out << names[static_cast<unsigned>(c[i])];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -20,12 +20,14 @@ Revision History:
|
|||
|
||||
#include "util/debug.h"
|
||||
#include "util/memory_manager.h"
|
||||
#include<ostream>
|
||||
#include<climits>
|
||||
#include<limits>
|
||||
#include<stdint.h>
|
||||
#include <ostream>
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX std::numeric_limits<std::size_t>::max()
|
||||
|
@ -410,3 +412,36 @@ inline size_t megabytes_to_bytes(unsigned mb) {
|
|||
r = SIZE_MAX;
|
||||
return r;
|
||||
}
|
||||
|
||||
/** Compact version of std::count */
|
||||
template <typename Container, typename Item>
|
||||
std::size_t count(Container const& c, Item x)
|
||||
{
|
||||
using std::begin, std::end; // allows begin(c) to also find c.begin()
|
||||
return std::count(begin(c), end(c), std::forward<Item>(x));
|
||||
}
|
||||
|
||||
/** Compact version of std::count_if */
|
||||
template <typename Container, typename Predicate>
|
||||
std::size_t count_if(Container const& c, Predicate p)
|
||||
{
|
||||
using std::begin, std::end; // allows begin(c) to also find c.begin()
|
||||
return std::count_if(begin(c), end(c), std::forward<Predicate>(p));
|
||||
}
|
||||
|
||||
/** Basic version of https://en.cppreference.com/w/cpp/experimental/scope_exit */
|
||||
template <typename Callable>
|
||||
class on_scope_exit final {
|
||||
Callable m_ef;
|
||||
public:
|
||||
on_scope_exit(Callable&& ef)
|
||||
: m_ef(std::forward<Callable>(ef))
|
||||
{ }
|
||||
~on_scope_exit() {
|
||||
m_ef();
|
||||
}
|
||||
};
|
||||
|
||||
/** Helper type for std::visit, see examples on https://en.cppreference.com/w/cpp/utility/variant/visit */
|
||||
template <typename T>
|
||||
struct always_false : std::false_type {};
|
||||
|
|
|
@ -89,6 +89,10 @@ public:
|
|||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
using const_iterator = decltype(m_queue)::const_iterator;
|
||||
const_iterator begin() const { return m_queue.begin(); }
|
||||
const_iterator end() const { return m_queue.end(); }
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, var_queue const& queue) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue