From 7e2fe64ae2799bea9c6a3edc2ccbe94fa84fb916 Mon Sep 17 00:00:00 2001 From: Mateusz Gancarz Date: Fri, 5 Jun 2026 15:32:52 +0200 Subject: [PATCH] Fix splitting functions containing fork logic (#7717) --- src/V3Timing.cpp | 19 +++++++++++-------- test_regress/t/t_fork_split.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) create mode 100755 test_regress/t/t_fork_split.py diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index e9d00d6ac..612f68a0f 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -711,14 +711,12 @@ class TimingControlVisitor final : public VNVisitor { m_netlistp->typeTablep()->addTypesp(m_forkDtp); return m_forkDtp; } - // Move `insertBeforep` into `AstCLocalScope` if necessary to avoid jumping over - // a variable initialization that whould be inserted before `insertBeforep`. All - // access to this variable should be contained within returned `AstCLocalScope`. - AstCLocalScope* addCLocalScope(FileLine* const flp, AstNode* const insertBeforep) const { - if (!insertBeforep || !m_underJumpBlock) return nullptr; + // Move `nodep` into `AstCLocalScope`. + AstCLocalScope* addCLocalScope(FileLine* const flp, AstNode* const nodep) const { + if (!nodep) return nullptr; VNRelinker handle; - insertBeforep->unlinkFrBack(&handle); - AstCLocalScope* const cscopep = new AstCLocalScope{flp, insertBeforep}; + nodep->unlinkFrBack(&handle); + AstCLocalScope* const cscopep = new AstCLocalScope{flp, nodep}; handle.relink(cscopep); return cscopep; } @@ -781,6 +779,8 @@ class TimingControlVisitor final : public VNVisitor { FileLine* const flp = forkp->fileline(); // Insert the sync var directly before the fork AstNode* const insertBeforep = forkp; + // Make sure all references to the fork var are contained within a single scope to prevent + // moving statements accessing it in case of large functions splitting. addCLocalScope(flp, insertBeforep); AstVarScope* forkVscp = createTemp(flp, forkp->name() + "__sync", getCreateForkSyncDTypep(), insertBeforep); @@ -1206,7 +1206,10 @@ class TimingControlVisitor final : public VNVisitor { controlp = forkp; } UASSERT_OBJ(nodep, controlp, "Assignment should have timing control"); - addCLocalScope(flp, insertBeforep); + // Move `insertBeforep` into `AstCLocalScope` if necessary to avoid jumping over + // a variable initialization that would be inserted before `insertBeforep`. All + // access to this variable should be contained within returned `AstCLocalScope`. + if (m_underJumpBlock) addCLocalScope(flp, insertBeforep); // Function for replacing values with intermediate variables const auto replaceWithIntermediate = [&](AstNodeExpr* const valuep, const std::string& name) { diff --git a/test_regress/t/t_fork_split.py b/test_regress/t/t_fork_split.py new file mode 100755 index 000000000..8e959bee6 --- /dev/null +++ b/test_regress/t/t_fork_split.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Test for splitting fork C logic. +# +# 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-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') +test.top_filename = "t/t_fork.v" + +test.compile(verilator_flags2=["--timing", "--output-split-cfuncs 10"]) + +test.execute() + +test.passes()