diff --git a/Changes b/Changes index 4bba0e73b..1579f5c30 100644 --- a/Changes +++ b/Changes @@ -36,6 +36,7 @@ Verilator 5.041 devel * Optimize dead functions in more cases (#6380) (#6430). [Artur Bieniek, Antmicro Ltd.] * Optimize constant folding in wide expression expansion (#6381). [Geza Lore] * Fix missing BLKSEQ when connecting module port to array (#2973). +* Fix unused parameterized class causing internal error (#4013). [Alberto Del Rio] * Fix false CONSTVAR error on initializers (#4992). * Fix interface exposure with `--public-depth` or `--trace-depth` (#5758). * Fix cell scoping performance (#6059). [Jerry Tianchen] diff --git a/src/V3Ast.h b/src/V3Ast.h index 3dc0a06d9..c3bb0c255 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2165,6 +2165,9 @@ public: bool brokeExists() const { return V3Broken::isLinkable(this); } bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); } + // Perform a function on every link in a node + virtual void foreachLink(std::function f) = 0; + #ifdef VL_LEAK_CHECKS static void* operator new(size_t size); static void operator delete(void* obj, size_t size); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 7dc98ff91..e61e052dc 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1110,7 +1110,7 @@ class ParamProcessor final { UINFO(8, " Done with " << modInfop->m_modp); newModp = modInfop->m_modp; } - if (defaultsResolved) { srcModp->user4p(newModp); } + if (defaultsResolved) srcModp->user4p(newModp); for (auto* stmtp = newModp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstParamTypeDType* dtypep = VN_CAST(stmtp, ParamTypeDType)) { @@ -1843,12 +1843,15 @@ public: resortNetlistModules(netlistp); // Remove defaulted classes + // Unlike modules, which we keep around and mark dead() for later V3Dead + std::unordered_set removedClassps; for (AstClass* const classp : m_state.m_paramClasses) { if (!classp->user3p()) { // If there was an error, don't remove classes as they might // have remained referenced, and will crash in V3Broken or // other locations. This is fine, we will abort imminently. if (V3Error::errorCount()) continue; + removedClassps.emplace(classp); VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp); } else { // Referenced. classp became a specialized class with the default @@ -1856,6 +1859,18 @@ public: classp->hasGParam(false); } } + // Remove references to defaulted classes + // Reuse user3 to mark all nodes being deleted + AstNode::user3ClearTree(); + for (AstClass* classp : removedClassps) { + classp->foreach([](AstNode* const nodep) { nodep->user3(true); }); + } + // Set all links pointing to a user3 (deleting) node as null + netlistp->foreach([](AstNode* const nodep) { + nodep->foreachLink([](AstNode** const linkpp, const char*) { + if (*linkpp && (*linkpp)->user3()) *linkpp = nullptr; + }); + }); } ~ParamTop() = default; VL_UNCOPYABLE(ParamTop); diff --git a/src/astgen b/src/astgen index b0f14d728..2a85de728 100755 --- a/src/astgen +++ b/src/astgen @@ -1027,6 +1027,16 @@ def write_ast_impl(filename): emitBlock("}}\n") + emitBlock("void Ast{t}::foreachLink(std::function f) {{\n", + t=node.name) + if node.superClass.name != 'Node': + emitBlock(" Ast{base}::foreachLink(f);\n", base=node.superClass.name) + for ptr in node.ptrs: + emitBlock(" f(reinterpret_cast(&m_{name}), \"{name}\");", + name=ptr['name']) + emitBlock("}}\n") + emitBlock("void Ast{t}::dumpJsonGen(std::ostream& str) const {{\n", t=node.name) if node.superClass.name != 'Node': emitBlock(" Ast{base}::dumpJson(str);\n", base=node.superClass.name) @@ -1088,6 +1098,7 @@ def write_ast_macros(filename): Ast{t}* clonep() const {{ return static_cast(AstNode::clonep()); }} Ast{t}* addNext(Ast{t}* nodep) {{ return static_cast(AstNode::addNext(this, nodep)); }} const char* brokenGen() const override; + void foreachLink(std::function f) override; bool wouldBreakGen(const AstNode* const oldp, const AstNode* const newp) const override; void dumpTreeJsonOpGen(std::ostream& str, const string& indent) const override; void dumpJsonGen(std::ostream& str) const; diff --git a/test_regress/t/t_class_param_mailbox.py b/test_regress/t/t_class_param_mailbox.py new file mode 100755 index 000000000..d4f986441 --- /dev/null +++ b/test_regress/t/t_class_param_mailbox.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_param_mailbox.v b/test_regress/t/t_class_param_mailbox.v new file mode 100644 index 000000000..58bd33700 --- /dev/null +++ b/test_regress/t/t_class_param_mailbox.v @@ -0,0 +1,29 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module sub #( + parameter type T = logic +); + mailbox #(T) mbox; +endmodule + +module try #( + parameter I = 1 +); +endmodule + +module t; + typedef struct packed { + logic a; + logic b; + } my_struct_t; + + sub #(my_struct_t) u_sub (); + try #(2) u_try2 (); + + initial $finish; + +endmodule