diff --git a/src/V3Param.cpp b/src/V3Param.cpp index eac1fc1f5..eabc0e1cc 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -252,7 +252,8 @@ class ParamProcessor final { // AstNodeModule::user2() // bool True if processed // AstGenFor::user2() // bool True if processed // AstVar::user2() // bool True if constant propagated - // AstCell::user2p() // string* Generate portion of hierarchical name + // AstIfaceRefDType::user2() // bool True if visited + // AstClassRefDType::user2() // bool True if visited // AstNodeModule:user4p() // AstNodeModule* Parametrized copy with default parameters const VNUser2InUse m_inuser2; const VNUser3InUse m_inuser3; @@ -2498,7 +2499,7 @@ class ParamVisitor final : public VNVisitor { bool m_iterateModule = false; // Iterating module body string m_unlinkedTxt; // Text for AstUnlinkedRef std::multimap m_cellps; // Cells left to process (in current module) - std::deque m_strings; // Allocator for temporary strings + std::unordered_map m_genHierNames; // Maps ast nodes to generated hierarchy names std::map m_isCircular; // Stores information whether `AstRefDType` is circular using VarsByName = std::unordered_map; @@ -2565,6 +2566,14 @@ class ParamVisitor final : public VNVisitor { AstNode* const cellp = itim->second; m_cellps.erase(itim); + // Consume the generated hierarchy name for the node + string genHierName; + const auto genHierIt = m_genHierNames.find(cellp); + if (genHierIt != m_genHierNames.end()) { + genHierName = std::move(genHierIt->second); + m_genHierNames.erase(genHierIt); + } + AstNodeModule* srcModp = nullptr; if (const AstCell* modCellp = VN_CAST(cellp, Cell)) { srcModp = modCellp->modp(); @@ -2582,11 +2591,7 @@ class ParamVisitor final : public VNVisitor { if (!srcModp) continue; // Update path - string someInstanceName = modp->someInstanceName(); - if (const string* const genHierNamep = cellp->user2u().to()) { - someInstanceName += *genHierNamep; - cellp->user2p(nullptr); - } + const string someInstanceName = modp->someInstanceName() + genHierName; // Apply parameter specialization if (AstNodeModule* const newModp @@ -2801,8 +2806,7 @@ class ParamVisitor final : public VNVisitor { // A generic visitor for cells and class refs void visitCellOrClassRef(AstNode* nodep, bool isIface) { // Must do ifaces first, so push to list and do in proper order - m_strings.emplace_back(m_generateHierName); - nodep->user2p(&m_strings.back()); + m_genHierNames.emplace(nodep, m_generateHierName); // Deparameterize iface cells early so types are available for lparams. if (isIface && VN_CAST(nodep, Cell) && VN_CAST(nodep, Cell)->paramsp()) { @@ -2882,6 +2886,9 @@ class ParamVisitor final : public VNVisitor { && (VN_IS(nodep->subDTypep()->skipRefOrNullp(), IfaceRefDType) || VN_IS(nodep->subDTypep()->skipRefOrNullp(), ClassRefDType)) && !nodep->skipRefp()->user2SetOnce()) { + // The visit() function for every valid nodep->skipRefp() type below + // must include a `user2SetOnce()` check to avoid adding duplicate + // nodes to m_cellps iterate(nodep->skipRefp()); } iterateChildren(nodep); @@ -2892,9 +2899,11 @@ class ParamVisitor final : public VNVisitor { visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface)); } void visit(AstIfaceRefDType* nodep) override { + nodep->skipRefp()->user2SetOnce(); if (nodep->ifacep()) visitCellOrClassRef(nodep, true); } void visit(AstClassRefDType* nodep) override { + nodep->skipRefp()->user2SetOnce(); checkParamNotHierRecurse(nodep->paramsp()); visitCellOrClassRef(nodep, false); } diff --git a/test_regress/t/t_class_param_typedef9.py b/test_regress/t/t_class_param_typedef9.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_class_param_typedef9.py @@ -0,0 +1,18 @@ +#!/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('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_param_typedef9.v b/test_regress/t/t_class_param_typedef9.v new file mode 100644 index 000000000..402e5fdfe --- /dev/null +++ b/test_regress/t/t_class_param_typedef9.v @@ -0,0 +1,33 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + +// A typedef of a parameterized class produces an AstRefDType whose skipRefp() +// is the AstClassRefDType, which V3Param also reaches directly. This validates +// that the AstClassRefDType is not enqueued to mcell_ps twice +class Wrap #(type T = int); + int m_v; +endclass + +class L0; + typedef Wrap#(L0) w_t; + w_t m_w; + function new(); + m_w = new; + m_w.m_v = 42; + endfunction +endclass + +module t; + L0 l0; + initial begin + l0 = new; + if (l0.m_w.m_v != 42) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule