From 709149c42af0503a9b4db5b0581bf6e28ebbd494 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 15 Jun 2026 16:55:04 +0200 Subject: [PATCH 1/5] opt_muxtree: add reconvergence test --- tests/opt/opt_muxtree_port_reconverge.ys | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/opt/opt_muxtree_port_reconverge.ys diff --git a/tests/opt/opt_muxtree_port_reconverge.ys b/tests/opt/opt_muxtree_port_reconverge.ys new file mode 100644 index 000000000..3353c9e46 --- /dev/null +++ b/tests/opt/opt_muxtree_port_reconverge.ys @@ -0,0 +1,55 @@ +read_rtlil << EOT +module \top + wire input 1 \a + wire input 2 \b + wire output 3 \out + wire $0\out[0:0] + wire \y + wire $1\out[0:0] + wire \s + wire \q + wire \n_a + + cell $mux \mux + parameter \WIDTH 1 + connect \A 1'0 + connect \B \a + connect \S \b + connect \Y \y + end + + attribute \full_case 1 + cell $pmux \pmux + parameter \WIDTH 1 + parameter \S_WIDTH 2 + connect \A 1'x + connect \B { \y \y } + connect \S { \n_a \s } + connect \Y \q + end + + attribute \full_case 1 + cell $not \not + parameter \A_SIGNED 0 + parameter \Y_WIDTH 1 + parameter \A_WIDTH 1 + connect \A \a + connect \Y \n_a + end + + connect $0\out[0:0] $1\out[0:0] + connect \s \a + connect $1\out[0:0] \q + connect \out \q +end +EOT +# a drives out +select -assert-any i:a %co* o:out %i + +opt_muxtree +# a gets optimized out +opt_reduce +opt_expr + +# a no longer drives out +select -assert-any o:out %ci* i:a %i \ No newline at end of file From 7d942b98930893496d7f441a67962cb0d89e6d95 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 15 Jun 2026 16:55:25 +0200 Subject: [PATCH 2/5] opt_muxtree: inhibit opt on reconvergence --- passes/opt/opt_muxtree.cc | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/passes/opt/opt_muxtree.cc b/passes/opt/opt_muxtree.cc index 4887355b2..e1424f94e 100644 --- a/passes/opt/opt_muxtree.cc +++ b/passes/opt/opt_muxtree.cc @@ -34,7 +34,7 @@ PRIVATE_NAMESPACE_BEGIN * A multiplexer tree (mux tree) is a tree composed exclusively of muxes. * By mux, I mean $mux or $pmux. By port, I usually mean input port. * The children of a node are all the muxes driving its input ports (A, B). - * It must be rooted in a "root mux", a mux which has multiple mux users + * It must be rooted in a "root mux", a mux which has multiple mux port users * or any number of non-mux users. Only the root and leaf nodes can be * root muxes, not the internal nodes. Leaf nodes that are root muxes * are roots of "input trees". @@ -64,10 +64,17 @@ struct OptMuxtreeWorker int removed_count; int glob_evals_left = 10'000'000; + struct MuxPort { + int mux; + // Hacky: -1 is port A, if positive then indexes B port slices (of which $mux has only one) + int port; + inline Hasher hash_into(Hasher h) const { h.eat(mux); h.eat(port); return h; } + bool operator==(const MuxPort &other) const { return mux == other.mux && port == other.port; } + }; struct bitinfo_t { // Is bit directly used by non-mux cells or ports? bool seen_non_mux; - pool mux_users; + pool mux_users; std::optional mux_driver; }; @@ -94,10 +101,10 @@ struct OptMuxtreeWorker vector root_enable_muxes; pool root_mux_rerun; - portinfo_t used_port_bit(RTLIL::SigSpec& sig, int mux_idx) { + portinfo_t used_port_bit(RTLIL::SigSpec& sig, int mux_idx, int port_idx) { portinfo_t portinfo = {}; for (int bit_idx : sig2bits(sig)) { - bit2info[bit_idx].mux_users.insert(mux_idx); + bit2info[bit_idx].mux_users.insert({mux_idx, port_idx}); portinfo.input_sigs.insert(bit_idx); } return portinfo; @@ -127,7 +134,7 @@ struct OptMuxtreeWorker for (int i = 0; i < GetSize(sig_s); i++) { RTLIL::SigSpec sig = sig_b.extract(i*GetSize(sig_a), GetSize(sig_a)); RTLIL::SigSpec ctrl_sig = assign_map(SigSpec{sig_s[i]}); - portinfo_t portinfo = used_port_bit(sig, this_mux_idx); + portinfo_t portinfo = used_port_bit(sig, this_mux_idx, i); portinfo.ctrl_sig = sig2bits(ctrl_sig, false).front(); portinfo.const_activated = ctrl_sig.is_fully_const() && ctrl_sig.as_bool(); portinfo.const_deactivated = ctrl_sig.is_fully_const() && !ctrl_sig.as_bool(); @@ -135,7 +142,7 @@ struct OptMuxtreeWorker } // Analyze port A - muxinfo.ports.push_back(used_port_bit(sig_a, this_mux_idx)); + muxinfo.ports.push_back(used_port_bit(sig_a, this_mux_idx, -1)); for (int idx : sig2bits(sig_y)) { if (bit2info[idx].mux_driver) @@ -170,17 +177,18 @@ struct OptMuxtreeWorker // bit2info knows the mux users and mux drivers of bits // use this to tell mux2info ports about what muxes are driven by it for (int i = 0; i < GetSize(bit2info); i++) - for (int j : bit2info[i].mux_users) - for (auto &p : mux2info[j].ports) { - if (p.input_sigs.count(i)) - if (bit2info[i].mux_driver) - p.input_muxes.insert(*bit2info[i].mux_driver); + for (auto muxport : bit2info[i].mux_users) { + for (auto &p : mux2info[muxport.mux].ports) { + if (p.input_sigs.count(i)) + if (bit2info[i].mux_driver) + p.input_muxes.insert(*bit2info[i].mux_driver); + } } } void populate_roots() { // mux_to_users[i] means "set of muxes using output of mux i" - dict> mux_to_users; + dict> mux_to_users; // Pure root muxes (outputs seen by non-muxes) root_enable_muxes.resize(GetSize(mux2info)); // All root muxes (outputs seen by non-muxes or multiple muxes) @@ -188,8 +196,8 @@ struct OptMuxtreeWorker for (auto &bi : bit2info) { if (bi.mux_driver) - for (int j : bi.mux_users) - mux_to_users[*bi.mux_driver].insert(j); + for (auto muxport : bi.mux_users) + mux_to_users[*bi.mux_driver].insert(muxport); if (!bi.seen_non_mux) continue; if (bi.mux_driver) { From 29a6295d7c904d52706a6ff4cb97ff826dab50ab Mon Sep 17 00:00:00 2001 From: Krystine Sherwin <93062060+KrystalDelusion@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:59:12 +1200 Subject: [PATCH 3/5] tests/opt: Fix comments --- tests/opt/opt_muxtree_port_reconverge.ys | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/opt/opt_muxtree_port_reconverge.ys b/tests/opt/opt_muxtree_port_reconverge.ys index 3353c9e46..592786dfc 100644 --- a/tests/opt/opt_muxtree_port_reconverge.ys +++ b/tests/opt/opt_muxtree_port_reconverge.ys @@ -47,9 +47,8 @@ EOT select -assert-any i:a %co* o:out %i opt_muxtree -# a gets optimized out opt_reduce opt_expr -# a no longer drives out -select -assert-any o:out %ci* i:a %i \ No newline at end of file +# a still drives out +select -assert-any o:out %ci* i:a %i From a216efad1cf69eb9e873d610af81489fd1a29428 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Mon, 27 Jul 2026 14:23:34 +0300 Subject: [PATCH 4/5] wheels: force static libffi in build - only build static libffi - improve resilience of cibw environment variables --- .github/workflows/wheels.yml | 8 ++++---- .github/workflows/wheels/cibw_before_all.sh | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 500431888..30214d6d8 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -93,14 +93,14 @@ jobs: CIBW_BEFORE_ALL: bash ./.github/workflows/wheels/cibw_before_all.sh CIBW_ENVIRONMENT: > OPTFLAGS=-O3 - PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig - PATH="$PWD/bison/src:$PATH" + PKG_CONFIG_PATH={project}/ffi/pfx/lib/pkgconfig + PATH="{project}/bison/src:$PATH" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release" CIBW_ENVIRONMENT_MACOS: > OPTFLAGS=-O3 - PKG_CONFIG_PATH=./ffi/pfx/lib/pkgconfig + PKG_CONFIG_PATH={project}/ffi/pfx/lib/pkgconfig MACOSX_DEPLOYMENT_TARGET=11 - PATH="$PWD/bison/src:$PATH" + PATH="{project}/bison/src:$PATH" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release" CIBW_BEFORE_BUILD: bash ./.github/workflows/wheels/cibw_before_build.sh CIBW_TEST_COMMAND: python3 {project}/tests/pyosys/run_tests.py diff --git a/.github/workflows/wheels/cibw_before_all.sh b/.github/workflows/wheels/cibw_before_all.sh index 6450ce273..61bfb2181 100644 --- a/.github/workflows/wheels/cibw_before_all.sh +++ b/.github/workflows/wheels/cibw_before_all.sh @@ -29,9 +29,7 @@ fi set -e -x cd ffi ## Ultimate libyosys.so will be shared, so we need fPIC for the static libraries - CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx + LDFLAGS=-fPIC CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure --prefix=$PWD/pfx --enable-static --disable-shared make clean make install -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu) - ## Forces static library to be used in all situations - sed -i.bak 's@-L${toolexeclibdir} -lffi@${toolexeclibdir}/libffi.a@' ./pfx/lib/pkgconfig/libffi.pc ) From 65856617400fe8ac3dc3cf6eba2930ddcba728f2 Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 27 Jul 2026 16:49:29 +0200 Subject: [PATCH 5/5] aigmap: remove unused and_cache --- passes/techmap/aigmap.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/passes/techmap/aigmap.cc b/passes/techmap/aigmap.cc index 6b7e0c377..c1ec214fd 100644 --- a/passes/techmap/aigmap.cc +++ b/passes/techmap/aigmap.cc @@ -92,7 +92,6 @@ struct AigmapPass : public Pass { } vector sigs; - dict, SigBit> and_cache; for (int node_idx = 0; node_idx < GetSize(aig.nodes); node_idx++) { @@ -115,15 +114,10 @@ struct AigmapPass : public Pass { goto skip_inverter; } else { - pair key(node.left_parent, node.right_parent); - if (and_cache.count(key)) - bit = and_cache.at(key); - else { - bit = module->addWire(NEW_ID); - auto gate = module->addAndGate(NEW_ID, A, B, bit); - if (select_mode) - new_sel.insert(gate->name); - } + bit = module->addWire(NEW_ID); + auto gate = module->addAndGate(NEW_ID, A, B, bit); + if (select_mode) + new_sel.insert(gate->name); } }