diff --git a/kernel/rtlil_bufnorm.cc b/kernel/rtlil_bufnorm.cc index 7f86feee0..871500bf8 100644 --- a/kernel/rtlil_bufnorm.cc +++ b/kernel/rtlil_bufnorm.cc @@ -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 cells_to_remove; - dict output_port_cells; - dict 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 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); } } diff --git a/kernel/unstable/patch.cc b/kernel/unstable/patch.cc index 316de14ee..e114c48d6 100644 --- a/kernel/unstable/patch.cc +++ b/kernel/unstable/patch.cc @@ -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* 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 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* raw = wire.release(); mod->wires_[raw->name] = raw; @@ -110,63 +61,142 @@ Cell* Patch::commit_cell(std::unique_ptr 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 Patch::commit_staged() { + std::vector 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> &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> &port_replacements, Cell* merge_src_into) { - std::vector 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& extras, + Cell* merge_src_into, pool& 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& src_pool, + const std::vector& 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 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& 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 src_pool; + collect_src(root_cell, extras, merge_src_into, src_pool); + + std::vector 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>& port_replacements, + const std::vector& 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 listed; + std::vector 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 src_pool; + collect_src(root_cell, extras, merge_src_into, src_pool); + + std::vector 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 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 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 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 \ No newline at end of file +YOSYS_NAMESPACE_END diff --git a/kernel/unstable/patch.h b/kernel/unstable/patch.h index 2eacaf107..3742387ee 100644 --- a/kernel/unstable/patch.h +++ b/kernel/unstable/patch.h @@ -9,9 +9,6 @@ YOSYS_NAMESPACE_BEGIN // No virtual methods — subclasses cannot be dispatched through a Patch pointer. struct RTLIL::Patch : public CellAdderMixin { -private: - void gc(Cell* old_cell, bool track = false, pool* 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); Wire* commit_wire(std::unique_ptr wire); - pool leaves = {}; + // Move staged cells_/wires_ into the module. Returns raw pointers to + // the committed new cells in insertion order. + std::vector commit_staged(); public: Module* mod; SigMap* map; - pool* removed_cells = nullptr; vector> wires_ = {}; vector> cells_ = {}; @@ -33,22 +31,36 @@ public: void connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs); const std::vector &connections() const; - void patch(Cell* old_cell, IdString old_port, SigSpec new_sig); - void patch(Cell* old_cell, const std::vector> &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& 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> &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>& port_replacements, + const std::vector& 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); diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 30627a46e..d4de4afde 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -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 *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> 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> 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 &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 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 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 */ diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index 37063e74d..00360aa46 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -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(); diff --git a/passes/opt/opt_merge_inc.cc b/passes/opt/opt_merge_inc.cc index 39a76709d..ffdce708c 100644 --- a/passes/opt/opt_merge_inc.cc +++ b/passes/opt/opt_merge_inc.cc @@ -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++; } diff --git a/passes/opt/pmux2shiftx.cc b/passes/opt/pmux2shiftx.cc index 38a9fb142..029a2bf10 100644 --- a/passes/opt/pmux2shiftx.cc +++ b/passes/opt/pmux2shiftx.cc @@ -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 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); } }