bitpattern: add analysis effort limit, use in proc_rmdead

This commit is contained in:
Emil J. Tywoniak 2026-05-04 23:09:21 +02:00
parent a966d06524
commit a3740057e3
4 changed files with 48 additions and 23 deletions

View File

@ -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<RTLIL::State> 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;
}

View File

@ -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);
}
}

View File

@ -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,21 +83,25 @@ struct FullyDefinedPool
void proc_rmdead(RTLIL::SwitchRule *sw, int &counter, int &full_case_counter);
template <class Pool>
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);
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;
if (!pool.take(sig))
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) {
if (sw->cases[i]->compare.size() == 0) {
@ -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<FullyDefinedPool>(sw, counter, full_case_counter);
else
proc_rmdead_impl<BitPatternPool>(sw, counter, full_case_counter);
if (can_use_fully_defined_pool(sw)) {
auto pool = FullyDefinedPool(sw->signal);
proc_rmdead_impl<FullyDefinedPool>(pool, sw, counter, full_case_counter);
} else {
auto pool = BitPatternPool(sw->signal);
pool.limit = 100000;
proc_rmdead_impl<BitPatternPool>(pool, sw, counter, full_case_counter);
}
}
struct ProcRmdeadPass : public Pass {

View File

@ -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