3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-24 01:25:33 +00:00
This commit is contained in:
Akash Levy 2024-12-19 21:40:30 -08:00
commit 1dcf75d175
68 changed files with 1270 additions and 879 deletions

View file

@ -30,7 +30,7 @@ struct BitPatternPool
int width;
struct bits_t {
std::vector<RTLIL::State> bitdata;
mutable unsigned int cached_hash;
mutable Hasher::hash_t cached_hash;
bits_t(int width = 0) : bitdata(width), cached_hash(0) { }
RTLIL::State &operator[](int index) {
return bitdata[index];
@ -39,14 +39,15 @@ struct BitPatternPool
return bitdata[index];
}
bool operator==(const bits_t &other) const {
if (hash() != other.hash())
if (run_hash(*this) != run_hash(other))
return false;
return bitdata == other.bitdata;
}
unsigned int hash() const {
Hasher hash_into(Hasher h) const {
if (!cached_hash)
cached_hash = hash_ops<std::vector<RTLIL::State>>::hash(bitdata);
return cached_hash;
cached_hash = run_hash(bitdata);
h.eat(cached_hash);
return h;
}
};
pool<bits_t> database;

View file

@ -39,13 +39,13 @@ bool AigNode::operator==(const AigNode &other) const
return true;
}
unsigned int AigNode::hash() const
Hasher AigNode::hash_into(Hasher h) const
{
unsigned int h = mkhash_init;
h = mkhash(portname.hash(), portbit);
h = mkhash(h, inverter);
h = mkhash(h, left_parent);
h = mkhash(h, right_parent);
h.eat(portname);
h.eat(portbit);
h.eat(inverter);
h.eat(left_parent);
h.eat(right_parent);
return h;
}
@ -54,9 +54,10 @@ bool Aig::operator==(const Aig &other) const
return name == other.name;
}
unsigned int Aig::hash() const
Hasher Aig::hash_into(Hasher h) const
{
return hash_ops<std::string>::hash(name);
h.eat(name);
return h;
}
struct AigMaker

View file

@ -34,7 +34,7 @@ struct AigNode
AigNode();
bool operator==(const AigNode &other) const;
unsigned int hash() const;
Hasher hash_into(Hasher h) const;
};
struct Aig
@ -44,7 +44,7 @@ struct Aig
Aig(Cell *cell);
bool operator==(const Aig &other) const;
unsigned int hash() const;
Hasher hash_into(Hasher h) const;
};
YOSYS_NAMESPACE_END

View file

@ -18,6 +18,7 @@
*/
#include "kernel/yosys.h"
#include "kernel/hashlib.h"
#include "libs/sha1/sha1.h"
#include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream>
@ -282,6 +283,8 @@ int main(int argc, char **argv)
("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
("autoidx", "start counting autoidx up from <seed>, similar effect to --hash-seed",
cxxopts::value<uint64_t>(), "<idx>")
("hash-seed", "mix up hashing values with <seed>, for extreme optimization and testing",
cxxopts::value<uint64_t>(), "<seed>")
("A,abort", "will call abort() at the end of the script. for debugging")
("x,experimental", "do not print warnings for the experimental <feature>",
cxxopts::value<std::vector<std::string>>(), "<feature>")
@ -437,6 +440,10 @@ int main(int argc, char **argv)
int idx = result["autoidx"].as<uint64_t>();
autoidx = idx;
}
if (result.count("hash-seed")) {
int seed = result["hash-seed"].as<uint64_t>();
Hasher::set_fudge((Hasher::hash_t)seed);
}
if (log_errfile == NULL) {
log_files.push_back(stdout);

View file

@ -74,10 +74,8 @@ struct DriveBitWire
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(wire->name.hash(), offset);
}
Hasher hash_into(Hasher h) const;
operator SigBit() const
{
@ -107,10 +105,8 @@ struct DriveBitPort
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
}
Hasher hash_into(Hasher h) const;
};
@ -133,10 +129,7 @@ struct DriveBitMarker
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(marker, offset);
}
Hasher hash_into(Hasher h) const;
};
@ -171,10 +164,7 @@ public:
return multiple_ == other.multiple_;
}
unsigned int hash() const
{
return multiple_.hash();
}
Hasher hash_into(Hasher h) const;
};
struct DriveBit
@ -362,35 +352,7 @@ public:
return *this;
}
unsigned int hash() const
{
unsigned int inner = 0;
switch (type_)
{
case DriveType::NONE:
inner = 0;
break;
case DriveType::CONSTANT:
inner = constant_;
break;
case DriveType::WIRE:
inner = wire_.hash();
break;
case DriveType::PORT:
inner = port_.hash();
break;
case DriveType::MARKER:
inner = marker_.hash();
break;
case DriveType::MULTIPLE:
inner = multiple_.hash();
break;
default:
log_abort();
break;
}
return mkhash((unsigned int)type_, inner);
}
Hasher hash_into(Hasher h) const;
bool operator==(const DriveBit &other) const
{
@ -511,10 +473,7 @@ struct DriveChunkWire
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(wire->name.hash(), width), offset);
}
Hasher hash_into(Hasher h) const;
explicit operator SigChunk() const
{
@ -572,10 +531,7 @@ struct DriveChunkPort
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(mkhash(cell->name.hash(), port.hash()), width), offset);
}
Hasher hash_into(Hasher h) const;
};
@ -616,10 +572,7 @@ struct DriveChunkMarker
return offset < other.offset;
}
unsigned int hash() const
{
return mkhash_add(mkhash(marker, width), offset);
}
Hasher hash_into(Hasher h) const;
};
struct DriveChunkMultiple
@ -659,10 +612,7 @@ public:
return false; // TODO implement, canonicalize order
}
unsigned int hash() const
{
return mkhash(width_, multiple_.hash());
}
Hasher hash_into(Hasher h) const;
};
struct DriveChunk
@ -913,35 +863,7 @@ public:
bool try_append(DriveBit const &bit);
bool try_append(DriveChunk const &chunk);
unsigned int hash() const
{
unsigned int inner = 0;
switch (type_)
{
case DriveType::NONE:
inner = 0;
break;
case DriveType::CONSTANT:
inner = constant_.hash();
break;
case DriveType::WIRE:
inner = wire_.hash();
break;
case DriveType::PORT:
inner = port_.hash();
break;
case DriveType::MARKER:
inner = marker_.hash();
break;
case DriveType::MULTIPLE:
inner = multiple_.hash();
break;
default:
log_abort();
break;
}
return mkhash((unsigned int)type_, inner);
}
Hasher hash_into(Hasher h) const;
bool operator==(const DriveChunk &other) const
{
@ -1144,17 +1066,20 @@ public:
DriveSpec &operator=(DriveBitMarker const &bit) { return *this = DriveBit(bit); }
DriveSpec &operator=(DriveBitMultiple const &bit) { return *this = DriveBit(bit); }
unsigned int hash() const {
if (hash_ != 0) return hash_;
void updhash() const {
if (hash_ != 0)
return;
pack();
hash_ = hash_ops<std::vector<DriveChunk>>().hash(chunks_);
hash_ = run_hash(chunks_);
hash_ |= (hash_ == 0);
return hash_;
}
Hasher hash_into(Hasher h) const;
bool operator==(DriveSpec const &other) const {
if (size() != other.size() || hash() != other.hash())
updhash();
other.updhash();
if (size() != other.size() || hash_ != other.hash_)
return false;
return chunks() == other.chunks();
}
@ -1187,7 +1112,7 @@ private:
bool operator==(const DriveBitId &other) const { return id == other.id; }
bool operator!=(const DriveBitId &other) const { return id != other.id; }
bool operator<(const DriveBitId &other) const { return id < other.id; }
unsigned int hash() const { return id; }
Hasher hash_into(Hasher h) const;
};
// Essentially a dict<DriveBitId, pool<DriveBitId>> but using less memory
// and fewer allocations
@ -1333,6 +1258,131 @@ private:
}
};
inline Hasher DriveBitWire::hash_into(Hasher h) const
{
h.eat(wire->name);
h.eat(offset);
return h;
}
inline Hasher DriveBitPort::hash_into(Hasher h) const
{
h.eat(cell->name);
h.eat(port);
h.eat(offset);
return h;
}
inline Hasher DriveBitMarker::hash_into(Hasher h) const
{
h.eat(marker);
h.eat(offset);
return h;
}
inline Hasher DriveBitMultiple::hash_into(Hasher h) const
{
h.eat(multiple_);
return h;
}
inline Hasher DriveBit::hash_into(Hasher h) const
{
switch (type_) {
case DriveType::NONE:
h.eat(0);
break;
case DriveType::CONSTANT:
h.eat(constant_);
break;
case DriveType::WIRE:
h.eat(wire_);
break;
case DriveType::PORT:
h.eat(port_);
break;
case DriveType::MARKER:
h.eat(marker_);
break;
case DriveType::MULTIPLE:
h.eat(multiple_);
break;
}
h.eat(type_);
return h;
}
inline Hasher DriveChunkWire::hash_into(Hasher h) const
{
h.eat(wire->name);
h.eat(width);
h.eat(offset);
return h;
}
inline Hasher DriveChunkPort::hash_into(Hasher h) const
{
h.eat(cell->name);
h.eat(port);
h.eat(width);
h.eat(offset);
return h;
}
inline Hasher DriveChunkMarker::hash_into(Hasher h) const
{
h.eat(marker);
h.eat(width);
h.eat(offset);
return h;
}
inline Hasher DriveChunkMultiple::hash_into(Hasher h) const
{
h.eat(width_);
h.eat(multiple_);
return h;
}
inline Hasher DriveChunk::hash_into(Hasher h) const
{
switch (type_) {
case DriveType::NONE:
h.eat(0);
break;
case DriveType::CONSTANT:
h.eat(constant_);
break;
case DriveType::WIRE:
h.eat(wire_);
break;
case DriveType::PORT:
h.eat(port_);
break;
case DriveType::MARKER:
h.eat(marker_);
break;
case DriveType::MULTIPLE:
h.eat(multiple_);
break;
}
h.eat(type_);
return h;
}
inline Hasher DriveSpec::hash_into(Hasher h) const
{
updhash();
h.eat(hash_);
return h;
}
inline Hasher DriverMap::DriveBitId::hash_into(Hasher h) const
{
h.eat(id);
return h;
}
YOSYS_NAMESPACE_END
#endif

View file

@ -151,7 +151,7 @@ namespace Functional {
// returns the data width of a bitvector sort, errors out for other sorts
int data_width() const { return std::get<1>(_v).second; }
bool operator==(Sort const& other) const { return _v == other._v; }
unsigned int hash() const { return mkhash(_v); }
Hasher hash_into(Hasher h) const { h.eat(_v); return h; }
};
class IR;
class Factory;
@ -225,8 +225,10 @@ namespace Functional {
const RTLIL::Const &as_const() const { return std::get<RTLIL::Const>(_extra); }
std::pair<IdString, IdString> as_idstring_pair() const { return std::get<std::pair<IdString, IdString>>(_extra); }
int as_int() const { return std::get<int>(_extra); }
int hash() const {
return mkhash((unsigned int) _fn, mkhash(_extra));
Hasher hash_into(Hasher h) const {
h.eat((unsigned int) _fn);
h.eat(_extra);
return h;
}
bool operator==(NodeData const &other) const {
return _fn == other._fn && _extra == other._extra;

View file

@ -17,27 +17,62 @@
#include <string>
#include <variant>
#include <vector>
#include <type_traits>
#include <stdint.h>
#define YS_HASHING_VERSION 1
namespace hashlib {
/**
* HASHING
*
* Also refer to docs/source/yosys_internals/hashing.rst
*
* 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 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.
* Never directly include kernel/hashlib.h in Yosys code.
* Instead include kernel/yosys_common.h
*
* The hash_ops type is now always left to its default value, derived
* from templated functions through SFINAE. Providing custom ops is
* still supported.
*
* HASH TABLES
*
* We implement associative data structures with separate chaining.
* Linked lists use integers into the indirection hashtable array
* instead of pointers.
*/
const int hashtable_size_trigger = 2;
const int hashtable_size_factor = 3;
// The XOR version of DJB2
inline unsigned int mkhash(unsigned int a, unsigned int b) {
return ((a << 5) + a) ^ b;
}
namespace legacy {
inline uint32_t djb2_add(uint32_t a, uint32_t b) {
return ((a << 5) + a) + b;
}
};
// traditionally 5381 is used as starting value for the djb2 hash
const unsigned int mkhash_init = 5381;
/**
* Hash a type with an accumulator in a record or array context
*/
template<typename T>
struct hash_ops;
// The ADD version of DJB2
// (use this version for cache locality in b)
inline unsigned int mkhash_add(unsigned int a, unsigned int b) {
return ((a << 5) + a) + b;
}
/**
* Hash a single instance in isolation.
* Can have explicit specialization, but the default redirects to hash_ops
*/
template<typename T>
struct hash_top_ops;
inline unsigned int mkhash_xorshift(unsigned int a) {
if (sizeof(a) == 4) {
@ -53,62 +88,100 @@ inline unsigned int mkhash_xorshift(unsigned int a) {
return a;
}
template<typename T> struct hash_ops {
class HasherDJB32 {
public:
using hash_t = uint32_t;
HasherDJB32() {
// traditionally 5381 is used as starting value for the djb2 hash
state = 5381;
}
static void set_fudge(hash_t f) {
fudge = f;
}
private:
uint32_t state;
static uint32_t fudge;
// The XOR version of DJB2
[[nodiscard]]
static uint32_t djb2_xor(uint32_t a, uint32_t b) {
uint32_t hash = ((a << 5) + a) ^ b;
return hash;
}
public:
void hash32(uint32_t i) {
state = djb2_xor(i, state);
state = mkhash_xorshift(fudge ^ state);
return;
}
void hash64(uint64_t i) {
state = djb2_xor((uint32_t)(i & 0xFFFFFFFFULL), state);
state = djb2_xor((uint32_t)(i >> 32ULL), state);
state = mkhash_xorshift(fudge ^ state);
return;
}
[[nodiscard]]
hash_t yield() {
return (hash_t)state;
}
template<typename T>
void eat(T&& t) {
*this = hash_ops<std::remove_cv_t<std::remove_reference_t<T>>>::hash_into(std::forward<T>(t), *this);
}
template<typename T>
void eat(const T& t) {
*this = hash_ops<T>::hash_into(t, *this);
}
void commutative_eat(hash_t t) {
state ^= t;
}
void force(hash_t new_state) {
state = new_state;
}
};
using Hasher = HasherDJB32;
template<typename T>
struct hash_top_ops {
static inline bool cmp(const T &a, const T &b) {
return hash_ops<T>::cmp(a, b);
}
static inline Hasher hash(const T &a) {
return hash_ops<T>::hash_into(a, Hasher());
}
};
template<typename T>
struct hash_ops {
static inline bool cmp(const T &a, const T &b) {
return a == b;
}
static inline unsigned int hash(const T &a) {
return a.hash();
}
};
struct hash_int_ops {
template<typename T>
static inline bool cmp(T a, T b) {
return a == b;
}
};
template<> struct hash_ops<bool> : hash_int_ops
{
static inline unsigned int hash(bool a) {
return a ? 1 : 0;
}
};
template<> struct hash_ops<int32_t> : hash_int_ops
{
static inline unsigned int hash(int32_t a) {
return a;
}
};
template<> struct hash_ops<int64_t> : hash_int_ops
{
static inline unsigned int hash(int64_t a) {
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
}
};
template<> struct hash_ops<uint32_t> : hash_int_ops
{
static inline unsigned int hash(uint32_t a) {
return a;
}
};
template<> struct hash_ops<uint64_t> : hash_int_ops
{
static inline unsigned int hash(uint64_t a) {
return mkhash((unsigned int)(a), (unsigned int)(a >> 32));
}
};
template<> struct hash_ops<std::string> {
static inline bool cmp(const std::string &a, const std::string &b) {
return a == b;
}
static inline unsigned int hash(const std::string &a) {
unsigned int v = 0;
for (auto c : a)
v = mkhash(v, c);
return v;
static inline Hasher hash_into(const T &a, Hasher h) {
if constexpr (std::is_integral_v<T>) {
static_assert(sizeof(T) <= sizeof(uint64_t));
if (sizeof(T) == sizeof(uint64_t))
h.hash64(a);
else
h.hash32(a);
return h;
} else if constexpr (std::is_enum_v<T>) {
using u_type = std::underlying_type_t<T>;
return hash_ops<u_type>::hash_into((u_type) a, h);
} else if constexpr (std::is_pointer_v<T>) {
return hash_ops<uintptr_t>::hash_into((uintptr_t) a, h);
} else if constexpr (std::is_same_v<T, std::string>) {
for (auto c : a)
h.hash32(c);
return h;
} else {
return a.hash_into(h);
}
}
};
@ -116,8 +189,10 @@ template<typename P, typename Q> struct hash_ops<std::pair<P, Q>> {
static inline bool cmp(std::pair<P, Q> a, std::pair<P, Q> b) {
return a == b;
}
static inline unsigned int hash(std::pair<P, Q> a) {
return mkhash(hash_ops<P>::hash(a.first), hash_ops<Q>::hash(a.second));
static inline Hasher hash_into(std::pair<P, Q> a, Hasher h) {
h = hash_ops<P>::hash_into(a.first, h);
h = hash_ops<Q>::hash_into(a.second, h);
return h;
}
};
@ -126,13 +201,15 @@ template<typename... T> struct hash_ops<std::tuple<T...>> {
return a == b;
}
template<size_t I = 0>
static inline typename std::enable_if<I == sizeof...(T), unsigned int>::type hash(std::tuple<T...>) {
return mkhash_init;
static inline typename std::enable_if<I == sizeof...(T), Hasher>::type hash_into(std::tuple<T...>, Hasher h) {
return h;
}
template<size_t I = 0>
static inline typename std::enable_if<I != sizeof...(T), unsigned int>::type hash(std::tuple<T...> a) {
static inline typename std::enable_if<I != sizeof...(T), Hasher>::type hash_into(std::tuple<T...> a, Hasher h) {
typedef hash_ops<typename std::tuple_element<I, std::tuple<T...>>::type> element_ops_t;
return mkhash(hash<I+1>(a), element_ops_t::hash(std::get<I>(a)));
h = hash_into<I+1>(a, h);
h = element_ops_t::hash_into(std::get<I>(a), h);
return h;
}
};
@ -140,35 +217,44 @@ template<typename T> struct hash_ops<std::vector<T>> {
static inline bool cmp(std::vector<T> a, std::vector<T> b) {
return a == b;
}
static inline unsigned int hash(std::vector<T> a) {
unsigned int h = mkhash_init;
static inline Hasher hash_into(std::vector<T> a, Hasher h) {
h.eat((uint32_t)a.size());
for (auto k : a)
h = mkhash(h, hash_ops<T>::hash(k));
h.eat(k);
return h;
}
};
template<typename T, size_t N> struct hash_ops<std::array<T, N>> {
static inline bool cmp(std::array<T, N> a, std::array<T, N> b) {
return a == b;
}
static inline Hasher hash_into(std::array<T, N> a, Hasher h) {
for (const auto& k : a)
h = hash_ops<T>::hash_into(k, h);
return h;
}
};
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;
return strcmp(a, b) == 0;
}
static inline unsigned int hash(const char *a) {
unsigned int hash = mkhash_init;
static inline Hasher hash_into(const char *a, Hasher h) {
while (*a)
hash = mkhash(hash, *(a++));
return hash;
h.hash32(*(a++));
return h;
}
};
template <> struct hash_ops<char*> : hash_cstr_ops {};
struct hash_ptr_ops {
static inline bool cmp(const void *a, const void *b) {
return a == b;
}
static inline unsigned int hash(const void *a) {
return (uintptr_t)a;
static inline Hasher hash_into(const void *a, Hasher h) {
return hash_ops<uintptr_t>::hash_into((uintptr_t)a, h);
}
};
@ -177,22 +263,40 @@ struct hash_obj_ops {
return a == b;
}
template<typename T>
static inline unsigned int hash(const T *a) {
return a ? a->hash() : 0;
static inline Hasher hash_into(const T *a, Hasher h) {
if (a)
a->hash_into(h);
else
h.eat(0);
return h;
}
};
/**
* If you find yourself using this function, think hard
* about if it's the right thing to do. Mixing finalized
* hashes together with XORs or worse can destroy
* desirable qualities of the hash function
*/
template<typename T>
[[nodiscard]]
Hasher::hash_t run_hash(const T& obj) {
return hash_top_ops<T>::hash(obj).yield();
}
/** Refer to docs/source/yosys_internals/hashing.rst */
template<typename T>
[[nodiscard]]
[[deprecated]]
inline unsigned int mkhash(const T &v) {
return hash_ops<T>().hash(v);
return (unsigned int) run_hash<T>(v);
}
template<> struct hash_ops<std::monostate> {
static inline bool cmp(std::monostate a, std::monostate b) {
return a == b;
}
static inline unsigned int hash(std::monostate) {
return mkhash_init;
static inline Hasher hash_into(std::monostate, Hasher h) {
return h;
}
};
@ -200,9 +304,10 @@ template<typename... T> struct hash_ops<std::variant<T...>> {
static inline bool cmp(std::variant<T...> a, std::variant<T...> b) {
return a == b;
}
static inline unsigned int hash(std::variant<T...> a) {
unsigned int h = std::visit([](const auto &v) { return mkhash(v); }, a);
return mkhash(a.index(), h);
static inline Hasher hash_into(std::variant<T...> a, Hasher h) {
std::visit([& h](const auto &v) { h.eat(v); }, a);
h.eat(a.index());
return h;
}
};
@ -210,11 +315,12 @@ template<typename T> struct hash_ops<std::optional<T>> {
static inline bool cmp(std::optional<T> a, std::optional<T> b) {
return a == b;
}
static inline unsigned int hash(std::optional<T> a) {
static inline Hasher hash_into(std::optional<T> a, Hasher h) {
if(a.has_value())
return mkhash(*a);
h.eat(*a);
else
return 0;
h.eat(0);
return h;
}
};
@ -246,14 +352,13 @@ inline int hashtable_size(int min_size)
throw std::length_error("hash table exceeded maximum size.");
}
template<typename K, typename T, typename OPS = hash_ops<K>> class dict;
template<typename K, int offset = 0, typename OPS = hash_ops<K>> class idict;
template<typename K, typename OPS = hash_ops<K>> class pool;
template<typename K, typename OPS = hash_ops<K>> class mfp;
template<typename K, typename T, typename OPS = hash_top_ops<K>> class dict;
template<typename K, int offset = 0, typename OPS = hash_top_ops<K>> class idict;
template<typename K, typename OPS = hash_top_ops<K>> class pool;
template<typename K, typename OPS = hash_top_ops<K>> class mfp;
template<typename K, typename T, typename OPS>
class dict
{
class dict {
struct entry_t
{
std::pair<K, T> udata;
@ -277,11 +382,11 @@ class dict
}
#endif
int do_hash(const K &key) const
Hasher::hash_t do_hash(const K &key) const
{
unsigned int hash = 0;
Hasher::hash_t hash = 0;
if (!hashtable.empty())
hash = ops.hash(key) % (unsigned int)(hashtable.size());
hash = ops.hash(key).yield() % (unsigned int)(hashtable.size());
return hash;
}
@ -292,13 +397,13 @@ class dict
for (int i = 0; i < int(entries.size()); i++) {
do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
int hash = do_hash(entries[i].udata.first);
Hasher::hash_t hash = do_hash(entries[i].udata.first);
entries[i].next = hashtable[hash];
hashtable[hash] = i;
}
}
int do_erase(int index, int hash)
int do_erase(int index, Hasher::hash_t hash)
{
do_assert(index < int(entries.size()));
if (hashtable.empty() || index < 0)
@ -321,7 +426,7 @@ class dict
if (index != back_idx)
{
int back_hash = do_hash(entries[back_idx].udata.first);
Hasher::hash_t back_hash = do_hash(entries[back_idx].udata.first);
k = hashtable[back_hash];
do_assert(0 <= k && k < int(entries.size()));
@ -347,7 +452,7 @@ class dict
return 1;
}
int do_lookup(const K &key, int &hash) const
int do_lookup(const K &key, Hasher::hash_t &hash) const
{
if (hashtable.empty())
return -1;
@ -367,7 +472,7 @@ class dict
return index;
}
int do_insert(const K &key, int &hash)
int do_insert(const K &key, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(std::pair<K, T>(key, T()), -1);
@ -380,7 +485,7 @@ class dict
return entries.size() - 1;
}
int do_insert(const std::pair<K, T> &value, int &hash)
int do_insert(const std::pair<K, T> &value, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(value, -1);
@ -393,7 +498,7 @@ class dict
return entries.size() - 1;
}
int do_insert(std::pair<K, T> &&rvalue, int &hash)
int do_insert(std::pair<K, T> &&rvalue, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
auto key = rvalue.first;
@ -505,7 +610,7 @@ public:
std::pair<iterator, bool> insert(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -515,7 +620,7 @@ public:
std::pair<iterator, bool> insert(const std::pair<K, T> &value)
{
int hash = do_hash(value.first);
Hasher::hash_t hash = do_hash(value.first);
int i = do_lookup(value.first, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -525,7 +630,7 @@ public:
std::pair<iterator, bool> insert(std::pair<K, T> &&rvalue)
{
int hash = do_hash(rvalue.first);
Hasher::hash_t hash = do_hash(rvalue.first);
int i = do_lookup(rvalue.first, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -535,7 +640,7 @@ public:
std::pair<iterator, bool> emplace(K const &key, T const &value)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -545,7 +650,7 @@ public:
std::pair<iterator, bool> emplace(K const &key, T &&rvalue)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -555,7 +660,7 @@ public:
std::pair<iterator, bool> emplace(K &&rkey, T const &value)
{
int hash = do_hash(rkey);
Hasher::hash_t hash = do_hash(rkey);
int i = do_lookup(rkey, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -565,7 +670,7 @@ public:
std::pair<iterator, bool> emplace(K &&rkey, T &&rvalue)
{
int hash = do_hash(rkey);
Hasher::hash_t hash = do_hash(rkey);
int i = do_lookup(rkey, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -575,35 +680,35 @@ public:
int erase(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int index = do_lookup(key, hash);
return do_erase(index, hash);
}
iterator erase(iterator it)
{
int hash = do_hash(it->first);
Hasher::hash_t hash = do_hash(it->first);
do_erase(it.index, hash);
return ++it;
}
int count(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 ? 0 : 1;
}
int count(const K &key, const_iterator it) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 || i > it.index ? 0 : 1;
}
iterator find(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -612,7 +717,7 @@ public:
const_iterator find(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -621,7 +726,7 @@ public:
T& at(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
throw std::out_of_range("dict::at()");
@ -630,7 +735,7 @@ public:
const T& at(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
throw std::out_of_range("dict::at()");
@ -639,7 +744,7 @@ public:
const T& at(const K &key, const T &defval) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return defval;
@ -648,7 +753,7 @@ public:
T& operator[](const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
i = do_insert(std::pair<K, T>(key, T()), hash);
@ -683,12 +788,14 @@ public:
return !operator==(other);
}
unsigned int hash() const {
unsigned int h = mkhash_init;
for (auto &entry : entries) {
h ^= hash_ops<K>::hash(entry.udata.first);
h ^= hash_ops<T>::hash(entry.udata.second);
Hasher hash_into(Hasher h) const {
for (auto &it : entries) {
Hasher entry_hash;
entry_hash.eat(it.udata.first);
entry_hash.eat(it.udata.second);
h.commutative_eat(entry_hash.yield());
}
h.eat(entries.size());
return h;
}
@ -734,11 +841,11 @@ protected:
}
#endif
int do_hash(const K &key) const
Hasher::hash_t do_hash(const K &key) const
{
unsigned int hash = 0;
Hasher::hash_t hash = 0;
if (!hashtable.empty())
hash = ops.hash(key) % (unsigned int)(hashtable.size());
hash = ops.hash(key).yield() % (unsigned int)(hashtable.size());
return hash;
}
@ -749,13 +856,13 @@ protected:
for (int i = 0; i < int(entries.size()); i++) {
do_assert(-1 <= entries[i].next && entries[i].next < int(entries.size()));
int hash = do_hash(entries[i].udata);
Hasher::hash_t hash = do_hash(entries[i].udata);
entries[i].next = hashtable[hash];
hashtable[hash] = i;
}
}
int do_erase(int index, int hash)
int do_erase(int index, Hasher::hash_t hash)
{
do_assert(index < int(entries.size()));
if (hashtable.empty() || index < 0)
@ -776,7 +883,7 @@ protected:
if (index != back_idx)
{
int back_hash = do_hash(entries[back_idx].udata);
Hasher::hash_t back_hash = do_hash(entries[back_idx].udata);
k = hashtable[back_hash];
if (k == back_idx) {
@ -800,7 +907,7 @@ protected:
return 1;
}
int do_lookup(const K &key, int &hash) const
int do_lookup(const K &key, Hasher::hash_t &hash) const
{
if (hashtable.empty())
return -1;
@ -820,7 +927,7 @@ protected:
return index;
}
int do_insert(const K &value, int &hash)
int do_insert(const K &value, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(value, -1);
@ -833,7 +940,7 @@ protected:
return entries.size() - 1;
}
int do_insert(K &&rvalue, int &hash)
int do_insert(K &&rvalue, Hasher::hash_t &hash)
{
if (hashtable.empty()) {
entries.emplace_back(std::forward<K>(rvalue), -1);
@ -940,7 +1047,7 @@ public:
std::pair<iterator, bool> insert(const K &value)
{
int hash = do_hash(value);
Hasher::hash_t hash = do_hash(value);
int i = do_lookup(value, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -950,7 +1057,7 @@ public:
std::pair<iterator, bool> insert(K &&rvalue)
{
int hash = do_hash(rvalue);
Hasher::hash_t hash = do_hash(rvalue);
int i = do_lookup(rvalue, hash);
if (i >= 0)
return std::pair<iterator, bool>(iterator(this, i), false);
@ -966,35 +1073,35 @@ public:
int erase(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int index = do_lookup(key, hash);
return do_erase(index, hash);
}
iterator erase(iterator it)
{
int hash = do_hash(*it);
Hasher::hash_t hash = do_hash(*it);
do_erase(it.index, hash);
return ++it;
}
int count(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 ? 0 : 1;
}
int count(const K &key, const_iterator it) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i < 0 || i > it.index ? 0 : 1;
}
iterator find(const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -1003,7 +1110,7 @@ public:
const_iterator find(const K &key) const
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
if (i < 0)
return end();
@ -1012,7 +1119,7 @@ public:
bool operator[](const K &key)
{
int hash = do_hash(key);
Hasher::hash_t hash = do_hash(key);
int i = do_lookup(key, hash);
return i >= 0;
}
@ -1051,11 +1158,12 @@ public:
return !operator==(other);
}
unsigned int hash() const {
unsigned int hashval = mkhash_init;
for (auto &it : entries)
hashval ^= ops.hash(it.udata);
return hashval;
Hasher hash_into(Hasher h) const {
for (auto &it : entries) {
h.commutative_eat(ops.hash(it.udata).yield());
}
h.eat(entries.size());
return h;
}
void reserve(size_t n) { entries.reserve(n); }
@ -1105,7 +1213,7 @@ public:
int operator()(const K &key)
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
if (i < 0)
i = database.do_insert(key, hash);
@ -1114,7 +1222,7 @@ public:
int at(const K &key) const
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
if (i < 0)
throw std::out_of_range("idict::at()");
@ -1123,7 +1231,7 @@ public:
int at(const K &key, int defval) const
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
if (i < 0)
return defval;
@ -1132,7 +1240,7 @@ public:
int count(const K &key) const
{
int hash = database.do_hash(key);
Hasher::hash_t hash = database.do_hash(key);
int i = database.do_lookup(key, hash);
return i < 0 ? 0 : 1;
}
@ -1176,7 +1284,7 @@ class mfp
mutable std::vector<int> parents;
public:
typedef typename idict<K, 0, OPS>::const_iterator const_iterator;
typedef typename idict<K, 0>::const_iterator const_iterator;
constexpr mfp()
{

View file

@ -367,13 +367,13 @@ void log_dump_val_worker(RTLIL::IdString v);
void log_dump_val_worker(RTLIL::SigSpec v);
void log_dump_val_worker(RTLIL::State v);
template<typename K, typename T, typename OPS> static inline void log_dump_val_worker(dict<K, T, OPS> &v);
template<typename K, typename OPS> static inline void log_dump_val_worker(pool<K, OPS> &v);
template<typename K, typename T> static inline void log_dump_val_worker(dict<K, T> &v);
template<typename K> static inline void log_dump_val_worker(pool<K> &v);
template<typename K> static inline void log_dump_val_worker(std::vector<K> &v);
template<typename T> static inline void log_dump_val_worker(T *ptr);
template<typename K, typename T, typename OPS>
static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
template<typename K, typename T>
static inline void log_dump_val_worker(dict<K, T> &v) {
log("{");
bool first = true;
for (auto &it : v) {
@ -386,8 +386,8 @@ static inline void log_dump_val_worker(dict<K, T, OPS> &v) {
log(" }");
}
template<typename K, typename OPS>
static inline void log_dump_val_worker(pool<K, OPS> &v) {
template<typename K>
static inline void log_dump_val_worker(pool<K> &v) {
log("{");
bool first = true;
for (auto &it : v) {

View file

@ -48,8 +48,11 @@ struct ModIndex : public RTLIL::Monitor
return cell == other.cell && port == other.port && offset == other.offset;
}
unsigned int hash() const {
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
Hasher hash_into(Hasher h) const {
h.eat(cell->name);
h.eat(port);
h.eat(offset);
return h;
}
};
@ -304,6 +307,7 @@ struct ModWalker
RTLIL::Cell *cell;
RTLIL::IdString port;
int offset;
PortBit(Cell* c, IdString p, int o) : cell(c), port(p), offset(o) {}
bool operator<(const PortBit &other) const {
if (cell != other.cell)
@ -317,8 +321,11 @@ struct ModWalker
return cell == other.cell && port == other.port && offset == other.offset;
}
unsigned int hash() const {
return mkhash_add(mkhash(cell->name.hash(), port.hash()), offset);
Hasher hash_into(Hasher h) const {
h.eat(cell->name);
h.eat(port);
h.eat(offset);
return h;
}
};
@ -355,7 +362,7 @@ struct ModWalker
{
for (int i = 0; i < int(bits.size()); i++)
if (bits[i].wire != NULL) {
PortBit pbit = { cell, port, i };
PortBit pbit {cell, port, i};
if (is_output) {
signal_drivers[bits[i]].insert(pbit);
cell_outputs[cell].insert(bits[i]);

View file

@ -36,7 +36,7 @@ YOSYS_NAMESPACE_BEGIN
bool RTLIL::IdString::destruct_guard_ok = false;
RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard;
std::vector<char*> RTLIL::IdString::global_id_storage_;
dict<char*, int, hash_cstr_ops> RTLIL::IdString::global_id_index_;
dict<char*, int> RTLIL::IdString::global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
std::vector<int> RTLIL::IdString::global_refcount_storage_;
std::vector<int> RTLIL::IdString::global_free_idx_list_;
@ -4492,17 +4492,17 @@ void RTLIL::SigSpec::updhash() const
cover("kernel.rtlil.sigspec.hash");
that->pack();
that->hash_ = mkhash_init;
Hasher h;
for (auto &c : that->chunks_)
if (c.wire == NULL) {
for (auto &v : c.data)
that->hash_ = mkhash(that->hash_, v);
h.eat(v);
} else {
that->hash_ = mkhash(that->hash_, c.wire->name.index_);
that->hash_ = mkhash(that->hash_, c.offset);
that->hash_ = mkhash(that->hash_, c.width);
h.eat(c.wire->name.index_);
h.eat(c.offset);
h.eat(c.width);
}
that->hash_ = h.yield();
if (that->hash_ == 0)
that->hash_ = 1;
}

View file

@ -76,329 +76,354 @@ namespace RTLIL
struct SyncRule;
struct Process;
struct Binding;
struct IdString;
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
#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, hash_cstr_ops> global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT
static std::vector<int> global_refcount_storage_;
static std::vector<int> global_free_idx_list_;
#ifdef YOSYS_XTRACE_GET_PUT
for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
{
if (global_id_storage_.at(idx) == nullptr)
log("#X# DB-DUMP index %d: FREE\n", idx);
else
log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
}
#endif
}
static inline void checkpoint()
{
#ifdef YOSYS_USE_STICKY_IDS
static int last_created_idx_ptr_;
static int last_created_idx_[8];
last_created_idx_ptr_ = 0;
for (int i = 0; i < 8; i++) {
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 void xtrace_db_dump()
{
#ifdef YOSYS_XTRACE_GET_PUT
for (int idx = 0; idx < GetSize(global_id_storage_); idx++)
{
if (global_id_storage_.at(idx) == nullptr)
log("#X# DB-DUMP index %d: FREE\n", idx);
else
log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
}
#endif
static inline int get_reference(int idx)
{
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;
}
static inline void checkpoint()
{
#ifdef YOSYS_USE_STICKY_IDS
last_created_idx_ptr_ = 0;
for (int i = 0; i < 8; i++) {
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
}
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);
static inline int get_reference(int idx)
{
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
#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;
}
int idx = global_id_storage_.size();
global_id_storage_.push_back(strdup(p));
global_id_index_[global_id_storage_.back()] = idx;
#endif
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;
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);
}
#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);
}
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);
}
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
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
// 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_);
if (yosys_xtrace) {
log("#X# New IdString '%s' with index %d.\n", p, idx);
log_backtrace("-X- ", yosys_xtrace-1);
}
inline void operator=(const char *rhs) {
IdString id(rhs);
*this = id;
#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
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) {
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;
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
return *(p + i);
static inline void put_reference(int) { }
#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_into(Hasher h) const { return hash_ops<int>::hash_into(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();
}
unsigned int hash() const {
return index_;
}
// 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();
}
};
};
/**
* How to not use these methods:
* 1. if(celltype.in({...})) -> if(celltype.in(...))
* 2. pool<IdString> p; ... a.in(p) -> (bool)p.count(a)
*/
[[deprecated]]
inline bool RTLIL::IdString::in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
[[deprecated]]
inline bool RTLIL::IdString::in(const pool<IdString> &&rhs) const { return rhs.count(*this) != 0; }
namespace RTLIL {
namespace ID {
#define X(_id) extern IdString _id;
#include "kernel/constids.inc"
#undef X
};
extern dict<std::string, std::string> constpad;
const pool<IdString> &builtin_ff_cell_types();
@ -802,11 +827,10 @@ public:
bv.resize(width, bv.empty() ? RTLIL::State::Sx : bv.back());
}
inline unsigned int hash() const {
unsigned int h = mkhash_init;
for (State b : *this)
h = mkhash(h, b);
inline Hasher hash_into(Hasher h) const {
h.eat(size());
for (auto b : *this)
h.eat(b);
return h;
}
};
@ -896,7 +920,20 @@ struct RTLIL::SigBit
bool operator <(const RTLIL::SigBit &other) const;
bool operator ==(const RTLIL::SigBit &other) const;
bool operator !=(const RTLIL::SigBit &other) const;
unsigned int hash() const;
Hasher hash_into(Hasher h) const;
Hasher hash_top() const;
};
namespace hashlib {
template <>
struct hash_top_ops<RTLIL::SigBit> {
static inline bool cmp(const RTLIL::SigBit &a, const RTLIL::SigBit &b) {
return a == b;
}
static inline Hasher hash(const RTLIL::SigBit sb) {
return sb.hash_top();
}
};
};
struct RTLIL::SigSpecIterator
@ -937,7 +974,7 @@ struct RTLIL::SigSpec
{
private:
int width_;
unsigned long hash_;
Hasher::hash_t hash_;
std::vector<RTLIL::SigChunk> chunks_; // LSB at index 0
std::vector<RTLIL::SigBit> bits_; // LSB at index 0
@ -978,11 +1015,6 @@ public:
SigSpec(const std::set<RTLIL::SigBit> &bits);
explicit SigSpec(bool bit);
size_t get_hash() const {
if (!hash_) hash();
return hash_;
}
inline const std::vector<RTLIL::SigChunk> &chunks() const { pack(); return chunks_; }
inline const std::vector<RTLIL::SigBit> &bits() const { inline_unpack(); return bits_; }
@ -1089,7 +1121,7 @@ public:
operator std::vector<RTLIL::SigBit>() const { return bits(); }
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < width_ ? (*this)[offset] : defval; }
unsigned int hash() const { if (!hash_) updhash(); return hash_; };
Hasher hash_into(Hasher h) const { if (!hash_) updhash(); h.eat(hash_); return h; }
#ifndef NDEBUG
void check(Module *mod = nullptr) const;
@ -1130,8 +1162,8 @@ struct RTLIL::Selection
struct RTLIL::Monitor
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
Monitor() {
static unsigned int hashidx_count = 123456789;
@ -1153,8 +1185,8 @@ struct define_map_t;
struct RTLIL::Design
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
pool<RTLIL::Monitor*> monitors;
dict<std::string, std::string> scratchpad;
@ -1258,8 +1290,8 @@ struct RTLIL::Design
struct RTLIL::Module : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
void add(RTLIL::Wire *wire);
@ -1616,8 +1648,8 @@ void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire);
struct RTLIL::Wire : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
// use module->addWire() and module->remove() to create or destroy wires
@ -1655,8 +1687,8 @@ inline int GetSize(RTLIL::Wire *wire) {
struct RTLIL::Memory : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
Memory();
@ -1670,8 +1702,8 @@ struct RTLIL::Memory : public RTLIL::AttrObject
struct RTLIL::Cell : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
// use module->addCell() and module->remove() to create or destroy cells
@ -1780,8 +1812,8 @@ struct RTLIL::SyncRule
struct RTLIL::Process : public RTLIL::AttrObject
{
unsigned int hashidx_;
unsigned int hash() const { return hashidx_; }
Hasher::hash_t hashidx_;
Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected:
// use module->addProcess() and module->remove() to create or destroy processes
@ -1825,10 +1857,25 @@ inline bool RTLIL::SigBit::operator!=(const RTLIL::SigBit &other) const {
return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data));
}
inline unsigned int RTLIL::SigBit::hash() const {
if (wire)
return mkhash_add(wire->name.hash(), offset);
return data;
inline Hasher RTLIL::SigBit::hash_into(Hasher h) const {
if (wire) {
h.eat(offset);
h.eat(wire->name);
return h;
}
h.eat(data);
return h;
}
inline Hasher RTLIL::SigBit::hash_top() const {
Hasher h;
if (wire) {
h.force(hashlib::legacy::djb2_add(wire->name.index_, offset));
return h;
}
h.force(data);
return h;
}
inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const {

View file

@ -169,8 +169,11 @@ public:
return !(*this == other);
}
int hash() const {
return mkhash(scope_name.hash(), hash_ptr_ops::hash(target));
Hasher hash_into(Hasher h) const
{
h.eat(scope_name);
h.eat(target);
return h;
}
bool valid() const {
@ -322,7 +325,7 @@ struct ModuleItem {
Cell *cell() const { return type == Type::Cell ? static_cast<Cell *>(ptr) : nullptr; }
bool operator==(const ModuleItem &other) const { return ptr == other.ptr && type == other.type; }
unsigned int hash() const { return (uintptr_t)ptr; }
Hasher hash_into(Hasher h) const { h.eat(ptr); return h; }
};
static inline void log_dump_val_worker(typename IdTree<ModuleItem>::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); }

View file

@ -29,7 +29,11 @@ struct SigPool
struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
unsigned int hash() const { return first->name.hash() + second; }
Hasher hash_into(Hasher h) const {
h.eat(first->name);
h.eat(second);
return h;
}
};
pool<bitDef_t> bits;
@ -143,7 +147,11 @@ struct SigSet
struct bitDef_t : public std::pair<RTLIL::Wire*, int> {
bitDef_t() : std::pair<RTLIL::Wire*, int>(NULL, 0) { }
bitDef_t(const RTLIL::SigBit &bit) : std::pair<RTLIL::Wire*, int>(bit.wire, bit.offset) { }
unsigned int hash() const { return first->name.hash() + second; }
Hasher hash_into(Hasher h) const {
h.eat(first->name);
h.eat(second);
return h;
}
};
dict<bitDef_t, std::set<T, Compare>> bits;

View file

@ -36,7 +36,6 @@ struct TimingInfo
explicit NameBit(const RTLIL::SigBit &b) : name(b.wire->name), offset(b.offset) {}
bool operator==(const NameBit& nb) const { return nb.name == name && nb.offset == offset; }
bool operator!=(const NameBit& nb) const { return !operator==(nb); }
unsigned int hash() const { return mkhash_add(name.hash(), offset); }
std::optional<SigBit> get_connection(RTLIL::Cell *cell) {
if (!cell->hasPort(name))
return {};
@ -45,6 +44,11 @@ struct TimingInfo
return {};
return port[offset];
}
Hasher hash_into(Hasher h) const {
h.eat(name);
h.eat(offset);
return h;
}
};
struct BitBit
{
@ -52,7 +56,11 @@ struct TimingInfo
BitBit(const NameBit &first, const NameBit &second) : first(first), second(second) {}
BitBit(const SigBit &first, const SigBit &second) : first(first), second(second) {}
bool operator==(const BitBit& bb) const { return bb.first == first && bb.second == second; }
unsigned int hash() const { return mkhash_add(first.hash(), second.hash()); }
Hasher hash_into(Hasher h) const {
h.eat(first);
h.eat(second);
return h;
}
};
struct ModuleTiming

View file

@ -31,17 +31,17 @@ YOSYS_NAMESPACE_BEGIN
// A map-like container, but you can save and restore the state
// ------------------------------------------------
template<typename Key, typename T, typename OPS = hash_ops<Key>>
template<typename Key, typename T>
struct stackmap
{
private:
std::vector<dict<Key, T*, OPS>> backup_state;
dict<Key, T, OPS> current_state;
std::vector<dict<Key, T*>> backup_state;
dict<Key, T> current_state;
static T empty_tuple;
public:
stackmap() { }
stackmap(const dict<Key, T, OPS> &other) : current_state(other) { }
stackmap(const dict<Key, T> &other) : current_state(other) { }
template<typename Other>
void operator=(const Other &other)
@ -94,7 +94,7 @@ public:
current_state.erase(k);
}
const dict<Key, T, OPS> &stdmap()
const dict<Key, T> &stdmap()
{
return current_state;
}
@ -128,7 +128,7 @@ public:
// A simple class for topological sorting
// ------------------------------------------------
template <typename T, typename C = std::less<T>, typename OPS = hash_ops<T>> class TopoSort
template <typename T, typename C = std::less<T>> class TopoSort
{
public:
// We use this ordering of the edges in the adjacency matrix for

View file

@ -96,6 +96,7 @@ std::set<std::string> yosys_input_files, yosys_output_files;
bool memhasher_active = false;
uint32_t memhasher_rng = 123456;
std::vector<void*> memhasher_store;
uint32_t Hasher::fudge = 0;
std::string yosys_share_dirname;
std::string yosys_abc_executable;

View file

@ -134,8 +134,7 @@ YOSYS_NAMESPACE_BEGIN
// Note: All headers included in hashlib.h must be included
// outside of YOSYS_NAMESPACE before this or bad things will happen.
#ifdef HASHLIB_H
# undef HASHLIB_H
# include "kernel/hashlib.h"
# error "You've probably included hashlib.h under two namespace paths. Bad idea."
#else
# include "kernel/hashlib.h"
# undef HASHLIB_H
@ -153,6 +152,15 @@ using std::get;
using std::min;
using std::max;
using hashlib::Hasher;
using hashlib::run_hash;
using hashlib::hash_ops;
using hashlib::mkhash_xorshift;
using hashlib::dict;
using hashlib::idict;
using hashlib::pool;
using hashlib::mfp;
// A primitive shared string implementation that does not
// move its .c_str() when the object is copied or moved.
struct shared_str {
@ -163,22 +171,12 @@ struct shared_str {
const char *c_str() const { return content->c_str(); }
const string &str() const { return *content; }
bool operator==(const shared_str &other) const { return *content == *other.content; }
unsigned int hash() const { return hashlib::hash_ops<std::string>::hash(*content); }
Hasher hash_into(Hasher h) const {
h.eat(*content);
return h;
}
};
using hashlib::mkhash;
using hashlib::mkhash_init;
using hashlib::mkhash_add;
using hashlib::mkhash_xorshift;
using hashlib::hash_ops;
using hashlib::hash_cstr_ops;
using hashlib::hash_ptr_ops;
using hashlib::hash_obj_ops;
using hashlib::dict;
using hashlib::idict;
using hashlib::pool;
using hashlib::mfp;
namespace RTLIL {
struct IdString;
struct Const;
@ -357,10 +355,6 @@ RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std:
static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })()
namespace ID = RTLIL::ID;
namespace hashlib {
template<> struct hash_ops<RTLIL::State> : hash_ops<int> {};
}
YOSYS_NAMESPACE_END

View file

@ -35,7 +35,10 @@ struct IdPath : public std::vector<RTLIL::IdString>
bool has_address() const { int tmp; return get_address(tmp); };
bool get_address(int &addr) const;
int hash() const { return hashlib::hash_ops<std::vector<RTLIL::IdString>>::hash(*this); }
Hasher hash_into(Hasher h) const {
h.eat(static_cast<const std::vector<RTLIL::IdString>&&>(*this));
return h;
}
};
struct WitnessHierarchyItem {