diff --git a/src/V3Ast.h b/src/V3Ast.h index 65054e378..ed1bc52f0 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -578,6 +578,7 @@ public: static constexpr int INSTR_COUNT_STR = 100; // String ops static constexpr int INSTR_COUNT_TIME = INSTR_COUNT_CALL + 5; // Determine simulation time static constexpr int INSTR_COUNT_PLI = 20; // PLI routines + static constexpr int INSTR_COUNT_SYM = 5; // Syms ctor/dtor statements // ACCESSORS virtual string name() const VL_MT_STABLE { return ""; } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index cff85daaf..e2d90c12b 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -101,24 +101,36 @@ class EmitCSyms final : EmitCBaseVisitorConst { std::map> m_vpiScopeHierarchy; int m_coverBins = 0; // Coverage bin number const bool m_dpiHdrOnly; // Only emit the DPI header - int m_numStmts = 0; // Number of statements output - size_t m_funcNum = 0; // CFunc split function number - V3OutCFile* m_ofpBase = nullptr; // Base (not split) C file - AstCFile* m_ofpBaseFile = nullptr; // Base (not split) AstCFile - std::vector> m_usesVfinal; // Split file index + uses __Vfinal + std::vector m_splitFuncNames; // Split file names VDouble0 m_statVarScopeBytes; // Statistic tracking const std::string m_symsFileBase = v3Global.opt.makeDir() + "/" + symClassName(); // METHODS + void openOutputFile(const std::string fileName) { + V3OutCFile* const fp = optSystemC() ? new V3OutScFile{fileName} : new V3OutCFile{fileName}; + AstCFile* const cfilep = newCFile(fileName, true /*slow*/, true /*source*/); + cfilep->support(true); + setOutputFile(fp, cfilep); + } + void emitSymHdr(); - void checkSplit(bool usesVfinal); - void closeSplit(); void emitSymImpPreamble(); - void emitScopeHier(bool destroy); - void emitSymImp(); + void emitScopeHier(std::vector& stmts, bool destroy); + void emitSymImp(AstNetlist* netlistp); void emitDpiHdr(); void emitDpiImp(); + void emitSplit(std::vector& stmts, const std::string name, size_t max_stmts); + + std::vector getSymCtorStmts(); + std::vector getSymDtorStmts(); + + static size_t stmtCost(const std::string& stmt) { + if (stmt.empty()) return 0; + if (VString::startsWith(stmt, "/")) return 0; + return static_cast(AstNode::INSTR_COUNT_SYM); + } + static void nameCheck(AstNode* nodep) { // Prevent GCC compile time error; name check all things that reach C++ code if (nodep->name().empty()) return; @@ -293,7 +305,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { // Output if (!m_dpiHdrOnly) { // Must emit implementation first to determine number of splits - emitSymImp(); + emitSymImp(nodep); emitSymHdr(); } if (v3Global.dpi()) { @@ -537,11 +549,7 @@ void EmitCSyms::emitSymHdr() { + "* modelp);\n"); puts("~" + symClassName() + "();\n"); - for (const auto& i : m_usesVfinal) { - puts("void " + symClassName() + "_" + std::to_string(i.first) + "("); - if (i.second) puts("int __Vfinal"); - puts(");\n"); - } + for (const std::string& funcName : m_splitFuncNames) { puts("void " + funcName + "();\n"); } puts("\n// METHODS\n"); puts("const char* name() { return TOP.vlNamep; }\n"); @@ -587,54 +595,6 @@ void EmitCSyms::emitSymHdr() { closeOutputFile(); } -void EmitCSyms::closeSplit() { - UASSERT(ofp(), "There should be an output file"); - // If we are in the base file, nothign to do - if (ofp() == m_ofpBase) return; - - // Close sub-function definition in split file and close the file - puts("}\n"); - closeOutputFile(); - - // Resume output to the base file - setOutputFile(m_ofpBase, m_ofpBaseFile); -} - -void EmitCSyms::checkSplit(bool usesVfinal) { - UASSERT(ofp(), "There should be an output file"); - if (!v3Global.opt.outputSplitCFuncs()) return; - if (m_numStmts < v3Global.opt.outputSplitCFuncs()) return; - - // Splitting file, so using parallel build. - v3Global.useParallelBuild(true); - - // If alredy in a split file, close it - if (ofp() != m_ofpBase) closeSplit(); - - // New function umber - const size_t funcNum = ++m_funcNum; - - // Call the sub-function in the base file - puts(symClassName() + "_" + std::to_string(funcNum) + "("); - if (usesVfinal) puts("__Vfinal"); - puts(");\n"); - - // Create new split file - m_usesVfinal.emplace_back(funcNum, usesVfinal); - const std::string filename = m_symsFileBase + "__" + std::to_string(funcNum) + ".cpp"; - AstCFile* const cfilep = newCFile(filename, true /*slow*/, true /*source*/); - cfilep->support(true); - V3OutCFile* const ofilep = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; - setOutputFile(ofilep, cfilep); - m_numStmts = 0; - - // Emit header and open sub function definition in the split file - emitSymImpPreamble(); - puts("void " + symClassName() + "::" + symClassName() + "_" + std::to_string(funcNum) + "("); - if (usesVfinal) puts("int __Vfinal"); - puts(") {\n"); -} - void EmitCSyms::emitSymImpPreamble() { ofp()->putsHeader(); puts("// DESCR" @@ -661,12 +621,16 @@ void EmitCSyms::emitSymImpPreamble() { if (needsNewLine) puts("\n"); } -void EmitCSyms::emitScopeHier(bool destroy) { +void EmitCSyms::emitScopeHier(std::vector& stmts, bool destroy) { if (!v3Global.opt.vpi()) return; - const std::string verb = destroy ? "Tear down" : "Set up"; + if (destroy) { + stmts.emplace_back("// Tear down scope hierarchy"); + } else { + stmts.emplace_back("// Set up scope hierarchy"); + } + const std::string method = destroy ? "remove" : "add"; - puts("\n// " + verb + " scope hierarchy\n"); for (const auto& itpair : m_scopeNames) { if (itpair.first == "TOP") continue; const ScopeData& sd = itpair.second; @@ -674,280 +638,152 @@ void EmitCSyms::emitScopeHier(bool destroy) { const std::string& scopeType = sd.m_type; if (name.find('.') != string::npos) continue; if (scopeType != "SCOPE_MODULE" && scopeType != "SCOPE_PACKAGE") continue; - putns(sd.m_nodep, - "__Vhier." + method + "(0, &" + protect("__Vscope_" + sd.m_symName) + ");\n"); + const std::string id = protect("__Vscope_" + sd.m_symName); + stmts.emplace_back("__Vhier." + method + "(0, &" + id + ");"); } - for (const auto& itpair : m_vpiScopeHierarchy) { - const std::string fromname = scopeSymString(itpair.first); - const ScopeData& from = m_scopeNames.at(fromname); + const std::string fromName = scopeSymString(itpair.first); + const std::string fromId = protect("__Vscope_" + m_scopeNames.at(fromName).m_symName); for (const std::string& name : itpair.second) { - const std::string toname = scopeSymString(name); - const ScopeData& to = m_scopeNames.at(toname); - puts("__Vhier." + method + "("); - puts("&" + protect("__Vscope_" + from.m_symName) + ", "); - puts("&" + protect("__Vscope_" + to.m_symName) + ");\n"); + const std::string toName = scopeSymString(name); + const std::string toId = protect("__Vscope_" + m_scopeNames.at(toName).m_symName); + stmts.emplace_back("__Vhier." + method + "(&" + fromId + ", &" + toId + ");"); } } - puts("\n"); } -void EmitCSyms::emitSymImp() { - UINFO(6, __FUNCTION__ << ": "); - const std::string filename = m_symsFileBase + ".cpp"; - m_ofpBaseFile = newCFile(filename, true /*slow*/, true /*source*/); - m_ofpBaseFile->support(true); - m_ofpBase = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename}; - setOutputFile(m_ofpBase, m_ofpBaseFile); +std::vector EmitCSyms::getSymCtorStmts() { + std::vector stmts; - emitSymImpPreamble(); - - if (v3Global.opt.savable()) { - for (const bool& de : {false, true}) { - const std::string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; - const std::string funcname = protect(de ? "__Vdeserialize" : "__Vserialize"); - const std::string op = de ? ">>" : "<<"; - // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - puts("void " + symClassName() + "::" + funcname + "(" + classname + "& os) {\n"); - puts("// Internal state\n"); - if (v3Global.opt.trace()) puts("os" + op + "__Vm_activity;\n"); - puts("os " + op + " __Vm_didInit;\n"); - puts("// Module instance state\n"); - for (const ScopeModPair& itpair : m_scopes) { - const AstScope* const scopep = itpair.first; - puts(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." - + funcname + "(os);\n"); - } - puts("}\n"); - } - puts("\n"); - } - - puts("// FUNCTIONS\n"); - - // Destructor - puts(symClassName() + "::~" + symClassName() + "()\n"); - puts("{\n"); - emitScopeHier(true); - if (v3Global.needTraceDumper()) { - puts("#ifdef VM_TRACE\n"); - puts("if (__Vm_dumping) _traceDumpClose();\n"); - puts("#endif // VM_TRACE\n"); - } - if (v3Global.opt.profPgo()) { - puts("_vm_pgoProfiler.write(\"" + topClassName() - + "\", _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"); - - if (v3Global.needTraceDumper()) { - if (!optSystemC()) { - puts("\nvoid " + symClassName() + "::_traceDump() {\n"); - // Caller checked for __Vm_dumperp non-nullptr - puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); - puts("__Vm_dumperp->dump(VL_TIME_Q());\n"); - puts("}\n"); - } - - puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n"); - puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); - puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n"); - puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n"); - puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n"); - puts("const std::string dumpfile = _vm_contextp__->dumpfileCheck();\n"); - puts("__Vm_dumperp->open(dumpfile.c_str());\n"); - puts("__Vm_dumping = true;\n"); - puts("}\n"); - puts("}\n"); - - puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n"); - puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); - puts("__Vm_dumping = false;\n"); - puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n"); - puts("}\n"); - } - puts("\n"); - - // Constructor - puts(symClassName() + "::" + symClassName() - + "(VerilatedContext* contextp, const char* namep, " + topClassName() + "* modelp)\n"); - puts(" : VerilatedSyms{contextp}\n"); - puts(" // Setup internal state of the Syms class\n"); - puts(" , __Vm_modelp{modelp}\n"); - - if (v3Global.opt.mtasks()) { - puts(" , __Vm_threadPoolp{static_cast(contextp->threadPoolp())}\n"); - } - - if (v3Global.opt.profExec()) { - puts(" , " - "__Vm_executionProfilerp{static_cast(contextp->" - "enableExecutionProfiler(&VlExecutionProfiler::construct))}\n"); - } - if (v3Global.opt.profPgo() && !v3Global.opt.libCreate().empty()) { - puts(" , _vm_pgoProfiler{" + std::to_string(v3Global.currentHierBlockCost()) + "}\n"); - } - - puts(" // Setup top module instance\n"); - for (const ScopeModPair& itpair : m_scopes) { - const AstScope* const scopep = itpair.first; - const AstNodeModule* const modp = itpair.second; - if (!modp->isTop()) continue; - puts(" , "); - putns(scopep, protect(scopep->nameDotless())); - puts("{this, namep}\n"); - ++m_numStmts; - break; - } - puts("{\n"); + const auto add = [&stmts](const std::string& stmt) { stmts.emplace_back(stmt); }; { - puts("// Check resources\n"); uint64_t stackSize = V3StackCount::count(v3Global.rootp()); if (v3Global.opt.debugStackCheck()) stackSize += 1024 * 1024 * 1024; V3Stats::addStat("Size prediction, Stack (bytes)", stackSize); - puts("Verilated::stackCheck(" + std::to_string(stackSize) + ");\n"); // TODO: 'm_statVarScopeBytes' is always 0, AstVarScope doesn't reach here (V3Descope) V3Stats::addStat("Size prediction, Heap, from Var Scopes (bytes)", m_statVarScopeBytes); V3Stats::addStat(V3Stats::STAT_MODEL_SIZE, stackSize + m_statVarScopeBytes); + + add("// Check resources"); + add("Verilated::stackCheck(" + std::to_string(stackSize) + ");"); } - puts("// Setup sub module instances\n"); + add("// Setup sub module instances"); 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; + const std::string name = V3OutFormatter::quoteNameControls( + VIdProtect::protectWordsIf(scopep->prettyName(), scopep->protect())); + add(protect(scopep->nameDotless()) + ".ctor(this, \"" + name + "\");"); } if (v3Global.opt.profPgo()) { - puts("// Configure profiling for PGO\n"); + add("// Configure profiling for PGO\n"); if (!v3Global.opt.hierChild()) { - puts("_vm_pgoProfiler.writeHeader(_vm_contextp__->profVltFilename());\n"); + add("_vm_pgoProfiler.writeHeader(_vm_contextp__->profVltFilename());"); } if (v3Global.opt.mtasks()) { v3Global.rootp()->topModulep()->foreach([&](const AstExecGraph* execGraphp) { for (const V3GraphVertex& vtx : execGraphp->depGraphp()->vertices()) { const ExecMTask& mt = static_cast(vtx); - puts("_vm_pgoProfiler.addCounter(" + std::to_string(mt.id()) + ", \"" - + mt.hashName() + "\");\n"); + add("_vm_pgoProfiler.addCounter(" + std::to_string(mt.id()) + ", \"" + + mt.hashName() + "\");"); } }); } } - puts("// Configure time unit / time precision\n"); + add("// Configure time unit / time precision"); if (!v3Global.rootp()->timeunit().isNone()) { - puts("_vm_contextp__->timeunit("); - puts(std::to_string(v3Global.rootp()->timeunit().powerOfTen())); - puts(");\n"); + const std::string unit = std::to_string(v3Global.rootp()->timeunit().powerOfTen()); + add("_vm_contextp__->timeunit(" + unit + ");"); } if (!v3Global.rootp()->timeprecision().isNone()) { - puts("_vm_contextp__->timeprecision("); - puts(std::to_string(v3Global.rootp()->timeprecision().powerOfTen())); - puts(");\n"); + const std::string prec = std::to_string(v3Global.rootp()->timeprecision().powerOfTen()); + add("_vm_contextp__->timeprecision(" + prec + ");"); } - puts("// Setup each module's pointers to their submodules\n"); + add("// Setup each module's pointers to their submodules"); for (const auto& i : m_scopes) { const AstScope* const scopep = i.first; const AstNodeModule* const modp = i.second; const AstScope* const abovep = scopep->aboveScopep(); if (!abovep) continue; - checkSplit(false); const std::string protName = VIdProtect::protectWordsIf(scopep->name(), scopep->protect()); + std::string stmt; if (VN_IS(modp, ClassPackage)) { // ClassPackage modules seem to be a bit out of place, so hard code... - putns(scopep, "TOP"); + stmt += "TOP"; } else { - putns(scopep, VIdProtect::protectIf(abovep->nameDotless(), abovep->protect())); + stmt += VIdProtect::protectIf(abovep->nameDotless(), abovep->protect()); } - puts("."); - puts(protName.substr(protName.rfind('.') + 1)); - puts(" = &"); - puts(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + ";\n"); - ++m_numStmts; + stmt += "."; + stmt += protName.substr(protName.rfind('.') + 1); + stmt += " = &"; + stmt += VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + ";"; + add(stmt); } - puts("// Setup each module's pointer back to symbol table (for public functions)\n"); + add("// Setup each module's pointer back to symbol table (for public functions)"); for (const ScopeModPair& i : m_scopes) { const AstScope* const scopep = i.first; AstNodeModule* const modp = i.second; - checkSplit(false); // first is used by AstCoverDecl's call to __vlCoverInsert const bool first = !modp->user1(); modp->user1(true); - putns(scopep, VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." - + protect("__Vconfigure") + "(" + (first ? "true" : "false") + ");\n"); - ++m_numStmts; + add(VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + + protect("__Vconfigure") + "(" + (first ? "true" : "false") + ");"); } - if (!m_scopeNames.empty()) { // Setup scope names - puts("// Setup scopes\n"); - for (const auto& itpair : m_scopeNames) { - checkSplit(false); - const ScopeData& sd = itpair.second; - putns(sd.m_nodep, protect("__Vscope_" + sd.m_symName) + ".configure(this, name(), "); - putsQuoted(VIdProtect::protectWordsIf(sd.m_prettyName, true)); - puts(", "); - putsQuoted(protect(scopeDecodeIdentifier(sd.m_prettyName))); - puts(", "); - putsQuoted(sd.m_defName); - puts(", "); - puts(std::to_string(sd.m_timeunit)); - puts(", VerilatedScope::" + sd.m_type + ");\n"); - ++m_numStmts; - } + add("// Setup scopes"); + for (const auto& itpair : m_scopeNames) { + const ScopeData& sd = itpair.second; + std::string stmt; + stmt += protect("__Vscope_" + sd.m_symName) + ".configure(this, name(), \""; + stmt += V3OutFormatter::quoteNameControls( + VIdProtect::protectWordsIf(sd.m_prettyName, true)); + stmt += "\", \""; + stmt += V3OutFormatter::quoteNameControls(protect(scopeDecodeIdentifier(sd.m_prettyName))); + stmt += "\", \""; + stmt += V3OutFormatter::quoteNameControls(sd.m_defName); + stmt += "\", "; + stmt += std::to_string(sd.m_timeunit); + stmt += ", VerilatedScope::" + sd.m_type + ");"; + add(stmt); } - emitScopeHier(false); + emitScopeHier(stmts, false); - // Everything past here is in the __Vfinal loop, so start a new split file if needed - closeSplit(); + if (!v3Global.dpi()) return stmts; - if (v3Global.dpi()) { - - puts("// Setup export functions\n"); - puts("for (int __Vfinal = 0; __Vfinal < 2; ++__Vfinal) {\n"); + for (const std::string vfinal : {"0", "1"}) { + add("// Setup export functions - final: " + vfinal); for (const auto& itpair : m_scopeFuncs) { const ScopeFuncData& sfd = itpair.second; const AstScopeName* const scopep = sfd.m_scopep; const AstCFunc* const funcp = sfd.m_cfuncp; const AstNodeModule* const modp = sfd.m_modp; - if (!funcp->dpiExportImpl()) continue; - checkSplit(true); - putns(scopep, - protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert(__Vfinal, "); - putsQuoted(funcp->cname()); // Not protected - user asked for import/export - puts(", (void*)(&"); - puts(EmitCUtil::prefixNameProtect(modp)); - puts("__"); - puts(funcp->nameProtect()); - puts("));\n"); - ++m_numStmts; + std::string stmt; + stmt += protect("__Vscope_" + scopep->scopeSymName()) + ".exportInsert("; + stmt += vfinal + ", \""; + // Not protected - user asked for import/export + stmt += V3OutFormatter::quoteNameControls(funcp->cname()); + stmt += "\", (void*)(&"; + stmt += EmitCUtil::prefixNameProtect(modp); + stmt += "__"; + stmt += funcp->nameProtect(); + stmt += "));"; + add(stmt); } // It would be less code if each module inserted its own variables. // Someday. For now public isn't common. for (const auto& itpair : m_scopeVars) { - checkSplit(true); const ScopeVarData& svd = itpair.second; - const AstScope* const scopep = svd.m_scopep; const AstVar* const varp = svd.m_varp; int pdim = 0; @@ -981,52 +817,238 @@ void EmitCSyms::emitSymImp() { } } - putns(scopep, protect("__Vscope_" + svd.m_scopeName)); - putns(varp, ".varInsert(__Vfinal,"); - putsQuoted(protect(svd.m_varBasePretty)); + std::string stmt; + stmt += protect("__Vscope_" + svd.m_scopeName) + ".varInsert("; + stmt += vfinal + ", \""; + stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"'; const std::string varName = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + protect(varp->name()); - if (varp->isParam()) { - if (varp->vlEnumType() == "VLVT_STRING" - && !VN_IS(varp->subDTypep(), UnpackArrayDType)) { - puts(", const_cast(static_cast("); - puts(varName); - puts(".c_str())), "); - } else { - puts(", const_cast(static_cast(&("); - puts(varName); - puts("))), "); - } + if (!varp->isParam()) { + stmt += ", &("; + stmt += varName; + stmt += "), false, "; + } else if (varp->vlEnumType() == "VLVT_STRING" + && !VN_IS(varp->subDTypep(), UnpackArrayDType)) { + stmt += ", const_cast(static_cast("; + stmt += varName; + stmt += ".c_str())), true, "; } else { - puts(", &("); - puts(varName); - puts("), "); + stmt += ", const_cast(static_cast(&("; + stmt += varName; + stmt += "))), true, "; } - puts(varp->isParam() ? "true" : "false"); - puts(", "); - puts(varp->vlEnumType()); // VLVT_UINT32 etc - puts(","); - puts(varp->vlEnumDir()); // VLVD_IN etc - puts(","); - puts(std::to_string(udim)); - puts(","); - puts(std::to_string(pdim)); - puts(bounds); - puts(");\n"); - ++m_numStmts; + stmt += varp->vlEnumType(); // VLVT_UINT32 etc + stmt += ", "; + stmt += varp->vlEnumDir(); // VLVD_IN etc + stmt += ", "; + stmt += std::to_string(udim); + stmt += ", "; + stmt += std::to_string(pdim); + stmt += bounds; + stmt += ");"; + add(stmt); + } + } + + return stmts; +} + +std::vector EmitCSyms::getSymDtorStmts() { + std::vector stmts; + + const auto add = [&stmts](const std::string& stmt) { stmts.emplace_back(stmt); }; + + emitScopeHier(stmts, true); + if (v3Global.needTraceDumper()) add("if (__Vm_dumping) _traceDumpClose();"); + if (v3Global.opt.profPgo()) { + add("_vm_pgoProfiler.write(\"" + topClassName() + + "\", _vm_contextp__->profVltFilename());"); + } + add("// Tear down sub module instances"); + 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; + add(protect(scopep->nameDotless()) + ".dtor();"); + } + return stmts; +} + +void EmitCSyms::emitSplit(std::vector& stmts, const std::string name, + size_t maxCost) { + const std::string baseName = m_symsFileBase + "__" + name; + size_t nSubFunctions = 0; + // Reduce into a balanced tree of sub-function calls until we end up with a single statement + while (stmts.size() > 1) { + size_t nSplits = 0; + size_t nStmts = stmts.size(); + for (size_t splitStart = 0, splitEnd = 0; splitStart < nStmts; splitStart = splitEnd) { + // Gather up at at most 'maxCost' worth of statements in this split, + // but always at least 2 (if less than 2, the reduction makes no + // progress and the loop will not terminate). + size_t cost = 0; + while (((cost < maxCost) || (splitEnd - splitStart < 2)) && splitEnd < nStmts) { + cost += stmtCost(stmts[splitEnd++]); + } + UASSERT(splitStart < splitEnd, "Empty split"); + + // Create new sub-function and emit current range of statementss + const std::string nStr = std::to_string(nSubFunctions++); + // Name of sub-function we are emitting now + const std::string funcName = symClassName() + "__" + name + "__" + nStr; + m_splitFuncNames.emplace_back(funcName); + // Open split file + openOutputFile(baseName + "__" + nStr + "__Slow.cpp"); + // Emit header + emitSymImpPreamble(); + // Open sub-function definition in the split file + puts("void " + symClassName() + "::" + funcName + "() {\n"); + + // Emit statements + for (size_t j = splitStart; j < splitEnd; ++j) { + m_ofp->putsNoTracking(" "); + m_ofp->putsNoTracking(stmts[j]); + m_ofp->putsNoTracking("\n"); + } + + // Close sub-function + puts("}\n"); + // Close split file + closeOutputFile(); + + // Replace statements with a call to the sub-function + stmts[nSplits++] = funcName + "();"; + } + // The statements at the front are now the calls to the sub-functions, drop the rest + stmts.resize(nSplits); + } +} + +void EmitCSyms::emitSymImp(AstNetlist* netlistp) { + UINFO(6, __FUNCTION__ << ": "); + + // Get the body of the constructor and destructor + std::vector ctorStmts = getSymCtorStmts(); + std::vector dtorStmts = getSymDtorStmts(); + + // Check if needs splitting and if so split into sub-functions + if (const size_t maxCost = static_cast(v3Global.opt.outputSplitCFuncs())) { + size_t totalCost = 200; // Starting from 200 to consider all other contents in main file + if (totalCost <= maxCost) { + for (const std::string& stmt : ctorStmts) { + totalCost += stmtCost(stmt); + if (totalCost > maxCost) break; + } + } + if (totalCost <= maxCost) { + for (const std::string& stmt : dtorStmts) { + totalCost += stmtCost(stmt); + if (totalCost > maxCost) break; + } + } + // Split them if needed + if (totalCost > maxCost) { + v3Global.useParallelBuild(true); // Splitting files, so using parallel build. + emitSplit(ctorStmts, "ctor", maxCost); + emitSplit(dtorStmts, "dtor", maxCost); + } + } + + openOutputFile(m_symsFileBase + "__Slow.cpp"); + emitSymImpPreamble(); + + // Constructor + const std::string ctorArgs + = "VerilatedContext* contextp, const char* namep, " + topClassName() + "* modelp"; + puts(symClassName() + "::" + symClassName() + "(" + ctorArgs + ")\n"); + puts(" : VerilatedSyms{contextp}\n"); + puts(" // Setup internal state of the Syms class\n"); + puts(" , __Vm_modelp{modelp}\n"); + if (v3Global.opt.mtasks()) { + puts(" , __Vm_threadPoolp{static_cast(contextp->threadPoolp())}\n"); + } + if (v3Global.opt.profExec()) { + puts(" , __Vm_executionProfilerp{static_cast(contextp->" + "enableExecutionProfiler(&VlExecutionProfiler::construct))}\n"); + } + if (v3Global.opt.profPgo() && !v3Global.opt.libCreate().empty()) { + puts(" , _vm_pgoProfiler{" + std::to_string(v3Global.currentHierBlockCost()) + "}\n"); + } + { + const AstScope* const scopep = netlistp->topScopep()->scopep(); + puts(" // Setup top module instance\n"); + puts(" , " + protect(scopep->nameDotless()) + "{this, namep}\n"); + } + puts("{\n"); + for (const std::string& stmt : ctorStmts) { + m_ofp->putsNoTracking(" "); + m_ofp->putsNoTracking(stmt); + m_ofp->putsNoTracking("\n"); + } + puts("}\n"); + + // Destructor + puts("\n" + symClassName() + "::~" + symClassName() + "() {\n"); + for (const std::string& stmt : dtorStmts) { + m_ofp->putsNoTracking(" "); + m_ofp->putsNoTracking(stmt); + m_ofp->putsNoTracking("\n"); + } + puts("}\n"); + + // Methods + if (v3Global.needTraceDumper()) { + if (!optSystemC()) { + puts("\nvoid " + symClassName() + "::_traceDump() {\n"); + puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); + // Caller checked for __Vm_dumperp non-nullptr + puts("__Vm_dumperp->dump(VL_TIME_Q());\n"); + puts("}\n"); } - closeSplit(); + puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n"); + puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); + puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n"); + puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n"); + puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n"); + puts("const std::string dumpfile = _vm_contextp__->dumpfileCheck();\n"); + puts("__Vm_dumperp->open(dumpfile.c_str());\n"); + puts("__Vm_dumping = true;\n"); + puts("}\n"); + puts("}\n"); + + puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n"); + puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n"); + puts("__Vm_dumping = false;\n"); + puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n"); puts("}\n"); } - puts("}\n"); + if (v3Global.opt.savable()) { + for (const bool& de : {false, true}) { + const std::string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + const std::string funcname = protect(de ? "__Vdeserialize" : "__Vserialize"); + const std::string op = de ? ">>" : "<<"; + puts("\nvoid " + symClassName() + "::" + funcname + "(" + classname + "& os) {\n"); + puts("// Internal state\n"); + if (v3Global.opt.trace()) puts("os" + op + "__Vm_activity;\n"); + puts("os " + op + " __Vm_didInit;\n"); + puts("// Module instance state\n"); + for (const ScopeModPair& itpair : m_scopes) { + const AstScope* const scopep = itpair.first; + const std::string scopeName + = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()); + puts(scopeName + "." + funcname + "(os);\n"); + } + puts("}\n"); + } + } - VL_DO_CLEAR(delete m_ofpBase, m_ofpBase = nullptr); + closeOutputFile(); } //###################################################################### diff --git a/test_regress/t/t_flag_csplit.py b/test_regress/t/t_flag_csplit.py index a63e3ac2a..11a0ae93c 100755 --- a/test_regress/t/t_flag_csplit.py +++ b/test_regress/t/t_flag_csplit.py @@ -16,14 +16,14 @@ def check_splits(): got1 = False gotSyms1 = False for filename in test.glob_some(test.obj_dir + "/*.cpp"): - if re.search(r'Syms__1', filename): + if re.search(r'Syms__.*__1', filename): gotSyms1 = True elif re.search(r'__1', filename): got1 = True if not got1: test.error("No __1 split file found") if not gotSyms1: - test.error("No Syms__1 split file found") + test.error("No Syms__*__1 split file found") def check_no_all_file(): diff --git a/test_regress/t/t_flag_csplit_groups.py b/test_regress/t/t_flag_csplit_groups.py index 0bfb1eb83..33ba50c14 100755 --- a/test_regress/t/t_flag_csplit_groups.py +++ b/test_regress/t/t_flag_csplit_groups.py @@ -18,14 +18,14 @@ def check_splits(): got1 = False gotSyms1 = False for filename in test.glob_some(test.obj_dir + "/*.cpp"): - if re.search(r'Syms__1', filename): + if re.search(r'Syms__.*__1', filename): gotSyms1 = True elif re.search(r'__1', filename): got1 = True if not got1: test.error("No __1 split file found") if not gotSyms1: - test.error("No Syms__1 split file found") + test.error("No Syms__*__1 split file found") def check_no_all_file(): @@ -125,7 +125,7 @@ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_clas test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2") # Check combine count -test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (239 if test.vltmt else 222)) +test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (270 if test.vltmt else 253)) test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2) test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2) diff --git a/test_regress/t/t_json_only_debugcheck.out b/test_regress/t/t_json_only_debugcheck.out index e73ebe86d..98c4306c8 100644 --- a/test_regress/t/t_json_only_debugcheck.out +++ b/test_regress/t/t_json_only_debugcheck.out @@ -2923,7 +2923,7 @@ ]} ], "filesp": [ - {"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck__Syms.cpp","addr":"(ZQB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []}, + {"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck__Syms__Slow.cpp","addr":"(ZQB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []}, {"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck__Syms.h","addr":"(ARB)","loc":"a,0:0,0:0","source":false,"slow":true,"tblockp": []}, {"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck.h","addr":"(BRB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []}, {"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck.cpp","addr":"(CRB)","loc":"a,0:0,0:0","source":true,"slow":false,"tblockp": []}, diff --git a/test_regress/t/t_timing_split.py b/test_regress/t/t_timing_split.py index 54cfda002..15b97428a 100755 --- a/test_regress/t/t_timing_split.py +++ b/test_regress/t/t_timing_split.py @@ -16,14 +16,14 @@ def check_splits(): got1 = False gotSyms1 = False for filename in test.glob_some(test.obj_dir + "/*.cpp"): - if re.search(r'Syms__1', filename): + if re.search(r'Syms__.*__1', filename): gotSyms1 = True elif re.search(r'__1', filename): got1 = True if not got1: test.error("No __1 split file found") if not gotSyms1: - test.error("No Syms__1 split file found") + test.error("No Syms__*__1 split file found") test.compile(timing_loop=True, diff --git a/test_regress/t/t_xml_debugcheck.out b/test_regress/t/t_xml_debugcheck.out index 3d90fa4b7..9e5469979 100644 --- a/test_regress/t/t_xml_debugcheck.out +++ b/test_regress/t/t_xml_debugcheck.out @@ -1782,7 +1782,7 @@ - +