diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index b72caf119c3..931454adaa1 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/docs/source/yosys_internals/hashing.rst b/docs/source/yosys_internals/hashing.rst new file mode 100644 index 00000000000..338ee5fd681 --- /dev/null +++ b/docs/source/yosys_internals/hashing.rst @@ -0,0 +1,155 @@ +Hashing and associative data structures in Yosys +------------------------------------------------ + +Container classes based on hashing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yosys uses ``dict`` and ``pool`` as main container classes. +``dict`` is essentially a replacement for ``std::unordered_map`` +and ``pool`` is a replacement for ``std::unordered_set``. +The main characteristics are: + +* ``dict`` and ``pool`` are about 2x faster than the std containers + (though this claim hasn't been verified for over 10 years) + +* references to elements in a ``dict`` or ``pool`` are invalidated by + insert and remove operations (similar to ``std::vector`` on ``push_back()``). + +* some iterators are invalidated by ``erase()``. specifically, iterators + that have not passed the erased element yet are invalidated. (``erase()`` + itself returns valid iterator to the next element.) + +* no iterators are invalidated by ``insert()``. elements are inserted at + ``begin()``. i.e. only a new iterator that starts at ``begin()`` will see the + inserted elements. + +* the method ``.count(key, iterator)`` is like ``.count(key)`` but only + considers elements that can be reached via the iterator. + +* iterators can be compared. ``it1 < it2`` means that the position of ``t2`` + can be reached via ``t1`` but not vice versa. + +* the method ``.sort()`` can be used to sort the elements in the container + the container stays sorted until elements are added or removed. + +* ``dict`` and ``pool`` will have the same order of iteration across + all compilers, standard libraries and architectures. + +In addition to ``dict`` and ``pool`` there is also an ``idict`` that +creates a bijective map from ``K`` to the integers. For example: + +:: + + idict si; + log("%d\n", si("hello")); // will print 42 + log("%d\n", si("world")); // will print 43 + log("%d\n", si.at("world")); // will print 43 + log("%d\n", si.at("dummy")); // will throw exception + log("%s\n", si[42].c_str())); // will print hello + log("%s\n", si[43].c_str())); // will print world + log("%s\n", si[44].c_str())); // will throw exception + +It is not possible to remove elements from an idict. + +Finally ``mfp`` implements a merge-find set data structure (aka. disjoint-set +or union-find) over the type ``K`` ("mfp" = merge-find-promote). + +The hash function +~~~~~~~~~~~~~~~~~ + +The hash function generally used in Yosys is the XOR version of DJB2: + +:: + + state = ((state << 5) + state) ^ value + +This is an old-school hash designed to hash ASCII characters. Yosys doesn't hash +a lot of ASCII text, but it still happens to be a local optimum due to factors +described later. + +Hash function quality is multi-faceted and highly dependent on what is being +hashed. Yosys isn't concerned by any cryptographic qualities, instead the goal +is minimizing total hashing collision risk given the data patterns within Yosys. +In general, a good hash function typically folds values into a state accumulator +with a mathematical function that is fast to compute and has some beneficial +properties. One of these is the avalanche property, which demands that a small +change such as flipping a bit or incrementing by one in the input produces a +large, unpredictable change in the output. Additionally, the bit independence +criterion states that any pair of output bits should change independently when +any single input bit is inverted. These properties are important for avoiding +hash collision on data patterns like the hash of a sequence not colliding with +its permutation, not losing from the state the information added by hashing +preceding elements, etc. + +DJB2 lacks these properties. Instead, since Yosys hashes large numbers of data +structures composed of incrementing integer IDs, Yosys abuses the predictability +of DJB2 to get lower hash collisions, with regular nature of the hashes +surviving through the interaction with the "modulo prime" operations in the +associative data structures. For example, some most common objects in Yosys are +interned ``IdString``\ s of incrementing indices or ``SigBit``\ s with bit +offsets into wire (represented by its unique ``IdString`` name) as the typical +case. This is what makes DJB2 a local optimum. Additionally, the ADD version of +DJB2 (like above but with addition instead of XOR) is used to this end for some +types, abandoning the general pattern of folding values into a state value. + +Making a type hashable +~~~~~~~~~~~~~~~~~~~~~~ + +Let's first take a look at the external interface on a simplified level. +Generally, to get the hash for ``T obj``, you would call the utility function +``run_hash(const T& obj)``, corresponding to ``hash_top_ops::hash(obj)``, +the default implementation of which is ``hash_ops::hash_into(Hasher(), obj)``. +``Hasher`` is the class actually implementing the hash function, hiding its +initialized internal state, and passing it out on ``hash_t yield()`` with +perhaps some finalization steps. + +``hash_ops`` is the star of the show. By default it pulls the ``Hasher h`` +through a ``Hasher T::hash_into(Hasher h)`` method. That's the method you have to +implement to make a record (class or struct) type easily hashable with Yosys +hashlib associative data structures. + +``hash_ops`` is specialized for built-in types like ``int`` or ``bool`` and +treats pointers the same as integers, so it doesn't dereference pointers. Since +many RTLIL data structures like ``RTLIL::Wire`` carry their own unique index +``Hasher::hash_t hashidx_;``, there are specializations for ``hash_ops`` +and others in ``kernel/hashlib.h`` that actually dereference the pointers and +call ``hash_into`` on the instances pointed to. + +``hash_ops`` is also specialized for simple compound types like +``std::pair`` by calling hash_into in sequence on its members. For flexible +size containers like ``std::vector`` the size of the container is hashed +first. That is also how implementing hashing for a custom record data type +should be - unless there is strong reason to do otherwise, call ``h.eat(m)`` on +the ``Hasher h`` you have received for each member in sequence and ``return +h;``. If you do have a strong reason to do so, look at how +``hash_top_ops`` is implemented in ``kernel/rtlil.h``. + +Porting plugins from the legacy interface +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, the interface to implement hashing on custom types was just +``unsigned int T::hash() const``. This meant hashes for members were computed +independently and then ad-hoc combined with the hash function with some xorshift +operations thrown in to mix bits together somewhat. A plugin can stay compatible +with both versions prior and after the break by implementing both interfaces +based on the existance and value of `YS_HASHING_VERSION`. + +.. code-block:: cpp + :caption: Example hash compatibility wrapper + :name: hash_plugin_compat + + #ifndef YS_HASHING_VERSION + unsigned int T::hash() const { + return mkhash(a, b); + } + #elif YS_HASHING_VERSION == 1 + Hasher T::hash_into(Hasher h) const { + h.eat(a); + h.eat(b); + return h; + } + #else + #error "Unsupported hashing interface" + #endif + +Feel free to contact Yosys maintainers with related issues. diff --git a/docs/source/yosys_internals/index.rst b/docs/source/yosys_internals/index.rst index 9631e865331..3dd4224faf1 100644 --- a/docs/source/yosys_internals/index.rst +++ b/docs/source/yosys_internals/index.rst @@ -39,3 +39,4 @@ as reference to implement a similar system in any language. extending_yosys/index techmap verilog + hashing 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/flake.nix b/flake.nix index 90fa5328c09..19ba59f17c4 100644 --- a/flake.nix +++ b/flake.nix @@ -14,15 +14,15 @@ }; # TODO: don't override src when ./abc is empty # which happens when the command used is `nix build` and not `nix build ?submodules=1` - abc-verifier = pkgs.abc-verifier.overrideAttrs(x: y: {src = ./abc;}); + abc-verifier = pkgs.abc-verifier; yosys = pkgs.clangStdenv.mkDerivation { name = "yosys"; src = ./. ; - buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git pkg-configUpstream llvmPackages.bintools ]; + buildInputs = with pkgs; [ clang bison flex libffi tcl readline python3 zlib git pkg-configUpstream llvmPackages.bintools ]; checkInputs = with pkgs; [ gtest ]; propagatedBuildInputs = [ abc-verifier ]; preConfigure = "make config-clang"; - checkTarget = "test"; + checkTarget = "unit-test"; installPhase = '' make install PREFIX=$out ABCEXTERNAL=yosys-abc ln -s ${abc-verifier}/bin/abc $out/bin/yosys-abc @@ -41,7 +41,7 @@ packages.default = yosys; defaultPackage = yosys; devShell = pkgs.mkShell { - buildInputs = with pkgs; [ clang llvmPackages.bintools bison flex libffi tcl readline python3 llvmPackages.libcxxClang zlib git gtest abc-verifier ]; + buildInputs = with pkgs; [ clang llvmPackages.bintools gcc bison flex libffi tcl readline python3 zlib git gtest abc-verifier verilog boost python3Packages.boost ]; }; } ); diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index bdff015e3f4..f7f087080ba 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_into(Hasher h) const { h.eat(hashidx_); return h; } // this nodes type AstNodeType type; diff --git a/frontends/verific/verific.cc b/frontends/verific/verific.cc index d39030c2d0d..8f1b07b100e 100644 --- a/frontends/verific/verific.cc +++ b/frontends/verific/verific.cc @@ -619,7 +619,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; @@ -1576,9 +1576,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) @@ -1841,10 +1841,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 3c77dd7c3b9..4e9c7a30598 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/guidelines/GettingStarted b/guidelines/GettingStarted index 110f6318518..17fe325233d 100644 --- a/guidelines/GettingStarted +++ b/guidelines/GettingStarted @@ -37,57 +37,15 @@ And then executed using the following command: Yosys Data Structures --------------------- -Here is a short list of data structures that you should make yourself familiar -with before you write C++ code for Yosys. The following data structures are all -defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used. + 1. Container classes based on hashing - 1. Yosys Container Classes +Yosys heavily relies on custom container data structures such as dict or pool +defined in kernel/hashlib.h. +dict is essentially a replacement for std::unordered_map +and pool is a replacement for std::unordered_set. Please refer to +docs/source/yosys_internals/hashing.rst for more information on those. -Yosys uses dict and pool as main container classes. dict is -essentially a replacement for std::unordered_map and pool is a -replacement for std::unordered_set. The main characteristics are: - - - dict and pool are about 2x faster than the std containers - - - references to elements in a dict or pool are invalidated by - insert and remove operations (similar to std::vector on push_back()). - - - some iterators are invalidated by erase(). specifically, iterators - that have not passed the erased element yet are invalidated. (erase() - itself returns valid iterator to the next element.) - - - no iterators are invalidated by insert(). elements are inserted at - begin(). i.e. only a new iterator that starts at begin() will see the - inserted elements. - - - the method .count(key, iterator) is like .count(key) but only - considers elements that can be reached via the iterator. - - - iterators can be compared. it1 < it2 means that the position of t2 - can be reached via t1 but not vice versa. - - - the method .sort() can be used to sort the elements in the container - the container stays sorted until elements are added or removed. - - - dict and pool will have the same order of iteration across - all compilers, standard libraries and architectures. - -In addition to dict and pool there is also an idict that -creates a bijective map from K to the integers. For example: - - idict si; - log("%d\n", si("hello")); // will print 42 - log("%d\n", si("world")); // will print 43 - log("%d\n", si.at("world")); // will print 43 - log("%d\n", si.at("dummy")); // will throw exception - log("%s\n", si[42].c_str())); // will print hello - log("%s\n", si[43].c_str())); // will print world - log("%s\n", si[44].c_str())); // will throw exception - -It is not possible to remove elements from an idict. - -Finally mfp implements a merge-find set data structure (aka. disjoint-set or -union-find) over the type K ("mfp" = merge-find-promote). +Otherwise, Yosys makes use of the following: 2. Standard STL data types diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index c1ceac14ca8..a9dc98a3348 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_into(Hasher h) const { if (!cached_hash) - cached_hash = hash_ops>::hash(bitdata); - return cached_hash; + cached_hash = run_hash(bitdata); + h.eat(cached_hash); + return h; } }; pool database; diff --git a/kernel/cellaigs.cc b/kernel/cellaigs.cc index de0a4939430..fd3c7bb67cd 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_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::hash(name); + h.eat(name); + return h; } struct AigMaker diff --git a/kernel/cellaigs.h b/kernel/cellaigs.h index 8f6d69ba62b..dd1b6dde650 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_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 diff --git a/kernel/driver.cc b/kernel/driver.cc index 6565c472c2a..a3d85bd90e9 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 @@ -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 , similar effect to --hash-seed", cxxopts::value(), "") + ("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>(), "") @@ -437,6 +440,10 @@ int main(int argc, char **argv) int idx = result["autoidx"].as(); autoidx = idx; } + 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 8929c342627..23eba9f1c05 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_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 @@ struct DriveBitMultiple return multiple_ == other.multiple_; } - unsigned int hash() const - { - return multiple_.hash(); - } + Hasher hash_into(Hasher h) const; }; struct DriveBit @@ -362,35 +352,7 @@ struct DriveBit 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 @@ struct DriveChunkMultiple 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 @@ struct DriveChunk 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 @@ 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 { + if (hash_ != 0) + return; pack(); - hash_ = hash_ops>().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 @@ 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; } + Hasher hash_into(Hasher h) const; }; // Essentially a dict> but using less memory // and fewer allocations @@ -1333,6 +1258,131 @@ struct DriverMap } }; +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 diff --git a/kernel/functional.h b/kernel/functional.h index 61b303e0b48..e132821405b 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_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(_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_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; diff --git a/kernel/hashlib.h b/kernel/hashlib.h index 859115829d2..6927702e156 100644 --- a/kernel/hashlib.h +++ b/kernel/hashlib.h @@ -17,27 +17,62 @@ #include #include #include - +#include #include +#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 +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 +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 struct hash_ops { - static inline bool cmp(const T &a, const T &b) { - return a == b; +class HasherDJB32 { +public: + using hash_t = uint32_t; + + HasherDJB32() { + // traditionally 5381 is used as starting value for the djb2 hash + state = 5381; } - static inline unsigned int hash(const T &a) { - return a.hash(); + 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; } -}; -struct hash_int_ops { template - static inline bool cmp(T a, T b) { - return a == b; + void eat(T&& t) { + *this = hash_ops>>::hash_into(std::forward(t), *this); } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(bool a) { - return a ? 1 : 0; + template + void eat(const T& t) { + *this = hash_ops::hash_into(t, *this); } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(int32_t a) { - return a; + + void commutative_eat(hash_t t) { + state ^= t; } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(int64_t a) { - return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); + + void force(hash_t new_state) { + state = new_state; } }; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(uint32_t a) { - return a; + +using Hasher = HasherDJB32; + +template +struct hash_top_ops { + static inline bool cmp(const T &a, const T &b) { + return hash_ops::cmp(a, b); } -}; -template<> struct hash_ops : hash_int_ops -{ - static inline unsigned int hash(uint64_t a) { - return mkhash((unsigned int)(a), (unsigned int)(a >> 32)); + static inline Hasher hash(const T &a) { + return hash_ops::hash_into(a, Hasher()); } }; -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_into(const T &a, Hasher h) { + 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_into((u_type) a, h); + } else if constexpr (std::is_pointer_v) { + return hash_ops::hash_into((uintptr_t) a, h); + } else if constexpr (std::is_same_v) { + for (auto c : a) + h.hash32(c); + return h; + } else { + return a.hash_into(h); + } } }; @@ -116,8 +189,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_into(std::pair a, Hasher h) { + h = hash_ops

::hash_into(a.first, h); + h = hash_ops::hash_into(a.second, h); + return h; } }; @@ -126,13 +201,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_into(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_into(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_into(a, h); + h = element_ops_t::hash_into(std::get(a), h); + return h; } }; @@ -140,35 +217,44 @@ 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_into(std::vector a, Hasher h) { + h.eat((uint32_t)a.size()); for (auto k : a) - h = mkhash(h, hash_ops::hash(k)); + h.eat(k); return h; } }; +template struct hash_ops> { + static inline bool cmp(std::array a, std::array b) { + return a == b; + } + static inline Hasher hash_into(std::array a, Hasher h) { + for (const auto& k : a) + h = hash_ops::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 : 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::hash_into((uintptr_t)a, h); } }; @@ -177,22 +263,40 @@ 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_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 +[[nodiscard]] +Hasher::hash_t run_hash(const T& obj) { + return hash_top_ops::hash(obj).yield(); +} +/** Refer to docs/source/yosys_internals/hashing.rst */ template +[[nodiscard]] +[[deprecated]] inline unsigned int mkhash(const T &v) { - return hash_ops().hash(v); + return (unsigned int) run_hash(v); } 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_into(std::monostate, Hasher h) { + return h; } }; @@ -200,9 +304,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_into(std::variant a, Hasher h) { + std::visit([& h](const auto &v) { h.eat(v); }, a); + h.eat(a.index()); + return h; } }; @@ -210,11 +315,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_into(std::optional 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> 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 -{ +class dict { struct entry_t { std::pair 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(key, T()), -1); @@ -380,7 +485,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 +498,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 +610,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 +620,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 +630,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 +640,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 +650,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 +660,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 +670,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 +680,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 +717,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 +726,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 +735,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 +744,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 +753,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,12 +788,14 @@ 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_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 @@ 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 = ops.hash(key).yield() % (unsigned int)(hashtable.size()); return hash; } @@ -749,13 +856,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 +883,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 +907,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 +927,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 +940,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 +1047,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 +1057,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 +1073,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 +1110,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 +1119,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 +1158,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_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 @@ 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 +1222,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 +1231,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 +1240,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; } @@ -1176,7 +1284,7 @@ class mfp 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..921df304ca2 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_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]); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index b279cce6e15..cb0f7da7807 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_; @@ -4476,17 +4476,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; } diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 6d3558621c1..330af649f74 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -76,329 +76,354 @@ namespace RTLIL struct SyncRule; struct Process; struct Binding; + struct IdString; typedef std::pair SigSig; +}; - struct 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 +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 global_id_storage_; + static dict global_id_index_; +#ifndef YOSYS_NO_IDS_REFCNT + static std::vector global_refcount_storage_; + static std::vector global_free_idx_list_; +#endif - 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; +#ifdef YOSYS_USE_STICKY_IDS + static int last_created_idx_ptr_; + static int last_created_idx_[8]; +#endif - static std::vector global_id_storage_; - static dict global_id_index_; - #ifndef YOSYS_NO_IDS_REFCNT - static std::vector global_refcount_storage_; - static std::vector global_free_idx_list_; + 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 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()); #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 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()); - #endif - } + static int get_reference(const char *p) + { + log_assert(destruct_guard_ok); - 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; - } + if (!p[0]) + return 0; - 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; - } + 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); - } + 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); - 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); - } + 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); + } - #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; + int idx = global_free_idx_list_.back(); + global_free_idx_list_.pop_back(); + global_id_storage_.at(idx) = strdup(p); + global_id_index_[global_id_storage_.at(idx)] = idx; + global_refcount_storage_.at(idx)++; + #else + if (global_id_storage_.empty()) { + 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 - #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 + if (yosys_xtrace) { + log("#X# New IdString '%s' with index %d.\n", p, idx); + log_backtrace("-X- ", yosys_xtrace-1); + } - int &refcount = global_refcount_storage_[idx]; + #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 - if (--refcount > 0) - return; + #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 - 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); - } + return idx; + } - 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); +#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)); } - #else - static inline void put_reference(int) { } #endif - // the actual IdString object is just is a single int - - int index_; + int &refcount = global_refcount_storage_[idx]; - 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_); } + if (--refcount > 0) + return; - inline void operator=(const IdString &rhs) { - put_reference(index_); - index_ = get_reference(rhs.index_); + 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 char *rhs) { - IdString id(rhs); - *this = id; - } + 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 + static inline void put_reference(int) { } +#endif - inline void operator=(const std::string &rhs) { - IdString id(rhs); - *this = id; - } + // the actual IdString object is just is a single int - inline const char *c_str() const { - return global_id_storage_.at(index_); - } + int index_; - inline std::string str() const { - return std::string(global_id_storage_.at(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 bool operator<(const IdString &rhs) const { - return index_ < rhs.index_; - } + inline void operator=(const IdString &rhs) { + put_reference(index_); + index_ = get_reference(rhs.index_); + } - inline bool operator==(const IdString &rhs) const { return index_ == rhs.index_; } - inline bool operator!=(const IdString &rhs) const { return index_ != rhs.index_; } + inline void operator=(const char *rhs) { + IdString id(rhs); + *this = id; + } - // The methods below are just convenience functions for better compatibility with std::string. + inline void operator=(const std::string &rhs) { + IdString id(rhs); + *this = id; + } - bool operator==(const std::string &rhs) const { return c_str() == rhs; } - bool operator!=(const std::string &rhs) const { return c_str() != rhs; } + inline const char *c_str() const { + return global_id_storage_.at(index_); + } - bool operator==(const char *rhs) const { return strcmp(c_str(), rhs) == 0; } - bool operator!=(const char *rhs) const { return strcmp(c_str(), rhs) != 0; } + inline std::string str() const { + return std::string(global_id_storage_.at(index_)); + } - char operator[](size_t i) const { - const char *p = c_str(); + 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; + for (; i != 0; i--, p++) + log_assert(*p != 0); + return *p; #else - return *(p + i); + 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); - } + 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); + } - bool begins_with(const char* prefix) const { - size_t len = strlen(prefix); - if (size() < len) return false; - return compare(0, len, prefix) == 0; - } + int compare(size_t pos, size_t len, const char* s) const { + return strncmp(c_str()+pos, s, len); + } - 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 begins_with(const char* prefix) const { + size_t len = strlen(prefix); + if (size() < len) return false; + return compare(0, len, prefix) == 0; + } - bool contains(const char* str) const { - return strstr(c_str(), str); - } + bool ends_with(const char* suffix) const { + size_t len = strlen(suffix); + if (size() < len) return false; + return compare(size()-len, len, suffix) == 0; + } - size_t size() const { - return strlen(c_str()); - } + bool contains(const char* str) const { + return strstr(c_str(), str); + } - bool empty() const { - return c_str()[0] == 0; - } + size_t size() const { + return strlen(c_str()); + } - void clear() { - *this = IdString(); - } + bool empty() const { + return c_str()[0] == 0; + } - unsigned int hash() const { - return index_; - } + void clear() { + *this = IdString(); + } - // The following is a helper key_compare class. Instead of for example std::set - // use std::set> if the order of cells in the - // set has an influence on the algorithm. + Hasher hash_into(Hasher h) const { return hash_ops::hash_into(index_, h); } - template 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); - } - }; + Hasher hash_top() const { + Hasher h; + h.force((Hasher::hash_t) index_); + return h; + } - // 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. + // The following is a helper key_compare class. Instead of for example std::set + // use std::set> if the order of cells in the + // set has an influence on the algorithm. - template - 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{ (result = result || in(args), 0)... }; - return result; + template 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); } + }; - 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 &rhs) const { return rhs.count(*this) != 0; } + // 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 + 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 &rhs) const; + inline bool in(const pool &&rhs) const; + + bool isPublic() const { return begins_with("\\"); } +}; - bool isPublic() const { return begins_with("\\"); } +namespace hashlib { + template <> + struct hash_top_ops { + 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 p; ... a.in(p) -> (bool)p.count(a) + */ +[[deprecated]] +inline bool RTLIL::IdString::in(const pool &rhs) const { return rhs.count(*this) != 0; } +[[deprecated]] +inline bool RTLIL::IdString::in(const pool &&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 constpad; const pool &builtin_ff_cell_types(); @@ -796,11 +821,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_into(Hasher h) const { + h.eat(size()); + for (auto b : *this) + h.eat(b); return h; } }; @@ -890,7 +914,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 { + 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 @@ -931,7 +968,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 @@ -972,11 +1009,6 @@ struct RTLIL::SigSpec SigSpec(const std::set &bits); explicit SigSpec(bool bit); - size_t get_hash() const { - if (!hash_) hash(); - return hash_; - } - inline const std::vector &chunks() const { pack(); return chunks_; } inline const std::vector &bits() const { inline_unpack(); return bits_; } @@ -1083,7 +1115,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_into(Hasher h) const { if (!hash_) updhash(); h.eat(hash_); return h; } #ifndef NDEBUG void check(Module *mod = nullptr) const; @@ -1124,8 +1156,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; @@ -1147,8 +1179,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 monitors; dict scratchpad; @@ -1252,8 +1284,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); @@ -1607,8 +1639,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 @@ -1646,8 +1678,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(); @@ -1661,8 +1693,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 @@ -1771,8 +1803,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 @@ -1816,10 +1848,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 { diff --git a/kernel/scopeinfo.h b/kernel/scopeinfo.h index 8c6e99fc066..f3ae0d7b682 100644 --- a/kernel/scopeinfo.h +++ b/kernel/scopeinfo.h @@ -169,8 +169,11 @@ class IdTree 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(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::Cursor cursor ) { log("%p %s", cursor.target, log_id(cursor.scope_name)); } diff --git a/kernel/sigtools.h b/kernel/sigtools.h index 63f1b51ec84..a22685ee2c3 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_into(Hasher h) const { + h.eat(first->name); + h.eat(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_into(Hasher h) const { + h.eat(first->name); + h.eat(second); + return h; + } }; dict> bits; diff --git a/kernel/timinginfo.h b/kernel/timinginfo.h index 8eb7eb738ac..677bbecb85d 100644 --- a/kernel/timinginfo.h +++ b/kernel/timinginfo.h @@ -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 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 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 bdd7303aa45..7cf60f068f1 100644 --- a/kernel/yosys.cc +++ b/kernel/yosys.cc @@ -93,6 +93,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 29dcd58c55a..e0bd91ec897 100644 --- a/kernel/yosys_common.h +++ b/kernel/yosys_common.h @@ -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::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; @@ -347,10 +345,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..34546d1e246 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_into(Hasher h) const { h.eat(*this); return h; } }; struct WitnessHierarchyItem { diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py index c62e624d2d2..03374c61019 100644 --- a/misc/py_wrap_generator.py +++ b/misc/py_wrap_generator.py @@ -855,7 +855,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" @@ -956,7 +960,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) ] ), @@ -970,23 +974,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..cecab4d61da 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_into(Hasher h) const { h.eat(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..5f5246de67b 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_into(Hasher h) const { + h.eat(name); + h.eat(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..195cb342448 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_into(Hasher h) const { + h.eat(type); + h.eat(parameters); + h.eat(port_sizes); + h.eat(connections); return h; } }; diff --git a/passes/proc/proc_dlatch.cc b/passes/proc/proc_dlatch.cc index 5b392ce512b..2e41afd09c6 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_into(Hasher h) const { + h.eat(signal); + h.eat(match); + h.eat(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..c18beafa1ee 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_into(Hasher h) const { - return mkhash_add(name.hash(), bit); + h.eat(name); + h.eat(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_into(Hasher h) const { - return mkhash(bit.hash(), inverted); + h.eat(bit); + h.eat(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 44fdc7e47aa..bdfb94170d8 100644 --- a/passes/techmap/clockgate.cc +++ b/passes/techmap/clockgate.cc @@ -233,10 +233,9 @@ struct ClockgatePass : public Pass { SigBit ce_bit; bool pol_clk; bool pol_ce; - unsigned int hash() const { + Hasher hash_into(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.eat(t); return h; } bool operator==(const ClkNetInfo& other) const { diff --git a/passes/techmap/flowmap.cc b/passes/techmap/flowmap.cc index b5b95132364..00d5369e4e0 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_into(Hasher h) const { - return hash_ops>::hash({node, is_bottom}); + std::pair p = {node, is_bottom}; + h.eat(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 b42823d8b10..bda7fb3bd24 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_into(Hasher h) const { h.eat(connections); return h; } bool operator==(const DspConfig &ref) const { return connections == ref.connections; } };