diff --git a/Changes b/Changes index a3badf96a..db6a674f5 100644 --- a/Changes +++ b/Changes @@ -26,6 +26,7 @@ Verilator 5.033 devel * Improve VPI write errors (#5712). [Andrew Nolte] * Improve `resetall support (#5728) (#5730). [Ethan Sifferman] * Optimize labels as final `if` block statements (#5744). +* Optimize empty function definition bodies (#5750). * Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer] * Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang] diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index e8b8c425f..91a1ab405 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -625,6 +625,7 @@ class AstCFunc final : public AstNode { bool m_isTrace : 1; // Function is related to tracing bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special bool m_declPrivate : 1; // Declare it private + bool m_keepIfEmpty : 1; // Keep declaration and definition separate, even if empty bool m_slow : 1; // Slow routine, called once or just at init time bool m_funcPublic : 1; // From user public task/function bool m_isConstructor : 1; // Is C class constructor @@ -655,6 +656,7 @@ public: m_isTrace = false; m_dontCombine = false; m_declPrivate = false; + m_keepIfEmpty = false; m_slow = false; m_funcPublic = false; m_isConstructor = false; @@ -706,6 +708,8 @@ public: bool dontInline() const { return dontCombine() || slow() || funcPublic(); } bool declPrivate() const { return m_declPrivate; } void declPrivate(bool flag) { m_declPrivate = flag; } + bool keepIfEmpty() const VL_MT_SAFE { return m_keepIfEmpty; } + void keepIfEmpty(bool flag) { m_keepIfEmpty = flag; } bool slow() const VL_MT_SAFE { return m_slow; } void slow(bool flag) { m_slow = flag; } bool funcPublic() const { return m_funcPublic; } @@ -749,8 +753,7 @@ public: void cost(int cost) { m_cost = cost; } // Special methods bool emptyBody() const { - return argsp() == nullptr && initsp() == nullptr && stmtsp() == nullptr - && finalsp() == nullptr; + return !keepIfEmpty() && !argsp() && !initsp() && !stmtsp() && !finalsp(); } }; class AstCLocalScope final : public AstNode { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index f8b4a6f9b..ab9c98fe0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2865,15 +2865,17 @@ void AstCFile::dumpJson(std::ostream& str) const { void AstCFunc::dump(std::ostream& str) const { this->AstNode::dump(str); if (slow()) str << " [SLOW]"; - if (dpiPure()) str << " [DPIPURE]"; if (isStatic()) str << " [STATIC]"; + if (dpiContext()) str << " [DPICTX]"; if (dpiExportDispatcher()) str << " [DPIED]"; if (dpiExportImpl()) str << " [DPIEI]"; if (dpiImportPrototype()) str << " [DPIIP]"; if (dpiImportWrapper()) str << " [DPIIW]"; - if (dpiContext()) str << " [DPICTX]"; + if (dpiPure()) str << " [DPIPURE]"; if (isConstructor()) str << " [CTOR]"; if (isDestructor()) str << " [DTOR]"; + if (isMethod()) str << " [METHOD]"; + if (isLoose()) str << " [LOOSE]"; if (isVirtual()) str << " [VIRT]"; if (isCoroutine()) str << " [CORO]"; if (needProcess()) str << " [NPRC]"; diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 045628e6a..30b5d5f69 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -62,6 +62,7 @@ class V3CCtorsBuilder final { AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"}; funcp->isStatic(false); funcp->isLoose(!m_type.isClass()); + funcp->keepIfEmpty(true); // TODO relax funcp->declPrivate(true); funcp->slow(!m_type.isClass()); // Only classes construct on fast path string preventUnusedStmt; @@ -210,6 +211,7 @@ void V3CCtors::evalAsserts() { funcp->declPrivate(true); funcp->isStatic(false); funcp->isLoose(true); + funcp->keepIfEmpty(true); funcp->slow(false); funcp->ifdef("VL_DEBUG"); modp->addStmtsp(funcp); diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 547a17884..2cb45c20b 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -171,7 +171,11 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo putns(funcp, "virtual "); } emitCFuncHeader(funcp, modp, /* withScope: */ false); - putns(funcp, ";\n"); + if (funcp->emptyBody() && !funcp->isLoose() && !cLinkage) { + putns(funcp, " {}\n"); + } else { + putns(funcp, ";\n"); + } if (!funcp->ifdef().empty()) putns(funcp, "#endif // " + funcp->ifdef() + "\n"); } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 972970647..00374ae1f 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -280,6 +280,7 @@ public: // VISITORS using EmitCConstInit::visit; void visit(AstCFunc* nodep) override { + if (nodep->emptyBody() && !nodep->isLoose()) return; VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_cfuncp); VL_RESTORER(m_instantiatesOwnProcess) diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 61a7f21d1..76f6cfbdc 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -70,6 +70,7 @@ AstCFunc* makeSubFunction(AstNetlist* netlistp, const string& name, bool slow) { AstCFunc* makeTopFunction(AstNetlist* netlistp, const string& name, bool slow) { AstCFunc* const funcp = makeSubFunction(netlistp, name, slow); funcp->entryPoint(true); + funcp->keepIfEmpty(true); return funcp; } diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index ac4dd1943..a00833c4e 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -185,7 +185,6 @@ -V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:274 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:173 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:247 -V{t#,#} Process forked at t/t_timing_class.v:246 finished @@ -499,7 +498,6 @@ -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:257 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:174 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131 -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip @@ -1038,9 +1036,7 @@ -V{t#,#} Process forked at t/t_timing_class.v:250 finished -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:245 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:175 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_delay --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_1__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76 @@ -1082,8 +1078,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1236,7 +1230,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1308,18 +1301,3 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__nba -V{t#,#}End-of-eval cleanup -V{t#,#}+ Vt_timing_debug2___024root___eval_final --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~