diff --git a/src/V3Ast.h b/src/V3Ast.h index a7161e2a1..910ad9697 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2194,8 +2194,6 @@ public: // Used by AstNode::broken() bool brokeExists() const { return V3Broken::isLinkable(this); } bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); } - bool brokeExistsBelow() const { return brokeExists() && !(m_brokenState >> 7); } - // Note: brokeExistsBelow is not quite precise, as it is true for sibling nodes as well // CONSTRUCTORS virtual ~AstNode() = default; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index b46c18028..549e304eb 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3279,14 +3279,13 @@ public: bool isDelayed() const { return m_delayed; } }; class AstJumpBlock final : public AstNodeStmt { - // Block of code including a single JumpLabel, and 0+ JumpGo's to that label + // Block of code that might contain AstJumpGo statements as children, + // which when exectued branch to right after the referenced AstJumpBlock. + // AstJumpBlocks can nest, and an AstJumpGo can reference any of the + // enclosing AstJumpBlocks (can break out of mulitple levels). // Parents: {statement list} - // Children: {statement list, with JumpGo and JumpLabel below} + // Children: {statement list, with JumpGo below} // @astgen op1 := stmtsp : List[AstNode] - // @astgen op2 := endStmtsp : List[AstNode] - // - // @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration - int m_labelNum = 0; // Set by V3EmitCSyms to tell final V3Emit what to increment VIsCached m_purity; // Pure state public: // After construction must call ->labelp to associate with appropriate label @@ -3295,66 +3294,35 @@ public: addStmtsp(stmtsp); } ASTGEN_MEMBERS_AstJumpBlock; - const char* broken() const override; int instrCount() const override { return 0; } bool maybePointedTo() const override VL_MT_SAFE { return true; } - bool sameNode(const AstNode* /*samep*/) const override { return true; } - int labelNum() const { return m_labelNum; } - void labelNum(int flag) { m_labelNum = flag; } - AstJumpLabel* labelp() const { return m_labelp; } - void labelp(AstJumpLabel* labelp) { m_labelp = labelp; } bool isPure() override; private: bool getPurityRecurse() const; }; class AstJumpGo final : public AstNodeStmt { - // Jump point; branch down to a JumpLabel - // No support for backward jumps at present - // Parents: {statement list with JumpBlock above} + // Branch to right after the referenced encloding AstJumpBlock + // Parents: statement, including the referenced AstJumpBlock // Children: none // - // @astgen ptr := m_labelp : AstJumpLabel // [After V3Jump] Pointer to declaration + // @astgen ptr := m_blockp : AstJumpBlock // The AstJumpBlock we are branching after public: - AstJumpGo(FileLine* fl, AstJumpLabel* labelp) + AstJumpGo(FileLine* fl, AstJumpBlock* blockp) : ASTGEN_SUPER_JumpGo(fl) - , m_labelp{labelp} {} + , m_blockp{blockp} {} ASTGEN_MEMBERS_AstJumpGo; const char* broken() const override; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; int instrCount() const override { return INSTR_COUNT_BRANCH; } bool sameNode(const AstNode* samep) const override { - return labelp() == VN_DBG_AS(samep, JumpGo)->labelp(); + return blockp() == VN_DBG_AS(samep, JumpGo)->blockp(); } bool isGateOptimizable() const override { return false; } bool isBrancher() const override { return true; // SPECIAL: We don't process code after breaks } - AstJumpLabel* labelp() const { return m_labelp; } -}; -class AstJumpLabel final : public AstNodeStmt { - // Jump point declaration - // Parents: {statement list with JumpBlock above} - // Children: none - // @astgen ptr := m_blockp : AstJumpBlock // [After V3Jump] Pointer to declaration -public: - AstJumpLabel(FileLine* fl, AstJumpBlock* blockp) - : ASTGEN_SUPER_JumpLabel(fl) - , m_blockp{blockp} {} - ASTGEN_MEMBERS_AstJumpLabel; - bool maybePointedTo() const override VL_MT_SAFE { return true; } - const char* broken() const override { - BROKEN_RTN(!blockp()->brokeExistsAbove()); - BROKEN_RTN(blockp()->labelp() != this); - return nullptr; - } - void dump(std::ostream& str) const override; - void dumpJson(std::ostream& str) const override; - int instrCount() const override { return 0; } - bool sameNode(const AstNode* samep) const override { - return blockp() == VN_DBG_AS(samep, JumpLabel)->blockp(); - } AstJumpBlock* blockp() const { return m_blockp; } }; class AstMonitorOff final : public AstNodeStmt { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 8ed5b6d2e..7cfc48210 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1139,10 +1139,6 @@ AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) { return nodep; } -const char* AstJumpBlock::broken() const { - BROKEN_RTN(!labelp()->brokeExistsBelow()); - return nullptr; -} bool AstJumpBlock::isPure() { if (!m_purity.isCached()) m_purity.set(getPurityRecurse()); return m_purity.get(); @@ -1977,21 +1973,6 @@ AstNodeExpr* AstInitArray::getIndexDefaultedValuep(uint64_t index) const { } void AstJumpGo::dump(std::ostream& str) const { - this->AstNodeStmt::dump(str); - str << " -> "; - if (labelp()) { - labelp()->dump(str); - } else { - str << "%E:UNLINKED"; - } -} -void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); } -const char* AstJumpGo::broken() const { - BROKEN_RTN(!labelp()->brokeExistsBelow()); - return nullptr; -} - -void AstJumpLabel::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); str << " -> "; if (blockp()) { @@ -2000,7 +1981,11 @@ void AstJumpLabel::dump(std::ostream& str) const { str << "%E:UNLINKED"; } } -void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); } +void AstJumpGo::dumpJson(std::ostream& str) const { dumpJsonGen(str); } +const char* AstJumpGo::broken() const { + BROKEN_RTN(!blockp()->brokeExistsAbove()); + return nullptr; +} void AstMemberDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index ae52fad2c..c8d54a8c9 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -38,6 +38,7 @@ #include #include #include +#include VL_DEFINE_DEBUG_FUNCTIONS; @@ -912,7 +913,6 @@ class ConstVisitor final : public VNVisitor { // ** only when m_warn/m_doExpensive is set. If state is needed other times, // ** must track down everywhere V3Const is called and make sure no overlaps. // AstVar::user4p -> Used by variable marking/finding - // AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label // AstEnum::user4 -> bool. Recursing. // STATE @@ -938,6 +938,7 @@ class ConstVisitor final : public VNVisitor { static uint32_t s_globalPassNum; // Counts number of times ConstVisitor invoked as global pass V3UniqueNames m_concswapNames; // For generating unique temporary variable names std::map m_containsMemberAccess; // Caches results of matchBiopToBitwise + std::unordered_set m_usedJumpBlocks; // JumpBlocks used by some JumpGo // METHODS @@ -2303,19 +2304,6 @@ class ConstVisitor final : public VNVisitor { } return false; } - bool replaceJumpGoNext(AstJumpGo* nodep, AstNode* abovep) { - // If JumpGo has an upper JumpBlock that is to same label, then - // code will by normal sequential operation do the JUMPGO and it - // can be removed. - if (nodep->nextp()) return false; // Label jumps other statements - AstJumpBlock* const aboveBlockp = VN_CAST(abovep, JumpBlock); - if (!aboveBlockp) return false; - if (aboveBlockp != nodep->labelp()->blockp()) return false; - if (aboveBlockp->endStmtsp() != nodep->labelp()) return false; - UINFO(4, "JUMPGO => last remove " << nodep); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return true; - } // Boolean replacements bool operandBoolShift(const AstNode* nodep) { @@ -3402,52 +3390,39 @@ class ConstVisitor final : public VNVisitor { // Jump elimination void visit(AstJumpGo* nodep) override { - iterateChildren(nodep); - // Jump to label where label immediately follows this JumpGo is not useful - if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) { - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - // Keep the label, might be other jumps pointing to it, gets cleaned later - return; - } - if (m_doExpensive) { - // Any non-label statements (at this statement level) can never execute - while (nodep->nextp() && !VN_IS(nodep->nextp(), JumpLabel)) { - pushDeletep(nodep->nextp()->unlinkFrBack()); + // Any statements following the JumpGo (at this statement level) never execute, delete + if (nodep->nextp()) pushDeletep(nodep->nextp()->unlinkFrBackWithNext()); + + // JumpGo as last statement in target JumpBlock (including last in a last sub-list), + // is a no-op, remove it. + for (AstNode* abovep = nodep->abovep(); abovep; abovep = abovep->abovep()) { + if (abovep == nodep->blockp()) { + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; } - // If last statement in a jump label we have JumpLabel(...., JumpGo) - // Often caused by "return" in a Verilog function. The Go is pointless, remove. - if (replaceJumpGoNext(nodep, nodep->abovep())) return; - // Also optimize If with a then or else's final statement being this JumpGo - // We only do single ifs... Ideally we'd look at control flow and delete any - // Jumps where any following control flow point is the label - if (!nodep->nextp()) { - if (AstNodeIf* const aboveIfp = VN_CAST(nodep->abovep(), NodeIf)) { - if (!aboveIfp->nextp()) { - if (replaceJumpGoNext(nodep, aboveIfp->abovep())) return; - } - } - } - nodep->labelp()->blockp()->user4(true); + // Stop if not doing expensive, or if the above node is not the last in its list, + // ... or if it's not an 'if' TODO: it would be enough if it was not a branch. + if (!m_doExpensive || abovep->nextp() || !VN_IS(abovep, If)) break; } + // Mark JumpBlock as used + m_usedJumpBlocks.emplace(nodep->blockp()); m_hasJumpDelay = true; } void visit(AstJumpBlock* nodep) override { - // Because JumpLabels disable many optimizations, - // remove JumpLabels that are not pointed to by any AstJumpGos - // Note this assumes all AstJumpGos are underneath the given label; V3Broken asserts this iterateChildren(nodep); - // AstJumpGo's below here that point to this node will set user4 - if (m_doExpensive && !nodep->user4()) { + + // Remove if empty + if (!nodep->stmtsp()) { + UINFO(4, "JUMPLABEL => empty " << nodep); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return; + } + + // If no JumpGo points to this node, replace it with its body + if (!m_usedJumpBlocks.count(nodep)) { UINFO(4, "JUMPLABEL => unused " << nodep); - AstNode* underp = nullptr; - if (nodep->stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext(); - if (underp) { - nodep->replaceWith(underp); - } else { - nodep->unlinkFrBack(); - } - pushDeletep(nodep->labelp()->unlinkFrBack()); + nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext()); VL_DO_DANGLING(pushDeletep(nodep), nodep); } } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 63d57aaf3..348e2f558 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -117,7 +118,7 @@ public: class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { VMemberMap m_memberMap; AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting - int m_labelNum = 0; // Next label number + std::unordered_map m_labelNumbers; // Label numbers for JumpBlocks bool m_inUC = false; // Inside an AstUCStmt or AstUCExpr bool m_emitConstInit = false; // Emitting constant initializer bool m_createdScopeHash = false; // Already created a scope hash @@ -325,6 +326,7 @@ public: VL_RESTORER(m_createdScopeHash); m_cfuncp = nodep; m_instantiatesOwnProcess = false; + m_labelNumbers.clear(); // No need to save/restore, all Jumps must be within the function splitSizeInc(nodep); @@ -993,25 +995,29 @@ public: puts(";\n"); } void visit(AstJumpBlock* nodep) override { - nodep->labelNum(++m_labelNum); + // Allocate label number + const size_t n = m_labelNumbers.size(); + const bool newEntry = m_labelNumbers.emplace(nodep, n).second; + UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twide"); + // Emit putns(nodep, "{\n"); // Make it visually obvious label jumps outside these VL_RESTORER(m_createdScopeHash); iterateAndNextConstNull(nodep->stmtsp()); - iterateAndNextConstNull(nodep->endStmtsp()); + puts("__Vlabel" + std::to_string(n) + ": ;\n"); puts("}\n"); } + void visit(AstJumpGo* nodep) override { + // Retrieve target label number - must already exist (from enclosing AstJumpBlock) + const size_t n = m_labelNumbers.at(nodep->blockp()); + // Emit + putns(nodep, "goto __Vlabel" + std::to_string(n) + ";\n"); + } void visit(AstCLocalScope* nodep) override { putns(nodep, "{\n"); VL_RESTORER(m_createdScopeHash); iterateAndNextConstNull(nodep->stmtsp()); puts("}\n"); } - void visit(AstJumpGo* nodep) override { - putns(nodep, "goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n"); - } - void visit(AstJumpLabel* nodep) override { - putns(nodep, "__Vlabel" + cvtToStr(nodep->blockp()->labelNum()) + ": ;\n"); - } void visit(AstWhile* nodep) override { VL_RESTORER(m_createdScopeHash); putns(nodep, "while ("); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index ca95d85b5..be6addce6 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -20,6 +20,7 @@ #include "V3EmitCBase.h" +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -37,6 +38,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars bool m_arrayPost = false; // Print array information that goes after identifier (vs after) std::deque m_packedps; // Packed arrays to print with BasicDType + std::unordered_map m_labelNumbers; // Label numbers for JumpBlocks // METHODS virtual void puts(const string& str) = 0; @@ -308,14 +310,23 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { puts(");\n"); } void visit(AstJumpBlock* nodep) override { - putbs("begin : label" + cvtToStr(nodep->labelNum()) + "\n"); - if (nodep->stmtsp()) iterateAndNextConstNull(nodep->stmtsp()); + // Allocate label number + const size_t n = m_labelNumbers.size(); + const bool newEntry = m_labelNumbers.emplace(nodep, n).second; + UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twide"); + // Emit + putbs("begin : label" + std::to_string(n) + "\n"); + iterateAndNextConstNull(nodep->stmtsp()); puts("end\n"); } void visit(AstJumpGo* nodep) override { - putbs("disable label" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n"); + // Retrieve target label number - Sometimes EmitV is used by debug code, + // so allow printing with an unknown target + const auto it = m_labelNumbers.find(nodep->blockp()); + const std::string label + = it != m_labelNumbers.end() ? "label" + std::to_string(it->second) : ""; + putbs("disable " + label + ";\n"); } - void visit(AstJumpLabel* nodep) override { putbs("// " + cvtToStr(nodep->blockp()) + ":\n"); } void visit(AstNodeReadWriteMem* nodep) override { putfs(nodep, nodep->verilogKwd()); putbs("("); diff --git a/src/V3Hasher.cpp b/src/V3Hasher.cpp index c9561cc2f..7608bbaed 100644 --- a/src/V3Hasher.cpp +++ b/src/V3Hasher.cpp @@ -320,9 +320,7 @@ class HasherVisitor final : public VNVisitorConst { }); } void visit(AstJumpGo* nodep) override { - m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { // - iterateConstNull(nodep->labelp()); - }); + m_hash += hashNodeAndIterate(nodep, false, false, []() {}); } void visit(AstTraceInc* nodep) override { m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [this, nodep]() { // diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 2fbae615d..e572b87fb 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -45,8 +45,8 @@ VL_DEFINE_DEBUG_FUNCTIONS; class LinkJumpVisitor final : public VNVisitor { // NODE STATE - // AstNode::user1() -> AstJumpLabel*, for this block if endOfIter - // AstNode::user2() -> AstJumpLabel*, for this block if !endOfIter + // AstNode::user1() -> AstJumpBlock*, for body of this loop + // AstNode::user2() -> AstJumpBlock*, for this block // AstNodeBlock::user3() -> bool, true if contains a fork const VNUser1InUse m_user1InUse; const VNUser2InUse m_user2InUse; @@ -65,36 +65,34 @@ class LinkJumpVisitor final : public VNVisitor { "__VprocessQueue"}; // Names for queues needed for 'disable' handling // METHODS - AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) { - // Put label under given node, and if WHILE optionally at end of iteration - UINFO(4, "Create label for " << nodep); - if (VN_IS(nodep, JumpLabel)) return VN_AS(nodep, JumpLabel); // Done + // Get (and create if necessary) the JumpBlock for this statement + AstJumpBlock* getJumpBlock(AstNode* nodep, bool endOfIter) { + // Wrap 'nodep' in JumpBlock. If loop, wrap the body instead if endOfIter is true + UINFO(4, "Create JumpBlock for " << nodep); // Made it previously? We always jump to the end, so this works out if (endOfIter) { - if (nodep->user1p()) return VN_AS(nodep->user1p(), JumpLabel); + if (nodep->user1p()) return VN_AS(nodep->user1p(), JumpBlock); } else { - if (nodep->user2p()) return VN_AS(nodep->user2p(), JumpLabel); + if (nodep->user2p()) return VN_AS(nodep->user2p(), JumpBlock); } AstNode* underp = nullptr; bool under_and_next = true; - if (VN_IS(nodep, NodeBlock)) { - underp = VN_AS(nodep, NodeBlock)->stmtsp(); - } else if (VN_IS(nodep, NodeFTask)) { - underp = VN_AS(nodep, NodeFTask)->stmtsp(); - } else if (VN_IS(nodep, Foreach)) { + if (AstNodeBlock* const blockp = VN_CAST(nodep, NodeBlock)) { + underp = blockp->stmtsp(); + } else if (AstNodeFTask* const fTaskp = VN_CAST(nodep, NodeFTask)) { + underp = fTaskp->stmtsp(); + } else if (AstForeach* const foreachp = VN_CAST(nodep, Foreach)) { if (endOfIter) { - underp = VN_AS(nodep, Foreach)->stmtsp(); + underp = foreachp->stmtsp(); } else { underp = nodep; under_and_next = false; // IE we skip the entire foreach } - } else if (VN_IS(nodep, While)) { + } else if (AstWhile* const whilep = VN_CAST(nodep, While)) { if (endOfIter) { - // Note we jump to end of bodysp; a FOR loop has its - // increment under incsp() which we don't skip - underp = VN_AS(nodep, While)->stmtsp(); + underp = whilep->stmtsp(); } else { underp = nodep; under_and_next = false; // IE we skip the entire while @@ -118,36 +116,32 @@ class LinkJumpVisitor final : public VNVisitor { UASSERT_OBJ(underp, nodep, "Break/disable/continue not under expected statement"); UINFO(5, " Underpoint is " << underp); - if (VN_IS(underp, JumpLabel)) { - return VN_AS(underp, JumpLabel); - } else { // Move underp stuff to be under a new label - AstJumpBlock* const blockp = new AstJumpBlock{nodep->fileline(), nullptr}; - AstJumpLabel* const labelp = new AstJumpLabel{nodep->fileline(), blockp}; - blockp->labelp(labelp); - - VNRelinker repHandle; - if (under_and_next) { - underp->unlinkFrBackWithNext(&repHandle); - } else { - underp->unlinkFrBack(&repHandle); - } - repHandle.relink(blockp); - - blockp->addStmtsp(underp); - // Keep any AstVars under the function not under the new JumpLabel - for (AstNode *nextp, *varp = underp; varp; varp = nextp) { - nextp = varp->nextp(); - if (VN_IS(varp, Var)) blockp->addHereThisAsNext(varp->unlinkFrBack()); - } - // Label goes last - blockp->addEndStmtsp(labelp); - if (endOfIter) { - nodep->user1p(labelp); - } else { - nodep->user2p(labelp); - } - return labelp; + // If already wrapped, we are done ... + if (!underp->nextp() || !under_and_next) { + if (AstJumpBlock* const blockp = VN_CAST(underp, JumpBlock)) return blockp; } + + // Move underp stuff to be under a new AstJumpBlock + VNRelinker repHandle; + if (under_and_next) { + underp->unlinkFrBackWithNext(&repHandle); + } else { + underp->unlinkFrBack(&repHandle); + } + AstJumpBlock* const blockp = new AstJumpBlock{nodep->fileline(), underp}; + if (endOfIter) { + nodep->user1p(blockp); + } else { + nodep->user2p(blockp); + } + repHandle.relink(blockp); + + // Keep any AstVars under the function not under the new JumpLabel + for (AstNode *nextp, *varp = underp; varp; varp = nextp) { + nextp = varp->nextp(); + if (VN_IS(varp, Var)) blockp->addHereThisAsNext(varp->unlinkFrBack()); + } + return blockp; } void addPrefixToBlocksRecurse(const std::string& prefix, AstNode* const nodep) { // Add a prefix to blocks @@ -379,8 +373,8 @@ class LinkJumpVisitor final : public VNVisitor { nodep->lhsp()->unlinkFrBackWithNext()}); } // Jump to the end of the function call - AstJumpLabel* const labelp = findAddLabel(m_ftaskp, false); - nodep->addHereThisAsNext(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(m_ftaskp, false); + nodep->addHereThisAsNext(new AstJumpGo{nodep->fileline(), blockp}); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -391,8 +385,8 @@ class LinkJumpVisitor final : public VNVisitor { nodep->v3error("break isn't underneath a loop"); } else { // Jump to the end of the loop - AstJumpLabel* const labelp = findAddLabel(m_loopp, false); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(m_loopp, false); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -404,8 +398,8 @@ class LinkJumpVisitor final : public VNVisitor { } else { // Jump to the end of this iteration // If a "for" loop then need to still do the post-loop increment - AstJumpLabel* const labelp = findAddLabel(m_loopp, true); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(m_loopp, true); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); } nodep->unlinkFrBack(); VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -444,8 +438,8 @@ class LinkJumpVisitor final : public VNVisitor { "Unsupported: disabling block that contains a fork"); } else { // Jump to the end of the named block - AstJumpLabel* const labelp = findAddLabel(beginp, false); - nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp}); + AstJumpBlock* const blockp = getJumpBlock(beginp, false); + nodep->addNextHere(new AstJumpGo{nodep->fileline(), blockp}); } } else { nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '" diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 9baaec0f4..59f2614b6 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -397,10 +397,9 @@ private: UASSERT_OBJ(vscp, nodep, "Not linked"); return vscp; } - bool jumpingOver(const AstNode* nodep) const { - // True to jump over this node - all visitors must call this up front - return (m_jumpp && m_jumpp->labelp() != nodep); - } + + // True if current node might be jumped over - all visitors must call this up front + bool jumpingOver() const { return m_jumpp; } void assignOutValue(AstNodeAssign* nodep, AstNode* vscp, const AstNodeExpr* valuep) { if (VN_IS(nodep, AssignDly)) { // Don't do setValue, as value isn't yet visible to following statements @@ -413,7 +412,7 @@ private: // VISITORS void visit(AstAlways* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateChildrenConst(nodep); } @@ -421,7 +420,7 @@ private: // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. } void visit(AstVarRef* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate UASSERT_OBJ(nodep->varp(), nodep, "Unlinked"); iterateChildrenConst(nodep->varp()); @@ -490,7 +489,7 @@ private: } } void visit(AstVarXRef* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (m_scoped) { badNodeType(nodep); return; @@ -500,7 +499,7 @@ private: } } void visit(AstNodeFTask* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!m_params) { badNodeType(nodep); return; @@ -520,7 +519,7 @@ private: iterateChildrenConst(nodep); } void visit(AstInitialStatic* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!m_params) { badNodeType(nodep); return; @@ -529,7 +528,7 @@ private: iterateChildrenConst(nodep); } void visit(AstNodeIf* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; UINFO(5, " IF " << nodep); checkNodeInfo(nodep); if (m_checkOnly) { @@ -856,7 +855,7 @@ private: } } void visit(AstNodeAssign* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); @@ -900,7 +899,7 @@ private: iterateChildrenConst(nodep); } void visit(AstNodeCase* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; UINFO(5, " CASE " << nodep); checkNodeInfo(nodep); if (m_checkOnly) { @@ -939,7 +938,7 @@ private: void visit(AstCaseItem* nodep) override { // Real handling is in AstNodeCase - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateChildrenConst(nodep); } @@ -947,12 +946,12 @@ private: void visit(AstComment*) override {} void visit(AstStmtExpr* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateChildrenConst(nodep); } void visit(AstExprStmt* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); iterateAndNextConstNull(nodep->stmtsp()); if (!optimizable()) return; @@ -962,30 +961,24 @@ private: } void visit(AstJumpBlock* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; iterateChildrenConst(nodep); + if (m_jumpp && m_jumpp->blockp() == nodep) { + UINFO(5, " JUMP DONE " << nodep); + m_jumpp = nullptr; + } } void visit(AstJumpGo* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; checkNodeInfo(nodep); if (!m_checkOnly) { UINFO(5, " JUMP GO " << nodep); m_jumpp = nodep; } } - void visit(AstJumpLabel* nodep) override { - // This only supports forward jumps. That's all we make at present, - // AstJumpGo::broken uses brokeExistsBelow() to check this. - if (jumpingOver(nodep)) return; - checkNodeInfo(nodep); - iterateChildrenConst(nodep); - if (m_jumpp && m_jumpp->labelp() == nodep) { - UINFO(5, " JUMP DONE " << nodep); - m_jumpp = nullptr; - } - } + void visit(AstStop* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (m_params) { // This message seems better than an obscure $stop // The spec says $stop is just ignored, it seems evil to ignore assertions clearOptimizable( @@ -1030,7 +1023,7 @@ private: void visit(AstWhile* nodep) override { // Doing lots of Whiles is slow, so only for parameters - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; UINFO(5, " WHILE " << nodep); if (!m_params) { badNodeType(nodep); @@ -1044,15 +1037,15 @@ private: while (true) { UINFO(5, " WHILE-ITER " << nodep); iterateAndNextConstNull(nodep->condp()); - if (jumpingOver(nodep)) break; + if (jumpingOver()) break; if (!optimizable()) break; if (!fetchConst(nodep->condp())->num().isNeqZero()) { // break; } iterateAndNextConstNull(nodep->stmtsp()); - if (jumpingOver(nodep)) break; + if (jumpingOver()) break; iterateAndNextConstNull(nodep->incsp()); - if (jumpingOver(nodep)) break; + if (jumpingOver()) break; // Prep for next loop if (loops++ @@ -1068,7 +1061,7 @@ private: } void visit(AstFuncRef* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate UINFO(5, " FUNCREF " << nodep); checkNodeInfo(nodep); @@ -1140,7 +1133,7 @@ private: } void visit(AstVar* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!m_params) { badNodeType(nodep); return; @@ -1148,12 +1141,12 @@ private: } void visit(AstScopeName* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; // Ignore } void visit(AstSFormatF* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); iterateChildrenConst(nodep); @@ -1214,7 +1207,7 @@ private: } void visit(AstDisplay* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; if (!optimizable()) return; // Accelerate // We ignore isPredictOptimizable as $display is often in constant // functions and we want them to work if used with parameters @@ -1242,11 +1235,11 @@ private: // Some CMethods such as size() on queues could be supported, but // instead we should change those methods to new Ast types so we can // properly dispatch them - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; knownBadNodeType(nodep); } void visit(AstMemberSel* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; knownBadNodeType(nodep); } // ==== @@ -1255,7 +1248,7 @@ private: // AstCoverInc, AstFinish, // AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt void visit(AstNode* nodep) override { - if (jumpingOver(nodep)) return; + if (jumpingOver()) return; badNodeType(nodep); } diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index dfb607cbe..03c910a7f 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -116,10 +116,10 @@ module Vt_debug_emitv_t; endtask function f; input int signed v; - begin : label0 + begin : label1 $display("stmt"); f = ((v == 'sh0) ? 'sh63 : ((~ v) + 'sh1)); - disable label0; + disable label1; end endfunction initial begin @@ -203,12 +203,12 @@ module Vt_debug_emitv_t; begin : unnamedblk3 int signed i; i = 'sh0; - begin : label0 + begin : label2 while ((i < cyc)) begin begin sum = (sum + i); if ((sum > 'sha)) begin - disable label0; + disable label2; end else begin sum = (sum + 'sh1); @@ -390,13 +390,13 @@ module Vt_debug_emitv_sub; endtask function f; input int signed v; - begin : label0 + begin : label3 if ((v == 'sh0)) begin f = 'sh21; - disable label0; + disable label3; end f = ({32'h1{{31'h0, v[2]}}} + 32'h1); - disable label0; + disable label3; end endfunction real r;