Address disable handling review feedback (#7856)

This commit is contained in:
wsxarcher 2026-07-10 21:37:42 +02:00
parent 7c1de44646
commit e8fd3ae41a
6 changed files with 36 additions and 54 deletions

View File

@ -730,6 +730,7 @@ public:
// ACCESSORS for specific types
// Alas these can't be virtual or they break when passed a nullptr
bool isDisableQueuePushSelfStmt();
inline bool isClassHandleValue() const;
inline bool isNull() const;
inline bool isZero() const;

View File

@ -108,6 +108,21 @@ int AstNodeSel::bitConst() const {
return (constp ? constp->toSInt() : 0);
}
bool AstNode::isDisableQueuePushSelfStmt() {
// Detect LinkJump-generated registration:
// __VprocessQueue_*.push_back(std::process::self())
AstStmtExpr* const stmtExprp = VN_CAST(this, StmtExpr);
if (!stmtExprp) return false;
AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
if (!methodp || methodp->name() != "push_back") return false;
AstNode* const basep = AstArraySel::baseFromp(methodp->fromp(), false);
if (AstVarRef* const refp = VN_CAST(basep, VarRef)) return refp->varp()->processQueue();
if (AstMemberSel* const selp = VN_CAST(basep, MemberSel)) {
return selp->varp() && selp->varp()->processQueue();
}
return false;
}
void AstNodeStmt::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstNodeStmt::dumpJson(std::ostream& str) const { dumpJsonGen(str); }

View File

@ -593,40 +593,18 @@ class ForkVisitor final : public VNVisitor {
const AstConst* const constp = VN_CAST(delayp->lhsp(), Const);
return constp && (constp->toUQuad() == std::numeric_limits<uint64_t>::max());
}
static bool isDisableProcessQueueExpr(const AstNodeExpr* const nodep) {
const AstNode* const basep
= AstArraySel::baseFromp(const_cast<AstNodeExpr*>(nodep), false);
if (const AstVarRef* const refp = VN_CAST(basep, VarRef))
return refp->varp()->processQueue();
if (const AstMemberSel* const selp = VN_CAST(basep, MemberSel)) {
return selp->varp() && selp->varp()->processQueue();
}
return false;
}
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;
return isDisableProcessQueueExpr(methodp->fromp());
}
static const AstNode* unwrapLeadingJumpBlocks(const AstNode* nodep) {
while (const AstJumpBlock* const jumpBlockp = VN_CAST(nodep, JumpBlock)) {
static bool isDisableQueuePushSelfPrefix(AstNode* nodep) {
while (AstJumpBlock* const jumpBlockp = VN_CAST(nodep, JumpBlock)) {
nodep = jumpBlockp->stmtsp();
}
return nodep;
}
static bool isDisableQueuePushSelfPrefix(const AstNode* const nodep) {
return isDisableQueuePushSelfStmt(unwrapLeadingJumpBlocks(nodep));
return nodep && nodep->isDisableQueuePushSelfStmt();
}
template <typename T_Owner>
static bool insertForkSentinelAfterDisableQueuePushes(T_Owner* const ownerp,
AstNode* const firstStmtp,
AstNode* const delayp) {
AstNode* insertBeforep = firstStmtp;
while (insertBeforep && isDisableQueuePushSelfStmt(insertBeforep)) {
while (insertBeforep && insertBeforep->isDisableQueuePushSelfStmt()) {
insertBeforep = insertBeforep->nextp();
}
if (AstJumpBlock* const jumpBlockp = VN_CAST(insertBeforep, JumpBlock)) {
@ -654,9 +632,8 @@ class ForkVisitor final : public VNVisitor {
= insertForkSentinelAfterDisableQueuePushes(beginp, afterSentinelp, delayp);
UASSERT_OBJ(moved, beginp, "Failed to move fork sentinel after disable queue pushes");
}
static bool forkIsDisableable(const AstFork* const nodep) {
for (const AstBegin* itemp = nodep->forksp(); itemp;
itemp = VN_AS(itemp->nextp(), Begin)) {
static bool forkIsDisableable(AstFork* const nodep) {
for (AstBegin* itemp = nodep->forksp(); itemp; itemp = VN_AS(itemp->nextp(), Begin)) {
if (isDisableQueuePushSelfPrefix(itemp->stmtsp())) return true;
}
return false;
@ -680,7 +657,8 @@ class ForkVisitor final : public VNVisitor {
// start executing until the parent process is blocked or terminates. Because join and
// join_any block the parent process, deferring branch start with a synthetic #0 delay is
// normally only needed for join_none. A fork that can be disabled by name needs the same
// deferral for every join type.
// deferral for every join type so all branches register their processes before any branch
// body can disable the block.
if (nodep->joinType().joinNone() || forkIsDisableable(nodep)) {
UINFO(9, "Adding fork branch start sentinels " << nodep);
FileLine* fl = nodep->fileline();

View File

@ -6209,7 +6209,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
nodep->targetRefp()->unlinkFrBack()->deleteTree();
if (!hasPartSelect) {
const string instancePath = targetInstancePath(nodep->targetp(), targetPath);
if (instancePath != "") {
if (!instancePath.empty()) {
// Keep only the instance prefix so V3LinkJump can reference the selected
// instance's process queue without reparsing the disable target.
nodep->targetRefp(

View File

@ -607,8 +607,8 @@ class LinkJumpVisitor final : public VNVisitor {
// 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* const branchp = innerForkBranchp(beginp);
AstBegin* const jumpTargetp = branchp ? branchp : m_beginDisableBegins[beginp];
UASSERT_OBJ(jumpTargetp, nodep, "Missing disable jump target");
AstBegin* const jumpTargetp
= branchp ? branchp : m_beginDisableBegins.at(beginp);
addJumpAfterKill(killStmtp, jumpTargetp);
}
} else {

View File

@ -749,31 +749,19 @@ class TimingControlVisitor final : public VNVisitor {
addDebugInfo(donep);
beginp->addStmtsp(donep->makeStmt());
}
static bool isDisableProcessQueueExpr(const AstNodeExpr* const nodep) {
const AstNode* const basep
= AstArraySel::baseFromp(const_cast<AstNodeExpr*>(nodep), false);
if (const AstVarRef* const refp = VN_CAST(basep, VarRef))
return refp->varp()->processQueue();
if (const AstMemberSel* const selp = VN_CAST(basep, MemberSel)) {
return selp->varp() && selp->varp()->processQueue();
}
return false;
}
static bool hasDisableQueuePushSelfPrefix(const AstBegin* const beginp) {
static bool hasDisableQueuePushSelfPrefix(AstBegin* const beginp) {
// LinkJump prepends disable-by-name registration as:
// __VprocessQueue_*.push_back(std::process::self())
// By this pass V3LiftExpr has lifted the std::process::self() argument into a preceding
// temporary (and left a leading comment), so the push_back is no longer necessarily the
// first statement. Scan across the leading registration statements for it, stopping at the
// fork-start sentinel or the branch body. The branch's own registration is at the front;
// registrations for nested forks live in sub-blocks and so are not in this statement list.
for (const AstNode* stmtp = beginp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
// V3LiftExpr lifts std::process::self() into an assignment to a temporary, then V3Task
// lowers that assignment's function call into a leading comment and AstStmtExpr. Thus the
// push_back is no longer necessarily the first statement. Scan across this generated
// prefix for it, stopping at the fork-start sentinel or the branch body. Registrations for
// nested forks live in sub-blocks and so are not in this statement list.
for (AstNode* stmtp = beginp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (VN_IS(stmtp, Comment)) continue;
const AstStmtExpr* const stmtExprp = VN_CAST(stmtp, StmtExpr);
AstStmtExpr* const stmtExprp = VN_CAST(stmtp, StmtExpr);
if (!stmtExprp) break;
const AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
if (!methodp || methodp->name() != "push_back") continue;
if (isDisableProcessQueueExpr(methodp->fromp())) return true;
if (stmtExprp->isDisableQueuePushSelfStmt()) return true;
}
return false;
}