3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-08-09 23:51:08 +00:00
yosys/kernel/sigtools.h
Emil J. Tywoniak 7617dae629 sigmap: take the signorm index's map directly instead of via connections()
While in signorm mode the module's index already holds a complete SigMap
of its alias connectivity; connections_ is only a scratch buffer that
restore_connections() materializes that map into on demand. So every
`SigMap sigmap(module)` was a round trip: restore_connections() walked
the entire union-find emitting (bit, sigmap(bit)) pairs, and SigMap::set()
hashed all of them straight back into an identical union-find. Worse,
set() iterates connections() twice, so each construction paid for two
full materializations.

Since restore_connections() emits the representative as the right-hand
side and SigMap::add() leaves the right-hand side as the root, the
rebuild reproduces exactly the classes and representatives it started
from -- so copy the mfp instead. Module::signorm_sigmap() hands it out
after merging any pending connections, and returns nullptr outside
signorm mode, where the old path stands.

The copy also carries entries for bits that map to themselves, which the
connection-derived rebuild would omit. That is only observable through
SigMap::allbits() or by iterating .database, neither of which has a
caller outside sigtools.h.

Measured on a 2000-stage pipeline with dead logic (Debug build, `opt`):
signorm_restore drops from 0.76s over 22 calls to 0.08s over 12, and the
whole run from 7.24s to 6.40s. The sigmap being round-tripped there peaks
at 1.29M entries, which is the alias structure of the post-proc netlist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MQU5XCqDYY8MbivGkNRrDo
2026-07-21 12:53:38 +02:00

482 lines
11 KiB
C++

/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef SIGTOOLS_H
#define SIGTOOLS_H
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN
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) { }
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(first->name);
h.eat(second);
return h;
}
};
pool<bitDef_t> bits;
void clear()
{
bits.clear();
}
void add(const RTLIL::SigSpec &sig)
{
for (auto &bit : sig)
if (bit.wire != NULL)
bits.insert(bit);
}
void add(const SigPool &other)
{
for (auto &bit : other.bits)
bits.insert(bit);
}
void del(const RTLIL::SigSpec &sig)
{
for (auto &bit : sig)
if (bit.wire != NULL)
bits.erase(bit);
}
void del(const SigPool &other)
{
for (auto &bit : other.bits)
bits.erase(bit);
}
void expand(const RTLIL::SigSpec &from, const RTLIL::SigSpec &to)
{
log_assert(GetSize(from) == GetSize(to));
for (int i = 0; i < GetSize(from); i++) {
bitDef_t bit_from(from[i]), bit_to(to[i]);
if (bit_from.first != NULL && bit_to.first != NULL && bits.count(bit_from) > 0)
bits.insert(bit_to);
}
}
RTLIL::SigSpec extract(const RTLIL::SigSpec &sig) const
{
RTLIL::SigSpec result;
for (auto &bit : sig)
if (bit.wire != NULL && bits.count(bit))
result.append(bit);
return result;
}
RTLIL::SigSpec remove(const RTLIL::SigSpec &sig) const
{
RTLIL::SigSpec result;
for (auto &bit : sig)
if (bit.wire != NULL && bits.count(bit) == 0)
result.append(bit);
return result;
}
bool check(const RTLIL::SigBit &bit) const
{
return bit.wire != NULL && bits.count(bit);
}
bool check_any(const RTLIL::SigSpec &sig) const
{
for (auto &bit : sig)
if (bit.wire != NULL && bits.count(bit))
return true;
return false;
}
bool check_all(const RTLIL::SigSpec &sig) const
{
for (auto &bit : sig)
if (bit.wire != NULL && bits.count(bit) == 0)
return false;
return true;
}
RTLIL::SigSpec export_one() const
{
for (auto &bit : bits)
return RTLIL::SigSpec(bit.first, bit.second);
return RTLIL::SigSpec();
}
RTLIL::SigSpec export_all() const
{
pool<RTLIL::SigBit> sig;
for (auto &bit : bits)
sig.insert(RTLIL::SigBit(bit.first, bit.second));
return sig;
}
size_t size() const
{
return bits.size();
}
};
template <typename T, class Compare = void>
struct SigSet
{
static_assert(!std::is_same<Compare,void>::value, "Default value for `Compare' class not found for SigSet<T>. Please specify.");
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) { }
[[nodiscard]] Hasher hash_into(Hasher h) const {
h.eat(first->name);
h.eat(second);
return h;
}
};
dict<bitDef_t, std::set<T, Compare>> bits;
void clear()
{
bits.clear();
}
void insert(const RTLIL::SigSpec &sig, T data)
{
for (const auto &bit : sig)
if (bit.wire != NULL)
bits[bit].insert(data);
}
void insert(const RTLIL::SigSpec& sig, const std::set<T> &data)
{
for (const auto &bit : sig)
if (bit.wire != NULL)
bits[bit].insert(data.begin(), data.end());
}
void erase(const RTLIL::SigSpec& sig)
{
for (const auto &bit : sig)
if (bit.wire != NULL)
bits[bit].clear();
}
void erase(const RTLIL::SigSpec &sig, T data)
{
for (const auto &bit : sig)
if (bit.wire != NULL)
bits[bit].erase(data);
}
void erase(const RTLIL::SigSpec &sig, const std::set<T> &data)
{
for (const auto &bit : sig)
if (bit.wire != NULL)
bits[bit].erase(data.begin(), data.end());
}
void find(const RTLIL::SigSpec &sig, std::set<T> &result)
{
for (const auto &bit : sig)
if (bit.wire != NULL) {
auto &data = bits[bit];
result.insert(data.begin(), data.end());
}
}
void find(const RTLIL::SigSpec &sig, pool<T> &result)
{
for (const auto &bit : sig)
if (bit.wire != NULL) {
auto &data = bits[bit];
result.insert(data.begin(), data.end());
}
}
std::set<T> find(const RTLIL::SigSpec &sig)
{
std::set<T> result;
find(sig, result);
return result;
}
bool has(const RTLIL::SigSpec &sig)
{
for (auto &bit : sig)
if (bit.wire != NULL && bits.count(bit))
return true;
return false;
}
};
template<typename T>
class SigSet<T, typename std::enable_if<!std::is_pointer<T>::value>::type> : public SigSet<T, std::less<T>> {};
template<typename T>
using sort_by_name_guard = typename std::enable_if<std::is_same<T,RTLIL::Cell*>::value>::type;
template<typename T>
class SigSet<T, sort_by_name_guard<T>> : public SigSet<T, RTLIL::sort_by_name<typename std::remove_pointer<T>::type>> {};
struct SigMapView
{
mfp<SigBit> database;
// Modify bit to its representative
void apply(RTLIL::SigBit &bit) const
{
bit = database.find(bit);
}
void apply(RTLIL::SigSpec &sig) const
{
for (auto &bit : sig)
apply(bit);
}
RTLIL::SigBit operator()(RTLIL::SigBit bit) const
{
apply(bit);
return bit;
}
RTLIL::SigSpec operator()(RTLIL::SigSpec sig) const
{
apply(sig);
return sig;
}
RTLIL::SigSpec operator()(RTLIL::Wire *wire) const
{
SigSpec sig(wire);
apply(sig);
return sig;
}
};
/**
* SigMap wraps a union-find "database"
* to map SigBits of a module to canonical representative SigBits.
* SigBits that are connected share a set in the underlying database.
* If a SigBit has a const state (impl: bit.wire is nullptr),
* it's promoted to a representative.
*/
struct SigMap final : public SigMapView
{
SigMap(RTLIL::Module *module = NULL)
{
if (module != NULL)
set(module);
}
void swap(SigMap &other)
{
database.swap(other.database);
}
void clear()
{
database.clear();
}
// Rebuild SigMap for all connections in module
void set(RTLIL::Module *module)
{
// In signorm mode the module already holds the union-find we would
// otherwise rebuild: `connections()` materializes it bit by bit only
// for us to hash it straight back in. `restore_connections` emits
// `(bit, sigmap(bit))` pairs and `add()` leaves the right-hand side as
// the root, so rebuilding reproduces the same classes with the same
// representatives -- copy them instead.
if (const SigMap *index_sigmap = module->signorm_sigmap()) {
database = index_sigmap->database;
return;
}
int bitcount = 0;
for (auto &it : module->connections())
bitcount += it.first.size();
database.clear();
database.reserve(bitcount);
for (auto &it : module->connections())
add(it.first, it.second);
}
// Add connections from "from" to "to", bit-by-bit
void add(const RTLIL::SigSpec& from, const RTLIL::SigSpec& to)
{
log_assert(GetSize(from) == GetSize(to));
for (int i = 0; i < GetSize(from); i++)
{
int bfi = database.lookup(from[i]);
int bti = database.lookup(to[i]);
const RTLIL::SigBit &bf = database[bfi];
const RTLIL::SigBit &bt = database[bti];
if (bf.wire || bt.wire)
{
database.imerge(bfi, bti);
if (bf.wire == nullptr)
database.ipromote(bfi);
if (bt.wire == nullptr)
database.ipromote(bti);
}
}
}
// Add sig as disconnected from anything
void add(const RTLIL::SigBit &bit)
{
const auto &b = database.find(bit);
if (b.wire != nullptr)
database.promote(bit);
}
void add(const RTLIL::SigSpec &sig)
{
for (const auto &bit : sig)
add(bit);
}
inline void add(Wire *wire) { return add(RTLIL::SigSpec(wire)); }
// All non-const bits
RTLIL::SigSpec allbits() const
{
RTLIL::SigSpec sig;
for (const auto &bit : database)
if (bit.wire != nullptr)
sig.append(bit);
return sig;
}
};
/**
* SiValgMap wraps a union-find "database" to map SigBits of a module to
* canonical representative SigBits plus some optional Val value associated with the bits.
* Val has a commutative, associative, idempotent operator|=, a default constructor
* which constructs an identity element, and a copy constructor.
* SigBits that are connected share a set in the underlying database;
* the associated value is the "sum" of all the values associated with the contributing bits.
* If any of the SigBits in a set are a constant, the canonical SigBit is a constant.
*/
template <class Val>
struct SigValMap final : public SigMapView
{
dict<SigBit, Val> values;
void swap(SigValMap<Val> &other)
{
database.swap(other.database);
values.swap(other.values);
}
void clear()
{
database.clear();
values.clear();
}
// Rebuild SigMap for all connections in module
void set(RTLIL::Module *module)
{
int bitcount = 0;
for (auto &it : module->connections())
bitcount += it.first.size();
database.clear();
values.clear();
database.reserve(bitcount);
for (auto &it : module->connections())
add(it.first, it.second);
}
// Add connections from "from" to "to", bit-by-bit.
void add(const RTLIL::SigSpec& from, const RTLIL::SigSpec& to)
{
log_assert(GetSize(from) == GetSize(to));
for (int i = 0; i < GetSize(from); i++)
{
int bfi = database.lookup(from[i]);
int bti = database.lookup(to[i]);
if (bfi == bti) {
continue;
}
const RTLIL::SigBit &bf = database[bfi];
const RTLIL::SigBit &bt = database[bti];
if (bf.wire == nullptr) {
// bf is constant so make it the canonical representative.
database.imerge(bti, bfi);
merge_value(bt, bf);
} else {
// Make bt the canonical representative.
database.imerge(bfi, bti);
merge_value(bf, bt);
}
}
}
void addVal(const RTLIL::SigBit &bit, const Val &val)
{
values[database.find(bit)] |= val;
}
void addVal(const RTLIL::SigSpec &sig, const Val &val)
{
for (const auto &bit : sig)
addVal(bit, val);
}
Val apply_and_get_value(RTLIL::SigBit &bit) const
{
bit = database.find(bit);
auto it = values.find(bit);
return it == values.end() ? Val() : it->second;
}
private:
void merge_value(const RTLIL::SigBit &from, const RTLIL::SigBit &to)
{
auto it = values.find(from);
if (it == values.end()) {
return;
}
// values[to] could resize the underlying `entries` so
// finish using `it` first.
Val v = it->second;
values.erase(it);
values[to] |= v;
}
};
YOSYS_NAMESPACE_END
#endif /* SIGTOOLS_H */