From 19cdb7a1f44f88a25f1b71da2320b8ecb912897c Mon Sep 17 00:00:00 2001 From: username Date: Fri, 10 Jul 2026 22:20:49 +0200 Subject: [PATCH] Refactor disable process queue plumbing --- src/V3Ast.h | 1 + src/V3AstNodes.cpp | 11 ++++++ src/V3Fork.cpp | 12 +------ src/V3LinkJump.cpp | 87 +++++++++++++++++++++++++--------------------- src/V3Timing.cpp | 7 +--- 5 files changed, 61 insertions(+), 57 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 2963987f2..2fd3ef754 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -730,6 +730,7 @@ public: // ACCESSORS for specific types // Alas these can't be virtual or they break when passed a nullptr + bool isDisableQueuePushSelfStmt() const; inline bool isClassHandleValue() const; inline bool isNull() const; inline bool isZero() const; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 26126e5fd..9b94d4e89 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -108,6 +108,17 @@ int AstNodeSel::bitConst() const { return (constp ? constp->toSInt() : 0); } +bool AstNode::isDisableQueuePushSelfStmt() const { + // Detect LinkJump-generated registration: + // __VprocessQueue_*.push_back(std::process::self()) + const AstStmtExpr* const stmtExprp = VN_CAST(this, 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->varp()->processQueue(); +} + void AstNodeStmt::dump(std::ostream& str) const { this->AstNode::dump(str); } void AstNodeStmt::dumpJson(std::ostream& str) const { dumpJsonGen(str); } diff --git a/src/V3Fork.cpp b/src/V3Fork.cpp index ee02c0781..025c13bdb 100644 --- a/src/V3Fork.cpp +++ b/src/V3Fork.cpp @@ -593,22 +593,12 @@ class ForkVisitor final : public VNVisitor { const AstConst* const constp = VN_CAST(delayp->lhsp(), Const); return constp && (constp->toUQuad() == std::numeric_limits::max()); } - static bool isDisableQueuePushSelfStmt(const AstNode* const nodep) { - // Detect LinkJump-generated registration: - // __VprocessQueue_*.push_back(std::process::self()) - const AstStmtExpr* const stmtExprp = VN_CAST(nodep, 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->varp()->processQueue(); - } static void moveForkSentinelAfterDisableQueuePushes(AstBegin* const beginp) { AstNode* const firstStmtp = beginp->stmtsp(); if (!isForkJoinNoneSentinelDelay(firstStmtp)) return; AstNode* insertBeforep = firstStmtp->nextp(); - while (insertBeforep && isDisableQueuePushSelfStmt(insertBeforep)) { + while (insertBeforep && insertBeforep->isDisableQueuePushSelfStmt()) { insertBeforep = insertBeforep->nextp(); } if (insertBeforep == firstStmtp->nextp()) return; diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 70115033f..848d05929 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -175,15 +175,17 @@ class LinkJumpVisitor final : public VNVisitor { new AstMethodCall{flp, queueRefp, "push_back", new AstArg{flp, "", v3Global.rootp()->stdPackageProcessSelfp(flp)}}}; } - static AstStmtExpr* getQueuePushProcessSelfp(FileLine* const fl, AstVar* const processQueuep) { + static AstVarRef* newQueueRefp(FileLine* const fl, AstVar* const processQueuep, + const VAccess& access) { AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); - AstVarRef* const queueWriteRefp - = new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE}; + return new AstVarRef{fl, topPkgp, processQueuep, access}; + } + static AstStmtExpr* getQueuePushProcessSelfp(FileLine* const fl, AstVar* const processQueuep) { + AstVarRef* const queueWriteRefp = newQueueRefp(fl, processQueuep, VAccess::WRITE); return getQueuePushProcessSelfp(queueWriteRefp); } static AstStmtExpr* getQueueKillStmtp(FileLine* const fl, AstVar* const processQueuep) { - AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); - AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE}; + AstVarRef* const queueRefp = newQueueRefp(fl, processQueuep, VAccess::READWRITE); AstTaskRef* killQueueCall = nullptr; for (AstNode* itemp = v3Global.rootp()->stdPackageProcessp()->stmtsp(); itemp; itemp = itemp->nextp()) { @@ -197,14 +199,7 @@ class LinkJumpVisitor final : public VNVisitor { killQueueCall->classOrPackagep(v3Global.rootp()->stdPackageProcessp()); return new AstStmtExpr{fl, killQueueCall}; } - static void prependStmtsp(AstNodeFTask* const nodep, AstNode* const stmtp) { - if (AstNode* const origStmtsp = nodep->stmtsp()) { - origStmtsp->unlinkFrBackWithNext(); - stmtp->addNext(origStmtsp); - } - nodep->addStmtsp(stmtp); - } - static void prependStmtsp(AstNodeBlock* const nodep, AstNode* const stmtp) { + static void prependStmtsp(AstBegin* const nodep, AstNode* const stmtp) { if (AstNode* const origStmtsp = nodep->stmtsp()) { origStmtsp->unlinkFrBackWithNext(); stmtp->addNext(origStmtsp); @@ -232,17 +227,21 @@ class LinkJumpVisitor final : public VNVisitor { m_taskDisableBegins.emplace(taskp, taskBodyp); return taskBodyp; } - AstVar* getProcessQueuep(AstNode* const nodep, FileLine* const fl) { - AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstVar* newProcessQueuep(AstNode* const nodep, FileLine* const fl, const VVarType& varType) { AstVar* const processQueuep = new AstVar{ - fl, VVarType::VAR, m_queueNames.get(nodep->name()), VFlagChildDType{}, + fl, varType, m_queueNames.get(nodep->name()), VFlagChildDType{}, new AstQueueDType{ fl, VFlagChildDType{}, new AstClassRefDType{fl, v3Global.rootp()->stdPackageProcessp(), nullptr}, nullptr}}; - processQueuep->lifetime(VLifetime::STATIC_EXPLICIT); processQueuep->processQueue(true); processQueuep->setIgnoreSchedWrite(); + return processQueuep; + } + AstVar* getProcessQueuep(AstNode* const nodep, FileLine* const fl) { + AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp(); + AstVar* const processQueuep = newProcessQueuep(nodep, fl, VVarType::VAR); + processQueuep->lifetime(VLifetime::STATIC_EXPLICIT); topPkgp->addStmtsp(processQueuep); return processQueuep; } @@ -274,6 +273,20 @@ class LinkJumpVisitor final : public VNVisitor { m_beginDisableBegins.emplace(beginp, beginBodyp); return beginBodyp; } + void prependForkBranchQueuePushes(AstFork* const forkp, AstVar* const processQueuep, + FileLine* const fl, bool needProcess) { + for (AstBegin* branchp = forkp->forksp(); branchp; + branchp = VN_AS(branchp->nextp(), Begin)) { + if (needProcess) branchp->setNeedProcess(); + prependStmtsp(branchp, getQueuePushProcessSelfp(fl, processQueuep)); + } + } + void prependNestedForkBranchQueuePushes(AstNode* const nodep, AstVar* const processQueuep, + FileLine* const fl, bool needProcess) { + nodep->foreach([&](AstFork* const forkp) { + prependForkBranchQueuePushes(forkp, processQueuep, fl, needProcess); + }); + } AstVar* getOrCreateBeginDisableQueuep(AstBegin* const beginp, FileLine* const fl) { const auto it = m_beginDisableQueues.find(beginp); if (it != m_beginDisableQueues.end()) return it->second; @@ -285,17 +298,19 @@ class LinkJumpVisitor final : public VNVisitor { // Named-block disable must also terminate detached descendants created by forks // under the block, so track each fork branch process in the same queue. - beginBodyp->foreach([&](AstFork* const forkp) { - for (AstBegin* branchp = forkp->forksp(); branchp; - branchp = VN_AS(branchp->nextp(), Begin)) { - AstStmtExpr* const pushBranchProcessp - = getQueuePushProcessSelfp(fl, processQueuep); - prependStmtsp(branchp, pushBranchProcessp); - } - }); + prependNestedForkBranchQueuePushes(beginBodyp, processQueuep, fl, false); m_beginDisableQueues.emplace(beginp, processQueuep); return processQueuep; } + AstStmtExpr* insertKillStmtp(AstDisable* const nodep, AstVar* const processQueuep) { + AstStmtExpr* const killStmtp = getQueueKillStmtp(nodep->fileline(), processQueuep); + nodep->addNextHere(killStmtp); + return killStmtp; + } + void addJumpAfterKill(AstStmtExpr* const killStmtp, AstNode* const targetp) { + AstJumpBlock* const jmpBlockp = getJumpBlock(targetp, false); + killStmtp->addNextHere(new AstJumpGo{killStmtp->fileline(), jmpBlockp}); + } void handleDisableOnFork(AstDisable* const nodep, const std::vector& forks) { // The support utilizes the process::kill()` method. For each `disable` a queue of // processes is declared. At the beginning of each fork that can be disabled, its process @@ -323,8 +338,7 @@ class LinkJumpVisitor final : public VNVisitor { } prependStmtsp(beginp, pushCurrentProcessp); } - AstStmtExpr* const killStmtp = getQueueKillStmtp(fl, processQueuep); - nodep->addNextHere(killStmtp); + AstStmtExpr* const killStmtp = insertKillStmtp(nodep, processQueuep); // 'process::kill' does not immediately kill the current process // executing the disable statement (because it's in the running state). @@ -334,8 +348,7 @@ class LinkJumpVisitor final : public VNVisitor { AstNodeBlock* forkBranchp = nullptr; for (AstNodeBlock* const blockp : vlstd::reverse_view(m_blockStack)) { if (blockp == targetp) { - AstJumpBlock* const jmpBlockp = getJumpBlock(VN_AS(forkBranchp, Begin), false); - killStmtp->addNextHere(new AstJumpGo{fl, jmpBlockp}); + addJumpAfterKill(killStmtp, VN_AS(forkBranchp, Begin)); break; } forkBranchp = blockp; @@ -522,8 +535,7 @@ class LinkJumpVisitor final : public VNVisitor { } if (AstTask* const taskp = VN_CAST(targetp, Task)) { AstVar* const processQueuep = getOrCreateTaskDisableQueuep(taskp, nodep->fileline()); - AstStmtExpr* const killStmtp = getQueueKillStmtp(nodep->fileline(), processQueuep); - nodep->addNextHere(killStmtp); + AstStmtExpr* const killStmtp = insertKillStmtp(nodep, processQueuep); // process::kill does not terminate the currently running process immediately. // If we disable the current task by name from inside itself, jump to its end. @@ -531,8 +543,7 @@ class LinkJumpVisitor final : public VNVisitor { AstNode* jumpTargetp = taskp; const auto it = m_taskDisableBegins.find(taskp); if (it != m_taskDisableBegins.end()) jumpTargetp = it->second; - AstJumpBlock* const blockp = getJumpBlock(jumpTargetp, false); - killStmtp->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); + addJumpAfterKill(killStmtp, jumpTargetp); } } else if (AstFork* const forkp = VN_CAST(targetp, Fork)) { std::vector forks; @@ -549,9 +560,7 @@ class LinkJumpVisitor final : public VNVisitor { } else { AstVar* const processQueuep = getOrCreateBeginDisableQueuep(beginp, nodep->fileline()); - AstStmtExpr* const killStmtp - = getQueueKillStmtp(nodep->fileline(), processQueuep); - nodep->addNextHere(killStmtp); + AstStmtExpr* const killStmtp = insertKillStmtp(nodep, processQueuep); // process::kill does not terminate the currently running process immediately. // If disable executes inside a fork branch of this named block, jump to the @@ -564,15 +573,13 @@ class LinkJumpVisitor final : public VNVisitor { } } if (currentBeginp && directlyUnderFork(currentBeginp)) { - AstJumpBlock* const blockp = getJumpBlock(currentBeginp, false); - killStmtp->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); + addJumpAfterKill(killStmtp, currentBeginp); } } } else { AstVar* const processQueuep = getOrCreateBeginDisableQueuep(beginp, nodep->fileline()); - AstStmtExpr* const killStmtp = getQueueKillStmtp(nodep->fileline(), processQueuep); - nodep->addNextHere(killStmtp); + insertKillStmtp(nodep, processQueuep); } } else { nodep->v3fatalSrc("Disable linked with node of unhandled type " diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 0c2e104e9..bbb1227c5 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -752,12 +752,7 @@ class TimingControlVisitor final : public VNVisitor { 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->varp()->processQueue(); + return beginp->stmtsp() && beginp->stmtsp()->isDisableQueuePushSelfStmt(); } // Register a callback so killing a process-backed fork branch decrements the join counter void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const {