Compare commits

...

39 Commits

Author SHA1 Message Date
Emil J 78361c051b
Merge e0285101c2 into 18b616578a 2024-11-20 18:27:20 +01:00
George Rennie 18b616578a pyosys: catch boost::python::error_already_set
* This catches exceptions from internal passes, printing them in a
  readable manner where the user would otherwise see an unspecified
  boost exception
2024-11-20 17:54:11 +01:00
Emil J. Tywoniak e0285101c2 hashlib: fixes from jix 2024-11-20 17:06:49 +01:00
Emil J 5b6baa3ef1
Merge pull request #4744 from YosysHQ/emil/clockgate-liberty
clockgate: add -liberty
2024-11-20 15:04:00 +01:00
Martin Povišer 53a4ec375b
Merge pull request #4762 from georgerennie/george/fix_read_ilang_test
tests: replace read_ilang with read_rtlil
2024-11-20 14:58:16 +01:00
George Rennie 9043dc0ad6
tests: replace read_ilang with read_rtlil
* #4612 was written before read_ilang was deprecated but merged after so caused test failures. This switches read_ilang to read_rtlil
2024-11-20 14:54:23 +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 4d96cbec75 clockgate: reduce errors to warnings 2024-11-18 18:32:18 +01:00
Emil J. Tywoniak 983c54c75f clockgate: help string add -dont_use and -liberty 2024-11-18 13:57:49 +01:00
Emil J. Tywoniak a5bc36f77e clockgate: add -dont_use 2024-11-18 13:45:30 +01:00
Emil J. Tywoniak e6793da9a0 clockgate: refactor 2024-11-18 12:50:25 +01:00
Emil J. Tywoniak b08441d95c clockgate: shuffle test liberty to exercise comparison better 2024-11-18 12:48:50 +01:00
Emil J. Tywoniak 1e3f8cc630 clockgate: add test liberty file 2024-11-18 12:45:27 +01:00
Emil J. Tywoniak c921d85a85 clockgate: fix test comments 2024-11-18 12:33:09 +01:00
Emil J. Tywoniak 45880ea7f2 clockgate: add -liberty 2024-11-14 20:37:59 +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
46 changed files with 1515 additions and 791 deletions

View File

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

View File

@ -90,7 +90,7 @@ struct ScopeinfoExamplePass : public Pass {
// Shuffle wires so this example produces more interesting outputs
std::sort(wires.begin(), wires.end(), [](Wire *a, Wire *b) {
return mkhash_xorshift(a->name.hash() * 0x2c9277b5) < mkhash_xorshift(b->name.hash() * 0x2c9277b5);
return mkhash_xorshift(run_hash(a->name) * 0x2c9277b5) < mkhash_xorshift(run_hash(b->name) * 0x2c9277b5);
});
ModuleHdlnameIndex index(module);

View File

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

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::Wire *dummy_wire = NULL;
@ -1567,9 +1567,9 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
module->fixup_ports();
dict<Net*, char, hash_ptr_ops> init_nets;
pool<Net*, hash_ptr_ops> anyconst_nets, anyseq_nets;
pool<Net*, hash_ptr_ops> allconst_nets, allseq_nets;
dict<Net*, char> init_nets;
pool<Net*> anyconst_nets, anyseq_nets;
pool<Net*> allconst_nets, allseq_nets;
any_all_nets.clear();
FOREACH_NET_OF_NETLIST(nl, mi, net)
@ -1832,10 +1832,10 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
module->connect(net_map_at(net), module->Anyseq(new_verific_id(net)));
#ifdef VERIFIC_SYSTEMVERILOG_SUPPORT
pool<Instance*, hash_ptr_ops> sva_asserts;
pool<Instance*, hash_ptr_ops> sva_assumes;
pool<Instance*, hash_ptr_ops> sva_covers;
pool<Instance*, hash_ptr_ops> sva_triggers;
pool<Instance*> sva_asserts;
pool<Instance*> sva_assumes;
pool<Instance*> sva_covers;
pool<Instance*> sva_triggers;
#endif
pool<RTLIL::Cell*> past_ffs;

View File

@ -71,7 +71,7 @@ struct VerificImporter
std::map<Verific::Net*, RTLIL::SigBit> net_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_autocover, mode_fullinit;
@ -89,7 +89,7 @@ struct VerificImporter
RTLIL::SigSpec operatorInput2(Verific::Instance *inst);
RTLIL::SigSpec operatorInport(Verific::Instance *inst, const char *portname);
RTLIL::SigSpec operatorInportCase(Verific::Instance *inst, const char *portname);
RTLIL::SigSpec operatorOutput(Verific::Instance *inst, const pool<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_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());
}
dict<Net*, bool, hash_ptr_ops> check_expression_cache;
dict<Net*, bool> check_expression_cache;
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
---------------------
Here is a short list of data structures that you should make yourself familiar
with before you write C++ code for Yosys. The following data structures are all
defined when "kernel/yosys.h" is included and USING_YOSYS_NAMESPACE is used.
1. Container classes based on hashing
1. Yosys Container Classes
Yosys heavily relies on custom container data structures such as dict or pool
defined in kernel/hashlib.h.
dict<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
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).
Otherwise, Yosys makes use of the following:
2. Standard STL data types

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -855,7 +855,11 @@ class WClass:
if self.hash_id != None:
text += "\n\t\tunsigned int get_hash_py()"
text += "\n\t\t{"
text += "\n\t\t\treturn get_cpp_obj()->" + self.hash_id + ";"
suffix = f"->{self.hash_id}" if self.hash_id else f"->{self.hash_id}"
if self.hash_id == "":
text += f"\n\t\t\treturn run_hash(*(get_cpp_obj()));"
else:
text += f"\n\t\t\treturn run_hash(get_cpp_obj()->{self.hash_id});"
text += "\n\t\t}"
text += "\n\t};\n"
@ -956,7 +960,7 @@ class WClass:
sources = [
Source("kernel/celltypes",[
WClass("CellType", link_types.pointer, None, None, "type.hash()", True),
WClass("CellType", link_types.pointer, None, None, "type", True),
WClass("CellTypes", link_types.pointer, None, None, None, True)
]
),
@ -970,23 +974,23 @@ sources = [
]
),
Source("kernel/rtlil",[
WClass("IdString", link_types.ref_copy, None, "str()", "hash()"),
WClass("Const", link_types.ref_copy, None, "as_string()", "hash()"),
WClass("IdString", link_types.ref_copy, None, "str()", ""),
WClass("Const", link_types.ref_copy, None, "as_string()", ""),
WClass("AttrObject", link_types.ref_copy, None, None, None),
WClass("Selection", link_types.ref_copy, None, None, None),
WClass("Monitor", link_types.derive, None, None, None),
WClass("CaseRule",link_types.ref_copy, None, None, None, True),
WClass("SwitchRule",link_types.ref_copy, None, None, None, True),
WClass("SyncRule", link_types.ref_copy, None, None, None, True),
WClass("Process", link_types.ref_copy, None, "name.c_str()", "name.hash()"),
WClass("Process", link_types.ref_copy, None, "name.c_str()", "name"),
WClass("SigChunk", link_types.ref_copy, None, None, None),
WClass("SigBit", link_types.ref_copy, None, None, "hash()"),
WClass("SigSpec", link_types.ref_copy, None, None, "hash()"),
WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", "hash()"),
WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "hash()")
WClass("SigBit", link_types.ref_copy, None, None, ""),
WClass("SigSpec", link_types.ref_copy, None, None, ""),
WClass("Cell", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Wire", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Memory", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Module", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "name.c_str()", ""),
WClass("Design", link_types.global_list, Attribute(WType("unsigned int"), "hashidx_"), "hashidx_", "")
]
),
#Source("kernel/satgen",[
@ -1579,10 +1583,15 @@ class WFunction:
return_stmt = "return " if self.ret_type.name != "void" else ""
text += ")\n\t\t{"
text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\"))"
text += f"\n\t\t\t\t{return_stmt}" + call_string
text += "\n\t\t\telse"
text += "\n\t\t\tif (boost::python::override py_" + self.alias + " = this->get_override(\"py_" + self.alias + "\")) {"
text += "\n\t\t\t\ttry {"
text += f"\n\t\t\t\t\t{return_stmt}" + call_string
text += "\n\t\t\t\t} catch (boost::python::error_already_set &) {"
text += "\n\t\t\t\t\tlog_python_exception_as_error();"
text += "\n\t\t\t\t}"
text += "\n\t\t\t} else {"
text += f"\n\t\t\t\t{return_stmt}" + self.member_of.name + "::" + call_string
text += "\n\t\t\t}"
text += "\n\t\t}"
text += "\n\n\t\t" + self.ret_type.gen_text() + " default_py_" + self.alias + "("
@ -2335,6 +2344,11 @@ USING_YOSYS_NAMESPACE
namespace YOSYS_PYTHON {
[[noreturn]] static void log_python_exception_as_error() {
PyErr_Print();
log_error("Python interpreter encountered an exception.\\n");
}
struct YosysStatics{};
""")

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; }
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; }
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -108,8 +108,8 @@ struct SigSnippets
struct SnippetSwCache
{
dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>, hash_ptr_ops> full_case_bits_cache;
dict<RTLIL::SwitchRule*, pool<int>, hash_ptr_ops> cache;
dict<RTLIL::SwitchRule*, pool<RTLIL::SigBit>> full_case_bits_cache;
dict<RTLIL::SwitchRule*, pool<int>> cache;
const SigSnippets *snippets;
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);
}
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::SigSpec result = defval;
@ -421,7 +421,7 @@ void proc_mux(RTLIL::Module *mod, RTLIL::Process *proc, bool ifxmode)
swcache.snippets = &sigsnip;
swcache.insert(&proc->root_case);
dict<RTLIL::SwitchRule*, bool, hash_ptr_ops> swpara;
dict<RTLIL::SwitchRule*, bool> swpara;
int cnt = 0;
for (int idx : sigsnip.snippets)

View File

@ -176,7 +176,7 @@ struct coverdb_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 *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; };
unsigned hash() const
Hasher hash_into(Hasher h) const
{
return mkhash_add(name.hash(), bit);
h.eat(name);
h.eat(bit);
return h;
}
IdString name;
@ -62,9 +64,11 @@ struct InvBit {
bool operator==(const InvBit &other) const { return bit == other.bit && inverted == other.inverted; };
bool operator!=(const InvBit &other) const { return bit != other.bit || inverted != other.inverted; };
unsigned hash() const
Hasher hash_into(Hasher h) const
{
return mkhash(bit.hash(), inverted);
h.eat(bit);
h.eat(inverted);
return h;
}
IdBit bit;

View File

@ -161,7 +161,7 @@ struct SimInstance
pool<SigBit> dirty_bits;
pool<Cell*> dirty_cells;
pool<IdString> dirty_memories;
pool<SimInstance*, hash_ptr_ops> dirty_children;
pool<SimInstance*> dirty_children;
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);
}
cell_stats.sort();
for (auto &it : cell_stats)
log("ABC RESULTS: %15s cells: %8d\n", it.first.c_str(), it.second);
int in_wires = 0, out_wires = 0;

View File

@ -111,7 +111,7 @@ struct AlumaccWorker
dict<RTLIL::SigBit, int> bit_users;
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;
AlumaccWorker(RTLIL::Module *module) : module(module), sigmap(module)
@ -226,7 +226,7 @@ struct AlumaccWorker
{
while (1)
{
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc)
{
@ -278,7 +278,7 @@ struct AlumaccWorker
void macc_to_alu()
{
pool<maccnode_t*, hash_ptr_ops> delete_nodes;
pool<maccnode_t*> delete_nodes;
for (auto &it : sig_macc)
{

View File

@ -1,5 +1,6 @@
#include "kernel/yosys.h"
#include "kernel/ff.h"
#include "libparse.h"
#include <optional>
USING_YOSYS_NAMESPACE
@ -10,6 +11,7 @@ struct ClockGateCell {
IdString ce_pin;
IdString clk_in_pin;
IdString clk_out_pin;
std::vector<IdString> tie_lo_pins;
};
ClockGateCell icg_from_arg(std::string& name, std::string& str) {
@ -37,6 +39,166 @@ ClockGateCell icg_from_arg(std::string& name, std::string& str) {
return c;
}
static std::pair<std::optional<ClockGateCell>, std::optional<ClockGateCell>>
find_icgs(std::string filename, std::vector<std::string> const& dont_use_cells) {
std::ifstream f;
f.open(filename.c_str());
if (f.fail())
log_cmd_error("Can't open liberty file `%s': %s\n", filename.c_str(), strerror(errno));
LibertyParser libparser(f);
f.close();
auto ast = libparser.ast;
// We will pick the most suitable ICG absed on tie_lo count and area
struct ICGRankable : public ClockGateCell { double area; };
std::optional<ICGRankable> best_pos;
std::optional<ICGRankable> best_neg;
if (ast->id != "library")
log_error("Format error in liberty file.\n");
// This is a lot of boilerplate, isn't it?
for (auto cell : ast->children)
{
if (cell->id != "cell" || cell->args.size() != 1)
continue;
const LibertyAst *dn = cell->find("dont_use");
if (dn != nullptr && dn->value == "true")
continue;
bool dont_use = false;
for (auto dont_use_cell : dont_use_cells)
{
if (patmatch(dont_use_cell.c_str(), cell->args[0].c_str()))
{
dont_use = true;
break;
}
}
if (dont_use)
continue;
const LibertyAst *icg_kind_ast = cell->find("clock_gating_integrated_cell");
if (icg_kind_ast == nullptr)
continue;
auto cell_name = cell->args[0];
auto icg_kind = icg_kind_ast->value;
auto starts_with = [&](std::string prefix) {
return icg_kind.compare(0, prefix.size(), prefix) == 0;
};
bool clk_pol;
if (icg_kind == "latch_posedge" || starts_with("latch_posedge_")) {
clk_pol = true;
} else if (icg_kind == "latch_negedge" || starts_with("latch_negedge_")) {
clk_pol = false;
} else {
log("Ignoring ICG primitive %s of kind '%s'\n", cell_name.c_str(), icg_kind.c_str());
continue;
}
log_debug("maybe valid icg: %s\n", cell_name.c_str());
ClockGateCell icg_interface;
icg_interface.name = RTLIL::escape_id(cell_name);
for (auto pin : cell->children) {
if (pin->id != "pin" || pin->args.size() != 1)
continue;
if (auto clk = pin->find("clock_gate_clock_pin")) {
if (!icg_interface.clk_in_pin.empty()) {
log_warning("Malformed liberty file - multiple clock_gate_clock_pin in cell %s\n",
cell_name.c_str());
continue;
} else
icg_interface.clk_in_pin = RTLIL::escape_id(pin->args[0]);
} else if (auto gclk = pin->find("clock_gate_out_pin")) {
if (!icg_interface.clk_out_pin.empty()) {
log_warning("Malformed liberty file - multiple clock_gate_out_pin in cell %s\n",
cell_name.c_str());
continue;
} else
icg_interface.clk_out_pin = RTLIL::escape_id(pin->args[0]);
} else if (auto en = pin->find("clock_gate_enable_pin")) {
if (!icg_interface.ce_pin.empty()) {
log_warning("Malformed liberty file - multiple clock_gate_enable_pin in cell %s\n",
cell_name.c_str());
continue;
} else
icg_interface.ce_pin = RTLIL::escape_id(pin->args[0]);
} else if (auto se = pin->find("clock_gate_test_pin")) {
icg_interface.tie_lo_pins.push_back(RTLIL::escape_id(pin->args[0]));
} else {
const LibertyAst *dir = pin->find("direction");
if (dir->value == "internal")
continue;
log_warning("Malformed liberty file - extra pin %s in cell %s\n",
pin->args[0].c_str(), cell_name.c_str());
continue;
}
}
if (icg_interface.clk_in_pin.empty()) {
log_warning("Malformed liberty file - missing clock_gate_clock_pin in cell %s",
cell_name.c_str());
continue;
}
if (icg_interface.clk_out_pin.empty()) {
log_warning("Malformed liberty file - missing clock_gate_out_pin in cell %s",
cell_name.c_str());
continue;
}
if (icg_interface.ce_pin.empty()) {
log_warning("Malformed liberty file - missing clock_gate_enable_pin in cell %s",
cell_name.c_str());
continue;
}
double area = 0;
const LibertyAst *ar = cell->find("area");
if (ar != nullptr && !ar->value.empty())
area = atof(ar->value.c_str());
std::optional<ICGRankable>& icg_to_beat = clk_pol ? best_pos : best_neg;
bool winning = false;
if (icg_to_beat) {
log_debug("ties: %zu ? %zu\n", icg_to_beat->tie_lo_pins.size(),
icg_interface.tie_lo_pins.size());
log_debug("area: %f ? %f\n", icg_to_beat->area, area);
// Prefer fewer test enables over area reduction (unlikely to matter)
auto goal = std::make_pair(icg_to_beat->tie_lo_pins.size(), icg_to_beat->area);
auto cost = std::make_pair(icg_interface.tie_lo_pins.size(), area);
winning = cost < goal;
if (winning)
log_debug("%s beats %s\n", icg_interface.name.c_str(), icg_to_beat->name.c_str());
} else {
log_debug("%s is the first of its polarity\n", icg_interface.name.c_str());
winning = true;
}
if (winning) {
ICGRankable new_icg {icg_interface, area};
icg_to_beat.emplace(new_icg);
}
}
std::optional<ClockGateCell> pos;
std::optional<ClockGateCell> neg;
if (best_pos) {
log("Selected rising edge ICG %s from Liberty file\n", best_pos->name.c_str());
pos.emplace(*best_pos);
}
if (best_neg) {
log("Selected falling edge ICG %s from Liberty file\n", best_neg->name.c_str());
neg.emplace(*best_neg);
}
return std::make_pair(pos, neg);
}
struct ClockgatePass : public Pass {
ClockgatePass() : Pass("clockgate", "extract clock gating out of flip flops") { }
void help() override {
@ -60,12 +222,20 @@ struct ClockgatePass : public Pass {
log(" user-specified <celltype> ICG (integrated clock gating)\n");
log(" cell with ports named <ce>, <clk>, <gclk>.\n");
log(" The ICG's clock enable pin must be active high.\n");
log(" -liberty <filename>\n");
log(" If specified, ICGs will be selected from the liberty file\n");
log(" if available. Priority is given to cells with fewer tie_lo\n");
log(" inputs and smaller size. This removes the need to manually\n");
log(" specify -pos or -neg and -tie_lo.\n");
log(" -dont_use <celltype>\n");
log(" Cells <celltype> won't be considered when searching for ICGs\n");
log(" in the liberty file specified by -liberty.\n");
log(" -tie_lo <port_name>\n");
log(" Port <port_name> of the ICG will be tied to zero.\n");
log(" Intended for DFT scan-enable pins.\n");
log(" -min_net_size <n>\n");
log(" Only transform sets of at least <n> eligible FFs.\n");
// log(" \n");
log(" \n");
}
// One ICG will be generated per ClkNetInfo
@ -77,10 +247,9 @@ struct ClockgatePass : public Pass {
SigBit ce_bit;
bool pol_clk;
bool pol_ce;
unsigned int hash() const {
Hasher hash_into(Hasher h) const {
auto t = std::make_tuple(clk_bit, ce_bit, pol_clk, pol_ce);
unsigned int h = mkhash_init;
h = mkhash(h, hash_ops<decltype(t)>::hash(t));
h.eat(t);
return h;
}
bool operator==(const ClkNetInfo& other) const {
@ -110,7 +279,9 @@ struct ClockgatePass : public Pass {
std::optional<ClockGateCell> pos_icg_desc;
std::optional<ClockGateCell> neg_icg_desc;
std::vector<std::string> tie_lo_ports;
std::vector<std::string> tie_lo_pins;
std::string liberty_file;
std::vector<std::string> dont_use_cells;
int min_net_size = 0;
size_t argidx;
@ -126,13 +297,33 @@ struct ClockgatePass : public Pass {
neg_icg_desc = icg_from_arg(name, rest);
}
if (args[argidx] == "-tie_lo" && argidx+1 < args.size()) {
tie_lo_ports.push_back(RTLIL::escape_id(args[++argidx]));
tie_lo_pins.push_back(RTLIL::escape_id(args[++argidx]));
}
if (args[argidx] == "-liberty" && argidx+1 < args.size()) {
liberty_file = args[++argidx];
rewrite_filename(liberty_file);
}
if (args[argidx] == "-dont_use" && argidx+1 < args.size()) {
dont_use_cells.push_back(args[++argidx]);
continue;
}
if (args[argidx] == "-min_net_size" && argidx+1 < args.size()) {
min_net_size = atoi(args[++argidx].c_str());
}
}
if (!liberty_file.empty())
std::tie(pos_icg_desc, neg_icg_desc) =
find_icgs(liberty_file, dont_use_cells);
else {
for (auto pin : tie_lo_pins) {
if (pos_icg_desc)
pos_icg_desc->tie_lo_pins.push_back(pin);
if (neg_icg_desc)
neg_icg_desc->tie_lo_pins.push_back(pin);
}
}
extra_args(args, argidx, design);
pool<Cell*> ce_ffs;
@ -185,7 +376,7 @@ struct ClockgatePass : public Pass {
gclk.new_net = module->addWire(NEW_ID);
icg->setPort(matching_icg_desc->clk_out_pin, gclk.new_net);
// Tie low DFT ports like scan chain enable
for (auto port : tie_lo_ports)
for (auto port : matching_icg_desc->tie_lo_pins)
icg->setPort(port, Const(0, 1));
// Fix CE polarity if needed
if (!clk.pol_ce) {

View File

@ -250,9 +250,11 @@ struct FlowGraph
{
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)

View File

@ -53,7 +53,7 @@ struct QlDspSimdPass : public Pass {
DspConfig(const DspConfig &ref) = default;
DspConfig(DspConfig &&ref) = default;
unsigned int hash() const { return connections.hash(); }
Hasher hash_into(Hasher h) const { h.eat(connections); return h; }
bool operator==(const DspConfig &ref) const { return connections == ref.connections; }
};

View File

@ -1,4 +1,4 @@
read_ilang <<EOT
read_rtlil <<EOT
autoidx 1
module \top
wire output 1 \Y

107
tests/techmap/clockgate.lib Normal file
View File

@ -0,0 +1,107 @@
library(test) {
/* Integrated clock gating cells */
cell (pos_small_tielo) {
area : 1;
clock_gating_integrated_cell : latch_posedge_precontrol;
pin (GCLK) {
clock_gate_out_pin : true;
direction : output;
}
pin (CLK) {
clock_gate_clock_pin : true;
direction : input;
}
pin (CE) {
clock_gate_enable_pin : true;
direction : input;
}
pin (SE) {
clock_gate_test_pin : true;
direction : input;
}
}
cell (pos_big) {
area : 10;
clock_gating_integrated_cell : latch_posedge;
pin (GCLK) {
clock_gate_out_pin : true;
direction : output;
}
pin (CLK) {
clock_gate_clock_pin : true;
direction : input;
}
pin (CE) {
clock_gate_enable_pin : true;
direction : input;
}
}
cell (pos_small) {
area : 1;
clock_gating_integrated_cell : latch_posedge;
pin (GCLK) {
clock_gate_out_pin : true;
direction : output;
}
pin (CLK) {
clock_gate_clock_pin : true;
direction : input;
}
pin (CE) {
clock_gate_enable_pin : true;
direction : input;
}
}
cell (neg_big) {
area : 10;
clock_gating_integrated_cell : latch_negedge;
pin (GCLK) {
clock_gate_out_pin : true;
direction : output;
}
pin (CLK) {
clock_gate_clock_pin : true;
direction : input;
}
pin (CE) {
clock_gate_enable_pin : true;
direction : input;
}
}
cell (neg_small_tielo) {
area : 1;
clock_gating_integrated_cell : latch_negedge_precontrol;
pin (GCLK) {
clock_gate_out_pin : true;
direction : output;
}
pin (CLK) {
clock_gate_clock_pin : true;
direction : input;
}
pin (CE) {
clock_gate_enable_pin : true;
direction : input;
}
pin (SE) {
clock_gate_test_pin : true;
direction : input;
}
}
cell (neg_small) {
area : 1;
clock_gating_integrated_cell : latch_negedge;
pin (GCLK) {
clock_gate_out_pin : true;
direction : output;
}
pin (CLK) {
clock_gate_clock_pin : true;
direction : input;
}
pin (CE) {
clock_gate_enable_pin : true;
direction : input;
}
}
}

View File

@ -61,7 +61,7 @@ clockgate -pos pdk_icg ce:clkin:clkout -tie_lo scanen
# falling edge clock flops don't get matched on -pos
select -module dffe_00 -assert-count 0 t:\\pdk_icg
select -module dffe_01 -assert-count 0 t:\\pdk_icg
# falling edge clock flops do get matched on -pos
# rising edge clock flops do get matched on -pos
select -module dffe_10 -assert-count 1 t:\\pdk_icg
select -module dffe_11 -assert-count 1 t:\\pdk_icg
# if necessary, EN is inverted, since the given ICG
@ -79,10 +79,10 @@ select -module dffe_wide_11 -assert-count 1 t:\\pdk_icg
design -load before
clockgate -min_net_size 1 -neg pdk_icg ce:clkin:clkout -tie_lo scanen
# rising edge clock flops don't get matched on -neg
# falling edge clock flops do get matched on -neg
select -module dffe_00 -assert-count 1 t:\\pdk_icg
select -module dffe_01 -assert-count 1 t:\\pdk_icg
# rising edge clock flops do get matched on -neg
# rising edge clock flops don't get matched on -neg
select -module dffe_10 -assert-count 0 t:\\pdk_icg
select -module dffe_11 -assert-count 0 t:\\pdk_icg
# if necessary, EN is inverted, since the given ICG
@ -193,4 +193,55 @@ select -assert-count 1 t:\\pdk_icg
#------------------------------------------------------------------------------
# TODO test -tie_lo
design -load before
clockgate -liberty clockgate.lib
# rising edge ICGs
select -module dffe_00 -assert-count 0 t:\\pos_small
select -module dffe_01 -assert-count 0 t:\\pos_small
select -module dffe_10 -assert-count 1 t:\\pos_small
select -module dffe_11 -assert-count 1 t:\\pos_small
# falling edge ICGs
select -module dffe_00 -assert-count 1 t:\\neg_small
select -module dffe_01 -assert-count 1 t:\\neg_small
select -module dffe_10 -assert-count 0 t:\\neg_small
select -module dffe_11 -assert-count 0 t:\\neg_small
# and nothing else
select -module dffe_00 -assert-count 0 t:\\pos_big
select -module dffe_01 -assert-count 0 t:\\pos_big
select -module dffe_10 -assert-count 0 t:\\pos_big
select -module dffe_11 -assert-count 0 t:\\pos_big
select -module dffe_00 -assert-count 0 t:\\pos_small_tielo
select -module dffe_01 -assert-count 0 t:\\pos_small_tielo
select -module dffe_10 -assert-count 0 t:\\pos_small_tielo
select -module dffe_11 -assert-count 0 t:\\pos_small_tielo
select -module dffe_00 -assert-count 0 t:\\neg_big
select -module dffe_01 -assert-count 0 t:\\neg_big
select -module dffe_10 -assert-count 0 t:\\neg_big
select -module dffe_11 -assert-count 0 t:\\neg_big
select -module dffe_00 -assert-count 0 t:\\neg_small_tielo
select -module dffe_01 -assert-count 0 t:\\neg_small_tielo
select -module dffe_10 -assert-count 0 t:\\neg_small_tielo
select -module dffe_11 -assert-count 0 t:\\neg_small_tielo
# if necessary, EN is inverted, since the given ICG
# is assumed to have an active-high EN
select -module dffe_10 -assert-count 1 t:\$_NOT_
select -module dffe_11 -assert-count 0 t:\$_NOT_
#------------------------------------------------------------------------------
design -load before
clockgate -liberty clockgate.lib -dont_use pos_small -dont_use neg_small
# rising edge ICGs
select -module dffe_10 -assert-count 1 t:\\pos_big
select -module dffe_11 -assert-count 1 t:\\pos_big
# falling edge ICGs
select -module dffe_00 -assert-count 1 t:\\neg_big
select -module dffe_01 -assert-count 1 t:\\neg_big