diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index 9123f125e..a0e7fd495 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -997,6 +997,48 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) { // ###################################################################### +class VCStmtType final { +public: + enum en : uint8_t { + NONE, // Unknown or not applicable + CTOR_VAR_RESET_CALL, + _ENUM_MAX // Leave last + }; + +private: + struct Item final { + enum en m_e; // Statement's enum mnemonic, for checking + const char* m_name; // Statements name, for debugging + }; + static Item s_itemData[]; + +public: + enum en m_e; + VCStmtType() + : m_e{NONE} {} + // cppcheck-suppress noExplicitConstructor + constexpr VCStmtType(en _e) + : m_e{_e} {} + explicit VCStmtType(int _e) + : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning + constexpr operator en() const { return m_e; } + const char* ascii() const VL_PURE { + static const char* const names[] = {"none", "ctor_var_reset_call"}; + return names[m_e]; + } + bool isNone() const { return m_e == NONE; } +}; +constexpr bool operator==(const VCStmtType& lhs, const VCStmtType& rhs) { + return lhs.m_e == rhs.m_e; +} +constexpr bool operator==(const VCStmtType& lhs, VCStmtType::en rhs) { return lhs.m_e == rhs; } +constexpr bool operator==(VCStmtType::en lhs, const VCStmtType& rhs) { return lhs == rhs.m_e; } +inline std::ostream& operator<<(std::ostream& os, const VCStmtType& rhs) { + return os << rhs.ascii(); +} + +// ###################################################################### + class VCaseType final { public: enum en : uint8_t { diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index ba0d51cf3..27881e00a 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -280,6 +280,7 @@ class AstNodeModule VL_NOT_FINAL : public AstNode { bool m_modPublic : 1; // Module has public references bool m_modTrace : 1; // Tracing this module bool m_inLibrary : 1; // From a library, no error if not used, never top level + bool m_ctorVarReset : 1; // Ctor needs to call ctor_var_reset bool m_dead : 1; // LinkDot believes is dead; will remove in Dead visitors bool m_hasGParam : 1; // Has global parameter (for link) bool m_hasParameterList : 1; // Has #() for parameter declaration @@ -301,6 +302,7 @@ protected: , m_modPublic{false} , m_modTrace{false} , m_inLibrary{false} + , m_ctorVarReset{false} , m_dead{false} , m_hasGParam{false} , m_hasParameterList{false} @@ -336,6 +338,8 @@ public: void modPublic(bool flag) { m_modPublic = flag; } bool modTrace() const { return m_modTrace; } void modTrace(bool flag) { m_modTrace = flag; } + bool ctorVarReset() const { return m_ctorVarReset; } + void ctorVarReset(bool flag) { m_ctorVarReset = flag; } bool dead() const { return m_dead; } void dead(bool flag) { m_dead = flag; } bool hasGParam() const { return m_hasGParam; } diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 7c3a013e8..76f32b64a 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -311,6 +311,7 @@ public: class AstCStmt final : public AstNodeStmt { // C statement emitted into output, with some arbitrary nodes interspersed // @astgen op1 := nodesp : List[AstNode] + const VCStmtType m_stmtType; // Special statement (instead of comparing name()) static AstCStmt* profExecSection(FileLine* flp, const std::string& section, bool push) { // Compute the label @@ -337,8 +338,10 @@ class AstCStmt final : public AstNodeStmt { } public: - explicit AstCStmt(FileLine* fl, const std::string& text = "") - : ASTGEN_SUPER_CStmt(fl) { + explicit AstCStmt(FileLine* fl, const std::string& text = "", + const VCStmtType& stmtType = VCStmtType::NONE) + : ASTGEN_SUPER_CStmt(fl) + , m_stmtType{stmtType} { if (!text.empty()) add(text); } ASTGEN_MEMBERS_AstCStmt; @@ -347,6 +350,9 @@ public: bool isPredictOptimizable() const override { return false; } bool isPure() override { return false; } bool sameNode(const AstNode*) const override { return true; } + void dump(std::ostream& str) const override; + void dumpJson(std::ostream& str) const override; + VCStmtType stmtType() const { return m_stmtType; } // Add some text, or a node to this statement void add(const std::string& text) { addNodesp(new AstText{fileline(), text}); } void add(AstNode* nodep) { addNodesp(nodep); } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c5cce1b69..95e18091e 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2633,6 +2633,7 @@ void AstNodeModule::dump(std::ostream& str) const { str << " D" << depth(); if (modPublic()) str << " [P]"; if (inLibrary()) str << " [LIB]"; + if (ctorVarReset()) str << " [CVRESET]"; if (dead()) str << " [DEAD]"; if (recursiveClone()) { str << " [RECURSIVE-CLONE]"; @@ -2650,6 +2651,7 @@ void AstNodeModule::dumpJson(std::ostream& str) const { dumpJsonNumFunc(str, level); dumpJsonBoolFuncIf(str, modPublic); dumpJsonBoolFuncIf(str, inLibrary); + dumpJsonBoolFuncIf(str, ctorVarReset); dumpJsonBoolFuncIf(str, dead); dumpJsonBoolFuncIf(str, recursiveClone); dumpJsonBoolFuncIf(str, recursive); @@ -3398,6 +3400,14 @@ void AstCMethodHard::setPurity() { } } +void AstCStmt::dump(std::ostream& str) const { + this->AstNodeStmt::dump(str); + if (!stmtType().isNone()) str << " [" << stmtType().ascii() << "]"; +} +void AstCStmt::dumpJson(std::ostream& str) const { + dumpJsonGen(str); + if (!stmtType().isNone()) dumpJsonStr(str, "stmtType", stmtType().ascii()); +} void AstCUse::dump(std::ostream& str) const { this->AstNode::dump(str); str << " [" << useType() << "]"; diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 68bfb493d..27e36a933 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -65,6 +65,7 @@ class V3CCtorsBuilder final { funcp->keepIfEmpty(true); // TODO relax funcp->declPrivate(true); funcp->slow(!m_type.isClass()); // Only classes construct on fast path + if (!m_type.isCoverage()) m_modp->ctorVarReset(true); string preventUnusedStmt; if (m_type.isClass()) { funcp->argTypes(EmitCUtil::symClassVar()); @@ -83,6 +84,7 @@ class V3CCtorsBuilder final { public: void add(AstNode* nodep) { + if (m_newFunctions.empty()) m_newFunctions.push_back(makeNewFunc()); if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) { m_newFunctions.push_back(makeNewFunc()); } @@ -94,13 +96,13 @@ public: : 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()); + // Expect coverage function to always exist, so must always create at least one. + if (m_type.isCoverage()) m_newFunctions.push_back(makeNewFunc()); } ~V3CCtorsBuilder() { - if (m_newFunctions.size() == 1) { + if (m_newFunctions.size() == 0) { + } else if (m_newFunctions.size() == 1) { // No split was necessary, rename the one function to the basename m_newFunctions.front()->name(m_basename); } else { @@ -136,6 +138,7 @@ class CCtorsVisitor final : public VNVisitor { AstNodeModule* m_modp = nullptr; // Current module AstCFunc* m_cfuncp = nullptr; // Current function V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset + std::map m_ctorCalls; // Calls to _ctor_var_reset // METHODS static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VSystemCSectionType type) { @@ -194,6 +197,13 @@ class CCtorsVisitor final : public VNVisitor { iterateChildren(nodep); if (nodep->name() == "new") insertSc(nodep, m_modp, VSystemCSectionType::CTOR); } + void visit(AstCStmt* nodep) override { + if (nodep->stmtType() == VCStmtType::CTOR_VAR_RESET_CALL) { + UASSERT_OBJ(m_modp, nodep, "ctor_var_reset call not under module"); + m_ctorCalls.emplace(nodep, m_modp); + } + iterateChildren(nodep); + } void visit(AstVar* nodep) override { if (nodep->needsCReset()) { AstNode* const crstp = new AstAssign{ @@ -215,7 +225,14 @@ class CCtorsVisitor final : public VNVisitor { public: // CONSTRUCTORS explicit CCtorsVisitor(AstNode* nodep) { iterate(nodep); } - ~CCtorsVisitor() override = default; + ~CCtorsVisitor() override { + // Remove CStmts to ctor_var_resets that are no longer needed + for (auto& itr : m_ctorCalls) { + AstCStmt* const nodep = itr.first; + const AstNodeModule* const modp = itr.second; + if (!modp->ctorVarReset()) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + } + } }; //###################################################################### diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index e093fc04e..0649fb888 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -92,8 +92,9 @@ class EmitCImp final : public EmitCFunc { const std::string modName = EmitCUtil::prefixNameProtect(modp); puts("\n"); - m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), - "(" + modName + "* vlSelf);"); + if (modp->ctorVarReset()) + m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"), + "(" + modName + "* vlSelf);"); puts("\n"); const std::string ctorArgs = EmitCUtil::symClassName() + "* symsp, const char* namep"; @@ -148,7 +149,7 @@ class EmitCImp final : public EmitCFunc { } putsDecoration(modp, "// Reset structure values\n"); - puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n"); + if (modp->ctorVarReset()) puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n"); emitSystemCSection(modp, VSystemCSectionType::CTOR); puts("}\n"); diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index cb580a212..a102115e8 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -285,7 +285,7 @@ private: if (m_logp) *m_logp << "Buckets assigned to Work Lists:\n"; int availableBuckets = v3Global.opt.outputGroups(); for (WorkList* listp : m_concatenableListsByDescSize) { - if (availableBuckets > 0) { + if (availableBuckets > 0 && idealBucketScore > 0) { listp->m_bucketsNum = std::min( availableBuckets, std::max(1, listp->m_totalScore / idealBucketScore)); availableBuckets -= listp->m_bucketsNum; diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 9eafa680d..f92d11c5b 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1345,7 +1345,8 @@ class TaskVisitor final : public VNVisitor { } if (!isInterfaceClass) { const string stmt = VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);"; - cfuncp->addStmtsp(new AstCStmt{nodep->fileline(), stmt}); + cfuncp->addStmtsp( + new AstCStmt{nodep->fileline(), stmt, VCStmtType::CTOR_VAR_RESET_CALL}); } } } diff --git a/test_regress/t/t_flag_output_groups.py b/test_regress/t/t_flag_output_groups.py index 1c6ccfaf8..11316ac58 100755 --- a/test_regress/t/t_flag_output_groups.py +++ b/test_regress/t/t_flag_output_groups.py @@ -16,10 +16,7 @@ test.compile(verilator_flags2=["--output-groups", "2"]) test.execute() # Check that only vm_classes_*.cpp are to be compiled -test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "Foo") -test.file_grep(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_Slow_1") test.file_grep(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_1") -test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_Slow_2") test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2") test.passes() diff --git a/test_regress/t/t_flag_output_groups.v b/test_regress/t/t_flag_output_groups.v index 05cf7a420..a70cf7b6d 100644 --- a/test_regress/t/t_flag_output_groups.v +++ b/test_regress/t/t_flag_output_groups.v @@ -4,6 +4,14 @@ // SPDX-FileCopyrightText: 2024 Antmicro Ltd // SPDX-License-Identifier: CC0-1.0 +// verilog_format: off +`ifdef verilator + `define no_optimize(v) $c(v) +`else + `define no_optimize(v) (v) +`endif +// verilog_format: on + virtual class Base; pure virtual function int get_param; endclass @@ -11,7 +19,7 @@ class Foo #( int N = 17 ) extends Base; function int get_param; - return N; + return `no_optimize(N); endfunction endclass diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index ebcf2aac4..c84f538fa 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -1,55 +1,22 @@ -V{t#,#}- Verilated::debug is on. Message prefix indicates {,}. -V{t#,#}+ Vt_timing_debug2___024root___ctor_var_reset --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2___024unit___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay__Vclpkg___ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass__Vclpkg___ctor_var_reset -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions -V{t#,#}+ Initial -V{t#,#}+ Vt_timing_debug2___024root___eval_static -V{t#,#}+ Vt_timing_debug2_t___eval_static__TOP__t --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::new --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::_ctor_var_reset --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::new --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::_ctor_var_reset --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::new --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::_ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::_ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::_ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::_ctor_var_reset --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::new -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2___024root___timing_ready @@ -68,7 +35,6 @@ -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::__VnoInFunc_do_delay -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__7 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::_ctor_var_reset -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::__VnoInFunc_do_fork -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::__VnoInFunc_do_fork____Vfork_1__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::__VnoInFunc_do_fork____Vfork_1__1 @@ -395,8 +361,6 @@ -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:123 -V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::__VnoInFunc_wake -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:261 --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::new --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::_ctor_var_reset -V{t#,#} Process forked at t/t_timing_class.v:260 finished -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:135 -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip