From da947b72d8d9afea6facf4539bf9ee9dce3fb2c5 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Wed, 24 Jun 2026 22:40:21 -0700 Subject: [PATCH 1/3] opt_compact_prefix fix 3 --- passes/silimate/opt_compact_prefix.cc | 79 +++++++++++++++++++++------ 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/passes/silimate/opt_compact_prefix.cc b/passes/silimate/opt_compact_prefix.cc index 1432790fe..20fafeda6 100644 --- a/passes/silimate/opt_compact_prefix.cc +++ b/passes/silimate/opt_compact_prefix.cc @@ -961,6 +961,22 @@ struct OptCompactPrefixWorker : CutRegionWorker true, std::max(8192, max_width * max_width * 4), max_width * (max_width + 8)); } + // True if any bit of `sig` is driven by a cell this pass emitted on an + // earlier fixpoint sweep (tagged in execute()). The emitted compaction + // logic still fingerprints as the same function, so this guard stops the + // outer fixpoint loop from re-rewriting its own output indefinitely. + bool root_driver_emitted(const SigSpec &sig) + { + for (auto bit : sigmap(sig)) { + if (!bit.wire) + continue; + Cell *drv = bit_to_driver.at(bit, nullptr); + if (drv && drv->get_bool_attribute(ID(opt_compact_prefix_emitted))) + return true; + } + return false; + } + void run() { if (module->has_processes_warn()) @@ -990,6 +1006,8 @@ struct OptCompactPrefixWorker : CutRegionWorker } if (root_claimed(root.sig)) continue; + if (root_driver_emitted(root.sig)) + continue; int width = GetSize(root.sig); // Wide roots (>62) are valid forward-pack candidates; the @@ -1505,28 +1523,57 @@ struct OptCompactPrefixPass : public Pass int total_removed = 0; int total_emitted = 0; + // Overlapping compaction loops can share post-synthesis logic (e.g. one + // loop's start mask feeding the other loop's cone), so a single sweep + // claims the first region and masks the second. Iterate each module to + // a fixpoint: rewrite, tag the freshly-emitted cells, clean, and re-scan + // until a sweep finds nothing new. The tag (consumed by + // root_driver_emitted) stops the next sweep from re-matching this pass's + // own output, which still fingerprints as the same compaction function. + const int max_sweeps = 16; for (auto module : design->selected_modules()) { - OptCompactPrefixWorker worker(module, max_width); - if (walk_budget > 0) - worker.walk_budget = walk_budget; - if (eval_budget > 0) - worker.eval_budget = eval_budget; - if (attempt_budget > 0) - worker.attempt_budget = attempt_budget; - worker.run(); - total_forward += worker.forward_rewrites; - total_reverse += worker.reverse_rewrites; - total_modulo += worker.modulo_rewrites; - total_removed += worker.old_cells_removed; - total_emitted += worker.new_cells_emitted; + for (int sweep = 0; sweep < max_sweeps; sweep++) { + pool before; + for (auto c : module->cells()) + before.insert(c); + + OptCompactPrefixWorker worker(module, max_width); + if (walk_budget > 0) + worker.walk_budget = walk_budget; + if (eval_budget > 0) + worker.eval_budget = eval_budget; + if (attempt_budget > 0) + worker.attempt_budget = attempt_budget; + worker.run(); + + total_forward += worker.forward_rewrites; + total_reverse += worker.reverse_rewrites; + total_modulo += worker.modulo_rewrites; + total_removed += worker.old_cells_removed; + total_emitted += worker.new_cells_emitted; + + if (worker.forward_rewrites + worker.reverse_rewrites + + worker.modulo_rewrites == 0) + break; + + // Tag the cells emitted by this sweep so the next sweep skips + // them, then drop the now-dangling old cone so the masked + // sibling region becomes visible. + for (auto c : module->cells()) + if (!before.count(c)) + c->set_bool_attribute(ID(opt_compact_prefix_emitted)); + Yosys::run_pass("clean -purge"); + } } + // Drop the internal bookkeeping tag so it never leaks into the netlist. + for (auto module : design->modules()) + for (auto c : module->cells()) + c->attributes.erase(ID(opt_compact_prefix_emitted)); + log("Rewrote %d forward pack(s), %d reverse suffix read(s), %d modulo decimation(s); " "removed %d old cell(s), emitted %d new cell(s).\n", total_forward, total_reverse, total_modulo, total_removed, total_emitted); - - if (total_forward || total_reverse || total_modulo) - Yosys::run_pass("clean -purge"); } } OptCompactPrefixPass; From 139caf991ca4168d5abbd5d573c4d884affc34ed Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Wed, 24 Jun 2026 23:52:30 -0700 Subject: [PATCH 2/3] opt_compact_prefix: scope per-sweep clean to module, warn on non-convergence Address review feedback on the fixpoint loop: - Scope the per-sweep cleanup to the module under rewrite via Pass::call_on_module(..., "clean -purge") instead of running clean over the whole design selection. This avoids O(N^2) work across modules and keeps untouched modules' dangling cells until their own sweep, matching the original single-call behavior. - Emit a log_warning if a module fails to reach a fixpoint within max_sweeps, so silent truncation of compaction is visible. Co-authored-by: Cursor --- passes/silimate/opt_compact_prefix.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/passes/silimate/opt_compact_prefix.cc b/passes/silimate/opt_compact_prefix.cc index 20fafeda6..fdc902a64 100644 --- a/passes/silimate/opt_compact_prefix.cc +++ b/passes/silimate/opt_compact_prefix.cc @@ -1555,14 +1555,20 @@ struct OptCompactPrefixPass : public Pass if (worker.forward_rewrites + worker.reverse_rewrites + worker.modulo_rewrites == 0) break; + if (sweep == max_sweeps - 1) + log_warning("opt_compact_prefix: fixpoint not reached for module %s " + "after %d sweeps; some compaction opportunities may remain.\n", + log_id(module), max_sweeps); // Tag the cells emitted by this sweep so the next sweep skips // them, then drop the now-dangling old cone so the masked - // sibling region becomes visible. + // sibling region becomes visible. Scope the clean to the module + // under rewrite so untouched modules keep their dangling cells + // until their own sweep, matching the original single-call run. for (auto c : module->cells()) if (!before.count(c)) c->set_bool_attribute(ID(opt_compact_prefix_emitted)); - Yosys::run_pass("clean -purge"); + Pass::call_on_module(design, module, "clean -purge"); } } From 2acff6a62ce7237799f08c544ca14a7795500ba3 Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Wed, 24 Jun 2026 23:52:31 -0700 Subject: [PATCH 3/3] tests: fix sv_implicit_ports for port-resize log severity change "Reduce port resize to warning" changed the resize message from log_warning() to log(), which -q suppresses. Run the resize case without -q and drop the stale "Warning: " prefix so the message is observed. Co-authored-by: Cursor --- tests/various/sv_implicit_ports.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/various/sv_implicit_ports.sh b/tests/various/sv_implicit_ports.sh index ace95b3ba..1be68917d 100755 --- a/tests/various/sv_implicit_ports.sh +++ b/tests/various/sv_implicit_ports.sh @@ -112,7 +112,9 @@ endmodule EOT # Mixed implicit and explicit 2 -(${YOSYS} -f "verilog -sv" -qp "prep -flatten -top top; select -assert-count 1 t:\$add" - <&1 | grep -F "Warning: Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null +) 2>&1 | grep -F "Resizing cell port top.add_i.b from 10 bits to 8 bits." > /dev/null