diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 2aa30b2f7..b8b989aeb 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -32,63 +32,99 @@ #include "V3CCtors.h" #include -#include +#include + +class VCtorType final { +public: + enum en : uint8_t { MODULE, CLASS, COVERAGE }; -class V3CCtorsVisitor final { private: - string m_basename; - string m_argsp; - string m_callargsp; - AstNodeModule* m_modp; // Current module - AstCFunc* m_tlFuncp; // Top level function being built - AstCFunc* m_funcp; // Current function + enum en m_e; + +public: + // cppcheck-suppress noExplicitConstructor + inline VCtorType(en _e) + : m_e{_e} {} + bool isClass() const { return m_e == CLASS; } + bool isCoverage() const { return m_e == COVERAGE; } +}; + +class V3CCtorsBuilder final { +private: + AstNodeModule* const m_modp; // Current module/class + const string m_basename; + const VCtorType m_type; // What kind of constructor are we creating + std::list m_newFunctions; // Created functions, latest is at back int m_numStmts = 0; // Number of statements output - int m_funcNum = 0; // Function number being built + + AstCFunc* makeNewFunc() { + const int funcNum = m_newFunctions.size(); + const string funcName = m_basename + "_" + cvtToStr(funcNum); + AstCFunc* const funcp = new AstCFunc(m_modp->fileline(), funcName, nullptr, "void"); + funcp->isStatic(!m_type.isClass()); // Class constructors are non static + funcp->declPrivate(true); + funcp->slow(!m_type.isClass()); // Only classes construct on fast path + string preventUnusedStmt; + if (m_type.isClass()) { + funcp->argTypes(EmitCBaseVisitor::symClassVar()); + preventUnusedStmt = "if (false && vlSymsp) {}"; + } else if (m_type.isCoverage()) { + funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self, " + + EmitCBaseVisitor::symClassVar() + ", bool first"); + preventUnusedStmt = "if (false && self && vlSymsp && first) {}"; + } else { // Module + funcp->argTypes(EmitCBaseVisitor::prefixNameProtect(m_modp) + "* self"); + preventUnusedStmt = "if (false && self) {}"; + } + preventUnusedStmt += " // Prevent unused\n"; + funcp->addStmtsp(new AstCStmt(m_modp->fileline(), preventUnusedStmt)); + m_modp->addStmtp(funcp); + m_numStmts = 0; + return funcp; + } public: void add(AstNode* nodep) { - if (v3Global.opt.outputSplitCFuncs() && v3Global.opt.outputSplitCFuncs() < m_numStmts) { - m_funcp = nullptr; + if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) { + m_newFunctions.push_back(makeNewFunc()); } - if (!m_funcp) { - m_funcp = new AstCFunc(m_modp->fileline(), m_basename + "_" + cvtToStr(++m_funcNum), - nullptr, "void"); - m_funcp->isStatic(false); - m_funcp->declPrivate(true); - m_funcp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path - m_funcp->argTypes(m_argsp); - m_modp->addStmtp(m_funcp); - - // Add a top call to it - AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp); - callp->argTypes(m_callargsp); - - m_tlFuncp->addStmtsp(callp); - m_numStmts = 0; - } - m_funcp->addStmtsp(nodep); + m_newFunctions.back()->addStmtsp(nodep); m_numStmts += 1; } - V3CCtorsVisitor(AstNodeModule* nodep, const string& basename, const string& argsp = "", - const string& callargsp = "", const string& stmt = "") { - m_basename = basename; - m_argsp = argsp; - m_callargsp = callargsp; - m_modp = nodep; - m_tlFuncp = new AstCFunc(nodep->fileline(), basename, nullptr, "void"); - m_tlFuncp->declPrivate(true); - m_tlFuncp->isStatic(false); - m_tlFuncp->slow(!VN_IS(m_modp, Class)); // Only classes construct on fast path - m_tlFuncp->argTypes(m_argsp); - if (stmt != "") m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - m_funcp = m_tlFuncp; - m_modp->addStmtp(m_tlFuncp); + V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type) + : m_modp(nodep) + , m_basename{basename} + , m_type(type) { + // Note: The constructor is always called, even if empty, so we must always create at least + // one. + m_newFunctions.push_back(makeNewFunc()); } - ~V3CCtorsVisitor() = default; + + ~V3CCtorsBuilder() { + if (m_newFunctions.size() == 1) { + // No split was necessary, rename the one function to the basename + m_newFunctions.front()->name(m_basename); + } else { + // Split was necessary, create root function and call all others from that + AstCFunc* const rootFuncp = makeNewFunc(); + rootFuncp->name(m_basename); + for (AstCFunc* const funcp : m_newFunctions) { + AstCCall* const callp = new AstCCall(m_modp->fileline(), funcp); + if (m_type.isClass()) { + callp->argTypes("vlSymsp"); + } else if (m_type.isCoverage()) { + callp->argTypes("self, vlSymsp, first"); + } else { // Module + callp->argTypes("self"); + } + rootFuncp->addStmtsp(callp); + } + } + }; private: - VL_UNCOPYABLE(V3CCtorsVisitor); + VL_UNCOPYABLE(V3CCtorsBuilder); }; //###################################################################### @@ -140,32 +176,24 @@ void V3CCtors::cctorsAll() { modp = VN_CAST(modp->nextp(), NodeModule)) { // Process each module in turn { - V3CCtorsVisitor var_reset( - modp, "_ctor_var_reset", - (VN_IS(modp, Class) ? EmitCBaseVisitor::symClassVar() : ""), - (VN_IS(modp, Class) ? "vlSymsp" : ""), - (VN_IS(modp, Class) ? "if (false && vlSymsp) {} // Prevent unused\n" : "")); + V3CCtorsBuilder var_reset(modp, "_ctor_var_reset", + VN_IS(modp, Class) ? VCtorType::CLASS : VCtorType::MODULE); for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { - if (AstVar* varp = VN_CAST(np, Var)) { + if (AstVar* const varp = VN_CAST(np, Var)) { if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) { - var_reset.add( - new AstCReset(varp->fileline(), - new AstVarRef(varp->fileline(), varp, VAccess::WRITE))); + const auto vrefp = new AstVarRef(varp->fileline(), varp, VAccess::WRITE); + var_reset.add(new AstCReset(varp->fileline(), vrefp)); } } } } if (v3Global.opt.coverage()) { - V3CCtorsVisitor configure_coverage( - modp, "_configure_coverage", EmitCBaseVisitor::symClassVar() + ", bool first", - "vlSymsp, first", "if (false && vlSymsp && first) {} // Prevent unused\n"); + V3CCtorsBuilder configure_coverage(modp, "_configure_coverage", VCtorType::COVERAGE); for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { - if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) { - AstNode* backp = coverp->backp(); - coverp->unlinkFrBack(); - configure_coverage.add(coverp); - np = backp; + if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) { + np = coverp->backp(); + configure_coverage.add(coverp->unlinkFrBack()); } } } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index b5c88dadc..d71fdd96b 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -459,7 +459,7 @@ public: iterateChildren(nodep); } virtual void visit(AstCoverDecl* nodep) override { - puts("__vlCoverInsert("); // As Declared in emitCoverageDecl + puts("self->__vlCoverInsert("); // As Declared in emitCoverageDecl puts("&(vlSymsp->__Vcoverage["); puts(cvtToStr(nodep->dataDeclThisp()->binNum())); puts("])"); @@ -1753,7 +1753,9 @@ class EmitCImp final : EmitCStmts { } void emitVarReset(AstVar* varp) { - AstNodeDType* dtypep = varp->dtypep()->skipRefp(); + AstNodeDType* const dtypep = varp->dtypep()->skipRefp(); + const string varNameProtected + = VN_IS(m_modp, Class) ? varp->nameProtect() : "self->" + varp->nameProtect(); if (varp->isIO() && m_modp->isTop() && optSystemC()) { // System C top I/O doesn't need loading, as the lower level subinst code does it.} } else if (varp->isParam()) { @@ -1766,44 +1768,44 @@ class EmitCImp final : EmitCStmts { if (initarp->defaultp()) { puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst())); puts("; ++__Vi) {\n"); - emitSetVarConstant(varp->nameProtect() + "[__Vi]", + emitSetVarConstant(varNameProtected + "[__Vi]", VN_CAST(initarp->defaultp(), Const)); puts("}\n"); } const AstInitArray::KeyItemMap& mapr = initarp->map(); for (const auto& itr : mapr) { AstNode* valuep = itr.second->valuep(); - emitSetVarConstant(varp->nameProtect() + "[" + cvtToStr(itr.first) + "]", + emitSetVarConstant(varNameProtected + "[" + cvtToStr(itr.first) + "]", VN_CAST(valuep, Const)); } } else { varp->v3fatalSrc("InitArray under non-arrayed var"); } } else { - puts(emitVarResetRecurse(varp, dtypep, 0, "")); + puts(emitVarResetRecurse(varp, varNameProtected, dtypep, 0, "")); } } - string emitVarResetRecurse(AstVar* varp, AstNodeDType* dtypep, int depth, - const string& suffix) { + string emitVarResetRecurse(const AstVar* varp, const string& varNameProtected, + AstNodeDType* dtypep, int depth, const string& suffix) { dtypep = dtypep->skipRefp(); AstBasicDType* basicp = dtypep->basicp(); // Returns string to do resetting, empty to do nothing (which caller should handle) if (AstAssocArrayDType* adtypep = VN_CAST(dtypep, AssocArrayDType)) { // Access std::array as C array string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); - return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1, + return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1, suffix + ".atDefault()" + cvtarray); } else if (VN_IS(dtypep, ClassRefDType)) { return ""; // Constructor does it } else if (AstDynArrayDType* adtypep = VN_CAST(dtypep, DynArrayDType)) { // Access std::array as C array string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); - return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1, + return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1, suffix + ".atDefault()" + cvtarray); } else if (AstQueueDType* adtypep = VN_CAST(dtypep, QueueDType)) { // Access std::array as C array string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); - return emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1, + return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1, suffix + ".atDefault()" + cvtarray); } else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) { UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp, @@ -1811,8 +1813,8 @@ class EmitCImp final : EmitCStmts { string ivar = string("__Vi") + cvtToStr(depth); string pre = ("for (int " + ivar + "=" + cvtToStr(0) + "; " + ivar + "<" + cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n"); - string below = emitVarResetRecurse(varp, adtypep->subDTypep(), depth + 1, - suffix + "[" + ivar + "]"); + string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), + depth + 1, suffix + "[" + ivar + "]"); string post = "}\n"; return below.empty() ? "" : pre + below + post; } else if (basicp && basicp->keyword() == AstBasicDTypeKwd::STRING) { @@ -1832,17 +1834,17 @@ class EmitCImp final : EmitCStmts { AstConst* const constp = VN_CAST(varp->valuep(), Const); if (!constp) varp->v3fatalSrc("non-const initializer for variable"); for (int w = 0; w < varp->widthWords(); ++w) { - out += varp->nameProtect() + suffix + "[" + cvtToStr(w) + "] = "; + out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = "; out += cvtToStr(constp->num().edataWord(w)) + "U;\n"; } } else { out += zeroit ? "VL_ZERO_RESET_W(" : "VL_RAND_RESET_W("; out += cvtToStr(dtypep->widthMin()); - out += ", " + varp->nameProtect() + suffix + ");\n"; + out += ", " + varNameProtected + suffix + ");\n"; } return out; } else { - string out = varp->nameProtect() + suffix; + string out = varNameProtected + suffix; // If --x-initial-edge is set, we want to force an initial // edge on uninitialized clocks (from 'X' to whatever the // first value is). Since the class is instantiated before @@ -2481,7 +2483,7 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) { puts("\n"); } putsDecoration("// Reset structure values\n"); - puts(protect("_ctor_var_reset") + "();\n"); + puts(protect("_ctor_var_reset") + "(this);\n"); emitTextSection(AstType::atScCtor); if (modp->isTop() && v3Global.opt.mtasks()) { @@ -2524,7 +2526,9 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) { puts("if (false && first) {} // Prevent unused\n"); puts("this->__VlSymsp = vlSymsp;\n"); // First, as later stuff needs it. puts("if (false && this->__VlSymsp) {} // Prevent unused\n"); - if (v3Global.opt.coverage()) { puts(protect("_configure_coverage") + "(vlSymsp, first);\n"); } + if (v3Global.opt.coverage()) { + puts(protect("_configure_coverage") + "(this, vlSymsp, first);\n"); + } if (modp->isTop() && !v3Global.rootp()->timeunit().isNone()) { puts("vlSymsp->_vm_contextp__->timeunit(" + cvtToStr(v3Global.rootp()->timeunit().powerOfTen()) + ");\n");