From 7e67f738440620bee1c087c39dc7411e8e57e972 Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Tue, 16 Dec 2025 00:09:45 +0100 Subject: [PATCH] Fix process reference giving no return statement error (#6767) (#6823) --- src/V3EmitCFunc.h | 6 ++++++ src/V3SchedTiming.cpp | 14 -------------- src/V3Timing.cpp | 21 +++++++-------------- test_regress/t/t_process_always.py | 18 ++++++++++++++++++ test_regress/t/t_process_always.v | 15 +++++++++++++++ 5 files changed, 46 insertions(+), 28 deletions(-) create mode 100755 test_regress/t/t_process_always.py create mode 100644 test_regress/t/t_process_always.v diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index da2ba68ec..de139ee6b 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -475,6 +475,12 @@ public: m_usevlSelfRef = false; + if (nodep->isCoroutine()) { + // Sometimes coroutines don't have co_awaits, + // so emit a co_return at the end to avoid compile errors. + puts("co_return;"); + } + puts("}\n"); if (nodep->ifdef() != "") puts("#endif // " + nodep->ifdef() + "\n"); } diff --git a/src/V3SchedTiming.cpp b/src/V3SchedTiming.cpp index 581424a5d..19c6b2d38 100644 --- a/src/V3SchedTiming.cpp +++ b/src/V3SchedTiming.cpp @@ -316,7 +316,6 @@ class TransformForksVisitor final : public VNVisitor { // STATE bool m_inClass = false; // Are we in a class? bool m_beginHasAwaits = false; // Does the current begin have awaits? - bool m_awaitMoved = false; // Has the current function lost awaits? AstFork* m_forkp = nullptr; // Current fork AstCFunc* m_funcp = nullptr; // Current function @@ -373,14 +372,7 @@ class TransformForksVisitor final : public VNVisitor { void visit(AstCFunc* nodep) override { VL_RESTORER(m_funcp); m_funcp = nodep; - m_awaitMoved = false; iterateChildren(nodep); - // cppcheck-suppress knownConditionTrueFalse - if (nodep->isCoroutine() && m_awaitMoved - && !nodep->stmtsp()->exists([](AstCAwait*) { return true; })) { - // co_return at the end (either that or a co_await is required in a coroutine - nodep->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;"}); - } } void visit(AstVar* nodep) override { if (!m_forkp) nodep->user1(true); @@ -459,12 +451,6 @@ class TransformForksVisitor final : public VNVisitor { newfuncp->setNeedProcess(); newfuncp->addStmtsp(new AstCStmt{flp, "vlProcess->state(VlProcess::FINISHED);"}); } - if (!m_beginHasAwaits) { - // co_return at the end (either that or a co_await is required in a coroutine - newfuncp->addStmtsp(new AstCStmt{flp, "co_return;"}); - } else { - m_awaitMoved = true; - } remapLocals(newfuncp, callp); } void visit(AstCAwait* nodep) override { diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 6c9f2cb46..885eacb3a 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -859,21 +859,14 @@ class TimingControlVisitor final : public VNVisitor { nodep->addStmtsp(cstmtp); } } - AstNode* firstCoStmtp = nullptr; // First co_* statement in the function - nodep->exists([&](AstCAwait* const awaitp) -> bool { return (firstCoStmtp = awaitp); }); - if (!firstCoStmtp) { - // It's a coroutine but has no awaits (a class method that overrides/is - // overridden by a suspendable, but doesn't have any awaits itself). Add a - // co_return at the end (either that or a co_await is required in a - // coroutine) - firstCoStmtp = new AstCStmt{nodep->fileline(), "co_return;"}; - nodep->addStmtsp(firstCoStmtp); - } if (nodep->dpiExportImpl()) { - // A DPI-exported coroutine won't be able to block the calling code - // Error on the await node; fall back to the function node - firstCoStmtp->v3warn(E_UNSUPPORTED, - "Unsupported: Timing controls inside DPI-exported tasks"); + nodep->exists([](AstCAwait* const awaitp) -> bool { + // A DPI-exported coroutine won't be able to block the calling code + // Error on the await node; fall back to the function node + awaitp->v3warn(E_UNSUPPORTED, + "Unsupported: Timing controls inside DPI-exported tasks"); + return true; + }); } } void visit(AstNodeCCall* nodep) override { diff --git a/test_regress/t/t_process_always.py b/test_regress/t/t_process_always.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_process_always.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_process_always.v b/test_regress/t/t_process_always.v new file mode 100644 index 000000000..f4d49a8f2 --- /dev/null +++ b/test_regress/t/t_process_always.v @@ -0,0 +1,15 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t; + always something(); + + function void something(); + void'(process::self()); + $write("*-* All Finished *-*\n"); + $finish; + endfunction +endmodule