From 59b85f670b0d379f84469ed4bc620e9a31c5c1ea Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Sun, 28 Jun 2026 09:30:48 -0400 Subject: [PATCH] Fix constant pool recache after dead scope removal (#7845) --- src/V3AstNodeOther.h | 4 +- src/V3AstNodes.cpp | 25 ++++++-- src/V3Dead.cpp | 2 +- test_regress/t/t_opt_constpool_recache.py | 21 +++++++ test_regress/t/t_opt_constpool_recache.v | 75 +++++++++++++++++++++++ 5 files changed, 119 insertions(+), 8 deletions(-) create mode 100755 test_regress/t/t_opt_constpool_recache.py create mode 100644 test_regress/t/t_opt_constpool_recache.v diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 666a3ec22..3c25a9992 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1000,8 +1000,8 @@ public: // this matters, the caller must handle the dtype difference as appropriate. If 'mergeDType' is // false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true. AstVarScope* findConst(AstConst* initp, bool mergeDType); - // Rebuild hashes after potential removals - void reCache(); + // Rebuild hashes and missing variable scopes after potential removals + void rebuildVarScopesAndCache(); }; class AstConstraint final : public AstNode { // Constraint diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index ec26f24a9..aba8a9d0f 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include // Routines for dumping dict fields (NOTE: due to leading ',' they can't be used for first field in @@ -1779,14 +1780,28 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { return varScopep; } -void AstConstPool::reCache() { +void AstConstPool::rebuildVarScopesAndCache() { m_tables.clear(); m_consts.clear(); + std::unordered_map varScopeps; for (AstVarScope* vscp = m_scopep->varsp(); vscp; vscp = VN_CAST(vscp->nextp(), VarScope)) { - AstNode* const valuep = vscp->varp()->valuep(); - const V3Hash hash = V3Hasher::uncachedHash(valuep); - if (VN_IS(valuep, InitArray)) m_tables.emplace(hash.value(), vscp); - if (VN_IS(valuep, Const)) m_consts.emplace(hash.value(), vscp); + varScopeps.emplace(vscp->varp(), vscp); + } + for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { + AstVar* const varp = VN_CAST(nodep, Var); + if (!varp) continue; + AstNode* const valuep = varp->valuep(); + if (!valuep) continue; + const bool isTable = VN_IS(valuep, InitArray); + const AstConst* const constp = VN_CAST(valuep, Const); + if (!isTable && !constp) continue; + AstVarScope*& vscp = varScopeps[varp]; + if (!vscp) { + vscp = new AstVarScope{varp->fileline(), m_scopep, varp}; + m_scopep->addVarsp(vscp); + } + if (isTable) m_tables.emplace(V3Hasher::uncachedHash(valuep).value(), vscp); + if (constp) m_consts.emplace(constp->num().toHash().value(), vscp); } } diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 677f0b2aa..411ec57eb 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -597,7 +597,7 @@ public: // We may have removed some datatypes, cleanup nodep->typeTablep()->repairCache(); VIsCached::clearCacheTree(); // Removing assignments may affect isPure - nodep->constPoolp()->reCache(); + nodep->constPoolp()->rebuildVarScopesAndCache(); } ~DeadVisitor() override { V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified); diff --git a/test_regress/t/t_opt_constpool_recache.py b/test_regress/t/t_opt_constpool_recache.py new file mode 100755 index 000000000..ac30d3a16 --- /dev/null +++ b/test_regress/t/t_opt_constpool_recache.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +test.compile(verilator_flags2=['--binary', '--stats']) + +test.execute() + +test.file_grep(test.stats, r'Optimizations, Cases table normal\s+(\d+)', 1) +test.file_grep(test.stats, r'ConstPool, Constants emitted\s+(\d+)', 1) + +test.passes() diff --git a/test_regress/t/t_opt_constpool_recache.v b/test_regress/t/t_opt_constpool_recache.v new file mode 100644 index 000000000..35ca38a27 --- /dev/null +++ b/test_regress/t/t_opt_constpool_recache.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module t; + logic clk = 1'b0; + always #5 clk = ~clk; + + logic [31:0] cyc = 0; + logic [3:0] idx; + assign idx = cyc[3:0]; + + // V3Case lowers this to a 512-bit constant-pool lookup table before V3Dead + // calls AstConstPool::rebuildVarScopesAndCache(). + logic [31:0] case_word; + always_comb + case (idx) + 4'h0: case_word = 32'h00000000; + 4'h1: case_word = 32'h00000001; + 4'h2: case_word = 32'h00000002; + 4'h3: case_word = 32'h00000003; + 4'h4: case_word = 32'h00000004; + 4'h5: case_word = 32'h00000005; + 4'h6: case_word = 32'h00000006; + 4'h7: case_word = 32'h00000007; + 4'h8: case_word = 32'h00000008; + 4'h9: case_word = 32'h00000009; + 4'ha: case_word = 32'h0000000a; + 4'hb: case_word = 32'h0000000b; + 4'hc: case_word = 32'h0000000c; + 4'hd: case_word = 32'h0000000d; + 4'he: case_word = 32'h0000000e; + default: case_word = 32'h0000000f; + endcase + + localparam logic [511:0] TABLE = { + 32'h0000000f, + 32'h0000000e, + 32'h0000000d, + 32'h0000000c, + 32'h0000000b, + 32'h0000000a, + 32'h00000009, + 32'h00000008, + 32'h00000007, + 32'h00000006, + 32'h00000005, + 32'h00000004, + 32'h00000003, + 32'h00000002, + 32'h00000001, + 32'h00000000 + }; + + // V3Premit extracts this matching wide constant after V3Dead recached the + // const-pool contents created by V3Case. + logic [31:0] static_word; + assign static_word = TABLE[{idx, 5'b0} +: 32]; + + always @(posedge clk) begin + `checkh(case_word, static_word); + cyc <= cyc + 32'd1; + if (cyc == 32'd32) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule