From a3740057e32b4d059a62a19c5a81949824c9beff Mon Sep 17 00:00:00 2001 From: "Emil J. Tywoniak" Date: Mon, 4 May 2026 23:09:21 +0200 Subject: [PATCH] bitpattern: add analysis effort limit, use in proc_rmdead --- kernel/bitpattern.h | 17 +++++++++-- passes/proc/proc_mux.cc | 2 +- passes/proc/proc_rmdead.cc | 46 +++++++++++++++++------------ tests/unit/kernel/bitpatternTest.cc | 6 ++++ 4 files changed, 48 insertions(+), 23 deletions(-) diff --git a/kernel/bitpattern.h b/kernel/bitpattern.h index e2071436c..ebe0dc934 100644 --- a/kernel/bitpattern.h +++ b/kernel/bitpattern.h @@ -25,6 +25,12 @@ YOSYS_NAMESPACE_BEGIN +enum TakeResult { + FALSE, + TRUE, + TOO_BIG +}; + /** * This file implements BitPatternPool for efficiently storing and querying * sets of fixed-width 2-valued logic constants compressed as "bit patterns". @@ -39,6 +45,7 @@ YOSYS_NAMESPACE_BEGIN */ struct BitPatternPool { + size_t limit = SIZE_MAX; int width; struct bits_t { std::vector bitdata; @@ -167,9 +174,10 @@ struct BitPatternPool * Taking 011 out of pool({01a}) -> pool({010}), returns true. * Taking 011 out of pool({010}) does nothing, returns false. */ - bool take(RTLIL::SigSpec sig) + [[nodiscard]] + TakeResult take(RTLIL::SigSpec sig) { - bool status = false; + TakeResult status = FALSE; bits_t bits = sig2bits(sig); for (auto it = database.begin(); it != database.end();) if (match(*it, bits)) { @@ -180,12 +188,15 @@ struct BitPatternPool new_pattern.bitdata = it->bitdata; new_pattern[i] = bits[i] == RTLIL::State::S1 ? RTLIL::State::S0 : RTLIL::State::S1; database.insert(new_pattern); + if (database.size() > limit) + return TOO_BIG; } it = database.erase(it); - status = true; + status = TRUE; continue; } else ++it; + return status; } diff --git a/passes/proc/proc_mux.cc b/passes/proc/proc_mux.cc index 68127d1bc..4e43e64ef 100644 --- a/passes/proc/proc_mux.cc +++ b/passes/proc/proc_mux.cc @@ -383,7 +383,7 @@ RTLIL::SigSpec signal_to_mux_tree(RTLIL::Module *mod, SnippetSwCache &swcache, d if (!pat.is_fully_const()) extra_group_for_next_case = true; else if (!ifxmode) - pool.take(pat); + log_assert(pool.take(pat) != TakeResult::TOO_BIG); } } diff --git a/passes/proc/proc_rmdead.cc b/passes/proc/proc_rmdead.cc index 8f5eda085..f447f71aa 100644 --- a/passes/proc/proc_rmdead.cc +++ b/passes/proc/proc_rmdead.cc @@ -54,12 +54,14 @@ struct FullyDefinedPool : max_patterns{signal.size() >= 32 ? 0ul : 1ul << signal.size()} {} - bool take(RTLIL::SigSpec sig) + // Yes, we never return TOO_BIG, since FullyDefinedPool::max_patterns + // and BitPatternPool::limits are different concepts + TakeResult take(RTLIL::SigSpec sig) { if (default_reached || patterns.count(sig)) - return false; + return FALSE; patterns.insert(sig); - return true; + return TRUE; } void take_all() @@ -81,20 +83,24 @@ struct FullyDefinedPool void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter); template -static void proc_rmdead_impl(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) +static void proc_rmdead_impl(Pool& pool, RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) { - Pool pool(sw->signal); - + bool is_too_big = false; for (size_t i = 0; i < sw->cases.size(); i++) { bool is_default = GetSize(sw->cases[i]->compare) == 0 && (!pool.empty() || GetSize(sw->signal) == 0); - - for (size_t j = 0; j < sw->cases[i]->compare.size(); j++) { - RTLIL::SigSpec sig = sw->cases[i]->compare[j]; - if (!sig.is_fully_const()) - continue; - if (!pool.take(sig)) - sw->cases[i]->compare.erase(sw->cases[i]->compare.begin() + (j--)); + if (!is_too_big) { + for (size_t j = 0; j < sw->cases[i]->compare.size(); j++) { + RTLIL::SigSpec sig = sw->cases[i]->compare[j]; + if (!sig.is_fully_const()) + continue; + auto res = pool.take(sig); + if (res == TakeResult::TOO_BIG) { + is_too_big = true; // Skip the rest of the analysis + } + if (res == TakeResult::FALSE) + sw->cases[i]->compare.erase(sw->cases[i]->compare.begin() + (j--)); + } } if (!is_default) { @@ -104,8 +110,6 @@ static void proc_rmdead_impl(RTLIL::SwitchRule *sw, int &counter, int &full_case counter++; continue; } - // if (pool.empty()) - // sw->cases[i]->compare.clear(); } for (auto switch_it : sw->cases[i]->switches) @@ -123,10 +127,14 @@ static void proc_rmdead_impl(RTLIL::SwitchRule *sw, int &counter, int &full_case void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter) { - if (can_use_fully_defined_pool(sw)) - proc_rmdead_impl(sw, counter, full_case_counter); - else - proc_rmdead_impl(sw, counter, full_case_counter); + if (can_use_fully_defined_pool(sw)) { + auto pool = FullyDefinedPool(sw->signal); + proc_rmdead_impl(pool, sw, counter, full_case_counter); + } else { + auto pool = BitPatternPool(sw->signal); + pool.limit = 100000; + proc_rmdead_impl(pool, sw, counter, full_case_counter); + } } struct ProcRmdeadPass : public Pass { diff --git a/tests/unit/kernel/bitpatternTest.cc b/tests/unit/kernel/bitpatternTest.cc index 001d47060..6cf346e96 100644 --- a/tests/unit/kernel/bitpatternTest.cc +++ b/tests/unit/kernel/bitpatternTest.cc @@ -27,6 +27,12 @@ TEST(BitpatternTest, has) // 01a is not covered by 011 EXPECT_FALSE(BitPatternPool(_011).has_all(_01a)); EXPECT_FALSE(BitPatternPool(_111).has_all(_01a)); + // test mutating and analysis effort limits + auto pool = BitPatternPool(3); + pool.limit = 4; + EXPECT_EQ(pool.take(_111), TakeResult::TRUE); + EXPECT_EQ(pool.take(_111), TakeResult::FALSE); + EXPECT_EQ(pool.take(_01a), TakeResult::TOO_BIG); } YOSYS_NAMESPACE_END