From c264649ae7b4510ab100b6ac1fe63e51ee529dab Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Fri, 29 May 2026 12:36:48 +0200 Subject: [PATCH] rtlil, patch: incremental signorm via connect_incremental, replacing batched sigNormalize in Patch::patch --- kernel/rtlil.h | 7 +++ kernel/rtlil_bufnorm.cc | 94 ++++++++++++++++++++++------------------ kernel/unstable/patch.cc | 33 ++++++-------- 3 files changed, 73 insertions(+), 61 deletions(-) diff --git a/kernel/rtlil.h b/kernel/rtlil.h index 5698441df..bfe755724 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -2666,6 +2666,13 @@ public: const pool &fanout(SigBit bit); const dict> &signorm_fanout() const; + // Equivalent to `connect(lhs, rhs)` followed by `sigNormalize()` for the + // merge implied by this single connection: updates the sigmap, promotes + // the driven side as canonical, and re-normalizes any existing fanout + // consumers of the demoted side. Lets callers in signorm mode merge + // signals without the overhead of a full normalize. + void connect_incremental(const SigSpec &lhs, const SigSpec &rhs); + template void rewrite_sigspecs(T &functor); template void rewrite_sigspecs2(T &functor); void cloneInto(RTLIL::Module *new_mod) const; diff --git a/kernel/rtlil_bufnorm.cc b/kernel/rtlil_bufnorm.cc index 6e48def17..7f86feee0 100644 --- a/kernel/rtlil_bufnorm.cc +++ b/kernel/rtlil_bufnorm.cc @@ -225,54 +225,56 @@ struct RTLIL::SigNormIndex } } - void flush_connections() { + // Process one (lhs, rhs) connection: apply current sigmap, then for each + // bit pair either merge into sigmap (and enqueue the demoted side into + // newly_driven) or emit a $connect cell when both sides have drivers. + void flush_one_connection(SigSpec lhs, SigSpec rhs) { std::vector connect_lhs; std::vector connect_rhs; + sigmap.apply(lhs); + sigmap.apply(rhs); + auto rhs_bits = rhs.bits().begin(); + + for (auto l : lhs.bits()) { + auto r = *rhs_bits; + ++rhs_bits; + if (l == r) + continue; + // TODO figure out what should happen with 'z + bool l_driven = !l.is_wire() || l.wire->known_driver(); + bool r_driven = !r.is_wire() || r.wire->known_driver(); + if (l_driven && r_driven) { + connect_lhs.push_back(l); + connect_rhs.push_back(r); + continue; + } + + sigmap.add(l, r); + if (l_driven) { + sigmap.database.promote(l); + newly_driven.insert(r); + } else { + sigmap.database.promote(r); + newly_driven.insert(l); + } + } + + if (!connect_lhs.empty()) { + Cell *cell = module->addCell(NEW_ID, ID($connect)); + xlog("add connect (1) %s\n", cell->name); + cell->setParam(ID::WIDTH, GetSize(connect_lhs)); + cell->setPort(ID::A, std::move(connect_lhs)); + cell->setPort(ID::B, std::move(connect_rhs)); + } + } + + void flush_connections() { auto begin = module->connections_.begin() + restored_connections; auto end = module->connections_.end(); - for (auto it = begin; it != end; ++it) { - auto &[lhs, rhs] = *it; - sigmap.apply(lhs); - sigmap.apply(rhs); - auto rhs_bits = rhs.bits().begin(); - - connect_lhs.clear(); - connect_rhs.clear(); - - for (auto l : lhs.bits()) { - auto r = *rhs_bits; - ++rhs_bits; - if (l == r) - continue; - // TODO figure out what should happen with 'z - bool l_driven = !l.is_wire() || l.wire->known_driver(); - bool r_driven = !r.is_wire() || r.wire->known_driver(); - if (l_driven && r_driven) { - connect_lhs.push_back(l); - connect_rhs.push_back(r); - continue; - } - - sigmap.add(l, r); - if (l_driven) { - sigmap.database.promote(l); - newly_driven.insert(r); - } else { - sigmap.database.promote(r); - newly_driven.insert(l); - } - } - - if (!connect_lhs.empty()) { - Cell *cell = module->addCell(NEW_ID, ID($connect)); - xlog("add connect (1) %s\n", cell->name); - cell->setParam(ID::WIDTH, GetSize(connect_lhs)); - cell->setPort(ID::A, std::move(connect_lhs)); - cell->setPort(ID::B, std::move(connect_rhs)); - } - } + for (auto it = begin; it != end; ++it) + flush_one_connection(it->first, it->second); module->connections_.clear(); restored_connections = 0; @@ -572,6 +574,14 @@ const dict> &RTLIL::Module::signorm_fanout() return sig_norm_index->fanout; } +void RTLIL::Module::connect_incremental(const SigSpec &lhs, const SigSpec &rhs) +{ + log_assert(sig_norm_index != nullptr); + log_assert(GetSize(lhs) == GetSize(rhs)); + sig_norm_index->flush_one_connection(lhs, rhs); + sig_norm_index->flush_newly_driven(); +} + void RTLIL::Module::remove(RTLIL::Cell *cell) { while (!cell->connections_.empty()) diff --git a/kernel/unstable/patch.cc b/kernel/unstable/patch.cc index 43fe439b0..61c25b0df 100644 --- a/kernel/unstable/patch.cc +++ b/kernel/unstable/patch.cc @@ -140,35 +140,29 @@ Cell* Patch::commit_cell(std::unique_ptr cell) { void Patch::patch(Cell* old_cell, IdString old_port, SigSpec new_sig) { SigSpec old_sig = old_cell->getPort(old_port); log_assert(old_sig.size() == new_sig.size()); - log("patching %s %s which is %s with %s:\n", old_cell->name, old_port, log_signal(old_sig), log_signal(new_sig)); + log_debug("patching %s %s which is %s with %s:\n", old_cell->name, old_port, log_signal(old_sig), log_signal(new_sig)); SrcCollector collector; collector.collect_src(old_sig); std::string src_str = AttrObject::strpool_attribute_to_str(collector.src); - old_cell->setPort(old_port, SigSpec()); - mod->connect(old_sig, new_sig); - if (map) - map->add(old_sig, new_sig); - - // Inefficient + // Record leaves (existing wires consumed as inputs by the new cells) so + // gc() stops at them. Detected via bit.wire->module being non-null: + // uncommitted wires belong to no module yet. for (auto& cell : cells_) { - log_debug("cell %s\n", cell->name); for (auto& [port_name, sig] : cell->connections()) { - log_debug("port %s\n", port_name); auto dir = cell->port_dir(port_name); if (dir == PD_INPUT || dir == PD_INOUT) { for (auto bit : sig) { - log("bit %s\n", log_signal(bit)); - if (bit.is_wire() && bit.wire->module) { + if (bit.is_wire() && bit.wire->module) leaves.insert(bit.wire); - log_debug("leaf %s\n", bit.wire->name); - } } } } } + // Commit new cells/wires first so new_sig becomes a driven signal in the + // signorm index before we merge. for (auto& cell: cells_) { cell->set_src_attribute(src_str); cell->fixup_parameters(); @@ -178,12 +172,13 @@ void Patch::patch(Cell* old_cell, IdString old_port, SigSpec new_sig) { for (auto& wire: wires_) commit_wire(std::move(wire)); - // Flush pending sigmap updates (from the mod->connect above) into the - // fanout index so gc() sees the updated fanout for cells whose outputs - // were the patched wires. Without this, downstream consumers like the - // $output_port / $public sentinels still appear in the OLD wire's fanout - // instead of the new representative. - mod->sigNormalize(); + // Now drop old_cell's driver so old_sig is undriven, then merge it into + // new_sig. connect_incremental updates sigmap and re-normalizes fanout + // consumers in place — no full sigNormalize needed. + old_cell->setPort(old_port, SigSpec()); + if (map) + map->add(old_sig, new_sig); + mod->connect_incremental(old_sig, new_sig); gc(old_cell); cells_.clear();