Use explicit ctor/dtor functions for VerilatedModules (#6660)

In order to avoid long compile times of the Syms constructor due to
having a very large number of member constructor sto call, move to using
explicit ctor/dtor functions for all but the root VerilatedModule. The
root module needs a constructor as it has non-default-constructible
members. The other modules don't.

This is only part of the fix, as in order to avoid having a default
constructor call the VerilatedModule needs to be default constructible.
I think this is now true for modules that do not contain strings or
other non trivially constructible/destructible variables.

Patch 1 of 3 to fix long compile times of the Syms module in some
scenarios.
This commit is contained in:
Geza Lore 2025-11-07 19:57:10 +00:00 committed by GitHub
parent eafad9742a
commit 2fabf50801
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 104 additions and 86 deletions

View File

@ -3415,18 +3415,6 @@ VerilatedModel::VerilatedModel(VerilatedContext& context)
std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; } std::unique_ptr<VerilatedTraceConfig> VerilatedModel::traceConfig() const { return nullptr; }
//===========================================================================
// VerilatedModule:: Methods
VerilatedModule::VerilatedModule(const char* namep)
: m_namep{strdup(namep)} {}
VerilatedModule::~VerilatedModule() {
// Memory cleanup - not called during normal operation
// cppcheck-suppress cstyleCast // NOLINTNEXTLINE(google-readability-casting)
if (m_namep) VL_DO_CLEAR(free((void*)(m_namep)), m_namep = nullptr);
}
//====================================================================== //======================================================================
// VerilatedVar:: Methods // VerilatedVar:: Methods

View File

@ -308,20 +308,6 @@ private:
virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const; virtual std::unique_ptr<VerilatedTraceConfig> traceConfig() const;
}; };
//=========================================================================
/// Base class for all Verilated module classes.
class VerilatedModule VL_NOT_FINAL {
VL_UNCOPYABLE(VerilatedModule);
private:
const char* m_namep; // Module name
public:
explicit VerilatedModule(const char* namep); // Create module with given hierarchy name
~VerilatedModule();
const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; } ///< Return name of module
};
//========================================================================= //=========================================================================
// Functions overridable by user defines // Functions overridable by user defines
// (Internals however must use VL_PRINTF_MT, which calls these.) // (Internals however must use VL_PRINTF_MT, which calls these.)
@ -720,7 +706,7 @@ private:
int8_t m_timeunit = 0; // Timeunit in negative power-of-10 int8_t m_timeunit = 0; // Timeunit in negative power-of-10
Type m_type = SCOPE_OTHER; // Type of the scope Type m_type = SCOPE_OTHER; // Type of the scope
public: // But internals only - called from VerilatedModule's public: // But internals only - called from verilated modules
VerilatedScope() = default; VerilatedScope() = default;
~VerilatedScope(); ~VerilatedScope();
void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp, void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,

View File

@ -71,7 +71,7 @@ static void makeVlToString(AstIface* nodep) {
funcp->isConst(false); funcp->isConst(false);
funcp->isStatic(false); funcp->isStatic(false);
funcp->protect(false); funcp->protect(false);
AstNodeExpr* const exprp = new AstCExpr{nodep->fileline(), "obj ? obj->name() : \"null\""}; AstNodeExpr* const exprp = new AstCExpr{nodep->fileline(), "obj ? obj->vlNamep : \"null\""};
exprp->dtypeSetString(); exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtsp(funcp); nodep->addStmtsp(funcp);

View File

@ -732,7 +732,7 @@ void EmitCFunc::emitVarResetScopeHash() {
= std::to_string(VString::hashMurmur(m_classOrPackage->name())) + "ULL"; = std::to_string(VString::hashMurmur(m_classOrPackage->name())) + "ULL";
} else { } else {
puts(string("const uint64_t __VscopeHash = VL_MURMUR64_HASH(") puts(string("const uint64_t __VscopeHash = VL_MURMUR64_HASH(")
+ (m_useSelfForThis ? "vlSelf" : "this") + "->name());\n"); + (m_useSelfForThis ? "vlSelf" : "this") + "->vlNamep);\n");
} }
m_createdScopeHash = true; m_createdScopeHash = true;
} }

View File

@ -130,7 +130,8 @@ class EmitCHeader final : public EmitCConstInit {
} }
} else { // not class } else { // not class
putsDecoration(nullptr, "\n// INTERNAL VARIABLES\n"); putsDecoration(nullptr, "\n// INTERNAL VARIABLES\n");
puts(EmitCUtil::symClassName() + "* const vlSymsp;\n"); puts(EmitCUtil::symClassName() + "* vlSymsp;\n");
puts("const char* vlNamep;\n");
} }
} }
void emitParamDecls(const AstNodeModule* modp) { void emitParamDecls(const AstNodeModule* modp) {
@ -155,14 +156,25 @@ class EmitCHeader final : public EmitCConstInit {
} }
} }
void emitCtorDtorDecls(const AstNodeModule* modp) { void emitCtorDtorDecls(const AstNodeModule* modp) {
if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor // Classes use CFuncs with isConstructor/isDestructor
const string& name = EmitCUtil::prefixNameProtect(modp); if (VN_IS(modp, Class)) return;
putsDecoration(nullptr, "\n// CONSTRUCTORS\n");
putns(modp, // The root module needs a proper constuctor/destructor, everything
name + "(" + EmitCUtil::symClassName() + "* symsp, const char* v__name);\n"); // else uses a 'ctor'/'dtor' function in order to be able to split up
// construction/destruction code
const std::string name = EmitCUtil::prefixNameProtect(modp);
putsDecoration(nullptr, "\n// CONSTRUCTORS\n");
const std::string ctorArgs = EmitCUtil::symClassName() + "* symsp, const char* namep";
if (modp->isTop()) {
putns(modp, name + "(" + ctorArgs + ");\n");
putns(modp, "~" + name + "();\n"); putns(modp, "~" + name + "();\n");
putns(modp, "VL_UNCOPYABLE(" + name + ");\n"); } else {
putns(modp, name + "() = default;\n");
putns(modp, "~" + name + "() = default;\n");
putns(modp, "void ctor(" + ctorArgs + ");\n");
putns(modp, "void dtor();\n");
} }
putns(modp, "VL_UNCOPYABLE(" + name + ");\n");
} }
void emitInternalMethodDecls(const AstNodeModule* modp) { void emitInternalMethodDecls(const AstNodeModule* modp) {
bool first = true; bool first = true;
@ -590,7 +602,7 @@ class EmitCHeader final : public EmitCConstInit {
puts("public virtual VlClass"); puts("public virtual VlClass");
} }
} else { } else {
puts(" final : public VerilatedModule"); puts(" final");
} }
puts(" {\n"); puts(" {\n");
ofp()->resetPrivate(); ofp()->resetPrivate();

View File

@ -113,46 +113,61 @@ class EmitCImp final : EmitCFunc {
if (!first) puts("\n"); if (!first) puts("\n");
} }
void emitCtorImp(const AstNodeModule* modp) { void emitCtorImp(const AstNodeModule* modp) {
const string modName = EmitCUtil::prefixNameProtect(modp); const std::string modName = EmitCUtil::prefixNameProtect(modp);
puts("\n"); puts("\n");
m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"),
"(" + modName + "* vlSelf);"); "(" + modName + "* vlSelf);");
puts("\n"); puts("\n");
putns(modp, modName + "::" + modName + "(" + EmitCUtil::symClassName() const std::string ctorArgs = EmitCUtil::symClassName() + "* symsp, const char* namep";
+ "* symsp, const char* v__name)\n");
puts(" : VerilatedModule{v__name}\n");
ofp()->indentInc(); // The root module needs a proper constuctor, everything else uses a
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { // 'ctor' function in order to be able to split up constructors
if (const AstVar* const varp = VN_CAST(nodep, Var)) { if (modp->isTop()) {
if (const AstBasicDType* const dtypep putns(modp, modName + "::" + modName + "(" + ctorArgs + ")\n");
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
if (dtypep->keyword().isMTaskState()) { ofp()->indentInc();
puts(", "); const char* sepp = " : ";
putns(varp, varp->nameProtect()); for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
puts("("); if (const AstVar* const varp = VN_CAST(nodep, Var)) {
iterateConst(varp->valuep()); if (const AstBasicDType* const dtypep
puts(")\n"); = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
} else if (varp->isIO() && varp->isSc()) { if (dtypep->keyword().isMTaskState()) {
puts(", "); puts(sepp);
putns(varp, varp->nameProtect()); putns(varp, varp->nameProtect());
puts("("); puts("(");
putsQuoted(varp->nameProtect()); iterateConst(varp->valuep());
puts(")\n"); puts(")\n");
} else if (dtypep->isDelayScheduler()) { } else if (varp->isIO() && varp->isSc()) {
puts(", "); puts(sepp);
putns(varp, varp->nameProtect()); putns(varp, varp->nameProtect());
puts("{*symsp->_vm_contextp__}\n"); puts("(");
putsQuoted(varp->nameProtect());
puts(")\n");
} else if (dtypep->isDelayScheduler()) {
puts(sepp);
putns(varp, varp->nameProtect());
puts("{*symsp->_vm_contextp__}\n");
} else {
continue;
}
sepp = ", ";
} }
} }
} }
ofp()->indentDec();
puts(" {\n");
} else {
putns(modp, "void " + modName + "::ctor(" + ctorArgs + ") {\n");
} }
puts(", vlSymsp{symsp}\n");
ofp()->indentDec();
puts(" {\n"); puts("vlSymsp = symsp;\n");
if (modp->isTop()) {
puts("vlNamep = strdup(namep);\n");
} else {
puts("vlNamep = strdup(Verilated::catName(vlSymsp->name(), namep));\n");
}
putsDecoration(modp, "// Reset structure values\n"); putsDecoration(modp, "// Reset structure values\n");
puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n"); puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n");
@ -195,13 +210,12 @@ class EmitCImp final : EmitCFunc {
} }
// static doesn't need save-restore as is constant // static doesn't need save-restore as is constant
puts("static uint32_t fake_zero_count = 0;\n"); puts("static uint32_t fake_zero_count = 0;\n");
puts("std::string fullhier = std::string{VerilatedModule::name()} + hierp;\n"); puts("std::string fullhier = std::string{vlNamep} + hierp;\n");
puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n"); puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n");
// Used for second++ instantiation of identical bin // Used for second++ instantiation of identical bin
puts("if (!enable) count32p = &fake_zero_count;\n"); puts("if (!enable) count32p = &fake_zero_count;\n");
puts("*count32p = 0;\n"); puts("*count32p = 0;\n");
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), VerilatedModule::name(), " puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), vlNamep, count32p,");
"count32p,");
puts(" \"filename\",filenamep,"); puts(" \"filename\",filenamep,");
puts(" \"lineno\",lineno,"); puts(" \"lineno\",lineno,");
puts(" \"column\",column,\n"); puts(" \"column\",column,\n");
@ -232,7 +246,7 @@ class EmitCImp final : EmitCFunc {
} }
// static doesn't need save-restore as is constant // static doesn't need save-restore as is constant
puts("static uint32_t fake_zero_count = 0;\n"); puts("static uint32_t fake_zero_count = 0;\n");
puts("std::string fullhier = std::string{VerilatedModule::name()} + hierp;\n"); puts("std::string fullhier = std::string{vlNamep} + hierp;\n");
puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n"); puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n");
puts("std::string commentWithIndex = commentp;\n"); puts("std::string commentWithIndex = commentp;\n");
puts("if (ranged) commentWithIndex += '[' + std::to_string(i) + ']';\n"); puts("if (ranged) commentWithIndex += '[' + std::to_string(i) + ']';\n");
@ -240,8 +254,7 @@ class EmitCImp final : EmitCFunc {
// Used for second++ instantiation of identical bin // Used for second++ instantiation of identical bin
puts("if (!enable) count32p = &fake_zero_count;\n"); puts("if (!enable) count32p = &fake_zero_count;\n");
puts("*count32p = 0;\n"); puts("*count32p = 0;\n");
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), VerilatedModule::name(), " puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), vlNamep, count32p,");
"count32p,");
puts(" \"filename\",filenamep,"); puts(" \"filename\",filenamep,");
puts(" \"lineno\",lineno,"); puts(" \"lineno\",lineno,");
puts(" \"column\",column,\n"); puts(" \"column\",column,\n");
@ -257,9 +270,14 @@ class EmitCImp final : EmitCFunc {
} }
} }
void emitDestructorImp(const AstNodeModule* modp) { void emitDestructorImp(const AstNodeModule* modp) {
const std::string modName = EmitCUtil::prefixNameProtect(modp);
puts("\n"); puts("\n");
putns(modp, EmitCUtil::prefixNameProtect(modp) + "::~" + EmitCUtil::prefixNameProtect(modp) if (modp->isTop()) {
+ "() {\n"); putns(modp, modName + "::~" + modName + "() {\n");
} else {
putns(modp, "void " + modName + "::dtor() {\n");
}
putns(modp, "VL_DO_DANGLING(free(const_cast<char*>(vlNamep)), vlNamep);\n");
emitSystemCSection(modp, VSystemCSectionType::DTOR); emitSystemCSection(modp, VSystemCSectionType::DTOR);
puts("}\n"); puts("}\n");
} }

View File

@ -544,7 +544,7 @@ void EmitCSyms::emitSymHdr() {
} }
puts("\n// METHODS\n"); puts("\n// METHODS\n");
puts("const char* name() { return TOP.name(); }\n"); puts("const char* name() { return TOP.vlNamep; }\n");
if (v3Global.hasEvents()) { if (v3Global.hasEvents()) {
if (v3Global.assignsEvents()) { if (v3Global.assignsEvents()) {
@ -738,6 +738,15 @@ void EmitCSyms::emitSymImp() {
puts("_vm_pgoProfiler.write(\"" + topClassName() puts("_vm_pgoProfiler.write(\"" + topClassName()
+ "\", _vm_contextp__->profVltFilename());\n"); + "\", _vm_contextp__->profVltFilename());\n");
} }
puts("// Tear down sub module instances\n");
for (const ScopeModPair& itpair : vlstd::reverse_view(m_scopes)) {
const AstScope* const scopep = itpair.first;
const AstNodeModule* const modp = itpair.second;
if (modp->isTop()) continue;
putns(scopep, protect(scopep->nameDotless()));
puts(".dtor();\n");
++m_numStmts;
}
puts("}\n"); puts("}\n");
if (v3Global.needTraceDumper()) { if (v3Global.needTraceDumper()) {
@ -788,23 +797,16 @@ void EmitCSyms::emitSymImp() {
puts(" , _vm_pgoProfiler{" + std::to_string(v3Global.currentHierBlockCost()) + "}\n"); puts(" , _vm_pgoProfiler{" + std::to_string(v3Global.currentHierBlockCost()) + "}\n");
} }
puts(" // Setup module instances\n"); puts(" // Setup top module instance\n");
for (const ScopeModPair& itpair : m_scopes) { for (const ScopeModPair& itpair : m_scopes) {
const AstScope* const scopep = itpair.first; const AstScope* const scopep = itpair.first;
const AstNodeModule* const modp = itpair.second; const AstNodeModule* const modp = itpair.second;
if (!modp->isTop()) continue;
puts(" , "); puts(" , ");
putns(scopep, protect(scopep->nameDotless())); putns(scopep, protect(scopep->nameDotless()));
puts("{this"); puts("{this, namep}\n");
if (modp->isTop()) {
puts(", namep");
} else {
// The "." is added by catName
puts(", Verilated::catName(namep, ");
putsQuoted(VIdProtect::protectWordsIf(scopep->prettyName(), scopep->protect()));
puts(")");
}
puts("}\n");
++m_numStmts; ++m_numStmts;
break;
} }
puts("{\n"); puts("{\n");
@ -819,6 +821,18 @@ void EmitCSyms::emitSymImp() {
V3Stats::addStat(V3Stats::STAT_MODEL_SIZE, stackSize + m_statVarScopeBytes); V3Stats::addStat(V3Stats::STAT_MODEL_SIZE, stackSize + m_statVarScopeBytes);
} }
puts("// Setup sub module instances\n");
for (const ScopeModPair& itpair : m_scopes) {
const AstScope* const scopep = itpair.first;
const AstNodeModule* const modp = itpair.second;
if (modp->isTop()) continue;
putns(scopep, protect(scopep->nameDotless()));
puts(".ctor(this, ");
putsQuoted(VIdProtect::protectWordsIf(scopep->prettyName(), scopep->protect()));
puts(");\n");
++m_numStmts;
}
if (v3Global.opt.profPgo()) { if (v3Global.opt.profPgo()) {
puts("// Configure profiling for PGO\n"); puts("// Configure profiling for PGO\n");
if (!v3Global.opt.hierChild()) { if (!v3Global.opt.hierChild()) {

View File

@ -111,7 +111,7 @@ module sub (/*AUTOARG*/
initial begin initial begin
// Test the naming // Test the naming
$c("mon_class_name(this->name());"); $c("mon_class_name(this->vlNamep);");
mon_scope_name("%m"); mon_scope_name("%m");
// Scheme A - pass pointer directly // Scheme A - pass pointer directly
$c("mon_register_a(\"in\", &", in, ", false, 0, 1);"); $c("mon_register_a(\"in\", &", in, ", false, 0, 1);");