diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index 117d56dd5..791b38a3d 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -608,7 +608,7 @@ class ForkVisitor final : public VNVisitor { } if (insertBeforep == firstStmtp->nextp()) return; - AstNode* const delayp = firstStmtp->unlinkFrBackWithNext(); + AstNode* const delayp = firstStmtp->unlinkFrBack(); if (insertBeforep) { insertBeforep->addHereThisAsNext(delayp); } else { diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 743398d46..a4875b78b 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -742,9 +742,19 @@ class TimingControlVisitor final : public VNVisitor { addDebugInfo(donep); beginp->addStmtsp(donep->makeStmt()); } + static bool hasDisableQueuePushSelfPrefix(const AstBegin* const beginp) { + // LinkJump prepends disable-by-name registration as: + // __VprocessQueue_*.push_back(std::process::self()) + const AstStmtExpr* const stmtExprp = VN_CAST(beginp->stmtsp(), StmtExpr); + if (!stmtExprp) return false; + const AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard); + if (!methodp || methodp->name() != "push_back") return false; + const AstVarRef* const queueRefp = VN_CAST(methodp->fromp(), VarRef); + return queueRefp && queueRefp->name().rfind("__VprocessQueue_", 0) == 0; + } // Register a callback so killing a process-backed fork branch decrements the join counter void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const { - if (!beginp->needProcess()) return; + if (!beginp->needProcess() && !hasDisableQueuePushSelfPrefix(beginp)) return; FileLine* const flp = beginp->fileline(); AstCMethodHard* const onKillp = new AstCMethodHard{ flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, VCMethod::FORK_ON_KILL}; diff --git a/test_regress/t/t_disable_task_join.v b/test_regress/t/t_disable_task_join.v index db8763baa..711d8ac5d 100644 --- a/test_regress/t/t_disable_task_join.v +++ b/test_regress/t/t_disable_task_join.v @@ -6,6 +6,17 @@ int x = 0; int y = 0; +int z = 0; +int w = 0; +int jf = 0; +int ddj = 0; +int ddja = 0; +int ddjn = 0; +int dj_done = 0; +int dja_done = 0; +int djn_done = 0; +int race_disable = 0; +int nd = 0; task increment_x; x++; @@ -73,6 +84,168 @@ module t; #3; if (y != 1) $stop; + // Additional regression: named-block disable with join + fork + begin : worker_join + z++; + #2; + z++; + end + #1 disable worker_join; + join + if (z != 1) $stop; + + // Additional regression: named-block disable with join_any + fork + begin : worker_join_any + w++; + #2; + w++; + end + #1 disable worker_join_any; + join_any + #3; + if (w != 1) $stop; + + // disable fork from inside a join_any branch + fork + begin + fork + begin + #1; + jf = 1; + end + begin + #5; + jf = 99; + end + join_none + #2; + disable fork; + end + begin + #3; + end + join_any + #6; + if (jf != 1) $stop; + + // multiple sequential disables of the same target under join + fork + begin : twice_join + #5; + ddj = 99; + end + begin + #1 disable twice_join; + #1 disable twice_join; + end + join + if (ddj != 0) $stop; + + // multiple sequential disables of the same target under join_any + fork + begin : twice_join_any + #5; + ddja = 99; + end + begin + #1 disable twice_join_any; + #1 disable twice_join_any; + end + join_any + #6; + if (ddja != 0) $stop; + + // multiple sequential disables of the same target under join_none + begin + fork + begin : twice_join_none + #5; + ddjn = 99; + end + join_none + #1 disable twice_join_none; + #1 disable twice_join_none; + #6; + end + if (ddjn != 0) $stop; + + // disable after target is already finished (join) + fork + begin : done_join + #1; + dj_done = 1; + end + join + disable done_join; + if (dj_done != 1) $stop; + + // disable after target is already finished (join_any) + fork + begin : done_join_any + #1; + dja_done = 1; + end + #2; + join_any + #2; + disable done_join_any; + if (dja_done != 1) $stop; + + // disable after target is already finished (join_none) + begin + fork + begin : done_join_none + #1; + djn_done = 1; + end + join_none + #2; + disable done_join_none; + #1; + end + if (djn_done != 1) $stop; + + // competing disables in the same time slot targeting the same block + fork + begin : race_target + #5; + race_disable = 99; + end + #1 disable race_target; + #1 disable race_target; + join + if (race_disable != 0) $stop; + + // nested descendants are disabled and outer join resumes + begin : nested_disable + fork + begin + fork + begin + #1; + nd += 1; + end + begin + #3; + nd += 10; + end + join_none + #5; + nd += 100; + end + begin + #2; + disable nested_disable; + end + join_none + #6; + nd += 1000; + end + #8; + if (nd != 1) $stop; + $write("*-* All Finished *-*\n"); $finish; end