diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index a56bfc03639..397d3da8ed8 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -47,7 +47,7 @@ struct Scheduler { struct Vertex { T *data; Vertex *prev, *next; - pool preds, succs; + pool preds, succs; Vertex() : data(NULL), prev(this), next(this) {} Vertex(T *data) : data(data), prev(NULL), next(NULL) {} @@ -300,10 +300,10 @@ struct FlowGraph { }; std::vector nodes; - dict> wire_comb_defs, wire_sync_defs, wire_uses; - dict, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses; + dict> wire_comb_defs, wire_sync_defs, wire_uses; + dict> node_comb_defs, node_sync_defs, node_uses; dict wire_def_inlinable; - dict> wire_use_inlinable; + dict> wire_use_inlinable; dict bit_has_state; ~FlowGraph() @@ -365,7 +365,7 @@ struct FlowGraph { return false; } - bool is_inlinable(const RTLIL::Wire *wire, const pool &nodes) const + bool is_inlinable(const RTLIL::Wire *wire, const pool &nodes) const { // Can the wire be inlined, knowing that the given nodes are reachable? if (nodes.size() != 1) @@ -3080,7 +3080,7 @@ struct CxxrtlWorker { // without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only // a single delta cycle. Scheduler scheduler; - dict::Vertex*, hash_ptr_ops> node_vertex_map; + dict::Vertex*> node_vertex_map; for (auto node : flow.nodes) node_vertex_map[node] = scheduler.add(node); for (auto node_comb_def : flow.node_comb_defs) { @@ -3095,7 +3095,7 @@ struct CxxrtlWorker { // Find out whether the order includes any feedback arcs. std::vector node_order; - pool evaluated_nodes; + pool evaluated_nodes; pool feedback_wires; for (auto vertex : scheduler.schedule()) { auto node = vertex->data; @@ -3139,7 +3139,7 @@ struct CxxrtlWorker { } // Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users. - pool worklist; + pool worklist; for (auto node : flow.nodes) { if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type)) worklist.insert(node); // node evaluates a submodule @@ -3159,8 +3159,8 @@ struct CxxrtlWorker { worklist.insert(node); // node drives public wires } } - dict> live_wires; - pool live_nodes; + dict> live_wires; + pool live_nodes; while (!worklist.empty()) { auto node = worklist.pop(); live_nodes.insert(node); @@ -3290,15 +3290,15 @@ struct CxxrtlWorker { // Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members) // and collect reachable wire users. - pool worklist; + pool worklist; for (auto node : flow.nodes) { if (flow.node_comb_defs.count(node)) for (auto wire : flow.node_comb_defs[node]) if (debug_wire_types[wire].is_outline()) worklist.insert(node); // node drives outline } - dict> debug_live_wires; - pool debug_live_nodes; + dict> debug_live_wires; + pool debug_live_nodes; while (!worklist.empty()) { auto node = worklist.pop(); debug_live_nodes.insert(node); diff --git a/examples/cxx-api/scopeinfo_example.cc b/examples/cxx-api/scopeinfo_example.cc index f163dff9eee..0882ba80484 100644 --- a/examples/cxx-api/scopeinfo_example.cc +++ b/examples/cxx-api/scopeinfo_example.cc @@ -90,7 +90,7 @@ struct ScopeinfoExamplePass : public Pass { // Shuffle wires so this example produces more interesting outputs std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) { - return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5); + return mkhash_xorshift(run_hash(a->name) * 0x2c9277b5) < mkhash_xorshift(run_hash(b->name) * 0x2c9277b5); }); ModuleHdlnameIndex index(module); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index bdff015e3f4..1a72e62850e 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -177,7 +177,7 @@ namespace AST { // for dict<> and pool<> unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher hash_acc(Hasher h) const { h.acc(hashidx_); return h; } // this nodes type AstNodeType type; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index 0cdf772a8f9..f496f60494c 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -611,7 +611,7 @@ RTLIL::SigSpec VerificImporter::operatorInportCase(Instance *inst, const char *p } } -RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool *any_all_nets) +RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool *any_all_nets) { RTLIL::SigSpec sig; RTLIL::Wire *dummy_wire = NULL; @@ -1567,9 +1567,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma module->fixup_ports(); - dict init_nets; - pool anyconst_nets, anyseq_nets; - pool allconst_nets, allseq_nets; + dict init_nets; + pool anyconst_nets, anyseq_nets; + pool allconst_nets, allseq_nets; any_all_nets.clear(); FOREACH_NET_OF_NETLIST(nl, mi, net) @@ -1832,10 +1832,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma module->connect(net_map_at(net), module->Anyseq(new_verific_id(net))); #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT - pool sva_asserts; - pool sva_assumes; - pool sva_covers; - pool sva_triggers; + pool sva_asserts; + pool sva_assumes; + pool sva_covers; + pool sva_triggers; #endif pool past_ffs; diff --git a/frontends/verific/verific.h b/frontends/verific/verific.h index 0b9616e1944..7974495d0a1 100644 --- a/frontends/verific/verific.h +++ b/frontends/verific/verific.h @@ -71,7 +71,7 @@ struct VerificImporter std::map net_map; std::map sva_posedge_map; - pool any_all_nets; + pool any_all_nets; bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific; bool mode_autocover, mode_fullinit; @@ -89,7 +89,7 @@ struct VerificImporter RTLIL::SigSpec operatorInput2(Verific::Instance *inst); RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname); RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname); - RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool *any_all_nets = nullptr); + RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool *any_all_nets = nullptr); bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name); bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name); diff --git a/frontends/verific/verificsva.cc b/frontends/verific/verificsva.cc index ef8247e83df..860d3c16630 100644 --- a/frontends/verific/verificsva.cc +++ b/frontends/verific/verificsva.cc @@ -1051,7 +1051,7 @@ struct VerificSvaImporter msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); } - dict check_expression_cache; + dict check_expression_cache; bool check_expression(Net *net, bool raise_error = false) { diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index c1ceac14ca8..3814f4672b2 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -30,7 +30,7 @@ struct BitPatternPool int width; struct bits_t { std::vector 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_acc(Hasher h) const { if (!cached_hash) - cached_hash = hash_ops>::hash(bitdata); - return cached_hash; + cached_hash = run_hash(bitdata); + h.acc(cached_hash); + return h; } }; pool database; diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index de0a4939430..81b486c79f8 100644 --- a/kernel/cellaigs.cc +++ b/kernel/cellaigs.cc @@ -39,13 +39,13 @@ bool AigNode::operator==(const AigNode &other) const return true; } -unsigned int AigNode::hash() const +Hasher AigNode::hash_acc(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.acc(portname); + h.acc(portbit); + h.acc(inverter); + h.acc(left_parent); + h.acc(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_acc(Hasher h) const { - return hash_ops::hash(name); + h.acc(name); + return h; } struct AigMaker diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h index 8f6d69ba62b..bdb8b3c073c 100644 --- a/kernel/cellaigs.h +++ b/kernel/cellaigs.h @@ -34,7 +34,7 @@ struct AigNode AigNode(); bool operator==(const AigNode &other) const; - unsigned int hash() const; + Hasher hash_acc(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_acc(Hasher h) const; }; YOSYS_NAMESPACE_END diff --git a/kernel/driver.cc b/kernel/driver.cc index 0c0dc9023d0..be0eba858f8 100644 --- a/kernel/driver.cc +++ b/kernel/driver.cc @@ -18,6 +18,7 @@ */ #include "kernel/yosys.h" +#include "kernel/hashlib.h" #include "libs/sha1/sha1.h" #include "libs/cxxopts/include/cxxopts.hpp" #include @@ -276,6 +277,8 @@ int main(int argc, char **argv) options.add_options("developer") ("X,trace", "enable tracing of core data structure changes. for debugging") ("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging") + ("hash-seed", "mix up hashing values with , for extreme optimization and testing", + cxxopts::value(), "") ("A,abort", "will call abort() at the end of the script. for debugging") ("x,experimental", "do not print warnings for the experimental ", cxxopts::value>(), "") @@ -427,6 +430,10 @@ int main(int argc, char **argv) if (result.count("infile")) { frontend_files = result["infile"].as>(); } + if (result.count("hash-seed")) { + int seed = result["hash-seed"].as(); + Hasher::set_fudge((Hasher::hash_t)seed); + } if (log_errfile == NULL) { log_files.push_back(stdout); diff --git a/kernel/drivertools.h b/kernel/drivertools.h index 079701c35cc..0e7b872e4ba 100644 --- a/kernel/drivertools.h +++ b/kernel/drivertools.h @@ -74,10 +74,8 @@ struct DriveBitWire return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(wire->name.hash(), offset); - } + Hasher hash_acc(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_acc(Hasher h) const; + }; @@ -133,10 +129,7 @@ struct DriveBitMarker return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(marker, offset); - } + Hasher hash_acc(Hasher h) const; }; @@ -171,10 +164,7 @@ struct DriveBitMultiple return multiple_ == other.multiple_; } - unsigned int hash() const - { - return multiple_.hash(); - } + Hasher hash_acc(Hasher h) const; }; struct DriveBit @@ -362,32 +352,7 @@ struct DriveBit return *this; } - unsigned int hash() const - { - unsigned int inner; - 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; - } - return mkhash((unsigned int)type_, inner); - } + Hasher hash_acc(Hasher h) const; bool operator==(const DriveBit &other) const { @@ -508,10 +473,7 @@ struct DriveChunkWire return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(mkhash(wire->name.hash(), width), offset); - } + Hasher hash_acc(Hasher h) const; explicit operator SigChunk() const { @@ -569,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_acc(Hasher h) const; }; @@ -613,10 +572,7 @@ struct DriveChunkMarker return offset < other.offset; } - unsigned int hash() const - { - return mkhash_add(mkhash(marker, width), offset); - } + Hasher hash_acc(Hasher h) const; }; struct DriveChunkMultiple @@ -656,10 +612,7 @@ struct DriveChunkMultiple return false; // TODO implement, canonicalize order } - unsigned int hash() const - { - return mkhash(width_, multiple_.hash()); - } + Hasher hash_acc(Hasher h) const; }; struct DriveChunk @@ -910,32 +863,7 @@ struct DriveChunk bool try_append(DriveBit const &bit); bool try_append(DriveChunk const &chunk); - unsigned int hash() const - { - unsigned int inner; - 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; - } - return mkhash((unsigned int)type_, inner); - } + Hasher hash_acc(Hasher h) const; bool operator==(const DriveChunk &other) const { @@ -1138,17 +1066,19 @@ struct DriveSpec 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 { + DriveSpec *that = (DriveSpec*)this; pack(); - hash_ = hash_ops>().hash(chunks_); - hash_ |= (hash_ == 0); - return hash_; + that->hash_ = run_hash(chunks_); + that->hash_ |= (that->hash_ == 0); } + Hasher hash_acc(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(); } @@ -1181,7 +1111,8 @@ struct DriverMap 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; } + // unsigned int hash() const { return id; } + Hasher hash_acc(Hasher h) const; }; // Essentially a dict> but using less memory // and fewer allocations @@ -1327,6 +1258,133 @@ struct DriverMap } }; +inline Hasher DriveBitWire::hash_acc(Hasher h) const +{ + h.acc(wire->name); + h.acc(offset); + return h; +} + +inline Hasher DriveBitPort::hash_acc(Hasher h) const +{ + h.acc(cell->name); + h.acc(port); + h.acc(offset); + return h; +} + +inline Hasher DriveBitMarker::hash_acc(Hasher h) const +{ + h.acc(marker); + h.acc(offset); + return h; +} + +inline Hasher DriveBitMultiple::hash_acc(Hasher h) const +{ + h.acc(multiple_); + return h; +} + +inline Hasher DriveBit::hash_acc(Hasher h) const +{ + switch (type_) { + case DriveType::NONE: + h.acc(0); + break; + case DriveType::CONSTANT: + h.acc(constant_); + break; + case DriveType::WIRE: + h.acc(wire_); + break; + case DriveType::PORT: + h.acc(port_); + break; + case DriveType::MARKER: + h.acc(marker_); + break; + case DriveType::MULTIPLE: + h.acc(multiple_); + break; + } + h.acc(type_); + return h; +} + +inline Hasher DriveChunkWire::hash_acc(Hasher h) const +{ + h.acc(wire->name); + h.acc(width); + h.acc(offset); + return h; +} + +inline Hasher DriveChunkPort::hash_acc(Hasher h) const +{ + h.acc(cell->name); + h.acc(port); + h.acc(width); + h.acc(offset); + return h; +} + +inline Hasher DriveChunkMarker::hash_acc(Hasher h) const +{ + h.acc(marker); + h.acc(width); + h.acc(offset); + return h; +} + +inline Hasher DriveChunkMultiple::hash_acc(Hasher h) const +{ + h.acc(width_); + h.acc(multiple_); + return h; +} + +inline Hasher DriveChunk::hash_acc(Hasher h) const +{ + switch (type_) { + case DriveType::NONE: + h.acc(0); + break; + case DriveType::CONSTANT: + h.acc(constant_); + break; + case DriveType::WIRE: + h.acc(wire_); + break; + case DriveType::PORT: + h.acc(port_); + break; + case DriveType::MARKER: + h.acc(marker_); + break; + case DriveType::MULTIPLE: + h.acc(multiple_); + break; + } + h.acc(type_); + return h; +} + +inline Hasher DriveSpec::hash_acc(Hasher h) const +{ + if (hash_ == 0) + updhash(); + + h.acc(hash_); + return h; +} + +inline Hasher DriverMap::DriveBitId::hash_acc(Hasher h) const +{ + h.acc(id); + return h; +} + YOSYS_NAMESPACE_END #endif diff --git a/kernel/functional.h b/kernel/functional.h index 61b303e0b48..b80c77be45b 100644 --- a/kernel/functional.h +++ b/kernel/functional.h @@ -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_acc(Hasher h) const { h.acc(_v); return h; } }; class IR; class Factory; @@ -225,8 +225,10 @@ namespace Functional { const RTLIL::Const &as_const() const { return std::get(_extra); } std::pair as_idstring_pair() const { return std::get>(_extra); } int as_int() const { return std::get(_extra); } - int hash() const { - return mkhash((unsigned int) _fn, mkhash(_extra)); + Hasher hash_acc(Hasher h) const { + h.acc((unsigned int) _fn); + h.acc(_extra); + return h; } bool operator==(NodeData const &other) const { return _fn == other._fn && _extra == other._extra; diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 859115829d2..13b53e38332 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -17,27 +17,49 @@ #include #include #include - +#include #include namespace hashlib { +/** + * HASHING + * + * 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. + */ + +// 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_factor = 3; -// The XOR version of DJB2 -inline unsigned int mkhash(unsigned int a, unsigned int b) { - return ((a << 5) + a) ^ b; -} +#define DJB2_32 -// traditionally 5381 is used as starting value for the djb2 hash -const unsigned int mkhash_init = 5381; -// 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; -} + +template +struct hash_ops; inline unsigned int mkhash_xorshift(unsigned int a) { if (sizeof(a) == 4) { @@ -53,62 +75,92 @@ inline unsigned int mkhash_xorshift(unsigned int a) { return a; } -template struct hash_ops { - static inline bool cmp(const T &a, const T &b) { - return a == b; +class Hasher { + public: + #ifdef DJB2_32 + using hash_t = uint32_t; + #endif + #ifdef DJB2_64 + using hash_t = uint64_t; + #endif + + Hasher() { + // 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 mkhash(uint32_t a, uint32_t b) { + uint32_t hash = ((a << 5) + a) ^ b; + hash = mkhash_xorshift(fudge ^ hash); + return hash; } - static inline unsigned int hash(const T &a) { - return a.hash(); + 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; + } + [[nodiscard]] + hash_t yield() { + return (hash_t)state; } -}; -struct hash_int_ops { template - static inline bool cmp(T a, T b) { - return a == b; + void acc(T&& t) { + *this = hash_ops>>::hash_acc(std::forward(t), *this); } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(bool a) { - return a ? 1 : 0; - } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(int32_t a) { - return a; - } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(int64_t a) { - return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); - } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(uint32_t a) { - return a; + template + void acc(const T& t) { + *this = hash_ops::hash_acc(t, *this); } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(uint64_t a) { - return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); + + void commutative_acc(uint32_t t) { + state ^= t; } + }; -template<> struct hash_ops { - static inline bool cmp(const std::string &a, const std::string &b) { +template +struct hash_ops { + static inline bool cmp(const T &a, const T &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_acc(const T &a, Hasher h) { + if constexpr (std::is_same_v) { + h.hash32(a ? 1 : 0); + return h; + } else if constexpr (std::is_integral_v) { + 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) { + using u_type = std::underlying_type_t; + return hash_ops::hash_acc((u_type) a, h); + } else if constexpr (std::is_pointer_v) { + return hash_ops::hash_acc((uintptr_t) a, h); + } else if constexpr (std::is_same_v) { + for (auto c : a) + h.hash32(c); + return h; + } else { + return a.hash_acc(h); + } } }; @@ -116,8 +168,10 @@ template struct hash_ops> { static inline bool cmp(std::pair a, std::pair b) { return a == b; } - static inline unsigned int hash(std::pair a) { - return mkhash(hash_ops

::hash(a.first), hash_ops::hash(a.second)); + 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; } }; @@ -126,13 +180,15 @@ template struct hash_ops> { return a == b; } template - static inline typename std::enable_if::type hash(std::tuple) { - return mkhash_init; + static inline typename std::enable_if::type hash_acc(std::tuple, Hasher h) { + return h; } template - static inline typename std::enable_if::type hash(std::tuple a) { + static inline typename std::enable_if::type hash_acc(std::tuple a, Hasher h) { typedef hash_ops>::type> element_ops_t; - return mkhash(hash(a), element_ops_t::hash(std::get(a))); + h = hash_acc(a, h); + h = element_ops_t::hash_acc(std::get(a), h); + return h; } }; @@ -140,14 +196,25 @@ template struct hash_ops> { static inline bool cmp(std::vector a, std::vector b) { return a == b; } - static inline unsigned int hash(std::vector a) { - unsigned int h = mkhash_init; + static inline Hasher hash_acc(std::vector a, Hasher h) { + h.acc(a.size()); for (auto k : a) - h = mkhash(h, hash_ops::hash(k)); + h.acc(k); return h; } }; +template struct hash_ops> { + static inline bool cmp(std::array a, std::array b) { + return a == b; + } + static inline Hasher hash_acc(std::array a, Hasher h) { + for (const auto& k : a) + h = hash_ops::hash_acc(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++) @@ -155,20 +222,21 @@ struct hash_cstr_ops { return false; return true; } - static inline unsigned int hash(const char *a) { - unsigned int hash = mkhash_init; + static inline Hasher hash_acc(const char *a, Hasher h) { while (*a) - hash = mkhash(hash, *(a++)); - return hash; + 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 unsigned int hash(const void *a) { - return (uintptr_t)a; + static inline Hasher hash_acc(const void *a, Hasher h) { + return hash_ops::hash_acc((uintptr_t)a, h); } }; @@ -177,22 +245,37 @@ struct hash_obj_ops { return a == b; } template - static inline unsigned int hash(const T *a) { - return a ? a->hash() : 0; + 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 + * hashes together with XORs or worse can destroy + * desirable qualities of the hash function + */ template -inline unsigned int mkhash(const T &v) { - return hash_ops().hash(v); +[[nodiscard]] +Hasher::hash_t run_hash(const T& obj) { + Hasher h; + h.acc(obj); + return h.yield(); } +// #ifdef OTHER_HASH... + +// [[deprecated]] +// inline unsigned int mkhash_add(unsigned int a, unsigned int b) { +// return mkhash(a, b); +// } + template<> struct hash_ops { 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_acc(std::monostate, Hasher h) { + return h; } }; @@ -200,9 +283,10 @@ template struct hash_ops> { static inline bool cmp(std::variant a, std::variant b) { return a == b; } - static inline unsigned int hash(std::variant a) { - unsigned int h = std::visit([](const auto &v) { return mkhash(v); }, a); - return mkhash(a.index(), h); + static inline Hasher hash_acc(std::variant a, Hasher h) { + std::visit([& h](const auto &v) { h.acc(v); }, a); + h.acc(a.index()); + return h; } }; @@ -210,11 +294,12 @@ template struct hash_ops> { static inline bool cmp(std::optional a, std::optional b) { return a == b; } - static inline unsigned int hash(std::optional a) { + static inline Hasher hash_acc(std::optional a, Hasher h) { if(a.has_value()) - return mkhash(*a); + h.acc(*a); else - return 0; + h.acc(0); + return h; } }; @@ -246,14 +331,13 @@ inline int hashtable_size(int min_size) throw std::length_error("hash table exceeded maximum size."); } -template> class dict; -template> class idict; -template> class pool; -template> class mfp; +template class dict; +template class idict; +template class pool; +template class mfp; -template -class dict -{ +template +class dict { struct entry_t { std::pair udata; @@ -267,7 +351,7 @@ class dict std::vector hashtable; std::vector entries; - OPS ops; + hash_ops ops; #ifdef NDEBUG static inline void do_assert(bool) { } @@ -277,11 +361,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 = run_hash(key) % (unsigned int)(hashtable.size()); return hash; } @@ -292,13 +376,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 +405,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 +431,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 +451,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(key, T()), -1); @@ -380,7 +464,7 @@ class dict return entries.size() - 1; } - int do_insert(const std::pair &value, int &hash) + int do_insert(const std::pair &value, Hasher::hash_t &hash) { if (hashtable.empty()) { entries.emplace_back(value, -1); @@ -393,7 +477,7 @@ class dict return entries.size() - 1; } - int do_insert(std::pair &&rvalue, int &hash) + int do_insert(std::pair &&rvalue, Hasher::hash_t &hash) { if (hashtable.empty()) { auto key = rvalue.first; @@ -505,7 +589,7 @@ class dict std::pair 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(this, i), false); @@ -515,7 +599,7 @@ class dict std::pair insert(const std::pair &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(this, i), false); @@ -525,7 +609,7 @@ class dict std::pair insert(std::pair &&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(this, i), false); @@ -535,7 +619,7 @@ class dict std::pair 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(this, i), false); @@ -545,7 +629,7 @@ class dict std::pair 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(this, i), false); @@ -555,7 +639,7 @@ class dict std::pair 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(this, i), false); @@ -565,7 +649,7 @@ class dict std::pair 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(this, i), false); @@ -575,35 +659,35 @@ class dict 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 +696,7 @@ class dict 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 +705,7 @@ class dict 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 +714,7 @@ class dict 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 +723,7 @@ class dict 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 +732,7 @@ class dict 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(key, T()), hash); @@ -683,11 +767,13 @@ class dict return !operator==(other); } - unsigned int hash() const { - unsigned int h = mkhash_init; - for (auto &entry : entries) { - h ^= hash_ops::hash(entry.udata.first); - h ^= hash_ops::hash(entry.udata.second); + Hasher hash_acc(Hasher h) const { + h.acc(entries.size()); + for (auto &it : entries) { + Hasher entry_hash; + entry_hash.acc(it.udata.first); + entry_hash.acc(it.udata.second); + h.commutative_acc(entry_hash.yield()); } return h; } @@ -706,10 +792,10 @@ class dict const_iterator end() const { return const_iterator(nullptr, -1); } }; -template +template class pool { - template friend class idict; + template friend class idict; protected: struct entry_t @@ -724,7 +810,7 @@ class pool std::vector hashtable; std::vector entries; - OPS ops; + hash_ops ops; #ifdef NDEBUG static inline void do_assert(bool) { } @@ -734,11 +820,11 @@ class pool } #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 = run_hash(key) % (unsigned int)(hashtable.size()); return hash; } @@ -749,13 +835,13 @@ class pool 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 +862,7 @@ class pool 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 +886,7 @@ class pool 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 +906,7 @@ class pool 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 +919,7 @@ class pool 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(rvalue), -1); @@ -940,7 +1026,7 @@ class pool std::pair 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(this, i), false); @@ -950,7 +1036,7 @@ class pool std::pair 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(this, i), false); @@ -966,35 +1052,35 @@ class pool 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 +1089,7 @@ class pool 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 +1098,7 @@ class pool 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 +1137,12 @@ class pool 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_acc(Hasher h) const { + h.acc(entries.size()); + for (auto &it : entries) { + h.commutative_acc(run_hash(it.udata)); + } + return h; } void reserve(size_t n) { entries.reserve(n); } @@ -1072,10 +1159,10 @@ class pool const_iterator end() const { return const_iterator(nullptr, -1); } }; -template +template class idict { - pool database; + pool database; public: class const_iterator @@ -1105,7 +1192,7 @@ class idict 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 +1201,7 @@ class idict 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 +1210,7 @@ class idict 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 +1219,7 @@ class idict 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; } @@ -1169,14 +1256,14 @@ class idict * mfp stands for "merge, find, promote" * i-prefixed methods operate on indices in parents */ -template +template class mfp { - mutable idict database; + mutable idict database; mutable std::vector parents; public: - typedef typename idict::const_iterator const_iterator; + typedef typename idict::const_iterator const_iterator; constexpr mfp() { diff --git a/kernel/log.h b/kernel/log.h index 4b90cf9dceb..e26ef072cbc 100644 --- a/kernel/log.h +++ b/kernel/log.h @@ -363,13 +363,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 static inline void log_dump_val_worker(dict &v); -template static inline void log_dump_val_worker(pool &v); +template static inline void log_dump_val_worker(dict &v); +template static inline void log_dump_val_worker(pool &v); template static inline void log_dump_val_worker(std::vector &v); template static inline void log_dump_val_worker(T *ptr); -template -static inline void log_dump_val_worker(dict &v) { +template +static inline void log_dump_val_worker(dict &v) { log("{"); bool first = true; for (auto &it : v) { @@ -382,8 +382,8 @@ static inline void log_dump_val_worker(dict &v) { log(" }"); } -template -static inline void log_dump_val_worker(pool &v) { +template +static inline void log_dump_val_worker(pool &v) { log("{"); bool first = true; for (auto &it : v) { diff --git a/kernel/modtools.h b/kernel/modtools.h index 34a23b37981..1afa0ad502a 100644 --- a/kernel/modtools.h +++ b/kernel/modtools.h @@ -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_acc(Hasher h) const { + h.acc(cell->name); + h.acc(port); + h.acc(offset); + return h; } }; @@ -57,6 +60,8 @@ struct ModIndex : public RTLIL::Monitor { bool is_input, is_output; pool ports; + // SigBitInfo() : SigBitInfo{} {} + // SigBitInfo& operator=(const SigBitInfo&) = default; SigBitInfo() : is_input(false), is_output(false) { } @@ -304,6 +309,8 @@ struct ModWalker RTLIL::Cell *cell; RTLIL::IdString port; int offset; + PortBit(Cell* c, IdString p, int o) : cell(c), port(p), offset(o) {} + // PortBit& operator=(const PortBit&) = default; bool operator<(const PortBit &other) const { if (cell != other.cell) @@ -317,8 +324,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_acc(Hasher h) const { + h.acc(cell->name); + h.acc(port); + h.acc(offset); + return h; } }; @@ -355,7 +365,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]); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 180982a7cba..b60315678e6 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -35,7 +35,7 @@ YOSYS_NAMESPACE_BEGIN bool RTLIL::IdString::destruct_guard_ok = false; RTLIL::IdString::destruct_guard_t RTLIL::IdString::destruct_guard; std::vector RTLIL::IdString::global_id_storage_; -dict RTLIL::IdString::global_id_index_; +dict RTLIL::IdString::global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT std::vector RTLIL::IdString::global_refcount_storage_; std::vector RTLIL::IdString::global_free_idx_list_; @@ -4461,17 +4461,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.acc(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.acc(c.wire->name.index_); + h.acc(c.offset); + h.acc(c.width); } - + that->hash_ = h.yield(); if (that->hash_ == 0) that->hash_ = 1; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 0f3984ab8fc..f20f6dc92b5 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -95,7 +95,7 @@ namespace RTLIL } destruct_guard; static std::vector global_id_storage_; - static dict global_id_index_; + static dict global_id_index_; #ifndef YOSYS_NO_IDS_REFCNT static std::vector global_refcount_storage_; static std::vector global_free_idx_list_; @@ -360,8 +360,8 @@ namespace RTLIL *this = IdString(); } - unsigned int hash() const { - return index_; + Hasher hash_acc(Hasher h) const { + return hash_ops::hash_acc(index_, h); } // The following is a helper key_compare class. Instead of for example std::set @@ -796,11 +796,10 @@ struct RTLIL::Const 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_acc(Hasher h) const { + // TODO hash size + for (auto b : *this) + h.acc(b); return h; } }; @@ -889,7 +888,7 @@ 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_acc(Hasher h) const; }; struct RTLIL::SigSpecIterator @@ -930,7 +929,7 @@ struct RTLIL::SigSpec { private: int width_; - unsigned long hash_; + Hasher::hash_t hash_; std::vector chunks_; // LSB at index 0 std::vector bits_; // LSB at index 0 @@ -971,9 +970,10 @@ struct RTLIL::SigSpec SigSpec(const std::set &bits); explicit SigSpec(bool bit); + [[deprecated]] size_t get_hash() const { - if (!hash_) hash(); - return hash_; + log_assert(false && "deprecated"); + return 0; } inline const std::vector &chunks() const { pack(); return chunks_; } @@ -1082,7 +1082,7 @@ struct RTLIL::SigSpec operator std::vector() 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_acc(Hasher h) const { if (!hash_) updhash(); h.acc(hash_); return h; } #ifndef NDEBUG void check(Module *mod = nullptr) const; @@ -1123,8 +1123,8 @@ struct RTLIL::Selection struct RTLIL::Monitor { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + Hasher hash_acc(Hasher h) const { h.acc(hashidx_); return h; } Monitor() { static unsigned int hashidx_count = 123456789; @@ -1146,8 +1146,8 @@ struct define_map_t; struct RTLIL::Design { - unsigned int hashidx_; - unsigned int hash() const { return hashidx_; } + Hasher::hash_t hashidx_; + Hasher hash_acc(Hasher h) const { h.acc(hashidx_); return h; } pool monitors; dict scratchpad; @@ -1251,8 +1251,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_acc(Hasher h) const { h.acc(hashidx_); return h; } protected: void add(RTLIL::Wire *wire); @@ -1602,8 +1602,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_acc(Hasher h) const { h.acc(hashidx_); return h; } protected: // use module->addWire() and module->remove() to create or destroy wires @@ -1641,8 +1641,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_acc(Hasher h) const { h.acc(hashidx_); return h; } Memory(); @@ -1656,8 +1656,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_acc(Hasher h) const { h.acc(hashidx_); return h; } protected: // use module->addCell() and module->remove() to create or destroy cells @@ -1766,8 +1766,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_acc(Hasher h) const { h.acc(hashidx_); return h; } protected: // use module->addProcess() and module->remove() to create or destroy processes @@ -1811,10 +1811,14 @@ 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_acc(Hasher h) const { + if (wire) { + h = wire->name.hash_acc(h); + h.acc(offset); + return h; + } + h.acc(data); + return h; } inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const { diff --git a/kernel/scopeinfo.h b/kernel/scopeinfo.h index 71af70344ce..3affe206248 100644 --- a/kernel/scopeinfo.h +++ b/kernel/scopeinfo.h @@ -318,7 +318,7 @@ struct ModuleItem { Cell *cell() const { return type == Type::Cell ? static_cast(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_acc(Hasher h) const { h.acc(ptr); return h; } }; static inline void log_dump_val_worker(typename IdTree::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); } diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 63f1b51ec84..f3779c37c4e 100644 --- a/kernel/sigtools.h +++ b/kernel/sigtools.h @@ -29,7 +29,11 @@ struct SigPool struct bitDef_t : public std::pair { bitDef_t() : std::pair(NULL, 0) { } bitDef_t(const RTLIL::SigBit &bit) : std::pair(bit.wire, bit.offset) { } - unsigned int hash() const { return first->name.hash() + second; } + Hasher hash_acc(Hasher h) const { + h.acc(first->name); + h.acc(second); + return h; + } }; pool bits; @@ -143,7 +147,11 @@ struct SigSet struct bitDef_t : public std::pair { bitDef_t() : std::pair(NULL, 0) { } bitDef_t(const RTLIL::SigBit &bit) : std::pair(bit.wire, bit.offset) { } - unsigned int hash() const { return first->name.hash() + second; } + Hasher hash_acc(Hasher h) const { + h.acc(first->name); + h.acc(second); + return h; + } }; dict> bits; diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h index e7e4eab6e2e..53aedb3d36d 100644 --- a/kernel/timinginfo.h +++ b/kernel/timinginfo.h @@ -36,7 +36,11 @@ 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); } + Hasher hash_acc(Hasher h) const { + h.acc(name); + h.acc(offset); + return h; + } }; struct BitBit { @@ -44,7 +48,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_acc(Hasher h) const { + h.acc(first); + h.acc(second); + return h; + } }; struct ModuleTiming diff --git a/kernel/utils.h b/kernel/utils.h index 99f327db4cd..07edad07479 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -31,17 +31,17 @@ YOSYS_NAMESPACE_BEGIN // A map-like container, but you can save and restore the state // ------------------------------------------------ -template> +template struct stackmap { private: - std::vector> backup_state; - dict current_state; + std::vector> backup_state; + dict current_state; static T empty_tuple; public: stackmap() { } - stackmap(const dict &other) : current_state(other) { } + stackmap(const dict &other) : current_state(other) { } template void operator=(const Other &other) @@ -94,7 +94,7 @@ struct stackmap current_state.erase(k); } - const dict &stdmap() + const dict &stdmap() { return current_state; } @@ -128,7 +128,7 @@ struct stackmap // A simple class for topological sorting // ------------------------------------------------ -template , typename OPS = hash_ops> class TopoSort +template > class TopoSort { public: // We use this ordering of the edges in the adjacency matrix for diff --git a/kernel/yosys.cc b/kernel/yosys.cc index 374b07d06b6..2a15acc90d2 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -92,6 +92,7 @@ std::set yosys_input_files, yosys_output_files; bool memhasher_active = false; uint32_t memhasher_rng = 123456; std::vector memhasher_store; +uint32_t Hasher::fudge = 0; std::string yosys_share_dirname; std::string yosys_abc_executable; diff --git a/kernel/yosys_common.h b/kernel/yosys_common.h index b0fbc3f1969..71b4ccc71b4 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -157,8 +157,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 @@ -176,6 +175,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 { @@ -186,22 +194,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::hash(*content); } + Hasher hash_acc(Hasher h) const { + h.acc(*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; @@ -240,26 +238,6 @@ using RTLIL::State; using RTLIL::SigChunk; using RTLIL::SigSig; -namespace hashlib { - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; - template<> struct hash_ops : hash_obj_ops {}; -} - void memhasher_on(); void memhasher_off(); void memhasher_do(); @@ -370,10 +348,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 : hash_ops {}; -} - YOSYS_NAMESPACE_END diff --git a/kernel/yw.h b/kernel/yw.h index c2f5921b1d9..a4bae55152b 100644 --- a/kernel/yw.h +++ b/kernel/yw.h @@ -35,7 +35,7 @@ struct IdPath : public std::vector bool has_address() const { int tmp; return get_address(tmp); }; bool get_address(int &addr) const; - int hash() const { return hashlib::hash_ops>::hash(*this); } + Hasher hash_acc(Hasher h) const { h.acc(*this); return h; } }; struct WitnessHierarchyItem { diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index 91a82081ae4..f9460b6f1eb 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -850,7 +850,11 @@ def gen_decl(self, filename): if self.hash_id != None: text += "\n\t\tunsigned int get_hash_py()" text += "\n\t\t{" - text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";" + suffix = f"->{self.hash_id}" if self.hash_id else f"->{self.hash_id}" + if self.hash_id == "": + text += f"\n\t\t\treturn run_hash(*(get_cpp_obj()));" + else: + text += f"\n\t\t\treturn run_hash(get_cpp_obj()->{self.hash_id});" text += "\n\t\t}" text += "\n\t};\n" @@ -951,7 +955,7 @@ def contains_default_constr(self): sources = [ Source("kernel/celltypes",[ - WClass("CellType", link_types.pointer, None, None, "type.hash()", True), + WClass("CellType", link_types.pointer, None, None, "type", True), WClass("CellTypes", link_types.pointer, None, None, None, True) ] ), @@ -965,23 +969,23 @@ def contains_default_constr(self): ] ), Source("kernel/rtlil",[ - WClass("IdString", link_types.ref_copy, None, "str()", "hash()"), - WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"), + WClass("IdString", link_types.ref_copy, None, "str()", ""), + WClass("Const", link_types.ref_copy, None, "as_string()", ""), WClass("AttrObject", link_types.ref_copy, None, None, None), WClass("Selection", link_types.ref_copy, None, None, None), WClass("Monitor", link_types.derive, None, None, None), WClass("CaseRule",link_types.ref_copy, None, None, None, True), WClass("SwitchRule",link_types.ref_copy, None, None, None, True), WClass("SyncRule", link_types.ref_copy, None, None, None, True), - WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"), + WClass("Process", link_types.ref_copy, None, "name.c_str()", "name"), WClass("SigChunk", link_types.ref_copy, None, None, None), - WClass("SigBit", link_types.ref_copy, None, None, "hash()"), - WClass("SigSpec", link_types.ref_copy, None, None, "hash()"), - WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), - WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()") + WClass("SigBit", link_types.ref_copy, None, None, ""), + WClass("SigSpec", link_types.ref_copy, None, None, ""), + WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""), + WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "") ] ), #Source("kernel/satgen",[ diff --git a/passes/cmds/dft_tag.cc b/passes/cmds/dft_tag.cc index 8947570e791..0b1127a11a9 100644 --- a/passes/cmds/dft_tag.cc +++ b/passes/cmds/dft_tag.cc @@ -47,7 +47,7 @@ struct DftTagWorker { bool operator<(const tag_set &other) const { return index < other.index; } bool operator==(const tag_set &other) const { return index == other.index; } - unsigned int hash() const { return hash_ops::hash(index); } + Hasher hash_acc(Hasher h) const { h.acc(index); return h; } bool empty() const { return index == 0; } }; diff --git a/passes/cmds/example_dt.cc b/passes/cmds/example_dt.cc index 1912d7da172..97ce26327bb 100644 --- a/passes/cmds/example_dt.cc +++ b/passes/cmds/example_dt.cc @@ -52,8 +52,10 @@ struct ExampleDtPass : public Pass return name == other.name && parameters == other.parameters; } - unsigned int hash() const { - return mkhash(name.hash(), parameters.hash()); + Hasher hash_acc(Hasher h) const { + h.acc(name); + h.acc(parameters); + return h; } }; diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index 3f8d807b36d..fe8b4a44498 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -20,7 +20,6 @@ #include "kernel/register.h" #include "kernel/rtlil.h" #include "kernel/log.h" -#include "kernel/hashlib.h" USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN diff --git a/passes/cmds/viz.cc b/passes/cmds/viz.cc index 3655f3f491b..9dd68bd0087 100644 --- a/passes/cmds/viz.cc +++ b/passes/cmds/viz.cc @@ -70,13 +70,13 @@ struct GraphNode { pool names_; dict tags_; - pool upstream_; - pool downstream_; + pool upstream_; + pool downstream_; pool &names() { return get()->names_; } dict &tags() { return get()->tags_; } - pool &upstream() { return get()->upstream_; } - pool &downstream() { return get()->downstream_; } + pool &upstream() { return get()->upstream_; } + pool &downstream() { return get()->downstream_; } uint8_t tag(int index) { return tags().at(index, 0); @@ -154,8 +154,8 @@ struct Graph { nodes.push_back(n); n->index = GetSize(nodes); - pool new_upstream; - pool new_downstream; + pool new_upstream; + pool new_downstream; for (auto g : n->upstream()) { if (n != (g = g->get())) @@ -302,7 +302,7 @@ struct Graph { } } - pool excluded; + pool excluded; for (auto grp : config.groups) { @@ -348,7 +348,7 @@ struct Graph { excluded.insert(g->get()); dict cell_nodes; - dict> sig_users; + dict> sig_users; for (auto cell : module->selected_cells()) { auto g = new GraphNode; @@ -483,8 +483,8 @@ struct Graph { { header("Any nodes with identical connections"); - typedef pair, pool> node_conn_t; - dict> nodes_by_conn; + typedef pair, pool> node_conn_t; + dict> nodes_by_conn; for (auto g : term ? term_nodes : nonterm_nodes) { auto &entry = nodes_by_conn[node_conn_t(g->upstream(), g->downstream())]; for (auto n : entry) @@ -506,8 +506,8 @@ struct Graph { header("Sibblings with identical tags"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { - dict, pool> nodes_by_tags; + auto process_conns = [&](const pool &stream) { + dict, pool> nodes_by_tags; for (auto n : stream) { if (n->terminal) continue; std::vector key; @@ -556,7 +556,7 @@ struct Graph { if (!term) { header("Sibblings with similar tags (strict)"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { + auto process_conns = [&](const pool &stream) { std::vector nodes; for (auto n : stream) if (!n->terminal) nodes.push_back(n); @@ -585,7 +585,7 @@ struct Graph { if (!term) { header("Sibblings with similar tags (non-strict)"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { + auto process_conns = [&](const pool &stream) { std::vector nodes; for (auto n : stream) if (!n->terminal) nodes.push_back(n); @@ -603,7 +603,7 @@ struct Graph { { header("Any nodes with identical fan-in or fan-out"); - dict, pool> nodes_by_conn[2]; + dict, pool> nodes_by_conn[2]; for (auto g : term ? term_nodes : nonterm_nodes) { auto &up_entry = nodes_by_conn[0][g->upstream()]; auto &down_entry = nodes_by_conn[1][g->downstream()]; @@ -629,7 +629,7 @@ struct Graph { if (!term) { header("Sibblings with similar tags (lax)"); for (auto g : nonterm_nodes) { - auto process_conns = [&](const pool &stream) { + auto process_conns = [&](const pool &stream) { std::vector nodes; for (auto n : stream) if (!n->terminal) nodes.push_back(n); @@ -720,9 +720,9 @@ struct VizWorker fprintf(f, "digraph \"%s\" {\n", log_id(module)); fprintf(f, " rankdir = LR;\n"); - dict>, hash_ptr_ops> extra_lines; - dict bypass_nodes; - pool bypass_candidates; + dict>> extra_lines; + dict bypass_nodes; + pool bypass_candidates; auto bypass = [&](GraphNode *g, GraphNode *n) { log_assert(g->terminal); diff --git a/passes/equiv/equiv_struct.cc b/passes/equiv/equiv_struct.cc index 39604994a15..55b364971f7 100644 --- a/passes/equiv/equiv_struct.cc +++ b/passes/equiv/equiv_struct.cc @@ -46,11 +46,11 @@ struct EquivStructWorker parameters == other.parameters && port_sizes == other.port_sizes; } - unsigned int hash() const { - unsigned int h = mkhash_init; - h = mkhash(h, mkhash(type)); - h = mkhash(h, mkhash(parameters)); - h = mkhash(h, mkhash(connections)); + Hasher hash_acc(Hasher h) const { + h.acc(type); + h.acc(parameters); + h.acc(port_sizes); + h.acc(connections); return h; } }; diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index eb3aa462e01..03f585b9079 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -26,6 +26,8 @@ #include #include #include +#include +#include USING_YOSYS_NAMESPACE @@ -42,6 +44,22 @@ struct OptMergeWorker CellTypes ct; int total_count; + static vector> sorted_pmux_in(const dict &conn) + { + SigSpec sig_s = conn.at(ID::S); + SigSpec sig_b = conn.at(ID::B); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + return sb_pairs; + } + static void sort_pmux_conn(dict &conn) { SigSpec sig_s = conn.at(ID::S); @@ -65,90 +83,73 @@ struct OptMergeWorker } } - std::string int_to_hash_string(unsigned int v) - { - if (v == 0) - return "0"; - std::string str = ""; - while (v > 0) { - str += 'a' + (v & 15); - v = v >> 4; - } - return str; - } - - uint64_t hash_cell_parameters_and_connections(const RTLIL::Cell *cell) + Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) { - vector hash_conn_strings; - std::string hash_string = cell->type.str() + "\n"; - - const dict *conn = &cell->connections(); - dict alt_conn; - + // TODO: when implemented, use celltypes to match: + // (builtin || stdcell) && (unary || binary) && symmetrical if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { - alt_conn = *conn; - if (assign_map(alt_conn.at(ID::A)) < assign_map(alt_conn.at(ID::B))) { - alt_conn[ID::A] = conn->at(ID::B); - alt_conn[ID::B] = conn->at(ID::A); + std::array inputs = { + assign_map(cell->getPort(ID::A)), + assign_map(cell->getPort(ID::B)) + }; + std::sort(inputs.begin(), inputs.end()); + h = hash_ops>::hash_acc(inputs, h); + h = assign_map(cell->getPort(ID::Y)).hash_acc(h); + } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort(); + h = a.hash_acc(h); + } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + SigSpec a = assign_map(cell->getPort(ID::A)); + a.sort_and_unify(); + h = a.hash_acc(h); + } else if (cell->type == ID($pmux)) { + dict conn = cell->connections(); + assign_map.apply(conn.at(ID::A)); + assign_map.apply(conn.at(ID::B)); + assign_map.apply(conn.at(ID::S)); + for (const auto& [s_bit, b_chunk] : sorted_pmux_in(conn)) { + h = s_bit.hash_acc(h); + h = b_chunk.hash_acc(h); } - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort(); - conn = &alt_conn; - } else - if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - alt_conn.at(ID::A).sort_and_unify(); - conn = &alt_conn; - } else - if (cell->type == ID($pmux)) { - alt_conn = *conn; - assign_map.apply(alt_conn.at(ID::A)); - assign_map.apply(alt_conn.at(ID::B)); - assign_map.apply(alt_conn.at(ID::S)); - sort_pmux_conn(alt_conn); - conn = &alt_conn; - } - - for (auto &it : *conn) { - RTLIL::SigSpec sig; - if (cell->output(it.first)) { - if (it.first == ID::Q && RTLIL::builtin_ff_cell_types().count(cell->type)) { - // For the 'Q' output of state elements, - // use its (* init *) attribute value - sig = initvals(it.second); - } - else - continue; + h = assign_map(cell->getPort(ID::A)).hash_acc(h); + } else { + std::vector> conns; + for (const auto& conn : cell->connections()) { + conns.push_back(conn); } - else - sig = assign_map(it.second); - string s = "C " + it.first.str() + "="; - for (auto &chunk : sig.chunks()) { - if (chunk.wire) - s += "{" + chunk.wire->name.str() + " " + - int_to_hash_string(chunk.offset) + " " + - int_to_hash_string(chunk.width) + "}"; - else - s += RTLIL::Const(chunk.data).as_string(); + std::sort(conns.begin(), conns.end()); + for (const auto& [port, sig] : conns) { + if (!cell->output(port)) { + h = port.hash_acc(h); + h = assign_map(sig).hash_acc(h); + } } - hash_conn_strings.push_back(s + "\n"); - } - for (auto &it : cell->parameters) - hash_conn_strings.push_back("P " + it.first.str() + "=" + it.second.as_string() + "\n"); + if (RTLIL::builtin_ff_cell_types().count(cell->type)) + h = initvals(cell->getPort(ID::Q)).hash_acc(h); - std::sort(hash_conn_strings.begin(), hash_conn_strings.end()); + } + return h; + } - for (auto it : hash_conn_strings) - hash_string += it; + static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) + { + using Paramvec = std::vector>; + Paramvec params; + for (const auto& param : cell->parameters) { + params.push_back(param); + } + std::sort(params.begin(), params.end()); + return hash_ops::hash_acc(params, h); + } - return std::hash{}(hash_string); + Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) + { + h = hash_cell_inputs(cell, h); + h = hash_cell_parameters(cell, h); + return h; } bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) @@ -252,21 +253,34 @@ struct OptMergeWorker initvals.set(&assign_map, module); bool did_something = true; + bool warned_collisions = false; + // A cell may have to go through a lot of collisions if the hash + // function is performing poorly, but it's a symptom of something bad + // beyond the user's control. + int threshold = 1000; // adjust to taste while (did_something) { std::vector cells; - cells.reserve(module->cells_.size()); - for (auto &it : module->cells_) { - if (!design->selected(module, it.second)) + cells.reserve(module->cells().size()); + for (auto cell : module->cells()) { + if (!design->selected(module, cell)) continue; - if (mode_keepdc && has_dont_care_initval(it.second)) + if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) { + // Ignore those for performance: meminit can have an excessively large port, + // mem can have an excessively large parameter holding the init data continue; - if (ct.cell_known(it.second->type) || (mode_share_all && it.second->known())) - cells.push_back(it.second); + } + if (mode_keepdc && has_dont_care_initval(cell)) + continue; + if (ct.cell_known(cell->type) || (mode_share_all && cell->known())) + cells.push_back(cell); } did_something = false; - dict sharemap; + // INVARIANT: All cells associated with the same hash in sharemap + // are distinct as far as compare_cell_parameters_and_connections + // can tell. + std::unordered_multimap sharemap; for (auto cell : cells) { if ((!mode_share_all && !ct.cell_known(cell->type)) || !cell->known()) @@ -275,22 +289,31 @@ struct OptMergeWorker if (cell->type == ID($scopeinfo)) continue; - uint64_t hash = hash_cell_parameters_and_connections(cell); - auto r = sharemap.insert(std::make_pair(hash, cell)); - if (!r.second) { - if (compare_cell_parameters_and_connections(cell, r.first->second)) { + Hasher::hash_t hash = hash_cell_function(cell, Hasher()).yield(); + // Get all cells with matching hashes + auto matching = sharemap.equal_range(hash); + int collisions = 0; + bool found = false; + for (auto it = matching.first; it != matching.second && !found; ++it) { + if (collisions > threshold && !warned_collisions) { + log_warning("High hash collision counts. opt_merge runtime may be excessive.\n" \ + "Please report to maintainers.\n"); + warned_collisions = true; + } + auto other_cell = it->second; + if (compare_cell_parameters_and_connections(cell, other_cell)) { + found = true; if (cell->has_keep_attr()) { - if (r.first->second->has_keep_attr()) + if (other_cell->has_keep_attr()) continue; - std::swap(r.first->second, cell); + std::swap(other_cell, cell); } - did_something = true; - log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), r.first->second->name.c_str()); + log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); for (auto &it : cell->connections()) { if (cell->output(it.first)) { - RTLIL::SigSpec other_sig = r.first->second->getPort(it.first); + RTLIL::SigSpec other_sig = other_cell->getPort(it.first); log_debug(" Redirecting output %s: %s = %s\n", it.first.c_str(), log_signal(it.second), log_signal(other_sig)); Const init = initvals(other_sig); @@ -304,8 +327,18 @@ struct OptMergeWorker log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type.c_str(), cell->name.c_str(), module->name.c_str()); module->remove(cell); total_count++; + break; + } else { + collisions++; + log_debug(" False hash match: `%s' and `%s'.\n", cell->name.c_str(), other_cell->name.c_str()); } } + if (!did_something) { + // This cell isn't represented yet in the sharemap. + // Either it's the first of its hash, + // or falsely matches all cells in sharemap sharing its hash. + sharemap.insert(std::make_pair(hash, cell)); + } } } @@ -336,6 +369,7 @@ struct OptMergePass : public Pass { } void execute(std::vector args, RTLIL::Design *design) override { + Pass::call(design, "dump"); log_header(design, "Executing OPT_MERGE pass (detect identical cells).\n"); bool mode_nomux = false; diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 5b392ce512b..5936502c09d 100644 --- a/passes/proc/proc_dlatch.cc +++ b/passes/proc/proc_dlatch.cc @@ -127,11 +127,10 @@ struct proc_dlatch_db_t return signal == other.signal && match == other.match && children == other.children; } - unsigned int hash() const { - unsigned int h = mkhash_init; - mkhash(h, signal.hash()); - mkhash(h, match.hash()); - for (auto i : children) mkhash(h, i); + Hasher hash_acc(Hasher h) const { + h.acc(signal); + h.acc(match); + h.acc(children); return h; } }; diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index b209057fe86..2f539c960bf 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -108,8 +108,8 @@ struct SigSnippets struct SnippetSwCache { - dict, hash_ptr_ops> full_case_bits_cache; - dict, hash_ptr_ops> cache; + dict> full_case_bits_cache; + dict> cache; const SigSnippets *snippets; int current_snippet; @@ -318,7 +318,7 @@ const pool &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul return swcache.full_case_bits_cache.at(sw); } -RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict &swpara, +RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict &swpara, RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) { RTLIL::SigSpec result = defval; @@ -421,7 +421,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode) swcache.snippets = &sigsnip; swcache.insert(&proc->root_case); - dict swpara; + dict swpara; int cnt = 0; for (int idx : sigsnip.snippets) diff --git a/passes/sat/mutate.cc b/passes/sat/mutate.cc index 02174be53dd..3075ef3f0c4 100644 --- a/passes/sat/mutate.cc +++ b/passes/sat/mutate.cc @@ -176,7 +176,7 @@ struct coverdb_t struct mutate_queue_t { - pool db; + pool db; mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) { mutate_t *m = nullptr; diff --git a/passes/sat/recover_names.cc b/passes/sat/recover_names.cc index 4870e2cac19..05ddf8e552e 100644 --- a/passes/sat/recover_names.cc +++ b/passes/sat/recover_names.cc @@ -46,9 +46,11 @@ struct IdBit { bool operator==(const IdBit &other) const { return name == other.name && bit == other.bit; }; bool operator!=(const IdBit &other) const { return name != other.name || bit != other.bit; }; - unsigned hash() const + Hasher hash_acc(Hasher h) const { - return mkhash_add(name.hash(), bit); + h.acc(name); + h.acc(bit); + return h; } IdString name; @@ -62,9 +64,11 @@ struct InvBit { bool operator==(const InvBit &other) const { return bit == other.bit && inverted == other.inverted; }; bool operator!=(const InvBit &other) const { return bit != other.bit || inverted != other.inverted; }; - unsigned hash() const + Hasher hash_acc(Hasher h) const { - return mkhash(bit.hash(), inverted); + h.acc(bit); + h.acc(inverted); + return h; } IdBit bit; diff --git a/passes/sat/sim.cc b/passes/sat/sim.cc index c3fa213f6e5..8fac93b98b5 100644 --- a/passes/sat/sim.cc +++ b/passes/sat/sim.cc @@ -161,7 +161,7 @@ struct SimInstance pool dirty_bits; pool dirty_cells; pool dirty_memories; - pool dirty_children; + pool dirty_children; struct ff_state_t { diff --git a/passes/techmap/abc.cc b/passes/techmap/abc.cc index 07f9ee45d82..cc37677ce21 100644 --- a/passes/techmap/abc.cc +++ b/passes/techmap/abc.cc @@ -1411,6 +1411,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin module->connect(conn); } + cell_stats.sort(); for (auto &it : cell_stats) log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); int in_wires = 0, out_wires = 0; diff --git a/passes/techmap/alumacc.cc b/passes/techmap/alumacc.cc index e4e70004c0f..05a3d17029f 100644 --- a/passes/techmap/alumacc.cc +++ b/passes/techmap/alumacc.cc @@ -111,7 +111,7 @@ struct AlumaccWorker dict bit_users; dict sig_macc; - dict> sig_alu; + dict> sig_alu; int macc_counter, alu_counter; AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) @@ -226,7 +226,7 @@ struct AlumaccWorker { while (1) { - pool delete_nodes; + pool delete_nodes; for (auto &it : sig_macc) { @@ -278,7 +278,7 @@ struct AlumaccWorker void macc_to_alu() { - pool delete_nodes; + pool delete_nodes; for (auto &it : sig_macc) { diff --git a/passes/techmap/clockgate.cc b/passes/techmap/clockgate.cc index bf53b02bbc8..bfd673bc181 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -77,10 +77,9 @@ struct ClockgatePass : public Pass { SigBit ce_bit; bool pol_clk; bool pol_ce; - unsigned int hash() const { + Hasher hash_acc(Hasher h) const { auto t = std::make_tuple(clk_bit, ce_bit, pol_clk, pol_ce); - unsigned int h = mkhash_init; - h = mkhash(h, hash_ops::hash(t)); + h.acc(t); return h; } bool operator==(const ClkNetInfo& other) const { diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index b5b95132364..b07097b9967 100644 --- a/passes/techmap/flowmap.cc +++ b/passes/techmap/flowmap.cc @@ -250,9 +250,11 @@ struct FlowGraph { return !(*this == other); } - unsigned int hash() const + Hasher hash_acc(Hasher h) const { - return hash_ops>::hash({node, is_bottom}); + std::pair p = {node, is_bottom}; + h.acc(p); + return h; } static NodePrime top(RTLIL::SigBit node) diff --git a/techlibs/quicklogic/ql_dsp_simd.cc b/techlibs/quicklogic/ql_dsp_simd.cc index 47b343dadc4..bec053594a7 100644 --- a/techlibs/quicklogic/ql_dsp_simd.cc +++ b/techlibs/quicklogic/ql_dsp_simd.cc @@ -53,7 +53,7 @@ struct QlDspSimdPass : public Pass { DspConfig(const DspConfig &ref) = default; DspConfig(DspConfig &&ref) = default; - unsigned int hash() const { return connections.hash(); } + Hasher hash_acc(Hasher h) const { h.acc(connections); return h; } bool operator==(const DspConfig &ref) const { return connections == ref.connections; } };