Fix self-disable of named blocks with forks (Part 3 of #7857) (#7996)

This commit is contained in:
Marco Bartoli 2026-07-28 23:31:32 +02:00 committed by GitHub
parent 5fd1c93d43
commit 48e575b885
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 138 additions and 14 deletions

View File

@ -167,6 +167,18 @@ class LinkJumpVisitor final : public VNVisitor {
}
return false;
}
AstBegin* innerForkBranchp(const AstNodeBlock* const targetp) const {
AstBegin* innerForkBranchp = nullptr;
AstNodeBlock* prevBlockp = nullptr;
for (AstNodeBlock* const blockp : vlstd::reverse_view(m_blockStack)) {
if (!innerForkBranchp && VN_IS(blockp, Fork)) {
innerForkBranchp = VN_CAST(prevBlockp, Begin);
}
if (blockp == targetp) return innerForkBranchp;
prevBlockp = blockp;
}
return nullptr;
}
static AstStmtExpr* getQueuePushProcessSelfp(AstVarRef* const queueRefp) {
// Constructs queue.push_back(std::process::self()) statement
FileLine* const flp = queueRefp->fileline();
@ -206,10 +218,6 @@ class LinkJumpVisitor final : public VNVisitor {
}
nodep->addStmtsp(stmtp);
}
static bool directlyUnderFork(const AstNode* const nodep) {
if (nodep->backp()->nextp() == nodep) return directlyUnderFork(nodep->backp());
return VN_IS(nodep->backp(), Fork);
}
AstBegin* getOrCreateTaskDisableBeginp(AstTask* const taskp, FileLine* const fl) {
const auto it = m_taskDisableBegins.find(taskp);
if (it != m_taskDisableBegins.end()) return it->second;
@ -565,16 +573,10 @@ class LinkJumpVisitor final : public VNVisitor {
// process::kill does not terminate the currently running process immediately.
// If disable executes inside a fork branch of this named block, jump to the
// end of that branch to prevent statements after disable from executing.
AstBegin* currentBeginp = nullptr;
for (AstNodeBlock* const blockp : vlstd::reverse_view(m_blockStack)) {
if (VN_IS(blockp, Begin)) {
currentBeginp = VN_AS(blockp, Begin);
break;
}
}
if (currentBeginp && directlyUnderFork(currentBeginp)) {
addJumpAfterKill(killStmtp, currentBeginp);
}
AstBegin* const branchp = innerForkBranchp(beginp);
AstBegin* const jumpTargetp
= branchp ? branchp : m_beginDisableBegins.at(beginp);
addJumpAfterKill(killStmtp, jumpTargetp);
}
} else {
AstVar* const processQueuep

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,58 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// A named begin block that contains a fork still has to behave like a normal
// named block when it disables itself: execution resumes after the block, so
// statements after the disable inside the block must not run. All activity
// enabled within the block, including forked child processes, must terminate.
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
bit after_disable = 1'b0;
bit after_disable_fork = 1'b0;
bit disable_fork_survived = 1'b0;
bit fork_survived = 1'b0;
initial begin : blk
fork
begin
#5;
fork_survived = 1'b1;
end
join_none
#1;
disable blk;
after_disable = 1'b1;
end
// A named fork disabled after join_none has no active enclosing fork branch.
initial begin
fork : fork_blk
begin
#5;
disable_fork_survived = 1'b1;
end
join_none
#1;
disable fork_blk;
after_disable_fork = 1'b1;
end
initial begin
#10;
`checkd(after_disable, 1'b0);
`checkd(after_disable_fork, 1'b1);
`checkd(disable_fork_survived, 1'b0);
`checkd(fork_survived, 1'b0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,5 @@
%Error: t/t_disable_begin_shadow_bad.v:19:7: break isn't underneath a loop
19 | break;
| ^~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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('vlt')
# Exit on the deliberate LinkJump error before later passes inspect the tree.
test.lint(verilator_flags2=["--no-debug-check"],
fails=True,
expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,22 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// The invalid break exits after LinkJump handles the shadowed disable target.
module t;
initial begin : blk
fork
join
end
initial fork : caller
begin : blk
disable t.blk;
break;
end
join
endmodule