From d066504bb927824397813a5cda7814cf3774383f Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sun, 2 Nov 2025 22:11:02 +0100 Subject: [PATCH] Optimize away calls to empty functions (#6626) --- src/V3AstNodeOther.h | 5 ++++- src/V3Const.cpp | 17 ++++++++++++++++- test_regress/t/t_timing_debug2.out | 18 ++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index a335e579a..0ed7cf774 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -598,7 +598,10 @@ public: bool recursive() const { return m_recursive; } void cost(int cost) { m_cost = cost; } // Special methods - bool emptyBody() const { return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp(); } + bool emptyBody() const { + return !keepIfEmpty() && !argsp() && !varsp() && !stmtsp() && !isVirtual() + && !dpiImportPrototype(); + } }; class AstCLocalScope final : public AstNode { // Pack statements into an unnamed scope when generating C++ diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 073997556..a1dd854b9 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3669,10 +3669,25 @@ class ConstVisitor final : public VNVisitor { void visit(AstStmtExpr* nodep) override { iterateChildren(nodep); - if (!nodep->exprp() || VN_IS(nodep->exprp(), Const)) { + // Malformed due to child being deleted, remove here + if (!nodep->exprp()) { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); return; } + // Remove if expression is trivially pure + if (VN_IS(nodep->exprp(), Const) || VN_IS(nodep->exprp(), VarRef)) { + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + // Remove calls to empty functions + if (AstCCall* const callp = VN_CAST(nodep->exprp(), CCall)) { + AstCFunc* const funcp = callp->funcp(); + if (!callp->argsp() && funcp->emptyBody()) { + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + } + // TODO if there's an ExprStmt underneath just keep lower statements // (No current test case needs this) // TODO if non-pure, can remove. First need to clean up that many expressions used diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index 3a07d9ac8..600ec9aa6 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -205,6 +205,7 @@ -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 @@ -568,6 +569,7 @@ -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 @@ -1202,7 +1204,9 @@ -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 @@ -1543,3 +1547,17 @@ -V{t#,#}+ Vt_timing_debug2___024root___trigger_anySet__act -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__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::~