Fix O(n*2) analysis in const-bit-op-tree (#6791)

Note this might miss some cases where a sub-tree within an And/Or/Xor
tree is optimizeable, but not the whole tree, but in practice this seems
to work better than the alternative of keeping a set of failed nodes and
bail early.
This commit is contained in:
Geza Lore 2025-12-11 14:32:25 +00:00 committed by GitHub
parent afc4bed0f8
commit af1be26b96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 26 additions and 17 deletions

View File

@ -1297,23 +1297,32 @@ class ConstVisitor final : public VNVisitor {
if (nodep->widthMin() != 1) return false;
if (!v3Global.opt.fConstBitOpTree()) return false;
string debugPrefix;
const int width = nodep->width();
AstNodeExpr* rootp = nodep;
unsigned externalOps = 0;
// Reach past a plain making AND
if (const AstAnd* const andp = VN_CAST(nodep, And)) {
if (isConst(andp->lhsp(), 1)) {
rootp = andp->rhsp();
externalOps = 1;
}
}
// Only optimize if rootp is in fact the root of a tree of identical
// operations, otherwise analysis on an eventually unoptimizable unablanced
// tree can go O(N^2) as we would re-analyze every time as we move up the
// tree, repeatedly re-discover the sub-tree is not optimizable.
if (rootp->type() == nodep->backp()->type()) return false;
std::string debugPrefix;
if (debug() >= 9) { // LCOV_EXCL_START
static int s_c = 0;
debugPrefix = "- matchBitOpTree[";
debugPrefix += cvtToStr(++s_c);
debugPrefix += "] ";
debugPrefix = "- matchBitOpTree[" + std::to_string(++s_c) + "] ";
nodep->dumpTree(debugPrefix + "INPUT: ");
} // LCOV_EXCL_STOP
AstNode* newp = nullptr;
const AstAnd* const andp = VN_CAST(nodep, And);
const int width = nodep->width();
if (andp && isConst(andp->lhsp(), 1)) { // 1 & BitOpTree
newp = ConstBitOpTreeVisitor::simplify(andp->rhsp(), width, 1, m_statBitOpReduction);
} else { // BitOpTree
newp = ConstBitOpTreeVisitor::simplify(nodep, width, 0, m_statBitOpReduction);
}
AstNodeExpr* const newp
= ConstBitOpTreeVisitor::simplify(rootp, width, externalOps, m_statBitOpReduction);
if (newp) {
nodep->replaceWithKeepDType(newp);

View File

@ -16,7 +16,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "-fno-dfg", "--stats", test.
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 44)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 49)
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes()

View File

@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", "--coverage", "--
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 478)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 550)
test.passes()

View File

@ -18,7 +18,7 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", test.pli_filename
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 37)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 43)
test.file_grep(test.stats, r'SplitVar, packed variables split automatically\s+(\d+)', 1)
test.passes()

View File

@ -19,6 +19,6 @@ test.compile(verilator_flags2=[
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 1)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 4)
test.passes()

View File

@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats"])
test.execute()
if test.vlt:
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 158)
test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 160)
test.passes()