Fix long C++ compilation due to VerilatedScope constructors (#6664)

The Syms class can contain a very large number of VeriltedScope
instances if `--vpi` is used, all of which need a call to the default
constructor in the constructor of the Syms class. This can lead to very
long compilation times, even without optimization on some compilers.

To avoid the constructor calls, hold VeriltedScope via pointers in the
Syms class, and explicitly new and delete them in the Syms
constructor/destructor. These explicit new/delte can then be
automatically split up into sub functions when the Syms
constructor/destructor become large.

Regarding run-time performance, this should have no significant effect,
most interactions are either during construction/destruction of the Syms
object, or are via pointers already. The one place where we used to
refer to VerilatedScope instances is when emitting an AstScopeName for
things like $display %m. For those there will be an extra load
instruction at run-time, which should not make a big difference.

Patch 3 of 3 to fix long compile times of the Syms module in some
scenarios.
This commit is contained in:
Geza Lore
2025-11-08 15:56:15 +00:00
committed by GitHub
parent ec3c9832de
commit 8ad8d4f807
6 changed files with 52 additions and 47 deletions
+20 -21
View File
@@ -3454,37 +3454,36 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const
//======================================================================
// VerilatedScope:: Methods
VerilatedScope::~VerilatedScope() {
// Memory cleanup - not called during normal operation
Verilated::threadContextp()->impp()->scopeErase(this);
if (m_namep) VL_DO_CLEAR(delete[] m_namep, m_namep = nullptr);
if (m_callbacksp) VL_DO_CLEAR(delete[] m_callbacksp, m_callbacksp = nullptr);
if (m_varsp) VL_DO_CLEAR(delete m_varsp, m_varsp = nullptr);
m_funcnumMax = 0; // Force callback table to empty
}
void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
VerilatedScope::VerilatedScope(VerilatedSyms* symsp, const char* prefixp, const char* suffixp,
const char* identifier, const char* defnamep, int8_t timeunit,
const Type& type) VL_MT_UNSAFE {
// Slowpath - called once/scope at construction
// We don't want the space and reference-count access overhead of strings.
m_symsp = symsp;
m_type = type;
m_timeunit = timeunit;
{
Type type)
: m_symsp{symsp}
, m_namep{[prefixp, suffixp]() {
// We don't want the space and reference-count access overhead of strings.
char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
char* dp = namep;
for (const char* sp = prefixp; *sp;) *dp++ = *sp++;
if (*prefixp && *suffixp) *dp++ = '.';
for (const char* sp = suffixp; *sp;) *dp++ = *sp++;
*dp++ = '\0';
m_namep = namep;
}
m_identifierp = identifier;
m_defnamep = defnamep;
return namep;
}()}
, m_identifierp{identifier}
, m_defnamep{defnamep}
, m_timeunit{timeunit}
, m_type{type} {
Verilated::threadContextp()->impp()->scopeInsert(this);
}
VerilatedScope::~VerilatedScope() {
// Memory cleanup - not called during normal operation
Verilated::threadContextp()->impp()->scopeErase(this);
VL_DO_DANGLING(delete[] m_namep, m_namep);
VL_DO_DANGLING(delete[] m_callbacksp, m_callbacksp);
VL_DO_DANGLING(delete m_varsp, m_varsp);
VL_DEBUG_IFDEF(m_funcnumMax = 0;);
}
void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE {
// Slowpath - called once/scope*export at construction
// Insert a exported function into scope table