hashlib: remove is_new from HasherDJB32, implement hash_top for IdString

This commit is contained in:
Emil J. Tywoniak 2024-11-06 12:58:04 +01:00
parent 8513021e0b
commit 60c1e41557
2 changed files with 314 additions and 313 deletions

View File

@ -48,14 +48,9 @@ namespace hashlib {
* instead of pointers. * instead of pointers.
*/ */
// TODO describe how comparison hashes are special
// TODO draw the line between generic and hash function specific code
const int hashtable_size_trigger = 2; const int hashtable_size_trigger = 2;
const int hashtable_size_factor = 3; const int hashtable_size_factor = 3;
#define DJB2_32
namespace legacy { namespace legacy {
inline uint32_t mkhash_add(uint32_t a, uint32_t b) { inline uint32_t mkhash_add(uint32_t a, uint32_t b) {
return ((a << 5) + a) + b; return ((a << 5) + a) + b;
@ -89,16 +84,11 @@ inline unsigned int mkhash_xorshift(unsigned int a) {
return a; return a;
} }
class Hasher { class HasherDJB32 {
public: public:
#ifdef DJB2_32
using hash_t = uint32_t; using hash_t = uint32_t;
#endif
#ifdef DJB2_64
using hash_t = uint64_t;
#endif
Hasher() { HasherDJB32() {
// traditionally 5381 is used as starting value for the djb2 hash // traditionally 5381 is used as starting value for the djb2 hash
state = 5381; state = 5381;
} }
@ -106,7 +96,7 @@ class Hasher {
fudge = f; fudge = f;
} }
private: private:
uint32_t state; uint32_t state;
static uint32_t fudge; static uint32_t fudge;
// The XOR version of DJB2 // The XOR version of DJB2
@ -142,19 +132,17 @@ class Hasher {
*this = hash_ops<T>::hash_acc(t, *this); *this = hash_ops<T>::hash_acc(t, *this);
} }
void commutative_acc(uint32_t t) { void commutative_acc(hash_t t) {
state ^= t; state ^= t;
} }
void force(hash_t new_state) { void force(hash_t new_state) {
state = new_state; state = new_state;
} }
bool is_new() const {
return state == Hasher().state;
}
}; };
using Hasher = HasherDJB32;
template<typename T> template<typename T>
struct hash_top_ops { struct hash_top_ops {
static inline bool cmp(const T &a, const T &b) { static inline bool cmp(const T &a, const T &b) {

View File

@ -76,335 +76,348 @@ namespace RTLIL
struct SyncRule; struct SyncRule;
struct Process; struct Process;
struct Binding; struct Binding;
struct IdString;
typedef std::pair<SigSpec, SigSpec> SigSig; typedef std::pair<SigSpec, SigSpec> SigSig;
};
struct IdString struct RTLIL::IdString
{
#undef YOSYS_XTRACE_GET_PUT
#undef YOSYS_SORT_ID_FREE_LIST
#undef YOSYS_USE_STICKY_IDS
#undef YOSYS_NO_IDS_REFCNT
// the global id string cache
static bool destruct_guard_ok; // POD, will be initialized to zero
static struct destruct_guard_t {
destruct_guard_t() { destruct_guard_ok = true; }
~destruct_guard_t() { destruct_guard_ok = false; }
} destruct_guard;
static std::vector<char*> global_id_storage_;
static dict<char*, int> global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
static std::vector<int> global_refcount_storage_;
static std::vector<int> global_free_idx_list_;
#endif
#ifdef YOSYS_USE_STICKY_IDS
static int last_created_idx_ptr_;
static int last_created_idx_[8];
#endif
static inline void xtrace_db_dump()
{ {
#undef YOSYS_XTRACE_GET_PUT #ifdef YOSYS_XTRACE_GET_PUT
#undef YOSYS_SORT_ID_FREE_LIST for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
#undef YOSYS_USE_STICKY_IDS {
#undef YOSYS_NO_IDS_REFCNT if (global_id_storage_.at(idx) == nullptr)
log("#X# DB-DUMP index %d: FREE\n", idx);
// the global id string cache else
log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
static bool destruct_guard_ok; // POD, will be initialized to zero }
static struct destruct_guard_t {
destruct_guard_t() { destruct_guard_ok = true; }
~destruct_guard_t() { destruct_guard_ok = false; }
} destruct_guard;
static std::vector<char*> global_id_storage_;
static dict<char*, int> global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
static std::vector<int> global_refcount_storage_;
static std::vector<int> global_free_idx_list_;
#endif #endif
}
static inline void checkpoint()
{
#ifdef YOSYS_USE_STICKY_IDS #ifdef YOSYS_USE_STICKY_IDS
static int last_created_idx_ptr_; last_created_idx_ptr_ = 0;
static int last_created_idx_[8]; for (int i = 0; i < 8; i++) {
if (last_created_idx_[i])
put_reference(last_created_idx_[i]);
last_created_idx_[i] = 0;
}
#endif #endif
#ifdef YOSYS_SORT_ID_FREE_LIST
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
#endif
}
static inline void xtrace_db_dump() static inline int get_reference(int idx)
{ {
#ifdef YOSYS_XTRACE_GET_PUT if (idx) {
for (int idx = 0; idx < GetSize(global_id_storage_); idx++) #ifndef YOSYS_NO_IDS_REFCNT
{ global_refcount_storage_[idx]++;
if (global_id_storage_.at(idx) == nullptr) #endif
log("#X# DB-DUMP index %d: FREE\n", idx); #ifdef YOSYS_XTRACE_GET_PUT
else if (yosys_xtrace)
log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx)); log("#X# GET-BY-INDEX '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
} #endif
#endif }
return idx;
}
static int get_reference(const char *p)
{
log_assert(destruct_guard_ok);
if (!p[0])
return 0;
auto it = global_id_index_.find((char*)p);
if (it != global_id_index_.end()) {
#ifndef YOSYS_NO_IDS_REFCNT
global_refcount_storage_.at(it->second)++;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second));
#endif
return it->second;
} }
static inline void checkpoint() log_assert(p[0] == '$' || p[0] == '\\');
{ log_assert(p[1] != 0);
#ifdef YOSYS_USE_STICKY_IDS for (const char *c = p; *c; c++)
last_created_idx_ptr_ = 0; if ((unsigned)*c <= (unsigned)' ')
for (int i = 0; i < 8; i++) { log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p);
if (last_created_idx_[i])
put_reference(last_created_idx_[i]);
last_created_idx_[i] = 0;
}
#endif
#ifdef YOSYS_SORT_ID_FREE_LIST
std::sort(global_free_idx_list_.begin(), global_free_idx_list_.end(), std::greater<int>());
#endif
}
static inline int get_reference(int idx) #ifndef YOSYS_NO_IDS_REFCNT
{ if (global_free_idx_list_.empty()) {
if (idx) {
#ifndef YOSYS_NO_IDS_REFCNT
global_refcount_storage_[idx]++;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-INDEX '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
#endif
}
return idx;
}
static int get_reference(const char *p)
{
log_assert(destruct_guard_ok);
if (!p[0])
return 0;
auto it = global_id_index_.find((char*)p);
if (it != global_id_index_.end()) {
#ifndef YOSYS_NO_IDS_REFCNT
global_refcount_storage_.at(it->second)++;
#endif
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(it->second), it->second, global_refcount_storage_.at(it->second));
#endif
return it->second;
}
log_assert(p[0] == '$' || p[0] == '\\');
log_assert(p[1] != 0);
for (const char *c = p; *c; c++)
if ((unsigned)*c <= (unsigned)' ')
log_error("Found control character or space (0x%02x) in string '%s' which is not allowed in RTLIL identifiers\n", *c, p);
#ifndef YOSYS_NO_IDS_REFCNT
if (global_free_idx_list_.empty()) {
if (global_id_storage_.empty()) {
global_refcount_storage_.push_back(0);
global_id_storage_.push_back((char*)"");
global_id_index_[global_id_storage_.back()] = 0;
}
log_assert(global_id_storage_.size() < 0x40000000);
global_free_idx_list_.push_back(global_id_storage_.size());
global_id_storage_.push_back(nullptr);
global_refcount_storage_.push_back(0);
}
int idx = global_free_idx_list_.back();
global_free_idx_list_.pop_back();
global_id_storage_.at(idx) = strdup(p);
global_id_index_[global_id_storage_.at(idx)] = idx;
global_refcount_storage_.at(idx)++;
#else
if (global_id_storage_.empty()) { if (global_id_storage_.empty()) {
global_refcount_storage_.push_back(0);
global_id_storage_.push_back((char*)""); global_id_storage_.push_back((char*)"");
global_id_index_[global_id_storage_.back()] = 0; global_id_index_[global_id_storage_.back()] = 0;
} }
int idx = global_id_storage_.size(); log_assert(global_id_storage_.size() < 0x40000000);
global_id_storage_.push_back(strdup(p)); global_free_idx_list_.push_back(global_id_storage_.size());
global_id_index_[global_id_storage_.back()] = idx; global_id_storage_.push_back(nullptr);
#endif global_refcount_storage_.push_back(0);
if (yosys_xtrace) {
log("#X# New IdString '%s' with index %d.\n", p, idx);
log_backtrace("-X- ", yosys_xtrace-1);
}
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace)
log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
#endif
#ifdef YOSYS_USE_STICKY_IDS
// Avoid Create->Delete->Create pattern
if (last_created_idx_[last_created_idx_ptr_])
put_reference(last_created_idx_[last_created_idx_ptr_]);
last_created_idx_[last_created_idx_ptr_] = idx;
get_reference(last_created_idx_[last_created_idx_ptr_]);
last_created_idx_ptr_ = (last_created_idx_ptr_ + 1) & 7;
#endif
return idx;
} }
#ifndef YOSYS_NO_IDS_REFCNT int idx = global_free_idx_list_.back();
static inline void put_reference(int idx) global_free_idx_list_.pop_back();
{ global_id_storage_.at(idx) = strdup(p);
// put_reference() may be called from destructors after the destructor of global_id_index_[global_id_storage_.at(idx)] = idx;
// global_refcount_storage_ has been run. in this case we simply do nothing. global_refcount_storage_.at(idx)++;
if (!destruct_guard_ok || !idx)
return;
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace) {
log("#X# PUT '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
}
#endif
int &refcount = global_refcount_storage_[idx];
if (--refcount > 0)
return;
log_assert(refcount == 0);
free_reference(idx);
}
static inline void free_reference(int idx)
{
if (yosys_xtrace) {
log("#X# Removed IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx);
log_backtrace("-X- ", yosys_xtrace-1);
}
global_id_index_.erase(global_id_storage_.at(idx));
free(global_id_storage_.at(idx));
global_id_storage_.at(idx) = nullptr;
global_free_idx_list_.push_back(idx);
}
#else #else
static inline void put_reference(int) { } if (global_id_storage_.empty()) {
global_id_storage_.push_back((char*)"");
global_id_index_[global_id_storage_.back()] = 0;
}
int idx = global_id_storage_.size();
global_id_storage_.push_back(strdup(p));
global_id_index_[global_id_storage_.back()] = idx;
#endif #endif
// the actual IdString object is just is a single int if (yosys_xtrace) {
log("#X# New IdString '%s' with index %d.\n", p, idx);
int index_; log_backtrace("-X- ", yosys_xtrace-1);
inline IdString() : index_(0) { }
inline IdString(const char *str) : index_(get_reference(str)) { }
inline IdString(const IdString &str) : index_(get_reference(str.index_)) { }
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
inline IdString(const std::string &str) : index_(get_reference(str.c_str())) { }
inline ~IdString() { put_reference(index_); }
inline void operator=(const IdString &rhs) {
put_reference(index_);
index_ = get_reference(rhs.index_);
} }
inline void operator=(const char *rhs) { #ifdef YOSYS_XTRACE_GET_PUT
IdString id(rhs); if (yosys_xtrace)
*this = id; log("#X# GET-BY-NAME '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
#endif
#ifdef YOSYS_USE_STICKY_IDS
// Avoid Create->Delete->Create pattern
if (last_created_idx_[last_created_idx_ptr_])
put_reference(last_created_idx_[last_created_idx_ptr_]);
last_created_idx_[last_created_idx_ptr_] = idx;
get_reference(last_created_idx_[last_created_idx_ptr_]);
last_created_idx_ptr_ = (last_created_idx_ptr_ + 1) & 7;
#endif
return idx;
}
#ifndef YOSYS_NO_IDS_REFCNT
static inline void put_reference(int idx)
{
// put_reference() may be called from destructors after the destructor of
// global_refcount_storage_ has been run. in this case we simply do nothing.
if (!destruct_guard_ok || !idx)
return;
#ifdef YOSYS_XTRACE_GET_PUT
if (yosys_xtrace) {
log("#X# PUT '%s' (index %d, refcount %d)\n", global_id_storage_.at(idx), idx, global_refcount_storage_.at(idx));
}
#endif
int &refcount = global_refcount_storage_[idx];
if (--refcount > 0)
return;
log_assert(refcount == 0);
free_reference(idx);
}
static inline void free_reference(int idx)
{
if (yosys_xtrace) {
log("#X# Removed IdString '%s' with index %d.\n", global_id_storage_.at(idx), idx);
log_backtrace("-X- ", yosys_xtrace-1);
} }
inline void operator=(const std::string &rhs) { global_id_index_.erase(global_id_storage_.at(idx));
IdString id(rhs); free(global_id_storage_.at(idx));
*this = id; global_id_storage_.at(idx) = nullptr;
} global_free_idx_list_.push_back(idx);
}
inline const char *c_str() const {
return global_id_storage_.at(index_);
}
inline std::string str() const {
return std::string(global_id_storage_.at(index_));
}
inline bool operator<(const IdString &rhs) const {
return index_ < rhs.index_;
}
inline bool operator==(const IdString &rhs) const { return index_ == rhs.index_; }
inline bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; }
// The methods below are just convenience functions for better compatibility with std::string.
bool operator==(const std::string &rhs) const { return c_str() == rhs; }
bool operator!=(const std::string &rhs) const { return c_str() != rhs; }
bool operator==(const char *rhs) const { return strcmp(c_str(), rhs) == 0; }
bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
char operator[](size_t i) const {
const char *p = c_str();
#ifndef NDEBUG
for (; i != 0; i--, p++)
log_assert(*p != 0);
return *p;
#else #else
return *(p + i); static inline void put_reference(int) { }
#endif #endif
// the actual IdString object is just is a single int
int index_;
inline IdString() : index_(0) { }
inline IdString(const char *str) : index_(get_reference(str)) { }
inline IdString(const IdString &str) : index_(get_reference(str.index_)) { }
inline IdString(IdString &&str) : index_(str.index_) { str.index_ = 0; }
inline IdString(const std::string &str) : index_(get_reference(str.c_str())) { }
inline ~IdString() { put_reference(index_); }
inline void operator=(const IdString &rhs) {
put_reference(index_);
index_ = get_reference(rhs.index_);
}
inline void operator=(const char *rhs) {
IdString id(rhs);
*this = id;
}
inline void operator=(const std::string &rhs) {
IdString id(rhs);
*this = id;
}
inline const char *c_str() const {
return global_id_storage_.at(index_);
}
inline std::string str() const {
return std::string(global_id_storage_.at(index_));
}
inline bool operator<(const IdString &rhs) const {
return index_ < rhs.index_;
}
inline bool operator==(const IdString &rhs) const { return index_ == rhs.index_; }
inline bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; }
// The methods below are just convenience functions for better compatibility with std::string.
bool operator==(const std::string &rhs) const { return c_str() == rhs; }
bool operator!=(const std::string &rhs) const { return c_str() != rhs; }
bool operator==(const char *rhs) const { return strcmp(c_str(), rhs) == 0; }
bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; }
char operator[](size_t i) const {
const char *p = c_str();
#ifndef NDEBUG
for (; i != 0; i--, p++)
log_assert(*p != 0);
return *p;
#else
return *(p + i);
#endif
}
std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
if (len == std::string::npos || len >= strlen(c_str() + pos))
return std::string(c_str() + pos);
else
return std::string(c_str() + pos, len);
}
int compare(size_t pos, size_t len, const char* s) const {
return strncmp(c_str()+pos, s, len);
}
bool begins_with(const char* prefix) const {
size_t len = strlen(prefix);
if (size() < len) return false;
return compare(0, len, prefix) == 0;
}
bool ends_with(const char* suffix) const {
size_t len = strlen(suffix);
if (size() < len) return false;
return compare(size()-len, len, suffix) == 0;
}
bool contains(const char* str) const {
return strstr(c_str(), str);
}
size_t size() const {
return strlen(c_str());
}
bool empty() const {
return c_str()[0] == 0;
}
void clear() {
*this = IdString();
}
Hasher hash_acc(Hasher h) const { return hash_ops<int>::hash_acc(index_, h); }
Hasher hash_top() const {
Hasher h;
h.force((Hasher::hash_t) index_);
return h;
}
// The following is a helper key_compare class. Instead of for example std::set<Cell*>
// use std::set<Cell*, IdString::compare_ptr_by_name<Cell>> if the order of cells in the
// set has an influence on the algorithm.
template<typename T> struct compare_ptr_by_name {
bool operator()(const T *a, const T *b) const {
return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name);
} }
std::string substr(size_t pos = 0, size_t len = std::string::npos) const {
if (len == std::string::npos || len >= strlen(c_str() + pos))
return std::string(c_str() + pos);
else
return std::string(c_str() + pos, len);
}
int compare(size_t pos, size_t len, const char* s) const {
return strncmp(c_str()+pos, s, len);
}
bool begins_with(const char* prefix) const {
size_t len = strlen(prefix);
if (size() < len) return false;
return compare(0, len, prefix) == 0;
}
bool ends_with(const char* suffix) const {
size_t len = strlen(suffix);
if (size() < len) return false;
return compare(size()-len, len, suffix) == 0;
}
bool contains(const char* str) const {
return strstr(c_str(), str);
}
size_t size() const {
return strlen(c_str());
}
bool empty() const {
return c_str()[0] == 0;
}
void clear() {
*this = IdString();
}
Hasher hash_acc(Hasher h) const {
// If we're starting a hashing sequence, simply start with unhashed ID
if (h.is_new()) {
h.force((Hasher::hash_t) index_);
return h;
}
return hash_ops<int>::hash_acc(index_, h);
}
// The following is a helper key_compare class. Instead of for example std::set<Cell*>
// use std::set<Cell*, IdString::compare_ptr_by_name<Cell>> if the order of cells in the
// set has an influence on the algorithm.
template<typename T> struct compare_ptr_by_name {
bool operator()(const T *a, const T *b) const {
return (a == nullptr || b == nullptr) ? (a < b) : (a->name < b->name);
}
};
// often one needs to check if a given IdString is part of a list (for example a list
// of cell types). the following functions helps with that.
template<typename... Args>
bool in(Args... args) const {
// Credit: https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html
bool result = false;
(void) std::initializer_list<int>{ (result = result || in(args), 0)... };
return result;
}
bool in(const IdString &rhs) const { return *this == rhs; }
bool in(const char *rhs) const { return *this == rhs; }
bool in(const std::string &rhs) const { return *this == rhs; }
bool in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
bool isPublic() const { return begins_with("\\"); }
}; };
// often one needs to check if a given IdString is part of a list (for example a list
// of cell types). the following functions helps with that.
template<typename... Args>
bool in(Args... args) const {
return (... || in(args));
}
bool in(const IdString &rhs) const { return *this == rhs; }
bool in(const char *rhs) const { return *this == rhs; }
bool in(const std::string &rhs) const { return *this == rhs; }
inline bool in(const pool<IdString> &rhs) const;
inline bool in(const pool<IdString> &&rhs) const;
bool isPublic() const { return begins_with("\\"); }
};
namespace hashlib {
template <>
struct hash_top_ops<RTLIL::IdString> {
static inline bool cmp(const RTLIL::IdString &a, const RTLIL::IdString &b) {
return a == b;
}
static inline Hasher hash(const RTLIL::IdString id) {
return id.hash_top();
}
};
};
// TODO deprecate this
inline bool RTLIL::IdString::in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
inline bool RTLIL::IdString::in(const pool<IdString> &&rhs) const { return rhs.count(*this) != 0; }
namespace RTLIL {
namespace ID { namespace ID {
#define X(_id) extern IdString _id; #define X(_id) extern IdString _id;
#include "kernel/constids.inc" #include "kernel/constids.inc"
#undef X #undef X
}; };
extern dict<std::string, std::string> constpad; extern dict<std::string, std::string> constpad;
const pool<IdString> &builtin_ff_cell_types(); const pool<IdString> &builtin_ff_cell_types();