Compare commits

...

31 Commits

Author SHA1 Message Date
Emil J 65c24647aa
Merge e0285101c2 into 29e8812bab 2024-11-26 04:07:22 +13:00
Miodrag Milanović 29e8812bab
Merge pull request #4724 from YosysHQ/micko/blackbox_verific
verific: fix blackbox regression and add test case
2024-11-25 15:06:54 +01:00
Miodrag Milanović 9512ec4bbc
Merge pull request #4764 from YosysHQ/micko/verific_vhdl_assert
verific : VHDL assert DFF initial value set on Verific library patch
2024-11-25 15:06:36 +01:00
Miodrag Milanovic d6bd521487 verific : VHDL assert DFF initial value set on Verific library patch side 2024-11-21 13:43:26 +01:00
Emil J. Tywoniak e0285101c2 hashlib: fixes from jix 2024-11-20 17:06:49 +01:00
Emil J. Tywoniak 80ec052878 hashlib: fixes from jix 2024-11-20 12:11:37 +01:00
Emil J. Tywoniak ae5b715107 hashlib: hash_eat -> hash_into 2024-11-19 20:04:19 +01:00
Emil J. Tywoniak 00a0457060 hashlib: declare YS_HASHING_VERSION = 1 2024-11-19 20:01:41 +01:00
Emil J. Tywoniak 5584e74128 hashlib: legacy mkhash_add -> djb2_add 2024-11-14 14:44:24 +01:00
Emil J. Tywoniak f2f7d3e5e1 hashlib: acc -> eat 2024-11-14 14:44:22 +01:00
Emil J. Tywoniak 7bffd7c885 hashlib: add deprecated mkhash function to prevent plugin breakage 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 795c0fb2d8 docs: move hashing-based container details into internal docs from guidelines 2024-11-14 14:43:40 +01:00
Emil J 9fcbcf3e0d docs: formatting and fixes
Co-authored-by: KrystalDelusion <93062060+KrystalDelusion@users.noreply.github.com>
2024-11-14 14:43:40 +01:00
Krystine Sherwin 3406b20006 Docs: Formatting and fixes 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 5dc3cb1ccc docs: document the ideas behind the hashing interface 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 950478edaa hashlib: run_hash uses hash_top_ops, not hash_ops 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak f83d6aff97 hashlib: remove is_new from HasherDJB32, implement hash_top for IdString 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak d4b7bf3ddf hashlib: restore hash_obj_ops for pointers to indexed types 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak aacb9ca55f hash: solo hashing interface, override for SigBit 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 3da1d26088 hashlib: prevent naive hashing of IdString when hashing SigBit 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak f79e634d33 hashlib: allow forcing Hasher state, use it for IdString trivial hashing 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 8f160c5ef7 hashlib: don't xorshift in between upper and lower word 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 0e6f631b5e hashlib: fudge always 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 7cbdc925ee hashlib: hash_t can be set to 64-bit 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 4923f12f28 hashlib: use hash_t across the board 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 3e6e5be519 hashlib: only include in one place 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak a2880c96da hashlib: fix pyosys 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak 0c1bc86119 abc: sort stats 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak fdcca7c165 driver: add --hash-seed 2024-11-14 14:43:40 +01:00
Emil J. Tywoniak ea0be3b403 hashlib: redo interface for flexibility 2024-11-14 14:43:39 +01:00
Miodrag Milanovic df391f5816 verific: fix blackbox regression and add test case 2024-11-08 14:57:04 +01:00
44 changed files with 1168 additions and 785 deletions

View File

@ -47,7 +47,7 @@ struct Scheduler {
struct Vertex { struct Vertex {
T *data; T *data;
Vertex *prev, *next; Vertex *prev, *next;
pool<Vertex*, hash_ptr_ops> preds, succs; pool<Vertex*> preds, succs;
Vertex() : data(NULL), prev(this), next(this) {} Vertex() : data(NULL), prev(this), next(this) {}
Vertex(T *data) : data(data), prev(NULL), next(NULL) {} Vertex(T *data) : data(data), prev(NULL), next(NULL) {}
@ -300,10 +300,10 @@ struct FlowGraph {
}; };
std::vector<Node*> nodes; std::vector<Node*> nodes;
dict<const RTLIL::Wire*, pool<Node*, hash_ptr_ops>> wire_comb_defs, wire_sync_defs, wire_uses; dict<const RTLIL::Wire*, pool<Node*>> wire_comb_defs, wire_sync_defs, wire_uses;
dict<Node*, pool<const RTLIL::Wire*>, hash_ptr_ops> node_comb_defs, node_sync_defs, node_uses; dict<Node*, pool<const RTLIL::Wire*>> node_comb_defs, node_sync_defs, node_uses;
dict<const RTLIL::Wire*, bool> wire_def_inlinable; dict<const RTLIL::Wire*, bool> wire_def_inlinable;
dict<const RTLIL::Wire*, dict<Node*, bool, hash_ptr_ops>> wire_use_inlinable; dict<const RTLIL::Wire*, dict<Node*, bool>> wire_use_inlinable;
dict<RTLIL::SigBit, bool> bit_has_state; dict<RTLIL::SigBit, bool> bit_has_state;
~FlowGraph() ~FlowGraph()
@ -365,7 +365,7 @@ struct FlowGraph {
return false; return false;
} }
bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*, hash_ptr_ops> &nodes) const bool is_inlinable(const RTLIL::Wire *wire, const pool<Node*> &nodes) const
{ {
// Can the wire be inlined, knowing that the given nodes are reachable? // Can the wire be inlined, knowing that the given nodes are reachable?
if (nodes.size() != 1) 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 // without feedback arcs can generally be evaluated in a single pass, i.e. it always requires only
// a single delta cycle. // a single delta cycle.
Scheduler<FlowGraph::Node> scheduler; Scheduler<FlowGraph::Node> scheduler;
dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*, hash_ptr_ops> node_vertex_map; dict<FlowGraph::Node*, Scheduler<FlowGraph::Node>::Vertex*> node_vertex_map;
for (auto node : flow.nodes) for (auto node : flow.nodes)
node_vertex_map[node] = scheduler.add(node); node_vertex_map[node] = scheduler.add(node);
for (auto node_comb_def : flow.node_comb_defs) { for (auto node_comb_def : flow.node_comb_defs) {
@ -3095,7 +3095,7 @@ struct CxxrtlWorker {
// Find out whether the order includes any feedback arcs. // Find out whether the order includes any feedback arcs.
std::vector<FlowGraph::Node*> node_order; std::vector<FlowGraph::Node*> node_order;
pool<FlowGraph::Node*, hash_ptr_ops> evaluated_nodes; pool<FlowGraph::Node*> evaluated_nodes;
pool<const RTLIL::Wire*> feedback_wires; pool<const RTLIL::Wire*> feedback_wires;
for (auto vertex : scheduler.schedule()) { for (auto vertex : scheduler.schedule()) {
auto node = vertex->data; auto node = vertex->data;
@ -3139,7 +3139,7 @@ struct CxxrtlWorker {
} }
// Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users. // Discover nodes reachable from primary outputs (i.e. members) and collect reachable wire users.
pool<FlowGraph::Node*, hash_ptr_ops> worklist; pool<FlowGraph::Node*> worklist;
for (auto node : flow.nodes) { for (auto node : flow.nodes) {
if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type)) if (node->type == FlowGraph::Node::Type::CELL_EVAL && !is_internal_cell(node->cell->type))
worklist.insert(node); // node evaluates a submodule worklist.insert(node); // node evaluates a submodule
@ -3159,8 +3159,8 @@ struct CxxrtlWorker {
worklist.insert(node); // node drives public wires worklist.insert(node); // node drives public wires
} }
} }
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> live_wires; dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> live_wires;
pool<FlowGraph::Node*, hash_ptr_ops> live_nodes; pool<FlowGraph::Node*> live_nodes;
while (!worklist.empty()) { while (!worklist.empty()) {
auto node = worklist.pop(); auto node = worklist.pop();
live_nodes.insert(node); 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) // Discover nodes reachable from primary outputs (i.e. outlines) up until primary inputs (i.e. members)
// and collect reachable wire users. // and collect reachable wire users.
pool<FlowGraph::Node*, hash_ptr_ops> worklist; pool<FlowGraph::Node*> worklist;
for (auto node : flow.nodes) { for (auto node : flow.nodes) {
if (flow.node_comb_defs.count(node)) if (flow.node_comb_defs.count(node))
for (auto wire : flow.node_comb_defs[node]) for (auto wire : flow.node_comb_defs[node])
if (debug_wire_types[wire].is_outline()) if (debug_wire_types[wire].is_outline())
worklist.insert(node); // node drives outline worklist.insert(node); // node drives outline
} }
dict<const RTLIL::Wire*, pool<FlowGraph::Node*, hash_ptr_ops>> debug_live_wires; dict<const RTLIL::Wire*, pool<FlowGraph::Node*>> debug_live_wires;
pool<FlowGraph::Node*, hash_ptr_ops> debug_live_nodes; pool<FlowGraph::Node*> debug_live_nodes;
while (!worklist.empty()) { while (!worklist.empty()) {
auto node = worklist.pop(); auto node = worklist.pop();
debug_live_nodes.insert(node); debug_live_nodes.insert(node);

View File

@ -0,0 +1,155 @@
Hashing and associative data structures in Yosys
------------------------------------------------
Container classes based on hashing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yosys uses ``dict<K, T>`` and ``pool<T>`` as main container classes.
``dict<K, T>`` is essentially a replacement for ``std::unordered_map<K, T>``
and ``pool<T>`` is a replacement for ``std::unordered_set<T>``.
The main characteristics are:
* ``dict<K, T>`` and ``pool<T>`` 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<K, T>`` or ``pool<T>`` are invalidated by
insert and remove operations (similar to ``std::vector<T>`` 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<K, T>`` and ``pool<T>`` will have the same order of iteration across
all compilers, standard libraries and architectures.
In addition to ``dict<K, T>`` and ``pool<T>`` there is also an ``idict<K>`` that
creates a bijective map from ``K`` to the integers. For example:
::
idict<string, 42> 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<K>`` 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<T>(const T& obj)``, corresponding to ``hash_top_ops<T>::hash(obj)``,
the default implementation of which is ``hash_ops<T>::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<T>`` 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<T>`` 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<Wire*>``
and others in ``kernel/hashlib.h`` that actually dereference the pointers and
call ``hash_into`` on the instances pointed to.
``hash_ops<T>`` is also specialized for simple compound types like
``std::pair<U>`` by calling hash_into in sequence on its members. For flexible
size containers like ``std::vector<U>`` 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<RTLIL::SigBit>`` 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.

View File

@ -38,3 +38,4 @@ as reference to implement a similar system in any language.
formats/index formats/index
extending_yosys/index extending_yosys/index
techmap techmap
hashing

View File

@ -90,7 +90,7 @@ struct ScopeinfoExamplePass : public Pass {
// Shuffle wires so this example produces more interesting outputs // Shuffle wires so this example produces more interesting outputs
std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) { 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); ModuleHdlnameIndex index(module);

View File

@ -177,7 +177,7 @@ namespace AST
{ {
// for dict<> and pool<> // for dict<> and pool<>
unsigned int hashidx_; unsigned int hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
// this nodes type // this nodes type
AstNodeType type; AstNodeType type;

View File

@ -611,7 +611,7 @@ RTLIL::SigSpec VerificImporter::operatorInportCase(Instance *inst, const char *p
} }
} }
RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*, hash_ptr_ops> *any_all_nets) RTLIL::SigSpec VerificImporter::operatorOutput(Instance *inst, const pool<Net*> *any_all_nets)
{ {
RTLIL::SigSpec sig; RTLIL::SigSpec sig;
RTLIL::Wire *dummy_wire = NULL; RTLIL::Wire *dummy_wire = NULL;
@ -1567,9 +1567,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
module->fixup_ports(); module->fixup_ports();
dict<Net*, char, hash_ptr_ops> init_nets; dict<Net*, char> init_nets;
pool<Net*, hash_ptr_ops> anyconst_nets, anyseq_nets; pool<Net*> anyconst_nets, anyseq_nets;
pool<Net*, hash_ptr_ops> allconst_nets, allseq_nets; pool<Net*> allconst_nets, allseq_nets;
any_all_nets.clear(); any_all_nets.clear();
FOREACH_NET_OF_NETLIST(nl, mi, net) FOREACH_NET_OF_NETLIST(nl, mi, net)
@ -1832,10 +1832,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
module->connect(net_map_at(net), module->Anyseq(new_verific_id(net))); module->connect(net_map_at(net), module->Anyseq(new_verific_id(net)));
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT #ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
pool<Instance*, hash_ptr_ops> sva_asserts; pool<Instance*> sva_asserts;
pool<Instance*, hash_ptr_ops> sva_assumes; pool<Instance*> sva_assumes;
pool<Instance*, hash_ptr_ops> sva_covers; pool<Instance*> sva_covers;
pool<Instance*, hash_ptr_ops> sva_triggers; pool<Instance*> sva_triggers;
#endif #endif
pool<RTLIL::Cell*> past_ffs; pool<RTLIL::Cell*> past_ffs;
@ -2126,12 +2126,6 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
log(" assert condition %s.\n", log_signal(cond)); log(" assert condition %s.\n", log_signal(cond));
Cell *cell = module->addAssert(new_verific_id(inst), cond, State::S1); Cell *cell = module->addAssert(new_verific_id(inst), cond, State::S1);
// Initialize FF feeding condition to 1, in case it is not
// used by rest of design logic, to prevent failing on
// initial uninitialized state
if (cond.is_wire() && !cond.wire->name.isPublic())
cond.wire->attributes[ID::init] = Const(1,1);
import_attributes(cell->attributes, inst); import_attributes(cell->attributes, inst);
continue; continue;
} }
@ -3428,6 +3422,7 @@ struct VerificPass : public Pass {
RuntimeFlags::SetVar("veri_preserve_assignments", 1); RuntimeFlags::SetVar("veri_preserve_assignments", 1);
RuntimeFlags::SetVar("veri_preserve_comments", 1); RuntimeFlags::SetVar("veri_preserve_comments", 1);
RuntimeFlags::SetVar("veri_preserve_drivers", 1); RuntimeFlags::SetVar("veri_preserve_drivers", 1);
RuntimeFlags::SetVar("veri_create_empty_box", 1);
// Workaround for VIPER #13851 // Workaround for VIPER #13851
RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1); RuntimeFlags::SetVar("veri_create_name_for_unnamed_gen_block", 1);

View File

@ -71,7 +71,7 @@ struct VerificImporter
std::map<Verific::Net*, RTLIL::SigBit> net_map; std::map<Verific::Net*, RTLIL::SigBit> net_map;
std::map<Verific::Net*, Verific::Net*> sva_posedge_map; std::map<Verific::Net*, Verific::Net*> sva_posedge_map;
pool<Verific::Net*, hash_ptr_ops> any_all_nets; pool<Verific::Net*> any_all_nets;
bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific; bool mode_gates, mode_keep, mode_nosva, mode_names, mode_verific;
bool mode_autocover, mode_fullinit; bool mode_autocover, mode_fullinit;
@ -89,7 +89,7 @@ struct VerificImporter
RTLIL::SigSpec operatorInput2(Verific::Instance *inst); RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname); RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname);
RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname); RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname);
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*, hash_ptr_ops> *any_all_nets = nullptr); RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<Verific::Net*> *any_all_nets = nullptr);
bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name); bool import_netlist_instance_gates(Verific::Instance *inst, RTLIL::IdString inst_name);
bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name); bool import_netlist_instance_cells(Verific::Instance *inst, RTLIL::IdString inst_name);

View File

@ -1051,7 +1051,7 @@ struct VerificSvaImporter
msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile()); msg.c_str(), inst->View()->Owner()->Name(), inst->Name()), inst->Linefile());
} }
dict<Net*, bool, hash_ptr_ops> check_expression_cache; dict<Net*, bool> check_expression_cache;
bool check_expression(Net *net, bool raise_error = false) bool check_expression(Net *net, bool raise_error = false)
{ {

View File

@ -37,57 +37,15 @@ And then executed using the following command:
Yosys Data Structures Yosys Data Structures
--------------------- ---------------------
Here is a short list of data structures that you should make yourself familiar 1. Container classes based on hashing
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. Yosys Container Classes Yosys heavily relies on custom container data structures such as dict or pool
defined in kernel/hashlib.h.
dict<K, T> is essentially a replacement for std::unordered_map<K, T>
and pool<T> is a replacement for std::unordered_set<T>. Please refer to
docs/source/yosys_internals/hashing.rst for more information on those.
Yosys uses dict<K, T> and pool<T> as main container classes. dict<K, T> is Otherwise, Yosys makes use of the following:
essentially a replacement for std::unordered_map<K, T> and pool<T> is a
replacement for std::unordered_set<T>. The main characteristics are:
- dict<K, T> and pool<T> are about 2x faster than the std containers
- references to elements in a dict<K, T> or pool<T> are invalidated by
insert and remove operations (similar to std::vector<T> 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<K, T> and pool<T> will have the same order of iteration across
all compilers, standard libraries and architectures.
In addition to dict<K, T> and pool<T> there is also an idict<K> that
creates a bijective map from K to the integers. For example:
idict<string, 42> 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<K> implements a merge-find set data structure (aka. disjoint-set or
union-find) over the type K ("mfp" = merge-find-promote).
2. Standard STL data types 2. Standard STL data types

View File

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@
*/ */
#include "kernel/yosys.h" #include "kernel/yosys.h"
#include "kernel/hashlib.h"
#include "libs/sha1/sha1.h" #include "libs/sha1/sha1.h"
#include "libs/cxxopts/include/cxxopts.hpp" #include "libs/cxxopts/include/cxxopts.hpp"
#include <iostream> #include <iostream>
@ -276,6 +277,8 @@ int main(int argc, char **argv)
options.add_options("developer") options.add_options("developer")
("X,trace", "enable tracing of core data structure changes. for debugging") ("X,trace", "enable tracing of core data structure changes. for debugging")
("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging") ("M,randomize-pointers", "will slightly randomize allocated pointer addresses. for debugging")
("hash-seed", "mix up hashing values with <seed>, for extreme optimization and testing",
cxxopts::value<uint64_t>(), "<seed>")
("A,abort", "will call abort() at the end of the script. for debugging") ("A,abort", "will call abort() at the end of the script. for debugging")
("x,experimental", "do not print warnings for the experimental <feature>", ("x,experimental", "do not print warnings for the experimental <feature>",
cxxopts::value<std::vector<std::string>>(), "<feature>") cxxopts::value<std::vector<std::string>>(), "<feature>")
@ -427,6 +430,10 @@ int main(int argc, char **argv)
if (result.count("infile")) { if (result.count("infile")) {
frontend_files = result["infile"].as<std::vector<std::string>>(); frontend_files = result["infile"].as<std::vector<std::string>>();
} }
if (result.count("hash-seed")) {
int seed = result["hash-seed"].as<uint64_t>();
Hasher::set_fudge((Hasher::hash_t)seed);
}
if (log_errfile == NULL) { if (log_errfile == NULL) {
log_files.push_back(stdout); log_files.push_back(stdout);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -76,11 +76,13 @@ namespace RTLIL
struct SyncRule; struct SyncRule;
struct Process; struct Process;
struct Binding; struct Binding;
struct IdString;
typedef std::pair<SigSpec, SigSpec> SigSig; typedef std::pair<SigSpec, SigSpec> SigSig;
};
struct IdString struct RTLIL::IdString
{ {
#undef YOSYS_XTRACE_GET_PUT #undef YOSYS_XTRACE_GET_PUT
#undef YOSYS_SORT_ID_FREE_LIST #undef YOSYS_SORT_ID_FREE_LIST
#undef YOSYS_USE_STICKY_IDS #undef YOSYS_USE_STICKY_IDS
@ -95,16 +97,16 @@ namespace RTLIL
} destruct_guard; } destruct_guard;
static std::vector<char*> global_id_storage_; static std::vector<char*> global_id_storage_;
static dict<char*, int, hash_cstr_ops> global_id_index_; static dict<char*, int> global_id_index_;
#ifndef YOSYS_NO_IDS_REFCNT #ifndef YOSYS_NO_IDS_REFCNT
static std::vector<int> global_refcount_storage_; static std::vector<int> global_refcount_storage_;
static std::vector<int> global_free_idx_list_; static std::vector<int> global_free_idx_list_;
#endif #endif
#ifdef YOSYS_USE_STICKY_IDS #ifdef YOSYS_USE_STICKY_IDS
static int last_created_idx_ptr_; static int last_created_idx_ptr_;
static int last_created_idx_[8]; static int last_created_idx_[8];
#endif #endif
static inline void xtrace_db_dump() static inline void xtrace_db_dump()
{ {
@ -223,7 +225,7 @@ namespace RTLIL
return idx; return idx;
} }
#ifndef YOSYS_NO_IDS_REFCNT #ifndef YOSYS_NO_IDS_REFCNT
static inline void put_reference(int idx) static inline void put_reference(int idx)
{ {
// put_reference() may be called from destructors after the destructor of // put_reference() may be called from destructors after the destructor of
@ -257,9 +259,9 @@ namespace RTLIL
global_id_storage_.at(idx) = nullptr; global_id_storage_.at(idx) = nullptr;
global_free_idx_list_.push_back(idx); global_free_idx_list_.push_back(idx);
} }
#else #else
static inline void put_reference(int) { } static inline void put_reference(int) { }
#endif #endif
// the actual IdString object is just is a single int // the actual IdString object is just is a single int
@ -360,8 +362,12 @@ namespace RTLIL
*this = IdString(); *this = IdString();
} }
unsigned int hash() const { Hasher hash_into(Hasher h) const { return hash_ops<int>::hash_into(index_, h); }
return index_;
Hasher hash_top() const {
Hasher h;
h.force((Hasher::hash_t) index_);
return h;
} }
// The following is a helper key_compare class. Instead of for example std::set<Cell*> // The following is a helper key_compare class. Instead of for example std::set<Cell*>
@ -376,29 +382,48 @@ namespace RTLIL
// often one needs to check if a given IdString is part of a list (for example a list // 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. // of cell types). the following functions helps with that.
template<typename... Args> template<typename... Args>
bool in(Args... args) const { bool in(Args... args) const {
// Credit: https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html return (... || in(args));
bool result = false;
(void) std::initializer_list<int>{ (result = result || in(args), 0)... };
return result;
} }
bool in(const IdString &rhs) const { return *this == rhs; } bool in(const IdString &rhs) const { return *this == rhs; }
bool in(const char *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 std::string &rhs) const { return *this == rhs; }
bool in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; } inline bool in(const pool<IdString> &rhs) const;
inline bool in(const pool<IdString> &&rhs) const;
bool isPublic() const { return begins_with("\\"); } bool isPublic() const { return begins_with("\\"); }
}; };
namespace hashlib {
template <>
struct hash_top_ops<RTLIL::IdString> {
static inline bool cmp(const RTLIL::IdString &a, const RTLIL::IdString &b) {
return a == b;
}
static inline Hasher hash(const RTLIL::IdString id) {
return id.hash_top();
}
};
};
/**
* How to not use these methods:
* 1. if(celltype.in({...})) -> if(celltype.in(...))
* 2. pool<IdString> p; ... a.in(p) -> (bool)p.count(a)
*/
[[deprecated]]
inline bool RTLIL::IdString::in(const pool<IdString> &rhs) const { return rhs.count(*this) != 0; }
[[deprecated]]
inline bool RTLIL::IdString::in(const pool<IdString> &&rhs) const { return rhs.count(*this) != 0; }
namespace RTLIL {
namespace ID { namespace ID {
#define X(_id) extern IdString _id; #define X(_id) extern IdString _id;
#include "kernel/constids.inc" #include "kernel/constids.inc"
#undef X #undef X
}; };
extern dict<std::string, std::string> constpad; extern dict<std::string, std::string> constpad;
const pool<IdString> &builtin_ff_cell_types(); const pool<IdString> &builtin_ff_cell_types();
@ -796,11 +821,10 @@ public:
bv.resize(width, bv.empty() ? RTLIL::State::Sx : bv.back()); bv.resize(width, bv.empty() ? RTLIL::State::Sx : bv.back());
} }
inline unsigned int hash() const { inline Hasher hash_into(Hasher h) const {
unsigned int h = mkhash_init; h.eat(size());
for (auto b : *this)
for (State b : *this) h.eat(b);
h = mkhash(h, b);
return h; return h;
} }
}; };
@ -889,7 +913,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; bool operator ==(const RTLIL::SigBit &other) const;
bool operator !=(const RTLIL::SigBit &other) const; bool operator !=(const RTLIL::SigBit &other) const;
unsigned int hash() const; Hasher hash_into(Hasher h) const;
Hasher hash_top() const;
};
namespace hashlib {
template <>
struct hash_top_ops<RTLIL::SigBit> {
static inline bool cmp(const RTLIL::SigBit &a, const RTLIL::SigBit &b) {
return a == b;
}
static inline Hasher hash(const RTLIL::SigBit sb) {
return sb.hash_top();
}
};
}; };
struct RTLIL::SigSpecIterator struct RTLIL::SigSpecIterator
@ -930,7 +967,7 @@ struct RTLIL::SigSpec
{ {
private: private:
int width_; int width_;
unsigned long hash_; Hasher::hash_t hash_;
std::vector<RTLIL::SigChunk> chunks_; // LSB at index 0 std::vector<RTLIL::SigChunk> chunks_; // LSB at index 0
std::vector<RTLIL::SigBit> bits_; // LSB at index 0 std::vector<RTLIL::SigBit> bits_; // LSB at index 0
@ -971,11 +1008,6 @@ public:
SigSpec(const std::set<RTLIL::SigBit> &bits); SigSpec(const std::set<RTLIL::SigBit> &bits);
explicit SigSpec(bool bit); explicit SigSpec(bool bit);
size_t get_hash() const {
if (!hash_) hash();
return hash_;
}
inline const std::vector<RTLIL::SigChunk> &chunks() const { pack(); return chunks_; } inline const std::vector<RTLIL::SigChunk> &chunks() const { pack(); return chunks_; }
inline const std::vector<RTLIL::SigBit> &bits() const { inline_unpack(); return bits_; } inline const std::vector<RTLIL::SigBit> &bits() const { inline_unpack(); return bits_; }
@ -1082,7 +1114,7 @@ public:
operator std::vector<RTLIL::SigBit>() const { return bits(); } operator std::vector<RTLIL::SigBit>() const { return bits(); }
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < width_ ? (*this)[offset] : defval; } 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 #ifndef NDEBUG
void check(Module *mod = nullptr) const; void check(Module *mod = nullptr) const;
@ -1123,8 +1155,8 @@ struct RTLIL::Selection
struct RTLIL::Monitor struct RTLIL::Monitor
{ {
unsigned int hashidx_; Hasher::hash_t hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
Monitor() { Monitor() {
static unsigned int hashidx_count = 123456789; static unsigned int hashidx_count = 123456789;
@ -1146,8 +1178,8 @@ struct define_map_t;
struct RTLIL::Design struct RTLIL::Design
{ {
unsigned int hashidx_; Hasher::hash_t hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
pool<RTLIL::Monitor*> monitors; pool<RTLIL::Monitor*> monitors;
dict<std::string, std::string> scratchpad; dict<std::string, std::string> scratchpad;
@ -1251,8 +1283,8 @@ struct RTLIL::Design
struct RTLIL::Module : public RTLIL::AttrObject struct RTLIL::Module : public RTLIL::AttrObject
{ {
unsigned int hashidx_; Hasher::hash_t hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected: protected:
void add(RTLIL::Wire *wire); void add(RTLIL::Wire *wire);
@ -1602,8 +1634,8 @@ void dump_wire(std::ostream &f, std::string indent, const RTLIL::Wire *wire);
struct RTLIL::Wire : public RTLIL::AttrObject struct RTLIL::Wire : public RTLIL::AttrObject
{ {
unsigned int hashidx_; Hasher::hash_t hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected: protected:
// use module->addWire() and module->remove() to create or destroy wires // use module->addWire() and module->remove() to create or destroy wires
@ -1641,8 +1673,8 @@ inline int GetSize(RTLIL::Wire *wire) {
struct RTLIL::Memory : public RTLIL::AttrObject struct RTLIL::Memory : public RTLIL::AttrObject
{ {
unsigned int hashidx_; Hasher::hash_t hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
Memory(); Memory();
@ -1656,8 +1688,8 @@ struct RTLIL::Memory : public RTLIL::AttrObject
struct RTLIL::Cell : public RTLIL::AttrObject struct RTLIL::Cell : public RTLIL::AttrObject
{ {
unsigned int hashidx_; Hasher::hash_t hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected: protected:
// use module->addCell() and module->remove() to create or destroy cells // use module->addCell() and module->remove() to create or destroy cells
@ -1766,8 +1798,8 @@ struct RTLIL::SyncRule
struct RTLIL::Process : public RTLIL::AttrObject struct RTLIL::Process : public RTLIL::AttrObject
{ {
unsigned int hashidx_; Hasher::hash_t hashidx_;
unsigned int hash() const { return hashidx_; } Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; }
protected: protected:
// use module->addProcess() and module->remove() to create or destroy processes // use module->addProcess() and module->remove() to create or destroy processes
@ -1811,10 +1843,25 @@ inline bool RTLIL::SigBit::operator!=(const RTLIL::SigBit &other) const {
return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data)); return (wire != other.wire) || (wire ? (offset != other.offset) : (data != other.data));
} }
inline unsigned int RTLIL::SigBit::hash() const { inline Hasher RTLIL::SigBit::hash_into(Hasher h) const {
if (wire) if (wire) {
return mkhash_add(wire->name.hash(), offset); h.eat(offset);
return data; 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 { inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -855,7 +855,11 @@ class WClass:
if self.hash_id != None: if self.hash_id != None:
text += "\n\t\tunsigned int get_hash_py()" text += "\n\t\tunsigned int get_hash_py()"
text += "\n\t\t{" 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\t}"
text += "\n\t};\n" text += "\n\t};\n"
@ -956,7 +960,7 @@ class WClass:
sources = [ sources = [
Source("kernel/celltypes",[ 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) WClass("CellTypes", link_types.pointer, None, None, None, True)
] ]
), ),
@ -970,23 +974,23 @@ sources = [
] ]
), ),
Source("kernel/rtlil",[ Source("kernel/rtlil",[
WClass("IdString", link_types.ref_copy, None, "str()", "hash()"), WClass("IdString", link_types.ref_copy, None, "str()", ""),
WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"), WClass("Const", link_types.ref_copy, None, "as_string()", ""),
WClass("AttrObject", link_types.ref_copy, None, None, None), WClass("AttrObject", link_types.ref_copy, None, None, None),
WClass("Selection", 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("Monitor", link_types.derive, None, None, None),
WClass("CaseRule",link_types.ref_copy, None, None, None, True), WClass("CaseRule",link_types.ref_copy, None, None, None, True),
WClass("SwitchRule",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("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("SigChunk", link_types.ref_copy, None, None, None),
WClass("SigBit", link_types.ref_copy, None, None, "hash()"), WClass("SigBit", link_types.ref_copy, None, None, ""),
WClass("SigSpec", link_types.ref_copy, None, None, "hash()"), WClass("SigSpec", link_types.ref_copy, None, None, ""),
WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"), 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()", "hash()"), 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()", "hash()"), 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()", "hash()"), 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_", "hash()") WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "")
] ]
), ),
#Source("kernel/satgen",[ #Source("kernel/satgen",[

View File

@ -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; }
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<int>::hash(index); } Hasher hash_into(Hasher h) const { h.eat(index); return h; }
bool empty() const { return index == 0; } bool empty() const { return index == 0; }
}; };

View File

@ -52,8 +52,10 @@ struct ExampleDtPass : public Pass
return name == other.name && parameters == other.parameters; return name == other.name && parameters == other.parameters;
} }
unsigned int hash() const { Hasher hash_into(Hasher h) const {
return mkhash(name.hash(), parameters.hash()); h.eat(name);
h.eat(parameters);
return h;
} }
}; };

View File

@ -20,7 +20,6 @@
#include "kernel/register.h" #include "kernel/register.h"
#include "kernel/rtlil.h" #include "kernel/rtlil.h"
#include "kernel/log.h" #include "kernel/log.h"
#include "kernel/hashlib.h"
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN PRIVATE_NAMESPACE_BEGIN

View File

@ -70,13 +70,13 @@ struct GraphNode {
pool<IdString> names_; pool<IdString> names_;
dict<int, uint8_t> tags_; dict<int, uint8_t> tags_;
pool<GraphNode*, hash_ptr_ops> upstream_; pool<GraphNode*> upstream_;
pool<GraphNode*, hash_ptr_ops> downstream_; pool<GraphNode*> downstream_;
pool<IdString> &names() { return get()->names_; } pool<IdString> &names() { return get()->names_; }
dict<int, uint8_t> &tags() { return get()->tags_; } dict<int, uint8_t> &tags() { return get()->tags_; }
pool<GraphNode*, hash_ptr_ops> &upstream() { return get()->upstream_; } pool<GraphNode*> &upstream() { return get()->upstream_; }
pool<GraphNode*, hash_ptr_ops> &downstream() { return get()->downstream_; } pool<GraphNode*> &downstream() { return get()->downstream_; }
uint8_t tag(int index) { uint8_t tag(int index) {
return tags().at(index, 0); return tags().at(index, 0);
@ -154,8 +154,8 @@ struct Graph {
nodes.push_back(n); nodes.push_back(n);
n->index = GetSize(nodes); n->index = GetSize(nodes);
pool<GraphNode*, hash_ptr_ops> new_upstream; pool<GraphNode*> new_upstream;
pool<GraphNode*, hash_ptr_ops> new_downstream; pool<GraphNode*> new_downstream;
for (auto g : n->upstream()) { for (auto g : n->upstream()) {
if (n != (g = g->get())) if (n != (g = g->get()))
@ -302,7 +302,7 @@ struct Graph {
} }
} }
pool<GraphNode*, hash_ptr_ops> excluded; pool<GraphNode*> excluded;
for (auto grp : config.groups) for (auto grp : config.groups)
{ {
@ -348,7 +348,7 @@ struct Graph {
excluded.insert(g->get()); excluded.insert(g->get());
dict<Cell*, GraphNode*> cell_nodes; dict<Cell*, GraphNode*> cell_nodes;
dict<SigBit, pool<GraphNode*, hash_ptr_ops>> sig_users; dict<SigBit, pool<GraphNode*>> sig_users;
for (auto cell : module->selected_cells()) { for (auto cell : module->selected_cells()) {
auto g = new GraphNode; auto g = new GraphNode;
@ -483,8 +483,8 @@ struct Graph {
{ {
header("Any nodes with identical connections"); header("Any nodes with identical connections");
typedef pair<pool<GraphNode*, hash_ptr_ops>, pool<GraphNode*, hash_ptr_ops>> node_conn_t; typedef pair<pool<GraphNode*>, pool<GraphNode*>> node_conn_t;
dict<node_conn_t, pool<GraphNode*, hash_ptr_ops>> nodes_by_conn; dict<node_conn_t, pool<GraphNode*>> nodes_by_conn;
for (auto g : term ? term_nodes : nonterm_nodes) { for (auto g : term ? term_nodes : nonterm_nodes) {
auto &entry = nodes_by_conn[node_conn_t(g->upstream(), g->downstream())]; auto &entry = nodes_by_conn[node_conn_t(g->upstream(), g->downstream())];
for (auto n : entry) for (auto n : entry)
@ -506,8 +506,8 @@ struct Graph {
header("Sibblings with identical tags"); header("Sibblings with identical tags");
for (auto g : nonterm_nodes) { for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) { auto process_conns = [&](const pool<GraphNode*> &stream) {
dict<std::vector<int>, pool<GraphNode*, hash_ptr_ops>> nodes_by_tags; dict<std::vector<int>, pool<GraphNode*>> nodes_by_tags;
for (auto n : stream) { for (auto n : stream) {
if (n->terminal) continue; if (n->terminal) continue;
std::vector<int> key; std::vector<int> key;
@ -556,7 +556,7 @@ struct Graph {
if (!term) { if (!term) {
header("Sibblings with similar tags (strict)"); header("Sibblings with similar tags (strict)");
for (auto g : nonterm_nodes) { for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) { auto process_conns = [&](const pool<GraphNode*> &stream) {
std::vector<GraphNode*> nodes; std::vector<GraphNode*> nodes;
for (auto n : stream) for (auto n : stream)
if (!n->terminal) nodes.push_back(n); if (!n->terminal) nodes.push_back(n);
@ -585,7 +585,7 @@ struct Graph {
if (!term) { if (!term) {
header("Sibblings with similar tags (non-strict)"); header("Sibblings with similar tags (non-strict)");
for (auto g : nonterm_nodes) { for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) { auto process_conns = [&](const pool<GraphNode*> &stream) {
std::vector<GraphNode*> nodes; std::vector<GraphNode*> nodes;
for (auto n : stream) for (auto n : stream)
if (!n->terminal) nodes.push_back(n); if (!n->terminal) nodes.push_back(n);
@ -603,7 +603,7 @@ struct Graph {
{ {
header("Any nodes with identical fan-in or fan-out"); header("Any nodes with identical fan-in or fan-out");
dict<pool<GraphNode*, hash_ptr_ops>, pool<GraphNode*, hash_ptr_ops>> nodes_by_conn[2]; dict<pool<GraphNode*>, pool<GraphNode*>> nodes_by_conn[2];
for (auto g : term ? term_nodes : nonterm_nodes) { for (auto g : term ? term_nodes : nonterm_nodes) {
auto &up_entry = nodes_by_conn[0][g->upstream()]; auto &up_entry = nodes_by_conn[0][g->upstream()];
auto &down_entry = nodes_by_conn[1][g->downstream()]; auto &down_entry = nodes_by_conn[1][g->downstream()];
@ -629,7 +629,7 @@ struct Graph {
if (!term) { if (!term) {
header("Sibblings with similar tags (lax)"); header("Sibblings with similar tags (lax)");
for (auto g : nonterm_nodes) { for (auto g : nonterm_nodes) {
auto process_conns = [&](const pool<GraphNode*, hash_ptr_ops> &stream) { auto process_conns = [&](const pool<GraphNode*> &stream) {
std::vector<GraphNode*> nodes; std::vector<GraphNode*> nodes;
for (auto n : stream) for (auto n : stream)
if (!n->terminal) nodes.push_back(n); if (!n->terminal) nodes.push_back(n);
@ -720,9 +720,9 @@ struct VizWorker
fprintf(f, "digraph \"%s\" {\n", log_id(module)); fprintf(f, "digraph \"%s\" {\n", log_id(module));
fprintf(f, " rankdir = LR;\n"); fprintf(f, " rankdir = LR;\n");
dict<GraphNode*, std::vector<std::vector<std::string>>, hash_ptr_ops> extra_lines; dict<GraphNode*, std::vector<std::vector<std::string>>> extra_lines;
dict<GraphNode*, GraphNode*, hash_ptr_ops> bypass_nodes; dict<GraphNode*, GraphNode*> bypass_nodes;
pool<GraphNode*, hash_ptr_ops> bypass_candidates; pool<GraphNode*> bypass_candidates;
auto bypass = [&](GraphNode *g, GraphNode *n) { auto bypass = [&](GraphNode *g, GraphNode *n) {
log_assert(g->terminal); log_assert(g->terminal);

View File

@ -46,11 +46,11 @@ struct EquivStructWorker
parameters == other.parameters && port_sizes == other.port_sizes; parameters == other.parameters && port_sizes == other.port_sizes;
} }
unsigned int hash() const { Hasher hash_into(Hasher h) const {
unsigned int h = mkhash_init; h.eat(type);
h = mkhash(h, mkhash(type)); h.eat(parameters);
h = mkhash(h, mkhash(parameters)); h.eat(port_sizes);
h = mkhash(h, mkhash(connections)); h.eat(connections);
return h; return h;
} }
}; };

View File

@ -127,11 +127,10 @@ struct proc_dlatch_db_t
return signal == other.signal && match == other.match && children == other.children; return signal == other.signal && match == other.match && children == other.children;
} }
unsigned int hash() const { Hasher hash_into(Hasher h) const {
unsigned int h = mkhash_init; h.eat(signal);
mkhash(h, signal.hash()); h.eat(match);
mkhash(h, match.hash()); h.eat(children);
for (auto i : children) mkhash(h, i);
return h; return h;
} }
}; };

View File

@ -108,8 +108,8 @@ struct SigSnippets
struct SnippetSwCache struct SnippetSwCache
{ {
dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>, hash_ptr_ops> full_case_bits_cache; dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>> full_case_bits_cache;
dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache; dict<RTLIL::SwitchRule*, pool<int>> cache;
const SigSnippets *snippets; const SigSnippets *snippets;
int current_snippet; int current_snippet;
@ -318,7 +318,7 @@ const pool<SigBit> &get_full_case_bits(SnippetSwCache &swcache, RTLIL::SwitchRul
return swcache.full_case_bits_cache.at(sw); return swcache.full_case_bits_cache.at(sw);
} }
RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> &swpara, RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, dict<RTLIL::SwitchRule*, bool> &swpara,
RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode) RTLIL::CaseRule *cs, const RTLIL::SigSpec &sig, const RTLIL::SigSpec &defval, bool ifxmode)
{ {
RTLIL::SigSpec result = defval; RTLIL::SigSpec result = defval;
@ -421,7 +421,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
swcache.snippets = &sigsnip; swcache.snippets = &sigsnip;
swcache.insert(&proc->root_case); swcache.insert(&proc->root_case);
dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara; dict<RTLIL::SwitchRule*, bool> swpara;
int cnt = 0; int cnt = 0;
for (int idx : sigsnip.snippets) for (int idx : sigsnip.snippets)

View File

@ -176,7 +176,7 @@ struct coverdb_t
struct mutate_queue_t struct mutate_queue_t
{ {
pool<mutate_t*, hash_ptr_ops> db; pool<mutate_t*> db;
mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) { mutate_t *pick(xs128_t &rng, coverdb_t &coverdb, const mutate_opts_t &opts) {
mutate_t *m = nullptr; mutate_t *m = nullptr;

View File

@ -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; };
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; 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; };
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; IdBit bit;

View File

@ -161,7 +161,7 @@ struct SimInstance
pool<SigBit> dirty_bits; pool<SigBit> dirty_bits;
pool<Cell*> dirty_cells; pool<Cell*> dirty_cells;
pool<IdString> dirty_memories; pool<IdString> dirty_memories;
pool<SimInstance*, hash_ptr_ops> dirty_children; pool<SimInstance*> dirty_children;
struct ff_state_t struct ff_state_t
{ {

View File

@ -1411,6 +1411,7 @@ void abc_module(RTLIL::Design *design, RTLIL::Module *current_module, std::strin
module->connect(conn); module->connect(conn);
} }
cell_stats.sort();
for (auto &it : cell_stats) for (auto &it : cell_stats)
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second); log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
int in_wires = 0, out_wires = 0; int in_wires = 0, out_wires = 0;

View File

@ -111,7 +111,7 @@ struct AlumaccWorker
dict<RTLIL::SigBit, int> bit_users; dict<RTLIL::SigBit, int> bit_users;
dict<RTLIL::SigSpec, maccnode_t*> sig_macc; dict<RTLIL::SigSpec, maccnode_t*> sig_macc;
dict<RTLIL::SigSig, pool<alunode_t*, hash_ptr_ops>> sig_alu; dict<RTLIL::SigSig, pool<alunode_t*>> sig_alu;
int macc_counter, alu_counter; int macc_counter, alu_counter;
AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module) AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module)
@ -226,7 +226,7 @@ struct AlumaccWorker
{ {
while (1) while (1)
{ {
pool<maccnode_t*, hash_ptr_ops> delete_nodes; pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc) for (auto &it : sig_macc)
{ {
@ -278,7 +278,7 @@ struct AlumaccWorker
void macc_to_alu() void macc_to_alu()
{ {
pool<maccnode_t*, hash_ptr_ops> delete_nodes; pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc) for (auto &it : sig_macc)
{ {

View File

@ -247,10 +247,9 @@ struct ClockgatePass : public Pass {
SigBit ce_bit; SigBit ce_bit;
bool pol_clk; bool pol_clk;
bool pol_ce; 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); auto t = std::make_tuple(clk_bit, ce_bit, pol_clk, pol_ce);
unsigned int h = mkhash_init; h.eat(t);
h = mkhash(h, hash_ops<decltype(t)>::hash(t));
return h; return h;
} }
bool operator==(const ClkNetInfo& other) const { bool operator==(const ClkNetInfo& other) const {

View File

@ -250,9 +250,11 @@ struct FlowGraph
{ {
return !(*this == other); return !(*this == other);
} }
unsigned int hash() const Hasher hash_into(Hasher h) const
{ {
return hash_ops<pair<RTLIL::SigBit, int>>::hash({node, is_bottom}); std::pair<RTLIL::SigBit, int> p = {node, is_bottom};
h.eat(p);
return h;
} }
static NodePrime top(RTLIL::SigBit node) static NodePrime top(RTLIL::SigBit node)

View File

@ -53,7 +53,7 @@ struct QlDspSimdPass : public Pass {
DspConfig(const DspConfig &ref) = default; DspConfig(const DspConfig &ref) = default;
DspConfig(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; } bool operator==(const DspConfig &ref) const { return connections == ref.connections; }
}; };

24
tests/verific/blackbox.ys Normal file
View File

@ -0,0 +1,24 @@
verific -sv -lib <<EOF
module TEST_CELL(input clk, input a, input b, output reg c);
parameter PATH = "DEFAULT";
always @(posedge clk) begin
if (PATH=="DEFAULT")
c <= a;
else
c <= b;
end
endmodule
EOF
verific -sv <<EOF
module top(input clk, input a, input b, output c, output d);
TEST_CELL #(.PATH("TEST")) test1(.clk(clk),.a(a),.b(1'b1),.c(c));
TEST_CELL #(.PATH("DEFAULT")) test2(.clk(clk),.a(a),.b(1'bx),.c(d));
endmodule
EOF
verific -import top
hierarchy -top top
stat
select -assert-count 2 t:TEST_CELL