From e87bb659569e0a2626c959adc645f649abb3fc21 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 22 Jan 2026 04:09:16 +0000 Subject: [PATCH 1/4] Move `Design::sort()` calls out of `opt` and `opt_clean` passes into the synth passes that need them. --- docs/source/code_examples/macro_commands/prep.ys | 1 + passes/opt/opt.cc | 1 - passes/opt/opt_clean.cc | 2 -- techlibs/common/prep.cc | 1 + techlibs/gowin/synth_gowin.cc | 1 + techlibs/xilinx/synth_xilinx.cc | 2 ++ 6 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/source/code_examples/macro_commands/prep.ys b/docs/source/code_examples/macro_commands/prep.ys index 1bec907f6..7ec7c7af8 100644 --- a/docs/source/code_examples/macro_commands/prep.ys +++ b/docs/source/code_examples/macro_commands/prep.ys @@ -17,6 +17,7 @@ coarse: opt_clean memory_collect opt -noff -keepdc -fast + sort check: stat diff --git a/passes/opt/opt.cc b/passes/opt/opt.cc index ec5760cd9..983437e64 100644 --- a/passes/opt/opt.cc +++ b/passes/opt/opt.cc @@ -193,7 +193,6 @@ struct OptPass : public Pass { } design->optimize(); - design->sort(); design->check(); log_header(design, "Finished fast OPT passes.%s\n", fast_mode ? "" : " (There is nothing left to do.)"); diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 3892c7581..b91577b53 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -715,7 +715,6 @@ struct OptCleanPass : public Pass { log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires); design->optimize(); - design->sort(); design->check(); keep_cache.reset(); @@ -780,7 +779,6 @@ struct CleanPass : public Pass { log("Removed %d unused cells and %d unused wires.\n", count_rm_cells, count_rm_wires); design->optimize(); - design->sort(); design->check(); keep_cache.reset(); diff --git a/techlibs/common/prep.cc b/techlibs/common/prep.cc index a98619abd..6798f2a5d 100644 --- a/techlibs/common/prep.cc +++ b/techlibs/common/prep.cc @@ -211,6 +211,7 @@ struct PrepPass : public ScriptPass run("memory_collect"); } run(nokeepdc ? "opt -noff -fast" : "opt -noff -keepdc -fast"); + run("sort"); } if (check_label("check")) diff --git a/techlibs/gowin/synth_gowin.cc b/techlibs/gowin/synth_gowin.cc index b9902659c..36d827b7c 100644 --- a/techlibs/gowin/synth_gowin.cc +++ b/techlibs/gowin/synth_gowin.cc @@ -311,6 +311,7 @@ struct SynthGowinPass : public ScriptPass if (check_label("map_luts")) { + run("sort"); if (nowidelut && abc9) { run("read_verilog -icells -lib -specify +/abc9_model.v"); run("abc9 -maxlut 4 -W 500"); diff --git a/techlibs/xilinx/synth_xilinx.cc b/techlibs/xilinx/synth_xilinx.cc index 46b30573c..c487206db 100644 --- a/techlibs/xilinx/synth_xilinx.cc +++ b/techlibs/xilinx/synth_xilinx.cc @@ -386,6 +386,8 @@ struct SynthXilinxPass : public ScriptPass run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')"); run("clean", " (skip if '-nosrl' and '-widemux=0')"); } + + run("sort"); } if (check_label("map_dsp", "(skip if '-nodsp')")) { From dcd7742d5220a2997eff9c80d699fd44ca2d2305 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 21 Jan 2026 04:02:02 +0000 Subject: [PATCH 2/4] Avoid scanning entire module if there are no wires to remove It's pretty common for `opt_clean` to find no wires to remove. In that case, there is no point scanning the entire design, which can be significantly expensive for huge designs. --- kernel/rtlil.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 42d5f56b6..eef1c319d 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -2990,6 +2990,8 @@ void RTLIL::Module::add(RTLIL::Binding *binding) void RTLIL::Module::remove(const pool &wires) { log_assert(refcount_wires_ == 0); + if (wires.empty()) + return; struct DeleteWireWorker { From 2468b391bfb146005569941835079e496fd32c3b Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Sat, 24 Jan 2026 01:48:15 +0000 Subject: [PATCH 3/4] Make `compare_signals` produce a total order. Currently when `s1` and `s2` are different bits of the same wire, it is possible for both `compare_signals(s1, s2)` and `compare_signals(s2, s1)` to return false. This means the calling code will call `assign_map.add()` for both `s1` and `s2`, which doesn't make much sense --- one of `s1` or `s2` should be consistently preferred. So fix that by preferring the `SigBit` with the smaller bit offset. --- passes/opt/opt_clean.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 3892c7581..661871d87 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -271,6 +271,9 @@ bool compare_signals(RTLIL::SigBit &s1, RTLIL::SigBit &s2, SigPool ®s, SigPoo return conns.check_any(s2); } + if (w1 == w2) + return s2.offset < s1.offset; + if (w1->port_output != w2->port_output) return w2->port_output; From 7d53d64a47b13e26105348961876c179a285a201 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Sat, 24 Jan 2026 01:51:34 +0000 Subject: [PATCH 4/4] Make the call to `compare_signals()` easier to read. The negation here is confusing. The intent of the code is "if `s1` is preferred over `s2` as the canonical `SigBit` for this signal, make `s1` the canonical `SigBit` in `assign_map`", so write the code that way instead of "if `s2` is not preferred over `s1` ...". This doesn't change any behavior now that `compare_signals()` is a total order, i.e. `s1` is preferred over `s2`, `s2` is preferred over `s1`, or `s1` and `s2` are equal. Now, when `s1` and `s2` are equal, we don't call `assign_map.add(s1)`, but that's already a noop in that case. --- passes/opt/opt_clean.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passes/opt/opt_clean.cc b/passes/opt/opt_clean.cc index 661871d87..ccdcbf7f9 100644 --- a/passes/opt/opt_clean.cc +++ b/passes/opt/opt_clean.cc @@ -346,7 +346,7 @@ bool rmunused_module_signals(RTLIL::Module *module, bool purge_mode, bool verbos RTLIL::Wire *wire = it.second; for (int i = 0; i < wire->width; i++) { RTLIL::SigBit s1 = RTLIL::SigBit(wire, i), s2 = assign_map(s1); - if (!compare_signals(s1, s2, register_signals, connected_signals, direct_wires)) + if (compare_signals(s2, s1, register_signals, connected_signals, direct_wires)) assign_map.add(s1); } }