From 0a69890e3f8bc91d360de54df7435f144797af8d Mon Sep 17 00:00:00 2001 From: Akash Levy Date: Tue, 7 Jul 2026 00:00:24 -0700 Subject: [PATCH] opt_prienc: require ConstEval fingerprint inputs to be a valid cut The round-robin (and PE/CLZ/CTZ) fingerprints pin candidate request/ start/select signals as free ConstEval inputs and evaluate the encoder output cone. ConstEval::eval() re-computes and re-set()s the FULL output of every combinational cell it needs. If a pinned bit is a combinational cell output and a sibling output bit of that same cell is pulled into the cone, evaluating the sibling re-sets the pinned bit to the cell's real value, contradicting the free value we pinned and tripping the assertion `current_val[i].wire != NULL || current_val[i] == value[i]` in kernel/consteval.h. The earlier clean_set_signals() guard only rejected constant/aliased bits; it did not ensure the pinned signals form a valid cut. Candidates are gathered purely by width, so an internal combinational wire (e.g. a slice of a wider arithmetic result) can be pinned, which is exactly what crashed on veer_speed1/picorv32/murax/raygentop. Add is_valid_consteval_cut(): a pinned bit is a safe leaf when it is a primary input, sequential-cell output or undriven (absent from bit_to_driver, which holds combinational drivers only); a combinational output is safe only if that cell's entire output lies within the pinned cut. Apply it in both fingerprint() and fingerprint_rr(). Declining an unclean cut only forgoes a possible rewrite, never yields a wrong one, and the intended arbiter inputs (request ports, idx_last flop outputs) remain valid cuts so real round-robin patterns still rewrite. Co-authored-by: Cursor --- passes/opt/opt_prienc.cc | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/passes/opt/opt_prienc.cc b/passes/opt/opt_prienc.cc index 07089283c..030cc9f60 100644 --- a/passes/opt/opt_prienc.cc +++ b/passes/opt/opt_prienc.cc @@ -260,6 +260,39 @@ struct OptPriEncWorker { return true; } + // A set of signals is a valid ConstEval "cut" to pin as free inputs only if + // pinning them can never collide with a value ConstEval derives while + // evaluating the cone. ConstEval::eval() re-computes and re-set()s the FULL + // output of any combinational cell it needs: so if a pinned bit is a + // combinational-cell output and a *sibling* output bit of that same cell + // lies outside the cut (and is pulled into the cone), evaluating the sibling + // re-sets the pinned bit to the cell's real value, which contradicts the + // free value we pinned -> the ConstEval assertion in set() fires. + // + // A bit is a safe leaf when it is a primary input, sequential-cell output or + // undriven (all absent from bit_to_driver, which holds combinational drivers + // only). A combinational-cell output is safe only if that cell's entire + // output lies within the cut. `cut` must be the union of every signal pinned + // together before a shared eval. + bool is_valid_consteval_cut(const SigSpec& cut) { + pool cut_bits; + for (auto bit : cut) + if (bit.wire) cut_bits.insert(bit); + for (auto bit : cut) { + if (bit.wire == nullptr) return false; + auto it = bit_to_driver.find(bit); + if (it == bit_to_driver.end()) continue; // safe leaf + Cell* d = it->second; + for (auto& conn : d->connections()) { + if (!d->output(conn.first)) continue; + for (auto ob : sigmap(conn.second)) + if (ob.wire && !cut_bits.count(ob)) + return false; + } + } + return true; + } + // Run all candidate test vectors through ConstEval and try to match each of // the four PE variants against the recorded outputs. Returns the matched // variant, or NONE. @@ -277,6 +310,9 @@ struct OptPriEncWorker { if (!clean_set_signals({&T_sig})) return PEVariant::NONE; + if (!is_valid_consteval_cut(T_sig)) + return PEVariant::NONE; + auto vs = gen_test_vectors(N); for (auto& v : vs) { ce.push(); @@ -462,6 +498,10 @@ struct OptPriEncWorker { ConstEval ce(module); if (!clean_set_signals({&req_sig, &start_sig})) return -1; + SigSpec cut = req_sig; + cut.append(start_sig); + if (!is_valid_consteval_cut(cut)) + return -1; bool ok0 = true, ok1 = true; auto deck = gen_test_vectors(N); int checks = 0;