3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-07-15 03:35:40 +00:00

patch: split into single-output patch + multi-output patch_ports; drop input-cone gc

This commit is contained in:
Emil J. Tywoniak 2026-06-03 00:02:18 +02:00
parent 61b0dfd3bf
commit 7656347b44
7 changed files with 220 additions and 256 deletions

View file

@ -54,7 +54,6 @@ struct RTLIL::SigNormIndex
module->fixup_ports();
setup_module_inputs();
setup_driven_wires();
setup_module_outputs_and_publics();
setup_fanout();
}
@ -111,72 +110,6 @@ struct RTLIL::SigNormIndex
}
}
// Creates $output_port cells consuming each pure-output module port wire
// and $public cells consuming each public-named wire that isn't already
// covered by an $input_port or $output_port. These act as fanout sentinels
// so local GC (e.g. in Patch) won't remove driver cells whose only purpose
// is to feed a port or a user-visible wire.
void setup_module_outputs_and_publics() {
std::vector<Cell *> cells_to_remove;
dict<Wire *, Cell *> output_port_cells;
dict<Wire *, Cell *> public_cells;
for (auto cell : module->cells()) {
if (cell->type != ID($output_port) && cell->type != ID($public))
continue;
auto const &sig_a = cell->getPort(ID::A);
Wire *wire = nullptr;
if (!sig_a.is_wire()) {
cells_to_remove.push_back(cell);
continue;
}
wire = sig_a.as_wire();
if (cell->type == ID($output_port)) {
if (wire->port_output && !wire->port_input && !output_port_cells.count(wire))
output_port_cells.emplace(wire, cell);
else
cells_to_remove.push_back(cell);
} else { // $public
bool is_pure_input = wire->port_input && !wire->port_output;
bool is_pure_output = wire->port_output && !wire->port_input;
if (wire->name.isPublic() && !is_pure_input && !is_pure_output && !public_cells.count(wire))
public_cells.emplace(wire, cell);
else
cells_to_remove.push_back(cell);
}
}
for (auto cell : cells_to_remove)
module->remove(cell);
for (auto portname : module->ports) {
Wire *wire = module->wire(portname);
if (wire->port_output && !wire->port_input && !output_port_cells.count(wire)) {
Cell *cell = module->addCell(NEW_ID, ID($output_port));
cell->setParam(ID::WIDTH, GetSize(wire));
cell->setPort(ID::A, wire);
output_port_cells.emplace(wire, cell);
}
}
for (auto &it : module->wires_) {
Wire *wire = it.second;
if (!wire->name.isPublic())
continue;
bool is_pure_input = wire->port_input && !wire->port_output;
bool is_pure_output = wire->port_output && !wire->port_input;
if (is_pure_input || is_pure_output)
continue;
if (public_cells.count(wire))
continue;
Cell *cell = module->addCell(NEW_ID, ID($public));
cell->setParam(ID::WIDTH, GetSize(wire));
cell->setPort(ID::A, wire);
public_cells.emplace(wire, cell);
}
}
void setup_driven_wires() {
for (auto cell : module->cells()) {
xlog("setup_driven_wires cell %s %s\n", cell->type, cell->name);
@ -421,7 +354,7 @@ void RTLIL::Design::sigNormalize(bool enable)
// TODO inefficient?
std::vector<Cell*> cells_snapshot = module->cells();
for (auto cell : cells_snapshot) {
if (cell->type.in(ID($input_port), ID($output_port), ID($public)))
if (cell->type == ID($input_port))
module->remove(cell);
}
}

View file

@ -1,6 +1,7 @@
#include "kernel/unstable/patch.h"
#include "kernel/celltypes.h"
#include "kernel/log.h"
#include "kernel/newcelltypes.h"
#include "kernel/rtlil.h"
YOSYS_NAMESPACE_BEGIN
@ -45,56 +46,6 @@ RTLIL::Wire *RTLIL::Patch::addWire(RTLIL::IdString name, const RTLIL::Wire *othe
return wire;
}
void Patch::gc(Cell* old_cell, bool track, pool<string>* src_pool) {
log_debug("gc %s\n", old_cell->name);
if (old_cell->type.in(ID($input_port), ID($output_port), ID($public)))
return;
pool<Cell*> inputs;
for (auto [port_name, sig] : old_cell->connections()) {
auto dir = old_cell->port_dir(port_name);
// Unknown port direction (e.g. user module instance whose interface
// isn't registered): can't decide input vs output, so don't gc it.
// TODO: should be log_assert once PD_UNKNOWN is fixed at the source
// (see claude-notes.md).
if (dir == PD_UNKNOWN)
return;
// TODO only running GC through whole connections?
log_debug("\tport %s\n", port_name);
if (sig.size() && sig.is_wire()) {
if (dir == PD_OUTPUT || dir == PD_INOUT) {
for (auto bit : sig) {
// Reject GC if used
if (!mod->fanout(bit).empty()) {
log_debug("\treject fanout\n");
return;
} else
log_debug("\tok\n");
}
}
if (dir == PD_INPUT || dir == PD_INOUT) {
Wire* in_wire = sig.as_wire();
log_assert(in_wire);
log_debug("\twire %s\n", in_wire->name);
if (in_wire->known_driver() && !leaves.count(in_wire))
inputs.insert(in_wire->driverCell());
}
}
}
log_debug("\tremove %s\n", old_cell->name);
// Only track recursively-removed cells. The top-level patched cell is the
// caller's current iteration variable and won't be re-encountered.
if (track && removed_cells)
removed_cells->insert(old_cell);
// The cell about to be removed contributes its src so all the cells gc'd
// in this patch (top-level + input cone) get merged into the new cells'
// src — that's the "transfer src automatically" guarantee.
if (src_pool)
src_pool->insert(old_cell->get_src_attribute());
old_cell->module->remove(old_cell);
for (auto input : inputs)
gc(input, /*track=*/true, src_pool);
}
Wire* Patch::commit_wire(std::unique_ptr<Wire> wire) {
Wire* raw = wire.release();
mod->wires_[raw->name] = raw;
@ -110,63 +61,142 @@ Cell* Patch::commit_cell(std::unique_ptr<Cell> cell) {
return raw;
}
void Patch::patch(Cell* old_cell, IdString old_port, SigSpec new_sig) {
patch(old_cell, {{old_port, new_sig}}, nullptr);
std::vector<Cell*> Patch::commit_staged() {
std::vector<Cell*> committed;
committed.reserve(cells_.size());
for (auto& cell : cells_) {
cell->fixup_parameters();
committed.push_back(commit_cell(std::move(cell)));
}
for (auto& wire : wires_)
commit_wire(std::move(wire));
cells_.clear();
wires_.clear();
return committed;
}
void Patch::patch(Cell* old_cell, const std::vector<std::pair<IdString, SigSpec>> &port_replacements) {
patch(old_cell, port_replacements, nullptr);
}
void Patch::patch(Cell* old_cell, IdString old_port, SigSpec new_sig, Cell* merge_src_into) {
patch(old_cell, {{old_port, new_sig}}, merge_src_into);
}
void Patch::patch(Cell* old_cell, const std::vector<std::pair<IdString, SigSpec>> &port_replacements, Cell* merge_src_into) {
std::vector<SigSpec> old_sigs;
for (auto &[port, new_sig] : port_replacements) {
SigSpec old_sig = old_cell->getPort(port);
if (old_sig.size() != new_sig.size())
log_error("patch size mismatch on cell %s port %s: old %d (%s) vs new %d (%s)\n",
log_id(old_cell->name), log_id(port), old_sig.size(), log_signal(old_sig),
new_sig.size(), log_signal(new_sig));
log_debug("patching %s %s which is %s with %s:\n", old_cell->name, port, log_signal(old_sig), log_signal(new_sig));
old_sigs.push_back(old_sig);
namespace {
void collect_src(Cell* root, const std::vector<Cell*>& extras,
Cell* merge_src_into, pool<std::string>& out)
{
out.insert(root->get_src_attribute());
for (Cell* c : extras)
if (c)
out.insert(c->get_src_attribute());
if (merge_src_into)
out.insert(merge_src_into->get_src_attribute());
}
// 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_) {
for (auto& [port_name, sig] : cell->connections()) {
auto dir = cell->port_dir(port_name);
if (dir == PD_INPUT || dir == PD_INOUT) {
for (auto bit : sig) {
if (bit.is_wire() && bit.wire->module)
leaves.insert(bit.wire);
}
void apply_src(const pool<std::string>& src_pool,
const std::vector<Cell*>& targets, Cell* merge_src_into)
{
std::string src_str = AttrObject::strpool_attribute_to_str(src_pool);
for (Cell* c : targets)
c->set_src_attribute(src_str);
if (merge_src_into)
merge_src_into->set_src_attribute(src_str);
}
// Verifies via newcelltypes that root_cell has exactly one output port
// and that it matches `expected_port`.
void assert_single_output(Cell* root_cell, IdString expected_port) {
int count = 0;
IdString found;
for (auto &[port, sig] : root_cell->connections()) {
if (root_cell->output(port)) {
found = port;
count++;
}
}
if (count != 1)
log_error("Patch: cell %s of type %s has %d output ports, expected exactly one\n",
log_id(root_cell->name), log_id(root_cell->type), count);
if (found != expected_port)
log_error("Patch: cell %s of type %s sole output port %s does not match patched port %s\n",
log_id(root_cell->name), log_id(root_cell->type),
log_id(found), log_id(expected_port));
}
}
// Commit new cells/wires first so new_sig becomes a driven signal in the
// signorm index before we merge. Track raw pointers so we can update
// their src attribute after gc finishes collecting from removed cells.
std::vector<Cell*> committed_new_cells;
committed_new_cells.reserve(cells_.size());
for (auto& cell: cells_) {
cell->fixup_parameters();
committed_new_cells.push_back(commit_cell(std::move(cell)));
void Patch::patch(Cell* root_cell, IdString old_port, SigSpec new_sig,
const std::vector<Cell*>& extras, Cell* merge_src_into)
{
assert_single_output(root_cell, old_port);
SigSpec old_sig = root_cell->getPort(old_port);
if (old_sig.size() != new_sig.size())
log_error("patch size mismatch on cell %s port %s: old %d (%s) vs new %d (%s)\n",
log_id(root_cell->name), log_id(old_port),
old_sig.size(), log_signal(old_sig),
new_sig.size(), log_signal(new_sig));
log_debug("patching %s %s which is %s with %s\n",
log_id(root_cell->name), log_id(old_port),
log_signal(old_sig), log_signal(new_sig));
pool<std::string> src_pool;
collect_src(root_cell, extras, merge_src_into, src_pool);
std::vector<Cell*> committed = commit_staged();
apply_src(src_pool, committed, merge_src_into);
// Drop root_cell's driver on the output port BEFORE wiring old_sig to
// new_sig — otherwise old_sig would briefly have two drivers (root_cell
// and new_sig) which signorm flags as conflicting.
root_cell->unsetPort(old_port);
if (map)
map->add(old_sig, new_sig);
mod->connect_incremental(old_sig, new_sig);
// Remove root cell only — no input-cone walk.
mod->remove(root_cell);
}
void Patch::patch_ports(Cell* root_cell,
const std::vector<std::pair<IdString, SigSpec>>& port_replacements,
const std::vector<Cell*>& extras, Cell* merge_src_into)
{
// Verify each listed port is an output of root_cell and that the
// replacements cover every output port of root_cell.
pool<IdString> listed;
std::vector<SigSpec> old_sigs;
old_sigs.reserve(port_replacements.size());
for (auto &[port, new_sig] : port_replacements) {
if (!root_cell->output(port))
log_error("patch_ports: cell %s of type %s port %s is not an output\n",
log_id(root_cell->name), log_id(root_cell->type), log_id(port));
SigSpec old_sig = root_cell->getPort(port);
if (old_sig.size() != new_sig.size())
log_error("patch_ports size mismatch on cell %s port %s: old %d (%s) vs new %d (%s)\n",
log_id(root_cell->name), log_id(port),
old_sig.size(), log_signal(old_sig),
new_sig.size(), log_signal(new_sig));
listed.insert(port);
old_sigs.push_back(old_sig);
}
for (auto &[port, sig] : root_cell->connections())
if (root_cell->output(port) && !listed.count(port))
log_error("patch_ports: cell %s of type %s has output port %s not in port_replacements\n",
log_id(root_cell->name), log_id(root_cell->type), log_id(port));
for (auto& wire: wires_)
commit_wire(std::move(wire));
pool<std::string> src_pool;
collect_src(root_cell, extras, merge_src_into, src_pool);
std::vector<Cell*> committed = commit_staged();
apply_src(src_pool, committed, merge_src_into);
// Drop every port (inputs included) so root_cell becomes a disconnected
// shell before we wire old_sigs to new_sigs. Doing this first ensures
// the old port signals are not briefly double-driven by root_cell and
// the new connection.
std::vector<IdString> all_ports;
all_ports.reserve(root_cell->connections().size());
for (auto &[port, sig] : root_cell->connections())
all_ports.push_back(port);
for (auto port : all_ports)
root_cell->unsetPort(port);
log_assert(root_cell->connections().empty());
// Now drop old_cell's drivers so old_sigs are undriven, then merge each
// into its new_sig. connect_incremental updates sigmap and re-normalizes
// fanout consumers in place — no full sigNormalize needed.
for (auto &[port, new_sig] : port_replacements)
old_cell->setPort(port, SigSpec());
for (size_t i = 0; i < port_replacements.size(); i++) {
auto &[port, new_sig] = port_replacements[i];
if (map)
@ -174,24 +204,7 @@ void Patch::patch(Cell* old_cell, const std::vector<std::pair<IdString, SigSpec>
mod->connect_incremental(old_sigs[i], new_sig);
}
// gc removes old_cell AND any newly-dead input-cone cells, contributing
// each removed cell's src into the pool. The merged-into cell (e.g. an
// opt_merge keep_cell) and any caller-bequeathed pool entries also get
// folded in here.
pool<string> src_pool;
if (merge_src_into)
src_pool.insert(merge_src_into->get_src_attribute());
gc(old_cell, /*track=*/false, &src_pool);
std::string src_str = AttrObject::strpool_attribute_to_str(src_pool);
for (Cell* c : committed_new_cells)
c->set_src_attribute(src_str);
if (merge_src_into)
merge_src_into->set_src_attribute(src_str);
cells_.clear();
wires_.clear();
leaves.clear();
mod->remove(root_cell);
}
void Patch::commit_inheriting_src(Cell* src_source) {
@ -207,4 +220,4 @@ void Patch::commit_inheriting_src(Cell* src_source) {
wires_.clear();
}
YOSYS_NAMESPACE_END
YOSYS_NAMESPACE_END

View file

@ -9,9 +9,6 @@ YOSYS_NAMESPACE_BEGIN
// No virtual methods — subclasses cannot be dispatched through a Patch pointer.
struct RTLIL::Patch : public CellAdderMixin<RTLIL::Patch>
{
private:
void gc(Cell* old_cell, bool track = false, pool<std::string>* src_pool = nullptr);
protected:
void add(RTLIL::Wire *wire);
void add(RTLIL::Cell *cell);
@ -20,12 +17,13 @@ protected:
Cell* commit_cell(std::unique_ptr<Cell> cell);
Wire* commit_wire(std::unique_ptr<Wire> wire);
pool<Wire*> leaves = {};
// Move staged cells_/wires_ into the module. Returns raw pointers to
// the committed new cells in insertion order.
std::vector<Cell*> commit_staged();
public:
Module* mod;
SigMap* map;
pool<Cell*>* removed_cells = nullptr;
vector<std::unique_ptr<Wire>> wires_ = {};
vector<std::unique_ptr<Cell>> cells_ = {};
@ -33,22 +31,36 @@ public:
void connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs);
const std::vector<RTLIL::SigSig> &connections() const;
void patch(Cell* old_cell, IdString old_port, SigSpec new_sig);
void patch(Cell* old_cell, const std::vector<std::pair<IdString, SigSpec>> &port_replacements);
// Compatible rewrite: root_cell's type has exactly one output port
// (asserted via kernel/newcelltypes.h). Rewires that output's signal to
// new_sig, auto-merges src from root_cell + each cell in `extras` (and
// merge_src_into if set) into every staged new cell and into
// merge_src_into, then removes root_cell from the module. No input-cone
// walk: only root_cell is removed.
void patch(Cell* root_cell, IdString old_port, SigSpec new_sig,
const std::vector<Cell*>& extras = {},
Cell* merge_src_into = nullptr);
// Variants for "merge old_cell into an existing keep_cell" (e.g.
// opt_merge): the old_cell's src attribute is collected and combined
// with merge_src_into's existing src, and the result is set on
// merge_src_into. Any new cells in cells_ also receive the combined src.
void patch(Cell* old_cell, IdString old_port, SigSpec new_sig, Cell* merge_src_into);
void patch(Cell* old_cell, const std::vector<std::pair<IdString, SigSpec>> &port_replacements, Cell* merge_src_into);
// Multi-output rewrite: transfer a list of output ports to a list of
// new sigs. Every entry in `port_replacements` must name an output port
// of root_cell, and the list must cover ALL of root_cell's output ports
// (both verified via kernel/newcelltypes.h). For each (port, new_sig)
// pair the original port signal is connected to new_sig at the module
// level. All of root_cell's ports are then unset and the cell is
// removed (asserted: no connections remain at the point of removal).
// Src is auto-merged from root_cell + extras + merge_src_into into
// every staged new cell and into merge_src_into.
void patch_ports(Cell* root_cell,
const std::vector<std::pair<IdString, SigSpec>>& port_replacements,
const std::vector<Cell*>& extras = {},
Cell* merge_src_into = nullptr);
// Flush staged cells_ / wires_ into the module without doing any
// connect_incremental or gc. Each committed cell's src attribute is
// pulled from `src_source` (typically the cell that's being expanded /
// unmapped into the staged helpers, so source-location tracking carries
// through transparently). Pass nullptr for src_source if the staged
// helpers have no natural ancestor.
// connect_incremental or port rewiring. Each committed cell's src
// attribute is pulled from `src_source` (typically the cell that's
// being expanded / unmapped into the staged helpers, so source-location
// tracking carries through transparently). Pass nullptr for src_source
// if the staged helpers have no natural ancestor.
void commit_inheriting_src(Cell* src_source);
RTLIL::Wire *addWire(RTLIL::IdString name, int width = 1);
RTLIL::Wire *addWire(RTLIL::IdString name, const RTLIL::Wire *other);

View file

@ -134,26 +134,28 @@ void log_replace_port(RTLIL::Module *module, RTLIL::Cell *cell,
}
struct OptExprPatcher : public RTLIL::Patch {
OptExprPatcher(Module *mod, SigMap *map, pool<Cell*> *removed = nullptr) : RTLIL::Patch(mod, map) {
removed_cells = removed;
}
OptExprPatcher(Module *mod, SigMap *map) : RTLIL::Patch(mod, map) {}
// Single-output rewrite via the compatible Patch::patch interface.
void patch(Cell *old_cell, IdString old_port, SigSpec new_sig, const std::string &info) {
new_sig.extend_u0(old_cell->getPort(old_port).size(), false);
log_replace_port(mod, old_cell, info, old_port, new_sig);
RTLIL::Patch::patch(old_cell, old_port, new_sig);
did_something = true;
}
void patch(Cell *old_cell, std::vector<std::pair<IdString, SigSpec>> ports, const std::string &info) {
// Multi-output rewrite via Patch::patch_ports. Used for cell types
// with more than one output port (e.g. $alu). patch_ports asserts
// `ports` covers every output of `old_cell` and removes the cell.
void patch_multi(Cell *old_cell, std::vector<std::pair<IdString, SigSpec>> ports, const std::string &info) {
for (auto &[port, new_sig] : ports) {
new_sig.extend_u0(old_cell->getPort(port).size(), false);
log_replace_port(mod, old_cell, info, port, new_sig);
}
RTLIL::Patch::patch(old_cell, ports);
RTLIL::Patch::patch_ports(old_cell, ports);
did_something = true;
}
};
bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap, bool keepdc, pool<Cell*> &removed_cells)
bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutative, SigMap &sigmap, bool keepdc)
{
IdString b_name = cell->hasPort(ID::B) ? ID::B : ID::A;
@ -224,7 +226,7 @@ bool group_cell_inputs(RTLIL::Module *module, RTLIL::Cell *cell, bool commutativ
log_debug("Replacing %s cell `%s' in module `%s' with cells using grouped bits:\n",
cell->type.unescape(), cell, module);
OptExprPatcher patcher(module, &sigmap, &removed_cells);
OptExprPatcher patcher(module, &sigmap);
// Replacement bit for each original sig_y bit.
dict<SigBit, SigBit> bit_map;
@ -584,12 +586,9 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
log("iterating over %d cells\n", GetSize(cells.sorted));
pool<Cell*> removed_cells;
for (auto cell : cells.sorted)
{
if (removed_cells.count(cell))
continue;
#define ACTION_DO(_p_, _s_) do { OptExprPatcher patcher(module, &assign_map, &removed_cells); patcher.patch(cell, _p_, _s_, input.as_string()); goto next_cell; } while (0)
#define ACTION_DO(_p_, _s_) do { OptExprPatcher patcher(module, &assign_map); patcher.patch(cell, _p_, _s_, input.as_string()); goto next_cell; } while (0)
#define ACTION_DO_Y(_v_) ACTION_DO(ID::Y, RTLIL::SigSpec(RTLIL::State::S ## _v_))
bool detect_const_and = false;
@ -639,19 +638,19 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
if (detect_const_and && (found_zero || found_inv || (found_undef && consume_x))) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, RTLIL::State::S0, "const_and");
goto next_cell;
}
if (detect_const_or && (found_one || found_inv || (found_undef && consume_x))) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, RTLIL::State::S1, "const_or");
goto next_cell;
}
if (non_const_input != State::Sm && !found_undef) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, non_const_input, "and_or_buffer");
goto next_cell;
}
@ -662,7 +661,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
SigBit sig_a = assign_map(cell->getPort(ID::A));
SigBit sig_b = assign_map(cell->getPort(ID::B));
if (!keepdc && (sig_a == sig_b || sig_a == State::Sx || sig_a == State::Sz || sig_b == State::Sx || sig_b == State::Sz)) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
if (cell->type.in(ID($xor), ID($_XOR_))) {
patcher.patch(cell, ID::Y, RTLIL::State::S0, "const_xor");
goto next_cell;
@ -679,7 +678,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (!sig_a.wire)
std::swap(sig_a, sig_b);
if (sig_b == State::S0 || sig_b == State::S1) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
bool is_gate = cell->type.in(ID($_XOR_), ID($_XNOR_));
int width = is_gate ? 1 : cell->getParam(ID::Y_WIDTH).as_int();
if (cell->type.in(ID($xor), ID($_XOR_))) {
@ -719,7 +718,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
cell->type = ID($not);
did_something = true;
} else {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, cell->getPort(ID::A), "unary_buffer");
}
goto next_cell;
@ -764,7 +763,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (cell->type == ID($xnor))
std::swap(b_group_0, b_group_1);
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
RTLIL::SigSpec y_new_0, y_new_1, y_new_x;
if (cell->type == ID($and)) {
@ -823,7 +822,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
RTLIL::SigSpec new_sig_y(width);
for (int i = 0; i < width; i++)
new_sig_y[i] = sig_s[i].data == State::S1 ? sig_b[i] : sig_a[i];
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, new_sig_y, "bwmux_const_s");
goto next_cell;
}
@ -846,7 +845,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
}
}
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
RTLIL::SigSpec y_new_0, y_new_1;
if (flip) {
@ -868,7 +867,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons
if (do_fine)
{
if (cell->type.in(ID($not), ID($pos), ID($and), ID($or), ID($xor), ID($xnor)))
if (group_cell_inputs(module, cell, true, assign_map, keepdc, removed_cells))
if (group_cell_inputs(module, cell, true, assign_map, keepdc))
goto next_cell;
if (cell->type.in(ID($logic_not), ID($logic_and), ID($logic_or), ID($reduce_or), ID($reduce_and), ID($reduce_bool)))
@ -1110,7 +1109,7 @@ skip_fine_alu:
if (0) {
found_the_x_bit:
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
if (cell->type.in(ID($reduce_xor), ID($reduce_xnor), ID($lt), ID($le), ID($ge), ID($gt)))
patcher.patch(cell, ID::Y, RTLIL::State::Sx, "x-bit in input");
else
@ -1142,7 +1141,7 @@ skip_fine_alu:
if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && GetSize(cell->getPort(ID::Y)) == 1 && GetSize(cell->getPort(ID::A)) == 1) {
if (auto inv_a = get_inverted(cell->getPort(ID::A), assign_map)) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, *inv_a, "double_invert");
goto next_cell;
}
@ -1285,7 +1284,7 @@ skip_fine_alu:
if (a[i].wire == NULL && b[i].wire == NULL && a[i] != b[i] && a[i].data <= RTLIL::State::S1 && b[i].data <= RTLIL::State::S1) {
RTLIL::SigSpec new_y = RTLIL::SigSpec(cell->type.in(ID($eq), ID($eqx)) ? RTLIL::State::S0 : RTLIL::State::S1);
new_y.extend_u0(cell->parameters[ID::Y_WIDTH].as_int(), false);
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, new_y, "isneq");
goto next_cell;
}
@ -1303,7 +1302,7 @@ skip_fine_alu:
if (new_a.size() == 0) {
RTLIL::SigSpec new_y = RTLIL::SigSpec(cell->type.in(ID($eq), ID($eqx)) ? RTLIL::State::S1 : RTLIL::State::S0);
new_y.extend_u0(cell->parameters[ID::Y_WIDTH].as_int(), false);
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, new_y, "empty");
goto next_cell;
}
@ -1401,7 +1400,7 @@ skip_fine_alu:
log_debug("Replacing %s cell `%s' (B=%s, SHR=%d) in module `%s' with fixed wiring: %s\n",
cell->type.unescape(), cell, log_signal(assign_map(cell->getPort(ID::B))), shift_bits, module, log_signal(sig_y));
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, sig_y, "shift_const");
goto next_cell;
}
@ -1478,7 +1477,7 @@ skip_fine_alu:
RTLIL::SigBit sig_ci = assign_map(cell->getPort(ID::CI));
int y_width = GetSize(cell->getPort(ID::Y));
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
SigSpec new_x, new_co;
SigSpec a_port;
bool a_port_signed;
@ -1512,7 +1511,7 @@ skip_fine_alu:
new_cell->setParam(ID::A_SIGNED, a_port_signed);
new_cell->setParam(ID::Y_WIDTH, y_width);
patcher.patch(cell, {{ID::Y, new_y}, {ID::X, new_x}, {ID::CO, new_co}}, "alu_identity");
patcher.patch_multi(cell, {{ID::Y, new_y}, {ID::X, new_x}, {ID::CO, new_co}}, "alu_identity");
goto next_cell;
}
@ -1536,7 +1535,7 @@ skip_identity:
if (mux_bool && cell->type.in(ID($mux), ID($_MUX_)) &&
cell->getPort(ID::A) == State::S0 && cell->getPort(ID::B) == State::S1) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, cell->getPort(ID::S), "mux_bool");
goto next_cell;
}
@ -1603,7 +1602,7 @@ skip_identity:
int width = GetSize(cell->getPort(ID::A));
if ((cell->getPort(ID::A).is_fully_undef() && cell->getPort(ID::B).is_fully_undef()) ||
cell->getPort(ID::S).is_fully_undef()) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, cell->getPort(ID::A), "mux_undef");
goto next_cell;
}
@ -1622,12 +1621,12 @@ skip_identity:
new_s = new_s.extract(0, new_s.size()-1);
}
if (new_s.size() == 0) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, new_a, "mux_empty");
goto next_cell;
}
if (new_a == RTLIL::SigSpec(RTLIL::State::S0) && new_b == RTLIL::SigSpec(RTLIL::State::S1)) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, new_s, "mux_sel01");
goto next_cell;
}
@ -1657,7 +1656,7 @@ skip_identity:
if (!conn.first.in(ID::S, ID::T, ID::U, ID::V, ID::Y))
undef_inputs += conn.second.is_fully_undef();
if (undef_inputs == num_inputs) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, cell->getPort(ID::A), "mux_undef");
goto next_cell;
}
@ -1672,7 +1671,7 @@ skip_identity:
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), dummy_arg, \
cell->parameters[ID::A_SIGNED].as_bool(), false, \
cell->parameters[ID::Y_WIDTH].as_int())); \
OptExprPatcher patcher(module, &assign_map, &removed_cells); \
OptExprPatcher patcher(module, &assign_map); \
patcher.patch(cell, ID::Y, y, stringf("%s", log_signal(a))); \
goto next_cell; \
} \
@ -1687,7 +1686,7 @@ skip_identity:
cell->parameters[ID::A_SIGNED].as_bool(), \
cell->parameters[ID::B_SIGNED].as_bool(), \
cell->parameters[ID::Y_WIDTH].as_int())); \
OptExprPatcher patcher(module, &assign_map, &removed_cells); \
OptExprPatcher patcher(module, &assign_map); \
patcher.patch(cell, ID::Y, y, stringf("%s, %s", log_signal(a), log_signal(b))); \
goto next_cell; \
} \
@ -1699,7 +1698,7 @@ skip_identity:
assign_map.apply(a), assign_map.apply(b); \
if (a.is_fully_const() && b.is_fully_const()) { \
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const())); \
OptExprPatcher patcher(module, &assign_map, &removed_cells); \
OptExprPatcher patcher(module, &assign_map); \
patcher.patch(cell, ID::Y, y, stringf("%s, %s", log_signal(a), log_signal(b))); \
goto next_cell; \
} \
@ -1712,7 +1711,7 @@ skip_identity:
assign_map.apply(a), assign_map.apply(b), assign_map.apply(s); \
if (a.is_fully_const() && b.is_fully_const() && s.is_fully_const()) { \
RTLIL::SigSpec y(RTLIL::const_ ## _t(a.as_const(), b.as_const(), s.as_const())); \
OptExprPatcher patcher(module, &assign_map, &removed_cells); \
OptExprPatcher patcher(module, &assign_map); \
patcher.patch(cell, ID::Y, y, stringf("%s, %s, %s", log_signal(a), log_signal(b), log_signal(s))); \
goto next_cell; \
} \
@ -1803,7 +1802,7 @@ skip_identity:
else {
log_debug("Replacing pow cell `%s' in module `%s' with multiply and left-shift\n",
cell->name.c_str(), module->name.c_str());
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
int a_width = cell->parameters[ID::A_WIDTH].as_int();
SigSpec y_wire = patcher.addWire(NEW_ID, y_size);
@ -1847,7 +1846,7 @@ skip_identity:
log_debug("Replacing multiply-by-zero cell `%s' in module `%s' with zero-driver.\n",
cell->name.c_str(), module->name.c_str());
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, RTLIL::SigSpec(0, sig_y.size()), "mul_zero");
goto next_cell;
}
@ -1892,7 +1891,7 @@ skip_identity:
a_zeros, b_zeros, cell->name.c_str(), module->name.c_str());
if (y_zeros >= GetSize(sig_y)) {
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, RTLIL::SigSpec(0, GetSize(sig_y)), "mul_low_zeros");
goto next_cell;
}
@ -1930,7 +1929,7 @@ skip_identity:
log_debug("Replacing divide-by-zero cell `%s' in module `%s' with undef-driver.\n",
cell->name.c_str(), module->name.c_str());
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, RTLIL::SigSpec(State::Sx, sig_y.size()), "div_zero");
goto next_cell;
}
@ -1977,7 +1976,7 @@ skip_identity:
// the sign bits are those of A (except when R=0)
if (is_truncating && a_signed && GetSize(sig_a) != 0 && exp != 0)
{
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
SigSpec truncating = sig_a.extract(0, exp);
SigSpec a_sign = sig_a[sig_a.size()-1];
@ -2064,7 +2063,7 @@ skip_identity:
RTLIL::SigSpec sig_ci = assign_map(cell->getPort(ID::CI));
int prev = 0;
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
SigSpec new_y, new_x, new_co_concat;
for (auto &p : split_points) {
@ -2101,7 +2100,7 @@ skip_identity:
sig_ci = p.second;
}
patcher.patch(cell, {{ID::Y, new_y}, {ID::X, new_x}, {ID::CO, new_co_concat}}, "alu_split");
patcher.patch_multi(cell, {{ID::Y, new_y}, {ID::X, new_x}, {ID::CO, new_co_concat}}, "alu_split");
goto next_cell;
}
skip_alu_split:
@ -2159,7 +2158,7 @@ skip_alu_split:
log_debug("Replacing cell `%s' in module `%s' with constant driver %s.\n",
cell, module, log_signal(y_value));
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
patcher.patch(cell, ID::Y, y_value, "eq_contradiction");
goto next_cell;
}
@ -2208,7 +2207,7 @@ skip_alu_split:
std::string condition, replacement;
SigSpec replace_sig(State::S0, GetSize(cell->getPort(ID::Y)));
bool replace = false;
OptExprPatcher patcher(module, &assign_map, &removed_cells);
OptExprPatcher patcher(module, &assign_map);
if (!is_signed)
{ /* unsigned */

View file

@ -313,7 +313,15 @@ struct OptMergeWorker
}
log_debug(" Removing %s cell `%s' from module `%s'.\n", remove_cell->type, remove_cell->name, module->name);
RTLIL::Patch patcher(module, &assign_map);
patcher.patch(remove_cell, port_replacements, keep_cell);
// Single-output cell types take the compatible Patch::patch
// path; multi-output types (e.g. $alu) use patch_ports,
// which covers all outputs and removes the cell.
if (port_replacements.size() == 1) {
patcher.patch(remove_cell, port_replacements[0].first, port_replacements[0].second,
/*extras=*/{}, keep_cell);
} else {
patcher.patch_ports(remove_cell, port_replacements, /*extras=*/{}, keep_cell);
}
total_count++;
}
did_something = !merged_duplicates.empty();

View file

@ -283,7 +283,12 @@ struct OptMergeIncWorker
log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name);
RTLIL::Patch patcher(module, &assign_map);
patcher.patch(cell, port_replacements, other_cell);
if (port_replacements.size() == 1) {
patcher.patch(cell, port_replacements[0].first, port_replacements[0].second,
/*extras=*/{}, other_cell);
} else {
patcher.patch_ports(cell, port_replacements, /*extras=*/{}, other_cell);
}
total_count++;
iter_count++;
}

View file

@ -775,13 +775,9 @@ struct OnehotPass : public Pass {
OnehotDatabase onehot_db(module, sigmap);
onehot_db.verbose = verbose_onehot;
// Track cells removed inline by patcher.gc so the outer loop
// (and any stale pointer accesses) can skip them.
pool<Cell*> removed_cells;
for (auto cell : module->selected_cells())
{
if (removed_cells.count(cell) || cell->type != ID($eq))
if (cell->type != ID($eq))
continue;
SigSpec A = sigmap(cell->getPort(ID::A));
@ -854,9 +850,7 @@ struct OnehotPass : public Pass {
replacement = sig;
}
removed_cells.insert(cell);
RTLIL::Patch patcher(module, &sigmap);
patcher.removed_cells = &removed_cells;
patcher.patch(cell, ID::Y, replacement);
}
}