diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 844e193ef73..ec95ef08c0c 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -24,10 +24,13 @@ namespace hashlib { /** * HASHING * - * The Hasher knows how to hash basic stuff. Primitives and their - * common standard containers and compositions. + * The Hasher knows how to hash 32 and 64-bit integers. That's it. + * In the future, it could be expanded to do vectors with SIMD. * - * The Hasher doesn't know how to hash silly Yosys-specific types. + * The Hasher doesn't know how to hash common standard containers + * and compositions. However, hashlib provides centralized wrappers. + * + * Hashlib doesn't know how to hash silly Yosys-specific types. * Hashlib doesn't depend on Yosys and can be used standalone. * Please don't use hashlib standalone for new projects. * @@ -52,243 +55,248 @@ const int hashtable_size_factor = 3; #ifdef DJB2_BROKEN_SIZE -class Hasher { +template +struct hash_ops; - // Tag structs - struct general_tag {}; - struct integral_tag : general_tag {}; - struct enum_tag : general_tag {}; - struct pointer_tag : general_tag {}; +class Hasher { public: //TODO - typedef uint32_t hash_t; - typedef hash_t hash_state_t; - - hash_state_t state; + using hash_t = uint32_t; Hasher() { // traditionally 5381 is used as starting value for the djb2 hash state = 5381; } + private: + uint32_t state; // The XOR version of DJB2 [[nodiscard]] - static hash_state_t mkhash(hash_state_t a, hash_state_t b) { + static uint32_t mkhash(uint32_t a, uint32_t b) { return ((a << 5) + a) ^ b; } - [[nodiscard]] - static hash_t mkhash_finish(hash_state_t s) { - return (hash_t)s; - } - void hash_int(hash_state_t i) { + public: + void hash32(uint32_t i) { state = mkhash(i, state); return; } + void hash64(uint64_t i) { + state = mkhash((uint32_t)(i % (1ULL << 32ULL)), state); + state = mkhash((uint32_t)(i >> 32ULL), state); + return; + } hash_t yield() { return (hash_t)state; } - template - struct hash_ops_impl { - static inline bool cmp(const T &a, const T &b) { - return a == b; - } - static inline Hasher hash_acc(const T &a, Hasher h) { - return a.hash_acc(h); - } - }; - template void acc(T t) { *this = hash_ops::hash_acc(t, *this); } - void commutative_acc(hash_state_t t) { + void commutative_acc(uint32_t t) { state ^= t; } - // Unless they're too big, - // hash integers as the size natural to the hasher. - template - struct hash_ops_impl { - // If this fails, adapt hash_ops - static_assert(sizeof(T) <= sizeof(hash_state_t)); - static inline Hasher hash_acc(T value, Hasher h) { - h.hash_int(value); - return h; - } - static inline bool cmp(T a, T b) { - return a == b; - } - }; - // Hash enums like their underlying type - template - struct hash_ops_impl { - using u_type = std::underlying_type_t; - static inline Hasher hash_acc(T value, Hasher h) { - return hash_ops::hash_acc((u_type) value, h); - } - static inline bool cmp(T a, T b) { - return a == b; - } - }; - // Hash pointers like pointer-sized ints - template - struct hash_ops_impl { - static inline Hasher hash_acc(T value, Hasher h) { - return hash_ops::hash_acc((uintptr_t) value, h); - } - static inline bool cmp(T a, T b) { - return a == b; - } - }; +}; +#endif - template - struct hash_ops { - typedef hash_ops_impl, - enum_tag, - - std::conditional_t< - std::is_pointer_v, - pointer_tag, - - std::conditional_t< - std::is_integral_v, - integral_tag, - - general_tag>>>> impl; - static inline Hasher hash_acc(const T &a, Hasher h) { - return impl::hash_acc(a, h); - } - static inline bool cmp(const T &a, const T &b) { - return impl::cmp(a, b); - } - }; - struct hash_int_ops { - template - static inline bool cmp(T a, T b) { - return a == b; - } - }; - template<> struct hash_ops : hash_int_ops - { - static inline Hasher hash_acc(bool a, Hasher h) { - h.hash_int(a ? 1 : 0); - return h; - } - }; +// Tag structs +struct general_tag {}; +struct integral_tag : general_tag {}; +struct enum_tag : general_tag {}; +struct pointer_tag : general_tag {}; - template<> struct hash_ops : hash_int_ops - { - static inline Hasher hash_acc(uint32_t a, Hasher h) { - h.hash_int(a); - return h; - } - }; - template<> struct hash_ops : hash_int_ops - { - static inline Hasher hash_acc(uint64_t a, Hasher h) { - h.hash_int((hash_state_t)a); - h.hash_int((hash_state_t)(a >> 32)); - return h; - } - }; +template +struct hash_ops_impl { + static inline bool cmp(const T &a, const T &b) { + return a == b; + } + static inline Hasher hash_acc(const T &a, Hasher h) { + return a.hash_acc(h); + } +}; +// Unless they're too big, +// hash integers as the size natural to the hasher. +template +struct hash_ops_impl { + // If this fails, adapt hash_ops + static_assert(sizeof(T) <= sizeof(uint64_t)); + static inline Hasher hash_acc(T value, Hasher h) { + if (sizeof(T) == sizeof(uint64_t)) + h.hash64(value); + else + h.hash32(value); - // Explicit specializations to ensure the correct types are used - template<> struct hash_ops : hash_ops {}; - template<> struct hash_ops : hash_ops {}; + return h; + } + static inline bool cmp(T a, T b) { + return a == b; + } +}; +// Hash enums like their underlying type +template +struct hash_ops_impl { + using u_type = std::underlying_type_t; + static inline Hasher hash_acc(T value, Hasher h) { + return hash_ops_impl::hash_acc((u_type) value, h); + } + static inline bool cmp(T a, T b) { + return a == b; + } +}; +// Hash pointers like pointer-sized ints +template +struct hash_ops_impl { + static inline Hasher hash_acc(T value, Hasher h) { + return hash_ops_impl::hash_acc((uintptr_t) value, h); + } + static inline bool cmp(T a, T b) { + return a == b; + } +}; - template<> struct hash_ops { - static inline bool cmp(const std::string &a, const std::string &b) { - return a == b; - } - static inline Hasher hash_acc(const std::string &a, Hasher h) { - for (auto c : a) - h.hash_int(c); - return h; - } - }; +template +struct hash_ops { + typedef hash_ops_impl, + enum_tag, - template struct hash_ops> { - static inline bool cmp(std::pair a, std::pair b) { - return a == b; - } - static inline Hasher hash_acc(std::pair a, Hasher h) { - h = hash_ops

::hash_acc(a.first, h); - h = hash_ops::hash_acc(a.second, h); - return h; - } - }; + std::conditional_t< + std::is_pointer_v, + pointer_tag, - template struct hash_ops> { - static inline bool cmp(std::tuple a, std::tuple b) { - return a == b; - } - template - static inline typename std::enable_if::type hash_acc(std::tuple, Hasher h) { - return h; - } - template - static inline typename std::enable_if::type hash_acc(std::tuple a, Hasher h) { - typedef hash_ops>::type> element_ops_t; - h = hash_acc(a, h); - h = element_ops_t::hash_acc(std::get(a), h); - return h; - } - }; + std::conditional_t< + std::is_integral_v, + integral_tag, - template struct hash_ops> { - static inline bool cmp(std::vector a, std::vector b) { - return a == b; - } - static inline Hasher hash_acc(std::vector a, Hasher h) { - h.acc(a.size()); - for (auto k : a) - h.acc(k); - return h; - } - }; + general_tag>>>> impl; + static inline Hasher hash_acc(const T &a, Hasher h) { + return impl::hash_acc(a, h); + } + static inline bool cmp(const T &a, const T &b) { + return impl::cmp(a, b); + } +}; - struct hash_cstr_ops { - static inline bool cmp(const char *a, const char *b) { - for (int i = 0; a[i] || b[i]; i++) - if (a[i] != b[i]) - return false; - return true; - } - static inline Hasher hash_acc(const char *a, Hasher h) { - while (*a) - h.hash_int(*(a++)); - return h; - } - }; +struct hash_int_ops { + template + static inline bool cmp(T a, T b) { + return a == b; + } +}; +template<> struct hash_ops : hash_int_ops +{ + static inline Hasher hash_acc(bool a, Hasher h) { + return hash_ops_impl::hash_acc(a ? 1 : 0, h); + } +}; - template <> struct hash_ops : hash_cstr_ops {}; +template<> struct hash_ops : hash_int_ops +{ + static inline Hasher hash_acc(uint32_t a, Hasher h) { + h.hash32(a); + return h; + } +}; +template<> struct hash_ops : hash_int_ops +{ + static inline Hasher hash_acc(uint64_t a, Hasher h) { + h.hash64(a); + return h; + } +}; - struct hash_ptr_ops { - static inline bool cmp(const void *a, const void *b) { - return a == b; - } - static inline Hasher hash_acc(const void *a, Hasher h) { - return hash_ops::hash_acc((uintptr_t)a, h); - } - }; +// Explicit specializations to ensure the correct types are used +template<> struct hash_ops : hash_ops {}; +template<> struct hash_ops : hash_ops {}; - struct hash_obj_ops { - static inline bool cmp(const void *a, const void *b) { - return a == b; - } - template - static inline Hasher hash_acc(const T *a, Hasher h) { - return a ? a->hash_acc(h) : h; - } - }; +template<> struct hash_ops { + static inline bool cmp(const std::string &a, const std::string &b) { + return a == b; + } + static inline Hasher hash_acc(const std::string &a, Hasher h) { + for (auto c : a) + h.hash32(c); + return h; + } +}; +template struct hash_ops> { + static inline bool cmp(std::pair a, std::pair b) { + return a == b; + } + static inline Hasher hash_acc(std::pair a, Hasher h) { + h = hash_ops

::hash_acc(a.first, h); + h = hash_ops::hash_acc(a.second, h); + return h; + } +}; + +template struct hash_ops> { + static inline bool cmp(std::tuple a, std::tuple b) { + return a == b; + } + template + static inline typename std::enable_if::type hash_acc(std::tuple, Hasher h) { + return h; + } + template + static inline typename std::enable_if::type hash_acc(std::tuple a, Hasher h) { + typedef hash_ops>::type> element_ops_t; + h = hash_acc(a, h); + h = element_ops_t::hash_acc(std::get(a), h); + return h; + } +}; + +template struct hash_ops> { + static inline bool cmp(std::vector a, std::vector b) { + return a == b; + } + static inline Hasher hash_acc(std::vector a, Hasher h) { + h.acc(a.size()); + for (auto k : a) + h.acc(k); + return h; + } }; -#endif +struct hash_cstr_ops { + static inline bool cmp(const char *a, const char *b) { + for (int i = 0; a[i] || b[i]; i++) + if (a[i] != b[i]) + return false; + return true; + } + static inline Hasher hash_acc(const char *a, Hasher h) { + while (*a) + h.hash32(*(a++)); + return h; + } +}; + +template <> struct hash_ops : hash_cstr_ops {}; + +struct hash_ptr_ops { + static inline bool cmp(const void *a, const void *b) { + return a == b; + } + static inline Hasher hash_acc(const void *a, Hasher h) { + return hash_ops::hash_acc((uintptr_t)a, h); + } +}; + +struct hash_obj_ops { + static inline bool cmp(const void *a, const void *b) { + return a == b; + } + template + static inline Hasher hash_acc(const T *a, Hasher h) { + return a ? a->hash_acc(h) : h; + } +}; /** * If you find yourself using this function, think hard * about if it's the right thing to do. Mixing finalized @@ -296,7 +304,7 @@ class Hasher { * desirable qualities of the hash function */ template -Hasher::hash_state_t run_hash(const T& obj) { +Hasher::hash_t run_hash(const T& obj) { Hasher h; h.acc(obj); return h.yield(); @@ -372,7 +380,7 @@ class dict { std::vector hashtable; std::vector entries; - Hasher::hash_ops ops; + hash_ops ops; #ifdef NDEBUG static inline void do_assert(bool) { } @@ -831,7 +839,7 @@ class pool std::vector hashtable; std::vector entries; - Hasher::hash_ops ops; + hash_ops ops; #ifdef NDEBUG static inline void do_assert(bool) { } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index c01e1da56ac..f7aaf320007 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -359,7 +359,7 @@ namespace RTLIL } Hasher hash_acc(Hasher h) const { - return Hasher::hash_ops::hash_acc(index_, h); + return hash_ops::hash_acc(index_, h); } // The following is a helper key_compare class. Instead of for example std::set diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index 45315414b32..c8cac2441cb 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -173,6 +173,7 @@ using std::max; using hashlib::Hasher; using hashlib::run_hash; +using hashlib::hash_ops; using hashlib::mkhash_xorshift; using hashlib::dict; using hashlib::idict;