diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 9d53020f8..0c4118a3f 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -21,12 +21,12 @@ // // Note this can be called multiple times. // Create a IACTIVE(initial), SACTIVE(combo) -// ALWAYS: Remove any-edges from sense list -// If no POS/NEG in senselist, Fold into SACTIVE(combo) -// Else fold into SACTIVE(sequent). -// OPTIMIZE: When support async clocks, fold into that active if possible -// INITIAL: Move into IACTIVE -// WIRE: Move into SACTIVE(combo) +// ALWAYS: Remove any-edges from sense list +// If no POS/NEG in senselist, Fold into SACTIVE(combo) +// Else fold into SACTIVE(sequent). +// OPTIMIZE: When support async clocks, fold into that active if possible +// INITIAL: Move into IACTIVE +// WIRE: Move into SACTIVE(combo) // //************************************************************************* @@ -56,66 +56,68 @@ class ActiveNamer : public ActiveBaseVisitor { private: typedef std::map ActiveNameMap; // STATE - AstScope* m_scopep; // Current scope to add statement to - AstActive* m_iActivep; // For current scope, the IActive we're building - AstActive* m_cActivep; // For current scope, the SActive(combo) we're building + AstScope* m_scopep; // Current scope to add statement to + AstActive* m_iActivep; // For current scope, the IActive we're building + AstActive* m_cActivep; // For current scope, the SActive(combo) we're building - SenTreeSet m_activeSens; // Sen lists for each active we've made + SenTreeSet m_activeSens; // Sen lists for each active we've made typedef vl_unordered_map ActiveMap; ActiveMap m_activeMap; // Map sentree to active, for folding. // METHODS void addActive(AstActive* nodep) { - if (!m_scopep) nodep->v3fatalSrc("NULL scope"); - m_scopep->addActivep(nodep); + if (!m_scopep) nodep->v3fatalSrc("NULL scope"); + m_scopep->addActivep(nodep); } // VISITORS virtual void visit(AstScope* nodep) { - m_scopep = nodep; - m_iActivep = NULL; - m_cActivep = NULL; + m_scopep = nodep; + m_iActivep = NULL; + m_cActivep = NULL; m_activeSens.clear(); m_activeMap.clear(); iterateChildren(nodep); - // Don't clear scopep, the namer persists beyond this visit + // Don't clear scopep, the namer persists beyond this visit } virtual void visit(AstSenTree* nodep) { - // Simplify sensitivity list - V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep); + // Simplify sensitivity list + V3Const::constifyExpensiveEdit(nodep); VL_DANGLING(nodep); } // Empty visitors, speed things up virtual void visit(AstNodeStmt* nodep) { } //-------------------- // Default virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } // METHODS public: AstScope* scopep() { return m_scopep; } AstActive* getCActive(FileLine* fl) { - if (!m_cActivep) { - m_cActivep = new AstActive(fl, "combo", - new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Combo()))); - m_cActivep->sensesStorep(m_cActivep->sensesp()); - addActive(m_cActivep); - } - return m_cActivep; + if (!m_cActivep) { + m_cActivep = new AstActive( + fl, "combo", + new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Combo()))); + m_cActivep->sensesStorep(m_cActivep->sensesp()); + addActive(m_cActivep); + } + return m_cActivep; } AstActive* getIActive(FileLine* fl) { - if (!m_iActivep) { - m_iActivep = new AstActive(fl, "initial", - new AstSenTree(fl, new AstSenItem(fl,AstSenItem::Initial()))); - m_iActivep->sensesStorep(m_iActivep->sensesp()); - addActive(m_iActivep); - } - return m_iActivep; + if (!m_iActivep) { + m_iActivep = new AstActive( + fl, "initial", + new AstSenTree(fl, new AstSenItem(fl, AstSenItem::Initial()))); + m_iActivep->sensesStorep(m_iActivep->sensesp()); + addActive(m_iActivep); + } + return m_iActivep; } AstActive* getActive(FileLine* fl, AstSenTree* sensesp) { - // Return a sentree in this scope that matches given sense list. + // Return a sentree in this scope that matches given sense list. - AstActive* activep = NULL; + AstActive* activep = NULL; AstSenTree* activeSenp = m_activeSens.find(sensesp); if (activeSenp) { ActiveMap::iterator it = m_activeMap.find(activeSenp); @@ -123,26 +125,26 @@ public: activep = it->second; } - // Not found, form a new one - if (!activep) { - AstSenTree* newsenp = sensesp->cloneTree(false); - activep = new AstActive(fl, "sequent", newsenp); - activep->sensesStorep(activep->sensesp()); - UINFO(8," New ACTIVE "<cloneTree(false); + activep = new AstActive(fl, "sequent", newsenp); + activep->sensesStorep(activep->sensesp()); + UINFO(8," New ACTIVE "<v3warn(INITIALDLY, "Delayed assignments (<=) in initial" " or final block; suggest blocking assignments (=)."); - } else if (m_check == CT_LATCH) { - // Suppress. Shouldn't matter that the interior of the latch races - } else { + } else if (m_check == CT_LATCH) { + // Suppress. Shouldn't matter that the interior of the latch races + } else { nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked" " (non flop or latch) block; suggest blocking assignments (=)."); - } + } AstNode* newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()); - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); - } + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); + } } virtual void visit(AstAssign* nodep) { - if (m_check == CT_SEQ) { - AstNode* las = m_assignp; - m_assignp = nodep; + if (m_check == CT_SEQ) { + AstNode* las = m_assignp; + m_assignp = nodep; iterateAndNextNull(nodep->lhsp()); - m_assignp = las; - } + m_assignp = las; + } } virtual void visit(AstVarRef* nodep) { - AstVar* varp=nodep->varp(); - if (m_check == CT_SEQ - && m_assignp - && !varp->isUsedLoopIdx() // Ignore loop indicies - && !varp->isTemp() - ) { - // Allow turning off warnings on the always, or the variable also - if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ) - && !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ) - && !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ) - ) { - m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always - varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); - nodep->v3warn(BLKSEQ,"Blocking assignments (=) in sequential (flop or latch) block; suggest delayed assignments (<=)."); - } - } + AstVar* varp = nodep->varp(); + if (m_check == CT_SEQ + && m_assignp + && !varp->isUsedLoopIdx() // Ignore loop indicies + && !varp->isTemp() + ) { + // Allow turning off warnings on the always, or the variable also + if (!m_alwaysp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ) + && !m_assignp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ) + && !varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ) + ) { + m_alwaysp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Complain just once for the entire always + varp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); + nodep->v3warn(BLKSEQ, "Blocking assignments (=) in sequential (flop or latch) block; suggest delayed assignments (<=)."); + } + } } //-------------------- virtual void visit(AstNode* nodep) { @@ -214,9 +216,9 @@ private: public: // CONSTUCTORS ActiveDlyVisitor(AstNode* nodep, CheckType check) { - m_alwaysp = nodep; - m_check = check; - m_assignp = NULL; + m_alwaysp = nodep; + m_check = check; + m_assignp = NULL; iterate(nodep); } virtual ~ActiveDlyVisitor() {} @@ -229,183 +231,188 @@ class ActiveVisitor : public ActiveBaseVisitor { private: // NODE STATE // Each call to V3Const::constify - // AstNode::user4() Used by V3Const::constify, called below + // AstNode::user4() Used by V3Const::constify, called below // STATE - ActiveNamer m_namer; // Tracking of active names - AstCFunc* m_scopeFinalp; // Final function for this scope - bool m_itemCombo; // Found a SenItem combo - bool m_itemSequent; // Found a SenItem sequential + ActiveNamer m_namer; // Tracking of active names + AstCFunc* m_scopeFinalp; // Final function for this scope + bool m_itemCombo; // Found a SenItem combo + bool m_itemSequent; // Found a SenItem sequential // VISITORS virtual void visit(AstScope* nodep) { - // Create required actives and add to scope - UINFO(4," SCOPE "<fileline()); - nodep->unlinkFrBack(); - wantactivep->addStmtsp(nodep); + // Relink to IACTIVE, unless already under it + UINFO(4," INITIAL "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); } virtual void visit(AstAssignAlias* nodep) { - // Relink to CACTIVE, unless already under it - UINFO(4," ASSIGNW "<fileline()); - nodep->unlinkFrBack(); - wantactivep->addStmtsp(nodep); + // Relink to CACTIVE, unless already under it + UINFO(4," ASSIGNW "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); } virtual void visit(AstAssignW* nodep) { - // Relink to CACTIVE, unless already under it - UINFO(4," ASSIGNW "<fileline()); - nodep->unlinkFrBack(); - wantactivep->addStmtsp(nodep); + // Relink to CACTIVE, unless already under it + UINFO(4," ASSIGNW "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); } virtual void visit(AstCoverToggle* nodep) { - // Relink to CACTIVE, unless already under it - UINFO(4," COVERTOGGLE "<fileline()); - nodep->unlinkFrBack(); - wantactivep->addStmtsp(nodep); + // Relink to CACTIVE, unless already under it + UINFO(4," COVERTOGGLE "<fileline()); + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); } virtual void visit(AstFinal* nodep) { - // Relink to CFUNC for the final - UINFO(4," FINAL "<bodysp()) { // Empty, Kill it. - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - return; - } - ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL); - if (!m_scopeFinalp) { - m_scopeFinalp = new AstCFunc(nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(), m_namer.scopep()); - m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar()); - m_scopeFinalp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); - m_scopeFinalp->dontCombine(true); - m_scopeFinalp->formCallTree(true); - m_scopeFinalp->slow(true); - m_namer.scopep()->addActivep(m_scopeFinalp); - } - nodep->unlinkFrBack(); - m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName())); - m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext()); - nodep->deleteTree(); VL_DANGLING(nodep); + // Relink to CFUNC for the final + UINFO(4," FINAL "<bodysp()) { // Empty, Kill it. + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + return; + } + ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_INITIAL); + if (!m_scopeFinalp) { + m_scopeFinalp = new AstCFunc( + nodep->fileline(), "_final_"+m_namer.scopep()->nameDotless(), + m_namer.scopep()); + m_scopeFinalp->argTypes(EmitCBaseVisitor::symClassVar()); + m_scopeFinalp->addInitsp( + new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); + m_scopeFinalp->dontCombine(true); + m_scopeFinalp->formCallTree(true); + m_scopeFinalp->slow(true); + m_namer.scopep()->addActivep(m_scopeFinalp); + } + nodep->unlinkFrBack(); + m_scopeFinalp->addStmtsp(new AstComment(nodep->fileline(), nodep->typeName())); + m_scopeFinalp->addStmtsp(nodep->bodysp()->unlinkFrBackWithNext()); + nodep->deleteTree(); VL_DANGLING(nodep); } // METHODS void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) { - // Move always to appropriate ACTIVE based on its sense list - if (oldsensesp - && oldsensesp->sensesp() + // Move always to appropriate ACTIVE based on its sense list + if (oldsensesp + && oldsensesp->sensesp() && VN_IS(oldsensesp->sensesp(), SenItem) && VN_CAST(oldsensesp->sensesp(), SenItem)->isNever()) { - // Never executing. Kill it. - if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated."); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - return; - } + // Never executing. Kill it. + if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated."); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + return; + } - // Read sensitivitues - m_itemCombo = false; - m_itemSequent = false; + // Read sensitivitues + m_itemCombo = false; + m_itemSequent = false; iterateAndNextNull(oldsensesp); - bool combo = m_itemCombo; - bool sequent = m_itemSequent; + bool combo = m_itemCombo; + bool sequent = m_itemSequent; - if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*) - if (combo && sequent) { - if (!v3Global.opt.bboxUnsup()) { - nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list"); - } - sequent = false; - } + if (!combo && !sequent) combo=true; // If no list, Verilog 2000: always @ (*) + if (combo && sequent) { + if (!v3Global.opt.bboxUnsup()) { + nodep->v3error("Unsupported: Mixed edge (pos/negedge) and activity (no edge) sensitive activity list"); + } + sequent = false; + } - AstActive* wantactivep = NULL; - if (combo && !sequent) { - // Combo: Relink to ACTIVE(combo) - wantactivep = m_namer.getCActive(nodep->fileline()); - } else { - // Sequential: Build a ACTIVE(name) - // OPTIMIZE: We could substitute a constant for things in the sense list, for example - // always (posedge RESET) { if (RESET).... } we know RESET is true. - // Summarize a long list of combo inputs as just "combo" -#ifndef __COVERITY__ // Else dead code on next line. - if (combo) oldsensesp->addSensesp - (new AstSenItem(nodep->fileline(),AstSenItem::Combo())); + AstActive* wantactivep = NULL; + if (combo && !sequent) { + // Combo: Relink to ACTIVE(combo) + wantactivep = m_namer.getCActive(nodep->fileline()); + } else { + // Sequential: Build a ACTIVE(name) + // OPTIMIZE: We could substitute a constant for things in the sense list, for example + // always (posedge RESET) { if (RESET).... } we know RESET is true. + // Summarize a long list of combo inputs as just "combo" +#ifndef __COVERITY__ // Else dead code on next line. + if (combo) oldsensesp->addSensesp + (new AstSenItem(nodep->fileline(), AstSenItem::Combo())); #endif - wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp); - } + wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp); + } - // Delete sensitivity list - if (oldsensesp) { - oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp); - } + // Delete sensitivity list + if (oldsensesp) { + oldsensesp->unlinkFrBackWithNext()->deleteTree(); VL_DANGLING(oldsensesp); + } - // Move node to new active - nodep->unlinkFrBack(); - wantactivep->addStmtsp(nodep); + // Move node to new active + nodep->unlinkFrBack(); + wantactivep->addStmtsp(nodep); - // Warn and/or convert any delayed assignments - if (combo && !sequent) { - if (kwd == VAlwaysKwd::ALWAYS_LATCH) { - ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH); - } else { - ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO); - } - } - else if (!combo && sequent) { - ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ); - } + // Warn and/or convert any delayed assignments + if (combo && !sequent) { + if (kwd == VAlwaysKwd::ALWAYS_LATCH) { + ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_LATCH); + } else { + ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_COMBO); + } + } + else if (!combo && sequent) { + ActiveDlyVisitor dlyvisitor (nodep, ActiveDlyVisitor::CT_SEQ); + } } virtual void visit(AstAlways* nodep) { - // Move always to appropriate ACTIVE based on its sense list - UINFO(4," ALW "<=9) nodep->dumpTree(cout," Alw: "); + // Move always to appropriate ACTIVE based on its sense list + UINFO(4," ALW "<=9) nodep->dumpTree(cout, " Alw: "); - if (!nodep->bodysp()) { - // Empty always. Kill it. - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - return; - } - visitAlways(nodep, nodep->sensesp(), nodep->keyword()); + if (!nodep->bodysp()) { + // Empty always. Kill it. + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + return; + } + visitAlways(nodep, nodep->sensesp(), nodep->keyword()); } virtual void visit(AstAlwaysPublic* nodep) { - // Move always to appropriate ACTIVE based on its sense list - UINFO(4," ALWPub "<=9) nodep->dumpTree(cout," Alw: "); - visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS); + // Move always to appropriate ACTIVE based on its sense list + UINFO(4," ALWPub "<=9) nodep->dumpTree(cout, " Alw: "); + visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS); } virtual void visit(AstSenGate* nodep) { - AstSenItem* subitemp = nodep->sensesp(); - if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE - && subitemp->edgeType() != AstEdgeType::ET_POSEDGE - && subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) { - nodep->v3fatalSrc("Strange activity type under SenGate"); - } + AstSenItem* subitemp = nodep->sensesp(); + if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE + && subitemp->edgeType() != AstEdgeType::ET_POSEDGE + && subitemp->edgeType() != AstEdgeType::ET_NEGEDGE) { + nodep->v3fatalSrc("Strange activity type under SenGate"); + } iterateChildren(nodep); } virtual void visit(AstSenItem* nodep) { - if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) { - m_itemCombo = true; - // Delete the sensitivity - // We'll add it as a generic COMBO SenItem in a moment. - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } else if (nodep->varrefp()) { - // V3LinkResolve should have cleaned most of these up - if (!nodep->varrefp()->width1()) nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: " - <varrefp()->prettyName()); - m_itemSequent = true; - nodep->varrefp()->varp()->usedClock(true); - } + if (nodep->edgeType() == AstEdgeType::ET_ANYEDGE) { + m_itemCombo = true; + // Delete the sensitivity + // We'll add it as a generic COMBO SenItem in a moment. + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } else if (nodep->varrefp()) { + // V3LinkResolve should have cleaned most of these up + if (!nodep->varrefp()->width1()) { + nodep->v3error("Unsupported: Non-single bit wide signal pos/negedge sensitivity: " + <varrefp()->prettyName()); + } + m_itemSequent = true; + nodep->varrefp()->varp()->usedClock(true); + } } // Empty visitors, speed things up @@ -418,9 +425,9 @@ private: public: // CONSTUCTORS explicit ActiveVisitor(AstNetlist* nodep) { - m_scopeFinalp = NULL; - m_itemCombo = false; - m_itemSequent = false; + m_scopeFinalp = NULL; + m_itemCombo = false; + m_itemSequent = false; iterate(nodep); } virtual ~ActiveVisitor() {} diff --git a/src/V3Active.h b/src/V3Active.h index 5277c898e..c17223b00 100644 --- a/src/V3Active.h +++ b/src/V3Active.h @@ -34,4 +34,4 @@ public: static void activeAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3ActiveTop.cpp b/src/V3ActiveTop.cpp index 5a9e5923b..828c9fd64 100644 --- a/src/V3ActiveTop.cpp +++ b/src/V3ActiveTop.cpp @@ -21,9 +21,9 @@ // // Note this can be called multiple times. // Across all ACTIVES -// SenTrees are now under each ACTIVE statement, we want them global: -// Find SenTree in under global TopScope, or create it there -// Move SenTree the global SenTree +// SenTrees are now under each ACTIVE statement, we want them global: +// Find SenTree in under global TopScope, or create it there +// Move SenTree the global SenTree // //************************************************************************* @@ -43,91 +43,95 @@ class ActiveTopVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist - // AstNode::user() bool. True if processed + // AstNode::user() bool. True if processed // Each call to V3Const::constify - // AstNode::user4() Used by V3Const::constify, called below - AstUser1InUse m_inuser1; + // AstNode::user4() Used by V3Const::constify, called below + AstUser1InUse m_inuser1; // STATE - AstTopScope* m_topscopep; // Top scope for adding sentrees under - SenTreeFinder m_finder; // Find global sentree's and add them + AstTopScope* m_topscopep; // Top scope for adding sentrees under + SenTreeFinder m_finder; // Find global sentree's and add them // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstTopScope* nodep) { - m_topscopep = nodep; - m_finder.main(m_topscopep); + m_topscopep = nodep; + m_finder.main(m_topscopep); iterateChildren(nodep); - m_topscopep = NULL; + m_topscopep = NULL; } virtual void visit(AstNodeModule* nodep) { - // Create required actives and add to module - // We can start ordering at a module, or a scope - UINFO(4," MOD "<sensesp(); - if (!sensesp) nodep->v3fatalSrc("NULL"); - if (sensesp->sensesp() + UINFO(4," ACTIVE "<sensesp(); + if (!sensesp) nodep->v3fatalSrc("NULL"); + if (sensesp->sensesp() && VN_IS(sensesp->sensesp(), SenItem) && VN_CAST(sensesp->sensesp(), SenItem)->isNever()) { - // Never executing. Kill it. - if (sensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated."); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - return; - } - // Copy combo tree to settlement tree with duplicated statements - if (sensesp->hasCombo()) { - AstSenTree* newsentreep + // Never executing. Kill it. + if (sensesp->sensesp()->nextp()) { + nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated."); + } + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + return; + } + // Copy combo tree to settlement tree with duplicated statements + if (sensesp->hasCombo()) { + AstSenTree* newsentreep = new AstSenTree(nodep->fileline(), - new AstSenItem (nodep->fileline(), AstSenItem::Settle())); - AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep); - newp->sensesStorep(newsentreep); - if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true)); - nodep->addNextHere(newp); - } - // Move the SENTREE for each active up to the global level. - // This way we'll easily see what clock domains are identical - AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp); - UINFO(4," lookdone\n"); - if (wantp != sensesp) { - // Move the active's contents to the other active - UINFO(4," merge active "<sensesStorep()) { - if (sensesp != nodep->sensesStorep()) nodep->v3fatalSrc("sensesStore should have been deleted earlier if different"); - sensesp->unlinkFrBack(); - // There may be other references to same sense tree, - // we'll be removing all references when we get to them, - // but don't dangle our pointer yet! - pushDeletep(sensesp); - } - nodep->sensesp(wantp); - } - // No need to do statements under it, they're already moved. + new AstSenItem(nodep->fileline(), AstSenItem::Settle())); + AstActive* newp = new AstActive(nodep->fileline(),"settle", newsentreep); + newp->sensesStorep(newsentreep); + if (nodep->stmtsp()) newp->addStmtsp(nodep->stmtsp()->cloneTree(true)); + nodep->addNextHere(newp); + } + // Move the SENTREE for each active up to the global level. + // This way we'll easily see what clock domains are identical + AstSenTree* wantp = m_finder.getSenTree(nodep->fileline(), sensesp); + UINFO(4," lookdone\n"); + if (wantp != sensesp) { + // Move the active's contents to the other active + UINFO(4," merge active "<sensesStorep()) { + if (sensesp != nodep->sensesStorep()) { + nodep->v3fatalSrc("sensesStore should have been deleted earlier if different"); + } + sensesp->unlinkFrBack(); + // There may be other references to same sense tree, + // we'll be removing all references when we get to them, + // but don't dangle our pointer yet! + pushDeletep(sensesp); + } + nodep->sensesp(wantp); + } + // No need to do statements under it, they're already moved. //iterateChildren(nodep); } virtual void visit(AstInitial* nodep) { - nodep->v3fatalSrc("Node should have been under ACTIVE"); + nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstAssignAlias* nodep) { - nodep->v3fatalSrc("Node should have been under ACTIVE"); + nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstAssignW* nodep) { - nodep->v3fatalSrc("Node should have been under ACTIVE"); + nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstAlways* nodep) { - nodep->v3fatalSrc("Node should have been under ACTIVE"); + nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstAlwaysPublic* nodep) { - nodep->v3fatalSrc("Node should have been under ACTIVE"); + nodep->v3fatalSrc("Node should have been under ACTIVE"); } virtual void visit(AstFinal* nodep) { - nodep->v3fatalSrc("Node should have been deleted"); + nodep->v3fatalSrc("Node should have been deleted"); } // Empty visitors, speed things up virtual void visit(AstNodeMath* nodep) {} @@ -139,7 +143,7 @@ private: public: // CONSTUCTORS explicit ActiveTopVisitor(AstNetlist* nodep) { - m_topscopep = NULL; + m_topscopep = NULL; iterate(nodep); } virtual ~ActiveTopVisitor() {} diff --git a/src/V3ActiveTop.h b/src/V3ActiveTop.h index 0452c10f9..aee8d84af 100644 --- a/src/V3ActiveTop.h +++ b/src/V3ActiveTop.h @@ -34,4 +34,4 @@ public: static void activeTopAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index f106e4900..543c2c90a 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -37,46 +37,46 @@ class AssertVisitor : public AstNVisitor { private: // NODE STATE/TYPES // Cleared on netlist - // AstNode::user() -> bool. True if processed - AstUser1InUse m_inuser1; + // AstNode::user() -> bool. True if processed + AstUser1InUse m_inuser1; // STATE - AstNodeModule* m_modp; // Last module - AstBegin* m_beginp; // Last begin + AstNodeModule* m_modp; // Last module + AstBegin* m_beginp; // Last begin unsigned m_modPastNum; // Module past numbering - V3Double0 m_statAsCover; // Statistic tracking - V3Double0 m_statAsPsl; // Statistic tracking - V3Double0 m_statAsFull; // Statistic tracking - V3Double0 m_statAsSV; // Statistic tracking + V3Double0 m_statAsCover; // Statistic tracking + V3Double0 m_statAsPsl; // Statistic tracking + V3Double0 m_statAsFull; // Statistic tracking + V3Double0 m_statAsSV; // Statistic tracking // METHODS string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) { - return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename() - +":"+cvtToStr(nodep->fileline()->lineno()) - +": Assertion failed in %m" - +((message != "")?": ":"")+message - +"\n"); + return (string("[%0t] "+prefix+": ")+nodep->fileline()->filebasename() + +":"+cvtToStr(nodep->fileline()->lineno()) + +": Assertion failed in %m" + +((message != "")?": ":"")+message + +"\n"); } void replaceDisplay(AstDisplay* nodep, const string& prefix) { - nodep->displayType(AstDisplayType::DT_WRITE); - nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text())); - // cppcheck-suppress nullPointer - AstNode* timenewp = new AstTime(nodep->fileline()); - if (AstNode* timesp = nodep->fmtp()->exprsp()) { - timesp->unlinkFrBackWithNext(); - timenewp->addNext(timesp); - } - nodep->fmtp()->addExprsp(timenewp); - if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) { - nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline())); - } + nodep->displayType(AstDisplayType::DT_WRITE); + nodep->fmtp()->text(assertDisplayMessage(nodep, prefix, nodep->fmtp()->text())); + // cppcheck-suppress nullPointer + AstNode* timenewp = new AstTime(nodep->fileline()); + if (AstNode* timesp = nodep->fmtp()->exprsp()) { + timesp->unlinkFrBackWithNext(); + timenewp->addNext(timesp); + } + nodep->fmtp()->addExprsp(timenewp); + if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) { + nodep->fmtp()->scopeNamep(new AstScopeName(nodep->fileline())); + } } AstNode* newIfAssertOn(AstNode* nodep) { - // Add a internal if to check assertions are on. - // Don't make this a AND term, as it's unlikely to need to test this. + // Add a internal if to check assertions are on. + // Don't make this a AND term, as it's unlikely to need to test this. FileLine* fl = nodep->fileline(); - AstNode* newp + AstNode* newp = new AstIf(fl, // If assertions are off, have constant propagation rip them out later // This allows syntax errors and such to be detected normally. @@ -84,46 +84,47 @@ private: ? static_cast(new AstCMath(fl, "Verilated::assertOn()", 1)) : static_cast(new AstConst(fl, AstConst::LogicFalse()))), nodep, NULL); - newp->user1(true); // Don't assert/cover this if - return newp; + newp->user1(true); // Don't assert/cover this if + return newp; } AstNode* newFireAssertUnchecked(AstNode* nodep, const string& message) { // Like newFireAssert() but omits the asserts-on check - AstDisplay* dispp = new AstDisplay(nodep->fileline(), AstDisplayType::DT_ERROR, message, NULL, NULL); - AstNode* bodysp = dispp; - replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format + AstDisplay* dispp = new AstDisplay(nodep->fileline(), + AstDisplayType::DT_ERROR, message, NULL, NULL); + AstNode* bodysp = dispp; + replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format bodysp->addNext(new AstStop(nodep->fileline())); return bodysp; } AstNode* newFireAssert(AstNode* nodep, const string& message) { AstNode* bodysp = newFireAssertUnchecked(nodep, message); - bodysp = newIfAssertOn(bodysp); - return bodysp; + bodysp = newIfAssertOn(bodysp); + return bodysp; } void newPslAssertion(AstNode* nodep, AstNode* propp, AstSenTree* sentreep, - AstNode* stmtsp, const string& message) { - propp->unlinkFrBack(); - sentreep->unlinkFrBack(); - if (stmtsp) stmtsp->unlinkFrBack(); - // - AstNode* bodysp = NULL; - bool selfDestruct = false; + AstNode* stmtsp, const string& message) { + propp->unlinkFrBack(); + sentreep->unlinkFrBack(); + if (stmtsp) stmtsp->unlinkFrBack(); + // + AstNode* bodysp = NULL; + bool selfDestruct = false; AstIf* ifp = NULL; if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) { ++m_statAsCover; - if (!v3Global.opt.coverageUser()) { - selfDestruct = true; - } else { - // V3Coverage assigned us a bucket to increment. + if (!v3Global.opt.coverageUser()) { + selfDestruct = true; + } else { + // V3Coverage assigned us a bucket to increment. AstCoverInc* covincp = VN_CAST(snodep->coverincp(), CoverInc); - if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion"); - covincp->unlinkFrBack(); - if (message!="") covincp->declp()->comment(message); - bodysp = covincp; - } + if (!covincp) snodep->v3fatalSrc("Missing AstCoverInc under assertion"); + covincp->unlinkFrBack(); + if (message!="") covincp->declp()->comment(message); + bodysp = covincp; + } if (bodysp && stmtsp) bodysp = bodysp->addNext(stmtsp); ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL); @@ -148,163 +149,167 @@ private: // IEEE says simulator ignores these pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); return; - } else { - nodep->v3fatalSrc("Unknown node type"); - } + } else { + nodep->v3fatalSrc("Unknown node type"); + } AstNode* newp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp); - // Install it - if (selfDestruct) { - // Delete it after making the tree. This way we can tell the user - // if it wasn't constructed nicely or has other errors without needing --coverage. - newp->deleteTree(); - nodep->unlinkFrBack(); - } else { - nodep->replaceWith(newp); - } - // Bye - pushDeletep(nodep); VL_DANGLING(nodep); + // Install it + if (selfDestruct) { + // Delete it after making the tree. This way we can tell the user + // if it wasn't constructed nicely or has other errors without needing --coverage. + newp->deleteTree(); + nodep->unlinkFrBack(); + } else { + nodep->replaceWith(newp); + } + // Bye + pushDeletep(nodep); VL_DANGLING(nodep); } void newVAssertion(AstVAssert* nodep, AstNode* propp) { - propp->unlinkFrBackWithNext(); - AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext(); - AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext(); - // + propp->unlinkFrBackWithNext(); + AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext(); + AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext(); + // if (VN_IS(nodep, VAssert)) { - if (passsp) passsp = newIfAssertOn(passsp); - if (failsp) failsp = newIfAssertOn(failsp); - } else { - nodep->v3fatalSrc("Unknown node type"); - } + if (passsp) passsp = newIfAssertOn(passsp); + if (failsp) failsp = newIfAssertOn(failsp); + } else { + nodep->v3fatalSrc("Unknown node type"); + } AstIf* ifp = new AstIf(nodep->fileline(), propp, passsp, failsp); - AstNode* newp = ifp; + AstNode* newp = ifp; if (VN_IS(nodep, VAssert)) ifp->branchPred(AstBranchPred::BP_UNLIKELY); - // - // Install it - nodep->replaceWith(newp); - // Bye - pushDeletep(nodep); VL_DANGLING(nodep); + // + // Install it + nodep->replaceWith(newp); + // Bye + pushDeletep(nodep); VL_DANGLING(nodep); } // VISITORS virtual void visit(AstIf* nodep) { - if (nodep->user1SetOnce()) return; - if (nodep->uniquePragma() || nodep->unique0Pragma()) { - AstNodeIf* ifp = nodep; - AstNode* propp = NULL; - bool hasDefaultElse = false; - do { - // If this statement ends with 'else if', then nextIf will point to the - // nextIf statement. Otherwise it will be null. - AstNodeIf* nextifp = dynamic_cast(ifp->elsesp()); + if (nodep->user1SetOnce()) return; + if (nodep->uniquePragma() || nodep->unique0Pragma()) { + AstNodeIf* ifp = nodep; + AstNode* propp = NULL; + bool hasDefaultElse = false; + do { + // If this statement ends with 'else if', then nextIf will point to the + // nextIf statement. Otherwise it will be null. + AstNodeIf* nextifp = dynamic_cast(ifp->elsesp()); iterateAndNextNull(ifp->condp()); - // Recurse into the true case. + // Recurse into the true case. iterateAndNextNull(ifp->ifsp()); - // If the last else is not an else if, recurse into that too. - if (ifp->elsesp() && !nextifp) { + // If the last else is not an else if, recurse into that too. + if (ifp->elsesp() && !nextifp) { iterateAndNextNull(ifp->elsesp()); - } + } - // Build a bitmask of the true predicates - AstNode* predp = ifp->condp()->cloneTree(false); - if (propp) { - propp = new AstConcat(nodep->fileline(), predp, propp); - } else { - propp = predp; - } + // Build a bitmask of the true predicates + AstNode* predp = ifp->condp()->cloneTree(false); + if (propp) { + propp = new AstConcat(nodep->fileline(), predp, propp); + } else { + propp = predp; + } - // Record if this ends with an 'else' that does not have an if - if (ifp->elsesp() && !nextifp) { - hasDefaultElse = true; - } + // Record if this ends with an 'else' that does not have an if + if (ifp->elsesp() && !nextifp) { + hasDefaultElse = true; + } - ifp = nextifp; - } while (ifp); + ifp = nextifp; + } while (ifp); - AstNode *newifp = nodep->cloneTree(false); - bool allow_none = nodep->unique0Pragma(); + AstNode *newifp = nodep->cloneTree(false); + bool allow_none = nodep->unique0Pragma(); - // Empty case means no property - if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse()); + // Empty case means no property + if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse()); - // Note: if this ends with an 'else', then we don't need to validate that one of the - // predicates evaluates to true. - AstNode* ohot = ((allow_none || hasDefaultElse) - ? static_cast(new AstOneHot0(nodep->fileline(), propp)) - : static_cast(new AstOneHot (nodep->fileline(), propp))); + // Note: if this ends with an 'else', then we don't need to validate that one of the + // predicates evaluates to true. + AstNode* ohot = ((allow_none || hasDefaultElse) + ? static_cast(new AstOneHot0(nodep->fileline(), propp)) + : static_cast(new AstOneHot(nodep->fileline(), propp))); AstIf* checkifp = new AstIf(nodep->fileline(), new AstLogNot(nodep->fileline(), ohot), newFireAssert(nodep, "'unique if' statement violated"), newifp); - checkifp->branchPred(AstBranchPred::BP_UNLIKELY); - nodep->replaceWith(checkifp); - pushDeletep(nodep); - } else { + checkifp->branchPred(AstBranchPred::BP_UNLIKELY); + nodep->replaceWith(checkifp); + pushDeletep(nodep); + } else { iterateChildren(nodep); - } + } } //========== Case assertions virtual void visit(AstCase* nodep) { iterateChildren(nodep); - if (!nodep->user1SetOnce()) { - bool has_default=false; - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - if (itemp->isDefault()) has_default=true; - } - if (nodep->fullPragma() || nodep->priorityPragma()) { - // Simply need to add a default if there isn't one already - ++m_statAsFull; - if (!has_default) { - nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/, - newFireAssert(nodep, "synthesis full_case, but non-match found"))); - } - } - if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) { - // Need to check that one, and only one of the case items match at any moment - // If there's a default, we allow none to match, else exactly one must match - ++m_statAsFull; - if (!has_default && !nodep->itemsp()) { - // Not parallel, but harmlessly so. - } else { - AstNode* propp = NULL; - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) { - AstNode* onep; - if (nodep->casex() || nodep->casez() || nodep->caseInside()) { - onep = AstEqWild::newTyped(itemp->fileline(), - nodep->exprp()->cloneTree(false), - icondp->cloneTree(false)); - } else { - onep = AstEq::newTyped(icondp->fileline(), - nodep->exprp()->cloneTree(false), - icondp->cloneTree(false)); - } - if (propp) propp = new AstConcat(icondp->fileline(), onep, propp); - else propp = onep; - } - } - // Empty case means no property - if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse()); + if (!nodep->user1SetOnce()) { + bool has_default = false; + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) { + if (itemp->isDefault()) has_default = true; + } + if (nodep->fullPragma() || nodep->priorityPragma()) { + // Simply need to add a default if there isn't one already + ++m_statAsFull; + if (!has_default) { + nodep->addItemsp(new AstCaseItem(nodep->fileline(), NULL/*DEFAULT*/, + newFireAssert(nodep, "synthesis full_case, but non-match found"))); + } + } + if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) { + // Need to check that one, and only one of the case items match at any moment + // If there's a default, we allow none to match, else exactly one must match + ++m_statAsFull; + if (!has_default && !nodep->itemsp()) { + // Not parallel, but harmlessly so. + } else { + AstNode* propp = NULL; + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { + for (AstNode* icondp = itemp->condsp(); + icondp!=NULL; icondp=icondp->nextp()) { + AstNode* onep; + if (nodep->casex() || nodep->casez() || nodep->caseInside()) { + onep = AstEqWild::newTyped(itemp->fileline(), + nodep->exprp()->cloneTree(false), + icondp->cloneTree(false)); + } else { + onep = AstEq::newTyped(icondp->fileline(), + nodep->exprp()->cloneTree(false), + icondp->cloneTree(false)); + } + if (propp) propp = new AstConcat(icondp->fileline(), onep, propp); + else propp = onep; + } + } + // Empty case means no property + if (!propp) propp = new AstConst(nodep->fileline(), AstConst::LogicFalse()); - bool allow_none = has_default || nodep->unique0Pragma(); - AstNode* ohot = (allow_none - ? static_cast(new AstOneHot0(nodep->fileline(), propp)) - : static_cast(new AstOneHot (nodep->fileline(), propp))); + bool allow_none = has_default || nodep->unique0Pragma(); + AstNode* ohot + = (allow_none + ? static_cast(new AstOneHot0(nodep->fileline(), propp)) + : static_cast(new AstOneHot(nodep->fileline(), propp))); AstIf* ifp = new AstIf(nodep->fileline(), new AstLogNot(nodep->fileline(), ohot), newFireAssert(nodep, "synthesis parallel_case, but multiple matches found"), NULL); - ifp->branchPred(AstBranchPred::BP_UNLIKELY); - nodep->addNotParallelp(ifp); - } - } - } + ifp->branchPred(AstBranchPred::BP_UNLIKELY); + nodep->addNotParallelp(ifp); + } + } + } } //========== Past @@ -341,46 +346,46 @@ private: //========== Statements virtual void visit(AstDisplay* nodep) { iterateChildren(nodep); - // Replace the special types with standard text - if (nodep->displayType()==AstDisplayType::DT_INFO) { - replaceDisplay(nodep, "-Info"); - } else if (nodep->displayType()==AstDisplayType::DT_WARNING) { - replaceDisplay(nodep, "%%Warning"); - } else if (nodep->displayType()==AstDisplayType::DT_ERROR - || nodep->displayType()==AstDisplayType::DT_FATAL) { - replaceDisplay(nodep, "%%Error"); - } + // Replace the special types with standard text + if (nodep->displayType()==AstDisplayType::DT_INFO) { + replaceDisplay(nodep, "-Info"); + } else if (nodep->displayType()==AstDisplayType::DT_WARNING) { + replaceDisplay(nodep, "%%Warning"); + } else if (nodep->displayType()==AstDisplayType::DT_ERROR + || nodep->displayType()==AstDisplayType::DT_FATAL) { + replaceDisplay(nodep, "%%Error"); + } } virtual void visit(AstNodePslCoverOrAssert* nodep) { iterateChildren(nodep); - if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name()); - newPslAssertion(nodep, nodep->propp(), nodep->sentreep(), - nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep); + if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name()); + newPslAssertion(nodep, nodep->propp(), nodep->sentreep(), + nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep); } virtual void visit(AstVAssert* nodep) { iterateChildren(nodep); - newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep); - ++m_statAsSV; + newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep); + ++m_statAsSV; } virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; + m_modp = nodep; m_modPastNum = 0; - // + // iterateChildren(nodep); - // Reset defaults - m_modp = NULL; + // Reset defaults + m_modp = NULL; } virtual void visit(AstBegin* nodep) { - // This code is needed rather than a visitor in V3Begin, - // because V3Assert is called before V3Begin - AstBegin* lastp = m_beginp; - { - m_beginp = nodep; + // This code is needed rather than a visitor in V3Begin, + // because V3Assert is called before V3Begin + AstBegin* lastp = m_beginp; + { + m_beginp = nodep; iterateChildren(nodep); - } - m_beginp = lastp; + } + m_beginp = lastp; } virtual void visit(AstNode* nodep) { @@ -389,17 +394,17 @@ private: public: // CONSTRUCTORS explicit AssertVisitor(AstNetlist* nodep) { - m_beginp = NULL; - m_modp = NULL; + m_beginp = NULL; + m_modp = NULL; m_modPastNum = 0; - // Process + // Process iterate(nodep); } virtual ~AssertVisitor() { - V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl); - V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV); - V3Stats::addStat("Assertions, cover statements", m_statAsCover); - V3Stats::addStat("Assertions, full/parallel case", m_statAsFull); + V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl); + V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV); + V3Stats::addStat("Assertions, cover statements", m_statAsCover); + V3Stats::addStat("Assertions, full/parallel case", m_statAsFull); } }; diff --git a/src/V3Assert.h b/src/V3Assert.h index 4eb9c7ab1..571136fb3 100644 --- a/src/V3Assert.h +++ b/src/V3Assert.h @@ -34,4 +34,4 @@ public: static void assertAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index a05c97815..22bf7a3b0 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -18,7 +18,7 @@ // //************************************************************************* // Pre steps: -// Attach clocks to each assertion +// Attach clocks to each assertion //************************************************************************* #include "config_build.h" @@ -51,37 +51,37 @@ private: VL_DEBUG_FUNC; // Declare debug() AstSenTree* newSenTree(AstNode* nodep) { - // Create sentree based on clocked or default clock - // Return NULL for always - AstSenTree* newp = NULL; + // Create sentree based on clocked or default clock + // Return NULL for always + AstSenTree* newp = NULL; AstNodeSenItem* senip = m_senip; if (!senip) senip = m_seniDefaultp; if (!senip) senip = m_seniAlwaysp; - if (!senip) { - nodep->v3error("Unsupported: Unclocked assertion"); - newp = new AstSenTree(nodep->fileline(), NULL); - } else { - newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true)); - } - return newp; + if (!senip) { + nodep->v3error("Unsupported: Unclocked assertion"); + newp = new AstSenTree(nodep->fileline(), NULL); + } else { + newp = new AstSenTree(nodep->fileline(), senip->cloneTree(true)); + } + return newp; } void clearAssertInfo() { - m_senip = NULL; + m_senip = NULL; } // VISITORS //========== Statements virtual void visit(AstClocking* nodep) { - UINFO(8," CLOCKING"<sensesp(); - // Trash it, keeping children - if (nodep->bodysp()) { - nodep->replaceWith(nodep->bodysp()->unlinkFrBack()); - } else { - nodep->unlinkFrBack(); - } - pushDeletep(nodep); VL_DANGLING(nodep); + UINFO(8," CLOCKING"<sensesp(); + // Trash it, keeping children + if (nodep->bodysp()) { + nodep->replaceWith(nodep->bodysp()->unlinkFrBack()); + } else { + nodep->unlinkFrBack(); + } + pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstAlways* nodep) { iterateAndNextNull(nodep->sensesp()); @@ -93,12 +93,12 @@ private: } virtual void visit(AstNodePslCoverOrAssert* nodep) { - if (nodep->sentreep()) return; // Already processed - clearAssertInfo(); + if (nodep->sentreep()) return; // Already processed + clearAssertInfo(); // Find PslClocking's burried under nodep->exprsp iterateChildren(nodep); - nodep->sentreep(newSenTree(nodep)); - clearAssertInfo(); + nodep->sentreep(newSenTree(nodep)); + clearAssertInfo(); } virtual void visit(AstPast* nodep) { if (nodep->sentreep()) return; // Already processed @@ -108,26 +108,26 @@ private: virtual void visit(AstPslClocked* nodep) { // No need to iterate the body, once replace will get iterated iterateAndNextNull(nodep->sensesp()); - if (m_senip) { - nodep->v3error("Unsupported: Only one PSL clock allowed per assertion"); - } - // Block is the new expression to evaluate - AstNode* blockp = nodep->propp()->unlinkFrBack(); - if (nodep->disablep()) { - blockp = new AstAnd(nodep->disablep()->fileline(), - new AstNot(nodep->disablep()->fileline(), - nodep->disablep()->unlinkFrBack()), - blockp); - } - // Unlink and just keep a pointer to it, convert to sentree as needed - m_senip = nodep->sensesp(); - nodep->replaceWith(blockp); - pushDeletep(nodep); VL_DANGLING(nodep); + if (m_senip) { + nodep->v3error("Unsupported: Only one PSL clock allowed per assertion"); + } + // Block is the new expression to evaluate + AstNode* blockp = nodep->propp()->unlinkFrBack(); + if (nodep->disablep()) { + blockp = new AstAnd(nodep->disablep()->fileline(), + new AstNot(nodep->disablep()->fileline(), + nodep->disablep()->unlinkFrBack()), + blockp); + } + // Unlink and just keep a pointer to it, convert to sentree as needed + m_senip = nodep->sensesp(); + nodep->replaceWith(blockp); + pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstNodeModule* nodep) { iterateChildren(nodep); - // Reset defaults - m_seniDefaultp = NULL; + // Reset defaults + m_seniDefaultp = NULL; } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -136,10 +136,10 @@ private: public: // CONSTRUCTORS explicit AssertPreVisitor(AstNetlist* nodep) { - m_seniDefaultp = NULL; + m_seniDefaultp = NULL; m_seniAlwaysp = NULL; - clearAssertInfo(); - // Process + clearAssertInfo(); + // Process iterate(nodep); } virtual ~AssertPreVisitor() {} diff --git a/src/V3AssertPre.h b/src/V3AssertPre.h index 4aef21a34..a6babb840 100644 --- a/src/V3AssertPre.h +++ b/src/V3AssertPre.h @@ -34,4 +34,4 @@ public: static void assertPreAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 7dd16620f..41fb419b9 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -34,24 +34,24 @@ //====================================================================== // Statics -vluint64_t AstNode::s_editCntLast=0; -vluint64_t AstNode::s_editCntGbl=0; // Hot cache line +vluint64_t AstNode::s_editCntLast = 0; +vluint64_t AstNode::s_editCntGbl = 0; // Hot cache line // To allow for fast clearing of all user pointers, we keep a "timestamp" // along with each userp, and thus by bumping this count we can make it look // as if we iterated across the entire tree to set all the userp's to null. -int AstNode::s_cloneCntGbl=0; -uint32_t AstUser1InUse::s_userCntGbl=0; // Hot cache line, leave adjacent -uint32_t AstUser2InUse::s_userCntGbl=0; // Hot cache line, leave adjacent -uint32_t AstUser3InUse::s_userCntGbl=0; // Hot cache line, leave adjacent -uint32_t AstUser4InUse::s_userCntGbl=0; // Hot cache line, leave adjacent -uint32_t AstUser5InUse::s_userCntGbl=0; // Hot cache line, leave adjacent +int AstNode::s_cloneCntGbl = 0; +uint32_t AstUser1InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent +uint32_t AstUser2InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent +uint32_t AstUser3InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent +uint32_t AstUser4InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent +uint32_t AstUser5InUse::s_userCntGbl = 0; // Hot cache line, leave adjacent -bool AstUser1InUse::s_userBusy=false; -bool AstUser2InUse::s_userBusy=false; -bool AstUser3InUse::s_userBusy=false; -bool AstUser4InUse::s_userBusy=false; -bool AstUser5InUse::s_userBusy=false; +bool AstUser1InUse::s_userBusy = false; +bool AstUser2InUse::s_userBusy = false; +bool AstUser3InUse::s_userBusy = false; +bool AstUser4InUse::s_userBusy = false; +bool AstUser5InUse::s_userBusy = false; int AstNodeDType::s_uniqueNum = 0; @@ -69,7 +69,7 @@ void AstNode::init() { m_fileline = NULL; m_nextp = NULL; m_backp = NULL; - m_headtailp = this; // When made, we're a list of only a single element + m_headtailp = this; // When made, we're a list of only a single element m_op1p = NULL; m_op2p = NULL; m_op3p = NULL; @@ -97,25 +97,25 @@ string AstNode::encodeName(const string& namein) { // Encode signal name raw from parser, then not called again on same signal string out; for (string::const_iterator pos = namein.begin(); pos!=namein.end(); ++pos) { - if ((pos==namein.begin()) ? isalpha(pos[0]) // digits can't lead identifiers - : isalnum(pos[0])) { - out += pos[0]; - } else if (pos[0]=='_') { - if (pos[1]=='_') { - out += "_"; out += "__05F"; // hex(_) = 0x5F - ++pos; - if (pos==namein.end()) break; - } else { - out += pos[0]; - } - } else { - // Need the leading 0 so this will never collide with - // a user identifier nor a temp we create in Verilator. - // We also do *NOT* use __DOT__ etc, as we search for those - // in some replacements, and don't want to mangle the user's names. - char hex[10]; sprintf(hex,"__0%02X",pos[0]); - out += hex; - } + if ((pos==namein.begin()) ? isalpha(pos[0]) // digits can't lead identifiers + : isalnum(pos[0])) { + out += pos[0]; + } else if (pos[0]=='_') { + if (pos[1]=='_') { + out += "_"; out += "__05F"; // hex(_) = 0x5F + ++pos; + if (pos==namein.end()) break; + } else { + out += pos[0]; + } + } else { + // Need the leading 0 so this will never collide with + // a user identifier nor a temp we create in Verilator. + // We also do *NOT* use __DOT__ etc, as we search for those + // in some replacements, and don't want to mangle the user's names. + char hex[10]; sprintf(hex, "__0%02X", pos[0]); + out += hex; + } } // Shorten names // TODO long term use VName in place of "string name" @@ -126,17 +126,17 @@ string AstNode::encodeName(const string& namein) { string AstNode::encodeNumber(vlsint64_t num) { if (num < 0) { - return "__02D"+cvtToStr(-num); // 2D=- + return "__02D"+cvtToStr(-num); // 2D=- } else { - return cvtToStr(num); + return cvtToStr(num); } } string AstNode::shortName() const { string pretty = name(); string::size_type pos; - while ((pos=pretty.find("__PVT__")) != string::npos) { - pretty.replace(pos, 7, ""); + while ((pos = pretty.find("__PVT__")) != string::npos) { + pretty.replace(pos, 7, ""); } return pretty; } @@ -144,10 +144,10 @@ string AstNode::shortName() const { string AstNode::dedotName(const string& namein) { string pretty = namein; string::size_type pos; - while ((pos=pretty.find("__DOT__")) != string::npos) { - pretty.replace(pos, 7, "."); + while ((pos = pretty.find("__DOT__")) != string::npos) { + pretty.replace(pos, 7, "."); } - if (pretty.substr(0,4) == "TOP.") pretty.replace(0,4,""); + if (pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, ""); return pretty; } @@ -156,11 +156,11 @@ string AstNode::vcdName(const string& namein) { // Dots are reserved for dots the user put in the name string pretty = namein; string::size_type pos; - while ((pos=pretty.find("__DOT__")) != string::npos) { - pretty.replace(pos, 7, " "); + while ((pos = pretty.find("__DOT__")) != string::npos) { + pretty.replace(pos, 7, " "); } while ((pos = pretty.find('.')) != string::npos) { - pretty.replace(pos, 1, " "); + pretty.replace(pos, 1, " "); } // Now convert escaped special characters, etc return prettyName(pretty); @@ -172,48 +172,48 @@ string AstNode::prettyName(const string& namein) { pretty = ""; pretty.reserve(namein.length()); for (const char* pos = namein.c_str(); *pos; ) { - if (pos[0]=='-' && pos[1]=='>') { // -> - pretty += "."; - pos += 2; - continue; - } - if (pos[0]=='_' && pos[1]=='_') { // Short-circuit - if (0==strncmp(pos,"__BRA__",7)) { - pretty += "["; - pos += 7; - continue; - } - if (0==strncmp(pos,"__KET__",7)) { - pretty += "]"; - pos += 7; - continue; - } - if (0==strncmp(pos,"__DOT__",7)) { - pretty += "."; - pos += 7; - continue; - } - if (0==strncmp(pos,"__PVT__",7)) { - pretty += ""; - pos += 7; - continue; - } - if (pos[0]=='_' && pos[1]=='_' && pos[2]=='0' - && isxdigit(pos[3]) && isxdigit(pos[4])) { - char value = 0; - value += 16*(isdigit(pos[3]) ? (pos[3]-'0') : (tolower(pos[3])-'a'+10)); - value += (isdigit(pos[4]) ? (pos[4]-'0') : (tolower(pos[4])-'a'+10)); - pretty += value; - pos += 5; - continue; - } - } - // Default - pretty += pos[0]; - ++pos; + if (pos[0]=='-' && pos[1]=='>') { // -> + pretty += "."; + pos += 2; + continue; + } + if (pos[0]=='_' && pos[1]=='_') { // Short-circuit + if (0==strncmp(pos, "__BRA__", 7)) { + pretty += "["; + pos += 7; + continue; + } + if (0==strncmp(pos, "__KET__", 7)) { + pretty += "]"; + pos += 7; + continue; + } + if (0==strncmp(pos, "__DOT__", 7)) { + pretty += "."; + pos += 7; + continue; + } + if (0==strncmp(pos, "__PVT__", 7)) { + pretty += ""; + pos += 7; + continue; + } + if (pos[0]=='_' && pos[1]=='_' && pos[2]=='0' + && isxdigit(pos[3]) && isxdigit(pos[4])) { + char value = 0; + value += 16*(isdigit(pos[3]) ? (pos[3]-'0') : (tolower(pos[3])-'a'+10)); + value += (isdigit(pos[4]) ? (pos[4]-'0') : (tolower(pos[4])-'a'+10)); + pretty += value; + pos += 5; + continue; + } + } + // Default + pretty += pos[0]; + ++pos; } - if (pretty[0]=='T' && pretty.substr(0,4) == "TOP.") pretty.replace(0,4,""); - if (pretty[0]=='T' && pretty.substr(0,5) == "TOP->") pretty.replace(0,5,""); + if (pretty[0]=='T' && pretty.substr(0, 4) == "TOP.") pretty.replace(0, 4, ""); + if (pretty[0]=='T' && pretty.substr(0, 5) == "TOP->") pretty.replace(0, 5, ""); return pretty; } @@ -233,15 +233,15 @@ inline void AstNode::debugTreeChange(const char* prefix, int lineno, bool next) //if (debug()) cout<<"-treeChange: V3Ast.cpp:"<"<dumpTree(cout,"-treeChange: "); - // if (next||1) this->dumpTreeAndNext(cout, prefix); - // else this->dumpTree(cout, prefix); - // this->checkTree(); - // v3Global.rootp()->checkTree(); + // cout<<"-treeChange: V3Ast.cpp:"<dumpTree(cout, "-treeChange: "); + // if (next||1) this->dumpTreeAndNext(cout, prefix); + // else this->dumpTree(cout, prefix); + // this->checkTree(); + // v3Global.rootp()->checkTree(); //} #endif } @@ -252,31 +252,31 @@ AstNode* AstNode::addNext(AstNode* nodep, AstNode* newp) { nodep->debugTreeChange("-addNextThs: ", __LINE__, false); newp->debugTreeChange("-addNextNew: ", __LINE__, true); if (!nodep) { // verilog.y and lots of other places assume this - return (newp); + return (newp); } else { - // Find end of old list - AstNode* oldtailp = nodep; - if (oldtailp->m_nextp) { - if (oldtailp->m_headtailp) { - oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end - UDEBUGONLY(if (oldtailp->m_nextp) nodep->v3fatalSrc("Node had next, but headtail says it shouldn't");); - } else { - // Though inefficent, we are occasionally passed a addNext in the middle of a list. - while (oldtailp->m_nextp != NULL) oldtailp = oldtailp->m_nextp; - } - } - // Link it in - oldtailp->m_nextp = newp; - newp->m_backp = oldtailp; - // New tail needs the head - AstNode* newtailp = newp->m_headtailp; - AstNode* headp = oldtailp->m_headtailp; - oldtailp->m_headtailp = NULL; // May be written again as new head - newp->m_headtailp = NULL; // May be written again as new tail - newtailp->m_headtailp = headp; - headp->m_headtailp = newtailp; - newp->editCountInc(); - if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item + // Find end of old list + AstNode* oldtailp = nodep; + if (oldtailp->m_nextp) { + if (oldtailp->m_headtailp) { + oldtailp = oldtailp->m_headtailp; // This=beginning of list, jump to end + UDEBUGONLY(if (oldtailp->m_nextp) nodep->v3fatalSrc("Node had next, but headtail says it shouldn't");); + } else { + // Though inefficent, we are occasionally passed a addNext in the middle of a list. + while (oldtailp->m_nextp != NULL) oldtailp = oldtailp->m_nextp; + } + } + // Link it in + oldtailp->m_nextp = newp; + newp->m_backp = oldtailp; + // New tail needs the head + AstNode* newtailp = newp->m_headtailp; + AstNode* headp = oldtailp->m_headtailp; + oldtailp->m_headtailp = NULL; // May be written again as new head + newp->m_headtailp = NULL; // May be written again as new tail + newtailp->m_headtailp = headp; + headp->m_headtailp = newtailp; + newp->editCountInc(); + if (oldtailp->m_iterpp) *(oldtailp->m_iterpp) = newp; // Iterate on new item } nodep->debugTreeChange("-addNextOut:", __LINE__, true); return nodep; @@ -291,19 +291,19 @@ void AstNode::addNextHere(AstNode* newp) { // Add to m_nextp on exact node passed, not at the end. // This could be at head, tail, or both (single) // New could be head of single node, or list - UASSERT(newp,"Null item passed to addNext"); + UASSERT(newp, "Null item passed to addNext"); UASSERT(!newp->backp(), "New node (back) already assigned?"); this->debugTreeChange("-addHereThs: ", __LINE__, false); newp->debugTreeChange("-addHereNew: ", __LINE__, true); newp->editCountInc(); - AstNode* addlastp = newp->m_headtailp; // Last node in list to be added + AstNode* addlastp = newp->m_headtailp; // Last node in list to be added UASSERT(!addlastp->m_nextp, "Headtailp tail isn't at the tail"); // Forward links AstNode* oldnextp = this->m_nextp; this->m_nextp = newp; - addlastp->m_nextp = oldnextp; // Perhaps null if 'this' is not list + addlastp->m_nextp = oldnextp; // Perhaps null if 'this' is not list // Backward links if (oldnextp) oldnextp->m_backp = addlastp; @@ -311,26 +311,27 @@ void AstNode::addNextHere(AstNode* newp) { // Head/tail AstNode* oldheadtailp = this->m_headtailp; - // (!oldheadtailp) // this was&is middle of list + // (!oldheadtailp) // this was&is middle of list // (oldheadtailp==this && !oldnext)// this was head AND tail (one node long list) - // (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not tail - // (oldheadtailp && !oldnextp) // this was tail of list, might also be head of one-node list + // (oldheadtailp && oldnextp) // this was&is head of list of not just one node, not tail + // (oldheadtailp && !oldnextp) // this was tail of list, might also + // be head of one-node list // - newp->m_headtailp = NULL; // Not at head any longer - addlastp->m_headtailp = NULL; // Presume middle of list + newp->m_headtailp = NULL; // Not at head any longer + addlastp->m_headtailp = NULL; // Presume middle of list // newp might happen to be head/tail after all, if so will be set again below - if (oldheadtailp) { // else in middle of list, no change - if (oldheadtailp==this) { // this was one node - this->m_headtailp = addlastp; // Was head/tail, now a tail - addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL) - } else if (!oldnextp) { // this was tail - this->m_headtailp = NULL; // No longer a tail - oldheadtailp->m_headtailp = addlastp; // Head gets new tail - addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL) - } // else is head, and we're inserting into the middle, so no other change + if (oldheadtailp) { // else in middle of list, no change + if (oldheadtailp==this) { // this was one node + this->m_headtailp = addlastp; // Was head/tail, now a tail + addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL) + } else if (!oldnextp) { // this was tail + this->m_headtailp = NULL; // No longer a tail + oldheadtailp->m_headtailp = addlastp; // Head gets new tail + addlastp->m_headtailp = oldheadtailp; // Tail needs to remember head (or NULL) + } // else is head, and we're inserting into the middle, so no other change } - if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item + if (this->m_iterpp) *(this->m_iterpp) = newp; // Iterate on new item this->debugTreeChange("-addHereOut: ", __LINE__, true); } @@ -435,32 +436,32 @@ AstNode* AstNode::unlinkFrBackWithNext(AstNRelinker* linkerp) { oldp->editCountInc(); AstNode* backp = oldp->m_backp; if (linkerp) { - linkerp->m_oldp = oldp; - linkerp->m_backp = backp; - linkerp->m_iterpp = oldp->m_iterpp; - if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT; - else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1; - else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2; - else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3; - else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4; - else oldp->v3fatalSrc("Unlink of node with back not pointing to it."); + linkerp->m_oldp = oldp; + linkerp->m_backp = backp; + linkerp->m_iterpp = oldp->m_iterpp; + if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT; + else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1; + else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2; + else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3; + else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4; + else oldp->v3fatalSrc("Unlink of node with back not pointing to it."); } if (backp->m_nextp== oldp) { - backp->m_nextp= NULL; - // Old list gets truncated - // New list becomes a list upon itself - // Most common case is unlinking a entire operand tree - // (else we'd probably call unlinkFrBack without next) - // We may be in the middle of a list; we have no way to find head or tail! - AstNode* oldtailp = oldp; - while (oldtailp->m_nextp) oldtailp=oldtailp->m_nextp; - // Create new head/tail of old list - AstNode* oldheadp = oldtailp->m_headtailp; - oldheadp->m_headtailp = oldp->m_backp; - oldheadp->m_headtailp->m_headtailp = oldheadp; - // Create new head/tail of extracted list - oldp->m_headtailp = oldtailp; - oldp->m_headtailp->m_headtailp = oldp; + backp->m_nextp= NULL; + // Old list gets truncated + // New list becomes a list upon itself + // Most common case is unlinking a entire operand tree + // (else we'd probably call unlinkFrBack without next) + // We may be in the middle of a list; we have no way to find head or tail! + AstNode* oldtailp = oldp; + while (oldtailp->m_nextp) oldtailp = oldtailp->m_nextp; + // Create new head/tail of old list + AstNode* oldheadp = oldtailp->m_headtailp; + oldheadp->m_headtailp = oldp->m_backp; + oldheadp->m_headtailp->m_headtailp = oldheadp; + // Create new head/tail of extracted list + oldp->m_headtailp = oldtailp; + oldp->m_headtailp->m_headtailp = oldp; } else if (backp->m_op1p == oldp) backp->m_op1p = NULL; else if (backp->m_op2p == oldp) backp->m_op2p = NULL; @@ -483,39 +484,39 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) { oldp->editCountInc(); AstNode* backp = oldp->m_backp; if (linkerp) { - linkerp->m_oldp = oldp; - linkerp->m_backp = backp; - linkerp->m_iterpp = oldp->m_iterpp; - if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT; - else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1; - else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2; - else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3; - else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4; - else this->v3fatalSrc("Unlink of node with back not pointing to it."); + linkerp->m_oldp = oldp; + linkerp->m_backp = backp; + linkerp->m_iterpp = oldp->m_iterpp; + if (backp->m_nextp == oldp) linkerp->m_chg = AstNRelinker::RELINK_NEXT; + else if (backp->m_op1p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP1; + else if (backp->m_op2p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP2; + else if (backp->m_op3p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP3; + else if (backp->m_op4p == oldp) linkerp->m_chg = AstNRelinker::RELINK_OP4; + else this->v3fatalSrc("Unlink of node with back not pointing to it."); } if (backp->m_nextp== oldp) { - // This node gets removed from middle (or tail) of list - // Not head, since then oldp wouldn't be a next of backp... - backp->m_nextp= oldp->m_nextp; - if (backp->m_nextp) backp->m_nextp->m_backp = backp; - // If it was a tail, back becomes new tail - if (oldp->m_headtailp) { - backp->m_headtailp = oldp->m_headtailp; - backp->m_headtailp->m_headtailp = backp; - } + // This node gets removed from middle (or tail) of list + // Not head, since then oldp wouldn't be a next of backp... + backp->m_nextp= oldp->m_nextp; + if (backp->m_nextp) backp->m_nextp->m_backp = backp; + // If it was a tail, back becomes new tail + if (oldp->m_headtailp) { + backp->m_headtailp = oldp->m_headtailp; + backp->m_headtailp->m_headtailp = backp; + } } else { - if (backp->m_op1p == oldp) backp->m_op1p = oldp->m_nextp; - else if (backp->m_op2p == oldp) backp->m_op2p = oldp->m_nextp; - else if (backp->m_op3p == oldp) backp->m_op3p = oldp->m_nextp; - else if (backp->m_op4p == oldp) backp->m_op4p = oldp->m_nextp; - else this->v3fatalSrc("Unlink of node with back not pointing to it."); - if (oldp->m_nextp) { - AstNode* newheadp = oldp->m_nextp; - newheadp->m_backp = backp; - newheadp->m_headtailp = oldp->m_headtailp; - newheadp->m_headtailp->m_headtailp = newheadp; - } + if (backp->m_op1p == oldp) backp->m_op1p = oldp->m_nextp; + else if (backp->m_op2p == oldp) backp->m_op2p = oldp->m_nextp; + else if (backp->m_op3p == oldp) backp->m_op3p = oldp->m_nextp; + else if (backp->m_op4p == oldp) backp->m_op4p = oldp->m_nextp; + else this->v3fatalSrc("Unlink of node with back not pointing to it."); + if (oldp->m_nextp) { + AstNode* newheadp = oldp->m_nextp; + newheadp->m_backp = backp; + newheadp->m_headtailp = oldp->m_headtailp; + newheadp->m_headtailp->m_headtailp = newheadp; + } } // Iterator fixup if (oldp->m_iterpp) *(oldp->m_iterpp) = oldp->m_nextp; @@ -529,7 +530,7 @@ AstNode* AstNode::unlinkFrBack(AstNRelinker* linkerp) { } void AstNode::relink(AstNRelinker* linkerp) { - if (debug()>8) { UINFO(0," EDIT: relink: "); dumpPtrs(); } + if (debug()>8) { UINFO(0," EDIT: relink: "); dumpPtrs(); } AstNode* newp = this; UASSERT(linkerp && linkerp->m_backp, "Need non-empty linker"); UASSERT(!newp->backp(), "New node already linked?"); @@ -548,21 +549,21 @@ void AstNode::relink(AstNRelinker* linkerp) { case AstNRelinker::RELINK_OP3: relinkOneLink(backp->m_op3p /*ref*/, newp); break; case AstNRelinker::RELINK_OP4: relinkOneLink(backp->m_op4p /*ref*/, newp); break; default: - this->v3fatalSrc("Relink of node without any link to change."); - break; + this->v3fatalSrc("Relink of node without any link to change."); + break; } // Relink newp->m_backp = backp; linkerp->m_backp = NULL; // Iterator fixup if (linkerp->m_iterpp) { - // If we're iterating over a next() link, we need to follow links off the - // NEW node. Thus we pass iteration information via a pointer in the node. - // This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing - // across every function. - // If anyone has a cleaner way, I'd be grateful. - *(linkerp->m_iterpp) = newp; - newp->m_iterpp = linkerp->m_iterpp; + // If we're iterating over a next() link, we need to follow links off the + // NEW node. Thus we pass iteration information via a pointer in the node. + // This adds a unfortunate hot 8 bytes to every AstNode, but is faster than passing + // across every function. + // If anyone has a cleaner way, I'd be grateful. + *(linkerp->m_iterpp) = newp; + newp->m_iterpp = linkerp->m_iterpp; } // Empty the linker so not used twice accidentally linkerp->m_backp = NULL; @@ -570,26 +571,26 @@ void AstNode::relink(AstNRelinker* linkerp) { } void AstNode::relinkOneLink(AstNode*& pointpr, // Ref to pointer that gets set to newp - AstNode* newp) { + AstNode* newp) { if (pointpr) { // We know there will be at least two elements when we are done, - // (newp & the old list). - // We *ALLOW* the new node to have its own next list. - // Likewise there may be a old list. - // Insert the whole old list following the new node's list. - // Thus a unlink without next, followed by relink, gives the same list. - AstNode* newlistlastp=newp->m_headtailp; + // (newp & the old list). + // We *ALLOW* the new node to have its own next list. + // Likewise there may be a old list. + // Insert the whole old list following the new node's list. + // Thus a unlink without next, followed by relink, gives the same list. + AstNode* newlistlastp = newp->m_headtailp; if (newlistlastp->m_nextp && newlistlastp!=newp) newp->v3fatalSrc("Headtailp tail isn't at the tail"); - AstNode* oldlistlastp = pointpr->m_headtailp; + AstNode* oldlistlastp = pointpr->m_headtailp; if (oldlistlastp->m_nextp && oldlistlastp!=pointpr) newp->v3fatalSrc("Old headtailp tail isn't at the tail"); - // Next links - newlistlastp->m_nextp = pointpr; - pointpr->m_backp = newlistlastp; - // Head/tail - pointpr->m_headtailp = NULL; // Old head - newlistlastp->m_headtailp = NULL; // Old tail - newp->m_headtailp = oldlistlastp; // Head points to tail - oldlistlastp->m_headtailp = newp; // Tail points to head + // Next links + newlistlastp->m_nextp = pointpr; + pointpr->m_backp = newlistlastp; + // Head/tail + pointpr->m_headtailp = NULL; // Old head + newlistlastp->m_headtailp = NULL; // Old tail + newp->m_headtailp = oldlistlastp; // Head points to tail + oldlistlastp->m_headtailp = newp; // Tail points to head } pointpr = newp; } @@ -622,8 +623,8 @@ AstNode* AstNode::cloneTreeIter() { if (this->m_op3p) newp->op3p(this->m_op3p->cloneTreeIterList()); if (this->m_op4p) newp->op4p(this->m_op4p->cloneTreeIterList()); newp->m_iterpp = NULL; - newp->clonep(this); // Save pointers to/from both to simplify relinking. - this->clonep(newp); // Save pointers to/from both to simplify relinking. + newp->clonep(this); // Save pointers to/from both to simplify relinking. + this->clonep(newp); // Save pointers to/from both to simplify relinking. return newp; } @@ -633,12 +634,12 @@ AstNode* AstNode::cloneTreeIterList() { AstNode* newtailp = NULL; // Audited to make sure this is never NULL for (AstNode* oldp = this; oldp; oldp=oldp->m_nextp) { - AstNode* newp = oldp->cloneTreeIter(); - newp->m_headtailp = NULL; - newp->m_backp = newtailp; - if (newtailp) newtailp->m_nextp = newp; - if (!newheadp) newheadp = newp; - newtailp = newp; + AstNode* newp = oldp->cloneTreeIter(); + newp->m_headtailp = NULL; + newp->m_backp = newtailp; + if (newtailp) newtailp->m_nextp = newp; + if (!newheadp) newheadp = newp; + newtailp = newp; } newheadp->m_headtailp = newtailp; newtailp->m_headtailp = newheadp; @@ -650,11 +651,11 @@ AstNode* AstNode::cloneTree(bool cloneNextLink) { cloneClearTree(); AstNode* newp; if (cloneNextLink && this->m_nextp) { - newp = cloneTreeIterList(); + newp = cloneTreeIterList(); } else { - newp = cloneTreeIter(); - newp->m_nextp = NULL; - newp->m_headtailp = newp; + newp = cloneTreeIter(); + newp->m_nextp = NULL; + newp->m_headtailp = newp; } newp->m_backp = NULL; newp->cloneRelinkTree(); @@ -694,15 +695,15 @@ void AstNode::deleteTreeIter() { // private: Delete list of nodes. Publicly call deleteTree() instead. // Audited to make sure this is never NULL for (AstNode* nodep=this, *nnextp; nodep; nodep=nnextp) { - nnextp = nodep->m_nextp; - // MUST be depth first! - if (nodep->m_op1p) nodep->m_op1p->deleteTreeIter(); - if (nodep->m_op2p) nodep->m_op2p->deleteTreeIter(); - if (nodep->m_op3p) nodep->m_op3p->deleteTreeIter(); - if (nodep->m_op4p) nodep->m_op4p->deleteTreeIter(); - nodep->m_nextp = NULL; - nodep->m_backp = NULL; - nodep->deleteNode(); + nnextp = nodep->m_nextp; + // MUST be depth first! + if (nodep->m_op1p) nodep->m_op1p->deleteTreeIter(); + if (nodep->m_op2p) nodep->m_op2p->deleteTreeIter(); + if (nodep->m_op3p) nodep->m_op3p->deleteTreeIter(); + if (nodep->m_op4p) nodep->m_op4p->deleteTreeIter(); + nodep->m_nextp = NULL; + nodep->m_backp = NULL; + nodep->deleteNode(); } } @@ -769,42 +770,42 @@ void AstNode::iterateAndNext(AstNVisitor& v) { // then the NEW node will be iterated on next, it isn't skipped! // Future versions of this function may require the node to have a back to be iterated; // there's no lower level reason yet though the back must exist. - AstNode* nodep=this; + AstNode* nodep = this; #ifdef VL_DEBUG // Otherwise too hot of a function for debug if (VL_UNLIKELY(nodep && !nodep->m_backp)) nodep->v3fatalSrc("iterateAndNext node has no back"); #endif if (nodep) ASTNODE_PREFETCH(nodep->m_nextp); while (nodep) { // effectively: if (!this) return; // Callers rely on this - if (nodep->m_nextp) ASTNODE_PREFETCH(nodep->m_nextp->m_nextp); - AstNode* niterp = nodep; // This address may get stomped via m_iterpp if the node is edited - // Desirable check, but many places where multiple iterations are OK - //if (VL_UNLIKELY(niterp->m_iterpp)) niterp->v3fatalSrc("IterateAndNext under iterateAndNext may miss edits"); - // Optimization note: Doing PREFETCH_RW on m_iterpp is a net even - // cppcheck-suppress nullPointer - niterp->m_iterpp = &niterp; - niterp->accept(v); - // accept may do a replaceNode and change niterp on us... + if (nodep->m_nextp) ASTNODE_PREFETCH(nodep->m_nextp->m_nextp); + AstNode* niterp = nodep; // This address may get stomped via m_iterpp if the node is edited + // Desirable check, but many places where multiple iterations are OK + //if (VL_UNLIKELY(niterp->m_iterpp)) niterp->v3fatalSrc("IterateAndNext under iterateAndNext may miss edits"); + // Optimization note: Doing PREFETCH_RW on m_iterpp is a net even + // cppcheck-suppress nullPointer + niterp->m_iterpp = &niterp; + niterp->accept(v); + // accept may do a replaceNode and change niterp on us... // niterp maybe NULL, so need cast if printing //if (niterp != nodep) UINFO(1,"iterateAndNext edited "<m_iterpp = NULL; - if (VL_UNLIKELY(niterp!=nodep)) { // Edited node inside accept - nodep = niterp; - } else { // Unchanged node, just continue loop - nodep = niterp->m_nextp; - } + if (!niterp) return; // Perhaps node deleted inside accept + niterp->m_iterpp = NULL; + if (VL_UNLIKELY(niterp!=nodep)) { // Edited node inside accept + nodep = niterp; + } else { // Unchanged node, just continue loop + nodep = niterp->m_nextp; + } } } void AstNode::iterateListBackwards(AstNVisitor& v) { - AstNode* nodep=this; - while (nodep->m_nextp) nodep=nodep->m_nextp; + AstNode* nodep = this; + while (nodep->m_nextp) nodep = nodep->m_nextp; while (nodep) { - // Edits not supported: nodep->m_iterpp = &nodep; - nodep->accept(v); - if (nodep->backp()->m_nextp == nodep) nodep=nodep->backp(); - else nodep = NULL; // else: backp points up the tree. + // Edits not supported: nodep->m_iterpp = &nodep; + nodep->accept(v); + if (nodep->backp()->m_nextp == nodep) nodep = nodep->backp(); + else nodep = NULL; // else: backp points up the tree. } } @@ -817,12 +818,12 @@ void AstNode::iterateChildrenBackwards(AstNVisitor& v) { void AstNode::iterateAndNextConst(AstNVisitor& v) { // Keep following the current list even if edits change it - AstNode* nodep=this; + AstNode* nodep = this; do { - AstNode* nnextp = nodep->m_nextp; - ASTNODE_PREFETCH(nnextp); - nodep->accept(v); - nodep = nnextp; + AstNode* nnextp = nodep->m_nextp; + ASTNODE_PREFETCH(nnextp); + nodep->accept(v); + nodep = nnextp; } while (nodep); } @@ -835,29 +836,31 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) { // which in many cases is just the same node that was passed in. AstNode* nodep = this; // Note "this" may point to bogus point later in this function if (VN_IS(nodep, Netlist)) { - // Calling on top level; we know the netlist won't get replaced - nodep->accept(v); + // Calling on top level; we know the netlist won't get replaced + nodep->accept(v); } else if (!nodep->backp()) { - // Calling on standalone tree; insert a shim node so we can keep track, then delete it on completion - AstBegin* tempp = new AstBegin(nodep->fileline(),"[EditWrapper]",nodep); - { - tempp->stmtsp()->accept(v); VL_DANGLING(nodep); // nodep to null as may be replaced - } - nodep = tempp->stmtsp()->unlinkFrBackWithNext(); - tempp->deleteTree(); VL_DANGLING(tempp); + // Calling on standalone tree; insert a shim node so we can keep + // track, then delete it on completion + AstBegin* tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", nodep); + { + tempp->stmtsp()->accept(v); VL_DANGLING(nodep); // nodep to null as may be replaced + } + nodep = tempp->stmtsp()->unlinkFrBackWithNext(); + tempp->deleteTree(); VL_DANGLING(tempp); } else { - // Use back to determine who's pointing at us (IE assume new node grafts into same place as old one) - AstNode** nextnodepp = NULL; - if (this->m_backp->m_op1p == this) nextnodepp = &(this->m_backp->m_op1p); - else if (this->m_backp->m_op2p == this) nextnodepp = &(this->m_backp->m_op2p); - else if (this->m_backp->m_op3p == this) nextnodepp = &(this->m_backp->m_op3p); - else if (this->m_backp->m_op4p == this) nextnodepp = &(this->m_backp->m_op4p); - else if (this->m_backp->m_nextp == this) nextnodepp = &(this->m_backp->m_nextp); - if (!nextnodepp) this->v3fatalSrc("Node's back doesn't point to forward to node itself"); - { - nodep->accept(v); VL_DANGLING(nodep); // nodep to null as may be replaced - } - nodep = *nextnodepp; // Grab new node from point where old was connected + // Use back to determine who's pointing at us (IE assume new node + // grafts into same place as old one) + AstNode** nextnodepp = NULL; + if (this->m_backp->m_op1p == this) nextnodepp = &(this->m_backp->m_op1p); + else if (this->m_backp->m_op2p == this) nextnodepp = &(this->m_backp->m_op2p); + else if (this->m_backp->m_op3p == this) nextnodepp = &(this->m_backp->m_op3p); + else if (this->m_backp->m_op4p == this) nextnodepp = &(this->m_backp->m_op4p); + else if (this->m_backp->m_nextp == this) nextnodepp = &(this->m_backp->m_nextp); + if (!nextnodepp) this->v3fatalSrc("Node's back doesn't point to forward to node itself"); + { + nodep->accept(v); VL_DANGLING(nodep); // nodep to null as may be replaced + } + nodep = *nextnodepp; // Grab new node from point where old was connected } return nodep; } @@ -866,15 +869,15 @@ AstNode* AstNode::iterateSubtreeReturnEdits(AstNVisitor& v) { void AstNode::cloneRelinkTree() { // private: Cleanup clone() operation on whole tree. Publicly call cloneTree() instead. - for (AstNode* nodep=this; nodep; nodep=nodep->m_nextp) { - if (m_dtypep && m_dtypep->clonep()) { - m_dtypep = m_dtypep->clonep(); - } - nodep->cloneRelink(); - if (nodep->m_op1p) nodep->m_op1p->cloneRelinkTree(); - if (nodep->m_op2p) nodep->m_op2p->cloneRelinkTree(); - if (nodep->m_op3p) nodep->m_op3p->cloneRelinkTree(); - if (nodep->m_op4p) nodep->m_op4p->cloneRelinkTree(); + for (AstNode* nodep=this; nodep; nodep = nodep->m_nextp) { + if (m_dtypep && m_dtypep->clonep()) { + m_dtypep = m_dtypep->clonep(); + } + nodep->cloneRelink(); + if (nodep->m_op1p) nodep->m_op1p->cloneRelinkTree(); + if (nodep->m_op2p) nodep->m_op2p->cloneRelinkTree(); + if (nodep->m_op3p) nodep->m_op3p->cloneRelinkTree(); + if (nodep->m_op4p) nodep->m_op4p->cloneRelinkTree(); } } @@ -891,22 +894,23 @@ bool AstNode::gateTreeIter() const { return true; } -bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly) { +bool AstNode::sameTreeIter(const AstNode* node1p, const AstNode* node2p, + bool ignNext, bool gateOnly) { // private: Return true if the two trees are identical if (!node1p && !node2p) return true; if (!node1p || !node2p) return false; if (node1p->type() != node2p->type() - || node1p->dtypep() != node2p->dtypep() - || !node1p->same(node2p) - || (gateOnly && !node1p->isGateOptimizable())) { - return false; + || node1p->dtypep() != node2p->dtypep() + || !node1p->same(node2p) + || (gateOnly && !node1p->isGateOptimizable())) { + return false; } - return (sameTreeIter(node1p->m_op1p, node2p->m_op1p,false,gateOnly) - && sameTreeIter(node1p->m_op2p, node2p->m_op2p,false,gateOnly) - && sameTreeIter(node1p->m_op3p, node2p->m_op3p,false,gateOnly) - && sameTreeIter(node1p->m_op4p, node2p->m_op4p,false,gateOnly) - && (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp,false,gateOnly)) - ); + return (sameTreeIter(node1p->m_op1p, node2p->m_op1p, false, gateOnly) + && sameTreeIter(node1p->m_op2p, node2p->m_op2p, false, gateOnly) + && sameTreeIter(node1p->m_op3p, node2p->m_op3p, false, gateOnly) + && sameTreeIter(node1p->m_op4p, node2p->m_op4p, false, gateOnly) + && (ignNext || sameTreeIter(node1p->m_nextp, node2p->m_nextp, false, gateOnly)) + ); } //====================================================================== @@ -920,9 +924,9 @@ std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) { V3Hash::V3Hash(const string& name) { uint32_t val = 0; for (string::const_iterator it = name.begin(); it!=name.end(); ++it) { - val = val*31 + *it; + val = val*31 + *it; } - setBoth(1,val); + setBoth(1, val); } //====================================================================== @@ -931,12 +935,12 @@ V3Hash::V3Hash(const string& name) { void AstNode::checkTreeIter(AstNode* backp) { // private: Check a tree and children if (backp != this->backp()) { - this->v3fatalSrc("Back node inconsistent"); + this->v3fatalSrc("Back node inconsistent"); } if (VN_IS(this, NodeTermop) || VN_IS(this, NodeVarRef)) { - // Termops have a short-circuited iterateChildren, so check usage - if (op1p()||op2p()||op3p()||op4p()) - this->v3fatalSrc("Terminal operation with non-terminals"); + // Termops have a short-circuited iterateChildren, so check usage + if (op1p()||op2p()||op3p()||op4p()) + this->v3fatalSrc("Terminal operation with non-terminals"); } if (m_op1p) m_op1p->checkTreeIterList(this); if (m_op2p) m_op2p->checkTreeIterList(this); @@ -950,10 +954,10 @@ void AstNode::checkTreeIterList(AstNode* backp) { AstNode* headp = this; AstNode* tailp = this; for (AstNode* nodep=headp; nodep; nodep=nodep->nextp()) { - nodep->checkTreeIter(backp); - if (headp!=this && nextp()) this->v3fatalSrc("Headtailp should be null in middle of lists"); - tailp=nodep; - backp=nodep; + nodep->checkTreeIter(backp); + if (headp!=this && nextp()) this->v3fatalSrc("Headtailp should be null in middle of lists"); + tailp = nodep; + backp = nodep; } if (headp->m_headtailp != tailp) headp->v3fatalSrc("Tail in headtailp is inconsistent"); if (tailp->m_headtailp != headp) tailp->v3fatalSrc("Head in headtailp is inconsistent"); @@ -962,10 +966,10 @@ void AstNode::checkTreeIterList(AstNode* backp) { void AstNode::checkTree() { if (!debug()) return; if (this->backp()) { - // Linked tree- check only the passed node - this->checkTreeIter(this->backp()); + // Linked tree- check only the passed node + this->checkTreeIter(this->backp()); } else { - this->checkTreeIterList(this->backp()); + this->checkTreeIterList(this->backp()); } } @@ -982,15 +986,15 @@ void AstNode::dumpTreeGdb() { // For GDB only dumpTree(cout); } void AstNode::dumpTreeFileGdb(const char* filenamep) { // For GDB only - string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree",98); + string filename = filenamep ? filenamep : v3Global.debugFilename("debug.tree", 98); v3Global.rootp()->dumpTreeFile(filename); } void AstNode::checkIter() const { if (m_iterpp) { - dumpPtrs(cout); - // Perhaps something forgot to clear m_iterpp? - this->v3fatalSrc("Iteration link should be NULL"); + dumpPtrs(cout); + // Perhaps something forgot to clear m_iterpp? + this->v3fatalSrc("Iteration link should be NULL"); } } @@ -1020,45 +1024,49 @@ void AstNode::dumpTree(std::ostream& os, const string& indent, int maxDepth) { os<8) { os<nextp()) { nodep->dumpTree(os,indent+"1:",maxDepth-1); } - for (AstNode* nodep=op2p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os,indent+"2:",maxDepth-1); } - for (AstNode* nodep=op3p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os,indent+"3:",maxDepth-1); } - for (AstNode* nodep=op4p(); nodep; nodep=nodep->nextp()) { nodep->dumpTree(os,indent+"4:",maxDepth-1); } + for (AstNode* nodep=op1p(); nodep; nodep=nodep->nextp()) { + nodep->dumpTree(os, indent+"1:", maxDepth-1); } + for (AstNode* nodep=op2p(); nodep; nodep=nodep->nextp()) { + nodep->dumpTree(os, indent+"2:", maxDepth-1); } + for (AstNode* nodep=op3p(); nodep; nodep=nodep->nextp()) { + nodep->dumpTree(os, indent+"3:", maxDepth-1); } + for (AstNode* nodep=op4p(); nodep; nodep=nodep->nextp()) { + nodep->dumpTree(os, indent+"4:", maxDepth-1); } } } void AstNode::dumpTreeAndNext(std::ostream& os, const string& indent, int maxDepth) { // Audited to make sure this is never NULL for (AstNode* nodep=this; nodep; nodep=nodep->nextp()) { - nodep->dumpTree(os, indent, maxDepth); + nodep->dumpTree(os, indent, maxDepth); } } void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump) { if (doDump) { - { // Write log & close - UINFO(2,"Dumping "< logsp (V3File::new_ofstream(filename, append)); if (logsp->fail()) v3fatal("Can't write "<"; *logsp<<" to "<=9)) { - *logsp<=9)) { + *logsp<(this)->dump(nsstr); nsstr<v3errorEnd(nsstr); + } + m_fileline->v3errorEnd(nsstr); } } @@ -1094,7 +1102,7 @@ string AstNode::warnMore() const { void AstNode::dtypeChgSigned(bool flag) { if (!dtypep()) this->v3fatalSrc("No dtype when changing to (un)signed"); dtypeChgWidthSigned(dtypep()->width(), dtypep()->widthMin(), - flag ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); + flag ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); } void AstNode::dtypeChgWidth(int width, int widthMin) { if (!dtypep()) this->v3fatalSrc("No dtype when changing width"); // Use ChgWidthSigned(...UNSIGNED) otherwise @@ -1103,18 +1111,18 @@ void AstNode::dtypeChgWidth(int width, int widthMin) { void AstNode::dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric) { if (!dtypep()) { - // We allow dtypep() to be null, as before/during widthing dtypes are not resolved - dtypeSetLogicSized(width, widthMin, numeric); + // We allow dtypep() to be null, as before/during widthing dtypes are not resolved + dtypeSetLogicSized(width, widthMin, numeric); } else { - if (width==dtypep()->width() - && widthMin==dtypep()->widthMin() - && numeric==dtypep()->numeric()) return; // Correct already - // FUTURE: We may be pointing at a two state data type, and this may - // convert it to logic. Since the AstVar remains correct, we - // work OK but this assumption may break in the future. - // Note we can't just clone and do a widthForce, as if it's a BasicDType - // the msb() indications etc will be incorrect. - dtypeSetLogicSized(width, widthMin, numeric); + if (width==dtypep()->width() + && widthMin==dtypep()->widthMin() + && numeric==dtypep()->numeric()) return; // Correct already + // FUTURE: We may be pointing at a two state data type, and this may + // convert it to logic. Since the AstVar remains correct, we + // work OK but this assumption may break in the future. + // Note we can't just clone and do a widthForce, as if it's a BasicDType + // the msb() indications etc will be incorrect. + dtypeSetLogicSized(width, widthMin, numeric); } } @@ -1122,23 +1130,24 @@ AstNodeDType* AstNode::findBasicDType(AstBasicDTypeKwd kwd) const { // For 'simple' types we use the global directory. These are all unsized. // More advanced types land under the module/task/etc return v3Global.rootp()->typeTablep() - ->findBasicDType(fileline(), kwd); + ->findBasicDType(fileline(), kwd); } AstNodeDType* AstNode::findBitDType(int width, int widthMin, AstNumeric numeric) const { return v3Global.rootp()->typeTablep() - ->findLogicBitDType(fileline(), AstBasicDTypeKwd::BIT, width, widthMin, numeric); + ->findLogicBitDType(fileline(), AstBasicDTypeKwd::BIT, width, widthMin, numeric); } AstNodeDType* AstNode::findLogicDType(int width, int widthMin, AstNumeric numeric) const { return v3Global.rootp()->typeTablep() - ->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, width, widthMin, numeric); + ->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, width, widthMin, numeric); } -AstNodeDType* AstNode::findLogicRangeDType(VNumRange range, int widthMin, AstNumeric numeric) const { +AstNodeDType* AstNode::findLogicRangeDType(VNumRange range, int widthMin, + AstNumeric numeric) const { return v3Global.rootp()->typeTablep() - ->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, range, widthMin, numeric); + ->findLogicBitDType(fileline(), AstBasicDTypeKwd::LOGIC, range, widthMin, numeric); } AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { return v3Global.rootp()->typeTablep() - ->findInsertSameDType(nodep); + ->findInsertSameDType(nodep); } //###################################################################### @@ -1146,7 +1155,7 @@ AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) { void AstNVisitor::doDeletes() { for (std::vector::iterator it = m_deleteps.begin(); it != m_deleteps.end(); ++it) { - (*it)->deleteTree(); + (*it)->deleteTree(); } m_deleteps.clear(); } diff --git a/src/V3Ast.h b/src/V3Ast.h index 84dec6e13..b989e66da 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -32,7 +32,7 @@ #include #include VL_INCLUDE_UNORDERED_SET -#include "V3Ast__gen_classes.h" // From ./astgen +#include "V3Ast__gen_classes.h" // From ./astgen // Things like: // class V3AstNode; @@ -56,7 +56,8 @@ typedef std::set MTaskIdSet; // Set of mtaskIds for Var sorting // (V)erilator (N)ode is: True if AstNode is of a a given AstType #define VN_IS(nodep,nodetypename) (AstNode::privateIs ## nodetypename(nodep)) -// (V)erilator (N)ode cast: Cast to given type if can; effectively dynamic_cast(nodep)(nodetypename) +// (V)erilator (N)ode cast: Cast to given type if can; effectively +// dynamic_cast(nodep)(nodetypename) #define VN_CAST(nodep,nodetypename) (AstNode::privateCast ## nodetypename(nodep)) #define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast ## nodetypename(nodep) ) @@ -64,7 +65,7 @@ typedef std::set MTaskIdSet; // Set of mtaskIds for Var sorting class AstType { public: -#include "V3Ast__gen_types.h" // From ./astgen +#include "V3Ast__gen_types.h" // From ./astgen // Above include has: // enum en {...}; // const char* ascii() const {...}; @@ -76,16 +77,16 @@ public: explicit inline AstType(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } }; - inline bool operator== (AstType lhs, AstType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstType lhs, AstType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstType::en lhs, AstType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstType lhs, AstType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstType lhs, AstType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstType::en lhs, AstType rhs) { return (lhs == rhs.m_e); } inline std::ostream& operator<<(std::ostream& os, const AstType& rhs) { return os<(_e)) {} operator en() const { return m_e; } inline bool isSigned() const { return m_e==SIGNED; } inline bool isNosign() const { return m_e==NOSIGN; } // No isUnsigned() as it's ambiguous if NOSIGN should be included or not. }; - inline bool operator== (AstNumeric lhs, AstNumeric rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstNumeric lhs, AstNumeric::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstNumeric::en lhs, AstNumeric rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstNumeric lhs, AstNumeric rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstNumeric lhs, AstNumeric::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstNumeric::en lhs, AstNumeric rhs) { return (lhs == rhs.m_e); } inline std::ostream& operator<<(std::ostream& os, const AstNumeric& rhs) { return os<(_e)) {} operator en() const { return m_e; } }; - inline bool operator== (AstPragmaType lhs, AstPragmaType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstPragmaType lhs, AstPragmaType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstPragmaType::en lhs, AstPragmaType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstPragmaType lhs, AstPragmaType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstPragmaType lhs, AstPragmaType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstPragmaType::en lhs, AstPragmaType rhs) { return (lhs == rhs.m_e); } //###################################################################### class AstCFuncType { public: enum en { - FT_NORMAL, - TRACE_INIT, - TRACE_INIT_SUB, - TRACE_FULL, - TRACE_FULL_SUB, - TRACE_CHANGE, - TRACE_CHANGE_SUB + FT_NORMAL, + TRACE_INIT, + TRACE_INIT_SUB, + TRACE_FULL, + TRACE_FULL_SUB, + TRACE_CHANGE, + TRACE_CHANGE_SUB }; enum en m_e; inline AstCFuncType() : m_e(FT_NORMAL) {} @@ -172,12 +173,12 @@ public: operator en() const { return m_e; } // METHODS bool isTrace() const { return (m_e==TRACE_INIT || m_e==TRACE_INIT_SUB - || m_e==TRACE_FULL || m_e==TRACE_FULL_SUB - || m_e==TRACE_CHANGE || m_e==TRACE_CHANGE_SUB); } + || m_e==TRACE_FULL || m_e==TRACE_FULL_SUB + || m_e==TRACE_CHANGE || m_e==TRACE_CHANGE_SUB); } }; - inline bool operator== (AstCFuncType lhs, AstCFuncType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstCFuncType lhs, AstCFuncType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstCFuncType::en lhs, AstCFuncType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstCFuncType lhs, AstCFuncType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstCFuncType lhs, AstCFuncType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstCFuncType::en lhs, AstCFuncType rhs) { return (lhs == rhs.m_e); } //###################################################################### @@ -185,54 +186,55 @@ class AstEdgeType { public: // REMEMBER to edit the strings below too enum en { - // These must be in general -> most specific order, as we sort by it in V3Const::visit AstSenTree - ET_ILLEGAL, - // Involving a variable - ET_ANYEDGE, // Default for sensitivities; rip them out - ET_BOTHEDGE, // POSEDGE | NEGEDGE - ET_POSEDGE, - ET_NEGEDGE, - ET_HIGHEDGE, // Is high now (latches) - ET_LOWEDGE, // Is low now (latches) - // Not involving anything - ET_COMBO, // Sensitive to all combo inputs to this block - ET_INITIAL, // User initial statements - ET_SETTLE, // Like combo but for initial wire resolutions after initial statement - ET_NEVER // Never occurs (optimized away) + // These must be in general -> most specific order, as we sort by it + // in V3Const::visit AstSenTree + ET_ILLEGAL, + // Involving a variable + ET_ANYEDGE, // Default for sensitivities; rip them out + ET_BOTHEDGE, // POSEDGE | NEGEDGE + ET_POSEDGE, + ET_NEGEDGE, + ET_HIGHEDGE, // Is high now (latches) + ET_LOWEDGE, // Is low now (latches) + // Not involving anything + ET_COMBO, // Sensitive to all combo inputs to this block + ET_INITIAL, // User initial statements + ET_SETTLE, // Like combo but for initial wire resolutions after initial statement + ET_NEVER // Never occurs (optimized away) }; enum en m_e; bool clockedStmt() const { - static const bool clocked[] = { - false, false, true, true, true, true, true, - false, false, false - }; - return clocked[m_e]; + static const bool clocked[] = { + false, false, true, true, true, true, true, + false, false, false + }; + return clocked[m_e]; } AstEdgeType invert() const { - switch (m_e) { - case ET_ANYEDGE: return ET_ANYEDGE; - case ET_BOTHEDGE: return ET_BOTHEDGE; - case ET_POSEDGE: return ET_NEGEDGE; - case ET_NEGEDGE: return ET_POSEDGE; - case ET_HIGHEDGE: return ET_LOWEDGE; - case ET_LOWEDGE: return ET_HIGHEDGE; - default: UASSERT_STATIC(0,"Inverting bad edgeType()"); - }; - return AstEdgeType::ET_ILLEGAL; + switch (m_e) { + case ET_ANYEDGE: return ET_ANYEDGE; + case ET_BOTHEDGE: return ET_BOTHEDGE; + case ET_POSEDGE: return ET_NEGEDGE; + case ET_NEGEDGE: return ET_POSEDGE; + case ET_HIGHEDGE: return ET_LOWEDGE; + case ET_LOWEDGE: return ET_HIGHEDGE; + default: UASSERT_STATIC(0, "Inverting bad edgeType()"); + }; + return AstEdgeType::ET_ILLEGAL; } const char* ascii() const { - static const char* const names[] = { - "%E-edge", "ANY", "BOTH", "POS", "NEG", "HIGH", "LOW", - "COMBO","INITIAL","SETTLE","NEVER" - }; - return names[m_e]; + static const char* const names[] = { + "%E-edge", "ANY", "BOTH", "POS", "NEG", "HIGH", "LOW", + "COMBO", "INITIAL", "SETTLE", "NEVER" + }; + return names[m_e]; } const char* verilogKwd() const { - static const char* const names[] = { - "%E-edge", "[any]", "edge", "posedge", "negedge", "[high]","[low]", - "*","[initial]","[settle]","[never]" - }; - return names[m_e]; + static const char* const names[] = { + "%E-edge", "[any]", "edge", "posedge", "negedge", "[high]", "[low]", + "*", "[initial]", "[settle]", "[never]" + }; + return names[m_e]; } // Return true iff this and the other have mutually exclusive transitions bool exclusiveEdge(const AstEdgeType& other) const { @@ -263,67 +265,68 @@ public: explicit inline AstEdgeType(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } }; - inline bool operator== (AstEdgeType lhs, AstEdgeType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstEdgeType lhs, AstEdgeType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstEdgeType::en lhs, AstEdgeType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstEdgeType lhs, AstEdgeType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstEdgeType lhs, AstEdgeType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstEdgeType::en lhs, AstEdgeType rhs) { return (lhs == rhs.m_e); } //###################################################################### class AstAttrType { public: enum en { - ILLEGAL, - // - DIM_BITS, // V3Const converts to constant - DIM_DIMENSIONS, // V3Width converts to constant - DIM_HIGH, // V3Width processes - DIM_INCREMENT, // V3Width processes - DIM_LEFT, // V3Width processes - DIM_LOW, // V3Width processes - DIM_RIGHT, // V3Width processes - DIM_SIZE, // V3Width processes - DIM_UNPK_DIMENSIONS, // V3Width converts to constant - // - DT_PUBLIC, // V3LinkParse moves to AstTypedef::attrPublic - // - ENUM_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes - ENUM_FIRST, // V3Width processes - ENUM_LAST, // V3Width processes - ENUM_NUM, // V3Width processes - ENUM_NEXT, // V3Width processes - ENUM_PREV, // V3Width processes - ENUM_NAME, // V3Width processes - // - MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes - // - VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes - VAR_CLOCK, // V3LinkParse moves to AstVar::attrScClocked - VAR_CLOCK_ENABLE, // V3LinkParse moves to AstVar::attrClockEn - VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic - VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic - VAR_PUBLIC_FLAT_RD, // V3LinkParse moves to AstVar::sigPublic - VAR_PUBLIC_FLAT_RW, // V3LinkParse moves to AstVar::sigPublic - VAR_ISOLATE_ASSIGNMENTS, // V3LinkParse moves to AstVar::attrIsolateAssign - VAR_SC_BV, // V3LinkParse moves to AstVar::attrScBv - VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat - VAR_CLOCKER, // V3LinkParse moves to AstVar::attrClocker - VAR_NO_CLOCKER // V3LinkParse moves to AstVar::attrClocker + ILLEGAL, + // + DIM_BITS, // V3Const converts to constant + DIM_DIMENSIONS, // V3Width converts to constant + DIM_HIGH, // V3Width processes + DIM_INCREMENT, // V3Width processes + DIM_LEFT, // V3Width processes + DIM_LOW, // V3Width processes + DIM_RIGHT, // V3Width processes + DIM_SIZE, // V3Width processes + DIM_UNPK_DIMENSIONS, // V3Width converts to constant + // + DT_PUBLIC, // V3LinkParse moves to AstTypedef::attrPublic + // + ENUM_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes + ENUM_FIRST, // V3Width processes + ENUM_LAST, // V3Width processes + ENUM_NUM, // V3Width processes + ENUM_NEXT, // V3Width processes + ENUM_PREV, // V3Width processes + ENUM_NAME, // V3Width processes + // + MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes + // + VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes + VAR_CLOCK, // V3LinkParse moves to AstVar::attrScClocked + VAR_CLOCK_ENABLE, // V3LinkParse moves to AstVar::attrClockEn + VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic + VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic + VAR_PUBLIC_FLAT_RD, // V3LinkParse moves to AstVar::sigPublic + VAR_PUBLIC_FLAT_RW, // V3LinkParse moves to AstVar::sigPublic + VAR_ISOLATE_ASSIGNMENTS, // V3LinkParse moves to AstVar::attrIsolateAssign + VAR_SC_BV, // V3LinkParse moves to AstVar::attrScBv + VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat + VAR_CLOCKER, // V3LinkParse moves to AstVar::attrClocker + VAR_NO_CLOCKER // V3LinkParse moves to AstVar::attrClocker }; enum en m_e; const char* ascii() const { - static const char* const names[] = { - "%E-AT", - "DIM_BITS", "DIM_DIMENSIONS", "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT", - "DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS", - "DT_PUBLIC", - "ENUM_BASE", "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM", "ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", - "MEMBER_BASE", - "VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", - "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW", - "VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER", - "VAR_NO_CLOCKER" - }; - return names[m_e]; + static const char* const names[] = { + "%E-AT", + "DIM_BITS", "DIM_DIMENSIONS", "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT", + "DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS", + "DT_PUBLIC", + "ENUM_BASE", "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM", + "ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", + "MEMBER_BASE", + "VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", + "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW", + "VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER", + "VAR_NO_CLOCKER" + }; + return names[m_e]; }; inline AstAttrType() : m_e(ILLEGAL) {} // cppcheck-suppress noExplicitConstructor @@ -331,59 +334,59 @@ public: explicit inline AstAttrType(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } }; - inline bool operator== (AstAttrType lhs, AstAttrType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstAttrType lhs, AstAttrType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstAttrType::en lhs, AstAttrType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstAttrType lhs, AstAttrType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstAttrType lhs, AstAttrType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstAttrType::en lhs, AstAttrType rhs) { return (lhs == rhs.m_e); } //###################################################################### class AstBasicDTypeKwd { public: enum en { - UNKNOWN, - BIT, BYTE, CHANDLE, INT, INTEGER, LOGIC, LONGINT, - DOUBLE, SHORTINT, FLOAT, TIME, - // Closer to a class type, but limited usage - STRING, - // Internal types for mid-steps - SCOPEPTR, CHARPTR, - // Unsigned and two state; fundamental types - UINT32, UINT64, - // Internal types, eliminated after parsing - LOGIC_IMPLICIT, - // Leave last - _ENUM_MAX + UNKNOWN, + BIT, BYTE, CHANDLE, INT, INTEGER, LOGIC, LONGINT, + DOUBLE, SHORTINT, FLOAT, TIME, + // Closer to a class type, but limited usage + STRING, + // Internal types for mid-steps + SCOPEPTR, CHARPTR, + // Unsigned and two state; fundamental types + UINT32, UINT64, + // Internal types, eliminated after parsing + LOGIC_IMPLICIT, + // Leave last + _ENUM_MAX }; enum en m_e; const char* ascii() const { - static const char* const names[] = { - "%E-unk", - "bit", "byte", "chandle", "int", "integer", "logic", "longint", - "real", "shortint", "shortreal", "time", - "string", - "VerilatedScope*", "char*", - "IData", "QData", - "LOGIC_IMPLICIT", - " MAX" - }; - return names[m_e]; + static const char* const names[] = { + "%E-unk", + "bit", "byte", "chandle", "int", "integer", "logic", "longint", + "real", "shortint", "shortreal", "time", + "string", + "VerilatedScope*", "char*", + "IData", "QData", + "LOGIC_IMPLICIT", + " MAX" + }; + return names[m_e]; }; const char* dpiType() const { - static const char* const names[] = { - "%E-unk", - "unsigned char", "char", "void*", "int", "int", "svLogic", "long long", - "double", "short int", "float", "long long", - "const char*", - "dpiScope", "const char*", - "IData", "QData", - "svLogic", // Though shouldn't be needed - " MAX" - }; - return names[m_e]; + static const char* const names[] = { + "%E-unk", + "unsigned char", "char", "void*", "int", "int", "svLogic", "long long", + "double", "short int", "float", "long long", + "const char*", + "dpiScope", "const char*", + "IData", "QData", + "svLogic", // Though shouldn't be needed + " MAX" + }; + return names[m_e]; }; static void selfTest() { - UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).ascii()," MAX"), "SelfTest: Enum mismatch"); - UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).dpiType()," MAX"),"SelfTest: Enum mismatch"); + UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).ascii(), " MAX"), "SelfTest: Enum mismatch"); + UASSERT(0==strcmp(AstBasicDTypeKwd(_ENUM_MAX).dpiType(), " MAX"), "SelfTest: Enum mismatch"); } inline AstBasicDTypeKwd() : m_e(UNKNOWN) {} // cppcheck-suppress noExplicitConstructor @@ -391,50 +394,50 @@ public: explicit inline AstBasicDTypeKwd(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } int width() const { - switch (m_e) { - case BIT: return 1; // scalar, can't bit extract unless ranged - case BYTE: return 8; - case CHANDLE: return 64; - case INT: return 32; - case INTEGER: return 32; - case LOGIC: return 1; // scalar, can't bit extract unless ranged - case LONGINT: return 64; - case DOUBLE: return 64; // opaque - case FLOAT: return 32; // opaque - case SHORTINT: return 16; - case TIME: return 64; - case STRING: return 64; // opaque // Just the pointer, for today - case SCOPEPTR: return 0; // opaque - case CHARPTR: return 0; // opaque - case UINT32: return 32; - case UINT64: return 64; - default: return 0; - } + switch (m_e) { + case BIT: return 1; // scalar, can't bit extract unless ranged + case BYTE: return 8; + case CHANDLE: return 64; + case INT: return 32; + case INTEGER: return 32; + case LOGIC: return 1; // scalar, can't bit extract unless ranged + case LONGINT: return 64; + case DOUBLE: return 64; // opaque + case FLOAT: return 32; // opaque + case SHORTINT: return 16; + case TIME: return 64; + case STRING: return 64; // opaque // Just the pointer, for today + case SCOPEPTR: return 0; // opaque + case CHARPTR: return 0; // opaque + case UINT32: return 32; + case UINT64: return 64; + default: return 0; + } } bool isSigned() const { - return m_e==BYTE || m_e==SHORTINT || m_e==INT || m_e==LONGINT || m_e==INTEGER - || m_e==DOUBLE || m_e==FLOAT; + return m_e==BYTE || m_e==SHORTINT || m_e==INT || m_e==LONGINT || m_e==INTEGER + || m_e==DOUBLE || m_e==FLOAT; } bool isUnsigned() const { - return m_e==CHANDLE || m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR - || m_e==UINT32 || m_e==UINT64; + return m_e==CHANDLE || m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR + || m_e==UINT32 || m_e==UINT64; } bool isFourstate() const { - return m_e==INTEGER || m_e==LOGIC || m_e==LOGIC_IMPLICIT; + return m_e==INTEGER || m_e==LOGIC || m_e==LOGIC_IMPLICIT; } - bool isZeroInit() const { // Otherwise initializes to X - return (m_e==BIT || m_e==BYTE || m_e==CHANDLE || m_e==INT || m_e==LONGINT || m_e==SHORTINT - || m_e==STRING || m_e==DOUBLE || m_e==FLOAT); + bool isZeroInit() const { // Otherwise initializes to X + return (m_e==BIT || m_e==BYTE || m_e==CHANDLE || m_e==INT || m_e==LONGINT || m_e==SHORTINT + || m_e==STRING || m_e==DOUBLE || m_e==FLOAT); } - bool isIntNumeric() const { // Enum increment supported - return (m_e==BIT || m_e==BYTE || m_e==INT || m_e==INTEGER || m_e==LOGIC - || m_e==LONGINT || m_e==SHORTINT || m_e==UINT32 || m_e==UINT64); + bool isIntNumeric() const { // Enum increment supported + return (m_e==BIT || m_e==BYTE || m_e==INT || m_e==INTEGER || m_e==LOGIC + || m_e==LONGINT || m_e==SHORTINT || m_e==UINT32 || m_e==UINT64); } - bool isSloppy() const { // Don't be as anal about width warnings - return !(m_e==LOGIC || m_e==BIT); + bool isSloppy() const { // Don't be as anal about width warnings + return !(m_e==LOGIC || m_e==BIT); } - bool isBitLogic() const { // Bit/logic vector types; can form a packed array - return (m_e==LOGIC || m_e==BIT); + bool isBitLogic() const { // Bit/logic vector types; can form a packed array + return (m_e==LOGIC || m_e==BIT); } bool isDpiBitVal() const { // DPI uses svBitVecVal return m_e==BIT; @@ -453,14 +456,14 @@ public: || m_e==LONGINT || m_e==DOUBLE || m_e==SHORTINT || m_e==UINT32 || m_e==UINT64); } bool isOpaque() const { // IE not a simple number we can bit optimize - return (m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR || m_e==DOUBLE || m_e==FLOAT); + return (m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR || m_e==DOUBLE || m_e==FLOAT); } bool isDouble() const { return m_e==DOUBLE; } bool isString() const { return m_e==STRING; } }; - inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstBasicDTypeKwd::en lhs, AstBasicDTypeKwd rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstBasicDTypeKwd lhs, AstBasicDTypeKwd rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstBasicDTypeKwd lhs, AstBasicDTypeKwd::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstBasicDTypeKwd::en lhs, AstBasicDTypeKwd rhs) { return (lhs == rhs.m_e); } //###################################################################### @@ -503,9 +506,9 @@ public: || m_e == REF; } bool isRefOrConstRef() const { return m_e == REF || m_e == CONSTREF; } }; - inline bool operator== (VDirection lhs, VDirection rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (VDirection lhs, VDirection::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (VDirection::en lhs, VDirection rhs) { return (lhs == rhs.m_e); } + inline bool operator==(VDirection lhs, VDirection rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(VDirection lhs, VDirection::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(VDirection::en lhs, VDirection rhs) { return (lhs == rhs.m_e); } inline std::ostream& operator<<(std::ostream& os, const VDirection& rhs) { return os<(_e)) {} const char* ascii() const { static const char* const names[] = { - "FALSE","TRUE","UNK"}; + "FALSE", "TRUE", "UNK"}; return names[m_e]; } bool trueU() const { return m_e == BU_TRUE || m_e == BU_UNKNOWN; } bool falseU() const { return m_e == BU_FALSE || m_e == BU_UNKNOWN; } bool unknown() const { return m_e == BU_UNKNOWN; } }; - inline bool operator== (VBoolOrUnknown lhs, VBoolOrUnknown rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (VBoolOrUnknown lhs, VBoolOrUnknown::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (VBoolOrUnknown::en lhs, VBoolOrUnknown rhs) { return (lhs == rhs.m_e); } + inline bool operator==(VBoolOrUnknown lhs, VBoolOrUnknown rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(VBoolOrUnknown lhs, VBoolOrUnknown::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(VBoolOrUnknown::en lhs, VBoolOrUnknown rhs) { return (lhs == rhs.m_e); } inline std::ostream& operator<<(std::ostream& os, const VBoolOrUnknown& rhs) { return os<(_e)) {} operator en() const { return m_e; } AstBranchPred invert() const { - if (m_e==BP_UNLIKELY) return BP_LIKELY; - else if (m_e==BP_LIKELY) return BP_UNLIKELY; - else return m_e; + if (m_e==BP_UNLIKELY) return BP_LIKELY; + else if (m_e==BP_LIKELY) return BP_UNLIKELY; + else return m_e; } const char* ascii() const { - static const char* const names[] = { - "","VL_LIKELY","VL_UNLIKELY"}; - return names[m_e]; } + static const char* const names[] = { + "", "VL_LIKELY", "VL_UNLIKELY"}; + return names[m_e]; } }; - inline bool operator== (AstBranchPred lhs, AstBranchPred rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstBranchPred lhs, AstBranchPred::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstBranchPred::en lhs, AstBranchPred rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstBranchPred lhs, AstBranchPred rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstBranchPred lhs, AstBranchPred::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstBranchPred::en lhs, AstBranchPred rhs) { return (lhs == rhs.m_e); } inline std::ostream& operator<<(std::ostream& os, const AstBranchPred& rhs) { return os<(_e)) {} operator en() const { return m_e; } const char* ascii() const { - static const char* const names[] = { - "always","always_ff","always_latch","always_comb"}; - return names[m_e]; } + static const char* const names[] = { + "always", "always_ff", "always_latch", "always_comb"}; + return names[m_e]; } }; - inline bool operator== (VAlwaysKwd lhs, VAlwaysKwd rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (VAlwaysKwd lhs, VAlwaysKwd::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (VAlwaysKwd::en lhs, VAlwaysKwd rhs) { return (lhs == rhs.m_e); } + inline bool operator==(VAlwaysKwd lhs, VAlwaysKwd rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(VAlwaysKwd lhs, VAlwaysKwd::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(VAlwaysKwd::en lhs, VAlwaysKwd rhs) { return (lhs == rhs.m_e); } //###################################################################### class VCaseType { public: enum en { - CT_CASE, - CT_CASEX, - CT_CASEZ, - CT_CASEINSIDE + CT_CASE, + CT_CASEX, + CT_CASEZ, + CT_CASEINSIDE }; enum en m_e; inline VCaseType() : m_e(CT_CASE) {} @@ -712,21 +715,21 @@ public: explicit inline VCaseType(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } }; - inline bool operator== (VCaseType lhs, VCaseType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (VCaseType lhs, VCaseType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (VCaseType::en lhs, VCaseType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(VCaseType lhs, VCaseType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(VCaseType lhs, VCaseType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(VCaseType::en lhs, VCaseType rhs) { return (lhs == rhs.m_e); } //###################################################################### class AstDisplayType { public: enum en { - DT_DISPLAY, - DT_WRITE, - DT_INFO, - DT_ERROR, - DT_WARNING, - DT_FATAL + DT_DISPLAY, + DT_WRITE, + DT_INFO, + DT_ERROR, + DT_WARNING, + DT_FATAL }; enum en m_e; inline AstDisplayType() : m_e(DT_DISPLAY) {} @@ -737,21 +740,21 @@ public: bool addNewline() const { return m_e!=DT_WRITE; } bool needScopeTracking() const { return m_e!=DT_DISPLAY && m_e!=DT_WRITE; } const char* ascii() const { - static const char* const names[] = { - "display","write","info","error","warning","fatal"}; - return names[m_e]; } + static const char* const names[] = { + "display", "write", "info", "error", "warning", "fatal"}; + return names[m_e]; } }; - inline bool operator== (AstDisplayType lhs, AstDisplayType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstDisplayType lhs, AstDisplayType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstDisplayType::en lhs, AstDisplayType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstDisplayType lhs, AstDisplayType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstDisplayType lhs, AstDisplayType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstDisplayType::en lhs, AstDisplayType rhs) { return (lhs == rhs.m_e); } //###################################################################### class AstParseRefExp { public: enum en { - PX_NONE, // Used in V3LinkParse only - PX_TEXT // Unknown ID component + PX_NONE, // Used in V3LinkParse only + PX_TEXT // Unknown ID component }; enum en m_e; inline AstParseRefExp() : m_e(PX_NONE) {} @@ -760,13 +763,13 @@ public: explicit inline AstParseRefExp(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } const char* ascii() const { - static const char* const names[] = { - "","TEXT","PREDOT"}; - return names[m_e]; } + static const char* const names[] = { + "", "TEXT", "PREDOT"}; + return names[m_e]; } }; - inline bool operator== (AstParseRefExp lhs, AstParseRefExp rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstParseRefExp lhs, AstParseRefExp::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstParseRefExp::en lhs, AstParseRefExp rhs) { return (lhs == rhs.m_e); } + inline bool operator==(AstParseRefExp lhs, AstParseRefExp rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(AstParseRefExp lhs, AstParseRefExp::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(AstParseRefExp::en lhs, AstParseRefExp rhs) { return (lhs == rhs.m_e); } inline std::ostream& operator<<(std::ostream& os, const AstParseRefExp& rhs) { return os<= LO - int m_lo; // LO + int m_hi; // HI part, HI always >= LO + int m_lo; // LO union { - int mu_flags; - struct { - bool m_ranged:1; // Has a range - bool m_littleEndian:1; // Bit vector is little endian - }; + int mu_flags; + struct { + bool m_ranged:1; // Has a range + bool m_littleEndian:1; // Bit vector is little endian + }; }; - inline bool operator== (const VNumRange& rhs) const { - return m_hi == rhs.m_hi - && m_lo == rhs.m_lo - && mu_flags == rhs.mu_flags; } + inline bool operator==(const VNumRange& rhs) const { + return m_hi == rhs.m_hi + && m_lo == rhs.m_lo + && mu_flags == rhs.mu_flags; } inline bool operator< (const VNumRange& rhs) const { - if ( (m_hi < rhs.m_hi)) return true; - if (!(m_hi == rhs.m_hi)) return false; // lhs > rhs - if ( (m_lo < rhs.m_lo)) return true; - if (!(m_lo == rhs.m_lo)) return false; // lhs > rhs - if ( (mu_flags < rhs.mu_flags)) return true; - if (!(mu_flags == rhs.mu_flags)) return false; // lhs > rhs - return false; + if ( (m_hi < rhs.m_hi)) return true; + if (!(m_hi == rhs.m_hi)) return false; // lhs > rhs + if ( (m_lo < rhs.m_lo)) return true; + if (!(m_lo == rhs.m_lo)) return false; // lhs > rhs + if ( (mu_flags < rhs.mu_flags)) return true; + if (!(mu_flags == rhs.mu_flags)) return false; // lhs > rhs + return false; } // class LeftRight {}; VNumRange() : m_hi(0), m_lo(0), mu_flags(0) {} VNumRange(int hi, int lo, bool littleEndian) - : m_hi(0), m_lo(0), mu_flags(0) - { init(hi,lo,littleEndian); } + : m_hi(0), m_lo(0), mu_flags(0) + { init(hi, lo, littleEndian); } VNumRange(LeftRight, int left, int right) : m_hi(0), m_lo(0), mu_flags(0) { init((right>left)?right:left, (right>left)?left:right, (right>left)); } ~VNumRange() {} // MEMBERS void init(int hi, int lo, bool littleEndian) { - m_hi=hi; m_lo=lo; mu_flags=0; m_ranged=true; m_littleEndian=littleEndian; + m_hi = hi; m_lo = lo; mu_flags = 0; m_ranged = true; m_littleEndian = littleEndian; } int hi() const { return m_hi; } int lo() const { return m_lo; } @@ -818,49 +821,50 @@ struct VNumRange { int elements() const { return hi()-lo()+1; } bool ranged() const { return m_ranged; } bool littleEndian() const { return m_littleEndian; } - int hiMaxSelect() const { return (lo()<0 ? hi()-lo() : hi()); } // Maximum value a [] select may index - bool representableByWidth() const // Could be represented by just width=1, or [width-1:0] - { return (!m_ranged || (m_lo==0 && m_hi>=1 && !m_littleEndian)); } - void dump(std::ostream& str) const { if (ranged()) str<<"["<=1 && !m_littleEndian)); } + void dump(std::ostream& str) const { + if (ranged()) str<<"["< rhs - if ( (m_widthMin < rhs.m_widthMin)) return true; - if (!(m_widthMin == rhs.m_widthMin)) return false; // lhs > rhs - if ( (m_numeric < rhs.m_numeric)) return true; - if (!(m_numeric == rhs.m_numeric)) return false; // lhs > rhs - if ( (m_keyword < rhs.m_keyword)) return true; - if (!(m_keyword == rhs.m_keyword)) return false; // lhs > rhs - if ( (m_nrange < rhs.m_nrange)) return true; - if (!(m_nrange == rhs.m_nrange)) return false; // lhs > rhs - return false; } + if ( (m_width < rhs.m_width)) return true; + if (!(m_width == rhs.m_width)) return false; // lhs > rhs + if ( (m_widthMin < rhs.m_widthMin)) return true; + if (!(m_widthMin == rhs.m_widthMin)) return false; // lhs > rhs + if ( (m_numeric < rhs.m_numeric)) return true; + if (!(m_numeric == rhs.m_numeric)) return false; // lhs > rhs + if ( (m_keyword < rhs.m_keyword)) return true; + if (!(m_keyword == rhs.m_keyword)) return false; // lhs > rhs + if ( (m_nrange < rhs.m_nrange)) return true; + if (!(m_nrange == rhs.m_nrange)) return false; // lhs > rhs + return false; } VBasicTypeKey(int width, int widthMin, AstNumeric numeric, AstBasicDTypeKwd kwd, - const VNumRange& nrange) - : m_width(width), m_widthMin(widthMin), m_numeric(numeric), - m_keyword(kwd), m_nrange(nrange) {} + const VNumRange& nrange) + : m_width(width), m_widthMin(widthMin), m_numeric(numeric), + m_keyword(kwd), m_nrange(nrange) {} ~VBasicTypeKey() {} }; //###################################################################### // AstNUser - Generic base class for AST User nodes. -// - Also used to allow parameter passing up/down iterate calls +// - Also used to allow parameter passing up/down iterate calls class WidthVP; class LinkVP; @@ -871,8 +875,8 @@ class VSymEnt; class VNUser { union { - void* up; - int ui; + void* up; + int ui; } m_u; public: VNUser() {} @@ -882,15 +886,15 @@ public: explicit VNUser(void* p) { m_u.up = p; } ~VNUser() {} // Casters - WidthVP* c() { return ((WidthVP*)m_u.up); } - LinkVP* toLinkVP() { return ((LinkVP*)m_u.up); } - VSymEnt* toSymEnt() { return ((VSymEnt*)m_u.up); } - AstNode* toNodep() { return ((AstNode*)m_u.up); } + WidthVP* c() { return ((WidthVP*)m_u.up); } + LinkVP* toLinkVP() { return ((LinkVP*)m_u.up); } + VSymEnt* toSymEnt() { return ((VSymEnt*)m_u.up); } + AstNode* toNodep() { return ((AstNode*)m_u.up); } OrderBlockNU* toOrderBlock() { return ((OrderBlockNU*)m_u.up); } - OrderVarNU* toOrderVar() { return ((OrderVarNU*)m_u.up); } + OrderVarNU* toOrderVar() { return ((OrderVarNU*)m_u.up); } V3GraphVertex* toGraphVertex() { return ((V3GraphVertex*)m_u.up); } - inline int toInt() { - return m_u.ui; + inline int toInt() { + return m_u.ui; } static inline VNUser fromZero() { return VNUser(0); } static inline VNUser fromInt(int i) { return VNUser(i); } @@ -901,7 +905,7 @@ public: // // Where AstNode->user2() is going to be used, for example, you write: // -// AstUser2InUse m_userres; +// AstUser2InUse m_userres; // // This will clear the tree, and prevent another visitor from clobering // user2. When the member goes out of scope it will be automagically @@ -909,26 +913,26 @@ public: class AstUserInUseBase { protected: - static void allocate(int id, uint32_t& cntGblRef, bool& userBusyRef) { - // Perhaps there's still a AstUserInUse in scope for this? - UASSERT_STATIC(!userBusyRef, "Conflicting user use; AstUser"+cvtToStr(id)+"InUse request when under another AstUserInUse"); - userBusyRef = true; - clearcnt(id, cntGblRef, userBusyRef); + static void allocate(int id, uint32_t& cntGblRef, bool& userBusyRef) { + // Perhaps there's still a AstUserInUse in scope for this? + UASSERT_STATIC(!userBusyRef, "Conflicting user use; AstUser"+cvtToStr(id)+"InUse request when under another AstUserInUse"); + userBusyRef = true; + clearcnt(id, cntGblRef, userBusyRef); } - static void free(int id, uint32_t& cntGblRef, bool& userBusyRef) { - UASSERT_STATIC(userBusyRef, "Free of User"+cvtToStr(id)+"() not under AstUserInUse"); - clearcnt(id, cntGblRef, userBusyRef); // Includes a checkUse for us - userBusyRef = false; + static void free(int id, uint32_t& cntGblRef, bool& userBusyRef) { + UASSERT_STATIC(userBusyRef, "Free of User"+cvtToStr(id)+"() not under AstUserInUse"); + clearcnt(id, cntGblRef, userBusyRef); // Includes a checkUse for us + userBusyRef = false; } static void clearcnt(int id, uint32_t& cntGblRef, bool& userBusyRef) { - UASSERT_STATIC(userBusyRef, "Clear of User"+cvtToStr(id)+"() not under AstUserInUse"); - // If this really fires and is real (after 2^32 edits???) - // we could just walk the tree and clear manually - ++cntGblRef; - UASSERT_STATIC(cntGblRef, "User*() overflowed!"); + UASSERT_STATIC(userBusyRef, "Clear of User"+cvtToStr(id)+"() not under AstUserInUse"); + // If this really fires and is real (after 2^32 edits???) + // we could just walk the tree and clear manually + ++cntGblRef; + UASSERT_STATIC(cntGblRef, "User*() overflowed!"); } static void checkcnt(int id, uint32_t&, bool& userBusyRef) { - UASSERT_STATIC(userBusyRef, "Check of User"+cvtToStr(id)+"() failed, not under AstUserInUse"); + UASSERT_STATIC(userBusyRef, "Check of User"+cvtToStr(id)+"() failed, not under AstUserInUse"); } }; @@ -938,8 +942,8 @@ protected: class AstUser1InUse : AstUserInUseBase { protected: friend class AstNode; - static uint32_t s_userCntGbl; // Count of which usage of userp() this is - static bool s_userBusy; // Count is in use + static uint32_t s_userCntGbl; // Count of which usage of userp() this is + static bool s_userBusy; // Count is in use public: AstUser1InUse() { allocate(1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } ~AstUser1InUse() { free (1, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } @@ -949,46 +953,46 @@ public: class AstUser2InUse : AstUserInUseBase { protected: friend class AstNode; - static uint32_t s_userCntGbl; // Count of which usage of userp() this is - static bool s_userBusy; // Count is in use + static uint32_t s_userCntGbl; // Count of which usage of userp() this is + static bool s_userBusy; // Count is in use public: - AstUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - ~AstUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void clear() { clearcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void check() { checkcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + AstUser2InUse() { allocate(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + ~AstUser2InUse() { free (2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void clear() { clearcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void check() { checkcnt(2, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } }; class AstUser3InUse : AstUserInUseBase { protected: friend class AstNode; - static uint32_t s_userCntGbl; // Count of which usage of userp() this is - static bool s_userBusy; // Count is in use + static uint32_t s_userCntGbl; // Count of which usage of userp() this is + static bool s_userBusy; // Count is in use public: - AstUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - ~AstUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void clear() { clearcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void check() { checkcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + AstUser3InUse() { allocate(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + ~AstUser3InUse() { free (3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void clear() { clearcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void check() { checkcnt(3, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } }; class AstUser4InUse : AstUserInUseBase { protected: friend class AstNode; - static uint32_t s_userCntGbl; // Count of which usage of userp() this is - static bool s_userBusy; // Count is in use + static uint32_t s_userCntGbl; // Count of which usage of userp() this is + static bool s_userBusy; // Count is in use public: - AstUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - ~AstUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void clear() { clearcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void check() { checkcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + AstUser4InUse() { allocate(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + ~AstUser4InUse() { free (4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void clear() { clearcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void check() { checkcnt(4, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } }; class AstUser5InUse : AstUserInUseBase { protected: friend class AstNode; - static uint32_t s_userCntGbl; // Count of which usage of userp() this is - static bool s_userBusy; // Count is in use + static uint32_t s_userCntGbl; // Count of which usage of userp() this is + static bool s_userBusy; // Count is in use public: - AstUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - ~AstUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void clear() { clearcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } - static void check() { checkcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + AstUser5InUse() { allocate(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + ~AstUser5InUse() { free (5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void clear() { clearcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } + static void check() { checkcnt(5, s_userCntGbl/*ref*/, s_userBusy/*ref*/); } }; //###################################################################### @@ -1008,13 +1012,13 @@ public: /// than an immediate deleteTree, as any pointers into this node will /// persist for the lifetime of the visitor void pushDeletep(AstNode* nodep) { - m_deleteps.push_back(nodep); + m_deleteps.push_back(nodep); } /// Call deleteTree on all previously pushDeletep()'ed nodes void doDeletes(); public: virtual ~AstNVisitor() { - doDeletes(); + doDeletes(); } /// Call visit()s on nodep void iterate(AstNode* nodep); @@ -1031,7 +1035,7 @@ public: /// Return edited nodep; see comments in V3Ast.cpp AstNode* iterateSubtreeReturnEdits(AstNode* nodep); -#include "V3Ast__gen_visitor.h" // From ./astgen +#include "V3Ast__gen_visitor.h" // From ./astgen // Things like: // virtual void visit(AstBreak* nodep) { visit((AstNodeStmt*)(nodep)); } // virtual void visit(AstNodeStmt* nodep) { visit((AstNode*)(nodep)); } @@ -1045,19 +1049,20 @@ class AstNRelinker { protected: friend class AstNode; enum RelinkWhatEn { - RELINK_BAD, RELINK_NEXT, RELINK_OP1, RELINK_OP2, RELINK_OP3, RELINK_OP4 + RELINK_BAD, RELINK_NEXT, RELINK_OP1, RELINK_OP2, RELINK_OP3, RELINK_OP4 }; - AstNode* m_oldp; // The old node that was linked to this point in the tree + AstNode* m_oldp; // The old node that was linked to this point in the tree AstNode* m_backp; RelinkWhatEn m_chg; AstNode** m_iterpp; public: - AstNRelinker() { m_oldp=NULL; m_backp=NULL; m_chg=RELINK_BAD; m_iterpp=NULL;} + AstNRelinker() { m_oldp = NULL; m_backp = NULL; m_chg = RELINK_BAD; m_iterpp = NULL; } void relink(AstNode* newp); AstNode* oldp() const { return m_oldp; } void dump(std::ostream& str=std::cout) const; }; -inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) { rhs.dump(os); return os;} +inline std::ostream& operator<<(std::ostream& os, const AstNRelinker& rhs) { + rhs.dump(os); return os; } //###################################################################### // V3Hash -- Node hashing for V3Combine @@ -1066,12 +1071,12 @@ class V3Hash { // A hash of a tree of nodes, consisting of 8 bits with the number of nodes in the hash // and 24 bit value hash of relevant information about the node. // A value of 0 is illegal - uint32_t m_both; + uint32_t m_both; static const uint32_t M24 = ((1<<24)-1); void setBoth(uint32_t depth, uint32_t hshval) { - if (depth==0) depth=1; - if (depth>255) depth=255; - m_both = (depth<<24) | (hshval & M24); + if (depth==0) depth = 1; + if (depth>255) depth = 255; + m_both = (depth<<24) | (hshval & M24); } public: // METHODS @@ -1080,32 +1085,32 @@ public: uint32_t depth() const { return (m_both >> 24) & 255; } uint32_t hshval() const { return m_both & M24; } // OPERATORS - inline bool operator== (const V3Hash& rh) const { return m_both==rh.m_both; } - inline bool operator!= (const V3Hash& rh) const { return m_both!=rh.m_both; } - inline bool operator< (const V3Hash& rh) const { return m_bothm_backp = this; } - void op2p(AstNode* nodep) { m_op2p = nodep; if (nodep) nodep->m_backp = this; } - void op3p(AstNode* nodep) { m_op3p = nodep; if (nodep) nodep->m_backp = this; } - void op4p(AstNode* nodep) { m_op4p = nodep; if (nodep) nodep->m_backp = this; } + void op1p(AstNode* nodep) { m_op1p = nodep; if (nodep) nodep->m_backp = this; } + void op2p(AstNode* nodep) { m_op2p = nodep; if (nodep) nodep->m_backp = this; } + void op3p(AstNode* nodep) { m_op3p = nodep; if (nodep) nodep->m_backp = this; } + void op4p(AstNode* nodep) { m_op4p = nodep; if (nodep) nodep->m_backp = this; } - void init(); // initialize value of AstNode + void init(); // initialize value of AstNode private: - AstNode* cloneTreeIter(); - AstNode* cloneTreeIterList(); - void checkTreeIter(AstNode* backp); - void checkTreeIterList(AstNode* backp); + AstNode* cloneTreeIter(); + AstNode* cloneTreeIterList(); + void checkTreeIter(AstNode* backp); + void checkTreeIterList(AstNode* backp); bool gateTreeIter() const; - static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, bool ignNext, bool gateOnly); - void deleteTreeIter(); - void deleteNode(); + static bool sameTreeIter(const AstNode* node1p, const AstNode* node2p, + bool ignNext, bool gateOnly); + void deleteTreeIter(); + void deleteNode(); public: - static void relinkOneLink(AstNode*& pointpr, AstNode* newp); + static void relinkOneLink(AstNode*& pointpr, AstNode* newp); // cppcheck-suppress functionConst - void debugTreeChange(const char* prefix, int lineno, bool next); + void debugTreeChange(const char* prefix, int lineno, bool next); protected: // CONSTUCTORS AstNode() {init(); } explicit AstNode(FileLine* fileline) {init(); m_fileline = fileline; } - virtual AstNode* clone() = 0; // Generally, cloneTree is what you want instead + virtual AstNode* clone() = 0; // Generally, cloneTree is what you want instead virtual void cloneRelink() {} - void cloneRelinkTree(); + void cloneRelinkTree(); // METHODS - void setOp1p(AstNode* newp); // Set non-list-type op1 to non-list element - void setOp2p(AstNode* newp); // Set non-list-type op2 to non-list element - void setOp3p(AstNode* newp); // Set non-list-type op3 to non-list element - void setOp4p(AstNode* newp); // Set non-list-type op4 to non-list element + void setOp1p(AstNode* newp); // Set non-list-type op1 to non-list element + void setOp2p(AstNode* newp); // Set non-list-type op2 to non-list element + void setOp3p(AstNode* newp); // Set non-list-type op3 to non-list element + void setOp4p(AstNode* newp); // Set non-list-type op4 to non-list element - void setNOp1p(AstNode* newp) { if (newp) setOp1p(newp); } - void setNOp2p(AstNode* newp) { if (newp) setOp2p(newp); } - void setNOp3p(AstNode* newp) { if (newp) setOp3p(newp); } - void setNOp4p(AstNode* newp) { if (newp) setOp4p(newp); } + void setNOp1p(AstNode* newp) { if (newp) setOp1p(newp); } + void setNOp2p(AstNode* newp) { if (newp) setOp2p(newp); } + void setNOp3p(AstNode* newp) { if (newp) setOp3p(newp); } + void setNOp4p(AstNode* newp) { if (newp) setOp4p(newp); } - void addOp1p(AstNode* newp); // Append newp to end of op1 - void addOp2p(AstNode* newp); // Append newp to end of op2 - void addOp3p(AstNode* newp); // Append newp to end of op3 - void addOp4p(AstNode* newp); // Append newp to end of op4 + void addOp1p(AstNode* newp); // Append newp to end of op1 + void addOp2p(AstNode* newp); // Append newp to end of op2 + void addOp3p(AstNode* newp); // Append newp to end of op3 + void addOp4p(AstNode* newp); // Append newp to end of op4 - void addNOp1p(AstNode* newp) { if (newp) addOp1p(newp); } - void addNOp2p(AstNode* newp) { if (newp) addOp2p(newp); } - void addNOp3p(AstNode* newp) { if (newp) addOp3p(newp); } - void addNOp4p(AstNode* newp) { if (newp) addOp4p(newp); } + void addNOp1p(AstNode* newp) { if (newp) addOp1p(newp); } + void addNOp2p(AstNode* newp) { if (newp) addOp2p(newp); } + void addNOp3p(AstNode* newp) { if (newp) addOp3p(newp); } + void addNOp4p(AstNode* newp) { if (newp) addOp4p(newp); } - void clonep(AstNode* nodep) { m_clonep=nodep; m_cloneCnt=s_cloneCntGbl; } - static void cloneClearTree() { s_cloneCntGbl++; UASSERT_STATIC(s_cloneCntGbl,"Rollover"); } + void clonep(AstNode* nodep) { m_clonep = nodep; m_cloneCnt = s_cloneCntGbl; } + static void cloneClearTree() { s_cloneCntGbl++; UASSERT_STATIC(s_cloneCntGbl, "Rollover"); } public: // ACCESSORS - virtual AstType type() const = 0; - const char* typeName() const { return type().ascii(); } // See also prettyTypeName - AstNode* nextp() const { return m_nextp; } - AstNode* backp() const { return m_backp; } - AstNode* op1p() const { return m_op1p; } - AstNode* op2p() const { return m_op2p; } - AstNode* op3p() const { return m_op3p; } - AstNode* op4p() const { return m_op4p; } + virtual AstType type() const = 0; + const char* typeName() const { return type().ascii(); } // See also prettyTypeName + AstNode* nextp() const { return m_nextp; } + AstNode* backp() const { return m_backp; } + AstNode* op1p() const { return m_op1p; } + AstNode* op2p() const { return m_op2p; } + AstNode* op3p() const { return m_op3p; } + AstNode* op4p() const { return m_op4p; } AstNodeDType* dtypep() const { return m_dtypep; } - AstNode* clonep() const { return ((m_cloneCnt==s_cloneCntGbl)?m_clonep:NULL); } + AstNode* clonep() const { return ((m_cloneCnt==s_cloneCntGbl)?m_clonep:NULL); } AstNode* firstAbovep() const { // Returns NULL when second or later in list return ((backp() && backp()->nextp()!=this) ? backp() : NULL); } - bool brokeExists() const; - bool brokeExistsAbove() const; + bool brokeExists() const; + bool brokeExistsAbove() const; // CONSTRUCTORS virtual ~AstNode() {} @@ -1236,18 +1242,18 @@ public: #endif // CONSTANT ACCESSORS - static int instrCountBranch() { return 4; } ///< Instruction cycles to branch - static int instrCountDiv() { return 10; } ///< Instruction cycles to divide - static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function - static int instrCountLd() { return 2; } ///< Instruction cycles to load memory - static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers - static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines - static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats - static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats - static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do triganomics - static int instrCountString() { return 100; } ///< Instruction cycles to do string ops - static int instrCountCall() { return instrCountBranch()+10; } ///< Instruction cycles to call subroutine - static int instrCountTime() { return instrCountCall()+5; } ///< Instruction cycles to determine simulation time + static int instrCountBranch() { return 4; } ///< Instruction cycles to branch + static int instrCountDiv() { return 10; } ///< Instruction cycles to divide + static int instrCountDpi() { return 1000; } ///< Instruction cycles to call user function + static int instrCountLd() { return 2; } ///< Instruction cycles to load memory + static int instrCountMul() { return 3; } ///< Instruction cycles to multiply integers + static int instrCountPli() { return 20; } ///< Instruction cycles to call pli routines + static int instrCountDouble() { return 8; } ///< Instruction cycles to convert or do floats + static int instrCountDoubleDiv() { return 40; } ///< Instruction cycles to divide floats + static int instrCountDoubleTrig() { return 200; } ///< Instruction cycles to do triganomics + static int instrCountString() { return 100; } ///< Instruction cycles to do string ops + static int instrCountCall() { return instrCountBranch()+10; } ///< Instruction cycles to call subroutine + static int instrCountTime() { return instrCountCall()+5; } ///< Instruction cycles to determine simulation time // ACCESSORS virtual string name() const { return ""; } @@ -1255,111 +1261,111 @@ public: virtual void tag(const string& text) {} virtual string tag() const { return ""; } virtual string verilogKwd() const { return ""; } - string shortName() const; // Name with __PVT__ removed for concatenating scopes - static string dedotName(const string& namein); // Name with dots removed - static string prettyName(const string& namein); // Name for printing out to the user - static string encodeName(const string& namein); // Encode user name into internal C representation + string shortName() const; // Name with __PVT__ removed for concatenating scopes + static string dedotName(const string& namein); // Name with dots removed + static string prettyName(const string& namein); // Name for printing out to the user + static string encodeName(const string& namein); // Encode user name into internal C representation static string encodeNumber(vlsint64_t num); // Encode number into internal C representation static string vcdName(const string& namein); // Name for printing out to vcd files - string prettyName() const { return prettyName(name()); } - string prettyTypeName() const; // "VARREF" for error messages + string prettyName() const { return prettyName(name()); } + string prettyTypeName() const; // "VARREF" for error messages virtual string prettyOperatorName() const { return "operator "+prettyTypeName(); } - FileLine* fileline() const { return m_fileline; } - void fileline(FileLine* fl) { m_fileline=fl; } - bool width1() const; - int widthInstrs() const; - void didWidth(bool flag) { m_didWidth=flag; } - bool didWidth() const { return m_didWidth; } - bool didWidthAndSet() { if (didWidth()) return true; didWidth(true); return false;} - void doingWidth(bool flag) { m_doingWidth=flag; } - bool doingWidth() const { return m_doingWidth; } + FileLine* fileline() const { return m_fileline; } + void fileline(FileLine* fl) { m_fileline = fl; } + bool width1() const; + int widthInstrs() const; + void didWidth(bool flag) { m_didWidth = flag; } + bool didWidth() const { return m_didWidth; } + bool didWidthAndSet() { if (didWidth()) return true; didWidth(true); return false; } + void doingWidth(bool flag) { m_doingWidth = flag; } + bool doingWidth() const { return m_doingWidth; } //TODO stomp these width functions out, and call via dtypep() instead - int width() const; - int widthMin() const; - int widthMinV() const { return v3Global.widthMinUsage()==VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width(); } - int widthWords() const { return VL_WORDS_I(width()); } - bool isQuad() const { return (width()>VL_WORDSIZE && width()<=VL_QUADSIZE); } - bool isWide() const { return (width()>VL_QUADSIZE); } - bool isDouble() const; - bool isSigned() const; - bool isString() const; + int width() const; + int widthMin() const; + int widthMinV() const { return v3Global.widthMinUsage()==VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width(); } + int widthWords() const { return VL_WORDS_I(width()); } + bool isQuad() const { return (width()>VL_WORDSIZE && width()<=VL_QUADSIZE); } + bool isWide() const { return (width()>VL_QUADSIZE); } + bool isDouble() const; + bool isSigned() const; + bool isString() const; - VNUser user1u() const { - // Slows things down measurably, so disabled by default - //UASSERT_STATIC(AstUser1InUse::s_userBusy, "userp set w/o busy"); - return ((m_user1Cnt==AstUser1InUse::s_userCntGbl) ? m_user1u : VNUser(0)); + VNUser user1u() const { + // Slows things down measurably, so disabled by default + //UASSERT_STATIC(AstUser1InUse::s_userBusy, "userp set w/o busy"); + return ((m_user1Cnt==AstUser1InUse::s_userCntGbl) ? m_user1u : VNUser(0)); } - AstNode* user1p() const { return user1u().toNodep(); } - void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=AstUser1InUse::s_userCntGbl; } - void user1p(void* userp) { user1u(VNUser(userp)); } - int user1() const { return user1u().toInt(); } - void user1(int val) { user1u(VNUser(val)); } - int user1Inc(int val=1) { int v=user1(); user1(v+val); return v; } - int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc() - static void user1ClearTree() { AstUser1InUse::clear(); } // Clear userp()'s across the entire tree + AstNode* user1p() const { return user1u().toNodep(); } + void user1u(const VNUser& user) { m_user1u=user; m_user1Cnt=AstUser1InUse::s_userCntGbl; } + void user1p(void* userp) { user1u(VNUser(userp)); } + int user1() const { return user1u().toInt(); } + void user1(int val) { user1u(VNUser(val)); } + int user1Inc(int val=1) { int v=user1(); user1(v+val); return v; } + int user1SetOnce() { int v=user1(); if (!v) user1(1); return v; } // Better for cache than user1Inc() + static void user1ClearTree() { AstUser1InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user2u() const { - // Slows things down measurably, so disabled by default - //UASSERT_STATIC(AstUser2InUse::s_userBusy, "userp set w/o busy"); - return ((m_user2Cnt==AstUser2InUse::s_userCntGbl) ? m_user2u : VNUser(0)); + VNUser user2u() const { + // Slows things down measurably, so disabled by default + //UASSERT_STATIC(AstUser2InUse::s_userBusy, "userp set w/o busy"); + return ((m_user2Cnt==AstUser2InUse::s_userCntGbl) ? m_user2u : VNUser(0)); } - AstNode* user2p() const { return user2u().toNodep(); } - void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=AstUser2InUse::s_userCntGbl; } - void user2p(void* userp) { user2u(VNUser(userp)); } - int user2() const { return user2u().toInt(); } - void user2(int val) { user2u(VNUser(val)); } - int user2Inc(int val=1) { int v=user2(); user2(v+val); return v; } - int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc() - static void user2ClearTree() { AstUser2InUse::clear(); } // Clear userp()'s across the entire tree + AstNode* user2p() const { return user2u().toNodep(); } + void user2u(const VNUser& user) { m_user2u=user; m_user2Cnt=AstUser2InUse::s_userCntGbl; } + void user2p(void* userp) { user2u(VNUser(userp)); } + int user2() const { return user2u().toInt(); } + void user2(int val) { user2u(VNUser(val)); } + int user2Inc(int val=1) { int v=user2(); user2(v+val); return v; } + int user2SetOnce() { int v=user2(); if (!v) user2(1); return v; } // Better for cache than user2Inc() + static void user2ClearTree() { AstUser2InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user3u() const { - // Slows things down measurably, so disabled by default - //UASSERT_STATIC(AstUser3InUse::s_userBusy, "userp set w/o busy"); - return ((m_user3Cnt==AstUser3InUse::s_userCntGbl) ? m_user3u : VNUser(0)); + VNUser user3u() const { + // Slows things down measurably, so disabled by default + //UASSERT_STATIC(AstUser3InUse::s_userBusy, "userp set w/o busy"); + return ((m_user3Cnt==AstUser3InUse::s_userCntGbl) ? m_user3u : VNUser(0)); } - AstNode* user3p() const { return user3u().toNodep(); } - void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=AstUser3InUse::s_userCntGbl; } - void user3p(void* userp) { user3u(VNUser(userp)); } - int user3() const { return user3u().toInt(); } - void user3(int val) { user3u(VNUser(val)); } - int user3Inc(int val=1) { int v=user3(); user3(v+val); return v; } - int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc() - static void user3ClearTree() { AstUser3InUse::clear(); } // Clear userp()'s across the entire tree + AstNode* user3p() const { return user3u().toNodep(); } + void user3u(const VNUser& user) { m_user3u=user; m_user3Cnt=AstUser3InUse::s_userCntGbl; } + void user3p(void* userp) { user3u(VNUser(userp)); } + int user3() const { return user3u().toInt(); } + void user3(int val) { user3u(VNUser(val)); } + int user3Inc(int val=1) { int v=user3(); user3(v+val); return v; } + int user3SetOnce() { int v=user3(); if (!v) user3(1); return v; } // Better for cache than user3Inc() + static void user3ClearTree() { AstUser3InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user4u() const { - // Slows things down measurably, so disabled by default - //UASSERT_STATIC(AstUser4InUse::s_userBusy, "userp set w/o busy"); - return ((m_user4Cnt==AstUser4InUse::s_userCntGbl) ? m_user4u : VNUser(0)); + VNUser user4u() const { + // Slows things down measurably, so disabled by default + //UASSERT_STATIC(AstUser4InUse::s_userBusy, "userp set w/o busy"); + return ((m_user4Cnt==AstUser4InUse::s_userCntGbl) ? m_user4u : VNUser(0)); } - AstNode* user4p() const { return user4u().toNodep(); } - void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=AstUser4InUse::s_userCntGbl; } - void user4p(void* userp) { user4u(VNUser(userp)); } - int user4() const { return user4u().toInt(); } - void user4(int val) { user4u(VNUser(val)); } - int user4Inc(int val=1) { int v=user4(); user4(v+val); return v; } - int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc() - static void user4ClearTree() { AstUser4InUse::clear(); } // Clear userp()'s across the entire tree + AstNode* user4p() const { return user4u().toNodep(); } + void user4u(const VNUser& user) { m_user4u=user; m_user4Cnt=AstUser4InUse::s_userCntGbl; } + void user4p(void* userp) { user4u(VNUser(userp)); } + int user4() const { return user4u().toInt(); } + void user4(int val) { user4u(VNUser(val)); } + int user4Inc(int val=1) { int v=user4(); user4(v+val); return v; } + int user4SetOnce() { int v=user4(); if (!v) user4(1); return v; } // Better for cache than user4Inc() + static void user4ClearTree() { AstUser4InUse::clear(); } // Clear userp()'s across the entire tree - VNUser user5u() const { - // Slows things down measurably, so disabled by default - //UASSERT_STATIC(AstUser5InUse::s_userBusy, "userp set w/o busy"); - return ((m_user5Cnt==AstUser5InUse::s_userCntGbl) ? m_user5u : VNUser(0)); + VNUser user5u() const { + // Slows things down measurably, so disabled by default + //UASSERT_STATIC(AstUser5InUse::s_userBusy, "userp set w/o busy"); + return ((m_user5Cnt==AstUser5InUse::s_userCntGbl) ? m_user5u : VNUser(0)); } - AstNode* user5p() const { return user5u().toNodep(); } - void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=AstUser5InUse::s_userCntGbl; } - void user5p(void* userp) { user5u(VNUser(userp)); } - int user5() const { return user5u().toInt(); } - void user5(int val) { user5u(VNUser(val)); } - int user5Inc(int val=1) { int v=user5(); user5(v+val); return v; } - int user5SetOnce() { int v=user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc() - static void user5ClearTree() { AstUser5InUse::clear(); } // Clear userp()'s across the entire tree + AstNode* user5p() const { return user5u().toNodep(); } + void user5u(const VNUser& user) { m_user5u=user; m_user5Cnt=AstUser5InUse::s_userCntGbl; } + void user5p(void* userp) { user5u(VNUser(userp)); } + int user5() const { return user5u().toInt(); } + void user5(int val) { user5u(VNUser(val)); } + int user5Inc(int val=1) { int v=user5(); user5(v+val); return v; } + int user5SetOnce() { int v=user5(); if (!v) user5(1); return v; } // Better for cache than user5Inc() + static void user5ClearTree() { AstUser5InUse::clear(); } // Clear userp()'s across the entire tree - vluint64_t editCount() const { return m_editCount; } - void editCountInc() { m_editCount = ++s_editCntGbl; } // Preincrement, so can "watch AstNode::s_editCntGbl=##" - static vluint64_t editCountLast() { return s_editCntLast; } - static vluint64_t editCountGbl() { return s_editCntGbl; } - static void editCountSetLast() { s_editCntLast = editCountGbl(); } + vluint64_t editCount() const { return m_editCount; } + void editCountInc() { m_editCount = ++s_editCntGbl; } // Preincrement, so can "watch AstNode::s_editCntGbl=##" + static vluint64_t editCountLast() { return s_editCntLast; } + static vluint64_t editCountGbl() { return s_editCntGbl; } + static void editCountSetLast() { s_editCntLast = editCountGbl(); } // ACCESSORS for specific types // Alas these can't be virtual or they break when passed a NULL @@ -1370,27 +1376,29 @@ public: bool isAllOnesV() const; // Verilog width rules apply // METHODS - data type changes especially for initial creation - void dtypep(AstNodeDType* nodep) { if (m_dtypep != nodep) { m_dtypep = nodep; editCountInc(); } } - void dtypeFrom(AstNode* fromp) { if (fromp) { dtypep(fromp->dtypep()); }} - void dtypeChgSigned(bool flag=true); - void dtypeChgWidth(int width, int widthMin); - void dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric); - void dtypeSetBitSized(int width, int widthMin, AstNumeric numeric) { dtypep(findBitDType(width,widthMin,numeric)); } - void dtypeSetLogicSized(int width, int widthMin, AstNumeric numeric) { dtypep(findLogicDType(width,widthMin,numeric)); } - void dtypeSetLogicBool() { dtypep(findLogicBoolDType()); } - void dtypeSetDouble() { dtypep(findDoubleDType()); } - void dtypeSetString() { dtypep(findStringDType()); } - void dtypeSetSigned32() { dtypep(findSigned32DType()); } - void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate - void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate + void dtypep(AstNodeDType* nodep) { if (m_dtypep != nodep) { m_dtypep = nodep; editCountInc(); } } + void dtypeFrom(AstNode* fromp) { if (fromp) { dtypep(fromp->dtypep()); }} + void dtypeChgSigned(bool flag=true); + void dtypeChgWidth(int width, int widthMin); + void dtypeChgWidthSigned(int width, int widthMin, AstNumeric numeric); + void dtypeSetBitSized(int width, int widthMin, AstNumeric numeric) { + dtypep(findBitDType(width, widthMin, numeric)); } + void dtypeSetLogicSized(int width, int widthMin, AstNumeric numeric) { + dtypep(findLogicDType(width, widthMin, numeric)); } + void dtypeSetLogicBool() { dtypep(findLogicBoolDType()); } + void dtypeSetDouble() { dtypep(findDoubleDType()); } + void dtypeSetString() { dtypep(findStringDType()); } + void dtypeSetSigned32() { dtypep(findSigned32DType()); } + void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate + void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate // Data type locators - AstNodeDType* findLogicBoolDType() { return findBasicDType(AstBasicDTypeKwd::LOGIC); } - AstNodeDType* findDoubleDType() { return findBasicDType(AstBasicDTypeKwd::DOUBLE); } - AstNodeDType* findStringDType() { return findBasicDType(AstBasicDTypeKwd::STRING); } - AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); } - AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } // Twostate - AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } // Twostate + AstNodeDType* findLogicBoolDType() { return findBasicDType(AstBasicDTypeKwd::LOGIC); } + AstNodeDType* findDoubleDType() { return findBasicDType(AstBasicDTypeKwd::DOUBLE); } + AstNodeDType* findStringDType() { return findBasicDType(AstBasicDTypeKwd::STRING); } + AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); } + AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } // Twostate + AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } // Twostate AstNodeDType* findBitDType(int width, int widthMin, AstNumeric numeric) const; AstNodeDType* findLogicDType(int width, int widthMin, AstNumeric numeric) const; AstNodeDType* findLogicRangeDType(VNumRange range, int widthMin, AstNumeric numeric) const; @@ -1398,28 +1406,28 @@ public: AstBasicDType* findInsertSameDType(AstBasicDType* nodep); // METHODS - dump and error - void v3errorEnd(std::ostringstream& str) const; - void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN; - string warnMore() const; + void v3errorEnd(std::ostringstream& str) const; + void v3errorEndFatal(std::ostringstream& str) const VL_ATTR_NORETURN; + string warnMore() const; virtual void dump(std::ostream& str=std::cout); - void dumpGdb(); // For GDB only - void dumpGdbHeader() const; + void dumpGdb(); // For GDB only + void dumpGdbHeader() const; // METHODS - Tree modifications - static AstNode* addNext(AstNode* nodep, AstNode* newp); // Returns nodep, adds newp to end of nodep's list - static AstNode* addNextNull(AstNode* nodep, AstNode* newp); // Returns nodep, adds newp (maybe NULL) to end of nodep's list + static AstNode* addNext(AstNode* nodep, AstNode* newp); // Returns nodep, adds newp to end of nodep's list + static AstNode* addNextNull(AstNode* nodep, AstNode* newp); // Returns nodep, adds newp (maybe NULL) to end of nodep's list inline AstNode* addNext(AstNode* newp) { return addNext(this, newp); } inline AstNode* addNextNull(AstNode* newp) { return addNextNull(this, newp); } void addNextHere(AstNode* newp); // Insert newp at this->nextp - void addPrev(AstNode* newp) { replaceWith(newp); newp->addNext(this); } - void addHereThisAsNext(AstNode* newp); // Adds at old place of this, this becomes next - void replaceWith(AstNode* newp); // Replace current node in tree with new node - AstNode* unlinkFrBack(AstNRelinker* linkerp=NULL); // Unlink this from whoever points to it. + void addPrev(AstNode* newp) { replaceWith(newp); newp->addNext(this); } + void addHereThisAsNext(AstNode* newp); // Adds at old place of this, this becomes next + void replaceWith(AstNode* newp); // Replace current node in tree with new node + AstNode* unlinkFrBack(AstNRelinker* linkerp=NULL); // Unlink this from whoever points to it. // Unlink this from whoever points to it, keep entire next list with unlinked node - AstNode* unlinkFrBackWithNext(AstNRelinker* linkerp=NULL); - void swapWith(AstNode* bp); - void relink(AstNRelinker* linkerp); // Generally use linker->relink() instead - void cloneRelinkNode() { cloneRelink(); } + AstNode* unlinkFrBackWithNext(AstNRelinker* linkerp=NULL); + void swapWith(AstNode* bp); + void relink(AstNRelinker* linkerp); // Generally use linker->relink() instead + void cloneRelinkNode() { cloneRelink(); } // Iterate and insert - assumes tree format virtual void addNextStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // When calling, "this" is second argument @@ -1427,38 +1435,39 @@ public: // METHODS - Iterate on a tree static AstNode* cloneTreeNull(AstNode* nodep, bool cloneNextLink) { // Clone or return NULL if NULL return nodep ? nodep->cloneTree(cloneNextLink) : NULL; } - AstNode* cloneTree(bool cloneNextLink); - bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable? - bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p? - bool sameGateTree(const AstNode* node2p) const; // Does tree of this == node2p?, not allowing non-isGateOptimizable - void deleteTree(); // Always deletes the next link - void checkTree(); // User Interface version - void checkIter() const; - void clearIter() { m_iterpp=NULL; } + AstNode* cloneTree(bool cloneNextLink); + bool gateTree() { return gateTreeIter(); } // Is tree isGateOptimizable? + bool sameTree(const AstNode* node2p) const; // Does tree of this == node2p? + bool sameGateTree(const AstNode* node2p) const; // Does tree of this == node2p?, not allowing non-isGateOptimizable + void deleteTree(); // Always deletes the next link + void checkTree(); // User Interface version + void checkIter() const; + void clearIter() { m_iterpp = NULL; } void dumpPtrs(std::ostream& os=std::cout) const; void dumpTree(std::ostream& os=std::cout, const string& indent=" ", int maxDepth=0); - void dumpTree(const string& indent, int maxDepth=0) { dumpTree(cout,indent,maxDepth); } + void dumpTree(const string& indent, int maxDepth=0) { dumpTree(cout, indent, maxDepth); } void dumpTreeGdb(); // For GDB only void dumpTreeAndNext(std::ostream& os=std::cout, const string& indent=" ", int maxDepth=0); void dumpTreeFile(const string& filename, bool append=false, bool doDump=true); static void dumpTreeFileGdb(const char* filenamep=NULL); // METHODS - queries - virtual bool isPure() const { return true; } // Else a $display, etc, that must be ordered with other displays - virtual bool isBrancher() const { return false; } // Changes control flow, disable some optimizations - virtual bool isGateOptimizable() const { return true; } // Else a AstTime etc that can't be pushed out + virtual bool isPure() const { return true; } // Else a $display, etc, that must be ordered with other displays + virtual bool isBrancher() const { return false; } // Changes control flow, disable some optimizations + virtual bool isGateOptimizable() const { return true; } // Else a AstTime etc that can't be pushed out // GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf) virtual bool isGateDedupable() const { return isGateOptimizable(); } - virtual bool isSubstOptimizable() const { return true; } // Else a AstTime etc that can't be substituted out - virtual bool isPredictOptimizable() const { return true; } // Else a AstTime etc which output can't be predicted from input - virtual bool isOutputter() const { return false; } // Else creates output or exits, etc, not unconsumed - // isUnlikely handles $stop or similar statement which means an above IF statement is unlikely to be taken + virtual bool isSubstOptimizable() const { return true; } // Else a AstTime etc that can't be substituted out + virtual bool isPredictOptimizable() const { return true; } // Else a AstTime etc which output can't be predicted from input + virtual bool isOutputter() const { return false; } // Else creates output or exits, etc, not unconsumed + // isUnlikely handles $stop or similar statement which means an above IF + // statement is unlikely to be taken virtual bool isUnlikely() const { return false; } - virtual int instrCount() const { return 0; } + virtual int instrCount() const { return 0; } virtual V3Hash sameHash() const { return V3Hash(V3Hash::Illegal()); } // Not a node that supports it virtual bool same(const AstNode*) const { return true; } - virtual bool hasDType() const { return false; } // Iff has a data type; dtype() must be non null - virtual AstNodeDType* getChildDTypep() const { return NULL; } // Iff has a non-null childDTypep(), as generic node function + virtual bool hasDType() const { return false; } // Iff has a data type; dtype() must be non null + virtual AstNodeDType* getChildDTypep() const { return NULL; } // Iff has a non-null childDTypep(), as generic node function // Another AstNode* may have a pointer into this node, other then normal front/back/etc. virtual bool maybePointedTo() const { return false; } virtual const char* broken() const { return NULL; } @@ -1480,19 +1489,21 @@ private: // CONVERSION public: -#include "V3Ast__gen_interface.h" // From ./astgen +#include "V3Ast__gen_interface.h" // From ./astgen // Things like: - // AstAlways* castAlways(); + // AstAlways* castAlways(); }; -inline std::ostream& operator<<(std::ostream& os, AstNode* rhs) { if (!rhs) os<<"NULL"; else rhs->dump(os); return os; } +inline std::ostream& operator<<(std::ostream& os, AstNode* rhs) { + if (!rhs) os<<"NULL"; else rhs->dump(os); return os; +} inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); } //###################################################################### //###################################################################### //=== AstNode* : Derived generic node types -#define ASTNODE_BASE_FUNCS(name) \ +#define ASTNODE_BASE_FUNCS(name) \ virtual ~Ast ##name() {} \ static Ast ##name * cloneTreeNull(Ast ##name * nodep, bool cloneNextLink) { \ return nodep ? nodep->cloneTree(cloneNextLink) : NULL; } \ @@ -1503,14 +1514,14 @@ class AstNodeMath : public AstNode { // Math -- anything that's part of an expression tree public: explicit AstNodeMath(FileLine* fl) - : AstNode(fl) {} + : AstNode(fl) {} ASTNODE_BASE_FUNCS(NodeMath) // METHODS virtual bool hasDType() const { return true; } virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV virtual string emitC() = 0; virtual string emitSimpleOperator() { return ""; } - virtual bool cleanOut() = 0; // True if output has extra upper bits zero + virtual bool cleanOut() = 0; // True if output has extra upper bits zero // Someday we will generically support data types on every math node // Until then isOpaque indicates we shouldn't constant optimize this node type bool isOpaque() { return VN_IS(this, CvtPackString); } @@ -1520,7 +1531,7 @@ class AstNodeTermop : public AstNodeMath { // Terminal operator -- a operator with no "inputs" public: explicit AstNodeTermop(FileLine* fl) - : AstNodeMath(fl) {} + : AstNodeMath(fl) {} ASTNODE_BASE_FUNCS(NodeTermop) // Know no children, and hot function, so skip iterator for speed // See checkTreeIter also that asserts no children @@ -1532,20 +1543,20 @@ class AstNodeUniop : public AstNodeMath { // Unary math public: AstNodeUniop(FileLine* fl, AstNode* lhsp) - : AstNodeMath(fl) { - dtypeFrom(lhsp); - setOp1p(lhsp); } + : AstNodeMath(fl) { + dtypeFrom(lhsp); + setOp1p(lhsp); } ASTNODE_BASE_FUNCS(NodeUniop) - AstNode* lhsp() const { return op1p(); } - void lhsp(AstNode* nodep) { return setOp1p(nodep); } + AstNode* lhsp() const { return op1p(); } + void lhsp(AstNode* nodep) { return setOp1p(nodep); } // METHODS - virtual void numberOperate(V3Number& out, const V3Number& lhs) = 0; // Set out to evaluation of a AstConst'ed lhs + virtual void numberOperate(V3Number& out, const V3Number& lhs) = 0; // Set out to evaluation of a AstConst'ed lhs virtual bool cleanLhs() = 0; - virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size - virtual bool doubleFlavor() const { return false; } // D flavor of nodes with both flavors? - virtual bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors? - virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors? - virtual int instrCount() const { return widthInstrs(); } + virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size + virtual bool doubleFlavor() const { return false; } // D flavor of nodes with both flavors? + virtual bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors? + virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors? + virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode*) const { return true; } }; @@ -1554,25 +1565,25 @@ class AstNodeBiop : public AstNodeMath { // Binary math public: AstNodeBiop(FileLine* fl, AstNode* lhs, AstNode* rhs) - : AstNodeMath(fl) { - setOp1p(lhs); setOp2p(rhs); } + : AstNodeMath(fl) { + setOp1p(lhs); setOp2p(rhs); } ASTNODE_BASE_FUNCS(NodeBiop) - virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp)=0; // Clone single node, just get same type back. + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0; // Clone single node, just get same type back. // ACCESSORS - AstNode* lhsp() const { return op1p(); } - AstNode* rhsp() const { return op2p(); } - void lhsp(AstNode* nodep) { return setOp1p(nodep); } - void rhsp(AstNode* nodep) { return setOp2p(nodep); } + AstNode* lhsp() const { return op1p(); } + AstNode* rhsp() const { return op2p(); } + void lhsp(AstNode* nodep) { return setOp1p(nodep); } + void rhsp(AstNode* nodep) { return setOp2p(nodep); } // METHODS - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) = 0; // Set out to evaluation of a AstConst'ed - virtual bool cleanLhs() = 0; // True if LHS must have extra upper bits zero - virtual bool cleanRhs() = 0; // True if RHS must have extra upper bits zero - virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size - virtual bool sizeMattersRhs() = 0; // True if output result depends on rhs size - virtual bool doubleFlavor() const { return false; } // D flavor of nodes with both flavors? - virtual bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors? - virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors? - virtual int instrCount() const { return widthInstrs(); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) = 0; // Set out to evaluation of a AstConst'ed + virtual bool cleanLhs() = 0; // True if LHS must have extra upper bits zero + virtual bool cleanRhs() = 0; // True if RHS must have extra upper bits zero + virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size + virtual bool sizeMattersRhs() = 0; // True if output result depends on rhs size + virtual bool doubleFlavor() const { return false; } // D flavor of nodes with both flavors? + virtual bool signedFlavor() const { return false; } // Signed flavor of nodes with both flavors? + virtual bool stringFlavor() const { return false; } // N flavor of nodes with both flavors? + virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode*) const { return true; } }; @@ -1581,26 +1592,26 @@ class AstNodeTriop : public AstNodeMath { // Trinary math public: AstNodeTriop(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths) - : AstNodeMath(fl) { - setOp1p(lhs); setOp2p(rhs); setOp3p(ths); } + : AstNodeMath(fl) { + setOp1p(lhs); setOp2p(rhs); setOp3p(ths); } ASTNODE_BASE_FUNCS(NodeTriop) - AstNode* lhsp() const { return op1p(); } - AstNode* rhsp() const { return op2p(); } - AstNode* thsp() const { return op3p(); } - void lhsp(AstNode* nodep) { return setOp1p(nodep); } - void rhsp(AstNode* nodep) { return setOp2p(nodep); } - void thsp(AstNode* nodep) { return setOp3p(nodep); } + AstNode* lhsp() const { return op1p(); } + AstNode* rhsp() const { return op2p(); } + AstNode* thsp() const { return op3p(); } + void lhsp(AstNode* nodep) { return setOp1p(nodep); } + void rhsp(AstNode* nodep) { return setOp2p(nodep); } + void thsp(AstNode* nodep) { return setOp3p(nodep); } // METHODS // Set out to evaluation of a AstConst'ed virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs, const V3Number& ths) = 0; - virtual bool cleanLhs() = 0; // True if LHS must have extra upper bits zero - virtual bool cleanRhs() = 0; // True if RHS must have extra upper bits zero - virtual bool cleanThs() = 0; // True if THS must have extra upper bits zero - virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size - virtual bool sizeMattersRhs() = 0; // True if output result depends on rhs size - virtual bool sizeMattersThs() = 0; // True if output result depends on ths size - virtual int instrCount() const { return widthInstrs(); } + virtual bool cleanLhs() = 0; // True if LHS must have extra upper bits zero + virtual bool cleanRhs() = 0; // True if RHS must have extra upper bits zero + virtual bool cleanThs() = 0; // True if THS must have extra upper bits zero + virtual bool sizeMattersLhs() = 0; // True if output result depends on lhs size + virtual bool sizeMattersRhs() = 0; // True if output result depends on rhs size + virtual bool sizeMattersThs() = 0; // True if output result depends on ths size + virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode*) const { return true; } }; @@ -1609,7 +1620,7 @@ class AstNodeBiCom : public AstNodeBiop { // Binary math with commutative properties public: AstNodeBiCom(FileLine* fl, AstNode* lhs, AstNode* rhs) - : AstNodeBiop(fl, lhs, rhs) {} + : AstNodeBiop(fl, lhs, rhs) {} ASTNODE_BASE_FUNCS(NodeBiCom) }; @@ -1617,15 +1628,15 @@ class AstNodeBiComAsv : public AstNodeBiCom { // Binary math with commutative & associative properties public: AstNodeBiComAsv(FileLine* fl, AstNode* lhs, AstNode* rhs) - : AstNodeBiCom(fl, lhs, rhs) {} + : AstNodeBiCom(fl, lhs, rhs) {} ASTNODE_BASE_FUNCS(NodeBiComAsv) }; class AstNodeCond : public AstNodeTriop { public: AstNodeCond(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p) - : AstNodeTriop(fl, condp, expr1p, expr2p) { - if (expr1p) dtypeFrom(expr1p); - else if (expr2p) dtypeFrom(expr2p); + : AstNodeTriop(fl, condp, expr1p, expr2p) { + if (expr1p) dtypeFrom(expr1p); + else if (expr2p) dtypeFrom(expr2p); } ASTNODE_BASE_FUNCS(NodeCond) virtual void numberOperate(V3Number& out, const V3Number& lhs, @@ -1637,8 +1648,10 @@ public: virtual string emitC() { return "VL_COND_%nq%lq%rq%tq(%nw,%lw,%rw,%tw, %P, %li, %ri, %ti)"; } virtual bool cleanOut() { return false; } // clean if e1 & e2 clean virtual bool cleanLhs() { return true; } - virtual bool cleanRhs() { return false; } virtual bool cleanThs() { return false; } // Propagates up - virtual bool sizeMattersLhs() { return false; } virtual bool sizeMattersRhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool cleanThs() { return false; } // Propagates up + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual bool sizeMattersThs() { return false; } virtual int instrCount() const { return instrCountBranch(); } virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) = 0; @@ -1648,18 +1661,18 @@ class AstNodePreSel : public AstNode { // Something that becomes an AstSel public: AstNodePreSel(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths) - : AstNode(fl) { - setOp1p(lhs); setOp2p(rhs); setNOp3p(ths); } + : AstNode(fl) { + setOp1p(lhs); setOp2p(rhs); setNOp3p(ths); } ASTNODE_BASE_FUNCS(NodePreSel) - AstNode* lhsp() const { return op1p(); } - AstNode* fromp() const { return lhsp(); } - AstNode* rhsp() const { return op2p(); } - AstNode* thsp() const { return op3p(); } + AstNode* lhsp() const { return op1p(); } + AstNode* fromp() const { return lhsp(); } + AstNode* rhsp() const { return op2p(); } + AstNode* thsp() const { return op3p(); } AstAttrOf* attrp() const { return VN_CAST(op4p(), AttrOf); } - void lhsp(AstNode* nodep) { return setOp1p(nodep); } - void rhsp(AstNode* nodep) { return setOp2p(nodep); } - void thsp(AstNode* nodep) { return setOp3p(nodep); } - void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); } + void lhsp(AstNode* nodep) { return setOp1p(nodep); } + void rhsp(AstNode* nodep) { return setOp2p(nodep); } + void thsp(AstNode* nodep) { return setOp3p(nodep); } + void attrp(AstAttrOf* nodep) { return setOp4p((AstNode*)nodep); } // METHODS virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode*) const { return true; } @@ -1669,7 +1682,7 @@ class AstNodeStmt : public AstNode { // Statement -- anything that's directly under a function public: explicit AstNodeStmt(FileLine* fl) - : AstNode(fl) {} + : AstNode(fl) {} ASTNODE_BASE_FUNCS(NodeStmt) // METHODS virtual bool isStatement() const { return true; } // Really a statement @@ -1680,20 +1693,20 @@ public: class AstNodeAssign : public AstNodeStmt { public: AstNodeAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp) - : AstNodeStmt(fl) { - setOp1p(rhsp); setOp2p(lhsp); - dtypeFrom(lhsp); + : AstNodeStmt(fl) { + setOp1p(rhsp); setOp2p(lhsp); + dtypeFrom(lhsp); } ASTNODE_BASE_FUNCS(NodeAssign) - virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp)=0; // Clone single node, just get same type back. + virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0; // Clone single node, just get same type back. // So iteration hits the RHS which is "earlier" in execution order, it's op1, not op2 - AstNode* rhsp() const { return op1p(); } // op1 = Assign from - AstNode* lhsp() const { return op2p(); } // op2 = Assign to + AstNode* rhsp() const { return op1p(); } // op1 = Assign from + AstNode* lhsp() const { return op2p(); } // op2 = Assign to void rhsp(AstNode* np) { setOp1p(np); } void lhsp(AstNode* np) { setOp2p(np); } virtual bool hasDType() const { return true; } virtual bool cleanRhs() { return true; } - virtual int instrCount() const { return widthInstrs(); } + virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode*) const { return true; } virtual string verilogKwd() const { return "="; } @@ -1703,56 +1716,56 @@ public: class AstNodeFor : public AstNodeStmt { public: AstNodeFor(FileLine* fileline, AstNode* initsp, AstNode* condp, - AstNode* incsp, AstNode* bodysp) - : AstNodeStmt(fileline) { - addNOp1p(initsp); setOp2p(condp); addNOp3p(incsp); addNOp4p(bodysp); + AstNode* incsp, AstNode* bodysp) + : AstNodeStmt(fileline) { + addNOp1p(initsp); setOp2p(condp); addNOp3p(incsp); addNOp4p(bodysp); } ASTNODE_BASE_FUNCS(NodeFor) - AstNode* initsp() const { return op1p(); } // op1= initial statements - AstNode* condp() const { return op2p(); } // op2= condition to continue - AstNode* incsp() const { return op3p(); } // op3= increment statements - AstNode* bodysp() const { return op4p(); } // op4= body of loop + AstNode* initsp() const { return op1p(); } // op1 = initial statements + AstNode* condp() const { return op2p(); } // op2 = condition to continue + AstNode* incsp() const { return op3p(); } // op3 = increment statements + AstNode* bodysp() const { return op4p(); } // op4 = body of loop virtual bool isGateOptimizable() const { return false; } - virtual int instrCount() const { return instrCountBranch(); } + virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; class AstNodeIf : public AstNodeStmt { private: - AstBranchPred m_branchPred; // Branch prediction as taken/untaken? + AstBranchPred m_branchPred; // Branch prediction as taken/untaken? public: AstNodeIf(FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp) - : AstNodeStmt(fl) { - setOp1p(condp); addNOp2p(ifsp); addNOp3p(elsesp); + : AstNodeStmt(fl) { + setOp1p(condp); addNOp2p(ifsp); addNOp3p(elsesp); } ASTNODE_BASE_FUNCS(NodeIf) - AstNode* condp() const { return op1p(); } // op1 = condition - AstNode* ifsp() const { return op2p(); } // op2 = list of true statements - AstNode* elsesp() const { return op3p(); } // op3 = list of false statements - void condp(AstNode* newp) { setOp1p(newp); } - void addIfsp(AstNode* newp) { addOp2p(newp); } - void addElsesp(AstNode* newp) { addOp3p(newp); } + AstNode* condp() const { return op1p(); } // op1 = condition + AstNode* ifsp() const { return op2p(); } // op2 = list of true statements + AstNode* elsesp() const { return op3p(); } // op3 = list of false statements + void condp(AstNode* newp) { setOp1p(newp); } + void addIfsp(AstNode* newp) { addOp2p(newp); } + void addElsesp(AstNode* newp) { addOp3p(newp); } virtual bool isGateOptimizable() const { return false; } virtual bool isGateDedupable() const { return true; } - virtual int instrCount() const { return instrCountBranch(); } + virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - void branchPred(AstBranchPred flag) { m_branchPred = flag; } + void branchPred(AstBranchPred flag) { m_branchPred = flag; } AstBranchPred branchPred() const { return m_branchPred; } }; class AstNodeCase : public AstNodeStmt { public: AstNodeCase(FileLine* fl, AstNode* exprp, AstNode* casesp) - : AstNodeStmt(fl) { - setOp1p(exprp); addNOp2p(casesp); + : AstNodeStmt(fl) { + setOp1p(exprp); addNOp2p(casesp); } ASTNODE_BASE_FUNCS(NodeCase) - virtual int instrCount() const { return instrCountBranch(); } - AstNode* exprp() const { return op1p(); } // op1 = case condition + virtual int instrCount() const { return instrCountBranch(); } + AstNode* exprp() const { return op1p(); } // op1 = case condition AstCaseItem* itemsp() const { return VN_CAST(op2p(), CaseItem); } // op2 = list of case expressions - AstNode* notParallelp() const { return op3p(); } // op3 = assertion code for non-full case's + AstNode* notParallelp() const { return op3p(); } // op3 = assertion code for non-full case's void addItemsp(AstNode* nodep) { addOp2p(nodep); } void addNotParallelp(AstNode* nodep) { setOp3p(nodep); } }; @@ -1772,45 +1785,45 @@ public: class AstNodeVarRef : public AstNodeMath { // An AstVarRef or AstVarXRef private: - bool m_lvalue; // Left hand side assignment - AstVar* m_varp; // [AfterLink] Pointer to variable itself - AstVarScope* m_varScopep; // Varscope for hierarchy - AstPackage* m_packagep; // Package hierarchy - string m_name; // Name of variable - string m_hiername; // Scope converted into name-> for emitting - bool m_hierThis; // Hiername points to "this" function + bool m_lvalue; // Left hand side assignment + AstVar* m_varp; // [AfterLink] Pointer to variable itself + AstVarScope* m_varScopep; // Varscope for hierarchy + AstPackage* m_packagep; // Package hierarchy + string m_name; // Name of variable + string m_hiername; // Scope converted into name-> for emitting + bool m_hierThis; // Hiername points to "this" function void init(); public: AstNodeVarRef(FileLine* fl, const string& name, bool lvalue) - : AstNodeMath(fl), m_lvalue(lvalue), m_varp(NULL), m_varScopep(NULL), - m_packagep(NULL), m_name(name), m_hierThis(false) { - init(); + : AstNodeMath(fl), m_lvalue(lvalue), m_varp(NULL), m_varScopep(NULL), + m_packagep(NULL), m_name(name), m_hierThis(false) { + init(); } AstNodeVarRef(FileLine* fl, const string& name, AstVar* varp, bool lvalue) - : AstNodeMath(fl), m_lvalue(lvalue), m_varp(varp), m_varScopep(NULL), - m_packagep(NULL), m_name(name), m_hierThis(false) { - // May have varp==NULL - init(); + : AstNodeMath(fl), m_lvalue(lvalue), m_varp(varp), m_varScopep(NULL), + m_packagep(NULL), m_name(name), m_hierThis(false) { + // May have varp==NULL + init(); } ASTNODE_BASE_FUNCS(NodeVarRef) virtual bool hasDType() const { return true; } virtual const char* broken() const; virtual int instrCount() const { return widthInstrs(); } virtual void cloneRelink(); - virtual string name() const { return m_name; } // * = Var name + virtual string name() const { return m_name; } // * = Var name virtual void name(const string& name) { m_name = name; } - bool lvalue() const { return m_lvalue; } - void lvalue(bool lval) { m_lvalue=lval; } // Avoid using this; Set in constructor - AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable - void varp(AstVar* varp) { m_varp=varp; } - AstVarScope* varScopep() const { return m_varScopep; } - void varScopep(AstVarScope* varscp) { m_varScopep=varscp; } + bool lvalue() const { return m_lvalue; } + void lvalue(bool lval) { m_lvalue = lval; } // Avoid using this; Set in constructor + AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable + void varp(AstVar* varp) { m_varp = varp; } + AstVarScope* varScopep() const { return m_varScopep; } + void varScopep(AstVarScope* varscp) { m_varScopep = varscp; } string hiername() const { return m_hiername; } void hiername(const string& hn) { m_hiername = hn; } bool hierThis() const { return m_hierThis; } void hierThis(bool flag) { m_hierThis = flag; } AstPackage* packagep() const { return m_packagep; } - void packagep(AstPackage* nodep) { m_packagep=nodep; } + void packagep(AstPackage* nodep) { m_packagep = nodep; } // Know no children, and hot function, so skip iterator for speed // See checkTreeIter also that asserts no children // cppcheck-suppress functionConst @@ -1819,19 +1832,19 @@ public: class AstNodeText : public AstNode { private: - string m_text; + string m_text; public: // Node that simply puts text into the output stream AstNodeText(FileLine* fileline, const string& textp) - : AstNode(fileline) { - m_text = textp; // Copy it + : AstNode(fileline) { + m_text = textp; // Copy it } ASTNODE_BASE_FUNCS(NodeText) virtual void dump(std::ostream& str=std::cout); virtual V3Hash sameHash() const { return V3Hash(text()); } virtual bool same(const AstNode* samep) const { - const AstNodeText* asamep = static_cast(samep); - return text() == asamep->text(); } + const AstNodeText* asamep = static_cast(samep); + return text() == asamep->text(); } const string& text() const { return m_text; } }; @@ -1840,16 +1853,16 @@ class AstNodeDType : public AstNode { // but it's currently so prevalent in the code we leave it here. // Note the below members are included in AstTypeTable::Key lookups private: - int m_width; // (also in AstTypeTable::Key) Bit width of operation - int m_widthMin; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation - AstNumeric m_numeric; // (also in AstTypeTable::Key) Node is signed + int m_width; // (also in AstTypeTable::Key) Bit width of operation + int m_widthMin; // (also in AstTypeTable::Key) If unsized, bitwidth of minimum implementation + AstNumeric m_numeric; // (also in AstTypeTable::Key) Node is signed // Other members - bool m_generic; // Simple globally referenced type, don't garbage collect - static int s_uniqueNum; // Unique number assigned to each dtype during creation for IEEE matching + bool m_generic; // Simple globally referenced type, don't garbage collect + static int s_uniqueNum; // Unique number assigned to each dtype during creation for IEEE matching public: // CONSTRUCTORS explicit AstNodeDType(FileLine* fl) : AstNode(fl) { - m_width=0; m_widthMin=0; m_generic=false; + m_width = 0; m_widthMin = 0; m_generic = false; } ASTNODE_BASE_FUNCS(NodeDType) // ACCESSORS @@ -1860,26 +1873,27 @@ public: virtual AstNodeDType* skipRefp() const = 0; // recurses over typedefs/const/enum to next non-typeref type virtual AstNodeDType* skipRefToConstp() const = 0; // recurses over typedefs to next non-typeref-or-const type virtual AstNodeDType* skipRefToEnump() const = 0; // recurses over typedefs/const to next non-typeref-or-enum/struct type - virtual int widthAlignBytes() const = 0; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) - virtual int widthTotalBytes() const = 0; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... + virtual int widthAlignBytes() const = 0; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) + virtual int widthTotalBytes() const = 0; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... virtual bool maybePointedTo() const { return true; } - virtual AstNodeDType* virtRefDTypep() const { return NULL; } // Iff has a non-null refDTypep(), as generic node function - virtual void virtRefDTypep(AstNodeDType* nodep) { } // Iff has refDTypep(), set as generic node function + virtual AstNodeDType* virtRefDTypep() const { return NULL; } // Iff has a non-null refDTypep(), as generic node function + virtual void virtRefDTypep(AstNodeDType* nodep) { } // Iff has refDTypep(), set as generic node function virtual bool similarDType(AstNodeDType* samep) const = 0; // Assignable equivalence. Call skipRefp() on this and samep before calling virtual AstNodeDType* subDTypep() const { return NULL; } // Iff has a non-null subDTypep(), as generic node function virtual bool isFourstate() const; // - // Changing the width may confuse the data type resolution, so must clear TypeTable cache after use. - void widthForce(int width, int sized) { m_width=width; m_widthMin=sized; } + // Changing the width may confuse the data type resolution, so must clear + // TypeTable cache after use. + void widthForce(int width, int sized) { m_width = width; m_widthMin = sized; } // For backward compatibility inherit width and signing from the subDType/base type - void widthFromSub(AstNodeDType* nodep) { m_width=nodep->m_width; m_widthMin=nodep->m_widthMin; m_numeric=nodep->m_numeric; } + void widthFromSub(AstNodeDType* nodep) { m_width = nodep->m_width; m_widthMin = nodep->m_widthMin; m_numeric = nodep->m_numeric; } // - int width() const { return m_width; } + int width() const { return m_width; } void numeric(AstNumeric flag) { m_numeric = flag; } bool isSigned() const { return m_numeric.isSigned(); } bool isNosign() const { return m_numeric.isNosign(); } AstNumeric numeric() const { return m_numeric; } - int widthWords() const { return VL_WORDS_I(width()); } + int widthWords() const { return VL_WORDS_I(width()); } int widthMin() const { // If sized, the size, if unsized the min digits to represent it return m_widthMin?m_widthMin:m_width; } int widthPow2() const; @@ -1889,7 +1903,7 @@ public: void generic(bool flag) { m_generic = flag; } AstNodeDType* dtypeDimensionp(int dimension); std::pair dimensions(bool includeBasic); - uint32_t arrayUnpackedElements(); // 1, or total multiplication of all dimensions + uint32_t arrayUnpackedElements(); // 1, or total multiplication of all dimensions static int uniqueNumInc() { return ++s_uniqueNum; } const char* charIQWN() const { return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); } }; @@ -1900,16 +1914,16 @@ private: typedef std::map MemberNameMap; // MEMBERS string m_name; // Name from upper typedef, if any - bool m_packed; - bool m_isFourstate; - MemberNameMap m_members; + bool m_packed; + bool m_isFourstate; + MemberNameMap m_members; public: AstNodeClassDType(FileLine* fl, AstNumeric numericUnpack) - : AstNodeDType(fl) { - // AstNumeric::NOSIGN overloaded to indicate not packed - m_packed = (numericUnpack != AstNumeric::NOSIGN); + : AstNodeDType(fl) { + // AstNumeric::NOSIGN overloaded to indicate not packed + m_packed = (numericUnpack != AstNumeric::NOSIGN); m_isFourstate = false; // V3Width computes - numeric(numericUnpack.isSigned() ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); + numeric(numericUnpack.isSigned() ? AstNumeric::SIGNED : AstNumeric::UNSIGNED); } ASTNODE_BASE_FUNCS(NodeClassDType) virtual const char* broken() const; @@ -1917,16 +1931,16 @@ public: // For basicp() we reuse the size to indicate a "fake" basic type of same size virtual AstBasicDType* basicp() const { return (isFourstate() - ? VN_CAST(findLogicDType(width(),width(),numeric()), BasicDType) - : VN_CAST(findBitDType(width(),width(),numeric()), BasicDType)); } + ? VN_CAST(findLogicDType(width(), width(), numeric()), BasicDType) + : VN_CAST(findBitDType(width(), width(), numeric()), BasicDType)); } virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } - virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) - virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... + virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) + virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... // op1 = members virtual bool similarDType(AstNodeDType* samep) const { - return this==samep; // We don't compare members, require exact equivalence + return this==samep; // We don't compare members, require exact equivalence } virtual string name() const { return m_name; } void name(const string& flag) { m_name = flag; } @@ -1939,8 +1953,8 @@ public: void clearCache() { m_members.clear(); } void repairMemberCache(); AstMemberDType* findMember(const string& name) const { - MemberNameMap::const_iterator it = m_members.find(name); - return (it==m_members.end()) ? NULL : it->second; + MemberNameMap::const_iterator it = m_members.find(name); + return (it==m_members.end()) ? NULL : it->second; } int lsb() const { return 0; } int msb() const { return dtypep()->width()-1; } // Packed classes look like arrays @@ -1952,43 +1966,44 @@ class AstNodeArrayDType : public AstNodeDType { // Children: DTYPE (moved to refDTypep() in V3Width) // Children: RANGE (array bounds) private: - AstNodeDType* m_refDTypep; // Elements of this type (after widthing) - AstNode* rangenp() const { return op2p(); } // op2 = Array(s) of variable + AstNodeDType* m_refDTypep; // Elements of this type (after widthing) + AstNode* rangenp() const { return op2p(); } // op2 = Array(s) of variable public: explicit AstNodeArrayDType(FileLine* fl) : AstNodeDType(fl) { - m_refDTypep = NULL; + m_refDTypep = NULL; } ASTNODE_BASE_FUNCS(NodeArrayDType) virtual void dump(std::ostream& str); virtual void dumpSmall(std::ostream& str); - virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) - || (!m_refDTypep && childDTypep()))); return NULL; } + virtual const char* broken() const { + BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) + || (!m_refDTypep && childDTypep()))); return NULL; } virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) { - m_refDTypep = m_refDTypep->clonep(); + m_refDTypep = m_refDTypep->clonep(); }} virtual bool same(const AstNode* samep) const { - const AstNodeArrayDType* asamep = static_cast(samep); - return (msb()==asamep->msb() - && subDTypep()==asamep->subDTypep() - && rangenp()->sameTree(asamep->rangenp())); } // HashedDT doesn't recurse, so need to check children + const AstNodeArrayDType* asamep = static_cast(samep); + return (msb()==asamep->msb() + && subDTypep()==asamep->subDTypep() + && rangenp()->sameTree(asamep->rangenp())); } // HashedDT doesn't recurse, so need to check children virtual bool similarDType(AstNodeDType* samep) const { - const AstNodeArrayDType* asamep = static_cast(samep); - return (asamep - && type() == samep->type() - && msb() == asamep->msb() - && rangenp()->sameTree(asamep->rangenp()) - && subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp())); + const AstNodeArrayDType* asamep = static_cast(samep); + return (asamep + && type() == samep->type() + && msb() == asamep->msb() + && rangenp()->sameTree(asamep->rangenp()) + && subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp())); } - virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(msb()),V3Hash(lsb())); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep), V3Hash(msb()), V3Hash(lsb())); } AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable - void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Array(s) of variable - void rangep(AstRange* nodep); + void rangep(AstRange* nodep); // METHODS virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } @@ -1996,9 +2011,9 @@ public: virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } virtual int widthTotalBytes() const { return elementsConst() * subDTypep()->widthTotalBytes(); } - int msb() const; - int lsb() const; - int elementsConst() const; + int msb() const; + int lsb() const; + int elementsConst() const; VNumRange declRange() const; }; @@ -2020,9 +2035,9 @@ class AstNodeStream : public AstNodeBiop { // Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp() public: AstNodeStream(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - if (lhsp->dtypep()) { - dtypeSetLogicSized(lhsp->dtypep()->width(), lhsp->dtypep()->width(), AstNumeric::UNSIGNED); - } + if (lhsp->dtypep()) { + dtypeSetLogicSized(lhsp->dtypep()->width(), lhsp->dtypep()->width(), AstNumeric::UNSIGNED); + } } ASTNODE_BASE_FUNCS(NodeStream) }; @@ -2032,53 +2047,53 @@ public: class AstNodeFTask : public AstNode { private: - string m_name; // Name of task - string m_cname; // Name of task if DPI import + string m_name; // Name of task + string m_cname; // Name of task if DPI import uint64_t m_dpiOpenParent; // DPI import open array, if !=0, how many callees - bool m_taskPublic:1; // Public task - bool m_attrIsolateAssign:1;// User isolate_assignments attribute - bool m_prototype:1; // Just a prototype - bool m_dpiExport:1; // DPI exported - bool m_dpiImport:1; // DPI imported - bool m_dpiContext:1; // DPI import context + bool m_taskPublic:1; // Public task + bool m_attrIsolateAssign:1;// User isolate_assignments attribute + bool m_prototype:1; // Just a prototype + bool m_dpiExport:1; // DPI exported + bool m_dpiImport:1; // DPI imported + bool m_dpiContext:1; // DPI import context bool m_dpiOpenChild:1; // DPI import open array child wrapper - bool m_dpiTask:1; // DPI import task (vs. void function) - bool m_pure:1; // DPI import pure + bool m_dpiTask:1; // DPI import task (vs. void function) + bool m_pure:1; // DPI import pure public: AstNodeFTask(FileLine* fileline, const string& name, AstNode* stmtsp) - : AstNode(fileline) + : AstNode(fileline) , m_name(name) , m_dpiOpenParent(0), m_taskPublic(false) - , m_attrIsolateAssign(false), m_prototype(false) - , m_dpiExport(false), m_dpiImport(false), m_dpiContext(false) + , m_attrIsolateAssign(false), m_prototype(false) + , m_dpiExport(false), m_dpiImport(false), m_dpiContext(false) , m_dpiOpenChild(false), m_dpiTask(false), m_pure(false) { - addNOp3p(stmtsp); - cname(name); // Might be overridden by dpi import/export + addNOp3p(stmtsp); + cname(name); // Might be overridden by dpi import/export } ASTNODE_BASE_FUNCS(NodeFTask) virtual void dump(std::ostream& str=std::cout); - virtual string name() const { return m_name; } // * = Var name + virtual string name() const { return m_name; } // * = Var name virtual bool maybePointedTo() const { return true; } virtual bool isGateOptimizable() const { return !((m_dpiExport || m_dpiImport) && !m_pure); } // {AstFunc only} op1 = Range output variable - virtual void name(const string& name) { m_name = name; } + virtual void name(const string& name) { m_name = name; } string cname() const { return m_cname; } void cname(const string& cname) { m_cname = cname; } // op1 = Output variable (functions only, NULL for tasks) - AstNode* fvarp() const { return op1p(); } - void addFvarp(AstNode* nodep) { addNOp1p(nodep); } - bool isFunction() const { return fvarp()!=NULL; } + AstNode* fvarp() const { return op1p(); } + void addFvarp(AstNode* nodep) { addNOp1p(nodep); } + bool isFunction() const { return fvarp()!=NULL; } // op3 = Statements/Ports/Vars - AstNode* stmtsp() const { return op3p(); } // op3 = List of statements - void addStmtsp(AstNode* nodep) { addNOp3p(nodep); } + AstNode* stmtsp() const { return op3p(); } // op3 = List of statements + void addStmtsp(AstNode* nodep) { addNOp3p(nodep); } // op4 = scope name AstScopeName* scopeNamep() const { return VN_CAST(op4p(), ScopeName); } // MORE ACCESSORS void dpiOpenParentInc() { ++m_dpiOpenParent; } - void dpiOpenParentClear() { m_dpiOpenParent=0; } + void dpiOpenParentClear() { m_dpiOpenParent = 0; } uint64_t dpiOpenParent() const { return m_dpiOpenParent; } void scopeNamep(AstNode* nodep) { setNOp4p(nodep); } - void taskPublic(bool flag) { m_taskPublic=flag; } + void taskPublic(bool flag) { m_taskPublic = flag; } bool taskPublic() const { return m_taskPublic; } void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } bool attrIsolateAssign() const { return m_attrIsolateAssign; } @@ -2102,11 +2117,11 @@ class AstNodeFTaskRef : public AstNodeStmt { // A reference to a task (or function) // Functions are not statements, while tasks are. AstNodeStmt needs isStatement() to deal. private: - AstNodeFTask* m_taskp; // [AfterLink] Pointer to task referenced - string m_name; // Name of variable + AstNodeFTask* m_taskp; // [AfterLink] Pointer to task referenced + string m_name; // Name of variable string m_dotted; // Dotted part of scope the name()ed task/func is under or "" - string m_inlinedDots; // Dotted hierarchy flattened out - AstPackage* m_packagep; // Package hierarchy + string m_inlinedDots; // Dotted hierarchy flattened out + AstPackage* m_packagep; // Package hierarchy public: AstNodeFTaskRef(FileLine* fl, AstNode* namep, AstNode* pinsp) : AstNodeStmt(fl) @@ -2121,29 +2136,29 @@ public: ASTNODE_BASE_FUNCS(NodeFTaskRef) virtual const char* broken() const { BROKEN_RTN(m_taskp && !m_taskp->brokeExists()); return NULL; } virtual void cloneRelink() { if (m_taskp && m_taskp->clonep()) { - m_taskp = m_taskp->clonep(); + m_taskp = m_taskp->clonep(); }} virtual void dump(std::ostream& str=std::cout); - virtual string name() const { return m_name; } // * = Var name + virtual string name() const { return m_name; } // * = Var name virtual bool isGateOptimizable() const { return m_taskp && m_taskp->isGateOptimizable(); } - string dotted() const { return m_dotted; } // * = Scope name or "" - string prettyDotted() const { return prettyName(dotted()); } - string inlinedDots() const { return m_inlinedDots; } - void inlinedDots(const string& flag) { m_inlinedDots = flag; } - AstNodeFTask* taskp() const { return m_taskp; } // [After Link] Pointer to variable - void taskp(AstNodeFTask* taskp) { m_taskp=taskp; } + string dotted() const { return m_dotted; } // * = Scope name or "" + string prettyDotted() const { return prettyName(dotted()); } + string inlinedDots() const { return m_inlinedDots; } + void inlinedDots(const string& flag) { m_inlinedDots = flag; } + AstNodeFTask* taskp() const { return m_taskp; } // [After Link] Pointer to variable + void taskp(AstNodeFTask* taskp) { m_taskp = taskp; } virtual void name(const string& name) { m_name = name; } - void dotted(const string& name) { m_dotted = name; } + void dotted(const string& name) { m_dotted = name; } AstPackage* packagep() const { return m_packagep; } - void packagep(AstPackage* nodep) { m_packagep=nodep; } + void packagep(AstPackage* nodep) { m_packagep = nodep; } // op1 = namep - AstNode* namep() const { return op1p(); } + AstNode* namep() const { return op1p(); } // op2 = Pin interconnection list - AstNode* pinsp() const { return op2p(); } + AstNode* pinsp() const { return op2p(); } void addPinsp(AstNode* nodep) { addOp2p(nodep); } // op3 = scope tracking AstScopeName* scopeNamep() const { return VN_CAST(op3p(), ScopeName); } - void scopeNamep(AstNode* nodep) { setNOp3p(nodep); } + void scopeNamep(AstNode* nodep) { setNOp3p(nodep); } }; class AstNodeModule : public AstNode { @@ -2151,30 +2166,30 @@ class AstNodeModule : public AstNode { // something that can live directly under the TOP, // excluding $unit package stuff private: - string m_name; // Name of the module - string m_origName; // Name of the module, ignoring name() changes, for dot lookup - bool m_modPublic:1; // Module has public references - bool m_modTrace:1; // Tracing this module - bool m_inLibrary:1; // From a library, no error if not used, never top level - bool m_dead:1; // LinkDot believes is dead; will remove in Dead visitors - bool m_internal:1; // Internally created - bool m_recursive:1; // Recursive module - bool m_recursiveClone:1; // If recursive, what module it clones, otherwise NULL - int m_level; // 1=top module, 2=cell off top module, ... - int m_varNum; // Incrementing variable number - int m_typeNum; // Incrementing implicit type number + string m_name; // Name of the module + string m_origName; // Name of the module, ignoring name() changes, for dot lookup + bool m_modPublic:1; // Module has public references + bool m_modTrace:1; // Tracing this module + bool m_inLibrary:1; // From a library, no error if not used, never top level + bool m_dead:1; // LinkDot believes is dead; will remove in Dead visitors + bool m_internal:1; // Internally created + bool m_recursive:1; // Recursive module + bool m_recursiveClone:1; // If recursive, what module it clones, otherwise NULL + int m_level; // 1=top module, 2=cell off top module, ... + int m_varNum; // Incrementing variable number + int m_typeNum; // Incrementing implicit type number public: AstNodeModule(FileLine* fl, const string& name) : AstNode(fl) - ,m_name(name), m_origName(name) - ,m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false) - ,m_internal(false), m_recursive(false), m_recursiveClone(false) - ,m_level(0), m_varNum(0), m_typeNum(0) { } + , m_name(name), m_origName(name) + , m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false) + , m_internal(false), m_recursive(false), m_recursiveClone(false) + , m_level(0), m_varNum(0), m_typeNum(0) { } ASTNODE_BASE_FUNCS(NodeModule) virtual void dump(std::ostream& str); virtual bool maybePointedTo() const { return true; } - virtual string name() const { return m_name; } - AstNode* stmtsp() const { return op2p(); } // op2 = List of statements + virtual string name() const { return m_name; } + AstNode* stmtsp() const { return op2p(); } // op2 = List of statements AstActive* activesp() const { return VN_CAST(op3p(), Active); } // op3 = List of i/sblocks // METHODS void addInlinesp(AstNode* nodep) { addOp1p(nodep); } @@ -2182,22 +2197,22 @@ public: void addActivep(AstNode* nodep) { addOp3p(nodep); } // ACCESSORS virtual void name(const string& name) { m_name = name; } - string origName() const { return m_origName; } - bool inLibrary() const { return m_inLibrary; } - void inLibrary(bool flag) { m_inLibrary = flag; } - void level(int level) { m_level = level; } - int level() const { return m_level; } - bool isTop() const { return level()==1; } - int varNumGetInc() { return ++m_varNum; } - int typeNumGetInc() { return ++m_typeNum; } - void modPublic(bool flag) { m_modPublic = flag; } - bool modPublic() const { return m_modPublic; } - void modTrace(bool flag) { m_modTrace = flag; } - bool modTrace() const { return m_modTrace; } - void dead(bool flag) { m_dead = flag; } - bool dead() const { return m_dead; } - void internal(bool flag) { m_internal = flag; } - bool internal() const { return m_internal; } + string origName() const { return m_origName; } + bool inLibrary() const { return m_inLibrary; } + void inLibrary(bool flag) { m_inLibrary = flag; } + void level(int level) { m_level = level; } + int level() const { return m_level; } + bool isTop() const { return level()==1; } + int varNumGetInc() { return ++m_varNum; } + int typeNumGetInc() { return ++m_typeNum; } + void modPublic(bool flag) { m_modPublic = flag; } + bool modPublic() const { return m_modPublic; } + void modTrace(bool flag) { m_modTrace = flag; } + bool modTrace() const { return m_modTrace; } + void dead(bool flag) { m_dead = flag; } + bool dead() const { return m_dead; } + void internal(bool flag) { m_internal = flag; } + bool internal() const { return m_internal; } void recursive(bool flag) { m_recursive = flag; } bool recursive() const { return m_recursive; } void recursiveClone(bool flag) { m_recursiveClone = flag; } @@ -2215,9 +2230,9 @@ public: #include "V3AstNodes.h" -#include "V3Ast__gen_impl.h" // From ./astgen +#include "V3Ast__gen_impl.h" // From ./astgen // Things like: -// inline AstAlways* AstNode::castAlways() { return dynamic_cast(this);} +// inline AstAlways* AstNode::castAlways() { return dynamic_cast(this); } // inline bool AstNode::privateIsaAlways(const AstNode* nodep) { return nodep && nodep->type() == AstType::atAlways; } //###################################################################### @@ -2248,11 +2263,11 @@ inline AstNode* AstNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) { //###################################################################### // Inline ACCESSORS -inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; } +inline int AstNode::width() const { return dtypep() ? dtypep()->width() : 0; } inline int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; } -inline bool AstNode::width1() const { // V3Const uses to know it can optimize +inline bool AstNode::width1() const { // V3Const uses to know it can optimize return dtypep() && dtypep()->width()==1; } -inline int AstNode::widthInstrs() const { +inline int AstNode::widthInstrs() const { return (!dtypep() ? 1 : (dtypep()->isWide() ? dtypep()->widthWords() : 1)); } inline bool AstNode::isDouble() const { return dtypep() && VN_IS(dtypep(), BasicDType) && VN_CAST(dtypep(), BasicDType)->isDouble(); } @@ -2266,8 +2281,10 @@ inline bool AstNode::isNeqZero() const { return (VN_IS(this, Const) && VN_CAST_ inline bool AstNode::isOne() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->num().isEqOne()); } inline bool AstNode::isAllOnes() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnes()); } inline bool AstNode::isAllOnesV() const { return (VN_IS(this, Const) && VN_CAST_CONST(this, Const)->isEqAllOnesV()); } -inline bool AstNode::sameTree(const AstNode* node2p) const { return sameTreeIter(this, node2p, true, false); } -inline bool AstNode::sameGateTree(const AstNode* node2p) const { return sameTreeIter(this, node2p, true, true); } +inline bool AstNode::sameTree(const AstNode* node2p) const { + return sameTreeIter(this, node2p, true, false); } +inline bool AstNode::sameGateTree(const AstNode* node2p) const { + return sameTreeIter(this, node2p, true, true); } inline void AstNodeVarRef::init() { if (m_varp) dtypep(m_varp->dtypep()); } @@ -2284,4 +2301,4 @@ inline void AstIfaceRefDType::cloneRelink() { if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep(); if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep(); } -#endif // Guard +#endif // Guard diff --git a/src/V3AstConstOnly.h b/src/V3AstConstOnly.h index 278ff9230..03ef384cf 100644 --- a/src/V3AstConstOnly.h +++ b/src/V3AstConstOnly.h @@ -30,4 +30,4 @@ #define deleteTree error_no_deleteTree_in_ConstOnlyVisitor #define unlinkFrBack error_no_unlinkFrBack_in_ConstOnlyVisitor -#endif // Guard +#endif // Guard diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 59cb70729..4a5dbe894 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -57,7 +57,7 @@ void AstNodeVarRef::cloneRelink() { } int AstNodeSel::bitConst() const { - AstConst* constp=VN_CAST(bitp(), Const); + AstConst* constp = VN_CAST(bitp(), Const); return (constp ? constp->toSInt() : 0); } @@ -66,20 +66,20 @@ void AstNodeClassDType::repairMemberCache() { for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { if (m_members.find(itemp->name())!=m_members.end()) { itemp->v3error("Duplicate declaration of member name: "<prettyName()); } - else m_members.insert(make_pair(itemp->name(), itemp)); + else m_members.insert(make_pair(itemp->name(), itemp)); } } const char* AstNodeClassDType::broken() const { vl_unordered_set exists; for (AstMemberDType* itemp = membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { - exists.insert(itemp); + exists.insert(itemp); } for (MemberNameMap::const_iterator it=m_members.begin(); it!=m_members.end(); ++it) { - if (VL_UNLIKELY(exists.find(it->second) == exists.end())) { - this->v3error("Internal: Structure member broken: "<first); - return "member broken"; - } + if (VL_UNLIKELY(exists.find(it->second) == exists.end())) { + this->v3error("Internal: Structure member broken: "<first); + return "member broken"; + } } return NULL; } @@ -121,37 +121,37 @@ int AstNodeClassDType::widthAlignBytes() const { AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isDouble() && rhsp->isDouble()) { - return new AstEqD(fl, lhsp, rhsp); + return new AstEqD(fl, lhsp, rhsp); } else { - return new AstEq(fl, lhsp, rhsp); + return new AstEq(fl, lhsp, rhsp); } } AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isDouble() && rhsp->isDouble()) { - return new AstGteD(fl, lhsp, rhsp); + return new AstGteD(fl, lhsp, rhsp); } else if (lhsp->isSigned() && rhsp->isSigned()) { - return new AstGteS(fl, lhsp, rhsp); + return new AstGteS(fl, lhsp, rhsp); } else { - return new AstGte(fl, lhsp, rhsp); + return new AstGte(fl, lhsp, rhsp); } } AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isDouble() && rhsp->isDouble()) { - return new AstLteD(fl, lhsp, rhsp); + return new AstLteD(fl, lhsp, rhsp); } else if (lhsp->isSigned() && rhsp->isSigned()) { - return new AstLteS(fl, lhsp, rhsp); + return new AstLteS(fl, lhsp, rhsp); } else { - return new AstLte(fl, lhsp, rhsp); + return new AstLte(fl, lhsp, rhsp); } } AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { if (lhsp->isDouble() && rhsp->isDouble()) { - return new AstEqD(fl, lhsp, rhsp); + return new AstEqD(fl, lhsp, rhsp); } else { - return new AstEqWild(fl, lhsp, rhsp); + return new AstEqWild(fl, lhsp, rhsp); } } @@ -173,10 +173,12 @@ bool AstVar::isScBv() const { return ((isSc() && width() >= v3Global.opt.pinsBv()) || m_attrScBv); } bool AstVar::isScUint() const { - return ((isSc() && v3Global.opt.pinsScUint() && width() >= 2 && width() <= 64) && !isScBv()); + return ((isSc() && v3Global.opt.pinsScUint() + && width() >= 2 && width() <= 64) && !isScBv()); } bool AstVar::isScBigUint() const { - return ((isSc() && v3Global.opt.pinsScBigUint() && width() >= 65 && width() <= 512) && !isScBv()); + return ((isSc() && v3Global.opt.pinsScBigUint() + && width() >= 65 && width() <= 512) && !isScBv()); } void AstVar::combineType(AstVarType type) { @@ -213,7 +215,7 @@ string AstVar::verilogKwd() const { } string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const { - if (forReturn) named=false; + if (forReturn) named = false; if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument"); string otype; AstBasicDType* bdtypep = basicp(); @@ -284,21 +286,21 @@ string AstVar::vlEnumType() const { AstBasicDType* bdtypep = basicp(); bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING; if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) { - return "VLVT_PTR"; + return "VLVT_PTR"; } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) { - return "VLVT_PTR"; + return "VLVT_PTR"; } else if (strtype) { - arg += "VLVT_STRING"; + arg += "VLVT_STRING"; } else if (widthMin() <= 8) { - arg += "VLVT_UINT8"; + arg += "VLVT_UINT8"; } else if (widthMin() <= 16) { - arg += "VLVT_UINT16"; + arg += "VLVT_UINT16"; } else if (widthMin() <= VL_WORDSIZE) { - arg += "VLVT_UINT32"; + arg += "VLVT_UINT32"; } else if (isQuad()) { - arg += "VLVT_UINT64"; + arg += "VLVT_UINT64"; } else if (isWide()) { - arg += "VLVT_WDATA"; + arg += "VLVT_WDATA"; } // else return "VLVT_UNKNOWN" return arg; @@ -344,28 +346,28 @@ string AstVar::vlPropInit() const { out += ", "+cvtToStr(adtypep->declRange().left())+", "+cvtToStr(adtypep->declRange().right()); dtp = adtypep->subDTypep(); } - else break; // AstBasicDType - nothing below + else break; // AstBasicDType - nothing below } return out; } string AstVar::cPubArgType(bool named, bool forReturn) const { - if (forReturn) named=false; + if (forReturn) named = false; string arg; if (isWide() && isReadOnly()) arg += "const "; if (widthMin() == 1) { - arg += "bool"; + arg += "bool"; } else if (widthMin() <= VL_WORDSIZE) { - arg += "uint32_t"; + arg += "uint32_t"; } else if (isWide()) { - arg += "uint32_t"; // []'s added later + arg += "uint32_t"; // []'s added later } else { - arg += "vluint64_t"; + arg += "vluint64_t"; } if (isWide()) { - if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead"); - arg += " (& "+name(); - arg += ")["+cvtToStr(widthWords())+"]"; + if (forReturn) v3error("Unsupported: Public functions with >64 bit outputs; make an output of a public task instead"); + arg += " (& "+name(); + arg += ")["+cvtToStr(widthWords())+"]"; } else { if (!forReturn && (isWritable() || direction().isRefOrConstRef())) arg += "&"; @@ -375,25 +377,25 @@ string AstVar::cPubArgType(bool named, bool forReturn) const { } string AstVar::dpiArgType(bool named, bool forReturn) const { - if (forReturn) named=false; + if (forReturn) named = false; string arg; if (isDpiOpenArray()) { arg = "const svOpenArrayHandle"; } else if (!basicp()) { arg = "UNKNOWN"; } else if (basicp()->keyword().isDpiBitVal()) { - if (widthMin() == 1) { - arg = "unsigned char"; + if (widthMin() == 1) { + arg = "unsigned char"; if (!forReturn && isWritable()) arg += "*"; } else { if (forReturn) { arg = "svBitVecVal"; } else if (isReadOnly()) { - arg = "const svBitVecVal*"; - } else { - arg = "svBitVecVal*"; - } - } + arg = "const svBitVecVal*"; + } else { + arg = "svBitVecVal*"; + } + } } else if (basicp()->keyword().isDpiLogicVal()) { if (widthMin() == 1) { arg = "unsigned char"; @@ -408,9 +410,9 @@ string AstVar::dpiArgType(bool named, bool forReturn) const { } } } else { - arg = basicp()->keyword().dpiType(); - if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) { - arg = "unsigned "+arg; + arg = basicp()->keyword().dpiType(); + if (basicp()->keyword().isDpiUnsignable() && !basicp()->isSigned()) { + arg = "unsigned "+arg; } if (!forReturn && isWritable()) arg += "*"; } @@ -420,23 +422,23 @@ string AstVar::dpiArgType(bool named, bool forReturn) const { string AstVar::scType() const { if (isScBigUint()) { - return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> + return (string("sc_biguint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> } else if (isScUint()) { - return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> + return (string("sc_uint<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> } else if (isScBv()) { - return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> + return (string("sc_bv<")+cvtToStr(widthMin())+"> "); // Keep the space so don't get >> } else if (widthMin() == 1) { - return "bool"; + return "bool"; } else if (widthMin() <= VL_WORDSIZE) { - if (widthMin() <= 8 && v3Global.opt.pinsUint8()) { - return "uint8_t"; - } else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) { - return "uint16_t"; - } else { - return "uint32_t"; - } + if (widthMin() <= 8 && v3Global.opt.pinsUint8()) { + return "uint8_t"; + } else if (widthMin() <= 16 && v3Global.opt.pinsUint8()) { + return "uint16_t"; + } else { + return "uint32_t"; + } } else { - return "vluint64_t"; + return "vluint64_t"; } } @@ -445,18 +447,18 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) { // Historically sc variables are identified by a variable // attribute. TODO it would better be a data type attribute. if (AstVar* anodep = VN_CAST(nodep, Var)) { - if (anodep->isSc()) return anodep; - else return NULL; + if (anodep->isSc()) return anodep; + else return NULL; } else if (VN_IS(nodep, VarRef)) { if (VN_CAST(nodep, VarRef)->varp()->isSc()) return VN_CAST(nodep, VarRef)->varp(); - else return NULL; + else return NULL; } else if (VN_IS(nodep, ArraySel)) { - if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p; - if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p; - if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p; - if (nodep->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p; + if (nodep->op1p()) if (AstVar* p = scVarRecurse(nodep->op1p())) return p; + if (nodep->op2p()) if (AstVar* p = scVarRecurse(nodep->op2p())) return p; + if (nodep->op3p()) if (AstVar* p = scVarRecurse(nodep->op3p())) return p; + if (nodep->op4p()) if (AstVar* p = scVarRecurse(nodep->op4p())) return p; } return NULL; } @@ -478,58 +480,58 @@ AstNodeDType* AstNodeDType::dtypeDimensionp(int dimension) { // ref order: a[1][2][3][4] // Created as: reg [4] a [1][2][3]; // *or* reg a [1][2][3][4]; - // // The bit select is optional; used only if "leftover" []'s - // SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a)))) - // DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3)))) - // *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT)))) - // SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0 + // // The bit select is optional; used only if "leftover" []'s + // SEL: SEL4(SEL3(SEL2(SEL1(VARREF0 a)))) + // DECL: VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (DT RANGE3)))) + // *or* VAR a (ARRAYSEL0 (ARRAYSEL1 (ARRAYSEL2 (ARRAYSEL3 (DT)))) + // SEL1 needs to select from entire variable which is a pointer to ARRAYSEL0 // TODO this function should be removed in favor of recursing the dtype(), // as that allows for more complicated data types. int dim = 0; for (AstNodeDType* dtypep=this; dtypep; ) { - dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) { - if ((dim++)==dimension) { - return dtypep; - } - dtypep = adtypep->subDTypep(); - continue; - } + if ((dim++)==dimension) { + return dtypep; + } + dtypep = adtypep->subDTypep(); + continue; + } else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) { - // AstBasicDType - nothing below, return null - if (adtypep->isRanged()) { - if ((dim++) == dimension) { - return adtypep; - } - } - return NULL; - } + // AstBasicDType - nothing below, return null + if (adtypep->isRanged()) { + if ((dim++) == dimension) { + return adtypep; + } + } + return NULL; + } else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) { - if (adtypep->packed()) { - if ((dim++) == dimension) { - return adtypep; - } - } - return NULL; - } - // Node no ->next in loop; use continue where necessary - break; + if (adtypep->packed()) { + if ((dim++) == dimension) { + return adtypep; + } + } + return NULL; + } + // Node no ->next in loop; use continue where necessary + break; } return NULL; } uint32_t AstNodeDType::arrayUnpackedElements() { - uint32_t entries=1; + uint32_t entries = 1; for (AstNodeDType* dtypep=this; dtypep; ) { - dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) { - entries *= adtypep->elementsConst(); - dtypep = adtypep->subDTypep(); - } - else { - // AstBasicDType - nothing below, 1 - break; - } + entries *= adtypep->elementsConst(); + dtypep = adtypep->subDTypep(); + } + else { + // AstBasicDType - nothing below, 1 + break; + } } return entries; } @@ -539,17 +541,17 @@ std::pair AstNodeDType::dimensions(bool includeBasic) { uint32_t packed = 0; uint32_t unpacked = 0; for (AstNodeDType* dtypep=this; dtypep; ) { - dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) { if (VN_IS(adtypep, PackArrayDType)) packed++; - else unpacked++; - dtypep = adtypep->subDTypep(); - continue; - } + else unpacked++; + dtypep = adtypep->subDTypep(); + continue; + } else if (const AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) { - if (includeBasic && adtypep->isRanged()) packed++; - } - break; + if (includeBasic && adtypep->isRanged()) packed++; + } + break; } return make_pair(packed, unpacked); } @@ -558,7 +560,7 @@ int AstNodeDType::widthPow2() const { // I.e. width 30 returns 32, width 32 returns 32. uint32_t width = this->width(); for (int p2=30; p2>=0; p2--) { - if (width > (1UL< (1UL<lhsp(); } - continue; - } - else break; + continue; + } + else break; } return nodep; } @@ -604,7 +606,7 @@ string AstScope::nameDotless() const { string out = shortName(); string::size_type pos; while ((pos = out.find('.')) != string::npos) { - out.replace(pos, 1, "__"); + out.replace(pos, 1, "__"); } return out; } @@ -612,29 +614,29 @@ string AstScope::nameDotless() const { string AstScopeName::scopePrettyNameFormatter(AstText* scopeTextp) const { string out; for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) { - out += textp->text(); + out += textp->text(); } // TOP will be replaced by top->name() - if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,""); - if (out.substr(0,7) == "__DOT__") out.replace(0,7,""); - if (out.substr(0,1) == ".") out.replace(0,1,""); + if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, ""); + if (out.substr(0, 7) == "__DOT__") out.replace(0, 7, ""); + if (out.substr(0, 1) == ".") out.replace(0, 1, ""); return AstNode::prettyName(out); } string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const { string out; for (AstText* textp=scopeTextp; textp; textp=VN_CAST(textp->nextp(), Text)) { - out += textp->text(); + out += textp->text(); } - if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,""); - if (out.substr(0,7) == "__DOT__") out.replace(0,7,""); - if (out.substr(0,1) == ".") out.replace(0,1,""); + if (out.substr(0, 10) == "__DOT__TOP") out.replace(0, 10, ""); + if (out.substr(0, 7) == "__DOT__") out.replace(0, 7, ""); + if (out.substr(0, 1) == ".") out.replace(0, 1, ""); string::size_type pos; while ((pos = out.find('.')) != string::npos) { - out.replace(pos, 1, "__"); + out.replace(pos, 1, "__"); } - while ((pos=out.find("__DOT__")) != string::npos) { - out.replace(pos, 7, "__"); + while ((pos = out.find("__DOT__")) != string::npos) { + out.replace(pos, 7, "__"); } return out; } @@ -642,28 +644,28 @@ string AstScopeName::scopeNameFormatter(AstText* scopeTextp) const { bool AstSenTree::hasClocked() const { if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { - if (senp->isClocked()) return true; + if (senp->isClocked()) return true; } return false; } bool AstSenTree::hasSettle() const { if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { - if (senp->isSettle()) return true; + if (senp->isSettle()) return true; } return false; } bool AstSenTree::hasInitial() const { if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { - if (senp->isInitial()) return true; + if (senp->isInitial()) return true; } return false; } bool AstSenTree::hasCombo() const { if (!sensesp()) this->v3fatalSrc("SENTREE without any SENITEMs under it"); for (AstNodeSenItem* senp = sensesp(); senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { - if (senp->isCombo()) return true; + if (senp->isCombo()) return true; } return false; } @@ -672,20 +674,20 @@ void AstTypeTable::clearCache() { // When we mass-change widthMin in V3WidthCommit, we need to correct the table. // Just clear out the maps; the search functions will be used to rebuild the map for (int i=0; i < static_cast(AstBasicDTypeKwd::_ENUM_MAX); ++i) { - m_basicps[i] = NULL; + m_basicps[i] = NULL; } for (int isbit=0; isbit<_IDX0_MAX; ++isbit) { - for (int numer=0; numernextp()) { if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) { - bdtypep->generic(false); - } + bdtypep->generic(false); + } } } @@ -694,8 +696,8 @@ void AstTypeTable::repairCache() { clearCache(); for (AstNode* nodep = typesp(); nodep; nodep=nodep->nextp()) { if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) { - (void)findInsertSameDType(bdtypep); - } + (void)findInsertSameDType(bdtypep); + } } } @@ -715,12 +717,12 @@ AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) } AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, - int width, int widthMin, AstNumeric numeric) { + int width, int widthMin, AstNumeric numeric) { int idx = IDX0_LOGIC; if (kwd == AstBasicDTypeKwd::LOGIC) idx = IDX0_LOGIC; else if (kwd == AstBasicDTypeKwd::BIT) idx = IDX0_BIT; else fl->v3fatalSrc("Bad kwd for findLogicBitDType"); - std::pair widths = make_pair(width,widthMin); + std::pair widths = make_pair(width, widthMin); LogicMap& mapr = m_logicMap[idx][static_cast(numeric)]; LogicMap::const_iterator it = mapr.find(widths); if (it != mapr.end()) return it->second; @@ -733,12 +735,12 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw if (newp != new1p) new1p->deleteTree(); else addTypesp(newp); // - mapr.insert(make_pair(widths,newp)); + mapr.insert(make_pair(widths, newp)); return newp; } AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd, - VNumRange range, int widthMin, AstNumeric numeric) { + VNumRange range, int widthMin, AstNumeric numeric) { AstBasicDType* new1p = new AstBasicDType(fl, kwd, numeric, range, widthMin); AstBasicDType* newp = findInsertSameDType(new1p); if (newp != new1p) new1p->deleteTree(); @@ -748,11 +750,11 @@ AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kw AstBasicDType* AstTypeTable::findInsertSameDType(AstBasicDType* nodep) { VBasicTypeKey key (nodep->width(), nodep->widthMin(), nodep->numeric(), - nodep->keyword(), nodep->nrange()); + nodep->keyword(), nodep->nrange()); DetailedMap& mapr = m_detailedMap; DetailedMap::const_iterator it = mapr.find(key); if (it != mapr.end()) return it->second; - mapr.insert(make_pair(key,nodep)); + mapr.insert(make_pair(key, nodep)); nodep->generic(true); // No addTypesp; the upper function that called new() is responsible for adding return nodep; @@ -784,39 +786,41 @@ void AstNodeStmt::addNextStmt(AstNode* newp, AstNode*) { void AstWhile::addBeforeStmt(AstNode* newp, AstNode* belowp) { // Special, as statements need to be put in different places // Belowp is how we came to recurse up to this point - // Preconditions insert first just before themselves (the normal rule for other statement types) + // Preconditions insert first just before themselves (the normal rule + // for other statement types) if (belowp == precondsp()) { - // Must have been first statement in precondsp list, so newp is new first statement - belowp->addHereThisAsNext(newp); + // Must have been first statement in precondsp list, so newp is new first statement + belowp->addHereThisAsNext(newp); } else if (belowp == condp()) { - // Goes before condition, IE in preconditions - addPrecondsp(newp); + // Goes before condition, IE in preconditions + addPrecondsp(newp); } else if (belowp == bodysp()) { - // Was first statement in body, so new front - belowp->addHereThisAsNext(newp); + // Was first statement in body, so new front + belowp->addHereThisAsNext(newp); } else { - belowp->v3fatalSrc("Doesn't look like this was really under the while"); + belowp->v3fatalSrc("Doesn't look like this was really under the while"); } } void AstWhile::addNextStmt(AstNode* newp, AstNode* belowp) { // Special, as statements need to be put in different places // Belowp is how we came to recurse up to this point - // Preconditions insert first just before themselves (the normal rule for other statement types) + // Preconditions insert first just before themselves (the normal rule + // for other statement types) if (belowp == precondsp()) { - // Next in precond list - belowp->addNextHere(newp); + // Next in precond list + belowp->addNextHere(newp); } else if (belowp == condp()) { - // Becomes first statement in body, body may have been empty - if (bodysp()) { - bodysp()->addHereThisAsNext(newp); - } else { - addBodysp(newp); - } + // Becomes first statement in body, body may have been empty + if (bodysp()) { + bodysp()->addHereThisAsNext(newp); + } else { + addBodysp(newp); + } } else if (belowp == bodysp()) { - // Next statement in body - belowp->addNextHere(newp); + // Next statement in body + belowp->addNextHere(newp); } else { - belowp->v3fatalSrc("Doesn't look like this was really under the while"); + belowp->v3fatalSrc("Doesn't look like this was really under the while"); } } @@ -836,12 +840,12 @@ void AstNode::dump(std::ostream& str) { if (user5p()) str<<" u5="<dumpSmall(str); - } - } else { // V3Broken will throw an error + if (AstNodeDType* dtp = dtypep()) { + dtp->dumpSmall(str); + } + } else { // V3Broken will throw an error if (dtypep()) str<<" %Error-dtype-exp=null,got="<AstNode::dump(str); if (declRange().ranged()) { - str<<" decl"<AstNode::dump(str); for (int i=0; i < static_cast(AstBasicDTypeKwd::_ENUM_MAX); ++i) { - if (AstBasicDType* subnodep=m_basicps[i]) { - str< "; - subnodep->dump(str); - } + str<<" -> "; + subnodep->dump(str); + } } for (int isbit=0; isbit<2; ++isbit) { - for (int issigned=0; issignedsecond; - str<second; + str<first.first<<"/"<first.second; + nsstr<<(isbit?"bw":"lw") + <first.first<<"/"<first.second; str<<"\t\t"< "; - dtypep->dump(str); - } - } + if (issigned) str<<" s"; else str<<" u"; + str<<" -> "; + dtypep->dump(str); + } + } } { - DetailedMap& mapr = m_detailedMap; - for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) { - AstBasicDType* dtypep = it->second; - str< "; - dtypep->dump(str); - } + DetailedMap& mapr = m_detailedMap; + for (DetailedMap::const_iterator it = mapr.begin(); it != mapr.end(); ++it) { + AstBasicDType* dtypep = it->second; + str< "; + dtypep->dump(str); + } } // Note get newline from caller too. } @@ -1158,8 +1162,8 @@ void AstBegin::dump(std::ostream& str) { void AstCoverDecl::dump(std::ostream& str) { this->AstNode::dump(str); if (this->dataDeclNullp()) { - str<<" -> "; - this->dataDeclNullp()->dump(str); + str<<" -> "; + this->dataDeclNullp()->dump(str); } else { if (binNum()) { str<<" bin"<AstNode::dump(str); if (funcp()) { - str<<" "<name()<<" => "; - funcp()->dump(str); + str<<" "<name()<<" => "; + funcp()->dump(str); } } void AstCFunc::dump(std::ostream& str) { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 016db2c1c..a1aa673f4 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -95,14 +95,14 @@ public: AstConst(FileLine* fl, Unsized32, uint32_t num) // Unsized 32-bit integer of specified value : AstNodeMath(fl) , m_num(V3Number(this, 32, num)) { - m_num.width(32,false); + m_num.width(32, false); dtypeSetLogicSized(32, m_num.widthMin(), AstNumeric::UNSIGNED); } class Signed32 {}; // for creator type-overload selection AstConst(FileLine* fl, Signed32, int32_t num) // Signed 32-bit integer of specified value : AstNodeMath(fl) , m_num(V3Number(this, 32, num)) { - m_num.width(32,32); + m_num.width(32, 32); dtypeSetLogicSized(32, m_num.widthMin(), AstNumeric::SIGNED); } class RealDouble {}; // for creator type-overload selection @@ -124,16 +124,16 @@ public: ASTNODE_NODE_FUNCS(Const) virtual string name() const { return num().ascii(); } // * = Value virtual const V3Number& num() const { return m_num; } // * = Value - uint32_t toUInt() const { return num().toUInt(); } - vlsint32_t toSInt() const { return num().toSInt(); } + uint32_t toUInt() const { return num().toUInt(); } + vlsint32_t toSInt() const { return num().toSInt(); } vluint64_t toUQuad() const { return num().toUQuad(); } virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return true; } virtual V3Hash sameHash() const { return V3Hash(num().toHash()); } virtual bool same(const AstNode* samep) const { - const AstConst* sp = static_cast(samep); - return num().isCaseEq(sp->num()); } + const AstConst* sp = static_cast(samep); + return num().isCaseEq(sp->num()); } virtual int instrCount() const { return widthInstrs(); } bool isEqAllOnes() const { return num().isEqAllOnes(width()); } bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); } @@ -142,35 +142,37 @@ public: class AstRange : public AstNodeRange { // Range specification, for use under variables and cells private: - bool m_littleEndian:1; // Bit vector is little endian + bool m_littleEndian:1; // Bit vector is little endian public: AstRange(FileLine* fl, AstNode* msbp, AstNode* lsbp) : AstNodeRange(fl) { - m_littleEndian = false; - setOp2p(msbp); setOp3p(lsbp); } + m_littleEndian = false; + setOp2p(msbp); setOp3p(lsbp); } AstRange(FileLine* fl, int msb, int lsb) : AstNodeRange(fl) { - m_littleEndian = false; - setOp2p(new AstConst(fl,msb)); setOp3p(new AstConst(fl,lsb)); + m_littleEndian = false; + setOp2p(new AstConst(fl, msb)); + setOp3p(new AstConst(fl, lsb)); } AstRange(FileLine* fl, const VNumRange& range) : AstNodeRange(fl) { - m_littleEndian = range.littleEndian(); - setOp2p(new AstConst(fl,range.hi())); setOp3p(new AstConst(fl,range.lo())); + m_littleEndian = range.littleEndian(); + setOp2p(new AstConst(fl, range.hi())); + setOp3p(new AstConst(fl, range.lo())); } ASTNODE_NODE_FUNCS(Range) AstNode* msbp() const { return op2p(); } // op2 = Msb expression AstNode* lsbp() const { return op3p(); } // op3 = Lsb expression AstNode* leftp() const { return littleEndian()?lsbp():msbp(); } // How to show a declaration AstNode* rightp() const { return littleEndian()?msbp():lsbp(); } - int msbConst() const { AstConst* constp=VN_CAST(msbp(), Const); return (constp?constp->toSInt():0); } - int lsbConst() const { AstConst* constp=VN_CAST(lsbp(), Const); return (constp?constp->toSInt():0); } + int msbConst() const { AstConst* constp = VN_CAST(msbp(), Const); return (constp?constp->toSInt():0); } + int lsbConst() const { AstConst* constp = VN_CAST(lsbp(), Const); return (constp?constp->toSInt():0); } int elementsConst() const { return (msbConst()>lsbConst()) ? msbConst()-lsbConst()+1 : lsbConst()-msbConst()+1; } - int leftConst() const { AstConst* constp=VN_CAST(leftp(), Const); return (constp?constp->toSInt():0); } - int rightConst() const { AstConst* constp=VN_CAST(rightp(), Const); return (constp?constp->toSInt():0); } + int leftConst() const { AstConst* constp = VN_CAST(leftp(), Const); return (constp?constp->toSInt():0); } + int rightConst() const { AstConst* constp = VN_CAST(rightp(), Const); return (constp?constp->toSInt():0); } int leftToRightInc() const { return littleEndian()?1:-1; } bool littleEndian() const { return m_littleEndian; } - void littleEndian(bool flag) { m_littleEndian=flag; } + void littleEndian(bool flag) { m_littleEndian = flag; } virtual void dump(std::ostream& str); virtual string emitC() { V3ERROR_NA; return ""; } virtual V3Hash sameHash() const { return V3Hash(); } @@ -192,13 +194,13 @@ class AstGatePin : public AstNodeMath { // Possibly expand a gate primitive input pin value to match the range of the gate primitive public: AstGatePin(FileLine* fl, AstNode* lhsp, AstRange* rangep) : AstNodeMath(fl) { - setOp1p(lhsp); setOp2p(rangep); + setOp1p(lhsp); setOp2p(rangep); } ASTNODE_NODE_FUNCS(GatePin) virtual string emitVerilog() { return "%l"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return true; } - AstNode* exprp() const { return op1p(); } // op1 = Pin expression + AstNode* exprp() const { return op1p(); } // op1 = Pin expression AstRange* rangep() const { return VN_CAST(op2p(), Range); } // op2 = Range of pin }; @@ -209,18 +211,18 @@ class AstParamTypeDType : public AstNodeDType { // Parents: MODULE // A parameter type statement; much like a var or typedef private: - AstVarType m_varType; // Type of variable (for localparam vs. param) - string m_name; // Name of variable + AstVarType m_varType; // Type of variable (for localparam vs. param) + string m_name; // Name of variable public: AstParamTypeDType(FileLine* fl, AstVarType type, const string& name, VFlagChildDType, AstNodeDType* dtp) - : AstNodeDType(fl), m_varType(type), m_name(name) { - childDTypep(dtp); // Only for parser - dtypep(NULL); // V3Width will resolve + : AstNodeDType(fl), m_varType(type), m_name(name) { + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve } ASTNODE_NODE_FUNCS(ParamTypeDType) AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to - void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return subDTypep()->skipRefp(); } @@ -228,7 +230,7 @@ public: virtual AstNodeDType* skipRefToEnump() const { return subDTypep()->skipRefToEnump(); } virtual bool similarDType(AstNodeDType* samep) const { const AstParamTypeDType* sp = static_cast(samep); - return (sp && this->subDTypep()->skipRefp()->similarDType(sp->subDTypep()->skipRefp())); + return (sp && this->subDTypep()->skipRefp()->similarDType(sp->subDTypep()->skipRefp())); } virtual int widthAlignBytes() const { return dtypep()->widthAlignBytes(); } virtual int widthTotalBytes() const { return dtypep()->widthTotalBytes(); } @@ -244,25 +246,25 @@ public: class AstTypedef : public AstNode { private: - string m_name; - bool m_attrPublic; - string m_tag; // Holds the string of the verilator tag -- used in XML output. + string m_name; + bool m_attrPublic; + string m_tag; // Holds the string of the verilator tag -- used in XML output. public: AstTypedef(FileLine* fl, const string& name, AstNode* attrsp, VFlagChildDType, AstNodeDType* dtp) - : AstNode(fl), m_name(name) { - childDTypep(dtp); // Only for parser - addAttrsp(attrsp); - dtypep(NULL); // V3Width will resolve - m_attrPublic = false; + : AstNode(fl), m_name(name) { + childDTypep(dtp); // Only for parser + addAttrsp(attrsp); + dtypep(NULL); // V3Width will resolve + m_attrPublic = false; } ASTNODE_NODE_FUNCS(Typedef) virtual void dump(std::ostream& str); AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to - void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } - void addAttrsp(AstNode* nodep) { addNOp4p(nodep); } - AstNode* attrsp() const { return op4p(); } // op4 = Attributes during early parse + void addAttrsp(AstNode* nodep) { addNOp4p(nodep); } + AstNode* attrsp() const { return op4p(); } // op4 = Attributes during early parse // METHODS virtual string name() const { return m_name; } virtual bool maybePointedTo() const { return true; } @@ -277,10 +279,10 @@ public: class AstTypedefFwd : public AstNode { // Forward declaration of a type; stripped after netlist parsing is complete private: - string m_name; + string m_name; public: AstTypedefFwd(FileLine* fl, const string& name) - : AstNode(fl), m_name(name) {} + : AstNode(fl), m_name(name) {} ASTNODE_NODE_FUNCS(TypedefFwd) // METHODS virtual string name() const { return m_name; } @@ -291,31 +293,31 @@ class AstDefImplicitDType : public AstNodeDType { // This allows "var enum {...} a,b" to share the enum definition for both variables // After link, these become typedefs private: - string m_name; - void* m_containerp; // In what scope is the name unique, so we can know what are duplicate definitions (arbitrary value) - int m_uniqueNum; + string m_name; + void* m_containerp; // In what scope is the name unique, so we can know what are duplicate definitions (arbitrary value) + int m_uniqueNum; public: AstDefImplicitDType(FileLine* fl, const string& name, void* containerp, - VFlagChildDType, AstNodeDType* dtp) - : AstNodeDType(fl), m_name(name), m_containerp(containerp) { - childDTypep(dtp); // Only for parser - dtypep(NULL); // V3Width will resolve - m_uniqueNum = uniqueNumInc(); + VFlagChildDType, AstNodeDType* dtp) + : AstNodeDType(fl), m_name(name), m_containerp(containerp) { + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve + m_uniqueNum = uniqueNumInc(); } ASTNODE_NODE_FUNCS(DefImplicitDType) virtual bool same(const AstNode* samep) const { - const AstDefImplicitDType* sp = static_cast(samep); - return m_uniqueNum == sp->m_uniqueNum; } + const AstDefImplicitDType* sp = static_cast(samep); + return m_uniqueNum == sp->m_uniqueNum; } virtual bool similarDType(AstNodeDType* samep) const { - return type()==samep->type() && same(samep); } + return type()==samep->type() && same(samep); } virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); } AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable - void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } - void* containerp() const { return m_containerp; } + void* containerp() const { return m_containerp; } // METHODS - AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable + AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } @@ -332,21 +334,21 @@ class AstPackArrayDType : public AstNodeArrayDType { // Children: RANGE (array bounds) public: AstPackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep) - : AstNodeArrayDType(fl) { - childDTypep(dtp); // Only for parser - refDTypep(NULL); - setOp2p(rangep); - dtypep(NULL); // V3Width will resolve - int width = subDTypep()->width() * rangep->elementsConst(); - widthForce(width,width); + : AstNodeArrayDType(fl) { + childDTypep(dtp); // Only for parser + refDTypep(NULL); + setOp2p(rangep); + dtypep(NULL); // V3Width will resolve + int width = subDTypep()->width() * rangep->elementsConst(); + widthForce(width, width); } AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep) - : AstNodeArrayDType(fl) { - refDTypep(dtp); - setOp2p(rangep); - dtypep(this); - int width = subDTypep()->width() * rangep->elementsConst(); - widthForce(width,width); + : AstNodeArrayDType(fl) { + refDTypep(dtp); + setOp2p(rangep); + dtypep(this); + int width = subDTypep()->width() * rangep->elementsConst(); + widthForce(width, width); } ASTNODE_NODE_FUNCS(PackArrayDType) }; @@ -357,21 +359,23 @@ class AstUnpackArrayDType : public AstNodeArrayDType { // Children: RANGE (array bounds) public: AstUnpackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep) - : AstNodeArrayDType(fl) { - childDTypep(dtp); // Only for parser - refDTypep(NULL); - setOp2p(rangep); - dtypep(NULL); // V3Width will resolve - // For backward compatibility AstNodeArrayDType and others inherit width and signing from the subDType/base type - widthFromSub(subDTypep()); + : AstNodeArrayDType(fl) { + childDTypep(dtp); // Only for parser + refDTypep(NULL); + setOp2p(rangep); + dtypep(NULL); // V3Width will resolve + // For backward compatibility AstNodeArrayDType and others inherit + // width and signing from the subDType/base type + widthFromSub(subDTypep()); } AstUnpackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep) - : AstNodeArrayDType(fl) { - refDTypep(dtp); - setOp2p(rangep); - dtypep(this); - // For backward compatibility AstNodeArrayDType and others inherit width and signing from the subDType/base type - widthFromSub(subDTypep()); + : AstNodeArrayDType(fl) { + refDTypep(dtp); + setOp2p(rangep); + dtypep(this); + // For backward compatibility AstNodeArrayDType and others inherit + // width and signing from the subDType/base type + widthFromSub(subDTypep()); } ASTNODE_NODE_FUNCS(UnpackArrayDType) }; @@ -424,117 +428,118 @@ class AstBasicDType : public AstNodeDType { // Children: RANGE (converted to constant in V3Width) private: struct Members { - AstBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type - VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword) - bool operator== (const Members& rhs) const { - return rhs.m_keyword == m_keyword - && rhs.m_nrange == m_nrange; } + AstBasicDTypeKwd m_keyword; // (also in VBasicTypeKey) What keyword created basic type + VNumRange m_nrange; // (also in VBasicTypeKey) Numeric msb/lsb (if non-opaque keyword) + bool operator==(const Members& rhs) const { + return rhs.m_keyword == m_keyword + && rhs.m_nrange == m_nrange; } } m; // See also in AstNodeDType: m_width, m_widthMin, m_numeric(issigned) public: AstBasicDType(FileLine* fl, AstBasicDTypeKwd kwd, VSignedState signst=signedst_NOSIGN) - : AstNodeDType(fl) { - init(kwd, AstNumeric(signst), 0, -1, NULL); + : AstNodeDType(fl) { + init(kwd, AstNumeric(signst), 0, -1, NULL); } AstBasicDType(FileLine* fl, VFlagLogicPacked, int wantwidth) - : AstNodeDType(fl) { - init(AstBasicDTypeKwd::LOGIC, AstNumeric::NOSIGN, wantwidth, -1, NULL); + : AstNodeDType(fl) { + init(AstBasicDTypeKwd::LOGIC, AstNumeric::NOSIGN, wantwidth, -1, NULL); } AstBasicDType(FileLine* fl, VFlagBitPacked, int wantwidth) - : AstNodeDType(fl) { - init(AstBasicDTypeKwd::BIT, AstNumeric::NOSIGN, wantwidth, -1, NULL); + : AstNodeDType(fl) { + init(AstBasicDTypeKwd::BIT, AstNumeric::NOSIGN, wantwidth, -1, NULL); } AstBasicDType(FileLine* fl, AstBasicDTypeKwd kwd, AstNumeric numer, int wantwidth, int widthmin) - : AstNodeDType(fl) { - init(kwd, numer, wantwidth, widthmin, NULL); + : AstNodeDType(fl) { + init(kwd, numer, wantwidth, widthmin, NULL); } AstBasicDType(FileLine* fl, AstBasicDTypeKwd kwd, AstNumeric numer, VNumRange range, int widthmin) - : AstNodeDType(fl) { - init(kwd, numer, range.elements(), widthmin, NULL); - m.m_nrange = range; // as init() presumes lsb==0, but range.lsb() might not be + : AstNodeDType(fl) { + init(kwd, numer, range.elements(), widthmin, NULL); + m.m_nrange = range; // as init() presumes lsb==0, but range.lsb() might not be } // See also addRange in verilog.y private: void init(AstBasicDTypeKwd kwd, AstNumeric numer, - int wantwidth, int wantwidthmin, AstRange* rangep) { - // wantwidth=0 means figure it out, but if a widthmin is >=0 - // we allow width 0 so that {{0{x}},y} works properly - // wantwidthmin=-1: default, use wantwidth if it is non zero - m.m_keyword = kwd; - // Implicitness: // "parameter X" is implicit and sized from initial value, "parameter reg x" not - if (keyword()==AstBasicDTypeKwd::LOGIC_IMPLICIT) { - if (rangep || wantwidth) m.m_keyword = AstBasicDTypeKwd::LOGIC; - } + int wantwidth, int wantwidthmin, AstRange* rangep) { + // wantwidth=0 means figure it out, but if a widthmin is >=0 + // we allow width 0 so that {{0{x}},y} works properly + // wantwidthmin=-1: default, use wantwidth if it is non zero + m.m_keyword = kwd; + // Implicitness: // "parameter X" is implicit and sized from initial + // value, "parameter reg x" not + if (keyword()==AstBasicDTypeKwd::LOGIC_IMPLICIT) { + if (rangep || wantwidth) m.m_keyword = AstBasicDTypeKwd::LOGIC; + } if (numer == AstNumeric::NOSIGN) { - if (keyword().isSigned()) numer = AstNumeric::SIGNED; - else if (keyword().isUnsigned()) numer = AstNumeric::UNSIGNED; - } - numeric(numer); - if (!rangep && (wantwidth || wantwidthmin>=0)) { // Constant width - if (wantwidth>1) m.m_nrange.init(wantwidth-1, 0, false); - int wmin = wantwidthmin>=0 ? wantwidthmin : wantwidth; - widthForce(wantwidth, wmin); - } else if (!rangep) { // Set based on keyword properties - // V3Width will pull from this width - if (keyword().width() > 1 && !isOpaque()) { - m.m_nrange.init(keyword().width()-1, 0, false); - } - widthForce(keyword().width(), keyword().width()); - } else { - widthForce(rangep->elementsConst(), rangep->elementsConst()); // Maybe unknown if parameters underneath it - } - setNOp1p(rangep); - dtypep(this); + if (keyword().isSigned()) numer = AstNumeric::SIGNED; + else if (keyword().isUnsigned()) numer = AstNumeric::UNSIGNED; + } + numeric(numer); + if (!rangep && (wantwidth || wantwidthmin>=0)) { // Constant width + if (wantwidth>1) m.m_nrange.init(wantwidth-1, 0, false); + int wmin = wantwidthmin>=0 ? wantwidthmin : wantwidth; + widthForce(wantwidth, wmin); + } else if (!rangep) { // Set based on keyword properties + // V3Width will pull from this width + if (keyword().width() > 1 && !isOpaque()) { + m.m_nrange.init(keyword().width()-1, 0, false); + } + widthForce(keyword().width(), keyword().width()); + } else { + widthForce(rangep->elementsConst(), rangep->elementsConst()); // Maybe unknown if parameters underneath it + } + setNOp1p(rangep); + dtypep(this); } public: ASTNODE_NODE_FUNCS(BasicDType) virtual void dump(std::ostream& str); virtual V3Hash sameHash() const { return V3Hash(V3Hash(m.m_keyword), V3Hash(m.m_nrange.hi())); } virtual bool same(const AstNode* samep) const { // width/widthMin/numeric compared elsewhere - const AstBasicDType* sp = static_cast(samep); - return m == sp->m; } + const AstBasicDType* sp = static_cast(samep); + return m == sp->m; } virtual bool similarDType(AstNodeDType* samep) const { - return type()==samep->type() && same(samep); } - virtual string name() const { return m.m_keyword.ascii(); } + return type()==samep->type() && same(samep); } + virtual string name() const { return m.m_keyword.ascii(); } virtual const char* broken() const { BROKEN_RTN(dtypep()!=this); return NULL; } AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range of variable - void rangep(AstRange* nodep) { setNOp1p(nodep); } + void rangep(AstRange* nodep) { setNOp1p(nodep); } void setSignedState(VSignedState signst) { - // Note NOSIGN does NOT change the state; this is required by the parser - if (signst==signedst_UNSIGNED) numeric(signst); - else if (signst==signedst_SIGNED) numeric(signst); + // Note NOSIGN does NOT change the state; this is required by the parser + if (signst==signedst_UNSIGNED) numeric(signst); + else if (signst==signedst_SIGNED) numeric(signst); } // METHODS virtual AstBasicDType* basicp() const { return (AstBasicDType*)this; } // (Slow) recurse down to find basic data type virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } - virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) - virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... + virtual int widthAlignBytes() const; // (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this) + virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,... virtual bool isFourstate() const { return keyword().isFourstate(); } AstBasicDTypeKwd keyword() const { return m.m_keyword; } // Avoid using - use isSomething accessors instead - bool isBitLogic() const { return keyword().isBitLogic(); } - bool isDouble() const { return keyword().isDouble(); } - bool isOpaque() const { return keyword().isOpaque(); } - bool isString() const { return keyword().isString(); } - bool isSloppy() const { return keyword().isSloppy(); } - bool isZeroInit() const { return keyword().isZeroInit(); } - bool isRanged() const { return rangep() || m.m_nrange.ranged(); } - const VNumRange& nrange() const { return m.m_nrange; } // Generally the msb/lsb/etc funcs should be used instead - int msb() const { return (rangep() ? rangep()->msbConst() : m.m_nrange.hi()); } - int lsb() const { return (rangep() ? rangep()->lsbConst() : m.m_nrange.lo()); } - int left() const { return littleEndian()?lsb():msb(); } // How to show a declaration - int right() const { return littleEndian()?msb():lsb(); } - bool littleEndian() const { return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian()); } - bool implicit() const { return keyword() == AstBasicDTypeKwd::LOGIC_IMPLICIT; } + bool isBitLogic() const { return keyword().isBitLogic(); } + bool isDouble() const { return keyword().isDouble(); } + bool isOpaque() const { return keyword().isOpaque(); } + bool isString() const { return keyword().isString(); } + bool isSloppy() const { return keyword().isSloppy(); } + bool isZeroInit() const { return keyword().isZeroInit(); } + bool isRanged() const { return rangep() || m.m_nrange.ranged(); } + const VNumRange& nrange() const { return m.m_nrange; } // Generally the msb/lsb/etc funcs should be used instead + int msb() const { return (rangep() ? rangep()->msbConst() : m.m_nrange.hi()); } + int lsb() const { return (rangep() ? rangep()->lsbConst() : m.m_nrange.lo()); } + int left() const { return littleEndian()?lsb():msb(); } // How to show a declaration + int right() const { return littleEndian()?msb():lsb(); } + bool littleEndian() const { return (rangep() ? rangep()->littleEndian() : m.m_nrange.littleEndian()); } + bool implicit() const { return keyword() == AstBasicDTypeKwd::LOGIC_IMPLICIT; } VNumRange declRange() const { return isRanged() ? VNumRange(msb(), lsb(), littleEndian()) : VNumRange(); } - void cvtRangeConst() { // Convert to smaller represenation + void cvtRangeConst() { // Convert to smaller represenation if (rangep() && VN_IS(rangep()->msbp(), Const) && VN_IS(rangep()->lsbp(), Const)) { - m.m_nrange.init(rangep()->msbConst(), rangep()->lsbConst(), - rangep()->littleEndian()); - rangep()->unlinkFrBackWithNext()->deleteTree(); - rangep(NULL); - } + m.m_nrange.init(rangep()->msbConst(), rangep()->lsbConst(), + rangep()->littleEndian()); + rangep()->unlinkFrBackWithNext()->deleteTree(); + rangep(NULL); + } } }; @@ -543,31 +548,31 @@ class AstConstDType : public AstNodeDType { // ConstDType are removed in V3LinkLValue and become AstVar::isConst. // When more generic types are supported AstConstDType will be propagated further. private: - AstNodeDType* m_refDTypep; // Inherit from this base data type + AstNodeDType* m_refDTypep; // Inherit from this base data type public: AstConstDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp) - : AstNodeDType(fl) { - childDTypep(dtp); // Only for parser - refDTypep(NULL); // V3Width will resolve - dtypep(NULL); // V3Width will resolve - widthFromSub(subDTypep()); + : AstNodeDType(fl) { + childDTypep(dtp); // Only for parser + refDTypep(NULL); // V3Width will resolve + dtypep(NULL); // V3Width will resolve + widthFromSub(subDTypep()); } ASTNODE_NODE_FUNCS(ConstDType) virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) - || (!m_refDTypep && childDTypep()))); return NULL; } + || (!m_refDTypep && childDTypep()))); return NULL; } virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) { - m_refDTypep = m_refDTypep->clonep(); + m_refDTypep = m_refDTypep->clonep(); }} virtual bool same(const AstNode* samep) const { - const AstConstDType* sp = static_cast(samep); - return (m_refDTypep == sp->m_refDTypep); } + const AstConstDType* sp = static_cast(samep); + return (m_refDTypep == sp->m_refDTypep); } virtual bool similarDType(AstNodeDType* samep) const { - return skipRefp()->similarDType(samep->skipRefp()); } + return skipRefp()->similarDType(samep->skipRefp()); } virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); } // node's type() included elsewhere AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } - virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable + virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } @@ -583,19 +588,19 @@ public: class AstIfaceRefDType : public AstNodeDType { // Reference to an interface, either for a port, or inside parent cell private: - string m_cellName; // "" = no cell, such as when connects to 'input' iface - string m_ifaceName; // Interface name - string m_modportName; // "" = no modport - AstIface* m_ifacep; // Pointer to interface; note cellp() should override - AstCell* m_cellp; // When exact parent cell known; not a guess - AstModport* m_modportp; // NULL = unlinked or no modport + string m_cellName; // "" = no cell, such as when connects to 'input' iface + string m_ifaceName; // Interface name + string m_modportName; // "" = no modport + AstIface* m_ifacep; // Pointer to interface; note cellp() should override + AstCell* m_cellp; // When exact parent cell known; not a guess + AstModport* m_modportp; // NULL = unlinked or no modport public: AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName) - : AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(""), - m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { } + : AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(""), + m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { } AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName, const string& modport) - : AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(modport), - m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { } + : AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(modport), + m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { } ASTNODE_NODE_FUNCS(IfaceRefDType) // METHODS virtual const char* broken() const; @@ -611,33 +616,33 @@ public: virtual int widthAlignBytes() const { return 1; } virtual int widthTotalBytes() const { return 1; } string cellName() const { return m_cellName; } - void cellName(const string& name) { m_cellName=name; } + void cellName(const string& name) { m_cellName = name; } string ifaceName() const { return m_ifaceName; } - void ifaceName(const string& name) { m_ifaceName=name; } + void ifaceName(const string& name) { m_ifaceName = name; } string modportName() const { return m_modportName; } - void modportName(const string& name) { m_modportName=name; } + void modportName(const string& name) { m_modportName = name; } AstIface* ifaceViaCellp() const; // Use cellp or ifacep AstIface* ifacep() const { return m_ifacep; } - void ifacep(AstIface* nodep) { m_ifacep=nodep; } + void ifacep(AstIface* nodep) { m_ifacep = nodep; } AstCell* cellp() const { return m_cellp; } - void cellp(AstCell* nodep) { m_cellp=nodep; } + void cellp(AstCell* nodep) { m_cellp = nodep; } AstModport* modportp() const { return m_modportp; } - void modportp(AstModport* modportp) { m_modportp=modportp; } + void modportp(AstModport* modportp) { m_modportp = modportp; } bool isModport() { return !m_modportName.empty(); } }; class AstRefDType : public AstNodeDType { private: - AstNodeDType* m_refDTypep; // data type pointed to, BELOW the AstTypedef - string m_name; // Name of an AstTypedef - AstPackage* m_packagep; // Package hierarchy + AstNodeDType* m_refDTypep; // data type pointed to, BELOW the AstTypedef + string m_name; // Name of an AstTypedef + AstPackage* m_packagep; // Package hierarchy public: AstRefDType(FileLine* fl, const string& name) - : AstNodeDType(fl), m_refDTypep(NULL), m_name(name), m_packagep(NULL) {} + : AstNodeDType(fl), m_refDTypep(NULL), m_name(name), m_packagep(NULL) {} AstRefDType(FileLine* fl, AstNodeDType* defp) - : AstNodeDType(fl), m_refDTypep(defp), m_packagep(NULL) { - dtypeFrom(defp->dtypep()); - widthFromSub(subDTypep()); + : AstNodeDType(fl), m_refDTypep(defp), m_packagep(NULL) { + dtypeFrom(defp->dtypep()); + widthFromSub(subDTypep()); } ASTNODE_NODE_FUNCS(RefDType) // METHODS @@ -646,47 +651,47 @@ public: m_refDTypep = m_refDTypep->clonep(); }} virtual bool same(const AstNode* samep) const { - const AstRefDType* asamep = static_cast(samep); - return (m_refDTypep == asamep->m_refDTypep - && m_name == asamep->m_name - && m_packagep == asamep->m_packagep); } + const AstRefDType* asamep = static_cast(samep); + return (m_refDTypep == asamep->m_refDTypep + && m_name == asamep->m_name + && m_packagep == asamep->m_packagep); } virtual bool similarDType(AstNodeDType* samep) const { - return skipRefp()->similarDType(samep->skipRefp()); } - virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep),V3Hash(m_packagep)); } + return skipRefp()->similarDType(samep->skipRefp()); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_refDTypep), V3Hash(m_packagep)); } virtual void dump(std::ostream& str=std::cout); virtual string name() const { return m_name; } virtual AstBasicDType* basicp() const { return subDTypep() ? subDTypep()->basicp() : NULL; } virtual AstNodeDType* skipRefp() const { - // Skip past both the Ref and the Typedef - if (defp()) return defp()->skipRefp(); - else { v3fatalSrc("Typedef not linked"); return NULL; } + // Skip past both the Ref and the Typedef + if (defp()) return defp()->skipRefp(); + else { v3fatalSrc("Typedef not linked"); return NULL; } } virtual AstNodeDType* skipRefToConstp() const { - if (defp()) return defp()->skipRefToConstp(); - else { v3fatalSrc("Typedef not linked"); return NULL; } + if (defp()) return defp()->skipRefToConstp(); + else { v3fatalSrc("Typedef not linked"); return NULL; } } virtual AstNodeDType* skipRefToEnump() const { - if (defp()) return defp()->skipRefToEnump(); - else { v3fatalSrc("Typedef not linked"); return NULL; } + if (defp()) return defp()->skipRefToEnump(); + else { v3fatalSrc("Typedef not linked"); return NULL; } } virtual int widthAlignBytes() const { return dtypeSkipRefp()->widthAlignBytes(); } virtual int widthTotalBytes() const { return dtypeSkipRefp()->widthTotalBytes(); } void name(const string& flag) { m_name = flag; } - AstNodeDType* dtypeSkipRefp() const { return defp()->skipRefp(); } // op1 = Range of variable - AstNodeDType* defp() const { return m_refDTypep; } // Code backward compatibility name for refDTypep + AstNodeDType* dtypeSkipRefp() const { return defp()->skipRefp(); } // op1 = Range of variable + AstNodeDType* defp() const { return m_refDTypep; } // Code backward compatibility name for refDTypep AstNodeDType* refDTypep() const { return m_refDTypep; } - void refDTypep(AstNodeDType* nodep) { m_refDTypep=nodep; } + void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } virtual AstNodeDType* virtRefDTypep() const { return refDTypep(); } virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } virtual AstNodeDType* subDTypep() const { return m_refDTypep; } AstPackage* packagep() const { return m_packagep; } - void packagep(AstPackage* nodep) { m_packagep=nodep; } + void packagep(AstPackage* nodep) { m_packagep = nodep; } }; class AstStructDType : public AstNodeClassDType { public: AstStructDType(FileLine* fl, AstNumeric numericUnpack) - : AstNodeClassDType(fl,numericUnpack) {} + : AstNodeClassDType(fl, numericUnpack) {} ASTNODE_NODE_FUNCS(StructDType) virtual string verilogKwd() const { return "struct"; }; }; @@ -695,7 +700,7 @@ class AstUnionDType : public AstNodeClassDType { public: //UNSUP: bool isTagged; AstUnionDType(FileLine* fl, AstNumeric numericUnpack) - : AstNodeClassDType(fl,numericUnpack) {} + : AstNodeClassDType(fl, numericUnpack) {} ASTNODE_NODE_FUNCS(UnionDType) virtual string verilogKwd() const { return "union"; }; }; @@ -704,41 +709,42 @@ class AstMemberDType : public AstNodeDType { // A member of a struct/union // PARENT: AstNodeClassDType private: - AstNodeDType* m_refDTypep; // Elements of this type (after widthing) - string m_name; // Name of variable - string m_tag; // Holds the string of the verilator tag -- used in XML output. - int m_lsb; // Within this level's packed struct, the LSB of the first bit of the member - //UNSUP: int m_randType; // Randomization type (IEEE) + AstNodeDType* m_refDTypep; // Elements of this type (after widthing) + string m_name; // Name of variable + string m_tag; // Holds the string of the verilator tag -- used in XML output. + int m_lsb; // Within this level's packed struct, the LSB of the first bit of the member + //UNSUP: int m_randType; // Randomization type (IEEE) public: AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp) - : AstNodeDType(fl) - , m_name(name), m_lsb(-1) { - childDTypep(dtp); // Only for parser - dtypep(NULL); // V3Width will resolve - refDTypep(NULL); + : AstNodeDType(fl) + , m_name(name), m_lsb(-1) { + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve + refDTypep(NULL); } AstMemberDType(FileLine* fl, const string& name, AstNodeDType* dtp) - : AstNodeDType(fl) - , m_name(name), m_lsb(-1) { - UASSERT(dtp,"AstMember created with no dtype"); - refDTypep(dtp); - dtypep(this); - widthFromSub(subDTypep()); + : AstNodeDType(fl) + , m_name(name), m_lsb(-1) { + UASSERT(dtp, "AstMember created with no dtype"); + refDTypep(dtp); + dtypep(this); + widthFromSub(subDTypep()); } ASTNODE_NODE_FUNCS(MemberDType) - virtual string name() const { return m_name; } // * = Var name + virtual string name() const { return m_name; } // * = Var name virtual bool hasDType() const { return true; } virtual bool maybePointedTo() const { return true; } AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable - void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } virtual bool similarDType(AstNodeDType* samep) const { return this==samep; } // - // (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType) + // (Slow) recurse down to find basic data type (Note don't need virtual - + // AstVar isn't a NodeDType) virtual AstBasicDType* basicp() const { return subDTypep()->basicp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); } @@ -754,17 +760,17 @@ public: virtual void tag(const string& text) { m_tag = text;} virtual string tag() const { return m_tag; } int lsb() const { return m_lsb; } - void lsb(int lsb) { m_lsb=lsb; } + void lsb(int lsb) { m_lsb = lsb; } }; class AstEnumItem : public AstNode { private: - string m_name; + string m_name; public: // Parents: ENUM AstEnumItem(FileLine* fl, const string& name, AstNode* rangep, AstNode* initp) - : AstNode(fl), m_name(name) - { addNOp1p(rangep); addNOp2p(initp); } + : AstNode(fl), m_name(name) + { addNOp1p(rangep); addNOp2p(initp); } ASTNODE_NODE_FUNCS(EnumItem) virtual string name() const { return m_name; } virtual bool maybePointedTo() const { return true; } @@ -772,18 +778,18 @@ public: void name(const string& flag) { m_name = flag; } AstRange* rangep() const { return VN_CAST(op1p(), Range); } // op1 = Range for name appending void rangep(AstNode* nodep) { addOp1p(nodep); } - AstNode* valuep() const { return op2p(); } // op2 = Value + AstNode* valuep() const { return op2p(); } // op2 = Value void valuep(AstNode* nodep) { addOp2p(nodep); } }; class AstEnumItemRef : public AstNodeMath { private: - AstEnumItem* m_itemp; // [AfterLink] Pointer to item - AstPackage* m_packagep; // Package hierarchy + AstEnumItem* m_itemp; // [AfterLink] Pointer to item + AstPackage* m_packagep; // Package hierarchy public: AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstPackage* packagep) - : AstNodeMath(fl), m_itemp(itemp), m_packagep(packagep) { - dtypeFrom(m_itemp); + : AstNodeMath(fl), m_itemp(itemp), m_packagep(packagep) { + dtypeFrom(m_itemp); } ASTNODE_NODE_FUNCS(EnumItemRef) virtual void dump(std::ostream& str); @@ -792,14 +798,14 @@ public: virtual int instrCount() const { return 0; } virtual void cloneRelink() { if (m_itemp->clonep()) m_itemp = VN_CAST(m_itemp->clonep(), EnumItem); } virtual bool same(const AstNode* samep) const { - const AstEnumItemRef* sp = static_cast(samep); - return itemp() == sp->itemp(); } + const AstEnumItemRef* sp = static_cast(samep); + return itemp() == sp->itemp(); } AstEnumItem* itemp() const { return m_itemp; } virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return true; } AstPackage* packagep() const { return m_packagep; } - void packagep(AstPackage* nodep) { m_packagep=nodep; } + void packagep(AstPackage* nodep) { m_packagep = nodep; } }; class AstEnumDType : public AstNodeDType { @@ -807,33 +813,33 @@ class AstEnumDType : public AstNodeDType { // Children: ENUMVALUEs private: string m_name; // Name from upper typedef, if any - AstNodeDType* m_refDTypep; // Elements are of this type after V3Width - int m_uniqueNum; + AstNodeDType* m_refDTypep; // Elements are of this type after V3Width + int m_uniqueNum; public: AstEnumDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstNode* itemsp) - : AstNodeDType(fl) { - childDTypep(dtp); // Only for parser - refDTypep(NULL); - addNOp2p(itemsp); - dtypep(NULL); // V3Width will resolve - widthFromSub(subDTypep()); - m_uniqueNum = uniqueNumInc(); + : AstNodeDType(fl) { + childDTypep(dtp); // Only for parser + refDTypep(NULL); + addNOp2p(itemsp); + dtypep(NULL); // V3Width will resolve + widthFromSub(subDTypep()); + m_uniqueNum = uniqueNumInc(); } ASTNODE_NODE_FUNCS(EnumDType) virtual const char* broken() const { BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) - || (!m_refDTypep && childDTypep()))); return NULL; } + || (!m_refDTypep && childDTypep()))); return NULL; } virtual void cloneRelink() { if (m_refDTypep && m_refDTypep->clonep()) { - m_refDTypep = m_refDTypep->clonep(); + m_refDTypep = m_refDTypep->clonep(); }} virtual bool same(const AstNode* samep) const { - const AstEnumDType* sp = static_cast(samep); - return m_uniqueNum == sp->m_uniqueNum; } + const AstEnumDType* sp = static_cast(samep); + return m_uniqueNum == sp->m_uniqueNum; } virtual bool similarDType(AstNodeDType* samep) const { return this==samep; } virtual V3Hash sameHash() const { return V3Hash(m_uniqueNum); } AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Data type void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } - virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable + virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } // op1 = Range of variable void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } @@ -856,7 +862,7 @@ class AstParseTypeDType : public AstNodeDType { // e.g. the data type is a container of any data type public: explicit AstParseTypeDType(FileLine* fl) - : AstNodeDType(fl) {} + : AstNodeDType(fl) {} ASTNODE_NODE_FUNCS(ParseTypeDType) AstNodeDType* dtypep() const { return NULL; } // METHODS @@ -877,52 +883,56 @@ class AstArraySel : public AstNodeSel { private: void init(AstNode* fromp) { if (fromp && VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) { - // Strip off array to find what array references + // Strip off array to find what array references dtypeFrom(VN_CAST(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep()); - } + } } public: AstArraySel(FileLine* fl, AstNode* fromp, AstNode* bitp) - :AstNodeSel(fl, fromp, bitp) { - init(fromp); + :AstNodeSel(fl, fromp, bitp) { + init(fromp); } AstArraySel(FileLine* fl, AstNode* fromp, int bit) - :AstNodeSel(fl, fromp, new AstConst(fl,bit)) { - init(fromp); + :AstNodeSel(fl, fromp, new AstConst(fl, bit)) { + init(fromp); } ASTNODE_NODE_FUNCS(ArraySel) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstArraySel(this->fileline(), lhsp, rhsp); } virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { - V3ERROR_NA; /* How can from be a const? */ } + V3ERROR_NA; /* How can from be a const? */ } virtual string emitVerilog() { return "%k(%l%f[%r])"; } virtual string emitC() { return "%li%k[%ri]"; } virtual bool cleanOut() { return true; } - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual bool isGateOptimizable() const { return true; } // esp for V3Const::ifSameAssign virtual bool isPredictOptimizable() const { return false; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } virtual int instrCount() const { return widthInstrs(); } // Special operators - static AstNode* baseFromp(AstNode* nodep); ///< What is the base variable (or const) this dereferences? + static AstNode* baseFromp(AstNode* nodep); ///< What is the base variable (or const) this dereferences? }; class AstWordSel : public AstNodeSel { // Select a single word from a multi-word wide value public: AstWordSel(FileLine* fl, AstNode* fromp, AstNode* bitp) - :AstNodeSel(fl, fromp, bitp) { - dtypeSetUInt32(); // Always used on IData arrays so returns word entities + :AstNodeSel(fl, fromp, bitp) { + dtypeSetUInt32(); // Always used on IData arrays so returns word entities } ASTNODE_NODE_FUNCS(WordSel) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstWordSel(this->fileline(), lhsp, rhsp); } virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit) { V3ERROR_NA; } virtual string emitVerilog() { return "%k(%l%f[%r])"; } - virtual string emitC() { return "%li[%ri]"; } // Not %k, as usually it's a small constant rhsp + virtual string emitC() { return "%li[%ri]"; } // Not %k, as usually it's a small constant rhsp virtual bool cleanOut() { return true; } - virtual bool cleanLhs() { return true; } virtual bool cleanRhs() { return true; } - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -931,10 +941,10 @@ class AstSelExtract : public AstNodePreSel { // Range extraction, gets replaced with AstSel public: AstSelExtract(FileLine* fl, AstNode* fromp, AstNode* msbp, AstNode* lsbp) - : AstNodePreSel(fl, fromp, msbp, lsbp) {} + : AstNodePreSel(fl, fromp, msbp, lsbp) {} ASTNODE_NODE_FUNCS(SelExtract) - AstNode* msbp() const { return rhsp(); } - AstNode* lsbp() const { return thsp(); } + AstNode* msbp() const { return rhsp(); } + AstNode* lsbp() const { return thsp(); } }; class AstSelBit : public AstNodePreSel { @@ -942,11 +952,11 @@ class AstSelBit : public AstNodePreSel { // Gets replaced during link with AstArraySel or AstSel public: AstSelBit(FileLine* fl, AstNode* fromp, AstNode* bitp) - :AstNodePreSel(fl, fromp, bitp, NULL) { - if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); } + :AstNodePreSel(fl, fromp, bitp, NULL) { + if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); } } ASTNODE_NODE_FUNCS(SelBit) - AstNode* bitp() const { return rhsp(); } + AstNode* bitp() const { return rhsp(); } }; class AstSelPlus : public AstNodePreSel { @@ -956,8 +966,8 @@ public: AstSelPlus(FileLine* fl, AstNode* fromp, AstNode* bitp, AstNode* widthp) : AstNodePreSel(fl, fromp, bitp, widthp) {} ASTNODE_NODE_FUNCS(SelPlus) - AstNode* bitp() const { return rhsp(); } - AstNode* widthp() const { return thsp(); } + AstNode* bitp() const { return rhsp(); } + AstNode* widthp() const { return thsp(); } }; class AstSelMinus : public AstNodePreSel { @@ -967,8 +977,8 @@ public: AstSelMinus(FileLine* fl, AstNode* fromp, AstNode* bitp, AstNode* widthp) : AstNodePreSel(fl, fromp, bitp, widthp) {} ASTNODE_NODE_FUNCS(SelMinus) - AstNode* bitp() const { return rhsp(); } - AstNode* widthp() const { return thsp(); } + AstNode* bitp() const { return rhsp(); } + AstNode* widthp() const { return thsp(); } }; class AstSel : public AstNodeTriop { @@ -978,8 +988,8 @@ class AstSel : public AstNodeTriop { // Tempting to have an lvalue() style method here as LHS selects are quite // different, but that doesn't play well with V3Inst and bidirects which don't know direction private: - VNumRange m_declRange; // Range of the 'from' array if isRanged() is set, else invalid - int m_declElWidth; // If a packed array, the number of bits per element + VNumRange m_declRange; // Range of the 'from' array if isRanged() is set, else invalid + int m_declElWidth; // If a packed array, the number of bits per element public: AstSel(FileLine* fl, AstNode* fromp, AstNode* lsbp, AstNode* widthp) : AstNodeTriop(fl, fromp, lsbp, widthp) { @@ -987,35 +997,37 @@ public: if (VN_IS(widthp, Const)) { dtypeSetLogicSized(VN_CAST(widthp, Const)->toUInt(), VN_CAST(widthp, Const)->toUInt(), - AstNumeric::UNSIGNED); - } + AstNumeric::UNSIGNED); + } } AstSel(FileLine* fl, AstNode* fromp, int lsb, int bitwidth) : AstNodeTriop(fl, fromp, - new AstConst(fl,lsb), new AstConst(fl,bitwidth)) { + new AstConst(fl, lsb), new AstConst(fl, bitwidth)) { m_declElWidth = 1; - dtypeSetLogicSized(bitwidth,bitwidth,AstNumeric::UNSIGNED); + dtypeSetLogicSized(bitwidth, bitwidth, AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(Sel) virtual void dump(std::ostream& str); virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& bit, const V3Number& width) { - out.opSel(from, bit.toUInt()+width.toUInt()-1, bit.toUInt()); } + out.opSel(from, bit.toUInt()+width.toUInt()-1, bit.toUInt()); } virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially virtual string emitC() { - return this->widthp()->isOne() - ? "VL_BITSEL_%nq%lq%rq%tq(%nw,%lw,%rw,%tw, %P, %li, %ri)" - : "VL_SEL_%nq%lq%rq%tq(%nw,%lw,%rw,%tw, %P, %li, %ri, %ti)"; } + return this->widthp()->isOne() + ? "VL_BITSEL_%nq%lq%rq%tq(%nw,%lw,%rw,%tw, %P, %li, %ri)" + : "VL_SEL_%nq%lq%rq%tq(%nw,%lw,%rw,%tw, %P, %li, %ri, %ti)"; } virtual bool cleanOut() { return false; } - virtual bool cleanLhs() { return true;} virtual bool cleanRhs() {return true;} - virtual bool cleanThs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual bool sizeMattersThs() {return false;} + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool cleanThs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual bool sizeMattersThs() { return false; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode*) const { return true; } virtual int instrCount() const { return widthInstrs()*(VN_CAST(lsbp(), Const)?3:10); } - AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing) - AstNode* lsbp() const { return op2p(); } // op2 = Msb selection expression - AstNode* widthp() const { return op3p(); } // op3 = Width + AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing) + AstNode* lsbp() const { return op2p(); } // op2 = Msb selection expression + AstNode* widthp() const { return op3p(); } // op3 = Width int widthConst() const { return VN_CAST(widthp(), Const)->toSInt(); } int lsbConst() const { return VN_CAST(lsbp(), Const)->toSInt(); } int msbConst() const { return lsbConst()+widthConst()-1; } @@ -1067,40 +1079,41 @@ private: string m_name; public: AstMemberSel(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name) - : AstNodeMath(fl), m_name(name) { - setOp1p(fromp); - dtypep(NULL); // V3Width will resolve + : AstNodeMath(fl), m_name(name) { + setOp1p(fromp); + dtypep(NULL); // V3Width will resolve } AstMemberSel(FileLine* fl, AstNode* fromp, AstMemberDType* dtp) - : AstNodeMath(fl) { - setOp1p(fromp); - dtypep(dtp); - m_name = dtp->name(); + : AstNodeMath(fl) { + setOp1p(fromp); + dtypep(dtp); + m_name = dtp->name(); } ASTNODE_NODE_FUNCS(MemberSel) virtual string name() const { return m_name; } virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { - V3ERROR_NA; /* How can from be a const? */ } + V3ERROR_NA; /* How can from be a const? */ } virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return false; } - virtual bool same(const AstNode* samep) const { return true; } // dtype comparison does it all for us + virtual bool same(const AstNode* samep) const { return true; } // dtype comparison does it all for us virtual int instrCount() const { return widthInstrs(); } - AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing) + AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing) void fromp(AstNode* nodep) { setOp1p(nodep); } }; class AstMethodSel : public AstNode { // A reference to a member task (or function) - // We do not support generic member calls yet, so this is only enough to make built-in methods work + // We do not support generic member calls yet, so this is only enough to + // make built-in methods work private: - string m_name; // Name of variable + string m_name; // Name of variable public: AstMethodSel(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name, AstNode* pinsp) - : AstNode(fl), m_name(name) { - setOp1p(fromp); - dtypep(NULL); // V3Width will resolve - addNOp2p(pinsp); + : AstNode(fl), m_name(name) { + setOp1p(fromp); + dtypep(NULL); // V3Width will resolve + addNOp2p(pinsp); } AstMethodSel(FileLine* fl, AstNode* fromp, const string& name, AstNode* pinsp) : AstNode(fl), m_name(name) { @@ -1108,75 +1121,78 @@ public: addNOp2p(pinsp); } ASTNODE_NODE_FUNCS(MethodSel) - virtual string name() const { return m_name; } // * = Var name + virtual string name() const { return m_name; } // * = Var name virtual void name(const string& name) { m_name = name; } - AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing) + AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing) void fromp(AstNode* nodep) { setOp1p(nodep); } - AstNode* pinsp() const { return op2p(); } // op2 = Pin interconnection list + AstNode* pinsp() const { return op2p(); } // op2 = Pin interconnection list void addPinsp(AstNode* nodep) { addOp2p(nodep); } }; class AstVar : public AstNode { // A variable (in/out/wire/reg/param) inside a module private: - string m_name; // Name of variable - string m_origName; // Original name before dot addition + string m_name; // Name of variable + string m_origName; // Original name before dot addition string m_tag; // Holds the string of the verilator tag -- used in XML output. - AstVarType m_varType; // Type of variable + AstVarType m_varType; // Type of variable VDirection m_direction; // Direction input/output etc VDirection m_declDirection; // Declared direction input/output etc AstBasicDTypeKwd m_declKwd; // Keyword at declaration time - bool m_tristate:1; // Inout or triwire or trireg - bool m_primaryIO:1; // In/out to top level (or directly assigned from same) - bool m_sc:1; // SystemC variable - bool m_scClocked:1; // SystemC sc_clk<> needed - bool m_scSensitive:1;// SystemC sensitive() needed - bool m_sigPublic:1; // User C code accesses this signal or is top signal - bool m_sigModPublic:1;// User C code accesses this signal and module - bool m_sigUserRdPublic:1; // User C code accesses this signal, read only - bool m_sigUserRWPublic:1; // User C code accesses this signal, read-write - bool m_usedClock:1; // Signal used as a clock - bool m_usedParam:1; // Parameter is referenced (on link; later signals not setup) - bool m_usedLoopIdx:1; // Variable subject of for unrolling - bool m_funcLocal:1; // Local variable for a function - bool m_funcReturn:1; // Return variable for a function - bool m_attrClockEn:1;// User clock enable attribute - bool m_attrScBv:1; // User force bit vector attribute - bool m_attrIsolateAssign:1;// User isolate_assignments attribute - bool m_attrSFormat:1;// User sformat attribute - bool m_fileDescr:1; // File descriptor - bool m_isConst:1; // Table contains constant data - bool m_isStatic:1; // Static variable - bool m_isPulldown:1; // Tri0 - bool m_isPullup:1; // Tri1 - bool m_isIfaceParent:1; // dtype is reference to interface present in this module + bool m_tristate:1; // Inout or triwire or trireg + bool m_primaryIO:1; // In/out to top level (or directly assigned from same) + bool m_sc:1; // SystemC variable + bool m_scClocked:1; // SystemC sc_clk<> needed + bool m_scSensitive:1;// SystemC sensitive() needed + bool m_sigPublic:1; // User C code accesses this signal or is top signal + bool m_sigModPublic:1;// User C code accesses this signal and module + bool m_sigUserRdPublic:1; // User C code accesses this signal, read only + bool m_sigUserRWPublic:1; // User C code accesses this signal, read-write + bool m_usedClock:1; // Signal used as a clock + bool m_usedParam:1; // Parameter is referenced (on link; later signals not setup) + bool m_usedLoopIdx:1; // Variable subject of for unrolling + bool m_funcLocal:1; // Local variable for a function + bool m_funcReturn:1; // Return variable for a function + bool m_attrClockEn:1;// User clock enable attribute + bool m_attrScBv:1; // User force bit vector attribute + bool m_attrIsolateAssign:1;// User isolate_assignments attribute + bool m_attrSFormat:1;// User sformat attribute + bool m_fileDescr:1; // File descriptor + bool m_isConst:1; // Table contains constant data + bool m_isStatic:1; // Static variable + bool m_isPulldown:1; // Tri0 + bool m_isPullup:1; // Tri1 + bool m_isIfaceParent:1; // dtype is reference to interface present in this module bool m_isDpiOpenArray:1; // DPI import open array bool m_noReset:1; // Do not do automated reset/randomization - bool m_noSubst:1; // Do not substitute out references - bool m_trace:1; // Trace this variable + bool m_noSubst:1; // Do not substitute out references + bool m_trace:1; // Trace this variable AstVarAttrClocker m_attrClocker; MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var void init() { - m_tristate=false; m_primaryIO=false; - m_sc=false; m_scClocked=false; m_scSensitive=false; - m_usedClock=false; m_usedParam=false; m_usedLoopIdx=false; - m_sigPublic=false; m_sigModPublic=false; m_sigUserRdPublic=false; m_sigUserRWPublic=false; - m_funcLocal=false; m_funcReturn=false; - m_attrClockEn=false; m_attrScBv=false; m_attrIsolateAssign=false; m_attrSFormat=false; - m_fileDescr=false; m_isConst=false; m_isStatic=false; m_isPulldown=false; m_isPullup=false; - m_isIfaceParent=false; m_isDpiOpenArray=false; - m_noReset=false; m_noSubst=false; m_trace=false; - m_attrClocker=AstVarAttrClocker::CLOCKER_UNKNOWN; + m_tristate = false; m_primaryIO = false; + m_sc = false; m_scClocked = false; m_scSensitive = false; + m_usedClock = false; m_usedParam = false; m_usedLoopIdx = false; + m_sigPublic = false; m_sigModPublic = false; + m_sigUserRdPublic = false; m_sigUserRWPublic = false; + m_funcLocal = false; m_funcReturn = false; + m_attrClockEn = false; m_attrScBv = false; + m_attrIsolateAssign = false; m_attrSFormat = false; + m_fileDescr = false; m_isConst = false; + m_isStatic = false; m_isPulldown = false; m_isPullup = false; + m_isIfaceParent = false; m_isDpiOpenArray = false; + m_noReset = false; m_noSubst = false; m_trace = false; + m_attrClocker = AstVarAttrClocker::CLOCKER_UNKNOWN; } public: AstVar(FileLine* fl, AstVarType type, const string& name, VFlagChildDType, AstNodeDType* dtp) : AstNode(fl) , m_name(name), m_origName(name) { init(); - combineType(type); - childDTypep(dtp); // Only for parser - dtypep(NULL); // V3Width will resolve + combineType(type); + childDTypep(dtp); // Only for parser + dtypep(NULL); // V3Width will resolve if (dtp->basicp()) m_declKwd = dtp->basicp()->keyword(); else m_declKwd = AstBasicDTypeKwd::LOGIC; } @@ -1184,9 +1200,9 @@ public: : AstNode(fl) , m_name(name), m_origName(name) { init(); - combineType(type); - UASSERT(dtp,"AstVar created with no dtype"); - dtypep(dtp); + combineType(type); + UASSERT(dtp, "AstVar created with no dtype"); + dtypep(dtp); if (dtp->basicp()) m_declKwd = dtp->basicp()->keyword(); else m_declKwd = AstBasicDTypeKwd::LOGIC; } @@ -1195,7 +1211,7 @@ public: , m_name(name), m_origName(name) { init(); combineType(type); - dtypeSetLogicSized(wantwidth,wantwidth,AstNumeric::UNSIGNED); + dtypeSetLogicSized(wantwidth, wantwidth, AstNumeric::UNSIGNED); m_declKwd = AstBasicDTypeKwd::LOGIC; } AstVar(FileLine* fl, AstVarType type, const string& name, VFlagBitPacked, int wantwidth) @@ -1203,26 +1219,26 @@ public: , m_name(name), m_origName(name) { init(); combineType(type); - dtypeSetLogicSized(wantwidth,wantwidth,AstNumeric::UNSIGNED); + dtypeSetLogicSized(wantwidth, wantwidth, AstNumeric::UNSIGNED); m_declKwd = AstBasicDTypeKwd::BIT; } AstVar(FileLine* fl, AstVarType type, const string& name, AstVar* examplep) : AstNode(fl) , m_name(name), m_origName(name) { - init(); - combineType(type); - if (examplep->childDTypep()) { - childDTypep(examplep->childDTypep()->cloneTree(true)); - } - dtypeFrom(examplep); + init(); + combineType(type); + if (examplep->childDTypep()) { + childDTypep(examplep->childDTypep()->cloneTree(true)); + } + dtypeFrom(examplep); m_declKwd = examplep->declKwd(); } ASTNODE_NODE_FUNCS(Var) virtual void dump(std::ostream& str); - virtual string name() const { return m_name; } // * = Var name + virtual string name() const { return m_name; } // * = Var name virtual bool hasDType() const { return true; } virtual bool maybePointedTo() const { return true; } - string origName() const { return m_origName; } // * = Original name + string origName() const { return m_origName; } // * = Original name void origName(const string& name) { m_origName = name; } AstVarType varType() const { return m_varType; } // * = Type of variable void direction(const VDirection& flag) { @@ -1233,58 +1249,59 @@ public: void declDirection(const VDirection& flag) { m_declDirection = flag; } VDirection declDirection() const { return m_declDirection; } void varType(AstVarType type) { m_varType = type; } - void varType2Out() { m_tristate=0; m_direction=VDirection::OUTPUT; } - void varType2In() { m_tristate=0; m_direction=VDirection::INPUT; } + void varType2Out() { m_tristate = 0; m_direction = VDirection::OUTPUT; } + void varType2In() { m_tristate = 0; m_direction = VDirection::INPUT; } AstBasicDTypeKwd declKwd() const { return m_declKwd; } - string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv - string cPubArgType(bool named, bool forReturn) const; // Return C /*public*/ type for argument: bool, uint32_t, uint64_t, etc. - string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument + string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv + string cPubArgType(bool named, bool forReturn) const; // Return C /*public*/ type for argument: bool, uint32_t, uint64_t, etc. + string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument // Return Verilator internal type for argument: CData, SData, IData, WData string vlArgType(bool named, bool forReturn, bool forFunc) const; - string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc - string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc + string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc + string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc string vlPropInit() const; // Return VerilatorVarProps initializer - void combineType(AstVarType type); + void combineType(AstVarType type); AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable AstNodeDType* dtypeSkipRefp() const { return subDTypep()->skipRefp(); } - // (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType) + // (Slow) recurse down to find basic data type (Note don't need virtual - + // AstVar isn't a NodeDType) AstBasicDType* basicp() const { return subDTypep()->basicp(); } - AstNode* valuep() const { return op3p(); } // op3 = Initial value that never changes (static const) - void valuep(AstNode* nodep) { setOp3p(nodep); } // It's valuep, not constp, as may be more complicated than an AstConst - void addAttrsp(AstNode* nodep) { addNOp4p(nodep); } - AstNode* attrsp() const { return op4p(); } // op4 = Attributes during early parse - void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + AstNode* valuep() const { return op3p(); } // op3 = Initial value that never changes (static const) + void valuep(AstNode* nodep) { setOp3p(nodep); } // It's valuep, not constp, as may be more complicated than an AstConst + void addAttrsp(AstNode* nodep) { addNOp4p(nodep); } + AstNode* attrsp() const { return op4p(); } // op4 = Attributes during early parse + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } - void attrClockEn(bool flag) { m_attrClockEn = flag; } - void attrClocker(AstVarAttrClocker flag) { m_attrClocker = flag; } - void attrFileDescr(bool flag) { m_fileDescr = flag; } - void attrScClocked(bool flag) { m_scClocked = flag; } - void attrScBv(bool flag) { m_attrScBv = flag; } - void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } - void attrSFormat(bool flag) { m_attrSFormat = flag; } - void usedClock(bool flag) { m_usedClock = flag; } - void usedParam(bool flag) { m_usedParam = flag; } - void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; } - void sigPublic(bool flag) { m_sigPublic = flag; } - void sigModPublic(bool flag) { m_sigModPublic = flag; } - void sigUserRdPublic(bool flag) { m_sigUserRdPublic = flag; if (flag) sigPublic(true); } - void sigUserRWPublic(bool flag) { m_sigUserRWPublic = flag; if (flag) sigUserRdPublic(true); } - void sc(bool flag) { m_sc = flag; } - void scSensitive(bool flag) { m_scSensitive = flag; } - void primaryIO(bool flag) { m_primaryIO = flag; } - void isConst(bool flag) { m_isConst = flag; } - void isStatic(bool flag) { m_isStatic = flag; } - void isIfaceParent(bool flag) { m_isIfaceParent = flag; } - void funcLocal(bool flag) { m_funcLocal = flag; } - void funcReturn(bool flag) { m_funcReturn = flag; } - void isDpiOpenArray(bool flag) { m_isDpiOpenArray=flag; } + void attrClockEn(bool flag) { m_attrClockEn = flag; } + void attrClocker(AstVarAttrClocker flag) { m_attrClocker = flag; } + void attrFileDescr(bool flag) { m_fileDescr = flag; } + void attrScClocked(bool flag) { m_scClocked = flag; } + void attrScBv(bool flag) { m_attrScBv = flag; } + void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } + void attrSFormat(bool flag) { m_attrSFormat = flag; } + void usedClock(bool flag) { m_usedClock = flag; } + void usedParam(bool flag) { m_usedParam = flag; } + void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; } + void sigPublic(bool flag) { m_sigPublic = flag; } + void sigModPublic(bool flag) { m_sigModPublic = flag; } + void sigUserRdPublic(bool flag) { m_sigUserRdPublic = flag; if (flag) sigPublic(true); } + void sigUserRWPublic(bool flag) { m_sigUserRWPublic = flag; if (flag) sigUserRdPublic(true); } + void sc(bool flag) { m_sc = flag; } + void scSensitive(bool flag) { m_scSensitive = flag; } + void primaryIO(bool flag) { m_primaryIO = flag; } + void isConst(bool flag) { m_isConst = flag; } + void isStatic(bool flag) { m_isStatic = flag; } + void isIfaceParent(bool flag) { m_isIfaceParent = flag; } + void funcLocal(bool flag) { m_funcLocal = flag; } + void funcReturn(bool flag) { m_funcReturn = flag; } + void isDpiOpenArray(bool flag) { m_isDpiOpenArray = flag; } bool isDpiOpenArray() const { return m_isDpiOpenArray; } - void noReset(bool flag) { m_noReset=flag; } + void noReset(bool flag) { m_noReset = flag; } bool noReset() const { return m_noReset; } - void noSubst(bool flag) { m_noSubst=flag; } + void noSubst(bool flag) { m_noSubst = flag; } bool noSubst() const { return m_noSubst; } - void trace(bool flag) { m_trace=flag; } + void trace(bool flag) { m_trace = flag; } // METHODS virtual void name(const string& name) { m_name = name; } virtual void tag(const string& text) { m_tag = text;} @@ -1296,73 +1313,75 @@ public: bool isTristate() const { return m_tristate; } bool isPrimaryIO() const { return m_primaryIO; } bool isPrimaryInish() const { return isPrimaryIO() && isNonOutput(); } - bool isIfaceRef() const { return (varType()==AstVarType::IFACEREF); } - bool isIfaceParent() const { return m_isIfaceParent; } - bool isSignal() const { return varType().isSignal(); } - bool isTemp() const { return (varType()==AstVarType::BLOCKTEMP || varType()==AstVarType::MODULETEMP - || varType()==AstVarType::STMTTEMP || varType()==AstVarType::XTEMP); } - bool isToggleCoverable() const { return ((isIO() || isSignal()) - && (isIO() || isBitLogic()) - // Wrapper would otherwise duplicate wrapped module's coverage - && !isSc() && !isPrimaryIO() && !isConst()); } - bool isStatementTemp() const { return (varType()==AstVarType::STMTTEMP); } - bool isMovableToBlock() const { return (varType()==AstVarType::BLOCKTEMP || isFuncLocal()); } - bool isXTemp() const { return (varType()==AstVarType::XTEMP); } - bool isParam() const { return (varType()==AstVarType::LPARAM || varType()==AstVarType::GPARAM); } - bool isGParam() const { return (varType()==AstVarType::GPARAM); } - bool isGenVar() const { return (varType()==AstVarType::GENVAR); } - bool isBitLogic() const { AstBasicDType* bdtypep = basicp(); return bdtypep && bdtypep->isBitLogic(); } - bool isUsedClock() const { return m_usedClock; } - bool isUsedParam() const { return m_usedParam; } - bool isUsedLoopIdx() const { return m_usedLoopIdx; } - bool isSc() const { return m_sc; } - bool isScQuad() const; - bool isScBv() const; - bool isScUint() const; - bool isScBigUint() const; - bool isScSensitive() const { return m_scSensitive; } - bool isSigPublic() const; - bool isSigModPublic() const { return m_sigModPublic; } - bool isSigUserRdPublic() const { return m_sigUserRdPublic; } - bool isSigUserRWPublic() const { return m_sigUserRWPublic; } - bool isTrace() const { return m_trace; } - bool isConst() const { return m_isConst; } - bool isStatic() const { return m_isStatic; } - bool isFuncLocal() const { return m_funcLocal; } - bool isFuncReturn() const { return m_funcReturn; } - bool isPullup() const { return m_isPullup; } - bool isPulldown() const { return m_isPulldown; } - bool attrClockEn() const { return m_attrClockEn; } - bool attrScBv() const { return m_attrScBv; } - bool attrFileDescr() const { return m_fileDescr; } - bool attrScClocked() const { return m_scClocked; } - bool attrSFormat() const { return m_attrSFormat; } - bool attrIsolateAssign() const { return m_attrIsolateAssign; } + bool isIfaceRef() const { return (varType()==AstVarType::IFACEREF); } + bool isIfaceParent() const { return m_isIfaceParent; } + bool isSignal() const { return varType().isSignal(); } + bool isTemp() const { + return (varType()==AstVarType::BLOCKTEMP || varType()==AstVarType::MODULETEMP + || varType()==AstVarType::STMTTEMP || varType()==AstVarType::XTEMP); } + bool isToggleCoverable() const { + return ((isIO() || isSignal()) + && (isIO() || isBitLogic()) + // Wrapper would otherwise duplicate wrapped module's coverage + && !isSc() && !isPrimaryIO() && !isConst()); } + bool isStatementTemp() const { return (varType()==AstVarType::STMTTEMP); } + bool isMovableToBlock() const { return (varType()==AstVarType::BLOCKTEMP || isFuncLocal()); } + bool isXTemp() const { return (varType()==AstVarType::XTEMP); } + bool isParam() const { return (varType()==AstVarType::LPARAM || varType()==AstVarType::GPARAM); } + bool isGParam() const { return (varType()==AstVarType::GPARAM); } + bool isGenVar() const { return (varType()==AstVarType::GENVAR); } + bool isBitLogic() const { AstBasicDType* bdtypep = basicp(); return bdtypep && bdtypep->isBitLogic(); } + bool isUsedClock() const { return m_usedClock; } + bool isUsedParam() const { return m_usedParam; } + bool isUsedLoopIdx() const { return m_usedLoopIdx; } + bool isSc() const { return m_sc; } + bool isScQuad() const; + bool isScBv() const; + bool isScUint() const; + bool isScBigUint() const; + bool isScSensitive() const { return m_scSensitive; } + bool isSigPublic() const; + bool isSigModPublic() const { return m_sigModPublic; } + bool isSigUserRdPublic() const { return m_sigUserRdPublic; } + bool isSigUserRWPublic() const { return m_sigUserRWPublic; } + bool isTrace() const { return m_trace; } + bool isConst() const { return m_isConst; } + bool isStatic() const { return m_isStatic; } + bool isFuncLocal() const { return m_funcLocal; } + bool isFuncReturn() const { return m_funcReturn; } + bool isPullup() const { return m_isPullup; } + bool isPulldown() const { return m_isPulldown; } + bool attrClockEn() const { return m_attrClockEn; } + bool attrScBv() const { return m_attrScBv; } + bool attrFileDescr() const { return m_fileDescr; } + bool attrScClocked() const { return m_scClocked; } + bool attrSFormat() const { return m_attrSFormat; } + bool attrIsolateAssign() const { return m_attrIsolateAssign; } AstVarAttrClocker attrClocker() const { return m_attrClocker; } virtual string verilogKwd() const; - void propagateAttrFrom(AstVar* fromp) { - // This is getting connected to fromp; keep attributes - // Note the method below too - if (fromp->attrClockEn()) attrClockEn(true); - if (fromp->attrFileDescr()) attrFileDescr(true); - if (fromp->attrIsolateAssign()) attrIsolateAssign(true); + void propagateAttrFrom(AstVar* fromp) { + // This is getting connected to fromp; keep attributes + // Note the method below too + if (fromp->attrClockEn()) attrClockEn(true); + if (fromp->attrFileDescr()) attrFileDescr(true); + if (fromp->attrIsolateAssign()) attrIsolateAssign(true); } - bool gateMultiInputOptimizable() const { - // Ok to gate optimize; must return false if propagateAttrFrom would do anything - return (!attrClockEn() && !isUsedClock()); + bool gateMultiInputOptimizable() const { + // Ok to gate optimize; must return false if propagateAttrFrom would do anything + return (!attrClockEn() && !isUsedClock()); } void combineType(AstVar* typevarp) { // This is same as typevarp (for combining input & reg decls) // "this" is the input var. typevarp is the reg var. - propagateAttrFrom(typevarp); - combineType(typevarp->varType()); - if (typevarp->isSigPublic()) sigPublic(true); - if (typevarp->isSigModPublic()) sigModPublic(true); - if (typevarp->isSigUserRdPublic()) sigUserRdPublic(true); - if (typevarp->isSigUserRWPublic()) sigUserRWPublic(true); - if (typevarp->attrScClocked()) attrScClocked(true); + propagateAttrFrom(typevarp); + combineType(typevarp->varType()); + if (typevarp->isSigPublic()) sigPublic(true); + if (typevarp->isSigModPublic()) sigModPublic(true); + if (typevarp->isSigUserRdPublic()) sigUserRdPublic(true); + if (typevarp->isSigUserRWPublic()) sigUserRWPublic(true); + if (typevarp->attrScClocked()) attrScClocked(true); } - void inlineAttrReset(const string& name) { + void inlineAttrReset(const string& name) { if (direction()==VDirection::INOUT && varType()==AstVarType::WIRE) { m_varType = AstVarType::TRIWIRE; } @@ -1381,22 +1400,22 @@ class AstDefParam : public AstNode { // Parents: MODULE // Children: math private: - string m_name; // Name of variable getting set - string m_path; // Dotted cellname to set parameter of + string m_name; // Name of variable getting set + string m_path; // Dotted cellname to set parameter of public: AstDefParam(FileLine* fl, const string& path, const string& name, AstNode* rhsp) - : AstNode(fl) { - setOp1p(rhsp); - m_name = name; - m_path = path; + : AstNode(fl) { + setOp1p(rhsp); + m_name = name; + m_path = path; } - virtual string name() const { return m_name; } // * = Scope name + virtual string name() const { return m_name; } // * = Scope name ASTNODE_NODE_FUNCS(DefParam) virtual bool cleanRhs() { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode*) const { return true; } - AstNode* rhsp() const { return op1p(); } // op1 = Assign from - string path() const { return m_path; } + AstNode* rhsp() const { return op1p(); } // op1 = Assign from + string path() const { return m_path; } }; class AstImplicit : public AstNode { @@ -1404,11 +1423,11 @@ class AstImplicit : public AstNode { // Parents: MODULE public: AstImplicit(FileLine* fl, AstNode* exprsp) - : AstNode(fl) { - addNOp1p(exprsp); + : AstNode(fl) { + addNOp1p(exprsp); } ASTNODE_NODE_FUNCS(Implicit) - AstNode* exprsp() const { return op1p(); } // op1 = Assign from + AstNode* exprsp() const { return op1p(); } // op1 = Assign from }; class AstScope : public AstNode { @@ -1417,30 +1436,30 @@ class AstScope : public AstNode { // Children: NODEBLOCK private: // An AstScope->name() is special: . indicates an uninlined scope, __DOT__ an inlined scope - string m_name; // Name - AstScope* m_aboveScopep; // Scope above this one in the hierarchy (NULL if top) - AstCell* m_aboveCellp; // Cell above this in the hierarchy (NULL if top) - AstNodeModule* m_modp; // Module scope corresponds to + string m_name; // Name + AstScope* m_aboveScopep; // Scope above this one in the hierarchy (NULL if top) + AstCell* m_aboveCellp; // Cell above this in the hierarchy (NULL if top) + AstNodeModule* m_modp; // Module scope corresponds to public: AstScope(FileLine* fl, AstNodeModule* modp, const string& name, AstScope* aboveScopep, AstCell* aboveCellp) : AstNode(fl) - ,m_name(name) ,m_aboveScopep(aboveScopep) ,m_aboveCellp(aboveCellp), m_modp(modp) {} + , m_name(name), m_aboveScopep(aboveScopep), m_aboveCellp(aboveCellp), m_modp(modp) {} ASTNODE_NODE_FUNCS(Scope) virtual void cloneRelink(); virtual const char* broken() const; virtual bool maybePointedTo() const { return true; } - virtual string name() const { return m_name; } // * = Scope name + virtual string name() const { return m_name; } // * = Scope name virtual void name(const string& name) { m_name = name; } string nameDotless() const; string nameVlSym() const { return ((string("vlSymsp->")) + nameDotless()); } - AstNodeModule* modp() const { return m_modp; } + AstNodeModule* modp() const { return m_modp; } void addVarp(AstNode* nodep) { addOp1p(nodep); } - AstNode* varsp() const { return op1p(); } // op1 = AstVarScope's + AstNode* varsp() const { return op1p(); } // op1 = AstVarScope's void addActivep(AstNode* nodep) { addOp2p(nodep); } - AstNode* blocksp() const { return op2p(); } // op2 = Block names + AstNode* blocksp() const { return op2p(); } // op2 = Block names void addFinalClkp(AstNode* nodep) { addOp3p(nodep); } - AstNode* finalClksp() const { return op3p(); } // op3 = Final assigns for clock correction + AstNode* finalClksp() const { return op3p(); } // op3 = Final assigns for clock correction AstScope* aboveScopep() const { return m_aboveScopep; } AstCell* aboveCellp() const { return m_aboveCellp; } bool isTop() const { return aboveScopep()==NULL; } // At top of hierarchy @@ -1456,49 +1475,50 @@ public: : AstNode(fl) {addNOp2p(ascopep);} ASTNODE_NODE_FUNCS(TopScope) - AstNode* stmtsp() const { return op1p(); } + AstNode* stmtsp() const { return op1p(); } void addStmtsp(AstNode* nodep) { addOp1p(nodep); } AstScope* scopep() const { return VN_CAST(op2p(), Scope); } // op1 = AstVarScope's }; class AstVarScope : public AstNode { // A particular scoped usage of a variable - // That is, as a module is used under multiple cells, we get a different varscope for each var in the module + // That is, as a module is used under multiple cells, we get a different + // varscope for each var in the module // Parents: MODULE // Children: none private: - AstScope* m_scopep; // Scope variable is underneath - AstVar* m_varp; // [AfterLink] Pointer to variable itself - bool m_circular:1; // Used in circular ordering dependency, need change detect - bool m_trace:1; // Tracing is turned on for this scope + AstScope* m_scopep; // Scope variable is underneath + AstVar* m_varp; // [AfterLink] Pointer to variable itself + bool m_circular:1; // Used in circular ordering dependency, need change detect + bool m_trace:1; // Tracing is turned on for this scope public: AstVarScope(FileLine* fl, AstScope* scopep, AstVar* varp) : AstNode(fl) , m_scopep(scopep), m_varp(varp) { m_circular = false; - m_trace = true; - dtypeFrom(varp); + m_trace = true; + dtypeFrom(varp); } ASTNODE_NODE_FUNCS(VarScope) virtual void cloneRelink() { if (m_varp && m_varp->clonep()) { - m_varp = m_varp->clonep(); - UASSERT(m_scopep->clonep(), "No clone cross link: "<clonep(); + m_varp = m_varp->clonep(); + UASSERT(m_scopep->clonep(), "No clone cross link: "<clonep(); }} virtual const char* broken() const { BROKEN_RTN(m_varp && !m_varp->brokeExists()); - BROKEN_RTN(m_scopep && !m_scopep->brokeExists()); return NULL; } + BROKEN_RTN(m_scopep && !m_scopep->brokeExists()); return NULL; } virtual bool maybePointedTo() const { return true; } - virtual string name() const {return scopep()->name()+"->"+varp()->name();} // * = Var name + virtual string name() const {return scopep()->name()+"->"+varp()->name();} // * = Var name virtual void dump(std::ostream& str); virtual bool hasDType() const { return true; } - AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable - AstScope* scopep() const { return m_scopep; } // Pointer to scope it's under - AstNode* valuep() const { return op1p(); } // op1 = Calculation of value of variable, NULL=complicated - void valuep(AstNode* valuep) { addOp1p(valuep); } - bool isCircular() const { return m_circular; } - void circular(bool flag) { m_circular = flag; } - bool isTrace() const { return m_trace; } - void trace(bool flag) { m_trace = flag; } + AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable + AstScope* scopep() const { return m_scopep; } // Pointer to scope it's under + AstNode* valuep() const { return op1p(); } // op1 = Calculation of value of variable, NULL=complicated + void valuep(AstNode* valuep) { addOp1p(valuep); } + bool isCircular() const { return m_circular; } + void circular(bool flag) { m_circular = flag; } + bool isTrace() const { return m_trace; } + void trace(bool flag) { m_trace = flag; } }; class AstVarRef : public AstNodeVarRef { @@ -1513,23 +1533,23 @@ public: // This form only allowed post-link (see above) AstVarRef(FileLine* fl, AstVarScope* varscp, bool lvalue) : AstNodeVarRef(fl, varscp->varp()->name(), varscp->varp(), lvalue) { - varScopep(varscp); + varScopep(varscp); } ASTNODE_NODE_FUNCS(VarRef) virtual void dump(std::ostream& str); - virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()->name()),V3Hash(hiername())); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()->name()), V3Hash(hiername())); } virtual bool same(const AstNode* samep) const { - return same(static_cast(samep)); } + return same(static_cast(samep)); } inline bool same(const AstVarRef* samep) const { - if (varScopep()) return (varScopep()==samep->varScopep() - && lvalue()==samep->lvalue()); - else return (hiername()==samep->hiername() - && varp()->name()==samep->varp()->name() - && lvalue()==samep->lvalue()); } + if (varScopep()) return (varScopep()==samep->varScopep() + && lvalue()==samep->lvalue()); + else return (hiername()==samep->hiername() + && varp()->name()==samep->varp()->name() + && lvalue()==samep->lvalue()); } inline bool sameNoLvalue(AstVarRef* samep) const { - if (varScopep()) return (varScopep()==samep->varScopep()); - else return (hiername()==samep->hiername() - && varp()->name()==samep->varp()->name()); } + if (varScopep()) return (varScopep()==samep->varScopep()); + else return (hiername()==samep->hiername() + && varp()->name()==samep->varp()->name()); } virtual int instrCount() const { return widthInstrs()*(lvalue()?1:instrCountLd()); } virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially virtual string emitC() { V3ERROR_NA; return ""; } @@ -1541,49 +1561,49 @@ class AstVarXRef : public AstNodeVarRef { // Includes pin on a cell, as part of a ASSIGN statement to connect I/Os until AstScope private: string m_dotted; // Dotted part of scope the name()'ed reference is under or "" - string m_inlinedDots; // Dotted hierarchy flattened out + string m_inlinedDots; // Dotted hierarchy flattened out public: AstVarXRef(FileLine* fl, const string& name, const string& dotted, bool lvalue) : AstNodeVarRef(fl, name, NULL, lvalue) , m_dotted(dotted) { } AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, bool lvalue) : AstNodeVarRef(fl, varp->name(), varp, lvalue) - , m_dotted(dotted) { - dtypeFrom(varp); + , m_dotted(dotted) { + dtypeFrom(varp); } ASTNODE_NODE_FUNCS(VarXRef) virtual void dump(std::ostream& str); - string dotted() const { return m_dotted; } - void dotted(const string& dotted) { m_dotted = dotted; } - string prettyDotted() const { return prettyName(dotted()); } - string inlinedDots() const { return m_inlinedDots; } - void inlinedDots(const string& flag) { m_inlinedDots = flag; } + string dotted() const { return m_dotted; } + void dotted(const string& dotted) { m_dotted = dotted; } + string prettyDotted() const { return prettyName(dotted()); } + string inlinedDots() const { return m_inlinedDots; } + void inlinedDots(const string& flag) { m_inlinedDots = flag; } virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return true; } virtual int instrCount() const { return widthInstrs(); } - virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()),V3Hash(dotted())); } + virtual V3Hash sameHash() const { return V3Hash(V3Hash(varp()), V3Hash(dotted())); } virtual bool same(const AstNode* samep) const { - const AstVarXRef* asamep = static_cast(samep); - return (hiername() == asamep->hiername() - && varp() == asamep->varp() - && name() == asamep->name() - && dotted() == asamep->dotted()); } + const AstVarXRef* asamep = static_cast(samep); + return (hiername() == asamep->hiername() + && varp() == asamep->varp() + && name() == asamep->name() + && dotted() == asamep->dotted()); } }; class AstPin : public AstNode { // A pin on a cell private: - int m_pinNum; // Pin number - string m_name; // Pin name, or "" for number based interconnect - AstVar* m_modVarp; // Input/output this pin connects to on submodule. - AstParamTypeDType* m_modPTypep; // Param type this pin connects to on submodule. - bool m_param; // Pin connects to parameter - bool m_svImplicit; // Pin is SystemVerilog .name'ed + int m_pinNum; // Pin number + string m_name; // Pin name, or "" for number based interconnect + AstVar* m_modVarp; // Input/output this pin connects to on submodule. + AstParamTypeDType* m_modPTypep; // Param type this pin connects to on submodule. + bool m_param; // Pin connects to parameter + bool m_svImplicit; // Pin is SystemVerilog .name'ed public: AstPin(FileLine* fl, int pinNum, const string& name, AstNode* exprp) : AstNode(fl) - ,m_name(name), m_param(false), m_svImplicit(false) { + , m_name(name), m_param(false), m_svImplicit(false) { m_pinNum = pinNum; m_modVarp = NULL; m_modPTypep = NULL; @@ -1592,18 +1612,18 @@ public: AstPin(FileLine* fl, int pinNum, AstVarRef* varname, AstNode* exprp) : AstNode(fl), m_param(false), m_svImplicit(false) { m_name = varname->name(); - m_pinNum = pinNum; - m_modVarp = NULL; - m_modPTypep = NULL; - setNOp1p(exprp); + m_pinNum = pinNum; + m_modVarp = NULL; + m_modPTypep = NULL; + setNOp1p(exprp); } ASTNODE_NODE_FUNCS(Pin) virtual void dump(std::ostream& str); virtual const char* broken() const { - BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists()); - BROKEN_RTN(m_modPTypep && !m_modPTypep->brokeExists()); - return NULL; } - virtual string name() const { return m_name; } // * = Pin name, ""=go by number + BROKEN_RTN(m_modVarp && !m_modVarp->brokeExists()); + BROKEN_RTN(m_modPTypep && !m_modPTypep->brokeExists()); + return NULL; } + virtual string name() const { return m_name; } // * = Pin name, ""=go by number virtual void name(const string& name) { m_name = name; } virtual string prettyOperatorName() const { return modVarp() ? ((modVarp()->direction().isAny() @@ -1612,43 +1632,43 @@ public: +"port connection '"+modVarp()->prettyName()+"'") : "port connection"; } bool dotStar() const { return name() == ".*"; } // Fake name for .* connections until linked - int pinNum() const { return m_pinNum; } - void exprp(AstNode* nodep) { addOp1p(nodep); } - AstNode* exprp() const { return op1p(); } // op1 = Expression connected to pin, NULL if unconnected - AstVar* modVarp() const { return m_modVarp; } // [After Link] Pointer to variable - void modVarp(AstVar* nodep) { m_modVarp=nodep; } - AstParamTypeDType* modPTypep() const { return m_modPTypep; } // [After Link] Pointer to variable - void modPTypep(AstParamTypeDType* nodep) { m_modPTypep=nodep; } - bool param() const { return m_param; } - void param(bool flag) { m_param=flag; } - bool svImplicit() const { return m_svImplicit; } - void svImplicit(bool flag) { m_svImplicit=flag; } + int pinNum() const { return m_pinNum; } + void exprp(AstNode* nodep) { addOp1p(nodep); } + AstNode* exprp() const { return op1p(); } // op1 = Expression connected to pin, NULL if unconnected + AstVar* modVarp() const { return m_modVarp; } // [After Link] Pointer to variable + void modVarp(AstVar* nodep) { m_modVarp = nodep; } + AstParamTypeDType* modPTypep() const { return m_modPTypep; } // [After Link] Pointer to variable + void modPTypep(AstParamTypeDType* nodep) { m_modPTypep = nodep; } + bool param() const { return m_param; } + void param(bool flag) { m_param = flag; } + bool svImplicit() const { return m_svImplicit; } + void svImplicit(bool flag) { m_svImplicit = flag; } }; class AstArg : public AstNode { // An argument to a function/task private: - string m_name; // Pin name, or "" for number based interconnect + string m_name; // Pin name, or "" for number based interconnect public: AstArg(FileLine* fl, const string& name, AstNode* exprp) - : AstNode(fl) - ,m_name(name) { - setNOp1p(exprp); + : AstNode(fl) + , m_name(name) { + setNOp1p(exprp); } ASTNODE_NODE_FUNCS(Arg) - virtual string name() const { return m_name; } // * = Pin name, ""=go by number + virtual string name() const { return m_name; } // * = Pin name, ""=go by number virtual void name(const string& name) { m_name = name; } virtual V3Hash sameHash() const { return V3Hash(); } - void exprp(AstNode* nodep) { addOp1p(nodep); } - AstNode* exprp() const { return op1p(); } // op1 = Expression connected to pin, NULL if unconnected - bool emptyConnectNoNext() const { return !exprp() && name()=="" && !nextp(); } + void exprp(AstNode* nodep) { addOp1p(nodep); } + AstNode* exprp() const { return op1p(); } // op1 = Expression connected to pin, NULL if unconnected + bool emptyConnectNoNext() const { return !exprp() && name()=="" && !nextp(); } }; class AstModule : public AstNodeModule { // A module declaration public: AstModule(FileLine* fl, const string& name) - : AstNodeModule(fl,name) {} + : AstNodeModule(fl, name) {} ASTNODE_NODE_FUNCS(Module) virtual string verilogKwd() const { return "module"; } }; @@ -1657,7 +1677,7 @@ class AstNotFoundModule : public AstNodeModule { // A missing module declaration public: AstNotFoundModule(FileLine* fl, const string& name) - : AstNodeModule(fl,name) {} + : AstNodeModule(fl, name) {} ASTNODE_NODE_FUNCS(NotFoundModule) virtual string verilogKwd() const { return "/*not-found-*/ module"; } }; @@ -1666,7 +1686,7 @@ class AstPackage : public AstNodeModule { // A package declaration public: AstPackage(FileLine* fl, const string& name) - : AstNodeModule(fl,name) {} + : AstNodeModule(fl, name) {} ASTNODE_NODE_FUNCS(Package) virtual string verilogKwd() const { return "package"; } static string dollarUnitName() { return AstNode::encodeName("$unit"); } @@ -1677,7 +1697,7 @@ class AstPrimitive : public AstNodeModule { // A primitive declaration public: AstPrimitive(FileLine* fl, const string& name) - : AstNodeModule(fl,name) {} + : AstNodeModule(fl, name) {} ASTNODE_NODE_FUNCS(Primitive) virtual string verilogKwd() const { return "primitive"; } }; @@ -1694,8 +1714,8 @@ public: class AstPackageExport : public AstNode { private: // A package export declaration - string m_name; - AstPackage* m_packagep; // Package hierarchy + string m_name; + AstPackage* m_packagep; // Package hierarchy public: AstPackageExport(FileLine* fl, AstPackage* packagep, const string& name) : AstNode(fl), m_name(name), m_packagep(packagep) {} @@ -1705,14 +1725,14 @@ public: virtual void dump(std::ostream& str); virtual string name() const { return m_name; } AstPackage* packagep() const { return m_packagep; } - void packagep(AstPackage* nodep) { m_packagep=nodep; } + void packagep(AstPackage* nodep) { m_packagep = nodep; } }; class AstPackageImport : public AstNode { private: // A package import declaration - string m_name; - AstPackage* m_packagep; // Package hierarchy + string m_name; + AstPackage* m_packagep; // Package hierarchy public: AstPackageImport(FileLine* fl, AstPackage* packagep, const string& name) : AstNode(fl), m_name(name), m_packagep(packagep) {} @@ -1722,28 +1742,29 @@ public: virtual void dump(std::ostream& str); virtual string name() const { return m_name; } AstPackage* packagep() const { return m_packagep; } - void packagep(AstPackage* nodep) { m_packagep=nodep; } + void packagep(AstPackage* nodep) { m_packagep = nodep; } }; class AstIface : public AstNodeModule { // A module declaration public: AstIface(FileLine* fl, const string& name) - : AstNodeModule(fl,name) { } + : AstNodeModule(fl, name) { } ASTNODE_NODE_FUNCS(Iface) }; class AstModportFTaskRef : public AstNode { // An import/export referenced under a modport - // The storage for the function itself is inside the interface/instantiator, thus this is a reference + // The storage for the function itself is inside the + // interface/instantiator, thus this is a reference // PARENT: AstModport private: - string m_name; // Name of the variable referenced - bool m_export; // Type of the function (import/export) - AstNodeFTask* m_ftaskp; // Link to the function + string m_name; // Name of the variable referenced + bool m_export; // Type of the function (import/export) + AstNodeFTask* m_ftaskp; // Link to the function public: AstModportFTaskRef(FileLine* fl, const string& name, bool isExport) - : AstNode(fl), m_name(name), m_export(isExport), m_ftaskp(NULL) { } + : AstNode(fl), m_name(name), m_export(isExport), m_ftaskp(NULL) { } ASTNODE_NODE_FUNCS(ModportFTaskRef) virtual const char* broken() const { BROKEN_RTN(m_ftaskp && !m_ftaskp->brokeExists()); return NULL; } virtual void dump(std::ostream& str); @@ -1751,8 +1772,8 @@ public: virtual void cloneRelink() { if (m_ftaskp && m_ftaskp->clonep()) m_ftaskp = m_ftaskp->clonep(); } bool isImport() const { return !m_export; } bool isExport() const { return m_export; } - AstNodeFTask* ftaskp() const { return m_ftaskp; } // [After Link] Pointer to variable - void ftaskp(AstNodeFTask* ftaskp) { m_ftaskp=ftaskp; } + AstNodeFTask* ftaskp() const { return m_ftaskp; } // [After Link] Pointer to variable + void ftaskp(AstNodeFTask* ftaskp) { m_ftaskp = ftaskp; } }; class AstModportVarRef : public AstNode { @@ -1760,7 +1781,7 @@ class AstModportVarRef : public AstNode { // The storage for the variable itself is inside the interface, thus this is a reference // PARENT: AstModport private: - string m_name; // Name of the variable referenced + string m_name; // Name of the variable referenced VDirection m_direction; // Direction of the variable (in/out) AstVar* m_varp; // Link to the actual Var public: @@ -1773,63 +1794,63 @@ public: virtual string name() const { return m_name; } void direction(const VDirection& flag) { m_direction = flag; } VDirection direction() const { return m_direction; } - AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable - void varp(AstVar* varp) { m_varp=varp; } + AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable + void varp(AstVar* varp) { m_varp = varp; } }; class AstModport : public AstNode { // A modport in an interface private: - string m_name; // Name of the modport + string m_name; // Name of the modport public: AstModport(FileLine* fl, const string& name, AstNode* varsp) - : AstNode(fl), m_name(name) { + : AstNode(fl), m_name(name) { addNOp1p(varsp); } virtual string name() const { return m_name; } virtual bool maybePointedTo() const { return true; } ASTNODE_NODE_FUNCS(Modport) - AstNode* varsp() const { return op1p(); } // op1 = List of Vars + AstNode* varsp() const { return op1p(); } // op1 = List of Vars }; class AstCell : public AstNode { // A instantiation cell or interface call (don't know which until link) private: - string m_name; // Cell name - string m_origName; // Original name before dot addition - string m_modName; // Module the cell instances - AstNodeModule* m_modp; // [AfterLink] Pointer to module instanced - bool m_hasIfaceVar:1; // True if a Var has been created for this cell - bool m_recursive:1; // Self-recursive module - bool m_trace:1; // Trace this cell + string m_name; // Cell name + string m_origName; // Original name before dot addition + string m_modName; // Module the cell instances + AstNodeModule* m_modp; // [AfterLink] Pointer to module instanced + bool m_hasIfaceVar:1; // True if a Var has been created for this cell + bool m_recursive:1; // Self-recursive module + bool m_trace:1; // Trace this cell public: AstCell(FileLine* fl, const string& instName, const string& modName, - AstPin* pinsp, AstPin* paramsp, AstRange* rangep) - : AstNode(fl) - , m_name(instName), m_origName(instName), m_modName(modName) - , m_modp(NULL), m_hasIfaceVar(false), m_recursive(false), m_trace(true) { - addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); } + AstPin* pinsp, AstPin* paramsp, AstRange* rangep) + : AstNode(fl) + , m_name(instName), m_origName(instName), m_modName(modName) + , m_modp(NULL), m_hasIfaceVar(false), m_recursive(false), m_trace(true) { + addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); } ASTNODE_NODE_FUNCS(Cell) // No cloneRelink, we presume cloneee's want the same module linkages virtual void dump(std::ostream& str); virtual const char* broken() const { BROKEN_RTN(m_modp && !m_modp->brokeExists()); return NULL; } virtual bool maybePointedTo() const { return true; } // ACCESSORS - virtual string name() const { return m_name; } // * = Cell name + virtual string name() const { return m_name; } // * = Cell name virtual void name(const string& name) { m_name = name; } - string origName() const { return m_origName; } // * = Original name - void origName(const string& name) { m_origName = name; } - string modName() const { return m_modName; } // * = Instance name - void modName(const string& name) { m_modName = name; } + string origName() const { return m_origName; } // * = Original name + void origName(const string& name) { m_origName = name; } + string modName() const { return m_modName; } // * = Instance name + void modName(const string& name) { m_modName = name; } AstPin* pinsp() const { return VN_CAST(op1p(), Pin); } // op1 = List of cell ports AstPin* paramsp() const { return VN_CAST(op2p(), Pin); } // op2 = List of parameter #(##) values AstRange* rangep() const { return VN_CAST(op3p(), Range); } // op3 = Range of arrayed instants (NULL=not ranged) - AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated + AstNodeModule* modp() const { return m_modp; } // [AfterLink] = Pointer to module instantiated void addPinsp(AstPin* nodep) { addOp1p(nodep); } void addParamsp(AstPin* nodep) { addOp2p(nodep); } - void modp(AstNodeModule* nodep) { m_modp = nodep; } + void modp(AstNodeModule* nodep) { m_modp = nodep; } bool hasIfaceVar() const { return m_hasIfaceVar; } void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; } - void trace(bool flag) { m_trace=flag; } + void trace(bool flag) { m_trace = flag; } bool isTrace() const { return m_trace; } void recursive(bool flag) { m_recursive = flag; } bool recursive() const { return m_recursive; } @@ -1840,17 +1861,17 @@ class AstCellInline : public AstNode { // For communication between V3Inline and V3LinkDot only // Children: When 2 levels inlined, other CellInline under this private: - string m_name; // Cell name, possibly {a}__DOT__{b}... - string m_origModName; // Original name of the module, ignoring name() changes, for dot lookup + string m_name; // Cell name, possibly {a}__DOT__{b}... + string m_origModName; // Original name of the module, ignoring name() changes, for dot lookup public: AstCellInline(FileLine* fl, const string& name, const string& origModName) - : AstNode(fl) - , m_name(name), m_origModName(origModName) {} + : AstNode(fl) + , m_name(name), m_origModName(origModName) {} ASTNODE_NODE_FUNCS(CellInline) virtual void dump(std::ostream& str); // ACCESSORS - virtual string name() const { return m_name; } // * = Cell name - string origModName() const { return m_origModName; } // * = modp()->origName() before inlining + virtual string name() const { return m_name; } // * = Cell name + string origModName() const { return m_origModName; } // * = modp()->origName() before inlining virtual void name(const string& name) { m_name = name; } }; @@ -1860,15 +1881,15 @@ private: string m_name; // Cell name public: AstCellRef(FileLine* fl, - const string& name, AstNode* cellp, AstNode* exprp) - : AstNode(fl) - , m_name(name) { - addNOp1p(cellp); addNOp2p(exprp); } + const string& name, AstNode* cellp, AstNode* exprp) + : AstNode(fl) + , m_name(name) { + addNOp1p(cellp); addNOp2p(exprp); } ASTNODE_NODE_FUNCS(CellRef) // ACCESSORS - virtual string name() const { return m_name; } // * = Array name - AstNode* cellp() const { return op1p(); } // op1 = Cell - AstNode* exprp() const { return op2p(); } // op2 = Expression + virtual string name() const { return m_name; } // * = Array name + AstNode* cellp() const { return op1p(); } // op1 = Cell + AstNode* exprp() const { return op2p(); } // op2 = Expression }; class AstCellArrayRef : public AstNode { @@ -1877,14 +1898,14 @@ private: string m_name; // Array name public: AstCellArrayRef(FileLine* fl, - const string& name, AstNode* selectExprp) - : AstNode(fl) - , m_name(name) { - addNOp1p(selectExprp); } + const string& name, AstNode* selectExprp) + : AstNode(fl) + , m_name(name) { + addNOp1p(selectExprp); } ASTNODE_NODE_FUNCS(CellArrayRef) // ACCESSORS - virtual string name() const { return m_name; } // * = Array name - AstNode* selp() const { return op1p(); } // op1 = Select expression + virtual string name() const { return m_name; } // * = Array name + AstNode* selp() const { return op1p(); } // op1 = Select expression }; class AstUnlinkedRef : public AstNode { @@ -1893,49 +1914,49 @@ private: string m_name; // Var name public: AstUnlinkedRef(FileLine* fl, - AstNode* refp, const string& name, AstNode* crp) - : AstNode(fl) - , m_name(name) { - addNOp1p(refp); addNOp2p(crp); } + AstNode* refp, const string& name, AstNode* crp) + : AstNode(fl) + , m_name(name) { + addNOp1p(refp); addNOp2p(crp); } ASTNODE_NODE_FUNCS(UnlinkedRef) // ACCESSORS - virtual string name() const { return m_name; } // * = Var name - AstNode* refp() const { return op1p(); } // op1 = VarXRef or AstNodeFTaskRef - AstNode* cellrefp() const { return op2p(); } // op2 = CellArrayRef or CellRef + virtual string name() const { return m_name; } // * = Var name + AstNode* refp() const { return op1p(); } // op1 = VarXRef or AstNodeFTaskRef + AstNode* cellrefp() const { return op2p(); } // op2 = CellArrayRef or CellRef }; class AstBind : public AstNode { // Parents: MODULE // Children: CELL private: - string m_name; // Binding to name + string m_name; // Binding to name public: AstBind(FileLine* fl, const string& name, AstNode* cellsp) - : AstNode(fl) - , m_name(name) { + : AstNode(fl) + , m_name(name) { if (!VN_IS(cellsp, Cell)) cellsp->v3fatalSrc("Only cells allowed to be bound"); - addNOp1p(cellsp); + addNOp1p(cellsp); } ASTNODE_NODE_FUNCS(Bind) // ACCESSORS - virtual string name() const { return m_name; } // * = Bind Target name + virtual string name() const { return m_name; } // * = Bind Target name virtual void name(const string& name) { m_name = name; } - AstNode* cellsp() const { return op1p(); } // op1= cells + AstNode* cellsp() const { return op1p(); } // op1 = cells }; class AstPort : public AstNode { // A port (in/out/inout) on a module private: - int m_pinNum; // Pin number - string m_name; // Name of pin + int m_pinNum; // Pin number + string m_name; // Name of pin public: AstPort(FileLine* fl, int pinnum, const string& name) : AstNode(fl) - ,m_pinNum(pinnum) ,m_name(name) {} + , m_pinNum(pinnum), m_name(name) {} ASTNODE_NODE_FUNCS(Port) - virtual string name() const { return m_name; } // * = Port name - int pinNum() const { return m_pinNum; } // * = Pin number, for order based instantiation - AstNode* exprp() const { return op1p(); } // op1 = Expression connected to port + virtual string name() const { return m_name; } // * = Port name + int pinNum() const { return m_pinNum; } // * = Pin number, for order based instantiation + AstNode* exprp() const { return op1p(); } // op1 = Expression connected to port }; //###################################################################### @@ -1946,12 +1967,12 @@ class AstGenerate : public AstNode { // Children: modItems public: AstGenerate(FileLine* fileline, AstNode* stmtsp) - : AstNode(fileline) { - addNOp1p(stmtsp); + : AstNode(fileline) { + addNOp1p(stmtsp); } ASTNODE_NODE_FUNCS(Generate) // op1 = Statements - AstNode* stmtsp() const { return op1p(); } // op1 = List of statements + AstNode* stmtsp() const { return op1p(); } // op1 = List of statements void addStmtp(AstNode* nodep) { addOp1p(nodep); } }; @@ -1962,48 +1983,48 @@ class AstParseRef : public AstNode { // Parents: math|stmt // Children: TEXT|DOT|SEL*|TASK|FUNC (or expression under sel) private: - AstParseRefExp m_expect; // Type we think it should resolve to - string m_name; + AstParseRefExp m_expect; // Type we think it should resolve to + string m_name; public: AstParseRef(FileLine* fl, AstParseRefExp expect, const string& name, AstNode* lhsp, AstNodeFTaskRef* ftaskrefp) : AstNode(fl), m_expect(expect), m_name(name) { setNOp1p(lhsp); setNOp2p(ftaskrefp); } ASTNODE_NODE_FUNCS(ParseRef) virtual void dump(std::ostream& str); - virtual string name() const { return m_name; } // * = Var name - virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_expect),V3Hash(m_name)); } + virtual string name() const { return m_name; } // * = Var name + virtual V3Hash sameHash() const { return V3Hash(V3Hash(m_expect), V3Hash(m_name)); } virtual bool same(const AstNode* samep) const { - const AstParseRef* asamep = static_cast(samep); - return (expect() == asamep->expect() - && m_name == asamep->m_name); } + const AstParseRef* asamep = static_cast(samep); + return (expect() == asamep->expect() + && m_name == asamep->m_name); } virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } - virtual void name(const string& name) { m_name = name; } + virtual void name(const string& name) { m_name = name; } AstParseRefExp expect() const { return m_expect; } - void expect(AstParseRefExp exp) { m_expect=exp; } + void expect(AstParseRefExp exp) { m_expect = exp; } // op1 = Components - AstNode* lhsp() const { return op1p(); } // op1 = List of statements - AstNode* ftaskrefp() const { return op2p(); } // op2 = Function/task reference - void ftaskrefp(AstNodeFTaskRef* nodep) { setNOp2p(nodep); } // op2 = Function/task reference + AstNode* lhsp() const { return op1p(); } // op1 = List of statements + AstNode* ftaskrefp() const { return op2p(); } // op2 = Function/task reference + void ftaskrefp(AstNodeFTaskRef* nodep) { setNOp2p(nodep); } // op2 = Function/task reference }; class AstPackageRef : public AstNode { private: - AstPackage* m_packagep; // Package hierarchy + AstPackage* m_packagep; // Package hierarchy public: AstPackageRef(FileLine* fl, AstPackage* packagep) - : AstNode(fl), m_packagep(packagep) {} + : AstNode(fl), m_packagep(packagep) {} ASTNODE_NODE_FUNCS(PackageRef) // METHODS virtual const char* broken() const { BROKEN_RTN(!m_packagep || !m_packagep->brokeExists()); return NULL; } virtual void cloneRelink() { if (m_packagep && m_packagep->clonep()) { - m_packagep = m_packagep->clonep(); + m_packagep = m_packagep->clonep(); }} virtual bool same(const AstNode* samep) const { - return (m_packagep==static_cast(samep)->m_packagep); } + return (m_packagep==static_cast(samep)->m_packagep); } virtual V3Hash sameHash() const { return V3Hash(m_packagep); } virtual void dump(std::ostream& str=std::cout); AstPackage* packagep() const { return m_packagep; } - void packagep(AstPackage* nodep) { m_packagep=nodep; } + void packagep(AstPackage* nodep) { m_packagep = nodep; } }; class AstDot : public AstNode { @@ -2014,8 +2035,8 @@ public: : AstNode(fl) { setOp1p(lhsp); setOp2p(rhsp); } ASTNODE_NODE_FUNCS(Dot) static AstNode* newIfPkg(FileLine*fl, AstPackage* packagep, AstNode* rhsp) { // For parser, make only if non-null package - if (!packagep) return rhsp; - return new AstDot(fl, new AstPackageRef(fl, packagep), rhsp); + if (!packagep) return rhsp; + return new AstDot(fl, new AstPackageRef(fl, packagep), rhsp); } virtual void dump(std::ostream& str); virtual string emitVerilog() { V3ERROR_NA; return ""; } @@ -2073,8 +2094,8 @@ class AstDpiExport : public AstNode { // however we're not *calling* it, so that seems somehow wrong. // (Probably AstNodeFTaskRef should be renamed AstNodeFTaskCall and have-a AstNodeFTaskRef) private: - string m_name; // Name of function - string m_cname; // Name of function on c side + string m_name; // Name of function + string m_cname; // Name of function on c side public: AstDpiExport(FileLine* fl, const string& vname, const string& cname) : AstNode(fl), m_name(vname), m_cname(cname) { } @@ -2091,45 +2112,45 @@ class AstSenItem : public AstNodeSenItem { // Parents: SENTREE // Children: (optional) VARREF SENGATE private: - AstEdgeType m_edgeType; // Edge type + AstEdgeType m_edgeType; // Edge type public: - class Combo {}; // for creator type-overload selection - class Illegal {}; // for creator type-overload selection - class Initial {}; // for creator type-overload selection - class Settle {}; // for creator type-overload selection - class Never {}; // for creator type-overload selection + class Combo {}; // for creator type-overload selection + class Illegal {}; // for creator type-overload selection + class Initial {}; // for creator type-overload selection + class Settle {}; // for creator type-overload selection + class Never {}; // for creator type-overload selection AstSenItem(FileLine* fl, AstEdgeType edgeType, AstNode* varrefp) - : AstNodeSenItem(fl), m_edgeType(edgeType) { - setOp1p(varrefp); + : AstNodeSenItem(fl), m_edgeType(edgeType) { + setOp1p(varrefp); } AstSenItem(FileLine* fl, Combo) - : AstNodeSenItem(fl) { - m_edgeType = AstEdgeType::ET_COMBO; + : AstNodeSenItem(fl) { + m_edgeType = AstEdgeType::ET_COMBO; } AstSenItem(FileLine* fl, Illegal) - : AstNodeSenItem(fl) { - m_edgeType = AstEdgeType::ET_ILLEGAL; + : AstNodeSenItem(fl) { + m_edgeType = AstEdgeType::ET_ILLEGAL; } AstSenItem(FileLine* fl, Initial) - : AstNodeSenItem(fl) { - m_edgeType = AstEdgeType::ET_INITIAL; + : AstNodeSenItem(fl) { + m_edgeType = AstEdgeType::ET_INITIAL; } AstSenItem(FileLine* fl, Settle) - : AstNodeSenItem(fl) { - m_edgeType = AstEdgeType::ET_SETTLE; + : AstNodeSenItem(fl) { + m_edgeType = AstEdgeType::ET_SETTLE; } AstSenItem(FileLine* fl, Never) - : AstNodeSenItem(fl) { - m_edgeType = AstEdgeType::ET_NEVER; + : AstNodeSenItem(fl) { + m_edgeType = AstEdgeType::ET_NEVER; } ASTNODE_NODE_FUNCS(SenItem) virtual void dump(std::ostream& str); virtual V3Hash sameHash() const { return V3Hash(edgeType()); } virtual bool same(const AstNode* samep) const { - return edgeType()==static_cast(samep)->edgeType(); } - AstEdgeType edgeType() const { return m_edgeType; } // * = Posedge/negedge - void edgeType(AstEdgeType type) { m_edgeType=type; editCountInc(); }// * = Posedge/negedge - AstNode* sensp() const { return op1p(); } // op1 = Signal sensitized + return edgeType()==static_cast(samep)->edgeType(); } + AstEdgeType edgeType() const { return m_edgeType; } // * = Posedge/negedge + void edgeType(AstEdgeType type) { m_edgeType = type; editCountInc(); } // * = Posedge/negedge + AstNode* sensp() const { return op1p(); } // op1 = Signal sensitized AstNodeVarRef* varrefp() const { return VN_CAST(op1p(), NodeVarRef); } // op1 = Signal sensitized // virtual bool isClocked() const { return edgeType().clockedStmt(); } @@ -2148,14 +2169,14 @@ class AstSenGate : public AstNodeSenItem { // Performing this gating is optional; it may be removed by later optimizations public: AstSenGate(FileLine* fl, AstSenItem* sensesp, AstNode* rhsp) : AstNodeSenItem(fl) { - dtypeSetLogicBool(); addOp1p(sensesp); setOp2p(rhsp); + dtypeSetLogicBool(); addOp1p(sensesp); setOp2p(rhsp); } ASTNODE_NODE_FUNCS(SenGate) virtual string emitVerilog() { return "(%l) %f&& (%r)"; } AstSenItem* sensesp() const { return VN_CAST(op1p(), SenItem); } - AstNode* rhsp() const { return op2p(); } - void sensesp(AstSenItem* nodep) { addOp1p(nodep); } - void rhsp(AstNode* nodep) { setOp2p(nodep); } + AstNode* rhsp() const { return op2p(); } + void sensesp(AstSenItem* nodep) { addOp1p(nodep); } + void rhsp(AstNode* nodep) { setOp2p(nodep); } // virtual bool isClocked() const { return true; } virtual bool isCombo() const { return false; } @@ -2169,11 +2190,11 @@ class AstSenTree : public AstNode { // Parents: MODULE | SBLOCK // Children: SENITEM list private: - bool m_multi; // Created from combo logic by ORing multiple clock domains + bool m_multi; // Created from combo logic by ORing multiple clock domains public: AstSenTree(FileLine* fl, AstNodeSenItem* sensesp) - : AstNode(fl), m_multi(false) { - addNOp1p(sensesp); + : AstNode(fl), m_multi(false) { + addNOp1p(sensesp); } ASTNODE_NODE_FUNCS(SenTree) virtual void dump(std::ostream& str); @@ -2184,18 +2205,18 @@ public: void addSensesp(AstNodeSenItem* nodep) { addOp1p(nodep); } void multi(bool flag) { m_multi = true; } // METHODS - bool hasClocked() const; // Includes a clocked statement - bool hasSettle() const; // Includes a SETTLE SenItem - bool hasInitial() const; // Includes a INITIAL SenItem - bool hasCombo() const; // Includes a COMBO SenItem + bool hasClocked() const; // Includes a clocked statement + bool hasSettle() const; // Includes a SETTLE SenItem + bool hasInitial() const; // Includes a INITIAL SenItem + bool hasCombo() const; // Includes a COMBO SenItem }; class AstAlways : public AstNode { VAlwaysKwd m_keyword; public: AstAlways(FileLine* fl, VAlwaysKwd keyword, AstSenTree* sensesp, AstNode* bodysp) - : AstNode(fl), m_keyword(keyword) { - addNOp1p(sensesp); addNOp2p(bodysp); + : AstNode(fl), m_keyword(keyword) { + addNOp1p(sensesp); addNOp2p(bodysp); } ASTNODE_NODE_FUNCS(Always) // @@ -2213,15 +2234,15 @@ class AstAlwaysPublic : public AstNodeStmt { // Body statements are just AstVarRefs to the public signals public: AstAlwaysPublic(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) - : AstNodeStmt(fl) { - addNOp1p(sensesp); addNOp2p(bodysp); + : AstNodeStmt(fl) { + addNOp1p(sensesp); addNOp2p(bodysp); } ASTNODE_NODE_FUNCS(AlwaysPublic) virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } // AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list - AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate + AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate void addStmtp(AstNode* nodep) { addOp2p(nodep); } // Special accessors bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); } @@ -2231,20 +2252,20 @@ class AstAlwaysPost : public AstNode { // Like always but post assignments for memory assignment IFs public: AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) - : AstNode(fl) { - addNOp1p(sensesp); addNOp2p(bodysp); + : AstNode(fl) { + addNOp1p(sensesp); addNOp2p(bodysp); } ASTNODE_NODE_FUNCS(AlwaysPost) // - AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate - void addBodysp(AstNode* newp) { addOp2p(newp); } + AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate + void addBodysp(AstNode* newp) { addOp2p(newp); } }; class AstAssign : public AstNodeAssign { public: AstAssign(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) - : AstNodeAssign(fileline, lhsp, rhsp) { - dtypeFrom(lhsp); + : AstNodeAssign(fileline, lhsp, rhsp) { + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Assign) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssign(this->fileline(), lhsp, rhsp); } @@ -2256,7 +2277,7 @@ class AstAssignAlias : public AstNodeAssign { // If both sides are wires, there's no LHS vs RHS, public: AstAssignAlias(FileLine* fileline, AstVarRef* lhsp, AstVarRef* rhsp) - : AstNodeAssign(fileline, lhsp, rhsp) {} + : AstNodeAssign(fileline, lhsp, rhsp) {} ASTNODE_NODE_FUNCS(AssignAlias) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { V3ERROR_NA; return NULL; } virtual bool brokeLhsMustBeLvalue() const { return false; } @@ -2265,7 +2286,7 @@ public: class AstAssignDly : public AstNodeAssign { public: AstAssignDly(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) - : AstNodeAssign(fileline, lhsp, rhsp) {} + : AstNodeAssign(fileline, lhsp, rhsp) {} ASTNODE_NODE_FUNCS(AssignDly) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignDly(this->fileline(), lhsp, rhsp); } virtual bool isGateOptimizable() const { return false; } @@ -2277,17 +2298,17 @@ class AstAssignW : public AstNodeAssign { // Like assign, but wire/assign's in verilog, the only setting of the specified variable public: AstAssignW(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) - : AstNodeAssign(fileline, lhsp, rhsp) { } + : AstNodeAssign(fileline, lhsp, rhsp) { } ASTNODE_NODE_FUNCS(AssignW) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignW(this->fileline(), lhsp, rhsp); } virtual bool brokeLhsMustBeLvalue() const { return true; } AstAlways* convertToAlways() { - AstNode* lhs1p = lhsp()->unlinkFrBack(); - AstNode* rhs1p = rhsp()->unlinkFrBack(); + AstNode* lhs1p = lhsp()->unlinkFrBack(); + AstNode* rhs1p = rhsp()->unlinkFrBack(); AstAlways* newp = new AstAlways(fileline(), VAlwaysKwd::ALWAYS, NULL, new AstAssign(fileline(), lhs1p, rhs1p)); - replaceWith(newp); // User expected to then deleteTree(); - return newp; + replaceWith(newp); // User expected to then deleteTree(); + return newp; } }; @@ -2295,8 +2316,8 @@ class AstAssignVarScope : public AstNodeAssign { // Assign two VarScopes to each other public: AstAssignVarScope(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) - : AstNodeAssign(fileline, lhsp, rhsp) { - dtypeFrom(rhsp); + : AstNodeAssign(fileline, lhsp, rhsp) { + dtypeFrom(rhsp); } ASTNODE_NODE_FUNCS(AssignVarScope) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignVarScope(this->fileline(), lhsp, rhsp); } @@ -2308,15 +2329,15 @@ private: bool m_direction; public: AstPull(FileLine* fileline, AstNode* lhsp, bool direction) - : AstNode(fileline) { - setOp1p(lhsp); - m_direction = direction; + : AstNode(fileline) { + setOp1p(lhsp); + m_direction = direction; } ASTNODE_NODE_FUNCS(Pull) virtual bool same(const AstNode* samep) const { - return direction()==static_cast(samep)->direction(); } + return direction()==static_cast(samep)->direction(); } void lhsp(AstNode* np) { setOp1p(np); } - AstNode* lhsp() const { return op1p(); } // op1 = Assign to + AstNode* lhsp() const { return op1p(); } // op1 = Assign to uint32_t direction() const { return (uint32_t) m_direction; } }; @@ -2324,7 +2345,7 @@ class AstAssignPre : public AstNodeAssign { // Like Assign, but predelayed assignment requiring special order handling public: AstAssignPre(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) - : AstNodeAssign(fileline, lhsp, rhsp) {} + : AstNodeAssign(fileline, lhsp, rhsp) {} ASTNODE_NODE_FUNCS(AssignPre) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignPre(this->fileline(), lhsp, rhsp); } virtual bool brokeLhsMustBeLvalue() const { return true; } @@ -2334,7 +2355,7 @@ class AstAssignPost : public AstNodeAssign { // Like Assign, but predelayed assignment requiring special order handling public: AstAssignPost(FileLine* fileline, AstNode* lhsp, AstNode* rhsp) - : AstNodeAssign(fileline, lhsp, rhsp) {} + : AstNodeAssign(fileline, lhsp, rhsp) {} ASTNODE_NODE_FUNCS(AssignPost) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignPost(this->fileline(), lhsp, rhsp); } virtual bool brokeLhsMustBeLvalue() const { return true; } @@ -2345,13 +2366,13 @@ class AstComment : public AstNodeStmt { // Parents: {statement list} // Children: none private: - string m_name; // Name of variable + string m_name; // Text of comment public: AstComment(FileLine* fl, const string& name) - : AstNodeStmt(fl) - , m_name(name) {} + : AstNodeStmt(fl) + , m_name(name) {} ASTNODE_NODE_FUNCS(Comment) - virtual string name() const { return m_name; } // * = Var name + virtual string name() const { return m_name; } // * = Text virtual V3Hash sameHash() const { return V3Hash(); } // Ignore name in comments virtual bool same(const AstNode* samep) const { return true; } // Ignore name in comments }; @@ -2362,10 +2383,10 @@ class AstCond : public AstNodeCond { // Children: MATH public: AstCond(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p) - : AstNodeCond(fl, condp, expr1p, expr2p) {} + : AstNodeCond(fl, condp, expr1p, expr2p) {} ASTNODE_NODE_FUNCS(Cond) virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) { - return new AstCond(this->fileline(), condp, expr1p, expr2p); } + return new AstCond(this->fileline(), condp, expr1p, expr2p); } }; class AstCondBound : public AstNodeCond { @@ -2374,10 +2395,10 @@ class AstCondBound : public AstNodeCond { // Children: MATH public: AstCondBound(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p) - : AstNodeCond(fl, condp, expr1p, expr2p) {} + : AstNodeCond(fl, condp, expr1p, expr2p) {} ASTNODE_NODE_FUNCS(CondBound) virtual AstNode* cloneType(AstNode* condp, AstNode* expr1p, AstNode* expr2p) { - return new AstCondBound(this->fileline(), condp, expr1p, expr2p); } + return new AstCondBound(this->fileline(), condp, expr1p, expr2p); } }; class AstCoverDecl : public AstNodeStmt { @@ -2385,50 +2406,51 @@ class AstCoverDecl : public AstNodeStmt { // Parents: {statement list} // Children: none private: - AstCoverDecl* m_dataDeclp; // [After V3CoverageJoin] Pointer to duplicate declaration to get data from instead - string m_page; - string m_text; - string m_hier; - int m_column; - int m_binNum; // Set by V3EmitCSyms to tell final V3Emit what to increment + AstCoverDecl* m_dataDeclp; // [After V3CoverageJoin] Pointer to duplicate declaration to get data from instead + string m_page; + string m_text; + string m_hier; + int m_column; + int m_binNum; // Set by V3EmitCSyms to tell final V3Emit what to increment public: AstCoverDecl(FileLine* fl, int column, const string& page, const string& comment) - : AstNodeStmt(fl) { - m_text = comment; m_page = page; m_column = column; - m_binNum = 0; - m_dataDeclp = NULL; + : AstNodeStmt(fl) { + m_text = comment; m_page = page; m_column = column; + m_binNum = 0; + m_dataDeclp = NULL; } ASTNODE_NODE_FUNCS(CoverDecl) virtual const char* broken() const { - BROKEN_RTN(m_dataDeclp && !m_dataDeclp->brokeExists()); + BROKEN_RTN(m_dataDeclp && !m_dataDeclp->brokeExists()); if (m_dataDeclp && m_dataDeclp->m_dataDeclp) { // Avoid O(n^2) accessing v3fatalSrc("dataDeclp should point to real data, not be a list"); } return NULL; } virtual void cloneRelink() { if (m_dataDeclp && m_dataDeclp->clonep()) m_dataDeclp = m_dataDeclp->clonep(); } virtual void dump(std::ostream& str); - virtual int instrCount() const { return 1+2*instrCountLd(); } + virtual int instrCount() const { return 1+2*instrCountLd(); } virtual bool maybePointedTo() const { return true; } - int column() const { return m_column; } - void binNum(int flag) { m_binNum = flag; } - int binNum() const { return m_binNum; } - const string& comment() const { return m_text; } // text to insert in code + int column() const { return m_column; } + void binNum(int flag) { m_binNum = flag; } + int binNum() const { return m_binNum; } + const string& comment() const { return m_text; } // text to insert in code const string& page() const { return m_page; } const string& hier() const { return m_hier; } - void hier(const string& flag) { m_hier=flag; } - void comment(const string& flag) { m_text=flag; } + void hier(const string& flag) { m_hier = flag; } + void comment(const string& flag) { m_text = flag; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { - const AstCoverDecl* asamep = static_cast(samep); - return (fileline() == asamep->fileline() - && hier() == asamep->hier() - && comment() == asamep->comment() - && column() == asamep->column()); } + const AstCoverDecl* asamep = static_cast(samep); + return (fileline() == asamep->fileline() + && hier() == asamep->hier() + && comment() == asamep->comment() + && column() == asamep->column()); } virtual bool isPredictOptimizable() const { return false; } - void dataDeclp(AstCoverDecl* nodep) { m_dataDeclp=nodep; } - // dataDecl NULL means "use this one", but often you want "this" to indicate to get data from here - AstCoverDecl* dataDeclNullp() const { return m_dataDeclp; } - AstCoverDecl* dataDeclThisp() { return dataDeclNullp()?dataDeclNullp():this; } + void dataDeclp(AstCoverDecl* nodep) { m_dataDeclp = nodep; } + // dataDecl NULL means "use this one", but often you want "this" to + // indicate to get data from here + AstCoverDecl* dataDeclNullp() const { return m_dataDeclp; } + AstCoverDecl* dataDeclThisp() { return dataDeclNullp()?dataDeclNullp():this; } }; class AstCoverInc : public AstNodeStmt { @@ -2436,25 +2458,25 @@ class AstCoverInc : public AstNodeStmt { // Parents: {statement list} // Children: none private: - AstCoverDecl* m_declp; // [After V3Coverage] Pointer to declaration + AstCoverDecl* m_declp; // [After V3Coverage] Pointer to declaration public: AstCoverInc(FileLine* fl, AstCoverDecl* declp) - : AstNodeStmt(fl) { - m_declp = declp; + : AstNodeStmt(fl) { + m_declp = declp; } ASTNODE_NODE_FUNCS(CoverInc) virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; } virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep(); } virtual void dump(std::ostream& str); - virtual int instrCount() const { return 1+2*instrCountLd(); } + virtual int instrCount() const { return 1+2*instrCountLd(); } virtual V3Hash sameHash() const { return V3Hash(declp()); } virtual bool same(const AstNode* samep) const { - return declp() == static_cast(samep)->declp(); } + return declp() == static_cast(samep)->declp(); } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } virtual bool isOutputter() const { return true; } // but isPure() true - AstCoverDecl* declp() const { return m_declp; } // Where defined + AstCoverDecl* declp() const { return m_declp; } // Where defined }; class AstCoverToggle : public AstNodeStmt { @@ -2463,21 +2485,21 @@ class AstCoverToggle : public AstNodeStmt { // Children: AstCoverInc, orig var, change det var public: AstCoverToggle(FileLine* fl, AstCoverInc* incp, AstNode* origp, AstNode* changep) - : AstNodeStmt(fl) { - setOp1p(incp); - setOp2p(origp); - setOp3p(changep); + : AstNodeStmt(fl) { + setOp1p(incp); + setOp2p(origp); + setOp3p(changep); } ASTNODE_NODE_FUNCS(CoverToggle) - virtual int instrCount() const { return 3+instrCountBranch()+instrCountLd(); } + virtual int instrCount() const { return 3+instrCountBranch()+instrCountLd(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return true; } - virtual bool isOutputter() const { return false; } // Though the AstCoverInc under this is an outputter + virtual bool isOutputter() const { return false; } // Though the AstCoverInc under this is an outputter // but isPure() true AstCoverInc* incp() const { return VN_CAST(op1p(), CoverInc); } - void incp(AstCoverInc* nodep) { setOp1p(nodep); } + void incp(AstCoverInc* nodep) { setOp1p(nodep); } AstNode* origp() const { return op2p(); } AstNode* changep() const { return op3p(); } }; @@ -2489,7 +2511,7 @@ class AstGenCase : public AstNodeCase { // casesp Children: CASEITEMs public: AstGenCase(FileLine* fileline, AstNode* exprp, AstNode* casesp) - : AstNodeCase(fileline, exprp, casesp) { + : AstNodeCase(fileline, exprp, casesp) { } ASTNODE_NODE_FUNCS(GenCase) }; @@ -2500,38 +2522,38 @@ class AstCase : public AstNodeCase { // exprp Children: MATHs // casesp Children: CASEITEMs private: - VCaseType m_casex; // 0=case, 1=casex, 2=casez - bool m_fullPragma; // Synthesis full_case - bool m_parallelPragma; // Synthesis parallel_case - bool m_uniquePragma; // unique case - bool m_unique0Pragma; // unique0 case - bool m_priorityPragma; // priority case + VCaseType m_casex; // 0=case, 1=casex, 2=casez + bool m_fullPragma; // Synthesis full_case + bool m_parallelPragma; // Synthesis parallel_case + bool m_uniquePragma; // unique case + bool m_unique0Pragma; // unique0 case + bool m_priorityPragma; // priority case public: AstCase(FileLine* fileline, VCaseType casex, AstNode* exprp, AstNode* casesp) - : AstNodeCase(fileline, exprp, casesp) { - m_casex=casex; - m_fullPragma=false; m_parallelPragma=false; - m_uniquePragma=false; m_unique0Pragma=false; m_priorityPragma=false; + : AstNodeCase(fileline, exprp, casesp) { + m_casex = casex; + m_fullPragma = false; m_parallelPragma = false; + m_uniquePragma = false; m_unique0Pragma = false; m_priorityPragma = false; } ASTNODE_NODE_FUNCS(Case) - virtual string verilogKwd() const { return casez()?"casez":casex()?"casex":"case"; } + virtual string verilogKwd() const { return casez()?"casez":casex()?"casex":"case"; } virtual bool same(const AstNode* samep) const { - return m_casex==static_cast(samep)->m_casex; } - bool casex() const { return m_casex==VCaseType::CT_CASEX; } - bool casez() const { return m_casex==VCaseType::CT_CASEZ; } - bool caseInside() const { return m_casex==VCaseType::CT_CASEINSIDE; } - bool caseSimple() const { return m_casex==VCaseType::CT_CASE; } - void caseInsideSet() { m_casex=VCaseType::CT_CASEINSIDE; } - bool fullPragma() const { return m_fullPragma; } - void fullPragma(bool flag) { m_fullPragma=flag; } - bool parallelPragma() const { return m_parallelPragma; } - void parallelPragma(bool flag) { m_parallelPragma=flag; } - bool uniquePragma() const { return m_uniquePragma; } - void uniquePragma(bool flag) { m_uniquePragma=flag; } - bool unique0Pragma() const { return m_unique0Pragma; } - void unique0Pragma(bool flag) { m_unique0Pragma=flag; } - bool priorityPragma() const { return m_priorityPragma; } - void priorityPragma(bool flag) { m_priorityPragma=flag; } + return m_casex==static_cast(samep)->m_casex; } + bool casex() const { return m_casex==VCaseType::CT_CASEX; } + bool casez() const { return m_casex==VCaseType::CT_CASEZ; } + bool caseInside() const { return m_casex==VCaseType::CT_CASEINSIDE; } + bool caseSimple() const { return m_casex==VCaseType::CT_CASE; } + void caseInsideSet() { m_casex = VCaseType::CT_CASEINSIDE; } + bool fullPragma() const { return m_fullPragma; } + void fullPragma(bool flag) { m_fullPragma = flag; } + bool parallelPragma() const { return m_parallelPragma; } + void parallelPragma(bool flag) { m_parallelPragma = flag; } + bool uniquePragma() const { return m_uniquePragma; } + void uniquePragma(bool flag) { m_uniquePragma = flag; } + bool unique0Pragma() const { return m_unique0Pragma; } + void unique0Pragma(bool flag) { m_unique0Pragma = flag; } + bool priorityPragma() const { return m_priorityPragma; } + void priorityPragma(bool flag) { m_priorityPragma = flag; } }; class AstCaseItem : public AstNode { @@ -2540,58 +2562,58 @@ class AstCaseItem : public AstNode { // condsp Children: MATH (Null condition used for default block) // bodysp Children: Statements private: - bool m_ignoreOverlap; // Default created by assertions; ignore overlaps + bool m_ignoreOverlap; // Default created by assertions; ignore overlaps public: AstCaseItem(FileLine* fileline, AstNode* condsp, AstNode* bodysp) - : AstNode(fileline) { - addNOp1p(condsp); addNOp2p(bodysp); - m_ignoreOverlap = false; + : AstNode(fileline) { + addNOp1p(condsp); addNOp2p(bodysp); + m_ignoreOverlap = false; } ASTNODE_NODE_FUNCS(CaseItem) - virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } - AstNode* condsp() const { return op1p(); } // op1= list of possible matching expressions - AstNode* bodysp() const { return op2p(); } // op2= what to do - void condsp(AstNode* nodep) { setOp1p(nodep); } - void addBodysp(AstNode* newp) { addOp2p(newp); } - bool isDefault() const { return condsp()==NULL; } - bool ignoreOverlap() const { return m_ignoreOverlap; } - void ignoreOverlap(bool flag) { m_ignoreOverlap = flag; } + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } + AstNode* condsp() const { return op1p(); } // op1 = list of possible matching expressions + AstNode* bodysp() const { return op2p(); } // op2 = what to do + void condsp(AstNode* nodep) { setOp1p(nodep); } + void addBodysp(AstNode* newp) { addOp2p(newp); } + bool isDefault() const { return condsp()==NULL; } + bool ignoreOverlap() const { return m_ignoreOverlap; } + void ignoreOverlap(bool flag) { m_ignoreOverlap = flag; } }; class AstSFormatF : public AstNode { // Convert format to string, generally under an AstDisplay or AstSFormat // Also used as "real" function for /*verilator sformat*/ functions - string m_text; - bool m_hidden; // Under display, etc - bool m_hasFormat; // Has format code + string m_text; + bool m_hidden; // Under display, etc + bool m_hasFormat; // Has format code public: class NoFormat {}; AstSFormatF(FileLine* fl, const string& text, bool hidden, AstNode* exprsp) - : AstNode(fl), m_text(text), m_hidden(hidden), m_hasFormat(true) { - dtypeSetString(); - addNOp1p(exprsp); addNOp2p(NULL); } + : AstNode(fl), m_text(text), m_hidden(hidden), m_hasFormat(true) { + dtypeSetString(); + addNOp1p(exprsp); addNOp2p(NULL); } AstSFormatF(FileLine* fl, NoFormat, AstNode* exprsp) - : AstNode(fl), m_text(""), m_hidden(true), m_hasFormat(false) { - dtypeSetString(); - addNOp1p(exprsp); addNOp2p(NULL); } + : AstNode(fl), m_text(""), m_hidden(true), m_hasFormat(false) { + dtypeSetString(); + addNOp1p(exprsp); addNOp2p(NULL); } ASTNODE_NODE_FUNCS(SFormatF) virtual string name() const { return m_text; } virtual int instrCount() const { return instrCountPli(); } virtual V3Hash sameHash() const { return V3Hash(text()); } virtual bool hasDType() const { return true; } virtual bool same(const AstNode* samep) const { - return text()==static_cast(samep)->text(); } + return text()==static_cast(samep)->text(); } virtual string verilogKwd() const { return "$sformatf"; } void addExprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output - AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output - string text() const { return m_text; } // * = Text to display - void text(const string& text) { m_text=text; } + AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output + string text() const { return m_text; } // * = Text to display + void text(const string& text) { m_text = text; } AstScopeName* scopeNamep() const { return VN_CAST(op2p(), ScopeName); } void scopeNamep(AstNode* nodep) { setNOp2p(nodep); } bool formatScopeTracking() const { // Track scopeNamep(); Ok if false positive - return (name().find("%m") != string::npos || name().find("%M") != string::npos); } + return (name().find("%m") != string::npos || name().find("%M") != string::npos); } bool hidden() const { return m_hidden; } - void hasFormat(bool flag) { m_hasFormat=flag; } + void hasFormat(bool flag) { m_hasFormat = flag; } bool hasFormat() const { return m_hasFormat; } }; @@ -2600,19 +2622,19 @@ class AstDisplay : public AstNodeStmt { // Children: file which must be a varref // Children: SFORMATF to generate print string private: - AstDisplayType m_displayType; + AstDisplayType m_displayType; public: AstDisplay(FileLine* fileline, AstDisplayType dispType, const string& text, AstNode* filep, AstNode* exprsp) : AstNodeStmt(fileline) { - setOp1p(new AstSFormatF(fileline,text,true,exprsp)); - setNOp3p(filep); - m_displayType = dispType; + setOp1p(new AstSFormatF(fileline, text, true, exprsp)); + setNOp3p(filep); + m_displayType = dispType; } AstDisplay(FileLine* fileline, AstDisplayType dispType, AstNode* filep, AstNode* exprsp) : AstNodeStmt(fileline) { - setOp1p(new AstSFormatF(fileline, AstSFormatF::NoFormat(), exprsp)); - setNOp3p(filep); - m_displayType = dispType; + setOp1p(new AstSFormatF(fileline, AstSFormatF::NoFormat(), exprsp)); + setNOp3p(filep); + m_displayType = dispType; } ASTNODE_NODE_FUNCS(Display) virtual void dump(std::ostream& str); @@ -2621,20 +2643,20 @@ public: : string("$")+string(displayType().ascii())); } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering - virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output + virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output virtual bool isUnlikely() const { return true; } virtual V3Hash sameHash() const { return V3Hash(displayType()); } virtual bool same(const AstNode* samep) const { - return displayType()==static_cast(samep)->displayType(); } - virtual int instrCount() const { return instrCountPli(); } - AstDisplayType displayType() const { return m_displayType; } - void displayType(AstDisplayType type) { m_displayType = type; } - bool addNewline() const { return displayType().addNewline(); } // * = Add a newline for $display - void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter + return displayType()==static_cast(samep)->displayType(); } + virtual int instrCount() const { return instrCountPli(); } + AstDisplayType displayType() const { return m_displayType; } + void displayType(AstDisplayType type) { m_displayType = type; } + bool addNewline() const { return displayType().addNewline(); } // * = Add a newline for $display + void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); } - AstNode* filep() const { return op3p(); } - void filep(AstNodeVarRef* nodep) { setNOp3p(nodep); } + AstNode* filep() const { return op3p(); } + void filep(AstNodeVarRef* nodep) { setNOp3p(nodep); } }; class AstSFormat : public AstNodeStmt { @@ -2644,8 +2666,8 @@ class AstSFormat : public AstNodeStmt { public: AstSFormat(FileLine* fileline, AstNode* lhsp, const string& text, AstNode* exprsp) : AstNodeStmt(fileline) { - setOp1p(new AstSFormatF(fileline,text,true,exprsp)); - setOp3p(lhsp); + setOp1p(new AstSFormatF(fileline, text, true, exprsp)); + setOp3p(lhsp); } ASTNODE_NODE_FUNCS(SFormat) virtual const char* broken() const { BROKEN_RTN(!fmtp()); return NULL; } @@ -2657,13 +2679,13 @@ public: virtual bool isPure() const { return true; } virtual bool isOutputter() const { return false; } virtual bool cleanOut() { return false; } - virtual int instrCount() const { return instrCountPli(); } + virtual int instrCount() const { return instrCountPli(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter + void fmtp(AstSFormatF* nodep) { addOp1p(nodep); } // op1 = To-String formatter AstSFormatF* fmtp() const { return VN_CAST(op1p(), SFormatF); } - AstNode* lhsp() const { return op3p(); } - void lhsp(AstNode* nodep) { setOp3p(nodep); } + AstNode* lhsp() const { return op3p(); } + void lhsp(AstNode* nodep) { setOp3p(nodep); } }; class AstSysFuncAsTask : public AstNodeStmt { @@ -2698,9 +2720,9 @@ public: virtual bool isPredictOptimizable() const { return false; } // Though deleted before opt virtual bool isPure() const { return false; } // Though deleted before opt virtual bool isOutputter() const { return true; } // Though deleted before opt - virtual int instrCount() const { return instrCountPli(); } - AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output - void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output + virtual int instrCount() const { return instrCountPli(); } + AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output + void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output }; class AstFClose : public AstNodeStmt { @@ -2709,7 +2731,7 @@ class AstFClose : public AstNodeStmt { public: AstFClose(FileLine* fileline, AstNode* filep) : AstNodeStmt(fileline) { - setNOp2p(filep); + setNOp2p(filep); } ASTNODE_NODE_FUNCS(FClose) virtual string verilogKwd() const { return "$fclose"; } @@ -2720,7 +2742,7 @@ public: virtual bool isUnlikely() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstNode* filep() const { return op2p(); } + AstNode* filep() const { return op2p(); } void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } }; @@ -2729,9 +2751,9 @@ class AstFOpen : public AstNodeStmt { public: AstFOpen(FileLine* fileline, AstNode* filep, AstNode* filenamep, AstNode* modep) : AstNodeStmt(fileline) { - setOp1p(filep); - setOp2p(filenamep); - setOp3p(modep); + setOp1p(filep); + setOp2p(filenamep); + setOp3p(modep); } ASTNODE_NODE_FUNCS(FOpen) virtual string verilogKwd() const { return "$fopen"; } @@ -2742,9 +2764,9 @@ public: virtual bool isUnlikely() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstNode* filep() const { return op1p(); } - AstNode* filenamep() const { return op2p(); } - AstNode* modep() const { return op3p(); } + AstNode* filep() const { return op1p(); } + AstNode* filenamep() const { return op2p(); } + AstNode* modep() const { return op3p(); } }; class AstFFlush : public AstNodeStmt { @@ -2753,7 +2775,7 @@ class AstFFlush : public AstNodeStmt { public: AstFFlush(FileLine* fileline, AstNode* filep) : AstNodeStmt(fileline) { - setNOp2p(filep); + setNOp2p(filep); } ASTNODE_NODE_FUNCS(FFlush) virtual string verilogKwd() const { return "$fflush"; } @@ -2764,7 +2786,7 @@ public: virtual bool isUnlikely() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstNode* filep() const { return op2p(); } + AstNode* filep() const { return op2p(); } void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } }; @@ -2809,32 +2831,32 @@ class AstFScanF : public AstNodeMath { // Children: file which must be a varref // Children: varrefs to load private: - string m_text; + string m_text; public: AstFScanF(FileLine* fileline, const string& text, AstNode* filep, AstNode* exprsp) : AstNodeMath(fileline), m_text(text) { - addNOp1p(exprsp); - setNOp2p(filep); + addNOp1p(exprsp); + setNOp2p(filep); } ASTNODE_NODE_FUNCS(FScanF) - virtual string name() const { return m_text; } + virtual string name() const { return m_text; } virtual string verilogKwd() const { return "$fscanf"; } virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual bool isPure() const { return false; } // SPECIAL: has 'visual' ordering - virtual bool isOutputter() const { return true; } // SPECIAL: makes output + virtual bool isPure() const { return false; } // SPECIAL: has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: makes output virtual bool cleanOut() { return false; } virtual V3Hash sameHash() const { return V3Hash(text()); } virtual bool same(const AstNode* samep) const { - return text()==static_cast(samep)->text(); } - AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output - void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output - string text() const { return m_text; } // * = Text to display - void text(const string& text) { m_text=text; } - AstNode* filep() const { return op2p(); } - void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } + return text()==static_cast(samep)->text(); } + AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output + void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output + string text() const { return m_text; } // * = Text to display + void text(const string& text) { m_text = text; } + AstNode* filep() const { return op2p(); } + void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); } }; class AstSScanF : public AstNodeMath { @@ -2842,43 +2864,43 @@ class AstSScanF : public AstNodeMath { // Children: file which must be a varref // Children: varrefs to load private: - string m_text; + string m_text; public: AstSScanF(FileLine* fileline, const string& text, AstNode* fromp, AstNode* exprsp) : AstNodeMath(fileline), m_text(text) { - addNOp1p(exprsp); - setOp2p(fromp); + addNOp1p(exprsp); + setOp2p(fromp); } ASTNODE_NODE_FUNCS(SScanF) - virtual string name() const { return m_text; } + virtual string name() const { return m_text; } virtual string verilogKwd() const { return "$sscanf"; } virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual bool isPure() const { return false; } // SPECIAL: has 'visual' ordering - virtual bool isOutputter() const { return true; } // SPECIAL: makes output + virtual bool isPure() const { return false; } // SPECIAL: has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: makes output virtual bool cleanOut() { return false; } virtual V3Hash sameHash() const { return V3Hash(text()); } virtual bool same(const AstNode* samep) const { - return text()==static_cast(samep)->text(); } - AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output - void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output - string text() const { return m_text; } // * = Text to display - void text(const string& text) { m_text=text; } - AstNode* fromp() const { return op2p(); } - void fromp(AstNode* nodep) { setOp2p(nodep); } + return text()==static_cast(samep)->text(); } + AstNode* exprsp() const { return op1p(); } // op1 = Expressions to output + void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output + string text() const { return m_text; } // * = Text to display + void text(const string& text) { m_text = text; } + AstNode* fromp() const { return op2p(); } + void fromp(AstNode* nodep) { setOp2p(nodep); } }; class AstNodeReadWriteMem : public AstNodeStmt { private: - bool m_isHex; // readmemh, not readmemb + bool m_isHex; // readmemh, not readmemb public: AstNodeReadWriteMem(FileLine* fileline, bool hex, AstNode* filenamep, AstNode* memp, AstNode* lsbp, AstNode* msbp) : AstNodeStmt(fileline), m_isHex(hex) { - setOp1p(filenamep); setOp2p(memp); setNOp3p(lsbp); setNOp4p(msbp); + setOp1p(filenamep); setOp2p(memp); setNOp3p(lsbp); setNOp4p(msbp); } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } @@ -2887,21 +2909,21 @@ public: virtual bool isUnlikely() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { - return isHex()==static_cast(samep)->isHex(); + return isHex()==static_cast(samep)->isHex(); } - bool isHex() const { return m_isHex; } - AstNode* filenamep() const { return op1p(); } - AstNode* memp() const { return op2p(); } - AstNode* lsbp() const { return op3p(); } - AstNode* msbp() const { return op4p(); } + bool isHex() const { return m_isHex; } + AstNode* filenamep() const { return op1p(); } + AstNode* memp() const { return op2p(); } + AstNode* lsbp() const { return op3p(); } + AstNode* msbp() const { return op4p(); } virtual const char* cFuncPrefixp() const = 0; }; class AstReadMem : public AstNodeReadWriteMem { public: AstReadMem(FileLine* fileline, bool hex, - AstNode* filenamep, AstNode* memp, AstNode* lsbp, AstNode* msbp) - : AstNodeReadWriteMem(fileline, hex, filenamep, memp, lsbp, msbp) + AstNode* filenamep, AstNode* memp, AstNode* lsbp, AstNode* msbp) + : AstNodeReadWriteMem(fileline, hex, filenamep, memp, lsbp, msbp) { } ASTNODE_NODE_FUNCS(ReadMem); virtual string verilogKwd() const { return (isHex()?"$readmemh":"$readmemb"); } @@ -2912,7 +2934,7 @@ class AstWriteMem : public AstNodeReadWriteMem { public: AstWriteMem(FileLine* fileline, AstNode* filenamep, AstNode* memp, AstNode* lsbp, AstNode* msbp) - : AstNodeReadWriteMem(fileline, true, filenamep, memp, lsbp, msbp) { } + : AstNodeReadWriteMem(fileline, true, filenamep, memp, lsbp, msbp) { } ASTNODE_NODE_FUNCS(WriteMem) virtual string verilogKwd() const { return (isHex()?"$writememh":"$writememb"); } virtual const char* cFuncPrefixp() const { return "VL_WRITEMEM_"; } @@ -2923,7 +2945,7 @@ class AstSystemT : public AstNodeStmt { public: AstSystemT(FileLine* fileline, AstNode* lhsp) : AstNodeStmt(fileline) { - setOp1p(lhsp); + setOp1p(lhsp); } ASTNODE_NODE_FUNCS(SystemT) virtual string verilogKwd() const { return "$system"; } @@ -2934,7 +2956,7 @@ public: virtual bool isUnlikely() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstNode* lhsp() const { return op1p(); } + AstNode* lhsp() const { return op1p(); } }; class AstSystemF : public AstNodeMath { @@ -2942,7 +2964,7 @@ class AstSystemF : public AstNodeMath { public: AstSystemF(FileLine* fileline, AstNode* lhsp) : AstNodeMath(fileline) { - setOp1p(lhsp); + setOp1p(lhsp); } ASTNODE_NODE_FUNCS(SystemF) virtual string verilogKwd() const { return "$system"; } @@ -2956,7 +2978,7 @@ public: virtual bool cleanOut() { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstNode* lhsp() const { return op1p(); } + AstNode* lhsp() const { return op1p(); } }; class AstValuePlusArgs : public AstNodeMath { @@ -2965,7 +2987,7 @@ class AstValuePlusArgs : public AstNodeMath { public: AstValuePlusArgs(FileLine* fileline, AstNode* searchp, AstNode* outp) : AstNodeMath(fileline) { - setOp1p(searchp); setOp2p(outp); + setOp1p(searchp); setOp2p(outp); } ASTNODE_NODE_FUNCS(ValuePlusArgs) virtual string verilogKwd() const { return "$value$plusargs"; } @@ -2976,22 +2998,22 @@ public: virtual bool cleanOut() { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstNode* searchp() const { return op1p(); } // op1 = Search expression - void searchp(AstNode* nodep) { setOp1p(nodep); } - AstNode* outp() const { return op2p(); } // op2 = Expressions to output - void outp(AstNode* nodep) { setOp2p(nodep); } + AstNode* searchp() const { return op1p(); } // op1 = Search expression + void searchp(AstNode* nodep) { setOp1p(nodep); } + AstNode* outp() const { return op2p(); } // op2 = Expressions to output + void outp(AstNode* nodep) { setOp2p(nodep); } }; class AstTestPlusArgs : public AstNodeMath { // Parents: expr // Child: variable to set. If NULL then this is a $test$plusargs instead of $value$plusargs private: - string m_text; + string m_text; public: AstTestPlusArgs(FileLine* fileline, const string& text) : AstNodeMath(fileline), m_text(text) { } ASTNODE_NODE_FUNCS(TestPlusArgs) - virtual string name() const { return m_text; } + virtual string name() const { return m_text; } virtual string verilogKwd() const { return "$test$plusargs"; } virtual string emitVerilog() { return verilogKwd(); } virtual string emitC() { return "VL_VALUEPLUSARGS_%nq(%lw, %P, NULL)"; } @@ -3000,16 +3022,16 @@ public: virtual bool cleanOut() { return true; } virtual V3Hash sameHash() const { return V3Hash(text()); } virtual bool same(const AstNode* samep) const { - return text() == static_cast(samep)->text(); } - string text() const { return m_text; } // * = Text to display - void text(const string& text) { m_text=text; } + return text() == static_cast(samep)->text(); } + string text() const { return m_text; } // * = Text to display + void text(const string& text) { m_text = text; } }; class AstGenFor : public AstNodeFor { public: AstGenFor(FileLine* fileline, AstNode* initsp, AstNode* condp, - AstNode* incsp, AstNode* bodysp) - : AstNodeFor(fileline, initsp, condp, incsp, bodysp) { + AstNode* incsp, AstNode* bodysp) + : AstNodeFor(fileline, initsp, condp, incsp, bodysp) { } ASTNODE_NODE_FUNCS(GenFor) }; @@ -3017,16 +3039,16 @@ public: class AstForeach : public AstNodeStmt { public: AstForeach(FileLine* fileline, AstNode* arrayp, AstNode* varsp, - AstNode* bodysp) - : AstNodeStmt(fileline) { - setOp1p(arrayp); addNOp2p(varsp); addNOp4p(bodysp); + AstNode* bodysp) + : AstNodeStmt(fileline) { + setOp1p(arrayp); addNOp2p(varsp); addNOp4p(bodysp); } ASTNODE_NODE_FUNCS(Foreach) - AstNode* arrayp() const { return op1p(); } // op1= array - AstNode* varsp() const { return op2p(); } // op2= variable index list - AstNode* bodysp() const { return op4p(); } // op4= body of loop + AstNode* arrayp() const { return op1p(); } // op1 = array + AstNode* varsp() const { return op2p(); } // op2 = variable index list + AstNode* bodysp() const { return op4p(); } // op4 = body of loop virtual bool isGateOptimizable() const { return false; } - virtual int instrCount() const { return instrCountBranch(); } + virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3034,14 +3056,14 @@ public: class AstRepeat : public AstNodeStmt { public: AstRepeat(FileLine* fileline, AstNode* countp, AstNode* bodysp) - : AstNodeStmt(fileline) { - setOp2p(countp); addNOp3p(bodysp); + : AstNodeStmt(fileline) { + setOp2p(countp); addNOp3p(bodysp); } ASTNODE_NODE_FUNCS(Repeat) - AstNode* countp() const { return op2p(); } // op2= condition to continue - AstNode* bodysp() const { return op3p(); } // op3= body of loop + AstNode* countp() const { return op2p(); } // op2 = condition to continue + AstNode* bodysp() const { return op3p(); } // op3 = body of loop virtual bool isGateOptimizable() const { return false; } // Not releavant - converted to FOR - virtual int instrCount() const { return instrCountBranch(); } + virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3049,19 +3071,19 @@ public: class AstWhile : public AstNodeStmt { public: AstWhile(FileLine* fileline, AstNode* condp, AstNode* bodysp, AstNode* incsp=NULL) - : AstNodeStmt(fileline) { - setOp2p(condp); addNOp3p(bodysp); addNOp4p(incsp); + : AstNodeStmt(fileline) { + setOp2p(condp); addNOp3p(bodysp); addNOp4p(incsp); } ASTNODE_NODE_FUNCS(While) - AstNode* precondsp() const { return op1p(); } // op1= prepare statements for condition (exec every loop) - AstNode* condp() const { return op2p(); } // op2= condition to continue - AstNode* bodysp() const { return op3p(); } // op3= body of loop - AstNode* incsp() const { return op4p(); } // op4= increment (if from a FOR loop) - void addPrecondsp(AstNode* newp) { addOp1p(newp); } - void addBodysp(AstNode* newp) { addOp3p(newp); } - void addIncsp(AstNode* newp) { addOp4p(newp); } + AstNode* precondsp() const { return op1p(); } // op1 = prepare statements for condition (exec every loop) + AstNode* condp() const { return op2p(); } // op2 = condition to continue + AstNode* bodysp() const { return op3p(); } // op3 = body of loop + AstNode* incsp() const { return op4p(); } // op4 = increment (if from a FOR loop) + void addPrecondsp(AstNode* newp) { addOp1p(newp); } + void addBodysp(AstNode* newp) { addOp3p(newp); } + void addIncsp(AstNode* newp) { addOp4p(newp); } virtual bool isGateOptimizable() const { return false; } - virtual int instrCount() const { return instrCountBranch(); } + virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } virtual void addBeforeStmt(AstNode* newp, AstNode* belowp); // Stop statement searchback here @@ -3075,7 +3097,7 @@ public: ASTNODE_NODE_FUNCS(Break) virtual string verilogKwd() const { return "break"; }; virtual V3Hash sameHash() const { return V3Hash(); } - virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks + virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks }; class AstContinue : public AstNodeStmt { @@ -3085,59 +3107,59 @@ public: ASTNODE_NODE_FUNCS(Continue) virtual string verilogKwd() const { return "continue"; }; virtual V3Hash sameHash() const { return V3Hash(); } - virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks + virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks }; class AstDisable : public AstNodeStmt { private: - string m_name; // Name of block + string m_name; // Name of block public: AstDisable(FileLine* fileline, const string& name) - : AstNodeStmt(fileline), m_name(name) {} + : AstNodeStmt(fileline), m_name(name) {} ASTNODE_NODE_FUNCS(Disable) - virtual string name() const { return m_name; } // * = Block name - void name(const string& flag) { m_name=flag; } - virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks + virtual string name() const { return m_name; } // * = Block name + void name(const string& flag) { m_name = flag; } + virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks }; class AstReturn : public AstNodeStmt { public: AstReturn(FileLine* fileline, AstNode* lhsp=NULL) : AstNodeStmt(fileline) { - setNOp1p(lhsp); + setNOp1p(lhsp); } ASTNODE_NODE_FUNCS(Return) virtual string verilogKwd() const { return "return"; }; virtual V3Hash sameHash() const { return V3Hash(); } - AstNode* lhsp() const { return op1p(); } - virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks + AstNode* lhsp() const { return op1p(); } + virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks }; class AstGenIf : public AstNodeIf { public: AstGenIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp) - : AstNodeIf(fileline, condp, ifsp, elsesp) { + : AstNodeIf(fileline, condp, ifsp, elsesp) { } ASTNODE_NODE_FUNCS(GenIf) }; class AstIf : public AstNodeIf { private: - bool m_uniquePragma; // unique case - bool m_unique0Pragma; // unique0 case - bool m_priorityPragma; // priority case + bool m_uniquePragma; // unique case + bool m_unique0Pragma; // unique0 case + bool m_priorityPragma; // priority case public: AstIf(FileLine* fileline, AstNode* condp, AstNode* ifsp, AstNode* elsesp=NULL) - : AstNodeIf(fileline, condp, ifsp, elsesp) { - m_uniquePragma=false; m_unique0Pragma=false; m_priorityPragma=false; + : AstNodeIf(fileline, condp, ifsp, elsesp) { + m_uniquePragma = false; m_unique0Pragma = false; m_priorityPragma = false; } ASTNODE_NODE_FUNCS(If) - bool uniquePragma() const { return m_uniquePragma; } - void uniquePragma(bool flag) { m_uniquePragma=flag; } - bool unique0Pragma() const { return m_unique0Pragma; } - void unique0Pragma(bool flag) { m_unique0Pragma=flag; } - bool priorityPragma() const { return m_priorityPragma; } - void priorityPragma(bool flag) { m_priorityPragma=flag; } + bool uniquePragma() const { return m_uniquePragma; } + void uniquePragma(bool flag) { m_uniquePragma = flag; } + bool unique0Pragma() const { return m_unique0Pragma; } + void unique0Pragma(bool flag) { m_unique0Pragma = flag; } + bool priorityPragma() const { return m_priorityPragma; } + void priorityPragma(bool flag) { m_priorityPragma = flag; } }; class AstJumpLabel : public AstNodeStmt { @@ -3146,44 +3168,44 @@ class AstJumpLabel : public AstNodeStmt { // Parents: {statement list} // Children: {statement list, with JumpGo below} private: - int m_labelNum; // Set by V3EmitCSyms to tell final V3Emit what to increment + int m_labelNum; // Set by V3EmitCSyms to tell final V3Emit what to increment public: AstJumpLabel(FileLine* fl, AstNode* stmtsp) - : AstNodeStmt(fl) ,m_labelNum(0) { - addNOp1p(stmtsp); + : AstNodeStmt(fl), m_labelNum(0) { + addNOp1p(stmtsp); } - virtual int instrCount() const { return 0; } + virtual int instrCount() const { return 0; } ASTNODE_NODE_FUNCS(JumpLabel) virtual bool maybePointedTo() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } // op1 = Statements - AstNode* stmtsp() const { return op1p(); } // op1 = List of statements + AstNode* stmtsp() const { return op1p(); } // op1 = List of statements void addStmtsp(AstNode* nodep) { addNOp1p(nodep); } int labelNum() const { return m_labelNum; } - void labelNum(int flag) { m_labelNum=flag; } + void labelNum(int flag) { m_labelNum = flag; } }; class AstJumpGo : public AstNodeStmt { // Jump point; branch up to the JumpLabel // Parents: {statement list} private: - AstJumpLabel* m_labelp; // [After V3Jump] Pointer to declaration + AstJumpLabel* m_labelp; // [After V3Jump] Pointer to declaration public: AstJumpGo(FileLine* fl, AstJumpLabel* labelp) - : AstNodeStmt(fl) { - m_labelp = labelp; + : AstNodeStmt(fl) { + m_labelp = labelp; } ASTNODE_NODE_FUNCS(JumpGo) virtual const char* broken() const { BROKEN_RTN(!labelp()->brokeExistsAbove()); return NULL; } virtual void cloneRelink() { if (m_labelp->clonep()) m_labelp = m_labelp->clonep(); } virtual void dump(std::ostream& str); - virtual int instrCount() const { return instrCountBranch(); } + virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(labelp()); } virtual bool same(const AstNode* samep) const { // Also same if identical tree structure all the way down, but hard to detect - return labelp() == static_cast(samep)->labelp(); } + return labelp() == static_cast(samep)->labelp(); } virtual bool isGateOptimizable() const { return false; } - virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks + virtual bool isBrancher() const { return true; } // SPECIAL: We don't process code after breaks AstJumpLabel* labelp() const { return m_labelp; } }; @@ -3193,17 +3215,17 @@ class AstUntilStable : public AstNodeStmt { // Children: VARREF, statements public: AstUntilStable(FileLine* fileline, AstVarRef* stablesp, AstNode* bodysp) - : AstNodeStmt(fileline) { - addNOp2p(stablesp); addNOp3p(bodysp); + : AstNodeStmt(fileline) { + addNOp2p(stablesp); addNOp3p(bodysp); } ASTNODE_NODE_FUNCS(UntilStable) - AstVarRef* stablesp() const { return VN_CAST(op2p(), VarRef); } // op2= list of variables that must become stable - AstNode* bodysp() const { return op3p(); } // op3= body of loop - void addStablesp(AstVarRef* newp) { addOp2p(newp); } - void addBodysp(AstNode* newp) { addOp3p(newp); } - virtual bool isGateOptimizable() const { return false; } // Not relevant - virtual bool isPredictOptimizable() const { return false; } // Not relevant - virtual int instrCount() const { return instrCountBranch(); } + AstVarRef* stablesp() const { return VN_CAST(op2p(), VarRef); } // op2 = list of variables that must become stable + AstNode* bodysp() const { return op3p(); } // op3 = body of loop + void addStablesp(AstVarRef* newp) { addOp2p(newp); } + void addBodysp(AstNode* newp) { addOp3p(newp); } + virtual bool isGateOptimizable() const { return false; } // Not relevant + virtual bool isPredictOptimizable() const { return false; } // Not relevant + virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3215,38 +3237,40 @@ class AstChangeXor : public AstNodeBiComAsv { // Children: VARREF public: AstChangeXor(FileLine* fl, AstNode* lhsp, AstNode* rhsp) - : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeSetUInt32(); // Always used on, and returns word entities + : AstNodeBiComAsv(fl, lhsp, rhsp) { + dtypeSetUInt32(); // Always used on, and returns word entities } ASTNODE_NODE_FUNCS(ChangeXor) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstChangeXor(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opChangeXor(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opChangeXor(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f^ %r)"; } virtual string emitC() { return "VL_CHANGEXOR_%li(%lw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "^"; } - virtual bool cleanOut() {return false;} // Lclean && Rclean - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs(); } + virtual bool cleanOut() { return false; } // Lclean && Rclean + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs(); } }; class AstChangeDet : public AstNodeStmt { // A comparison to determine change detection, common & must be fast. private: - bool m_clockReq; // Type of detection + bool m_clockReq; // Type of detection public: // Null lhs+rhs used to indicate change needed with no spec vars AstChangeDet(FileLine* fl, AstNode* lhsp, AstNode* rhsp, bool clockReq) - : AstNodeStmt(fl) { - setNOp1p(lhsp); setNOp2p(rhsp); m_clockReq=clockReq; + : AstNodeStmt(fl) { + setNOp1p(lhsp); setNOp2p(rhsp); m_clockReq = clockReq; } ASTNODE_NODE_FUNCS(ChangeDet) - AstNode* lhsp() const { return op1p(); } - AstNode* rhsp() const { return op2p(); } - bool isClockReq() const { return m_clockReq; } + AstNode* lhsp() const { return op1p(); } + AstNode* rhsp() const { return op2p(); } + bool isClockReq() const { return m_clockReq; } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual int instrCount() const { return widthInstrs(); } + virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3256,26 +3280,26 @@ class AstBegin : public AstNode { // Parents: statement // Children: statements private: - string m_name; // Name of block - bool m_unnamed; // Originally unnamed - bool m_generate; // Underneath a generate + string m_name; // Name of block + bool m_unnamed; // Originally unnamed + bool m_generate; // Underneath a generate public: // Node that simply puts name into the output stream AstBegin(FileLine* fileline, const string& name, AstNode* stmtsp, bool generate=false) - : AstNode(fileline) - , m_name(name) { - addNOp1p(stmtsp); - m_unnamed = (name==""); - m_generate = generate; + : AstNode(fileline) + , m_name(name) { + addNOp1p(stmtsp); + m_unnamed = (name==""); + m_generate = generate; } ASTNODE_NODE_FUNCS(Begin) virtual void dump(std::ostream& str); - virtual string name() const { return m_name; } // * = Block name + virtual string name() const { return m_name; } // * = Block name virtual void name(const string& name) { m_name = name; } // op1 = Statements - AstNode* stmtsp() const { return op1p(); } // op1 = List of statements + AstNode* stmtsp() const { return op1p(); } // op1 = List of statements void addStmtsp(AstNode* nodep) { addNOp1p(nodep); } - AstNode* genforp() const { return op2p(); } // op2 = GENFOR, if applicable, + AstNode* genforp() const { return op2p(); } // op2 = GENFOR, if applicable, // might NOT be a GenFor, as loop unrolling replaces with Begin void addGenforp(AstGenFor* nodep) { addOp2p(nodep); } bool unnamed() const { return m_unnamed; } @@ -3286,11 +3310,11 @@ public: class AstInitial : public AstNode { public: AstInitial(FileLine* fl, AstNode* bodysp) - : AstNode(fl) { - addNOp1p(bodysp); + : AstNode(fl) { + addNOp1p(bodysp); } ASTNODE_NODE_FUNCS(Initial) - AstNode* bodysp() const { return op1p(); } // op1 = Expressions to evaluate + AstNode* bodysp() const { return op1p(); } // op1 = Expressions to evaluate // Special accessors bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); } }; @@ -3298,23 +3322,23 @@ public: class AstFinal : public AstNode { public: AstFinal(FileLine* fl, AstNode* bodysp) - : AstNode(fl) { - addNOp1p(bodysp); + : AstNode(fl) { + addNOp1p(bodysp); } ASTNODE_NODE_FUNCS(Final) - AstNode* bodysp() const { return op1p(); } // op1 = Expressions to evaluate + AstNode* bodysp() const { return op1p(); } // op1 = Expressions to evaluate }; class AstInside : public AstNodeMath { public: AstInside(FileLine* fl, AstNode* exprp, AstNode* itemsp) - : AstNodeMath(fl) { - addOp1p(exprp); addOp2p(itemsp); - dtypeSetLogicBool(); + : AstNodeMath(fl) { + addOp1p(exprp); addOp2p(itemsp); + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Inside) - AstNode* exprp() const { return op1p(); } // op1 = LHS expression to compare with - AstNode* itemsp() const { return op2p(); } // op2 = RHS, possibly a list of expr or AstInsideRange + AstNode* exprp() const { return op1p(); } // op1 = LHS expression to compare with + AstNode* itemsp() const { return op2p(); } // op2 = RHS, possibly a list of expr or AstInsideRange virtual string emitVerilog() { return "%l inside { %r }"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return false; } // NA @@ -3323,12 +3347,12 @@ public: class AstInsideRange : public AstNodeMath { public: AstInsideRange(FileLine* fl, AstNode* lhsp, AstNode* rhsp) - : AstNodeMath(fl) { - addOp1p(lhsp); addOp2p(rhsp); + : AstNodeMath(fl) { + addOp1p(lhsp); addOp2p(rhsp); } ASTNODE_NODE_FUNCS(InsideRange) - AstNode* lhsp() const { return op1p(); } // op1 = LHS - AstNode* rhsp() const { return op2p(); } // op2 = RHS + AstNode* lhsp() const { return op1p(); } // op1 = LHS + AstNode* rhsp() const { return op2p(); } // op2 = RHS virtual string emitVerilog() { return "[%l:%r]"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return false; } // NA @@ -3344,82 +3368,82 @@ class AstInitArray : public AstNode { std::deque m_indices; // Which array index each entry in the list is for (if defaultp) public: AstInitArray(FileLine* fl, AstNodeArrayDType* newDTypep, AstNode* defaultp) - : AstNode(fl) { - dtypep(newDTypep); - addNOp1p(defaultp); + : AstNode(fl) { + dtypep(newDTypep); + addNOp1p(defaultp); } ASTNODE_NODE_FUNCS(InitArray) - AstNode* defaultp() const { return op1p(); } // op1 = Default if sparse + AstNode* defaultp() const { return op1p(); } // op1 = Default if sparse void defaultp(AstNode* newp) { setOp1p(newp); } - AstNode* initsp() const { return op2p(); } // op2 = Initial value expressions + AstNode* initsp() const { return op2p(); } // op2 = Initial value expressions void addValuep(AstNode* newp) { addIndexValuep(m_indices.size(), newp); } void addIndexValuep(uint32_t index, AstNode* newp) { - // Must insert in sorted order - if (!m_indices.empty()) UASSERT(index > m_indices.back(), "InitArray adding index <= previous index"); - m_indices.push_back(index); - addOp2p(newp); } + // Must insert in sorted order + if (!m_indices.empty()) UASSERT(index > m_indices.back(), "InitArray adding index <= previous index"); + m_indices.push_back(index); + addOp2p(newp); } void addFrontValuep(AstNode* newp) { // Add to front of list, e.g. index 0. - // e.g. 0:100, 1:101 when addFront(200), get 0:200, 1:100, 2:101 - initsp()->addHereThisAsNext(newp); - m_indices.push_back(m_indices.size()); + // e.g. 0:100, 1:101 when addFront(200), get 0:200, 1:100, 2:101 + initsp()->addHereThisAsNext(newp); + m_indices.push_back(m_indices.size()); } - int posIndex(int listPos) { + int posIndex(int listPos) { UASSERT(listPos < (int)m_indices.size(), "InitArray past end of indices list"); - return m_indices[listPos]; } + return m_indices[listPos]; } virtual bool hasDType() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { - return m_indices == static_cast(samep)->m_indices; } + return m_indices == static_cast(samep)->m_indices; } }; class AstPragma : public AstNode { private: - AstPragmaType m_pragType; // Type of pragma + AstPragmaType m_pragType; // Type of pragma public: // Pragmas don't result in any output code, they're just flags that affect // other processing in verilator. AstPragma(FileLine* fl, AstPragmaType pragType) - : AstNode(fl) { - m_pragType = pragType; + : AstNode(fl) { + m_pragType = pragType; } ASTNODE_NODE_FUNCS(Pragma) - AstPragmaType pragType() const { return m_pragType; } // *=type of the pragma + AstPragmaType pragType() const { return m_pragType; } // *=type of the pragma virtual V3Hash sameHash() const { return V3Hash(pragType()); } virtual bool isPredictOptimizable() const { return false; } virtual bool same(const AstNode* samep) const { - return pragType() == static_cast(samep)->pragType(); } + return pragType() == static_cast(samep)->pragType(); } }; class AstStop : public AstNodeStmt { public: explicit AstStop(FileLine* fl) - : AstNodeStmt(fl) {} + : AstNodeStmt(fl) {} ASTNODE_NODE_FUNCS(Stop) virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering - virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output + virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output virtual bool isUnlikely() const { return true; } - virtual int instrCount() const { return 0; } // Rarely executes + virtual int instrCount() const { return 0; } // Rarely executes virtual V3Hash sameHash() const { return V3Hash(fileline()->lineno()); } virtual bool same(const AstNode* samep) const { - return fileline() == samep->fileline(); } + return fileline() == samep->fileline(); } }; class AstFinish : public AstNodeStmt { public: explicit AstFinish(FileLine* fl) - : AstNodeStmt(fl) {} + : AstNodeStmt(fl) {} ASTNODE_NODE_FUNCS(Finish) virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering - virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output + virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering + virtual bool isOutputter() const { return true; } // SPECIAL: $display makes output virtual bool isUnlikely() const { return true; } - virtual int instrCount() const { return 0; } // Rarely executes + virtual int instrCount() const { return 0; } // Rarely executes virtual V3Hash sameHash() const { return V3Hash(fileline()->lineno()); } virtual bool same(const AstNode* samep) const { - return fileline() == samep->fileline(); } + return fileline() == samep->fileline(); } }; class AstTraceDecl : public AstNodeStmt { @@ -3428,11 +3452,11 @@ class AstTraceDecl : public AstNodeStmt { // Parents: {statement list} // Children: none private: - string m_showname; // Name of variable - uint32_t m_code; // Trace identifier code; converted to ASCII by trace routines - VNumRange m_bitRange; // Property of var the trace details - VNumRange m_arrayRange; // Property of var the trace details - uint32_t m_codeInc; // Code increment + string m_showname; // Name of variable + uint32_t m_code; // Trace identifier code; converted to ASCII by trace routines + VNumRange m_bitRange; // Property of var the trace details + VNumRange m_arrayRange; // Property of var the trace details + uint32_t m_codeInc; // Code increment AstVarType m_varType; // Type of variable (for localparam vs. param) AstBasicDTypeKwd m_declKwd; // Keyword at declaration time VDirection m_declDirection; // Declared direction input/output etc @@ -3440,13 +3464,13 @@ public: AstTraceDecl(FileLine* fl, const string& showname, AstVar* varp, // For input/output state etc AstNode* valuep, - const VNumRange& bitRange, const VNumRange& arrayRange) - : AstNodeStmt(fl) - , m_showname(showname), m_bitRange(bitRange), m_arrayRange(arrayRange) { - dtypeFrom(valuep); - m_code = 0; - m_codeInc = ((arrayRange.ranged() ? arrayRange.elements() : 1) - * valuep->dtypep()->widthWords()); + const VNumRange& bitRange, const VNumRange& arrayRange) + : AstNodeStmt(fl) + , m_showname(showname), m_bitRange(bitRange), m_arrayRange(arrayRange) { + dtypeFrom(valuep); + m_code = 0; + m_codeInc = ((arrayRange.ranged() ? arrayRange.elements() : 1) + * valuep->dtypep()->widthWords()); m_varType = varp->varType(); m_declKwd = varp->declKwd(); m_declDirection = varp->declDirection(); @@ -3457,10 +3481,10 @@ public: virtual bool maybePointedTo() const { return true; } virtual bool hasDType() const { return true; } virtual bool same(const AstNode* samep) const { return false; } - string showname() const { return m_showname; } // * = Var name + string showname() const { return m_showname; } // * = Var name // Details on what we're tracing uint32_t code() const { return m_code; } - void code(uint32_t code) { m_code=code; } + void code(uint32_t code) { m_code = code; } uint32_t codeInc() const { return m_codeInc; } const VNumRange& bitRange() const { return m_bitRange; } const VNumRange& arrayRange() const { return m_arrayRange; } @@ -3474,33 +3498,33 @@ class AstTraceInc : public AstNodeStmt { // Parents: {statement list} // Children: incremental value private: - AstTraceDecl* m_declp; // [After V3Trace] Pointer to declaration + AstTraceDecl* m_declp; // [After V3Trace] Pointer to declaration public: AstTraceInc(FileLine* fl, AstTraceDecl* declp, AstNode* valuep) - : AstNodeStmt(fl) { - dtypeFrom(declp); - m_declp = declp; - addNOp2p(valuep); + : AstNodeStmt(fl) { + dtypeFrom(declp); + m_declp = declp; + addNOp2p(valuep); } ASTNODE_NODE_FUNCS(TraceInc) virtual const char* broken() const { BROKEN_RTN(!declp()->brokeExists()); return NULL; } virtual void cloneRelink() { if (m_declp->clonep()) m_declp = m_declp->clonep(); } virtual void dump(std::ostream& str); - virtual int instrCount() const { return 10+2*instrCountLd(); } + virtual int instrCount() const { return 10+2*instrCountLd(); } virtual bool hasDType() const { return true; } virtual V3Hash sameHash() const { return V3Hash(declp()); } virtual bool same(const AstNode* samep) const { - return declp() == static_cast(samep)->declp(); } + return declp() == static_cast(samep)->declp(); } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } virtual bool isOutputter() const { return true; } // but isPure() true // op1 = Statements before the value - AstNode* precondsp() const { return op1p(); } // op1= prepare statements for condition (exec every loop) - void addPrecondsp(AstNode* newp) { addOp1p(newp); } + AstNode* precondsp() const { return op1p(); } // op1 = prepare statements for condition (exec every loop) + void addPrecondsp(AstNode* newp) { addOp1p(newp); } // op2 = Value to trace - AstTraceDecl* declp() const { return m_declp; } // Where defined - AstNode* valuep() const { return op2p(); } + AstTraceDecl* declp() const { return m_declp; } // Where defined + AstNode* valuep() const { return op2p(); } }; class AstActive : public AstNode { @@ -3508,33 +3532,33 @@ class AstActive : public AstNode { // Parents: MODULE | CFUNC // Children: SENTREE, statements private: - string m_name; - AstSenTree* m_sensesp; + string m_name; + AstSenTree* m_sensesp; public: AstActive(FileLine* fileline, const string& name, AstSenTree* sensesp) - : AstNode(fileline) { - m_name = name; // Copy it - UASSERT(sensesp, "Sensesp required arg"); - m_sensesp = sensesp; + : AstNode(fileline) { + m_name = name; // Copy it + UASSERT(sensesp, "Sensesp required arg"); + m_sensesp = sensesp; } ASTNODE_NODE_FUNCS(Active) virtual void dump(std::ostream& str=std::cout); - virtual string name() const { return m_name; } + virtual string name() const { return m_name; } virtual const char* broken() const { BROKEN_RTN(m_sensesp && !m_sensesp->brokeExists()); return NULL; } virtual void cloneRelink() { - if (m_sensesp->clonep()) { - m_sensesp = m_sensesp->clonep(); - UASSERT(m_sensesp, "Bad clone cross link: "<clonep()) { + m_sensesp = m_sensesp->clonep(); + UASSERT(m_sensesp, "Bad clone cross link: "<hasInitial(); } @@ -3545,17 +3569,17 @@ public: class AstAttrOf : public AstNode { private: // Return a value of a attribute, for example a LSB or array LSB of a signal - AstAttrType m_attrType; // What sort of extraction + AstAttrType m_attrType; // What sort of extraction public: AstAttrOf(FileLine* fl, AstAttrType attrtype, AstNode* fromp=NULL, AstNode* dimp=NULL) - : AstNode(fl) { - setNOp1p(fromp); - setNOp2p(dimp); - m_attrType = attrtype; } + : AstNode(fl) { + setNOp1p(fromp); + setNOp2p(dimp); + m_attrType = attrtype; } ASTNODE_NODE_FUNCS(AttrOf) - AstNode* fromp() const { return op1p(); } - AstNode* dimp() const { return op2p(); } - AstAttrType attrType() const { return m_attrType; } + AstNode* fromp() const { return op1p(); } + AstNode* dimp() const { return op2p(); } + AstAttrType attrType() const { return m_attrType; } virtual void dump(std::ostream& str=std::cout); }; @@ -3564,16 +3588,16 @@ class AstScopeName : public AstNodeMath { // Parents: DISPLAY // Children: TEXT private: - bool m_dpiExport; // Is for dpiExport + bool m_dpiExport; // Is for dpiExport string scopeNameFormatter(AstText* scopeTextp) const; string scopePrettyNameFormatter(AstText* scopeTextp) const; public: explicit AstScopeName(FileLine* fl) : AstNodeMath(fl), m_dpiExport(false) { - dtypeSetUInt64(); } + dtypeSetUInt64(); } ASTNODE_NODE_FUNCS(ScopeName) virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { - return m_dpiExport == static_cast(samep)->m_dpiExport; } + return m_dpiExport == static_cast(samep)->m_dpiExport; } virtual string emitVerilog() { return ""; } virtual string emitC() { V3ERROR_NA; return ""; } virtual bool cleanOut() { return true; } @@ -3590,26 +3614,26 @@ public: string scopePrettyDpiName() const { // Name for __Vscope variable including children return scopePrettyNameFormatter(scopeEntrp()); } bool dpiExport() const { return m_dpiExport; } - void dpiExport(bool flag) { m_dpiExport=flag; } + void dpiExport(bool flag) { m_dpiExport = flag; } }; class AstUdpTable : public AstNode { public: AstUdpTable(FileLine* fl, AstNode* bodysp) - : AstNode(fl) { - addNOp1p(bodysp); + : AstNode(fl) { + addNOp1p(bodysp); } ASTNODE_NODE_FUNCS(UdpTable) AstUdpTableLine* bodysp() const { return VN_CAST(op1p(), UdpTableLine); } // op1 = List of UdpTableLines }; class AstUdpTableLine : public AstNode { - string m_text; + string m_text; public: AstUdpTableLine(FileLine* fl, const string& text) - : AstNode(fl), m_text(text) {} + : AstNode(fl), m_text(text) {} ASTNODE_NODE_FUNCS(UdpTableLine) - virtual string name() const { return m_text; } + virtual string name() const { return m_text; } string text() const { return m_text; } }; @@ -3619,21 +3643,21 @@ public: class AstRand : public AstNodeTermop { // Return a random number, based upon width() private: - bool m_reset; // Random reset, versus always random + bool m_reset; // Random reset, versus always random public: AstRand(FileLine* fl, AstNodeDType* dtp, bool reset) : AstNodeTermop(fl) { - dtypep(dtp); m_reset=reset; } + dtypep(dtp); m_reset = reset; } explicit AstRand(FileLine* fl) : AstNodeTermop(fl), m_reset(false) { } ASTNODE_NODE_FUNCS(Rand) virtual string emitVerilog() { return "%f$random"; } virtual string emitC() { - return (m_reset ? - "VL_RAND_RESET_%nq(%nw, %P)" - :"VL_RANDOM_%nq(%nw, %P)"); } + return (m_reset ? + "VL_RAND_RESET_%nq(%nw, %P)" + :"VL_RANDOM_%nq(%nw, %P)"); } virtual bool cleanOut() { return true; } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual int instrCount() const { return instrCountPli(); } + virtual int instrCount() const { return instrCountPli(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3641,14 +3665,14 @@ public: class AstTime : public AstNodeTermop { public: explicit AstTime(FileLine* fl) : AstNodeTermop(fl) { - dtypeSetUInt64(); } + dtypeSetUInt64(); } ASTNODE_NODE_FUNCS(Time) virtual string emitVerilog() { return "%f$time"; } virtual string emitC() { return "VL_TIME_%nq()"; } virtual bool cleanOut() { return true; } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual int instrCount() const { return instrCountTime(); } + virtual int instrCount() const { return instrCountTime(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3656,14 +3680,14 @@ public: class AstTimeD : public AstNodeTermop { public: explicit AstTimeD(FileLine* fl) : AstNodeTermop(fl) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(TimeD) virtual string emitVerilog() { return "%f$realtime"; } virtual string emitC() { return "VL_TIME_D()"; } virtual bool cleanOut() { return true; } virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual int instrCount() const { return instrCountTime(); } + virtual int instrCount() const { return instrCountTime(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3673,20 +3697,20 @@ class AstUCFunc : public AstNodeMath { // Perhaps this should be an AstNodeListop; but there's only one list math right now public: AstUCFunc(FileLine* fl, AstNode* exprsp) - : AstNodeMath(fl) { - addNOp1p(exprsp); + : AstNodeMath(fl) { + addNOp1p(exprsp); } ASTNODE_NODE_FUNCS(UCFunc) virtual bool cleanOut() { return false; } virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially virtual string emitC() { V3ERROR_NA; return ""; } - AstNode* bodysp() const { return op1p(); } // op1= expressions to print - virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs + AstNode* bodysp() const { return op1p(); } // op1 = expressions to print + virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs virtual bool isOutputter() const { return true; } virtual bool isGateOptimizable() const { return false; } virtual bool isSubstOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } - virtual int instrCount() const { return instrCountPli(); } + virtual int instrCount() const { return instrCountPli(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -3697,76 +3721,81 @@ public: class AstNegate : public AstNodeUniop { public: AstNegate(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Negate) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opNegate(lhs); } virtual string emitVerilog() { return "%f(- %l)"; } virtual string emitC() { return "VL_NEGATE_%lq(%lW, %P, %li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} - virtual bool sizeMattersLhs() {return true;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool sizeMattersLhs() { return true; } }; class AstNegateD : public AstNodeUniop { public: AstNegateD(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(NegateD) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opNegateD(lhs); } virtual string emitVerilog() { return "%f(- %l)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "-"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return false;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstRedAnd : public AstNodeUniop { public: AstRedAnd(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(RedAnd) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedAnd(lhs); } virtual string emitVerilog() { return "%f(& %l)"; } virtual string emitC() { return "VL_REDAND_%nq%lq(%nw,%lw, %P, %li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } }; class AstRedOr : public AstNodeUniop { public: AstRedOr(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(RedOr) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedOr(lhs); } virtual string emitVerilog() { return "%f(| %l)"; } virtual string emitC() { return "VL_REDOR_%lq(%lW, %P, %li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } }; class AstRedXor : public AstNodeUniop { public: AstRedXor(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(RedXor) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedXor(lhs); } virtual string emitVerilog() { return "%f(^ %l)"; } virtual string emitC() { return "VL_REDXOR_%lq(%lW, %P, %li)"; } - virtual bool cleanOut() {return false;} + virtual bool cleanOut() { return false; } virtual bool cleanLhs() {int w = lhsp()->width(); - return (w!=1 && w!=2 && w!=4 && w!=8 && w!=16); } - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return 1+V3Number::log2b(width()); } + return (w!=1 && w!=2 && w!=4 && w!=8 && w!=16); } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return 1+V3Number::log2b(width()); } }; class AstRedXnor : public AstNodeUniop { // AstRedXnors are replaced with AstRedXors in V3Const. public: AstRedXnor(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(RedXnor) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRedXnor(lhs); } virtual string emitVerilog() { return "%f(~^ %l)"; } virtual string emitC() { v3fatalSrc("REDXNOR should have became REDXOR"); return ""; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return 1+V3Number::log2b(width()); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return 1+V3Number::log2b(width()); } }; class AstLenN : public AstNodeUniop { @@ -3778,153 +3807,165 @@ public: virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opLenN(lhs); } virtual string emitVerilog() { return "%f(%l)"; } virtual string emitC() { return "VL_LEN_IN(%li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } }; class AstLogNot : public AstNodeUniop { public: AstLogNot(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LogNot) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opLogNot(lhs); } virtual string emitVerilog() { return "%f(! %l)"; } virtual string emitC() { return "VL_LOGNOT_%nq%lq(%nw,%lw, %P, %li)"; } virtual string emitSimpleOperator() { return "!"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } }; class AstNot : public AstNodeUniop { public: AstNot(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Not) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opNot(lhs); } virtual string emitVerilog() { return "%f(~ %l)"; } virtual string emitC() { return "VL_NOT_%lq(%lW, %P, %li)"; } virtual string emitSimpleOperator() { return "~"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} - virtual bool sizeMattersLhs() {return true;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool sizeMattersLhs() { return true; } }; class AstExtend : public AstNodeUniop { // Expand a value into a wider entity by 0 extension. Width is implied from nodep->width() public: AstExtend(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {} AstExtend(FileLine* fl, AstNode* lhsp, int width) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicSized(width,width,AstNumeric::UNSIGNED); } + dtypeSetLogicSized(width, width, AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(Extend) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); } virtual string emitVerilog() { return "%l"; } virtual string emitC() { return "VL_EXTEND_%nq%lq(%nw,%lw, %P, %li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} // Because the EXTEND operator self-casts - virtual int instrCount() const { return 0; } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } // Because the EXTEND operator self-casts + virtual int instrCount() const { return 0; } }; class AstExtendS : public AstNodeUniop { // Expand a value into a wider entity by sign extension. Width is implied from nodep->width() public: AstExtendS(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {} AstExtendS(FileLine* fl, AstNode* lhsp, int width) : AstNodeUniop(fl, lhsp) { - // Important that widthMin be correct, as opExtend requires it after V3Expand - dtypeSetLogicSized(width,width,AstNumeric::UNSIGNED); } + // Important that widthMin be correct, as opExtend requires it after V3Expand + dtypeSetLogicSized(width, width, AstNumeric::UNSIGNED); } ASTNODE_NODE_FUNCS(ExtendS) virtual void numberOperate(V3Number& out, const V3Number& lhs) { - out.opExtendS(lhs, lhsp()->widthMinV()); + out.opExtendS(lhs, lhsp()->widthMinV()); } virtual string emitVerilog() { return "%l"; } virtual string emitC() { return "VL_EXTENDS_%nq%lq(%nw,%lw, %P, %li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} // Because the EXTEND operator self-casts - virtual int instrCount() const { return 0; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } // Because the EXTEND operator self-casts + virtual int instrCount() const { return 0; } virtual bool signedFlavor() const { return true; } }; class AstSigned : public AstNodeUniop { // $signed(lhs) public: AstSigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); } + if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); } } ASTNODE_NODE_FUNCS(Signed) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); } virtual string emitVerilog() { return "%f$signed(%l)"; } virtual string emitC() { V3ERROR_NA; return ""; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters - virtual bool sizeMattersLhs() {return true;} // Eliminated before matters - virtual int instrCount() const { return 0; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } // Eliminated before matters + virtual bool sizeMattersLhs() { return true; } // Eliminated before matters + virtual int instrCount() const { return 0; } }; class AstUnsigned : public AstNodeUniop { // $unsigned(lhs) public: AstUnsigned(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); } + if (v3Global.assertDTypesResolved()) { v3fatalSrc("not coded to create after dtypes resolved"); } } ASTNODE_NODE_FUNCS(Unsigned) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); out.isSigned(false); } virtual string emitVerilog() { return "%f$unsigned(%l)"; } virtual string emitC() { V3ERROR_NA; return ""; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters - virtual bool sizeMattersLhs() {return true;} // Eliminated before matters - virtual int instrCount() const { return 0; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } // Eliminated before matters + virtual bool sizeMattersLhs() { return true; } // Eliminated before matters + virtual int instrCount() const { return 0; } }; class AstRToIS : public AstNodeUniop { // $rtoi(lhs) public: AstRToIS(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetSigned32(); } + dtypeSetSigned32(); } ASTNODE_NODE_FUNCS(RToIS) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRToIS(lhs); } virtual string emitVerilog() { return "%f$rtoi(%l)"; } virtual string emitC() { return "VL_RTOI_I_D(%li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters - virtual bool sizeMattersLhs() {return false;} // Eliminated before matters - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } // Eliminated before matters + virtual bool sizeMattersLhs() { return false; } // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } }; class AstRToIRoundS : public AstNodeUniop { public: AstRToIRoundS(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetSigned32(); } + dtypeSetSigned32(); } ASTNODE_NODE_FUNCS(RToIRoundS) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRToIRoundS(lhs); } virtual string emitVerilog() { return "%f$rtoi_rounded(%l)"; } virtual string emitC() { return "VL_RTOIROUND_I_D(%li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters - virtual bool sizeMattersLhs() {return false;} // Eliminated before matters - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } // Eliminated before matters + virtual bool sizeMattersLhs() { return false; } // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } }; class AstIToRD : public AstNodeUniop { public: AstIToRD(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(IToRD) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opIToRD(lhs); } virtual string emitVerilog() { return "%f$itor(%l)"; } virtual string emitC() { return "VL_ITOR_D_I(%li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters - virtual bool sizeMattersLhs() {return false;} // Eliminated before matters - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } // Eliminated before matters + virtual bool sizeMattersLhs() { return false; } // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } }; class AstRealToBits : public AstNodeUniop { public: AstRealToBits(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetUInt64(); } + dtypeSetUInt64(); } ASTNODE_NODE_FUNCS(RealToBits) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opRealToBits(lhs); } virtual string emitVerilog() { return "%f$realtobits(%l)"; } virtual string emitC() { return "VL_CVT_Q_D(%li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters - virtual bool sizeMattersLhs() {return false;} // Eliminated before matters - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } // Eliminated before matters + virtual bool sizeMattersLhs() { return false; } // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } }; class AstBitsToRealD : public AstNodeUniop { public: AstBitsToRealD(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(BitsToRealD) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opBitsToRealD(lhs); } virtual string emitVerilog() { return "%f$bitstoreal(%l)"; } virtual string emitC() { return "VL_CVT_D_Q(%li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} // Eliminated before matters - virtual bool sizeMattersLhs() {return false;} // Eliminated before matters - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } // Eliminated before matters + virtual bool sizeMattersLhs() { return false; } // Eliminated before matters + virtual int instrCount() const { return instrCountDouble(); } }; class AstCLog2 : public AstNodeUniop { @@ -3934,9 +3975,10 @@ public: virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opCLog2(lhs); } virtual string emitVerilog() { return "%f$clog2(%l)"; } virtual string emitC() { return "VL_CLOG2_%lq(%lW, %P, %li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return widthInstrs()*16; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return widthInstrs()*16; } }; class AstCountOnes : public AstNodeUniop { // Number of bits set in vector @@ -3946,62 +3988,67 @@ public: virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opCountOnes(lhs); } virtual string emitVerilog() { return "%f$countones(%l)"; } virtual string emitC() { return "VL_COUNTONES_%lq(%lW, %P, %li)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return widthInstrs()*16; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return widthInstrs()*16; } }; class AstIsUnknown : public AstNodeUniop { // True if any unknown bits public: AstIsUnknown(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(IsUnknown) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opIsUnknown(lhs); } virtual string emitVerilog() { return "%f$isunknown(%l)"; } virtual string emitC() { V3ERROR_NA; return ""; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return false;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool sizeMattersLhs() { return false; } }; class AstOneHot : public AstNodeUniop { // True if only single bit set in vector public: AstOneHot(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(OneHot) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opOneHot(lhs); } virtual string emitVerilog() { return "%f$onehot(%l)"; } virtual string emitC() { return "VL_ONEHOT_%lq(%lW, %P, %li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return widthInstrs()*4; } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return widthInstrs()*4; } }; class AstOneHot0 : public AstNodeUniop { // True if only single bit, or no bits set in vector public: AstOneHot0(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(OneHot0) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opOneHot0(lhs); } virtual string emitVerilog() { return "%f$onehot0(%l)"; } virtual string emitC() { return "VL_ONEHOT0_%lq(%lW, %P, %li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return widthInstrs()*3; } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return widthInstrs()*3; } }; class AstCast : public AstNode { // Cast to appropriate data type - note lhsp is value, to match AstTypedef, AstCCast, etc public: AstCast(FileLine* fl, AstNode* lhsp, AstNodeDType* dtp) : AstNode(fl) { - setOp1p(lhsp); setOp2p(dtp); - dtypeFrom(dtp); + setOp1p(lhsp); setOp2p(dtp); + dtypeFrom(dtp); } ASTNODE_NODE_FUNCS(Cast) virtual bool hasDType() const { return true; } virtual string emitVerilog() { return "((%d)'(%l))"; } virtual string emitC() { V3ERROR_NA; return ""; } - virtual bool cleanOut() { V3ERROR_NA; return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { V3ERROR_NA; return true;} + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } AstNode* lhsp() const { return op1p(); } AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op2p(), NodeDType); } @@ -4011,13 +4058,14 @@ class AstCastParse : public AstNode { // Cast to appropriate type, where we haven't determined yet what the data type is public: AstCastParse(FileLine* fl, AstNode* lhsp, AstNode* dtp) : AstNode(fl) { - setOp1p(lhsp); setOp2p(dtp); + setOp1p(lhsp); setOp2p(dtp); } ASTNODE_NODE_FUNCS(CastParse) virtual string emitVerilog() { return "((%d)'(%l))"; } virtual string emitC() { V3ERROR_NA; return ""; } - virtual bool cleanOut() { V3ERROR_NA; return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { V3ERROR_NA; return true;} + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } AstNode* lhsp() const { return op1p(); } AstNode* dtp() const { return op2p(); } }; @@ -4026,14 +4074,15 @@ class AstCastSize : public AstNode { // Cast to specific size; signed/twostate inherited from lower element per IEEE public: AstCastSize(FileLine* fl, AstNode* lhsp, AstConst* rhsp) : AstNode(fl) { - setOp1p(lhsp); setOp2p(rhsp); + setOp1p(lhsp); setOp2p(rhsp); } ASTNODE_NODE_FUNCS(CastSize) // No hasDType because widthing removes this node before the hasDType check virtual string emitVerilog() { return "((%r)'(%l))"; } virtual string emitC() { V3ERROR_NA; return ""; } - virtual bool cleanOut() { V3ERROR_NA; return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { V3ERROR_NA; return true;} + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } AstNode* lhsp() const { return op1p(); } AstNode* rhsp() const { return op2p(); } }; @@ -4041,44 +4090,46 @@ public: class AstCCast : public AstNodeUniop { // Cast to C-based data type private: - int m_size; + int m_size; public: AstCCast(FileLine* fl, AstNode* lhsp, int setwidth, int minwidth=-1) : AstNodeUniop(fl, lhsp) { - m_size=setwidth; - if (setwidth) { - if (minwidth==-1) minwidth=setwidth; - dtypeSetLogicSized(setwidth,minwidth,AstNumeric::UNSIGNED); - } + m_size = setwidth; + if (setwidth) { + if (minwidth==-1) minwidth = setwidth; + dtypeSetLogicSized(setwidth, minwidth, AstNumeric::UNSIGNED); + } } AstCCast(FileLine* fl, AstNode* lhsp, AstNode* typeFromp) : AstNodeUniop(fl, lhsp) { - dtypeFrom(typeFromp); - m_size=width(); + dtypeFrom(typeFromp); + m_size = width(); } ASTNODE_NODE_FUNCS(CCast) virtual void numberOperate(V3Number& out, const V3Number& lhs) { out.opAssign(lhs); } virtual string emitVerilog() { return "%f$_CAST(%l)"; } virtual string emitC() { return "VL_CAST_%nq%lq(%nw,%lw, %P, %li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} // Special cased in V3Cast + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } // Special cased in V3Cast virtual V3Hash sameHash() const { return V3Hash(size()); } virtual bool same(const AstNode* samep) const { - return size() == static_cast(samep)->size(); } + return size() == static_cast(samep)->size(); } virtual void dump(std::ostream& str=std::cout); // - int size() const { return m_size; } + int size() const { return m_size; } }; class AstCvtPackString : public AstNodeUniop { // Convert to Verilator Packed String (aka verilog "string") public: AstCvtPackString(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { - dtypeSetString(); } // Really, width should be dtypep -> STRING + dtypeSetString(); } // Really, width should be dtypep -> STRING ASTNODE_NODE_FUNCS(CvtPackString) virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; } virtual string emitVerilog() { return "%f$_CAST(%l)"; } virtual string emitC() { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } }; @@ -4090,11 +4141,12 @@ public: virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; } virtual string emitVerilog() { return "%f$feof(%l)"; } virtual string emitC() { return "(%li ? feof(VL_CVT_I_FP(%li)) : true)"; } - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return widthInstrs()*16; } - virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering - AstNode* filep() const { return lhsp(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return widthInstrs()*16; } + virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering + AstNode* filep() const { return lhsp(); } }; class AstFGetC : public AstNodeUniop { @@ -4105,11 +4157,12 @@ public: virtual string emitVerilog() { return "%f$fgetc(%l)"; } // Non-existent filehandle returns EOF virtual string emitC() { return "(%li ? fgetc(VL_CVT_I_FP(%li)) : -1)"; } - virtual bool cleanOut() {return false;} virtual bool cleanLhs() {return true;} - virtual bool sizeMattersLhs() {return false;} - virtual int instrCount() const { return widthInstrs()*64; } - virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering - AstNode* filep() const { return lhsp(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual int instrCount() const { return widthInstrs()*64; } + virtual bool isPure() const { return false; } // SPECIAL: $display has 'visual' ordering + AstNode* filep() const { return lhsp(); } }; class AstNodeSystemUniop : public AstNodeUniop { @@ -4117,8 +4170,9 @@ public: AstNodeSystemUniop(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) { dtypeSetDouble(); } ASTNODE_BASE_FUNCS(NodeSystemUniop) - virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return false;} - virtual bool sizeMattersLhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool sizeMattersLhs() { return false; } virtual int instrCount() const { return instrCountDoubleTrig(); } virtual bool doubleFlavor() const { return true; } }; @@ -4290,861 +4344,972 @@ public: class AstLogOr : public AstNodeBiop { public: AstLogOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LogOr) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLogOr(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogOr(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogOr(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f|| %r)"; } virtual string emitC() { return "VL_LOGOR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "||"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } }; class AstLogAnd : public AstNodeBiop { public: AstLogAnd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LogAnd) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLogAnd(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogAnd(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogAnd(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f&& %r)"; } virtual string emitC() { return "VL_LOGAND_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "&&"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } }; class AstLogIf : public AstNodeBiop { public: AstLogIf(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LogIf) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLogIf(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogIf(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogIf(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f-> %r)"; } virtual string emitC() { return "VL_LOGIF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "->"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } }; class AstLogIff : public AstNodeBiCom { public: AstLogIff(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LogIff) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLogIff(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogIff(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLogIff(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f<-> %r)"; } virtual string emitC() { return "VL_LOGIFF_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "<->"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()+instrCountBranch(); } }; class AstOr : public AstNodeBiComAsv { public: AstOr(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Or) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstOr(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opOr(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opOr(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f| %r)"; } virtual string emitC() { return "VL_OR_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "|"; } virtual bool cleanOut() {V3ERROR_NA; return false;} // Lclean && Rclean - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstAnd : public AstNodeBiComAsv { public: AstAnd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(And) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAnd(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAnd(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAnd(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f& %r)"; } virtual string emitC() { return "VL_AND_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "&"; } virtual bool cleanOut() {V3ERROR_NA; return false;} // Lclean || Rclean - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstXor : public AstNodeBiComAsv { public: AstXor(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Xor) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstXor(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opXor(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opXor(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f^ %r)"; } virtual string emitC() { return "VL_XOR_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "^"; } - virtual bool cleanOut() {return false;} // Lclean && Rclean - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return false; } // Lclean && Rclean + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstXnor : public AstNodeBiComAsv { public: AstXnor(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Xnor) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstXnor(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opXnor(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opXnor(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f^ ~ %r)"; } virtual string emitC() { return "VL_XNOR_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "^ ~"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } }; class AstEq : public AstNodeBiCom { public: AstEq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Eq) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstEq(this->fileline(), lhsp, rhsp); } static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstEq/AstEqD - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEq(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEq(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f== %r)"; } virtual string emitC() { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "=="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstEqD : public AstNodeBiCom { public: AstEqD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(EqD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstEqD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEqD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEqD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f== %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "=="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstEqN : public AstNodeBiCom { public: AstEqN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(EqN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstEqN(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEqN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEqN(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f== %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "=="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountString(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountString(); } virtual bool stringFlavor() const { return true; } }; class AstNeq : public AstNodeBiCom { public: AstNeq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Neq) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstNeq(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeq(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeq(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f!= %r)"; } virtual string emitC() { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "!="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstNeqD : public AstNodeBiCom { public: AstNeqD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(NeqD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstNeqD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeqD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeqD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f!= %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "!="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstNeqN : public AstNodeBiCom { public: AstNeqN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(NeqN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstNeqN(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeqN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opNeqN(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f!= %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "!="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountString(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountString(); } virtual bool stringFlavor() const { return true; } }; class AstLt : public AstNodeBiop { public: AstLt(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Lt) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLt(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLt(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLt(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f< %r)"; } virtual string emitC() { return "VL_LT_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "<"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstLtD : public AstNodeBiop { public: AstLtD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LtD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLtD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f< %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "<"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstLtS : public AstNodeBiop { public: AstLtS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LtS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLtS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f< %r)"; } virtual string emitC() { return "VL_LTS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ""; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual bool signedFlavor() const { return true; } }; class AstLtN : public AstNodeBiop { public: AstLtN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LtN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLtN(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLtN(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f< %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "<"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountString(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountString(); } virtual bool stringFlavor() const { return true; } }; class AstGt : public AstNodeBiop { public: AstGt(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Gt) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGt(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGt(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGt(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f> %r)"; } virtual string emitC() { return "VL_GT_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ">"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstGtD : public AstNodeBiop { public: AstGtD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(GtD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGtD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f> %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return ">"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstGtS : public AstNodeBiop { public: AstGtS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(GtS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGtS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f> %r)"; } virtual string emitC() { return "VL_GTS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ""; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual bool signedFlavor() const { return true; } }; class AstGtN : public AstNodeBiop { public: AstGtN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(GtN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGtN(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGtN(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f> %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return ">"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountString(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountString(); } virtual bool stringFlavor() const { return true; } }; class AstGte : public AstNodeBiop { public: AstGte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Gte) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGte(this->fileline(), lhsp, rhsp); } static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstGte/AstGteS/AstGteD - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGte(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGte(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f>= %r)"; } virtual string emitC() { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ">="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstGteD : public AstNodeBiop { public: AstGteD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(GteD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGteD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f>= %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return ">="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstGteS : public AstNodeBiop { public: AstGteS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(GteS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGteS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f>= %r)"; } virtual string emitC() { return "VL_GTES_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ""; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual bool signedFlavor() const { return true; } }; class AstGteN : public AstNodeBiop { public: AstGteN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(GteN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstGteN(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGteN(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f>= %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return ">="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountString(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountString(); } virtual bool stringFlavor() const { return true; } }; class AstLte : public AstNodeBiop { public: AstLte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Lte) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLte(this->fileline(), lhsp, rhsp); } static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstLte/AstLteS/AstLteD - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLte(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLte(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f<= %r)"; } virtual string emitC() { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "<="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstLteD : public AstNodeBiop { public: AstLteD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LteD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLteD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f<= %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "<="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstLteS : public AstNodeBiop { public: AstLteS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LteS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLteS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f<= %r)"; } virtual string emitC() { return "VL_LTES_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ""; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual bool signedFlavor() const { return true; } }; class AstLteN : public AstNodeBiop { public: AstLteN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(LteN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstLteN(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLteN(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f<= %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "<="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountString(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountString(); } virtual bool stringFlavor() const { return true; } }; class AstShiftL : public AstNodeBiop { public: AstShiftL(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0) - : AstNodeBiop(fl, lhsp, rhsp) { - if (setwidth) { dtypeSetLogicSized(setwidth,setwidth,AstNumeric::UNSIGNED); } + : AstNodeBiop(fl, lhsp, rhsp) { + if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::UNSIGNED); } } ASTNODE_NODE_FUNCS(ShiftL) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstShiftL(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftL(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftL(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f<< %r)"; } virtual string emitC() { return "VL_SHIFTL_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "<<"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return false; } }; class AstShiftR : public AstNodeBiop { public: AstShiftR(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0) - : AstNodeBiop(fl, lhsp, rhsp) { - if (setwidth) { dtypeSetLogicSized(setwidth,setwidth,AstNumeric::UNSIGNED); } + : AstNodeBiop(fl, lhsp, rhsp) { + if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::UNSIGNED); } } ASTNODE_NODE_FUNCS(ShiftR) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstShiftR(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftR(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opShiftR(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f>> %r)"; } virtual string emitC() { return "VL_SHIFTR_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ">>"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } // LHS size might be > output size, so don't want to force size - virtual bool sizeMattersLhs() {return false;} - virtual bool sizeMattersRhs() {return false;} + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstShiftRS : public AstNodeBiop { // Shift right with sign extension, >>> operator // Output data type's width determines which bit is used for sign extension public: AstShiftRS(FileLine* fl, AstNode* lhsp, AstNode* rhsp, int setwidth=0) - : AstNodeBiop(fl, lhsp, rhsp) { - // Important that widthMin be correct, as opExtend requires it after V3Expand - if (setwidth) { dtypeSetLogicSized(setwidth,setwidth,AstNumeric::SIGNED); } + : AstNodeBiop(fl, lhsp, rhsp) { + // Important that widthMin be correct, as opExtend requires it after V3Expand + if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::SIGNED); } } ASTNODE_NODE_FUNCS(ShiftRS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstShiftRS(this->fileline(), lhsp, rhsp); } virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { - out.opShiftRS(lhs,rhs,lhsp()->widthMinV()); } + out.opShiftRS(lhs, rhs, lhsp()->widthMinV()); } virtual string emitVerilog() { return "%k(%l %f>>> %r)"; } virtual string emitC() { return "VL_SHIFTRS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ""; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } virtual bool signedFlavor() const { return true; } }; class AstAdd : public AstNodeBiComAsv { public: AstAdd(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Add) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAdd(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAdd(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAdd(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f+ %r)"; } virtual string emitC() { return "VL_ADD_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "+"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } }; class AstAddD : public AstNodeBiComAsv { public: AstAddD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(AddD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAddD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAddD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAddD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f+ %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "+"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstSub : public AstNodeBiop { public: AstSub(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Sub) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstSub(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opSub(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opSub(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f- %r)"; } virtual string emitC() { return "VL_SUB_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "-"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } }; class AstSubD : public AstNodeBiop { public: AstSubD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(SubD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstSubD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opSubD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opSubD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f- %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "-"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstMul : public AstNodeBiComAsv { public: AstMul(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Mul) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstMul(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMul(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMul(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f* %r)"; } virtual string emitC() { return "VL_MUL_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "*"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} - virtual int instrCount() const { return widthInstrs()*instrCountMul(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } + virtual int instrCount() const { return widthInstrs()*instrCountMul(); } }; class AstMulD : public AstNodeBiComAsv { public: AstMulD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(MulD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstMulD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMulD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMulD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f* %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "*"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} - virtual int instrCount() const { return instrCountDouble(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } + virtual int instrCount() const { return instrCountDouble(); } virtual bool doubleFlavor() const { return true; } }; class AstMulS : public AstNodeBiComAsv { public: AstMulS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiComAsv(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(MulS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstMulS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMulS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opMulS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f* %r)"; } virtual string emitC() { return "VL_MULS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return ""; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} - virtual int instrCount() const { return widthInstrs()*instrCountMul(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } + virtual int instrCount() const { return widthInstrs()*instrCountMul(); } virtual bool signedFlavor() const { return true; } }; class AstDiv : public AstNodeBiop { public: AstDiv(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Div) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstDiv(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDiv(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDiv(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f/ %r)"; } virtual string emitC() { return "VL_DIV_%nq%lq%rq(%lw, %P, %li, %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} - virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } }; class AstDivD : public AstNodeBiop { public: AstDivD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(DivD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstDivD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDivD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDivD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f/ %r)"; } virtual string emitC() { V3ERROR_NA; return ""; } virtual string emitSimpleOperator() { return "/"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDoubleDiv(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDoubleDiv(); } virtual bool doubleFlavor() const { return true; } }; class AstDivS : public AstNodeBiop { public: AstDivS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(DivS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstDivS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDivS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opDivS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f/ %r)"; } virtual string emitC() { return "VL_DIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} - virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } virtual bool signedFlavor() const { return true; } }; class AstModDiv : public AstNodeBiop { public: AstModDiv(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(ModDiv) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstModDiv(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opModDiv(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opModDiv(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f%% %r)"; } virtual string emitC() { return "VL_MODDIV_%nq%lq%rq(%lw, %P, %li, %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} - virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } }; class AstModDivS : public AstNodeBiop { public: AstModDivS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(ModDivS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstModDivS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opModDivS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opModDivS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f%% %r)"; } virtual string emitC() { return "VL_MODDIVS_%nq%lq%rq(%lw, %P, %li, %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return true;} - virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return true; } + virtual int instrCount() const { return widthInstrs()*instrCountDiv(); } virtual bool signedFlavor() const { return true; } }; class AstPow : public AstNodeBiop { public: AstPow(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(Pow) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstPow(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPow(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPow(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f** %r)"; } virtual string emitC() { return "VL_POW_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } }; class AstPowD : public AstNodeBiop { public: AstPowD(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetDouble(); } + dtypeSetDouble(); } ASTNODE_NODE_FUNCS(PowD) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstPowD(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowD(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowD(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f** %r)"; } virtual string emitC() { return "pow(%li,%ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDoubleDiv()*5; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDoubleDiv()*5; } virtual bool doubleFlavor() const { return true; } }; class AstPowSU : public AstNodeBiop { public: AstPowSU(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(PowSU) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstPowSU(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowSU(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowSU(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f** %r)"; } virtual string emitC() { return "VL_POWSS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri, 1,0)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } virtual bool signedFlavor() const { return true; } }; class AstPowSS : public AstNodeBiop { public: AstPowSS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(PowSS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstPowSS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowSS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowSS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f** %r)"; } virtual string emitC() { return "VL_POWSS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri, 1,1)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } virtual bool signedFlavor() const { return true; } }; class AstPowUS : public AstNodeBiop { public: AstPowUS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(PowUS) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstPowUS(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowUS(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opPowUS(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f** %r)"; } virtual string emitC() { return "VL_POWSS_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri, 0,1)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*instrCountMul()*10; } virtual bool signedFlavor() const { return true; } }; class AstEqCase : public AstNodeBiCom { public: AstEqCase(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(EqCase) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstEqCase(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opCaseEq(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opCaseEq(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f=== %r)"; } virtual string emitC() { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "=="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstNeqCase : public AstNodeBiCom { public: AstNeqCase(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(NeqCase) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstNeqCase(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opCaseNeq(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opCaseNeq(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f!== %r)"; } virtual string emitC() { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "!="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstEqWild : public AstNodeBiop { // Note wildcard operator rhs differs from lhs public: AstEqWild(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(EqWild) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstEqWild(this->fileline(), lhsp, rhsp); } static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstEqWild/AstEqD - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opWildEq(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opWildEq(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f==? %r)"; } virtual string emitC() { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "=="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstNeqWild : public AstNodeBiop { public: AstNeqWild(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetLogicBool(); } + dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(NeqWild) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstNeqWild(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opWildNeq(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opWildNeq(lhs, rhs); } virtual string emitVerilog() { return "%k(%l %f!=? %r)"; } virtual string emitC() { return "VL_NEQ_%lq(%lW, %P, %li, %ri)"; } virtual string emitSimpleOperator() { return "!="; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstConcat : public AstNodeBiop { // If you're looking for {#{}}, see AstReplicate public: AstConcat(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - if (lhsp->dtypep() && rhsp->dtypep()) { - dtypeSetLogicSized(lhsp->dtypep()->width()+rhsp->dtypep()->width(), - lhsp->dtypep()->width()+rhsp->dtypep()->width(), - AstNumeric::UNSIGNED); - } + if (lhsp->dtypep() && rhsp->dtypep()) { + dtypeSetLogicSized(lhsp->dtypep()->width()+rhsp->dtypep()->width(), + lhsp->dtypep()->width()+rhsp->dtypep()->width(), + AstNumeric::UNSIGNED); + } } ASTNODE_NODE_FUNCS(Concat) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstConcat(this->fileline(), lhsp, rhsp); } virtual string emitVerilog() { return "%f{%l, %k%r}"; } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opConcat(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opConcat(lhs, rhs); } virtual string emitC() { return "VL_CONCAT_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*2; } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*2; } }; class AstConcatN : public AstNodeBiop { // String concatenate public: AstConcatN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeSetString(); + dtypeSetString(); } ASTNODE_NODE_FUNCS(ConcatN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstConcatN(this->fileline(), lhsp, rhsp); } virtual string emitVerilog() { return "%f{%l, %k%r}"; } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opConcatN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opConcatN(lhs, rhs); } virtual string emitC() { return "VL_CONCATN_NNN(%li, %ri)"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountString(); } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountString(); } virtual bool stringFlavor() const { return true; } }; class AstReplicate : public AstNodeBiop { @@ -5152,26 +5317,30 @@ class AstReplicate : public AstNodeBiop { // Verilog {rhs{lhs}} - Note rhsp() is the replicate value, not the lhsp() private: void init() { - if (lhsp()) { + if (lhsp()) { if (const AstConst* constp = VN_CAST(rhsp(), Const)) { - dtypeSetLogicSized(lhsp()->width()*constp->toUInt(), lhsp()->width()*constp->toUInt(), AstNumeric::UNSIGNED); - } - } + dtypeSetLogicSized(lhsp()->width()*constp->toUInt(), + lhsp()->width()*constp->toUInt(), + AstNumeric::UNSIGNED); + } + } } public: AstReplicate(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { init(); } AstReplicate(FileLine* fl, AstNode* lhsp, uint32_t repCount) - : AstNodeBiop(fl, lhsp, new AstConst(fl, repCount)) { init(); } + : AstNodeBiop(fl, lhsp, new AstConst(fl, repCount)) { init(); } ASTNODE_NODE_FUNCS(Replicate) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstReplicate(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opRepl(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opRepl(lhs, rhs); } virtual string emitVerilog() { return "%f{%r{%k%l}}"; } virtual string emitC() { return "VL_REPLICATE_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*2; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*2; } }; class AstReplicateN : public AstNodeBiop { // String replicate @@ -5181,16 +5350,18 @@ public: AstReplicateN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { init(); } AstReplicateN(FileLine* fl, AstNode* lhsp, uint32_t repCount) - : AstNodeBiop(fl, lhsp, new AstConst(fl, repCount)) { init(); } + : AstNodeBiop(fl, lhsp, new AstConst(fl, repCount)) { init(); } ASTNODE_NODE_FUNCS(ReplicateN) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstReplicateN(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opReplN(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opReplN(lhs, rhs); } virtual string emitVerilog() { return "%f{%r{%k%l}}"; } virtual string emitC() { return "VL_REPLICATEN_NN%rq(0,0,%rw, %li, %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*2; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*2; } virtual bool stringFlavor() const { return true; } }; class AstStreamL : public AstNodeStream { @@ -5200,12 +5371,14 @@ public: ASTNODE_NODE_FUNCS(StreamL) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstStreamL(this->fileline(), lhsp, rhsp); } virtual string emitVerilog() { return "%f{ << %r %k{%l} }"; } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opStreamL(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opStreamL(lhs, rhs); } virtual string emitC() { return "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; } - virtual bool cleanOut() {return true;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*2; } + virtual bool cleanOut() { return true; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*2; } }; class AstStreamR : public AstNodeStream { // Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp() @@ -5216,26 +5389,31 @@ public: virtual string emitVerilog() { return "%f{ >> %r %k{%l} }"; } virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAssign(lhs); } virtual string emitC() { return isWide() ? "VL_ASSIGN_W(%nw, %P, %li)" : "%li"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*2; } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return true; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*2; } }; class AstBufIf1 : public AstNodeBiop { // lhs is enable, rhs is data to drive - // Note unlike the Verilog bufif1() UDP, this allows any width; each lhsp bit enables respective rhsp bit + // Note unlike the Verilog bufif1() UDP, this allows any width; each lhsp + // bit enables respective rhsp bit public: AstBufIf1(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { - dtypeFrom(lhsp); } + dtypeFrom(lhsp); } ASTNODE_NODE_FUNCS(BufIf1) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstBufIf1(this->fileline(), lhsp, rhsp); } - virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opBufIf1(lhs,rhs); } + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opBufIf1(lhs, rhs); } virtual string emitVerilog() { return "bufif(%r,%l)"; } virtual string emitC() { V3ERROR_NA; return "";} // Lclean || Rclean virtual string emitSimpleOperator() { V3ERROR_NA; return "";} // Lclean || Rclean virtual bool cleanOut() {V3ERROR_NA; return "";} // Lclean || Rclean - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } }; class AstFGetS : public AstNodeBiop { public: @@ -5245,22 +5423,26 @@ public: virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; } virtual string emitVerilog() { return "%f$fgets(%l,%r)"; } virtual string emitC() { return "VL_FGETS_%nqX%rq(%lw, %P, &(%li), %ri)"; } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return widthInstrs()*64; } - AstNode* strgp() const { return lhsp(); } - AstNode* filep() const { return rhsp(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return true; } + virtual bool cleanRhs() { return true; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return widthInstrs()*64; } + AstNode* strgp() const { return lhsp(); } + AstNode* filep() const { return rhsp(); } }; class AstNodeSystemBiop : public AstNodeBiop { public: AstNodeSystemBiop(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { dtypeSetDouble(); } - virtual bool cleanOut() {return false;} - virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} - virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} - virtual int instrCount() const { return instrCountDoubleTrig(); } + virtual bool cleanOut() { return false; } + virtual bool cleanLhs() { return false; } + virtual bool cleanRhs() { return false; } + virtual bool sizeMattersLhs() { return false; } + virtual bool sizeMattersRhs() { return false; } + virtual int instrCount() const { return instrCountDoubleTrig(); } virtual bool doubleFlavor() const { return true; } }; @@ -5316,7 +5498,7 @@ class AstPattern : public AstNodeMath { // Children: expression, AstPattern, AstPatReplicate public: AstPattern(FileLine* fl, AstNode* itemsp) : AstNodeMath(fl) { - addNOp2p(itemsp); + addNOp2p(itemsp); } ASTNODE_NODE_FUNCS(Pattern) virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially @@ -5324,32 +5506,32 @@ public: virtual string emitC() { V3ERROR_NA; return "";} virtual string emitSimpleOperator() { V3ERROR_NA; return "";} virtual bool cleanOut() {V3ERROR_NA; return "";} - virtual int instrCount() const { return widthInstrs(); } + virtual int instrCount() const { return widthInstrs(); } AstNodeDType* getChildDTypep() const { return childDTypep(); } AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Type assigning to void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } virtual AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); } - AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc + AstNode* itemsp() const { return op2p(); } // op2 = AstPatReplicate, AstPatMember, etc }; class AstPatMember : public AstNodeMath { // Verilog '{a} or '{a{b}} // Parents: AstPattern // Children: expression, AstPattern, replication count private: - bool m_default; + bool m_default; public: AstPatMember(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* repp) : AstNodeMath(fl) { - addOp1p(lhsp), setNOp2p(keyp), setNOp3p(repp); m_default = false; } + addOp1p(lhsp), setNOp2p(keyp), setNOp3p(repp); m_default = false; } ASTNODE_NODE_FUNCS(PatMember) virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; } virtual string emitVerilog() { return lhssp()?"%f{%r{%k%l}}":"%l"; } virtual string emitC() { V3ERROR_NA; return "";} virtual string emitSimpleOperator() { V3ERROR_NA; return "";} virtual bool cleanOut() {V3ERROR_NA; return "";} - virtual int instrCount() const { return widthInstrs()*2; } - AstNode* lhssp() const { return op1p(); } // op1 = expression to assign or another AstPattern (list if replicated) - AstNode* keyp() const { return op2p(); } // op2 = assignment key (Const, id Text) - AstNode* repp() const { return op3p(); } // op3 = replication count, or NULL for count 1 + virtual int instrCount() const { return widthInstrs()*2; } + AstNode* lhssp() const { return op1p(); } // op1 = expression to assign or another AstPattern (list if replicated) + AstNode* keyp() const { return op2p(); } // op2 = assignment key (Const, id Text) + AstNode* repp() const { return op3p(); } // op3 = replication count, or NULL for count 1 bool isDefault() const { return m_default; } void isDefault(bool flag) { m_default = flag; } }; @@ -5363,17 +5545,17 @@ class AstVAssert : public AstNodeStmt { // Children: expression, if pass statements, if fail statements public: AstVAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp) - : AstNodeStmt(fl) { - addOp1p(propp); - addNOp2p(passsp); - addNOp3p(failsp); + : AstNodeStmt(fl) { + addOp1p(propp); + addNOp2p(passsp); + addNOp3p(failsp); } ASTNODE_NODE_FUNCS(VAssert) virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstNode* propp() const { return op1p(); } // op1 = property - AstNode* passsp() const { return op2p(); } // op2 = if passes - AstNode* failsp() const { return op3p(); } // op3 = if fails + AstNode* propp() const { return op1p(); } // op1 = property + AstNode* passsp() const { return op2p(); } // op2 = if passes + AstNode* failsp() const { return op3p(); } // op3 = if fails }; //====================================================================== @@ -5385,13 +5567,13 @@ class AstClocking : public AstNode { // Children: Assertions public: AstClocking(FileLine* fl, AstNodeSenItem* sensesp, AstNode* bodysp) - : AstNode(fl) { - addOp1p(sensesp); - addNOp2p(bodysp); + : AstNode(fl) { + addOp1p(sensesp); + addNOp2p(bodysp); } ASTNODE_NODE_FUNCS(Clocking) AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list - AstNode* bodysp() const { return op2p(); } // op2 = Body + AstNode* bodysp() const { return op2p(); } // op2 = Body }; //====================================================================== @@ -5403,16 +5585,16 @@ class AstPslClocked : public AstNode { // Children: SENITEM, Properties public: AstPslClocked(FileLine* fl, AstNodeSenItem* sensesp, AstNode* disablep, AstNode* propp) - : AstNode(fl) { - addNOp1p(sensesp); - addNOp2p(disablep); - addOp3p(propp); + : AstNode(fl) { + addNOp1p(sensesp); + addNOp2p(disablep); + addOp3p(propp); } ASTNODE_NODE_FUNCS(PslClocked) virtual bool hasDType() const { return true; } // Used under PslCover, which expects a bool child AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list - AstNode* disablep() const { return op2p(); } // op2 = disable - AstNode* propp() const { return op3p(); } // op3 = property + AstNode* disablep() const { return op2p(); } // op2 = disable + AstNode* propp() const { return op3p(); } // op3 = property }; class AstNodePslCoverOrAssert : public AstNodeStmt { @@ -5420,13 +5602,13 @@ class AstNodePslCoverOrAssert : public AstNodeStmt { // Parents: {statement list} // Children: expression, report string private: - string m_name; // Name to report + string m_name; // Name to report public: AstNodePslCoverOrAssert(FileLine* fl, AstNode* propp, AstNode* stmtsp, const string& name="") - : AstNodeStmt(fl) - , m_name(name) { - addOp1p(propp); - addNOp4p(stmtsp); + : AstNodeStmt(fl) + , m_name(name) { + addOp1p(propp); + addNOp4p(stmtsp); } ASTNODE_BASE_FUNCS(NodePslCoverOrAssert) virtual string name() const { return m_name; } // * = Var name @@ -5467,10 +5649,10 @@ public: class AstText : public AstNodeText { private: - bool m_tracking; // When emit, it's ok to parse the string to do indentation + bool m_tracking; // When emit, it's ok to parse the string to do indentation public: AstText(FileLine* fl, const string& textp, bool tracking=false) - : AstNodeText(fl, textp), m_tracking(tracking) {} + : AstNodeText(fl, textp), m_tracking(tracking) {} ASTNODE_NODE_FUNCS(Text) void tracking(bool flag) { m_tracking = flag; } bool tracking() const { return m_tracking; } @@ -5479,54 +5661,54 @@ public: class AstScCtor : public AstNodeText { public: AstScCtor(FileLine* fl, const string& textp) - : AstNodeText(fl, textp) {} + : AstNodeText(fl, textp) {} ASTNODE_NODE_FUNCS(ScCtor) - virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs virtual bool isOutputter() const { return true; } }; class AstScDtor : public AstNodeText { public: AstScDtor(FileLine* fl, const string& textp) - : AstNodeText(fl, textp) {} + : AstNodeText(fl, textp) {} ASTNODE_NODE_FUNCS(ScDtor) - virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs virtual bool isOutputter() const { return true; } }; class AstScHdr : public AstNodeText { public: AstScHdr(FileLine* fl, const string& textp) - : AstNodeText(fl, textp) {} + : AstNodeText(fl, textp) {} ASTNODE_NODE_FUNCS(ScHdr) - virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs virtual bool isOutputter() const { return true; } }; class AstScImp : public AstNodeText { public: AstScImp(FileLine* fl, const string& textp) - : AstNodeText(fl, textp) {} + : AstNodeText(fl, textp) {} ASTNODE_NODE_FUNCS(ScImp) - virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs virtual bool isOutputter() const { return true; } }; class AstScImpHdr : public AstNodeText { public: AstScImpHdr(FileLine* fl, const string& textp) - : AstNodeText(fl, textp) {} + : AstNodeText(fl, textp) {} ASTNODE_NODE_FUNCS(ScImpHdr) - virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs virtual bool isOutputter() const { return true; } }; class AstScInt : public AstNodeText { public: AstScInt(FileLine* fl, const string& textp) - : AstNodeText(fl, textp) {} + : AstNodeText(fl, textp) {} ASTNODE_NODE_FUNCS(ScInt) - virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs + virtual bool isPure() const { return false; } // SPECIAL: User may order w/other sigs virtual bool isOutputter() const { return true; } }; @@ -5534,11 +5716,11 @@ class AstUCStmt : public AstNodeStmt { // User $c statement public: AstUCStmt(FileLine* fl, AstNode* exprsp) - : AstNodeStmt(fl) { - addNOp1p(exprsp); + : AstNodeStmt(fl) { + addNOp1p(exprsp); } ASTNODE_NODE_FUNCS(UCStmt) - AstNode* bodysp() const { return op1p(); } // op1= expressions to print + AstNode* bodysp() const { return op1p(); } // op1 = expressions to print virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } virtual bool isPure() const { return false; } @@ -5555,29 +5737,29 @@ class AstCFile : public AstNode { // Parents: NETLIST // Children: nothing yet private: - string m_name; ///< Filename - bool m_slow:1; ///< Compile w/o optimization - bool m_source:1; ///< Source file (vs header file) - bool m_support:1; ///< Support file (non systemc) + string m_name; ///< Filename + bool m_slow:1; ///< Compile w/o optimization + bool m_source:1; ///< Source file (vs header file) + bool m_support:1; ///< Support file (non systemc) public: AstCFile(FileLine* fl, const string& name) - : AstNode(fl) { - m_name = name; - m_slow = false; - m_source = false; - m_support = false; + : AstNode(fl) { + m_name = name; + m_slow = false; + m_source = false; + m_support = false; } ASTNODE_NODE_FUNCS(CFile) - virtual string name() const { return m_name; } + virtual string name() const { return m_name; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } virtual void dump(std::ostream& str=std::cout); - bool slow() const { return m_slow; } - void slow(bool flag) { m_slow = flag; } - bool source() const { return m_source; } - void source(bool flag) { m_source = flag; } - bool support() const { return m_support; } - void support(bool flag) { m_support = flag; } + bool slow() const { return m_slow; } + void slow(bool flag) { m_slow = flag; } + bool source() const { return m_source; } + void source(bool flag) { m_source = flag; } + bool support() const { return m_support; } + void support(bool flag) { m_support = flag; } }; class AstCFunc : public AstNode { @@ -5586,121 +5768,121 @@ class AstCFunc : public AstNode { // Children: VAR/statements private: AstCFuncType m_funcType; - AstScope* m_scopep; - string m_name; - string m_cname; // C name, for dpiExports - string m_rtnType; // void, bool, or other return type - string m_argTypes; + AstScope* m_scopep; + string m_name; + string m_cname; // C name, for dpiExports + string m_rtnType; // void, bool, or other return type + string m_argTypes; string m_ifdef; // #ifdef symbol around this function VBoolOrUnknown m_isStatic; // Function is declared static (no this) bool m_dontCombine:1; // V3Combine shouldn't compare this func tree, it's special - bool m_skipDecl:1; // Don't declare it - bool m_declPrivate:1; // Declare it private - bool m_formCallTree:1; // Make a global function to call entire tree of functions - bool m_slow:1; // Slow routine, called once or just at init time - bool m_funcPublic:1; // From user public task/function - bool m_isInline:1; // Inline function - bool m_symProlog:1; // Setup symbol table for later instructions - bool m_entryPoint:1; // User may call into this top level function - bool m_pure:1; // Pure function - bool m_dpiExport:1; // From dpi export - bool m_dpiExportWrapper:1; // From dpi export; static function with dispatch table - bool m_dpiImport:1; // From dpi import + bool m_skipDecl:1; // Don't declare it + bool m_declPrivate:1; // Declare it private + bool m_formCallTree:1; // Make a global function to call entire tree of functions + bool m_slow:1; // Slow routine, called once or just at init time + bool m_funcPublic:1; // From user public task/function + bool m_isInline:1; // Inline function + bool m_symProlog:1; // Setup symbol table for later instructions + bool m_entryPoint:1; // User may call into this top level function + bool m_pure:1; // Pure function + bool m_dpiExport:1; // From dpi export + bool m_dpiExportWrapper:1; // From dpi export; static function with dispatch table + bool m_dpiImport:1; // From dpi import bool m_dpiImportWrapper:1; // Wrapper from dpi import public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType="") - : AstNode(fl) { + : AstNode(fl) { m_funcType = AstCFuncType::FT_NORMAL; m_isStatic = VBoolOrUnknown::BU_UNKNOWN; // Unknown until see where thisp needed m_scopep = scopep; - m_name = name; - m_rtnType = rtnType; - m_dontCombine = false; - m_skipDecl = false; - m_declPrivate = false; - m_formCallTree = false; - m_slow = false; - m_funcPublic = false; - m_isInline = false; - m_symProlog = false; - m_entryPoint = false; - m_pure = false; - m_dpiExport = false; - m_dpiExportWrapper = false; - m_dpiImport = false; + m_name = name; + m_rtnType = rtnType; + m_dontCombine = false; + m_skipDecl = false; + m_declPrivate = false; + m_formCallTree = false; + m_slow = false; + m_funcPublic = false; + m_isInline = false; + m_symProlog = false; + m_entryPoint = false; + m_pure = false; + m_dpiExport = false; + m_dpiExportWrapper = false; + m_dpiImport = false; m_dpiImportWrapper = false; } ASTNODE_NODE_FUNCS(CFunc) - virtual string name() const { return m_name; } + virtual string name() const { return m_name; } virtual const char* broken() const { BROKEN_RTN((m_scopep && !m_scopep->brokeExists())); return NULL; } virtual bool maybePointedTo() const { return true; } virtual void dump(std::ostream& str=std::cout); virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { - const AstCFunc* asamep = static_cast(samep); - return ((funcType() == asamep->funcType()) - && (rtnTypeVoid() == asamep->rtnTypeVoid()) - && (argTypes() == asamep->argTypes()) - && (!(dpiImport() || dpiExport()) - || name() == asamep->name())); } + const AstCFunc* asamep = static_cast(samep); + return ((funcType() == asamep->funcType()) + && (rtnTypeVoid() == asamep->rtnTypeVoid()) + && (argTypes() == asamep->argTypes()) + && (!(dpiImport() || dpiExport()) + || name() == asamep->name())); } // virtual void name(const string& name) { m_name = name; } virtual int instrCount() const { return dpiImport() ? instrCountDpi() : 0; } VBoolOrUnknown isStatic() const { return m_isStatic; } void isStatic(bool flag) { m_isStatic = flag ? VBoolOrUnknown::BU_TRUE : VBoolOrUnknown::BU_FALSE; } void isStatic(VBoolOrUnknown flag) { m_isStatic = flag; } - void cname(const string& name) { m_cname = name; } - string cname() const { return m_cname; } - AstScope* scopep() const { return m_scopep; } - void scopep(AstScope* nodep) { m_scopep = nodep; } - string rtnTypeVoid() const { return ((m_rtnType=="") ? "void" : m_rtnType); } - bool dontCombine() const { return m_dontCombine || funcType()!=AstCFuncType::FT_NORMAL; } - void dontCombine(bool flag) { m_dontCombine = flag; } - bool dontInline() const { return dontCombine() || slow() || skipDecl() || funcPublic(); } - bool skipDecl() const { return m_skipDecl; } - void skipDecl(bool flag) { m_skipDecl = flag; } - bool declPrivate() const { return m_declPrivate; } - void declPrivate(bool flag) { m_declPrivate = flag; } - bool formCallTree() const { return m_formCallTree; } - void formCallTree(bool flag) { m_formCallTree = flag; } - bool slow() const { return m_slow; } - void slow(bool flag) { m_slow = flag; } - bool funcPublic() const { return m_funcPublic; } - void funcPublic(bool flag) { m_funcPublic = flag; } - void argTypes(const string& str) { m_argTypes = str; } - string argTypes() const { return m_argTypes; } - void ifdef(const string& str) { m_ifdef = str; } - string ifdef() const { return m_ifdef; } - void funcType(AstCFuncType flag) { m_funcType = flag; } + void cname(const string& name) { m_cname = name; } + string cname() const { return m_cname; } + AstScope* scopep() const { return m_scopep; } + void scopep(AstScope* nodep) { m_scopep = nodep; } + string rtnTypeVoid() const { return ((m_rtnType=="") ? "void" : m_rtnType); } + bool dontCombine() const { return m_dontCombine || funcType()!=AstCFuncType::FT_NORMAL; } + void dontCombine(bool flag) { m_dontCombine = flag; } + bool dontInline() const { return dontCombine() || slow() || skipDecl() || funcPublic(); } + bool skipDecl() const { return m_skipDecl; } + void skipDecl(bool flag) { m_skipDecl = flag; } + bool declPrivate() const { return m_declPrivate; } + void declPrivate(bool flag) { m_declPrivate = flag; } + bool formCallTree() const { return m_formCallTree; } + void formCallTree(bool flag) { m_formCallTree = flag; } + bool slow() const { return m_slow; } + void slow(bool flag) { m_slow = flag; } + bool funcPublic() const { return m_funcPublic; } + void funcPublic(bool flag) { m_funcPublic = flag; } + void argTypes(const string& str) { m_argTypes = str; } + string argTypes() const { return m_argTypes; } + void ifdef(const string& str) { m_ifdef = str; } + string ifdef() const { return m_ifdef; } + void funcType(AstCFuncType flag) { m_funcType = flag; } AstCFuncType funcType() const { return m_funcType; } - bool isInline() const { return m_isInline; } - void isInline(bool flag) { m_isInline = flag; } - bool symProlog() const { return m_symProlog; } - void symProlog(bool flag) { m_symProlog = flag; } - bool entryPoint() const { return m_entryPoint; } - void entryPoint(bool flag) { m_entryPoint = flag; } - bool pure() const { return m_pure; } - void pure(bool flag) { m_pure = flag; } - bool dpiExport() const { return m_dpiExport; } - void dpiExport(bool flag) { m_dpiExport = flag; } - bool dpiExportWrapper() const { return m_dpiExportWrapper; } - void dpiExportWrapper(bool flag) { m_dpiExportWrapper = flag; } - bool dpiImport() const { return m_dpiImport; } - void dpiImport(bool flag) { m_dpiImport = flag; } + bool isInline() const { return m_isInline; } + void isInline(bool flag) { m_isInline = flag; } + bool symProlog() const { return m_symProlog; } + void symProlog(bool flag) { m_symProlog = flag; } + bool entryPoint() const { return m_entryPoint; } + void entryPoint(bool flag) { m_entryPoint = flag; } + bool pure() const { return m_pure; } + void pure(bool flag) { m_pure = flag; } + bool dpiExport() const { return m_dpiExport; } + void dpiExport(bool flag) { m_dpiExport = flag; } + bool dpiExportWrapper() const { return m_dpiExportWrapper; } + void dpiExportWrapper(bool flag) { m_dpiExportWrapper = flag; } + bool dpiImport() const { return m_dpiImport; } + void dpiImport(bool flag) { m_dpiImport = flag; } bool dpiImportWrapper() const { return m_dpiImportWrapper; } void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; } // // If adding node accessors, see below emptyBody - AstNode* argsp() const { return op1p(); } + AstNode* argsp() const { return op1p(); } void addArgsp(AstNode* nodep) { addOp1p(nodep); } - AstNode* initsp() const { return op2p(); } + AstNode* initsp() const { return op2p(); } void addInitsp(AstNode* nodep) { addOp2p(nodep); } - AstNode* stmtsp() const { return op3p(); } + AstNode* stmtsp() const { return op3p(); } void addStmtsp(AstNode* nodep) { addOp3p(nodep); } - AstNode* finalsp() const { return op4p(); } + AstNode* finalsp() const { return op4p(); } void addFinalsp(AstNode* nodep) { addOp4p(nodep); } // Special methods - bool emptyBody() const { return argsp()==NULL && initsp()==NULL && stmtsp()==NULL && finalsp()==NULL; } + bool emptyBody() const { return argsp()==NULL && initsp()==NULL && stmtsp()==NULL && finalsp()==NULL; } }; class AstCCall : public AstNodeStmt { @@ -5708,47 +5890,47 @@ class AstCCall : public AstNodeStmt { // Parents: Anything above a statement // Children: Args to the function private: - AstCFunc* m_funcp; - string m_hiername; - string m_argTypes; + AstCFunc* m_funcp; + string m_hiername; + string m_argTypes; public: AstCCall(FileLine* fl, AstCFunc* funcp, AstNode* argsp=NULL) - : AstNodeStmt(fl) { - m_funcp = funcp; - addNOp1p(argsp); + : AstNodeStmt(fl) { + m_funcp = funcp; + addNOp1p(argsp); } - AstCCall(AstCCall* oldp, AstCFunc* funcp) // Replacement form for V3Combine - // Note this removes old attachments from the oldp - : AstNodeStmt(oldp->fileline()) { - m_funcp = funcp; - m_hiername = oldp->hiername(); - m_argTypes = oldp->argTypes(); - if (oldp->argsp()) addNOp1p(oldp->argsp()->unlinkFrBackWithNext()); + AstCCall(AstCCall* oldp, AstCFunc* funcp) // Replacement form for V3Combine + // Note this removes old attachments from the oldp + : AstNodeStmt(oldp->fileline()) { + m_funcp = funcp; + m_hiername = oldp->hiername(); + m_argTypes = oldp->argTypes(); + if (oldp->argsp()) addNOp1p(oldp->argsp()->unlinkFrBackWithNext()); } ASTNODE_NODE_FUNCS(CCall) virtual void dump(std::ostream& str=std::cout); virtual void cloneRelink() { if (m_funcp && m_funcp->clonep()) { - m_funcp = m_funcp->clonep(); + m_funcp = m_funcp->clonep(); }} virtual const char* broken() const { BROKEN_RTN(m_funcp && !m_funcp->brokeExists()); return NULL; } - virtual int instrCount() const { return instrCountCall(); } + virtual int instrCount() const { return instrCountCall(); } virtual V3Hash sameHash() const { return V3Hash(funcp()); } virtual bool same(const AstNode* samep) const { - const AstCCall* asamep = static_cast(samep); - return (funcp() == asamep->funcp() - && argTypes() == asamep->argTypes()); } - AstNode* exprsp() const { return op1p(); } // op1= expressions to print + const AstCCall* asamep = static_cast(samep); + return (funcp() == asamep->funcp() + && argTypes() == asamep->argTypes()); } + AstNode* exprsp() const { return op1p(); } // op1 = expressions to print virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } virtual bool isPure() const { return funcp()->pure(); } virtual bool isOutputter() const { return !(funcp()->pure()); } - AstCFunc* funcp() const { return m_funcp; } + AstCFunc* funcp() const { return m_funcp; } string hiername() const { return m_hiername; } void hiername(const string& hn) { m_hiername = hn; } - void argTypes(const string& str) { m_argTypes = str; } - string argTypes() const { return m_argTypes; } + void argTypes(const string& str) { m_argTypes = str; } + string argTypes() const { return m_argTypes; } // - AstNode* argsp() const { return op1p(); } + AstNode* argsp() const { return op1p(); } void addArgsp(AstNode* nodep) { addOp1p(nodep); } }; @@ -5758,31 +5940,31 @@ class AstCReturn : public AstNodeStmt { // Children: Math public: AstCReturn(FileLine* fl, AstNode* lhsp) - : AstNodeStmt(fl) { - setOp1p(lhsp); + : AstNodeStmt(fl) { + setOp1p(lhsp); } ASTNODE_NODE_FUNCS(CReturn) - virtual int instrCount() const { return widthInstrs(); } + virtual int instrCount() const { return widthInstrs(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } // - AstNode* lhsp() const { return op1p(); } + AstNode* lhsp() const { return op1p(); } }; class AstCMath : public AstNodeMath { private: - bool m_cleanOut; + bool m_cleanOut; public: // Emit C textual math function (like AstUCFunc) AstCMath(FileLine* fl, AstNode* exprsp) - : AstNodeMath(fl), m_cleanOut(true) { - addOp1p(exprsp); - dtypeFrom(exprsp); + : AstNodeMath(fl), m_cleanOut(true) { + addOp1p(exprsp); + dtypeFrom(exprsp); } AstCMath(FileLine* fl, const string& textStmt, int setwidth, bool cleanOut=true) - : AstNodeMath(fl), m_cleanOut(cleanOut) { - addNOp1p(new AstText(fl, textStmt, true)); - if (setwidth) { dtypeSetLogicSized(setwidth,setwidth,AstNumeric::UNSIGNED); } + : AstNodeMath(fl), m_cleanOut(cleanOut) { + addNOp1p(new AstText(fl, textStmt, true)); + if (setwidth) { dtypeSetLogicSized(setwidth, setwidth, AstNumeric::UNSIGNED); } } ASTNODE_NODE_FUNCS(CMath) virtual bool isGateOptimizable() const { return false; } @@ -5793,7 +5975,7 @@ public: virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } void addBodysp(AstNode* nodep) { addNOp1p(nodep); } - AstNode* bodysp() const { return op1p(); } // op1= expressions to print + AstNode* bodysp() const { return op1p(); } // op1 = expressions to print }; @@ -5801,27 +5983,27 @@ class AstCReset : public AstNodeStmt { // Reset variable at startup public: AstCReset(FileLine* fl, AstNode* exprsp) - : AstNodeStmt(fl) { - addNOp1p(exprsp); + : AstNodeStmt(fl) { + addNOp1p(exprsp); } ASTNODE_NODE_FUNCS(CReset) virtual bool isGateOptimizable() const { return false; } virtual bool isPredictOptimizable() const { return false; } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } - AstVarRef* varrefp() const { return VN_CAST(op1p(), VarRef); } // op1= varref to reset + AstVarRef* varrefp() const { return VN_CAST(op1p(), VarRef); } // op1 = varref to reset }; class AstCStmt : public AstNodeStmt { // Emit C statement public: AstCStmt(FileLine* fl, AstNode* exprsp) - : AstNodeStmt(fl) { - addNOp1p(exprsp); + : AstNodeStmt(fl) { + addNOp1p(exprsp); } AstCStmt(FileLine* fl, const string& textStmt) - : AstNodeStmt(fl) { - addNOp1p(new AstText(fl, textStmt, true)); + : AstNodeStmt(fl) { + addNOp1p(new AstText(fl, textStmt, true)); } ASTNODE_NODE_FUNCS(CStmt) virtual bool isGateOptimizable() const { return false; } @@ -5829,7 +6011,7 @@ public: virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(const AstNode* samep) const { return true; } void addBodysp(AstNode* nodep) { addNOp1p(nodep); } - AstNode* bodysp() const { return op1p(); } // op1= expressions to print + AstNode* bodysp() const { return op1p(); } // op1 = expressions to print }; class AstMTaskBody : public AstNode { @@ -5894,16 +6076,16 @@ class AstTypeTable : public AstNode { DetailedMap m_detailedMap; public: explicit AstTypeTable(FileLine* fl) : AstNode(fl) { - for (int i=0; iinLibrary(true); // packages are always libraries; don't want to make them a "top" - m_dollarUnitPkgp->modTrace(false); // may reconsider later - m_dollarUnitPkgp->internal(true); - addModulep(m_dollarUnitPkgp); - } - return m_dollarUnitPkgp; } + if (!m_dollarUnitPkgp) { + m_dollarUnitPkgp = new AstPackage(fileline(), AstPackage::dollarUnitName()); + m_dollarUnitPkgp->inLibrary(true); // packages are always libraries; don't want to make them a "top" + m_dollarUnitPkgp->modTrace(false); // may reconsider later + m_dollarUnitPkgp->internal(true); + addModulep(m_dollarUnitPkgp); + } + return m_dollarUnitPkgp; } AstCFunc* evalp() const { return m_evalp; } void evalp(AstCFunc* evalp) { m_evalp = evalp; } AstExecGraph* execGraphp() const { return m_execGraphp; } @@ -5964,4 +6146,4 @@ public: //###################################################################### -#endif // Guard +#endif // Guard diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 01bfb407c..9389c4358 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -20,9 +20,9 @@ // V3Begin's Transformations: // // Each module: -// Look for BEGINs -// BEGIN(VAR...) -> VAR ... {renamed} -// FOR -> WHILEs +// Look for BEGINs +// BEGIN(VAR...) -> VAR ... {renamed} +// FOR -> WHILEs // // There are two scopes; named BEGINs change %m and variable scopes. // Unnamed BEGINs change only variable, not $display("%m") scope. @@ -47,17 +47,17 @@ class BeginState { private: // NODE STATE //Entire netlist: - // AstNodeFTask::user1 -> bool, 1=processed - AstUser1InUse m_inuser1; - bool m_anyFuncInBegin; + // AstNodeFTask::user1 -> bool, 1=processed + AstUser1InUse m_inuser1; + bool m_anyFuncInBegin; public: BeginState() { - m_anyFuncInBegin = false; + m_anyFuncInBegin = false; } ~BeginState() {} void userMarkChanged(AstNode* nodep) { - nodep->user1(true); - m_anyFuncInBegin = true; + nodep->user1(true); + m_anyFuncInBegin = true; } bool anyFuncInBegin() const { return m_anyFuncInBegin; } }; @@ -67,159 +67,161 @@ public: class BeginVisitor : public AstNVisitor { private: // STATE - BeginState* m_statep; // Current global state - AstNodeModule* m_modp; // Current module - AstNodeFTask* m_ftaskp; // Current function/task - string m_namedScope; // Name of begin blocks above us - string m_unnamedScope; // Name of begin blocks, including unnamed blocks - int m_repeatNum; // Repeat counter - int m_ifDepth; // Current if depth + BeginState* m_statep; // Current global state + AstNodeModule* m_modp; // Current module + AstNodeFTask* m_ftaskp; // Current function/task + string m_namedScope; // Name of begin blocks above us + string m_unnamedScope; // Name of begin blocks, including unnamed blocks + int m_repeatNum; // Repeat counter + int m_ifDepth; // Current if depth // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; - m_repeatNum = 0; + m_modp = nodep; + m_repeatNum = 0; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstNodeFTask* nodep) { - UINFO(8," "<name(m_unnamedScope+"__DOT__"+nodep->name()); - UINFO(8," rename to "<name()<userMarkChanged(nodep); - } - // BEGIN wrapping a function rename that function, but don't affect the inside function's variables - // We then restart with empty naming; so that any begin's inside the function will rename inside the function - // Process children - string oldScope = m_namedScope; - string oldUnnamed = m_unnamedScope; - { - m_namedScope = ""; - m_unnamedScope = ""; - m_ftaskp = nodep; + UINFO(8," "<name(m_unnamedScope+"__DOT__"+nodep->name()); + UINFO(8," rename to "<name()<userMarkChanged(nodep); + } + // BEGIN wrapping a function rename that function, but don't affect + // the inside function's variables. We then restart with empty + // naming; so that any begin's inside the function will rename + // inside the function. + // Process children + string oldScope = m_namedScope; + string oldUnnamed = m_unnamedScope; + { + m_namedScope = ""; + m_unnamedScope = ""; + m_ftaskp = nodep; iterateChildren(nodep); - m_ftaskp = NULL; - } - m_namedScope = oldScope; - m_unnamedScope = oldUnnamed; + m_ftaskp = NULL; + } + m_namedScope = oldScope; + m_unnamedScope = oldUnnamed; } virtual void visit(AstBegin* nodep) { - // Begin blocks were only useful in variable creation, change names and delete - UINFO(8," "<name() != "") { // Else unneeded unnamed block - // Create data for dotted variable resolution - string dottedname = nodep->name() + "__DOT__"; // So always found - string::size_type pos; - while ((pos=dottedname.find("__DOT__")) != string::npos) { - string ident = dottedname.substr(0,pos); - dottedname = dottedname.substr(pos+strlen("__DOT__")); - if (!nodep->unnamed()) { - if (m_namedScope=="") m_namedScope = ident; - else m_namedScope = m_namedScope + "__DOT__"+ident; - } - if (m_unnamedScope=="") m_unnamedScope = ident; - else m_unnamedScope = m_unnamedScope + "__DOT__"+ident; - // Create CellInline for dotted var resolution - if (!m_ftaskp) { - AstCellInline* inlinep = new AstCellInline(nodep->fileline(), - m_unnamedScope, "__BEGIN__"); - m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells - } - } - } + // Begin blocks were only useful in variable creation, change names and delete + UINFO(8," "<name() != "") { // Else unneeded unnamed block + // Create data for dotted variable resolution + string dottedname = nodep->name() + "__DOT__"; // So always found + string::size_type pos; + while ((pos=dottedname.find("__DOT__")) != string::npos) { + string ident = dottedname.substr(0, pos); + dottedname = dottedname.substr(pos+strlen("__DOT__")); + if (!nodep->unnamed()) { + if (m_namedScope=="") m_namedScope = ident; + else m_namedScope = m_namedScope + "__DOT__"+ident; + } + if (m_unnamedScope=="") m_unnamedScope = ident; + else m_unnamedScope = m_unnamedScope + "__DOT__"+ident; + // Create CellInline for dotted var resolution + if (!m_ftaskp) { + AstCellInline* inlinep = new AstCellInline(nodep->fileline(), + m_unnamedScope, "__BEGIN__"); + m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells + } + } + } - // Remap var names and replace lower Begins + // Remap var names and replace lower Begins iterateAndNextNull(nodep->stmtsp()); - if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier"); - } - m_namedScope = oldScope; - m_unnamedScope = oldUnnamed; + if (nodep->genforp()) nodep->v3fatalSrc("GENFORs should have been expanded earlier"); + } + m_namedScope = oldScope; + m_unnamedScope = oldUnnamed; - // Cleanup - AstNode* addsp = NULL; - if (AstNode* stmtsp = nodep->stmtsp()) { - stmtsp->unlinkFrBackWithNext(); - if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; } - } - if (addsp) { - nodep->replaceWith(addsp); - } else { - nodep->unlinkFrBack(); - } - pushDeletep(nodep); VL_DANGLING(nodep); + // Cleanup + AstNode* addsp = NULL; + if (AstNode* stmtsp = nodep->stmtsp()) { + stmtsp->unlinkFrBackWithNext(); + if (addsp) { addsp = addsp->addNextNull(stmtsp); } else { addsp = stmtsp; } + } + if (addsp) { + nodep->replaceWith(addsp); + } else { + nodep->unlinkFrBack(); + } + pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstVar* nodep) { - if (m_unnamedScope != "") { - // Rename it - nodep->name(m_unnamedScope+"__DOT__"+nodep->name()); - m_statep->userMarkChanged(nodep); - // Move to module - nodep->unlinkFrBack(); - if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func - else m_modp->addStmtp(nodep); - } + if (m_unnamedScope != "") { + // Rename it + nodep->name(m_unnamedScope+"__DOT__"+nodep->name()); + m_statep->userMarkChanged(nodep); + // Move to module + nodep->unlinkFrBack(); + if (m_ftaskp) m_ftaskp->addStmtsp(nodep); // Begins under funcs just move into the func + else m_modp->addStmtp(nodep); + } } virtual void visit(AstCell* nodep) { - UINFO(8," CELL "<userMarkChanged(nodep); - // Rename it - nodep->name(m_namedScope+"__DOT__"+nodep->name()); - UINFO(8," rename to "<name()<unlinkFrBack(); - m_modp->addStmtp(nodep); - } + UINFO(8," CELL "<userMarkChanged(nodep); + // Rename it + nodep->name(m_namedScope+"__DOT__"+nodep->name()); + UINFO(8," rename to "<name()<unlinkFrBack(); + m_modp->addStmtp(nodep); + } iterateChildren(nodep); } virtual void visit(AstVarXRef* nodep) { - UINFO(9, " VARXREF "<inlinedDots() == "") { - nodep->inlinedDots(m_namedScope); - UINFO(9, " rescope to "<inlinedDots() == "") { + nodep->inlinedDots(m_namedScope); + UINFO(9, " rescope to "<user1SetOnce()) return; // Don't double-add text's - if (m_namedScope != "") { - // To keep correct visual order, must add before other Text's - AstNode* afterp = nodep->scopeAttrp(); - if (afterp) afterp->unlinkFrBackWithNext(); + // If there's a %m in the display text, we add a special node that will contain the name() + // Similar code in V3Inline + if (nodep->user1SetOnce()) return; // Don't double-add text's + if (m_namedScope != "") { + // To keep correct visual order, must add before other Text's + AstNode* afterp = nodep->scopeAttrp(); + if (afterp) afterp->unlinkFrBackWithNext(); nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_namedScope)); - if (afterp) nodep->scopeAttrp(afterp); - } + if (afterp) nodep->scopeAttrp(afterp); + } iterateChildren(nodep); } virtual void visit(AstCoverDecl* nodep) { - // Don't need to fix path in coverage statements, they're not under - // any BEGINs, but V3Coverage adds them all under the module itself. + // Don't need to fix path in coverage statements, they're not under + // any BEGINs, but V3Coverage adds them all under the module itself. iterateChildren(nodep); } // VISITORS - LINT CHECK - virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered - // Check IFDEPTH warning - could be in other transform files if desire - int prevIfDepth = m_ifDepth; - if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off - } else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) { - m_ifDepth = -1; - } else if (++m_ifDepth > v3Global.opt.ifDepth()) { - nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic"); - nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once - m_ifDepth = -1; - } + virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered + // Check IFDEPTH warning - could be in other transform files if desire + int prevIfDepth = m_ifDepth; + if (m_ifDepth == -1 || v3Global.opt.ifDepth()<1) { // Turned off + } else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) { + m_ifDepth = -1; + } else if (++m_ifDepth > v3Global.opt.ifDepth()) { + nodep->v3warn(IFDEPTH,"Deep 'if' statement; suggest unique/priority to avoid slow logic"); + nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once + m_ifDepth = -1; + } iterateChildren(nodep); - m_ifDepth = prevIfDepth; + m_ifDepth = prevIfDepth; } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -227,11 +229,11 @@ private: public: // CONSTUCTORS BeginVisitor(AstNetlist* nodep, BeginState* statep) { - m_statep = statep; - m_modp = NULL; - m_ftaskp = NULL; - m_repeatNum = 0; - m_ifDepth = 0; + m_statep = statep; + m_modp = NULL; + m_ftaskp = NULL; + m_repeatNum = 0; + m_ifDepth = 0; iterate(nodep); } virtual ~BeginVisitor() {} @@ -244,29 +246,29 @@ class BeginRelinkVisitor : public AstNVisitor { private: // NODE STATE // Input: - // AstNodeFTask::user1p // Node replaced, rename it + // AstNodeFTask::user1p // Node replaced, rename it // VISITORS virtual void visit(AstNodeFTaskRef* nodep) { - if (nodep->taskp()->user1()) { // It was converted - UINFO(9, " relinkFTask "<name(nodep->taskp()->name()); - } + if (nodep->taskp()->user1()) { // It was converted + UINFO(9, " relinkFTask "<name(nodep->taskp()->name()); + } iterateChildren(nodep); } virtual void visit(AstVarRef* nodep) { - if (nodep->varp()->user1()) { // It was converted - UINFO(9, " relinVarRef "<name(nodep->varp()->name()); - } + if (nodep->varp()->user1()) { // It was converted + UINFO(9, " relinVarRef "<name(nodep->varp()->name()); + } iterateChildren(nodep); } virtual void visit(AstIfaceRefDType* nodep) { - // May have changed cell names - // TypeTable is always after all modules, so names are stable - UINFO(8," IFACEREFDTYPE "<cellp()) nodep->cellName(nodep->cellp()->name()); - UINFO(8," rename to "<cellp()) nodep->cellName(nodep->cellp()->name()); + UINFO(8," rename to "< int. Number of references - AstUser1InUse m_inuser1; + // AstFTask::user1() -> int. Number of references + AstUser1InUse m_inuser1; // TYPES typedef std::vector CFuncVec; // STATE - int m_likely; // Excuses for branch likely taken - int m_unlikely; // Excuses for branch likely not taken - CFuncVec m_cfuncsp; // List of all tasks + int m_likely; // Excuses for branch likely taken + int m_unlikely; // Excuses for branch likely not taken + CFuncVec m_cfuncsp; // List of all tasks // METHODS VL_DEBUG_FUNC; // Declare debug() void reset() { - m_likely = false; - m_unlikely = false; + m_likely = false; + m_unlikely = false; } void checkUnlikely(AstNode* nodep) { - if (nodep->isUnlikely()) { - UINFO(4," UNLIKELY: "<isUnlikely()) { + UINFO(4," UNLIKELY: "<ifsp()); - int ifLikely = m_likely; - int ifUnlikely = m_unlikely; - // Do else - reset(); + int ifLikely = m_likely; + int ifUnlikely = m_unlikely; + // Do else + reset(); iterateAndNextNull(nodep->elsesp()); - int elseLikely = m_likely; - int elseUnlikely = m_unlikely; - // Compute - int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely); - if (likeness>0) { - nodep->branchPred(AstBranchPred::BP_LIKELY); - } else if (likeness<0) { - nodep->branchPred(AstBranchPred::BP_UNLIKELY); - } // else leave unknown - } - m_likely = lastLikely; - m_unlikely = lastUnlikely; + int elseLikely = m_likely; + int elseUnlikely = m_unlikely; + // Compute + int likeness = ifLikely - ifUnlikely - (elseLikely - elseUnlikely); + if (likeness>0) { + nodep->branchPred(AstBranchPred::BP_LIKELY); + } else if (likeness<0) { + nodep->branchPred(AstBranchPred::BP_UNLIKELY); + } // else leave unknown + } + m_likely = lastLikely; + m_unlikely = lastUnlikely; } virtual void visit(AstCCall* nodep) { - checkUnlikely(nodep); - nodep->funcp()->user1Inc(); + checkUnlikely(nodep); + nodep->funcp()->user1Inc(); iterateChildren(nodep); } virtual void visit(AstCFunc* nodep) { - checkUnlikely(nodep); - m_cfuncsp.push_back(nodep); + checkUnlikely(nodep); + m_cfuncsp.push_back(nodep); iterateChildren(nodep); } virtual void visit(AstNode* nodep) { - checkUnlikely(nodep); + checkUnlikely(nodep); iterateChildren(nodep); } // METHODS void calc_tasks() { - for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) { - AstCFunc* nodep = *it; - if (!nodep->dontInline()) { - nodep->isInline(true); - } - } + for (CFuncVec::iterator it=m_cfuncsp.begin(); it!=m_cfuncsp.end(); ++it) { + AstCFunc* nodep = *it; + if (!nodep->dontInline()) { + nodep->isInline(true); + } + } } public: // CONSTUCTORS explicit BranchVisitor(AstNetlist* nodep) { - reset(); + reset(); iterateChildren(nodep); - calc_tasks(); + calc_tasks(); } virtual ~BranchVisitor() {} }; diff --git a/src/V3Branch.h b/src/V3Branch.h index 69408ad49..cfdd755c9 100644 --- a/src/V3Branch.h +++ b/src/V3Branch.h @@ -35,4 +35,4 @@ public: static void branchAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index d8d3fd602..51612ec29 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -20,8 +20,8 @@ // V3Broken's Transformations: // // Entire netlist -// Mark all nodes -// Check all links point to marked nodes +// Mark all nodes +// Check all links point to marked nodes // //************************************************************************* @@ -47,125 +47,125 @@ private: // MEMBERS // For each node, we keep if it exists or not. typedef vl_unordered_map NodeMap; // Performance matters (when --debug) - static NodeMap s_nodes; // Set of all nodes that exist + static NodeMap s_nodes; // Set of all nodes that exist // BITMASK - enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed - enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree - enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to - enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked - enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node + enum { FLAG_ALLOCATED = 0x01 }; // new() and not delete()ed + enum { FLAG_IN_TREE = 0x02 }; // Is in netlist tree + enum { FLAG_LINKABLE = 0x04 }; // Is in netlist tree, can be linked to + enum { FLAG_LEAKED = 0x08 }; // Known to have been leaked + enum { FLAG_UNDER_NOW = 0x10 }; // Is in tree as parent of current node public: // METHODS static void deleted(const AstNode* nodep) { - // Called by operator delete on any node - only if VL_LEAK_CHECKS + // Called by operator delete on any node - only if VL_LEAK_CHECKS if (debug()>=9) cout<<"-nodeDel: "<second & FLAG_ALLOCATED)) { + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter==s_nodes.end() || !(iter->second & FLAG_ALLOCATED)) { reinterpret_cast(nodep) ->v3fatalSrc("Deleting AstNode object that was never tracked or already deleted"); - } - if (iter!=s_nodes.end()) s_nodes.erase(iter); + } + if (iter!=s_nodes.end()) s_nodes.erase(iter); } #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 4 // GCC 4.4.* compiler warning bug, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39390 # pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif static void addNewed(const AstNode* nodep) { - // Called by operator new on any node - only if VL_LEAK_CHECKS + // Called by operator new on any node - only if VL_LEAK_CHECKS if (debug()>=9) cout<<"-nodeNew: "<second & FLAG_ALLOCATED)) { - nodep->v3fatalSrc("Newing AstNode object that is already allocated"); - } - if (iter == s_nodes.end()) { - int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2 - s_nodes.insert(make_pair(nodep,flags)); - } + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter!=s_nodes.end() || (iter->second & FLAG_ALLOCATED)) { + nodep->v3fatalSrc("Newing AstNode object that is already allocated"); + } + if (iter == s_nodes.end()) { + int flags = FLAG_ALLOCATED; // This int needed to appease GCC 4.1.2 + s_nodes.insert(make_pair(nodep, flags)); + } } static void setUnder(const AstNode* nodep, bool flag) { - // Called by BrokenCheckVisitor when each node entered/exited - if (!okIfLinkedTo(nodep)) return; - NodeMap::iterator iter = s_nodes.find(nodep); - if (iter!=s_nodes.end()) { - iter->second &= ~FLAG_UNDER_NOW; - if (flag) iter->second |= FLAG_UNDER_NOW; - } + // Called by BrokenCheckVisitor when each node entered/exited + if (!okIfLinkedTo(nodep)) return; + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter!=s_nodes.end()) { + iter->second &= ~FLAG_UNDER_NOW; + if (flag) iter->second |= FLAG_UNDER_NOW; + } } static void addInTree(AstNode* nodep, bool linkable) { #ifndef VL_LEAK_CHECKS - if (!linkable) return; // save some time, else the map will get huge! + if (!linkable) return; // save some time, else the map will get huge! #endif - NodeMap::iterator iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) { + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter == s_nodes.end()) { #ifdef VL_LEAK_CHECKS - nodep->v3fatalSrc("AstNode is in tree, but not allocated"); + nodep->v3fatalSrc("AstNode is in tree, but not allocated"); #endif - } else { - if (!(iter->second & FLAG_ALLOCATED)) { + } else { + if (!(iter->second & FLAG_ALLOCATED)) { #ifdef VL_LEAK_CHECKS - nodep->v3fatalSrc("AstNode is in tree, but not allocated"); + nodep->v3fatalSrc("AstNode is in tree, but not allocated"); #endif - } - if (iter->second & FLAG_IN_TREE) { - nodep->v3fatalSrc("AstNode is already in tree at another location"); - } - } - int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0); - if (iter == s_nodes.end()) { - s_nodes.insert(make_pair(nodep,or_flags)); - } else { - iter->second |= or_flags; - } + } + if (iter->second & FLAG_IN_TREE) { + nodep->v3fatalSrc("AstNode is already in tree at another location"); + } + } + int or_flags = FLAG_IN_TREE | (linkable?FLAG_LINKABLE:0); + if (iter == s_nodes.end()) { + s_nodes.insert(make_pair(nodep, or_flags)); + } else { + iter->second |= or_flags; + } } static bool okIfLinkedTo(const AstNode* nodep) { - // Someone has a pointer to this node. Is it kosher? - NodeMap::iterator iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; + // Someone has a pointer to this node. Is it kosher? + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter == s_nodes.end()) return false; #ifdef VL_LEAK_CHECKS - if (!(iter->second & FLAG_ALLOCATED)) return false; + if (!(iter->second & FLAG_ALLOCATED)) return false; #endif - if (!(iter->second & FLAG_IN_TREE)) return false; - if (!(iter->second & FLAG_LINKABLE)) return false; - return true; + if (!(iter->second & FLAG_IN_TREE)) return false; + if (!(iter->second & FLAG_LINKABLE)) return false; + return true; } static bool okIfBelow(const AstNode* nodep) { - // Must be linked to and below current node - if (!okIfLinkedTo(nodep)) return false; - NodeMap::iterator iter = s_nodes.find(nodep); - if (iter == s_nodes.end()) return false; - if (!(iter->second & FLAG_UNDER_NOW)) return false; - return true; + // Must be linked to and below current node + if (!okIfLinkedTo(nodep)) return false; + NodeMap::iterator iter = s_nodes.find(nodep); + if (iter == s_nodes.end()) return false; + if (!(iter->second & FLAG_UNDER_NOW)) return false; + return true; } static void prepForTree() { #ifndef VL_LEAK_CHECKS - s_nodes.clear(); + s_nodes.clear(); #endif - for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { - it->second &= ~FLAG_IN_TREE; - it->second &= ~FLAG_LINKABLE; - } + for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { + it->second &= ~FLAG_IN_TREE; + it->second &= ~FLAG_LINKABLE; + } } static void doneWithTree() { - for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without - for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { - if ((it->second & FLAG_ALLOCATED) - && !(it->second & FLAG_IN_TREE) - && !(it->second & FLAG_LEAKED) - && (it->first->backp() ? backs==1 : backs==0)) { - // Use only AstNode::dump instead of the virtual one, as there - // may be varp() and other cross links that are bad. - if (v3Global.opt.debugCheck()) { + for (int backs=0; backs<2; backs++) { // Those with backp() are probably under one leaking without + for (NodeMap::iterator it = s_nodes.begin(); it != s_nodes.end(); ++it) { + if ((it->second & FLAG_ALLOCATED) + && !(it->second & FLAG_IN_TREE) + && !(it->second & FLAG_LEAKED) + && (it->first->backp() ? backs==1 : backs==0)) { + // Use only AstNode::dump instead of the virtual one, as there + // may be varp() and other cross links that are bad. + if (v3Global.opt.debugCheck()) { std::cerr<<"%Error: LeakedNode"<<(it->first->backp()?"Back: ":": "); AstNode* rawp = const_cast (static_cast(it->first)); rawp->AstNode::dump(std::cerr); std::cerr<second |= FLAG_LEAKED; - } - } - } + V3Error::incErrors(); + } + it->second |= FLAG_LEAKED; + } + } + } } public: // CONSTUCTORS @@ -190,16 +190,16 @@ class BrokenMarkVisitor : public AstNVisitor { // Mark every node in the tree private: // NODE STATE - // Nothing! // This may be called deep inside other routines - // // so userp and friends may not be used + // Nothing! // This may be called deep inside other routines + // // so userp and friends may not be used // METHODS void processAndIterate(AstNode* nodep) { - BrokenTable::addInTree(nodep, nodep->maybePointedTo()); + BrokenTable::addInTree(nodep, nodep->maybePointedTo()); iterateChildrenConst(nodep); } // VISITORS virtual void visit(AstNode* nodep) { - processAndIterate(nodep); + processAndIterate(nodep); } public: // CONSTUCTORS @@ -215,17 +215,17 @@ public: class BrokenCheckVisitor : public AstNVisitor { private: void checkWidthMin(const AstNode* nodep) { - if (nodep->width() != nodep->widthMin() - && v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) { - nodep->v3fatalSrc("Width != WidthMin"); - } + if (nodep->width() != nodep->widthMin() + && v3Global.widthMinUsage()==VWidthMinUsage::MATCHES_WIDTH) { + nodep->v3fatalSrc("Width != WidthMin"); + } } void processAndIterate(AstNode* nodep) { - BrokenTable::setUnder(nodep,true); - if (const char* whyp=nodep->broken()) { - nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<dtypep()) { + BrokenTable::setUnder(nodep, true); + if (const char* whyp=nodep->broken()) { + nodep->v3fatalSrc("Broken link in node (or something without maybePointedTo): "<dtypep()) { if (!nodep->dtypep()->brokeExists()) { nodep->v3fatalSrc("Broken link in node->dtypep() to " <dtypep())); @@ -233,31 +233,34 @@ private: nodep->v3fatalSrc("Non-dtype link in node->dtypep() to " <dtypep())); } - } - if (v3Global.assertDTypesResolved()) { - if (nodep->hasDType()) { - if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype on node with hasDType(): "<prettyTypeName()); - } else { - if (nodep->dtypep()) nodep->v3fatalSrc("DType on node without hasDType(): "<prettyTypeName()); - } - if (nodep->getChildDTypep()) nodep->v3fatalSrc("childDTypep() non-null on node after should have removed"); + } + if (v3Global.assertDTypesResolved()) { + if (nodep->hasDType()) { + if (!nodep->dtypep()) nodep->v3fatalSrc( + "No dtype on node with hasDType(): "<prettyTypeName()); + } else { + if (nodep->dtypep()) nodep->v3fatalSrc( + "DType on node without hasDType(): "<prettyTypeName()); + } + if (nodep->getChildDTypep()) nodep->v3fatalSrc( + "childDTypep() non-null on node after should have removed"); if (const AstNodeDType* dnodep = VN_CAST(nodep, NodeDType)) checkWidthMin(dnodep); - } - checkWidthMin(nodep); + } + checkWidthMin(nodep); iterateChildrenConst(nodep); - BrokenTable::setUnder(nodep,false); + BrokenTable::setUnder(nodep, false); } virtual void visit(AstNodeAssign* nodep) { - processAndIterate(nodep); - if (v3Global.assertDTypesResolved() - && nodep->brokeLhsMustBeLvalue() + processAndIterate(nodep); + if (v3Global.assertDTypesResolved() + && nodep->brokeLhsMustBeLvalue() && VN_IS(nodep->lhsp(), NodeVarRef) && !VN_CAST(nodep->lhsp(), NodeVarRef)->lvalue()) { - nodep->v3fatalSrc("Assignment LHS is not an lvalue"); - } + nodep->v3fatalSrc("Assignment LHS is not an lvalue"); + } } virtual void visit(AstNode* nodep) { - processAndIterate(nodep); + processAndIterate(nodep); } public: // CONSTUCTORS @@ -274,15 +277,15 @@ void V3Broken::brokenAll(AstNetlist* nodep) { //UINFO(9,__FUNCTION__<<": "<fileline(), m_basename + "_" + cvtToStr(++m_funcNum), NULL, "void"); - m_funcp->isStatic(false); - m_funcp->declPrivate(true); - m_funcp->slow(true); - m_funcp->argTypes(m_argsp); - m_modp->addStmtp(m_funcp); + if (v3Global.opt.outputSplitCFuncs() + && v3Global.opt.outputSplitCFuncs() < m_numStmts) { + m_funcp = NULL; + } + if (!m_funcp) { + m_funcp = new AstCFunc(m_modp->fileline(), + m_basename+"_"+cvtToStr(++m_funcNum), NULL, "void"); + m_funcp->isStatic(false); + m_funcp->declPrivate(true); + m_funcp->slow(true); + m_funcp->argTypes(m_argsp); + m_modp->addStmtp(m_funcp); - // Add a top call to it - AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp); - callp->argTypes(m_callargsp); + // Add a top call to it + AstCCall* callp = new AstCCall(m_modp->fileline(), m_funcp); + callp->argTypes(m_callargsp); - m_tlFuncp->addStmtsp(callp); - m_numStmts = 0; - } - m_funcp->addStmtsp(nodep); - m_numStmts += 1; + m_tlFuncp->addStmtsp(callp); + m_numStmts = 0; + } + m_funcp->addStmtsp(nodep); + m_numStmts += 1; } V3CCtorsVisitor(AstNodeModule* nodep, const string& basename, const string& argsp="", const string& callargsp="", - const string& stmt="") { - m_basename = basename; - m_argsp = argsp; - m_callargsp = callargsp; - m_modp = nodep; - m_numStmts = 0; - m_funcNum = 0; - m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void"); - m_tlFuncp->declPrivate(true); - m_tlFuncp->isStatic(false); - m_tlFuncp->slow(true); - m_tlFuncp->argTypes(m_argsp); - if (stmt != "") { - m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - } - m_funcp = m_tlFuncp; - m_modp->addStmtp(m_tlFuncp); + const string& stmt="") { + m_basename = basename; + m_argsp = argsp; + m_callargsp = callargsp; + m_modp = nodep; + m_numStmts = 0; + m_funcNum = 0; + m_tlFuncp = new AstCFunc(nodep->fileline(), basename, NULL, "void"); + m_tlFuncp->declPrivate(true); + m_tlFuncp->isStatic(false); + m_tlFuncp->slow(true); + m_tlFuncp->argTypes(m_argsp); + if (stmt != "") { + m_tlFuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + } + m_funcp = m_tlFuncp; + m_modp->addStmtp(m_tlFuncp); } ~V3CCtorsVisitor() {} private: @@ -116,36 +117,38 @@ void V3CCtors::evalAsserts() { if (AstVar* varp = VN_CAST(np, Var)) { if (varp->isPrimaryInish() && !varp->isSc()) { if (AstBasicDType* basicp = VN_CAST(varp->dtypeSkipRefp(), BasicDType)) { - int storedWidth = basicp->widthAlignBytes() * 8; - int lastWordWidth = varp->width() % storedWidth; - if (lastWordWidth != 0) { - // if (signal & CONST(upper_non_clean_mask)) { fail; } - AstNode* newp = new AstVarRef(varp->fileline(), varp, false); - if (varp->isWide()) { - newp = new AstWordSel(varp->fileline(), newp, - new AstConst(varp->fileline(), varp->widthWords()-1)); - } + int storedWidth = basicp->widthAlignBytes() * 8; + int lastWordWidth = varp->width() % storedWidth; + if (lastWordWidth != 0) { + // if (signal & CONST(upper_non_clean_mask)) { fail; } + AstNode* newp = new AstVarRef(varp->fileline(), varp, false); + if (varp->isWide()) { + newp = new AstWordSel( + varp->fileline(), newp, + new AstConst(varp->fileline(), varp->widthWords()-1)); + } uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth); V3Number num (varp, storedWidth, value); newp = new AstAnd(varp->fileline(), newp, - new AstConst(varp->fileline(), num)); - AstNodeIf* ifp = new AstIf(varp->fileline(), newp, + new AstConst(varp->fileline(), num)); + AstNodeIf* ifp = new AstIf(varp->fileline(), newp, new AstCStmt(varp->fileline(), "Verilated::overWidthError(\""+varp->prettyName()+"\");")); - ifp->branchPred(AstBranchPred::BP_UNLIKELY); - newp = ifp; - funcp->addStmtsp(newp); - } - } - } - } + ifp->branchPred(AstBranchPred::BP_UNLIKELY); + newp = ifp; + funcp->addStmtsp(newp); + } + } + } + } } } void V3CCtors::cctorsAll() { UINFO(2,__FUNCTION__<<": "<modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) { + for (AstNodeModule* modp = v3Global.rootp()->modulesp(); + modp; modp = VN_CAST(modp->nextp(), NodeModule)) { // Process each module in turn { V3CCtorsVisitor var_reset (modp, "_ctor_var_reset"); @@ -153,14 +156,16 @@ void V3CCtors::cctorsAll() { if (AstVar* varp = VN_CAST(np, Var)) { if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) { - var_reset.add(new AstCReset(varp->fileline(), new AstVarRef(varp->fileline(), varp, true))); + var_reset.add(new AstCReset(varp->fileline(), + new AstVarRef(varp->fileline(), varp, true))); } } } } if (v3Global.opt.coverage()) { V3CCtorsVisitor configure_coverage - (modp, "_configure_coverage", EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first", + (modp, "_configure_coverage", + EmitCBaseVisitor::symClassVar()+ ", bool first", "vlSymsp, first", "if (0 && vlSymsp && first) {} // Prevent unused\n"); for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { if (AstCoverDecl* coverp = VN_CAST(np, CoverDecl)) { diff --git a/src/V3CCtors.h b/src/V3CCtors.h index 7a935c23c..aa0a436ae 100644 --- a/src/V3CCtors.h +++ b/src/V3CCtors.h @@ -37,4 +37,4 @@ private: }; -#endif // Guard +#endif // Guard diff --git a/src/V3Case.cpp b/src/V3Case.cpp index bbc6594ca..781f8d793 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -20,20 +20,21 @@ // V3Case's Transformations: // // Each module: -// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel -// Need __en in changed list if a signal is on the LHS of a assign -// Cases: -// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask) -// (AND item1 mask)) -// (other items)) -// body -// Or, converts to a if/else tree. -// FUTURES: -// Large 16+ bit tables with constants and no masking (address muxes) +// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel +// Need __en in changed list if a signal is on the LHS of a assign +// Cases: +// CASE(v) CASEITEM(items,body) -> IF (OR (EQ (AND v mask) +// (AND item1 mask)) +// (other items)) +// body +// Or, converts to a if/else tree. +// FUTURES: +// Large 16+ bit tables with constants and no masking (address muxes) // Enter all into std::multimap, sort by value and use a tree of < and == compares. -// "Diagonal" find of {rightmost,leftmost} bit {set,clear} +// "Diagonal" find of {rightmost,leftmost} bit {set,clear} // Ignoring mask, check each value is unique (using std::multimap as above?) -// Each branch is then mask-and-compare operation (IE <000000001_000000000 at midpoint.) +// Each branch is then mask-and-compare operation (IE +// <000000001_000000000 at midpoint.) // //************************************************************************* @@ -48,60 +49,62 @@ #include #include -#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in -#define CASE_BARF 999999 // Magic width when non-constant -#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree +#define CASE_OVERLAP_WIDTH 12 // Maximum width we can check for overlaps in +#define CASE_BARF 999999 // Magic width when non-constant +#define CASE_ENCODER_GROUP_DEPTH 8 // Levels of priority to be ORed together in top IF tree //###################################################################### class CaseLintVisitor : public AstNVisitor { private: - AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement + AstNodeCase* m_caseExprp; // Under a CASE value node, if so the relevant case statement // METHODS VL_DEBUG_FUNC; // Declare debug() virtual void visit(AstNodeCase* nodep) { if (VN_IS(nodep, Case) && VN_CAST(nodep, Case)->casex()) { - nodep->v3warn(CASEX,"Suggest casez (with ?'s) in place of casex (with X's)"); - } - // Detect multiple defaults - bool hitDefault = false; - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - if (itemp->isDefault()) { - if (hitDefault) { - nodep->v3error("Multiple default statements in case statement."); - } - hitDefault = true; - } - } + nodep->v3warn(CASEX, "Suggest casez (with ?'s) in place of casex (with X's)"); + } + // Detect multiple defaults + bool hitDefault = false; + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { + if (itemp->isDefault()) { + if (hitDefault) { + nodep->v3error("Multiple default statements in case statement."); + } + hitDefault = true; + } + } - // Check for X/Z in non-casex statements - { - m_caseExprp = nodep; + // Check for X/Z in non-casex statements + { + m_caseExprp = nodep; iterate(nodep->exprp()); - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { iterateAndNextNull(itemp->condsp()); - } - m_caseExprp = NULL; - } + } + m_caseExprp = NULL; + } } virtual void visit(AstConst* nodep) { - // See also neverItem - if (m_caseExprp && nodep->num().isFourState()) { + // See also neverItem + if (m_caseExprp && nodep->num().isFourState()) { if (VN_IS(m_caseExprp, GenCase)) { - nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')"); + nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')"); } else if (VN_IS(m_caseExprp, Case) && VN_CAST(m_caseExprp, Case)->casex()) { - // Don't sweat it, we already complained about casex in general + // Don't sweat it, we already complained about casex in general } else if (VN_IS(m_caseExprp, Case) && (VN_CAST(m_caseExprp, Case)->casez() || VN_CAST(m_caseExprp, Case)->caseInside())) { - if (nodep->num().isUnknown()) { - nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)"); - } - } else { - nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)"); - } - } + if (nodep->num().isUnknown()) { + nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)"); + } + } else { + nodep->v3warn(CASEWITHX, "Use of x/? constant in case statement, (perhaps intended casex/casez)"); + } + } } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -109,7 +112,7 @@ private: public: // CONSTUCTORS explicit CaseLintVisitor(AstNodeCase* nodep) { - m_caseExprp = NULL; + m_caseExprp = NULL; iterate(nodep); } virtual ~CaseLintVisitor() {} @@ -122,340 +125,347 @@ class CaseVisitor : public AstNVisitor { private: // NODE STATE // Cleared each Case - // AstIf::user3() -> bool. Set true to indicate clone not needed - AstUser3InUse m_inuser3; + // AstIf::user3() -> bool. Set true to indicate clone not needed + AstUser3InUse m_inuser3; // STATE - V3Double0 m_statCaseFast; // Statistic tracking - V3Double0 m_statCaseSlow; // Statistic tracking + V3Double0 m_statCaseFast; // Statistic tracking + V3Double0 m_statCaseSlow; // Statistic tracking // Per-CASE - int m_caseWidth; // Width of valueItems - int m_caseItems; // Number of caseItem unique values - bool m_caseNoOverlapsAllCovered; // Proven to be synopsys parallel_case compliant - AstNode* m_valueItem[1<itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) { - if (icondp->width() > width) width = icondp->width(); - if (icondp->isDouble()) opaque = true; + int width = 0; + bool opaque = false; + m_caseItems = 0; + m_caseNoOverlapsAllCovered = true; + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { + for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) { + if (icondp->width() > width) width = icondp->width(); + if (icondp->isDouble()) opaque = true; if (!VN_IS(icondp, Const)) width = CASE_BARF; // Can't parse; not a constant - m_caseItems++; - } - } - m_caseWidth = width; - if (width==0 || width > CASE_OVERLAP_WIDTH || opaque) { - m_caseNoOverlapsAllCovered = false; - return false; // Too wide for analysis - } - UINFO(8,"Simple case statement: "< CASE_OVERLAP_WIDTH || opaque) { + m_caseNoOverlapsAllCovered = false; + return false; // Too wide for analysis + } + UINFO(8,"Simple case statement: "<itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) { - //if (debug()>=9) icondp->dumpTree(cout," caseitem: "); + // Now pick up the values for each assignment + // We can cheat and use uint32_t's because we only support narrow case's + bool bitched = false; + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { + for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondp->nextp()) { + //if (debug()>=9) icondp->dumpTree(cout, " caseitem: "); AstConst* iconstp = VN_CAST(icondp, Const); - if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this"); - if (neverItem(nodep, iconstp)) { - // X in casez can't ever be executed - } else { + if (!iconstp) nodep->v3fatalSrc("above 'can't parse' should have caught this"); + if (neverItem(nodep, iconstp)) { + // X in casez can't ever be executed + } else { V3Number nummask (itemp, iconstp->width()); nummask.opBitsNonX(iconstp->num()); uint32_t mask = nummask.toUInt(); V3Number numval (itemp, iconstp->width()); - numval.opBitsOne(iconstp->num()); - uint32_t val = numval.toUInt(); + numval.opBitsOne(iconstp->num()); + uint32_t val = numval.toUInt(); for (uint32_t i=0; i<(1UL<ignoreOverlap() && !bitched) { - itemp->v3warn(CASEOVERLAP,"Case values overlap (example pattern 0x"<isDefault()) { // Case statement's default... Fill the table + if ((i & mask) == val) { + if (!m_valueItem[i]) { + m_valueItem[i] = itemp; + } else if (!itemp->ignoreOverlap() && !bitched) { + itemp->v3warn(CASEOVERLAP, "Case values overlap (example pattern 0x" + <isDefault()) { // Case statement's default... Fill the table for (uint32_t i=0; i<(1UL<v3warn(CASEINCOMPLETE,"Case values incompletely covered (example pattern 0x"<v3warn(CASEINCOMPLETE, "Case values incompletely covered (example pattern 0x" + <bodysp(); - } - return true; // All is fine + } + return true; // All is fine } AstNode* replaceCaseFastRecurse(AstNode* cexprp, int msb, uint32_t upperValue) { - if (msb<0) { - // There's no space for a IF. We know upperValue is thus down to a specific - // exact value, so just return the tree value - // Note can't clone here, as we're going to check for equivelence above - return m_valueItem[upperValue]; - } - else { - // Make left and right subtrees - // cexpr[msb:lsb] == 1 - AstNode* tree0p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | 0); - AstNode* tree1p = replaceCaseFastRecurse(cexprp, msb-1, upperValue | (1UL<deleteTree(); VL_DANGLING(tree1p); - return tree0p; - } + if (tree0p == tree1p) { + // Same logic on both sides, so we can just return one of 'em + return tree0p; + } + // We could have a "checkerboard" with A B A B, we can use the same IF on both edges + bool same = true; + for (uint32_t a=upperValue, + b=(upperValue|(1UL<deleteTree(); VL_DANGLING(tree1p); + return tree0p; + } - // Must have differing logic, so make a selection + // Must have differing logic, so make a selection - // Case expressions can't be linked twice, so clone them - if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true); - if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true); + // Case expressions can't be linked twice, so clone them + if (tree0p && !tree0p->user3()) tree0p = tree0p->cloneTree(true); + if (tree1p && !tree1p->user3()) tree1p = tree1p->cloneTree(true); // Alternate scheme if we ever do multiple bits at a time: //V3Number nummask (cexprp, cexprp->width(), (1UL<fileline(), cexprp->cloneTree(false), - // new AstConst(cexprp->fileline(), nummask)); - AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false), - msb, 1); - AstNode* eqp = new AstNeq(cexprp->fileline(), - new AstConst(cexprp->fileline(), 0), - and1p); - AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p); - ifp->user3(1); // So we don't bother to clone it - return ifp; - } + // new AstConst(cexprp->fileline(), nummask)); + AstNode* and1p = new AstSel(cexprp->fileline(), cexprp->cloneTree(false), + msb, 1); + AstNode* eqp = new AstNeq(cexprp->fileline(), + new AstConst(cexprp->fileline(), 0), + and1p); + AstIf* ifp = new AstIf(cexprp->fileline(), eqp, tree1p, tree0p); + ifp->user3(1); // So we don't bother to clone it + return ifp; + } } void replaceCaseFast(AstCase* nodep) { - // CASEx(cexpr,.... - // -> tree of IF(msb, IF(msb-1, 11, 10) - // IF(msb-1, 01, 00)) - AstNode* cexprp = nodep->exprp()->unlinkFrBack(); + // CASEx(cexpr,.... + // -> tree of IF(msb, IF(msb-1, 11, 10) + // IF(msb-1, 01, 00)) + AstNode* cexprp = nodep->exprp()->unlinkFrBack(); - if (debug()>=9) { + if (debug()>=9) { for (uint32_t i=0; i<(1UL<user3()) ifrootp = ifrootp->cloneTree(true); + AstNode::user3ClearTree(); + AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth-1, 0UL); + // Case expressions can't be linked twice, so clone them + if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true); - if (ifrootp) nodep->replaceWith(ifrootp); - else nodep->unlinkFrBack(); - nodep->deleteTree(); VL_DANGLING(nodep); - cexprp->deleteTree(); VL_DANGLING(cexprp); - if (debug()>=9) ifrootp->dumpTree(cout," _simp: "); + if (ifrootp) nodep->replaceWith(ifrootp); + else nodep->unlinkFrBack(); + nodep->deleteTree(); VL_DANGLING(nodep); + cexprp->deleteTree(); VL_DANGLING(cexprp); + if (debug()>=9) ifrootp->dumpTree(cout, " _simp: "); } void replaceCaseComplicated(AstCase* nodep) { - // CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3)) - // -> IF((cexpr==icond1),istmts1, - // IF((EQ (AND MASK cexpr) (AND MASK icond1) - // ,istmts2, istmts3 - AstNode* cexprp = nodep->exprp()->unlinkFrBack(); - // We'll do this in two stages. First stage, convert the conditions to - // the appropriate IF AND terms. - if (debug()>=9) nodep->dumpTree(cout," _comp_IN: "); - bool hadDefault = false; + // CASEx(cexpr,ITEM(icond1,istmts1),ITEM(icond2,istmts2),ITEM(default,istmts3)) + // -> IF((cexpr==icond1),istmts1, + // IF((EQ (AND MASK cexpr) (AND MASK icond1) + // ,istmts2, istmts3 + AstNode* cexprp = nodep->exprp()->unlinkFrBack(); + // We'll do this in two stages. First stage, convert the conditions to + // the appropriate IF AND terms. + if (debug()>=9) nodep->dumpTree(cout, " _comp_IN: "); + bool hadDefault = false; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - if (!itemp->condsp()) { - // Default clause. Just make true, we'll optimize it away later - itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue())); - hadDefault = true; - } else { - // Expressioned clause - AstNode* icondNextp = NULL; - AstNode* ifexprp = NULL; // If expression to test - for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) { - icondNextp = icondp->nextp(); - icondp->unlinkFrBack(); + if (!itemp->condsp()) { + // Default clause. Just make true, we'll optimize it away later + itemp->condsp(new AstConst(itemp->fileline(), AstConst::LogicTrue())); + hadDefault = true; + } else { + // Expressioned clause + AstNode* icondNextp = NULL; + AstNode* ifexprp = NULL; // If expression to test + for (AstNode* icondp = itemp->condsp(); icondp!=NULL; icondp=icondNextp) { + icondNextp = icondp->nextp(); + icondp->unlinkFrBack(); - AstNode* condp = NULL; // Default is to use and1p/and2p + AstNode* condp = NULL; // Default is to use and1p/and2p AstConst* iconstp = VN_CAST(icondp, Const); - if (iconstp && neverItem(nodep, iconstp)) { - // X in casez can't ever be executed - icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp); - // For simplicity, make expression that is not equal, and let later - // optimizations remove it - condp = new AstConst(itemp->fileline(), AstConst::LogicFalse()); + if (iconstp && neverItem(nodep, iconstp)) { + // X in casez can't ever be executed + icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp); + // For simplicity, make expression that is not equal, and let later + // optimizations remove it + condp = new AstConst(itemp->fileline(), AstConst::LogicFalse()); } else if (AstInsideRange* irangep = VN_CAST(icondp, InsideRange)) { - // Similar logic in V3Width::visit(AstInside) - AstNode* ap = AstGte::newTyped(itemp->fileline(), - cexprp->cloneTree(false), - irangep->lhsp()->unlinkFrBack()); - AstNode* bp = AstLte::newTyped(itemp->fileline(), - cexprp->cloneTree(false), - irangep->rhsp()->unlinkFrBack()); - condp = new AstAnd(itemp->fileline(), ap, bp); - } else if (iconstp && iconstp->num().isFourState() - && (nodep->casex() || nodep->casez() || nodep->caseInside())) { + // Similar logic in V3Width::visit(AstInside) + AstNode* ap = AstGte::newTyped(itemp->fileline(), + cexprp->cloneTree(false), + irangep->lhsp()->unlinkFrBack()); + AstNode* bp = AstLte::newTyped(itemp->fileline(), + cexprp->cloneTree(false), + irangep->rhsp()->unlinkFrBack()); + condp = new AstAnd(itemp->fileline(), ap, bp); + } else if (iconstp && iconstp->num().isFourState() + && (nodep->casex() || nodep->casez() || nodep->caseInside())) { V3Number nummask (itemp, iconstp->width()); nummask.opBitsNonX(iconstp->num()); V3Number numval (itemp, iconstp->width()); - numval.opBitsOne(iconstp->num()); - AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false), - new AstConst(itemp->fileline(), nummask)); - AstNode* and2p = new AstAnd(itemp->fileline(), - new AstConst(itemp->fileline(), numval), - new AstConst(itemp->fileline(), nummask)); - icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp); - condp = AstEq::newTyped(itemp->fileline(), and1p, and2p); - } else { - // Not a caseX mask, we can simply build CASEEQ(cexpr icond) - AstNode* and1p = cexprp->cloneTree(false); - AstNode* and2p = icondp; - condp = AstEq::newTyped(itemp->fileline(), and1p, and2p); - } - if (!ifexprp) { - ifexprp = condp; - } else { - ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp); - } - } - // Replace expression in tree - itemp->condsp(ifexprp); - } - } - cexprp->deleteTree(); VL_DANGLING(cexprp); - if (!hadDefault) { - // If there was no default, add a empty one, this greatly simplifies below code - // and constant propagation will just eliminate it for us later. - nodep->addItemsp(new AstCaseItem(nodep->fileline(), - new AstConst(nodep->fileline(), AstConst::LogicTrue()), - NULL)); - } - if (debug()>=9) nodep->dumpTree(cout," _comp_COND: "); - // Now build the IF statement tree - // The tree can be quite huge. Pull ever group of 8 out, and make a OR tree. - // This reduces the depth for the bottom elements, at the cost of some of the top elements. - // If we ever have profiling data, we should pull out the most common item from here and - // instead make it the first IF branch. - int depth = 0; - AstNode* grouprootp = NULL; - AstIf* groupnextp = NULL; - AstIf* itemnextp = NULL; + numval.opBitsOne(iconstp->num()); + AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false), + new AstConst(itemp->fileline(), nummask)); + AstNode* and2p = new AstAnd(itemp->fileline(), + new AstConst(itemp->fileline(), numval), + new AstConst(itemp->fileline(), nummask)); + icondp->deleteTree(); VL_DANGLING(icondp); VL_DANGLING(iconstp); + condp = AstEq::newTyped(itemp->fileline(), and1p, and2p); + } else { + // Not a caseX mask, we can simply build CASEEQ(cexpr icond) + AstNode* and1p = cexprp->cloneTree(false); + AstNode* and2p = icondp; + condp = AstEq::newTyped(itemp->fileline(), and1p, and2p); + } + if (!ifexprp) { + ifexprp = condp; + } else { + ifexprp = new AstLogOr(itemp->fileline(), ifexprp, condp); + } + } + // Replace expression in tree + itemp->condsp(ifexprp); + } + } + cexprp->deleteTree(); VL_DANGLING(cexprp); + if (!hadDefault) { + // If there was no default, add a empty one, this greatly simplifies below code + // and constant propagation will just eliminate it for us later. + nodep->addItemsp(new AstCaseItem(nodep->fileline(), + new AstConst(nodep->fileline(), AstConst::LogicTrue()), + NULL)); + } + if (debug()>=9) nodep->dumpTree(cout, " _comp_COND: "); + // Now build the IF statement tree + // The tree can be quite huge. Pull ever group of 8 out, and make a OR tree. + // This reduces the depth for the bottom elements, at the cost of + // some of the top elements. If we ever have profiling data, we + // should pull out the most common item from here and instead make + // it the first IF branch. + int depth = 0; + AstNode* grouprootp = NULL; + AstIf* groupnextp = NULL; + AstIf* itemnextp = NULL; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action. - if (istmtsp) istmtsp->unlinkFrBackWithNext(); - // Expressioned clause - AstNode* ifexprp = itemp->condsp()->unlinkFrBack(); - { // Prepare for next group - if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1; - if (depth == 1) { // First group or starting new group - itemnextp = NULL; - AstIf* newp = new AstIf(itemp->fileline(), ifexprp->cloneTree(true), NULL, NULL); - if (groupnextp) groupnextp->addElsesp(newp); - else grouprootp = newp; - groupnextp = newp; - } else { // Continue group, modify if condition to OR in this new condition - AstNode* condp = groupnextp->condp()->unlinkFrBack(); - groupnextp->condp(new AstOr(ifexprp->fileline(), - condp, - ifexprp->cloneTree(true))); - } - } - { // Make the new lower IF and attach in the tree - AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp); - if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition - itemexprp->deleteTree(); VL_DANGLING(itemexprp); - itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue()); - } - AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL); - if (itemnextp) itemnextp->addElsesp(newp); - else groupnextp->addIfsp(newp); // First in a new group - itemnextp = newp; - } - } - if (debug()>=9) nodep->dumpTree(cout," _comp_TREE: "); - // Handle any assertions - replaceCaseParallel(nodep, false); - // Replace the CASE... with IF... - if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout," _new: "); - if (grouprootp) nodep->replaceWith(grouprootp); - else nodep->unlinkFrBack(); - nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* istmtsp = itemp->bodysp(); // Maybe null -- no action. + if (istmtsp) istmtsp->unlinkFrBackWithNext(); + // Expressioned clause + AstNode* ifexprp = itemp->condsp()->unlinkFrBack(); + { // Prepare for next group + if (++depth > CASE_ENCODER_GROUP_DEPTH) depth = 1; + if (depth == 1) { // First group or starting new group + itemnextp = NULL; + AstIf* newp = new AstIf(itemp->fileline(), + ifexprp->cloneTree(true), NULL, NULL); + if (groupnextp) groupnextp->addElsesp(newp); + else grouprootp = newp; + groupnextp = newp; + } else { // Continue group, modify if condition to OR in this new condition + AstNode* condp = groupnextp->condp()->unlinkFrBack(); + groupnextp->condp(new AstOr(ifexprp->fileline(), + condp, + ifexprp->cloneTree(true))); + } + } + { // Make the new lower IF and attach in the tree + AstNode* itemexprp = ifexprp; VL_DANGLING(ifexprp); + if (depth == (CASE_ENCODER_GROUP_DEPTH)) { // End of group - can skip the condition + itemexprp->deleteTree(); VL_DANGLING(itemexprp); + itemexprp = new AstConst(itemp->fileline(), AstConst::LogicTrue()); + } + AstIf* newp = new AstIf(itemp->fileline(), itemexprp, istmtsp, NULL); + if (itemnextp) itemnextp->addElsesp(newp); + else groupnextp->addIfsp(newp); // First in a new group + itemnextp = newp; + } + } + if (debug()>=9) nodep->dumpTree(cout, " _comp_TREE: "); + // Handle any assertions + replaceCaseParallel(nodep, false); + // Replace the CASE... with IF... + if (debug()>=9 && grouprootp) grouprootp->dumpTree(cout, " _new: "); + if (grouprootp) nodep->replaceWith(grouprootp); + else nodep->unlinkFrBack(); + nodep->deleteTree(); VL_DANGLING(nodep); } void replaceCaseParallel(AstCase* nodep, bool noOverlapsAllCovered) { - // Take the notParallelp tree under the case statement created by V3Assert - // If the statement was proven to have no overlaps and all cases covered, we're done with it. - // Else, convert to a normal statement parallel with the case statement. - if (nodep->notParallelp() && !noOverlapsAllCovered) { - AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext(); - nodep->addNextHere(parp); - } + // Take the notParallelp tree under the case statement created by V3Assert + // If the statement was proven to have no overlaps and all cases + // covered, we're done with it. + // Else, convert to a normal statement parallel with the case statement. + if (nodep->notParallelp() && !noOverlapsAllCovered) { + AstNode* parp = nodep->notParallelp()->unlinkFrBackWithNext(); + nodep->addNextHere(parp); + } } bool neverItem(AstCase* casep, AstConst* itemp) { - // Xs in case or casez are impossible due to two state simulations - if (casep->casex()) { - } else if (casep->casez() || casep->caseInside()) { - if (itemp->num().isUnknown()) return true; - } else { - if (itemp->num().isFourState()) return true; - } - return false; + // Xs in case or casez are impossible due to two state simulations + if (casep->casex()) { + } else if (casep->casez() || casep->caseInside()) { + if (itemp->num().isUnknown()) return true; + } else { + if (itemp->num().isFourState()) return true; + } + return false; } // VISITORS virtual void visit(AstCase* nodep) { - V3Case::caseLint(nodep); + V3Case::caseLint(nodep); iterateChildren(nodep); - if (debug()>=9) nodep->dumpTree(cout," case_old: "); - if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) { - // It's a simple priority encoder or complete statement - // we can make a tree of statements to avoid extra comparisons - ++m_statCaseFast; - replaceCaseFast(nodep); VL_DANGLING(nodep); - } else { - ++m_statCaseSlow; - replaceCaseComplicated(nodep); VL_DANGLING(nodep); - } + if (debug()>=9) nodep->dumpTree(cout, " case_old: "); + if (isCaseTreeFast(nodep) && v3Global.opt.oCase()) { + // It's a simple priority encoder or complete statement + // we can make a tree of statements to avoid extra comparisons + ++m_statCaseFast; + replaceCaseFast(nodep); VL_DANGLING(nodep); + } else { + ++m_statCaseSlow; + replaceCaseComplicated(nodep); VL_DANGLING(nodep); + } } //-------------------- // Default: Just iterate @@ -468,15 +478,15 @@ public: explicit CaseVisitor(AstNetlist* nodep) { m_caseWidth = 0; m_caseItems = 0; - m_caseNoOverlapsAllCovered = false; + m_caseNoOverlapsAllCovered = false; for (uint32_t i=0; i<(1UL<, or >= +// Unsignedness can be lost on results of the below operations, as they +// may need the sign bit for proper operation: +// /, %, /=, %=, <, <=, >, or >= // -// Signed values are always sign extended on promotion or right shift, -// even if assigning to a unsigned. +// Signed values are always sign extended on promotion or right shift, +// even if assigning to a unsigned. // //************************************************************************* @@ -58,8 +58,8 @@ class CastVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: - // AstNode::user() // bool. Indicates node is of known size - AstUser1InUse m_inuser1; + // AstNode::user() // bool. Indicates node is of known size + AstUser1InUse m_inuser1; // STATE @@ -67,97 +67,97 @@ private: VL_DEBUG_FUNC; // Declare debug() void insertCast(AstNode* nodep, int needsize) { // We'll insert ABOVE passed node - UINFO(4," NeedCast "<unlinkFrBack(&relinkHandle); - // + UINFO(4," NeedCast "<unlinkFrBack(&relinkHandle); + // AstCCast* castp = new AstCCast(nodep->fileline(), nodep, needsize, nodep->widthMin()); - relinkHandle.relink(castp); - //if (debug()>8) castp->dumpTree(cout,"-castins: "); - // - insureLower32Cast(castp); - nodep->user1(1); // Now must be of known size + relinkHandle.relink(castp); + //if (debug()>8) castp->dumpTree(cout, "-castins: "); + // + insureLower32Cast(castp); + nodep->user1(1); // Now must be of known size } int castSize(AstNode* nodep) { - if (nodep->isQuad()) return VL_QUADSIZE; - else if (nodep->width()<=8) return 8; - else if (nodep->width()<=16) return 16; - else return VL_WORDSIZE; + if (nodep->isQuad()) return VL_QUADSIZE; + else if (nodep->width()<=8) return 8; + else if (nodep->width()<=16) return 16; + else return VL_WORDSIZE; } void insureCast(AstNode* nodep) { - if (castSize(nodep->backp()) != castSize(nodep) - || !nodep->user1()) { - insertCast(nodep, castSize(nodep->backp())); - } + if (castSize(nodep->backp()) != castSize(nodep) + || !nodep->user1()) { + insertCast(nodep, castSize(nodep->backp())); + } } void insureLower32Cast(AstCCast* nodep) { - // If we have uint64 = CAST(uint64(x)) then the upcasting - // really needs to be CAST(uint64(CAST(uint32(x))). - // Otherwise a (uint64)(a>b) would return wrong value, as - // less than has undeterministic signedness. - if (nodep->isQuad() && !nodep->lhsp()->isQuad() + // If we have uint64 = CAST(uint64(x)) then the upcasting + // really needs to be CAST(uint64(CAST(uint32(x))). + // Otherwise a (uint64)(a>b) would return wrong value, as + // less than has undeterministic signedness. + if (nodep->isQuad() && !nodep->lhsp()->isQuad() && !VN_IS(nodep->lhsp(), CCast)) { - insertCast(nodep->lhsp(), VL_WORDSIZE); - } + insertCast(nodep->lhsp(), VL_WORDSIZE); + } } // VISITORS virtual void visit(AstNodeUniop* nodep) { iterateChildren(nodep); - nodep->user1(nodep->lhsp()->user1()); - if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp()); + nodep->user1(nodep->lhsp()->user1()); + if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp()); } virtual void visit(AstNodeBiop* nodep) { iterateChildren(nodep); - nodep->user1(nodep->lhsp()->user1() - | nodep->rhsp()->user1()); - if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp()); - if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp()); + nodep->user1(nodep->lhsp()->user1() + | nodep->rhsp()->user1()); + if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp()); + if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp()); } virtual void visit(AstNodeTriop* nodep) { iterateChildren(nodep); - nodep->user1(nodep->lhsp()->user1() - | nodep->rhsp()->user1() - | nodep->thsp()->user1()); - if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp()); - if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp()); - if (nodep->sizeMattersThs()) insureCast(nodep->thsp()); + nodep->user1(nodep->lhsp()->user1() + | nodep->rhsp()->user1() + | nodep->thsp()->user1()); + if (nodep->sizeMattersLhs()) insureCast(nodep->lhsp()); + if (nodep->sizeMattersRhs()) insureCast(nodep->rhsp()); + if (nodep->sizeMattersThs()) insureCast(nodep->thsp()); } virtual void visit(AstCCast* nodep) { iterateChildren(nodep); - insureLower32Cast(nodep); - nodep->user1(1); + insureLower32Cast(nodep); + nodep->user1(1); } virtual void visit(AstNegate* nodep) { iterateChildren(nodep); - nodep->user1(nodep->lhsp()->user1()); - if (nodep->lhsp()->widthMin()==1) { - // We want to avoid a GCC "converting of negative value" warning - // from our expansion of - // out = {32{a out = - (alhsp(), castSize(nodep)); - } else { - insureCast(nodep->lhsp()); - } + nodep->user1(nodep->lhsp()->user1()); + if (nodep->lhsp()->widthMin()==1) { + // We want to avoid a GCC "converting of negative value" warning + // from our expansion of + // out = {32{a out = - (alhsp(), castSize(nodep)); + } else { + insureCast(nodep->lhsp()); + } } virtual void visit(AstVarRef* nodep) { - if (!nodep->lvalue() + if (!nodep->lvalue() && !VN_IS(nodep->backp(), CCast) && VN_IS(nodep->backp(), NodeMath) && !VN_IS(nodep->backp(), ArraySel) - && nodep->backp()->width() - && castSize(nodep) != castSize(nodep->varp())) { - // Cast vars to IData first, else below has upper bits wrongly set - // CData x=3; out = (QData)(x<<30); + && nodep->backp()->width() + && castSize(nodep) != castSize(nodep->varp())) { + // Cast vars to IData first, else below has upper bits wrongly set + // CData x=3; out = (QData)(x<<30); insertCast(nodep, castSize(nodep)); - } - nodep->user1(1); + } + nodep->user1(1); } virtual void visit(AstConst* nodep) { - // Constants are of unknown size if smaller than 33 bits, becase - // we're too lazy to wrap every constant in the universe in - // ((IData)#). - nodep->user1(nodep->isQuad() || nodep->isWide()); + // Constants are of unknown size if smaller than 33 bits, becase + // we're too lazy to wrap every constant in the universe in + // ((IData)#). + nodep->user1(nodep->isQuad() || nodep->isWide()); } // NOPs diff --git a/src/V3Cast.h b/src/V3Cast.h index 5744a7279..080eb19e7 100644 --- a/src/V3Cast.h +++ b/src/V3Cast.h @@ -34,4 +34,4 @@ public: static void castAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index d90fda15d..8d5f6c572 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -44,7 +44,7 @@ #include #include -#define CDC_WEIGHT_ASYNC 0x1000 // Weight for edges that feed async logic +#define CDC_WEIGHT_ASYNC 0x1000 // Weight for edges that feed async logic //###################################################################### @@ -57,19 +57,19 @@ public: // Graph support classes class CdcEitherVertex : public V3GraphVertex { - AstScope* m_scopep; - AstNode* m_nodep; - AstSenTree* m_srcDomainp; - AstSenTree* m_dstDomainp; - bool m_srcDomainSet:1; - bool m_dstDomainSet:1; - bool m_asyncPath:1; + AstScope* m_scopep; + AstNode* m_nodep; + AstSenTree* m_srcDomainp; + AstSenTree* m_dstDomainp; + bool m_srcDomainSet:1; + bool m_dstDomainSet:1; + bool m_asyncPath:1; public: CdcEitherVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep) - : V3GraphVertex(graphp), m_scopep(scopep), m_nodep(nodep) - , m_srcDomainp(NULL), m_dstDomainp(NULL) - , m_srcDomainSet(false), m_dstDomainSet(false) - , m_asyncPath(false) {} + : V3GraphVertex(graphp), m_scopep(scopep), m_nodep(nodep) + , m_srcDomainp(NULL), m_dstDomainp(NULL) + , m_srcDomainSet(false), m_dstDomainSet(false) + , m_asyncPath(false) {} virtual ~CdcEitherVertex() {} // ACCESSORS virtual FileLine* fileline() const { return nodep()->fileline(); } @@ -89,30 +89,32 @@ public: class CdcVarVertex : public CdcEitherVertex { AstVarScope* m_varScp; - int m_cntAsyncRst; - bool m_fromFlop; + int m_cntAsyncRst; + bool m_fromFlop; public: CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : CdcEitherVertex(graphp, scopep, varScp), m_varScp(varScp), m_cntAsyncRst(0), m_fromFlop(false) {} + : CdcEitherVertex(graphp, scopep, varScp) + , m_varScp(varScp), m_cntAsyncRst(0), m_fromFlop(false) {} virtual ~CdcVarVertex() {} // ACCESSORS AstVarScope* varScp() const { return m_varScp; } virtual string name() const { return (cvtToHex(m_varScp)+" "+varScp()->name()); } - virtual string dotColor() const { return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue"; } + virtual string dotColor() const { + return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue"; } int cntAsyncRst() const { return m_cntAsyncRst; } - void cntAsyncRst(int flag) { m_cntAsyncRst=flag; } + void cntAsyncRst(int flag) { m_cntAsyncRst = flag; } bool fromFlop() const { return m_fromFlop; } void fromFlop(bool flag) { m_fromFlop = flag; } }; class CdcLogicVertex : public CdcEitherVertex { - bool m_hazard:1; - bool m_isFlop:1; + bool m_hazard:1; + bool m_isFlop:1; public: CdcLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstSenTree* sensenodep) - : CdcEitherVertex(graphp,scopep,nodep) - , m_hazard(false), m_isFlop(false) - { srcDomainp(sensenodep); dstDomainp(sensenodep); } + : CdcEitherVertex(graphp, scopep, nodep) + , m_hazard(false), m_isFlop(false) + { srcDomainp(sensenodep); dstDomainp(sensenodep); } virtual ~CdcLogicVertex() {} // ACCESSORS virtual string name() const { return (cvtToHex(nodep())+"@"+scopep()->prettyName()); } @@ -130,32 +132,32 @@ class CdcDumpVisitor : public CdcBaseVisitor { private: // NODE STATE //Entire netlist: - // {statement}Node::user3 -> bool, indicating not hazard + // {statement}Node::user3 -> bool, indicating not hazard std::ofstream* m_ofp; // Output file - string m_prefix; + string m_prefix; virtual void visit(AstNode* nodep) { - *m_ofp<user3()) *m_ofp<<" %%"; - else *m_ofp<<" "; - *m_ofp<prettyTypeName()<<" "<user3()) *m_ofp<<" %%"; + else *m_ofp<<" "; + *m_ofp<prettyTypeName()<<" "<op1p()); - m_prefix = lastPrefix + "2:"; + m_prefix = lastPrefix + "2:"; iterateAndNextNull(nodep->op2p()); - m_prefix = lastPrefix + "3:"; + m_prefix = lastPrefix + "3:"; iterateAndNextNull(nodep->op3p()); - m_prefix = lastPrefix + "4:"; + m_prefix = lastPrefix + "4:"; iterateAndNextNull(nodep->op4p()); - m_prefix = lastPrefix; + m_prefix = lastPrefix; } public: // CONSTUCTORS CdcDumpVisitor(AstNode* nodep, std::ofstream* ofp, const string& prefix) { - m_ofp = ofp; - m_prefix = prefix; + m_ofp = ofp; + m_prefix = prefix; iterate(nodep); } virtual ~CdcDumpVisitor() {} @@ -165,34 +167,34 @@ public: class CdcWidthVisitor : public CdcBaseVisitor { private: - int m_maxLineno; - size_t m_maxFilenameLen; + int m_maxLineno; + size_t m_maxFilenameLen; virtual void visit(AstNode* nodep) { iterateChildren(nodep); - // Keeping line+filename lengths separate is much faster than calling ascii().length() - if (nodep->fileline()->lineno() >= m_maxLineno) { - m_maxLineno = nodep->fileline()->lineno()+1; - } - if (nodep->fileline()->filename().length() >= m_maxFilenameLen) { - m_maxFilenameLen = nodep->fileline()->filename().length()+1; - } + // Keeping line+filename lengths separate is much faster than calling ascii().length() + if (nodep->fileline()->lineno() >= m_maxLineno) { + m_maxLineno = nodep->fileline()->lineno()+1; + } + if (nodep->fileline()->filename().length() >= m_maxFilenameLen) { + m_maxFilenameLen = nodep->fileline()->filename().length()+1; + } } public: // CONSTUCTORS explicit CdcWidthVisitor(AstNode* nodep) { - m_maxLineno = 0; - m_maxFilenameLen = 0; + m_maxLineno = 0; + m_maxFilenameLen = 0; iterate(nodep); } virtual ~CdcWidthVisitor() {} // ACCESSORS int maxWidth() { - size_t width=1; - width += m_maxFilenameLen; - width += 1; // The : - width += cvtToStr(m_maxLineno).length(); - width += 1; // Final : + size_t width = 1; + width += m_maxFilenameLen; + width += 1; // The : + width += cvtToStr(m_maxLineno).length(); + width += 1; // Final : return static_cast(width); } }; @@ -204,60 +206,61 @@ class CdcVisitor : public CdcBaseVisitor { private: // NODE STATE //Entire netlist: - // AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet - // AstVarScope::user2 -> bool Used in sensitivity list - // {statement}Node::user1p -> CdcLogicVertex* for this statement - // AstNode::user3 -> bool True indicates to print %% (via V3EmitV) - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; + // AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet + // AstVarScope::user2 -> bool Used in sensitivity list + // {statement}Node::user1p -> CdcLogicVertex* for this statement + // AstNode::user3 -> bool True indicates to print %% (via V3EmitV) + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; // STATE - V3Graph m_graph; // Scoreboard of var usages/dependencies - CdcLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored - AstScope* m_scopep; // Current scope being processed - AstNodeModule* m_modp; // Current module - AstSenTree* m_domainp; // Current sentree - bool m_inDly; // In delayed assign - int m_inSenItem; // Number of senitems - string m_ofFilename; // Output filename + V3Graph m_graph; // Scoreboard of var usages/dependencies + CdcLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored + AstScope* m_scopep; // Current scope being processed + AstNodeModule* m_modp; // Current module + AstSenTree* m_domainp; // Current sentree + bool m_inDly; // In delayed assign + int m_inSenItem; // Number of senitems + string m_ofFilename; // Output filename std::ofstream* m_ofp; // Output file - uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices - int m_filelineWidth; // Characters in longest fileline + uint32_t m_userGeneration; // Generation count to avoid slow userClearVertices + int m_filelineWidth; // Characters in longest fileline // METHODS void iterateNewStmt(AstNode* nodep) { - if (m_scopep) { - UINFO(4," STMT "<hasClocked()) { // To/from a flop - m_logicVertexp->isFlop(true); - m_logicVertexp->srcDomainp(m_domainp); - m_logicVertexp->srcDomainSet(true); - m_logicVertexp->dstDomainp(m_domainp); - m_logicVertexp->dstDomainSet(true); - } + if (m_scopep) { + UINFO(4," STMT "<hasClocked()) { // To/from a flop + m_logicVertexp->isFlop(true); + m_logicVertexp->srcDomainp(m_domainp); + m_logicVertexp->srcDomainSet(true); + m_logicVertexp->dstDomainp(m_domainp); + m_logicVertexp->dstDomainSet(true); + } iterateChildren(nodep); - m_logicVertexp = NULL; + m_logicVertexp = NULL; - if (0 && debug()>=9) { - UINFO(9, "Trace Logic:\n"); - nodep->dumpTree(cout, "-log1: "); - } - } + if (0 && debug()>=9) { + UINFO(9, "Trace Logic:\n"); + nodep->dumpTree(cout, "-log1: "); + } + } } CdcVarVertex* makeVarVertex(AstVarScope* varscp) { CdcVarVertex* vertexp = reinterpret_cast(varscp->user1p()); - if (!vertexp) { - UINFO(6,"New vertex "<user1p(vertexp); - if (varscp->varp()->isUsedClock()) {} - if (varscp->varp()->isPrimaryIO()) { - // Create IO vertex - note it's relative to the pointed to var, not where we are now - // This allows reporting to easily print the input statement - CdcLogicVertex* ioVertexp = new CdcLogicVertex(&m_graph, varscp->scopep(), varscp->varp(), NULL); + if (!vertexp) { + UINFO(6,"New vertex "<user1p(vertexp); + if (varscp->varp()->isUsedClock()) {} + if (varscp->varp()->isPrimaryIO()) { + // Create IO vertex - note it's relative to the pointed to var, not where we are now + // This allows reporting to easily print the input statement + CdcLogicVertex* ioVertexp = new CdcLogicVertex(&m_graph, varscp->scopep(), + varscp->varp(), NULL); if (varscp->varp()->isWritable()) { new V3GraphEdge(&m_graph, vertexp, ioVertexp, 1); } else { @@ -265,286 +268,289 @@ private: } } } - if (m_inSenItem) { - varscp->user2(true); // It's like a clock... - // TODO: In the future we could mark it here and do normal clock tree glitch checks also - } else if (varscp->user2()) { // It was detected in a sensitivity list earlier - // And now it's used as logic. So must be a reset. - vertexp->cntAsyncRst(vertexp->cntAsyncRst()+1); - } - return vertexp; + if (m_inSenItem) { + varscp->user2(true); // It's like a clock... + // TODO: In the future we could mark it here and do normal clock tree glitch checks also + } else if (varscp->user2()) { // It was detected in a sensitivity list earlier + // And now it's used as logic. So must be a reset. + vertexp->cntAsyncRst(vertexp->cntAsyncRst()+1); + } + return vertexp; } void warnAndFile(AstNode* nodep, V3ErrorCode code, const string& msg) { - static bool told_file = false; - nodep->v3warnCode(code,msg); - if (!told_file) { - told_file = 1; + static bool told_file = false; + nodep->v3warnCode(code, msg); + if (!told_file) { + told_file = 1; std::cerr<fileline()<<" "<fileline()<<" "<hasCombo()) { // Source flop logic in a posedge block is OK for reset (not async though) - if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) { - UINFO(8,"Set hazard "<setHazard(nodep); - } - } + // Need to not clear if warnings are off (rather than when report it) + // as bypassing this warning may turn up another path that isn't warning off'ed. + // We can't modifyWarnOff here, as one instantiation might not be + // an issue until we find a hitting flop. + // Furthermore, a module like a "Or" module would only get flagged + // once, even though the signals feeding it are radically different. + if (!m_domainp || m_domainp->hasCombo()) { // Source flop logic in a posedge block is OK for reset (not async though) + if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) { + UINFO(8,"Set hazard "<setHazard(nodep); + } + } } - string spaces(int level) { string out; while (level--) out+=" "; return out; } + string spaces(int level) { string out; while (level--) out += " "; return out; } string pad(unsigned column, const string& in) { - string out = in; - while (out.length()6) m_graph.dump(); - if (debug()>6) m_graph.dumpDotFilePrefixed("cdc_pre"); - // - m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights - // - m_graph.dumpDotFilePrefixed("cdc_simp"); - // - analyzeReset(); + UINFO(3,__FUNCTION__<<": "<6) m_graph.dump(); + if (debug()>6) m_graph.dumpDotFilePrefixed("cdc_pre"); + // + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); // This will MAX across edge weights + // + m_graph.dumpDotFilePrefixed("cdc_simp"); + // + analyzeReset(); } int filelineWidth() { - if (!m_filelineWidth) { - CdcWidthVisitor visitor (v3Global.rootp()); - m_filelineWidth = visitor.maxWidth(); - } - return m_filelineWidth; + if (!m_filelineWidth) { + CdcWidthVisitor visitor (v3Global.rootp()); + m_filelineWidth = visitor.maxWidth(); + } + return m_filelineWidth; } //---------------------------------------- // RESET REPORT void analyzeReset() { - // Find all async reset wires, and trace backwards - // userClearVertices is very slow, so we use a generation count instead - m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { - if (vvertexp->cntAsyncRst()) { - m_userGeneration++; // Effectively a userClearVertices() - UINFO(8, " Trace One async: "<verticesNextp()) { + if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { + if (vvertexp->cntAsyncRst()) { + m_userGeneration++; // Effectively a userClearVertices() + UINFO(8, " Trace One async: "<user()>=m_userGeneration) return NULL; // Processed - prevent loop - vertexp->user(m_userGeneration); + // First pass: Return vertex of any hazardous stuff attached, or NULL if OK + // If first pass returns true, second pass calls asyncPath() on appropriate nodes + if (vertexp->user()>=m_userGeneration) return NULL; // Processed - prevent loop + vertexp->user(m_userGeneration); - CdcEitherVertex* mark_outp = NULL; - UINFO(9," Trace: "<asyncPath(false); + // Clear out in prep for marking next path + if (!mark) vertexp->asyncPath(false); - if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { - // Any logic considered bad, at the moment, anyhow - if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp; - // And keep tracing back so the user can understand what's up - } - else if (CdcVarVertex* vvertexp = dynamic_cast(vertexp)) { - if (mark) vvertexp->asyncPath(true); + if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { + // Any logic considered bad, at the moment, anyhow + if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp; + // And keep tracing back so the user can understand what's up + } + else if (CdcVarVertex* vvertexp = dynamic_cast(vertexp)) { + if (mark) vvertexp->asyncPath(true); // If primary I/O, it's ok here back if (vvertexp->varScp()->varp()->isPrimaryInish()) { // Show the source "input" statement if it exists - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { CdcEitherVertex* eFromVertexp = static_cast(edgep->fromp()); - eFromVertexp->asyncPath(true); - } - return NULL; - } - // Also ok if from flop, but partially trace the flop so more obvious to users - if (vvertexp->fromFlop()) { - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + eFromVertexp->asyncPath(true); + } + return NULL; + } + // Also ok if from flop, but partially trace the flop so more obvious to users + if (vvertexp->fromFlop()) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { CdcEitherVertex* eFromVertexp = static_cast(edgep->fromp()); - eFromVertexp->asyncPath(true); - } - return NULL; - } - } + eFromVertexp->asyncPath(true); + } + return NULL; + } + } - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { CdcEitherVertex* eFromVertexp = static_cast(edgep->fromp()); - CdcEitherVertex* submarkp = traceAsyncRecurse(eFromVertexp, mark); - if (submarkp && !mark_outp) mark_outp = submarkp; - } + CdcEitherVertex* submarkp = traceAsyncRecurse(eFromVertexp, mark); + if (submarkp && !mark_outp) mark_outp = submarkp; + } - if (mark) vertexp->asyncPath(true); - return mark_outp; + if (mark) vertexp->asyncPath(true); + return mark_outp; } void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) { - AstNode* nodep = vertexp->varScp(); - *m_ofp<<"\n"; - *m_ofp<<"\n"; - CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many) - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { + AstNode* nodep = vertexp->varScp(); + *m_ofp<<"\n"; + *m_ofp<<"\n"; + CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many) + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { CdcEitherVertex* eToVertexp = static_cast(edgep->top()); - if (!eToVertexp) targetp = eToVertexp; - if (CdcLogicVertex* vvertexp = dynamic_cast(eToVertexp)) { - if (vvertexp->isFlop() // IE the target flop that is upsetting us - && edgep->weight() >= CDC_WEIGHT_ASYNC) { // And this signal feeds an async reset line - targetp = eToVertexp; - //UINFO(9," targetasync "<name()<<" "<<" from "<name()<name()<<" "<nodep()->fileline()<nodep(),V3ErrorCode::CDCRSTLOGIC,"Logic in path that feeds async reset, via signal: "+nodep->prettyName()); - dumpAsyncRecurse(targetp, "", " ",0); + if (!eToVertexp) targetp = eToVertexp; + if (CdcLogicVertex* vvertexp = dynamic_cast(eToVertexp)) { + if (vvertexp->isFlop() // IE the target flop that is upsetting us + && edgep->weight() >= CDC_WEIGHT_ASYNC) { // And this signal feeds an async reset line + targetp = eToVertexp; + //UINFO(9," targetasync "<name()<<" "<<" from "<name()<name()<<" "<nodep()->fileline()<nodep(), V3ErrorCode::CDCRSTLOGIC, + "Logic in path that feeds async reset, via signal: "+nodep->prettyName()); + dumpAsyncRecurse(targetp, "", " ", 0); } - bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& sep, int level) { - // level=0 is special, indicates to dump destination flop - // Return true if printed anything - // If mark, also mark the output even if nothing hazardous below - if (vertexp->user()>=m_userGeneration) return false; // Processed - prevent loop - vertexp->user(m_userGeneration); - if (!vertexp->asyncPath() && level!=0) return false; // Not part of path + bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, + const string& sep, int level) { + // level=0 is special, indicates to dump destination flop + // Return true if printed anything + // If mark, also mark the output even if nothing hazardous below + if (vertexp->user()>=m_userGeneration) return false; // Processed - prevent loop + vertexp->user(m_userGeneration); + if (!vertexp->asyncPath() && level!=0) return false; // Not part of path - // Other logic in the path - string cont = prefix+sep; - string nextsep = " "; - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + // Other logic in the path + string cont = prefix+sep; + string nextsep = " "; + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { CdcEitherVertex* eFromVertexp = static_cast(edgep->fromp()); - if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level+1)) { - nextsep = " | "; - } - } + if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level+1)) { + nextsep = " | "; + } + } - // Dump single variable/logic block - // See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) - AstNode* nodep = vertexp->nodep(); - string front = pad(filelineWidth(),nodep->fileline()->ascii()+":")+" "+prefix+" +- "; + // Dump single variable/logic block + // See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) + AstNode* nodep = vertexp->nodep(); + string front = pad(filelineWidth(), nodep->fileline()->ascii()+":")+" "+prefix+" +- "; if (VN_IS(nodep, VarScope)) { - *m_ofp<prettyName()<srcDomainp(), true); - if (debug()) { - CdcDumpVisitor visitor (nodep, m_ofp, front+"DBG: "); - } - } + *m_ofp<prettyName()<srcDomainp(), true); + if (debug()) { + CdcDumpVisitor visitor (nodep, m_ofp, front+"DBG: "); + } + } - nextsep = " | "; - if (level) *m_ofp<(vertexp)) { - // Now that we've printed a path with this hazard, don't bother to print any more - // Otherwise, we'd get a path for almost every destination flop - vvertexp->clearHazard(); - } - return true; + if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { + // Now that we've printed a path with this hazard, don't bother to print any more + // Otherwise, we'd get a path for almost every destination flop + vvertexp->clearHazard(); + } + return true; } //---------------------------------------- // EDGE REPORTS void edgeReport() { - // Make report of all signal names and what clock edges they have - // - // Due to flattening, many interesting direct-connect signals are - // lost, so we can't make a report showing I/Os for a low level - // module. Disabling flattening though makes us consider each - // signal in it's own unique clock domain. + // Make report of all signal names and what clock edges they have + // + // Due to flattening, many interesting direct-connect signals are + // lost, so we can't make a report showing I/Os for a low level + // module. Disabling flattening though makes us consider each + // signal in it's own unique clock domain. - UINFO(3,__FUNCTION__<<": "<verticesNextp()) { - if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { - UINFO(9, " Trace One edge: "<verticesNextp()) { + if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { + UINFO(9, " Trace One edge: "< ofp (V3File::new_ofstream(filename)); if (ofp->fail()) v3fatal("Can't write "< report; // Sort output by name - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { - AstVar* varp = vvertexp->varScp()->varp(); - if (1) { // varp->isPrimaryIO() + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (CdcVarVertex* vvertexp = dynamic_cast(itp)) { + AstVar* varp = vvertexp->varScp()->varp(); + if (1) { // varp->isPrimaryIO() string what = "wire"; if (varp->isPrimaryIO()) what = varp->direction().prettyName(); std::ostringstream os; os.setf(std::ios::left); - // Module name - doesn't work due to flattening having lost the original - // so we assume the modulename matches the filebasename - string fname = vvertexp->varScp()->fileline()->filebasename() + ":"; + // Module name - doesn't work due to flattening having lost the original + // so we assume the modulename matches the filebasename + string fname = vvertexp->varScp()->fileline()->filebasename() + ":"; os<<" "<varScp()->prettyName(); - os<<" SRC="; - if (vvertexp->srcDomainp()) V3EmitV::verilogForTree(vvertexp->srcDomainp(), os); - os<<" DST="; - if (vvertexp->dstDomainp()) V3EmitV::verilogForTree(vvertexp->dstDomainp(), os); + os<<" SRC="; + if (vvertexp->srcDomainp()) V3EmitV::verilogForTree(vvertexp->srcDomainp(), os); + os<<" DST="; + if (vvertexp->dstDomainp()) V3EmitV::verilogForTree(vvertexp->dstDomainp(), os); os<::iterator it = report.begin(); it!=report.end(); ++it) { - *ofp << *it; - } + *ofp << *it; + } } void edgeDomainRecurse(CdcEitherVertex* vertexp, bool traceDests, int level) { - // Scan back to inputs/outputs, flops, and compute clock domain information - UINFO(8,spaces(level)<<" Tracein "<user()>=m_userGeneration) return; // Mid-Processed - prevent loop - vertexp->user(m_userGeneration); + // Scan back to inputs/outputs, flops, and compute clock domain information + UINFO(8,spaces(level)<<" Tracein "<user()>=m_userGeneration) return; // Mid-Processed - prevent loop + vertexp->user(m_userGeneration); - // Variables from flops already are domained - if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) return; // Fully computed + // Variables from flops already are domained + if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) return; // Fully computed typedef std::set SenSet; - SenSet senouts; // List of all sensitivities for new signal - if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { - if (vvertexp) {} // Unused - } - else if (CdcVarVertex* vvertexp = dynamic_cast(vertexp)) { - // If primary I/O, give it domain of the input - AstVar* varp = vvertexp->varScp()->varp(); + SenSet senouts; // List of all sensitivities for new signal + if (CdcLogicVertex* vvertexp = dynamic_cast(vertexp)) { + if (vvertexp) {} // Unused + } + else if (CdcVarVertex* vvertexp = dynamic_cast(vertexp)) { + // If primary I/O, give it domain of the input + AstVar* varp = vvertexp->varScp()->varp(); if (varp->isPrimaryIO() && varp->isNonOutput() && !traceDests) { senouts.insert( new AstSenTree(varp->fileline(), @@ -552,138 +558,138 @@ private: } } - // Now combine domains of sources/dests - if (traceDests) { - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { + // Now combine domains of sources/dests + if (traceDests) { + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { CdcEitherVertex* eToVertexp = static_cast(edgep->top()); - edgeDomainRecurse(eToVertexp, traceDests, level+1); - if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp()); - } - } else { - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + edgeDomainRecurse(eToVertexp, traceDests, level+1); + if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp()); + } + } else { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { CdcEitherVertex* eFromVertexp = static_cast(edgep->fromp()); - edgeDomainRecurse(eFromVertexp, traceDests, level+1); - if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp()); - } - } + edgeDomainRecurse(eFromVertexp, traceDests, level+1); + if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp()); + } + } - // Convert list of senses into one sense node - AstSenTree* senoutp = NULL; - bool senedited = false; - for (SenSet::iterator it=senouts.begin(); it!=senouts.end(); ++it) { - if (!senoutp) senoutp = *it; - else { - if (!senedited) { - senedited = true; - senoutp = senoutp->cloneTree(true); - } - senoutp->addSensesp((*it)->sensesp()->cloneTree(true)); - } - } - // If multiple domains need to do complicated optimizations - if (senedited) { + // Convert list of senses into one sense node + AstSenTree* senoutp = NULL; + bool senedited = false; + for (SenSet::iterator it=senouts.begin(); it!=senouts.end(); ++it) { + if (!senoutp) senoutp = *it; + else { + if (!senedited) { + senedited = true; + senoutp = senoutp->cloneTree(true); + } + senoutp->addSensesp((*it)->sensesp()->cloneTree(true)); + } + } + // If multiple domains need to do complicated optimizations + if (senedited) { senoutp = VN_CAST(V3Const::constifyExpensiveEdit(senoutp), SenTree); - } - if (traceDests) { - vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that - vertexp->dstDomainp(senoutp); - if (debug()>=9) { - UINFO(9,spaces(level)+" Tracedst "<srcDomainSet(true); // Note it's set - domainp may be null, so can't use that - vertexp->srcDomainp(senoutp); - if (debug()>=9) { - UINFO(9,spaces(level)+" Tracesrc "<dstDomainSet(true); // Note it's set - domainp may be null, so can't use that + vertexp->dstDomainp(senoutp); + if (debug()>=9) { + UINFO(9,spaces(level)+" Tracedst "<srcDomainSet(true); // Note it's set - domainp may be null, so can't use that + vertexp->srcDomainp(senoutp); + if (debug()>=9) { + UINFO(9,spaces(level)+" Tracesrc "<sensesp(); - if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) { // IE not hasSettle/hasInitial - iterateNewStmt(nodep); - } - m_domainp = NULL; - AstNode::user2ClearTree(); + // Create required blocks and add to module + UINFO(4," BLOCK "<sensesp(); + if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) { // IE not hasSettle/hasInitial + iterateNewStmt(nodep); + } + m_domainp = NULL; + AstNode::user2ClearTree(); } virtual void visit(AstNodeVarRef* nodep) { - if (m_scopep) { - if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block"); - AstVarScope* varscp = nodep->varScopep(); - if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); - CdcVarVertex* varvertexp = makeVarVertex(varscp); - UINFO(5," VARREF to "<lvalue()) { - new V3GraphEdge(&m_graph, m_logicVertexp, varvertexp, 1); - if (m_inDly) { - varvertexp->fromFlop(true); - varvertexp->srcDomainp(m_domainp); - varvertexp->srcDomainSet(true); - } - } else { - if (varvertexp->cntAsyncRst()) { - //UINFO(9," edgeasync "<name()<<" to "<name()<<" to "<v3fatalSrc("Var ref not under a logic block"); + AstVarScope* varscp = nodep->varScopep(); + if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); + CdcVarVertex* varvertexp = makeVarVertex(varscp); + UINFO(5," VARREF to "<lvalue()) { + new V3GraphEdge(&m_graph, m_logicVertexp, varvertexp, 1); + if (m_inDly) { + varvertexp->fromFlop(true); + varvertexp->srcDomainp(m_domainp); + varvertexp->srcDomainSet(true); + } + } else { + if (varvertexp->cntAsyncRst()) { + //UINFO(9," edgeasync "<name()<<" to "<name()<<" to "<fail()) v3fatal("Can't write "<=1) edgeReport(); // Not useful to users at the moment - - if (0) { *m_ofp<<"\nDBG-test-dumper\n"; V3EmitV::verilogPrefixedTree(nodep, *m_ofp, "DBG ",40,NULL,true); *m_ofp<=1) edgeReport(); // Not useful to users at the moment + if (0) { + *m_ofp<<"\nDBG-test-dumper\n"; + V3EmitV::verilogPrefixedTree(nodep, *m_ofp, "DBG ", 40, NULL, true); + *m_ofp<fileline(), "_change_request_" + cvtToStr(++m_funcNum), m_scopetopp, "QData"); - m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar()); - m_chgFuncp->symProlog(true); - m_chgFuncp->declPrivate(true); - m_scopetopp->addActivep(m_chgFuncp); + // Don't create an extra function call if splitting is disabled + if (!v3Global.opt.outputSplitCFuncs()) { + m_chgFuncp = m_tlChgFuncp; + return; + } + if (!m_chgFuncp || v3Global.opt.outputSplitCFuncs() < m_numStmts) { + m_chgFuncp = new AstCFunc(m_scopetopp->fileline(), + "_change_request_" + cvtToStr(++m_funcNum), + m_scopetopp, "QData"); + m_chgFuncp->argTypes(EmitCBaseVisitor::symClassVar()); + m_chgFuncp->symProlog(true); + m_chgFuncp->declPrivate(true); + m_scopetopp->addActivep(m_chgFuncp); - // Add a top call to it - AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp); - callp->argTypes("vlSymsp"); + // Add a top call to it + AstCCall* callp = new AstCCall(m_scopetopp->fileline(), m_chgFuncp); + callp->argTypes("vlSymsp"); - if (!m_tlChgFuncp->stmtsp()) { - m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp)); - } else { + if (!m_tlChgFuncp->stmtsp()) { + m_tlChgFuncp->addStmtsp(new AstCReturn(m_scopetopp->fileline(), callp)); + } else { AstCReturn* returnp = VN_CAST(m_tlChgFuncp->stmtsp(), CReturn); - if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function"); - // This is currently using AstLogOr which will shortcut the evaluation if - // any function returns true. This is likely what we want and is similar to the logic already in use - // inside V3EmitC, however, it also means that verbose logging may miss to print change detect variables. - AstNode* newp = new AstCReturn(m_scopetopp->fileline(), - new AstLogOr(m_scopetopp->fileline(), callp, - returnp->lhsp()->unlinkFrBack())); - returnp->replaceWith(newp); - returnp->deleteTree(); VL_DANGLING(returnp); - } - m_numStmts = 0; - } + if (!returnp) m_scopetopp->v3fatalSrc("Lost CReturn in top change function"); + // This is currently using AstLogOr which will shortcut the + // evaluation if any function returns true. This is likely what + // we want and is similar to the logic already in use inside + // V3EmitC, however, it also means that verbose logging may + // miss to print change detect variables. + AstNode* newp = new AstCReturn(m_scopetopp->fileline(), + new AstLogOr(m_scopetopp->fileline(), callp, + returnp->lhsp()->unlinkFrBack())); + returnp->replaceWith(newp); + returnp->deleteTree(); VL_DANGLING(returnp); + } + m_numStmts = 0; + } } }; @@ -106,82 +110,84 @@ public: class ChangedInsertVisitor : public AstNVisitor { private: // STATE - ChangedState* m_statep; // Shared state across visitors - AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting - AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting - AstNode* m_varEqnp; // Original var's equation to get var value - AstNode* m_newLvEqnp; // New var's equation to read value - AstNode* m_newRvEqnp; // New var's equation to set value - uint32_t m_detects; // # detects created + ChangedState* m_statep; // Shared state across visitors + AstVarScope* m_vscp; // Original (non-change) variable we're change-detecting + AstVarScope* m_newvscp; // New (change detect) variable we're change-detecting + AstNode* m_varEqnp; // Original var's equation to get var value + AstNode* m_newLvEqnp; // New var's equation to read value + AstNode* m_newRvEqnp; // New var's equation to set value + uint32_t m_detects; // # detects created // CONSTANTS enum MiscConsts { - DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error - // Ok to increase this, but may result in much slower model + DETECTARRAY_MAX_INDEXES = 256 // How many indexes before error + // Ok to increase this, but may result in much slower model }; void newChangeDet() { - if (++m_detects > DETECTARRAY_MAX_INDEXES) { - m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than "<prettyName()<warnMore() - <<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<maybeCreateChgFuncp(); + if (++m_detects > DETECTARRAY_MAX_INDEXES) { + m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect more than " + <prettyName()<warnMore() + <<"... Could recompile with DETECTARRAY_MAX_INDEXES increased"<maybeCreateChgFuncp(); AstChangeDet* changep = new AstChangeDet(m_vscp->fileline(), m_varEqnp->cloneTree(true), m_newRvEqnp->cloneTree(true), false); - m_statep->m_chgFuncp->addStmtsp(changep); + m_statep->m_chgFuncp->addStmtsp(changep); AstAssign* initp = new AstAssign(m_vscp->fileline(), m_newLvEqnp->cloneTree(true), m_varEqnp->cloneTree(true)); - m_statep->m_chgFuncp->addFinalsp(initp); - EmitCBaseCounterVisitor visitor(initp); - m_statep->m_numStmts += visitor.count(); + m_statep->m_chgFuncp->addFinalsp(initp); + EmitCBaseCounterVisitor visitor(initp); + m_statep->m_numStmts += visitor.count(); } virtual void visit(AstBasicDType* nodep) { - newChangeDet(); + newChangeDet(); } virtual void visit(AstPackArrayDType* nodep) { - newChangeDet(); + newChangeDet(); } virtual void visit(AstUnpackArrayDType* nodep) { - for (int index=0; index < nodep->elementsConst(); ++index) { - AstNode* origVEp = m_varEqnp; - AstNode* origNLEp = m_newLvEqnp; - AstNode* origNREp = m_newRvEqnp; + for (int index=0; index < nodep->elementsConst(); ++index) { + AstNode* origVEp = m_varEqnp; + AstNode* origNLEp = m_newLvEqnp; + AstNode* origNREp = m_newRvEqnp; - m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index); - m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index); - m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index); + m_varEqnp = new AstArraySel(nodep->fileline(), m_varEqnp->cloneTree(true), index); + m_newLvEqnp = new AstArraySel(nodep->fileline(), m_newLvEqnp->cloneTree(true), index); + m_newRvEqnp = new AstArraySel(nodep->fileline(), m_newRvEqnp->cloneTree(true), index); iterate(nodep->subDTypep()->skipRefp()); - m_varEqnp->deleteTree(); - m_newLvEqnp->deleteTree(); - m_newRvEqnp->deleteTree(); + m_varEqnp->deleteTree(); + m_newLvEqnp->deleteTree(); + m_newRvEqnp->deleteTree(); - m_varEqnp = origVEp; - m_newLvEqnp = origNLEp; - m_newRvEqnp = origNREp; - } + m_varEqnp = origVEp; + m_newLvEqnp = origNLEp; + m_newRvEqnp = origNREp; + } } virtual void visit(AstNodeClassDType* nodep) { - if (nodep->packedUnsup()) { - newChangeDet(); - } else { - if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-class-"); + if (nodep->packedUnsup()) { + newChangeDet(); + } else { + if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-class-"); m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable" " (probably with UNOPTFLAT warning suppressed): " <varp()->prettyName()); - } + } } virtual void visit(AstNode* nodep) { iterateChildren(nodep); - if (debug()) nodep->dumpTree(cout,"-DETECTARRAY-general-"); + if (debug()) nodep->dumpTree(cout, "-DETECTARRAY-general-"); m_vscp->v3warn(E_DETECTARRAY, "Unsupported: Can't detect changes on complex variable" " (probably with UNOPTFLAT warning suppressed): " <varp()->prettyName()); @@ -189,29 +195,31 @@ private: public: // CONSTUCTORS ChangedInsertVisitor(AstVarScope* vscp, ChangedState* statep) { - m_statep = statep; - m_vscp = vscp; - m_detects = 0; - { - AstVar* varp = m_vscp->varp(); - string newvarname = "__Vchglast__"+m_vscp->scopep()->nameDotless()+"__"+varp->shortName(); - // Create: VARREF(_last) - // ASSIGN(VARREF(_last), VARREF(var)) - // ... - // CHANGEDET(VARREF(_last), VARREF(var)) - AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp); - m_statep->m_topModp->addStmtp(newvarp); - m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp); - m_statep->m_scopetopp->addVarp(m_newvscp); + m_statep = statep; + m_vscp = vscp; + m_detects = 0; + { + AstVar* varp = m_vscp->varp(); + string newvarname = ("__Vchglast__"+m_vscp->scopep()->nameDotless() + +"__"+varp->shortName()); + // Create: VARREF(_last) + // ASSIGN(VARREF(_last), VARREF(var)) + // ... + // CHANGEDET(VARREF(_last), VARREF(var)) + AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP, + newvarname, varp); + m_statep->m_topModp->addStmtp(newvarp); + m_newvscp = new AstVarScope(m_vscp->fileline(), m_statep->m_scopetopp, newvarp); + m_statep->m_scopetopp->addVarp(m_newvscp); - m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false); - m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true); - m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false); - } + m_varEqnp = new AstVarRef(m_vscp->fileline(), m_vscp, false); + m_newLvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, true); + m_newRvEqnp = new AstVarRef(m_vscp->fileline(), m_newvscp, false); + } iterate(vscp->dtypep()->skipRefp()); - m_varEqnp->deleteTree(); - m_newLvEqnp->deleteTree(); - m_newRvEqnp->deleteTree(); + m_varEqnp->deleteTree(); + m_newLvEqnp->deleteTree(); + m_newRvEqnp->deleteTree(); } virtual ~ChangedInsertVisitor() {} }; @@ -223,61 +231,63 @@ class ChangedVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: - // AstVarScope::user1() -> bool. True indicates processed - AstUser1InUse m_inuser1; + // AstVarScope::user1() -> bool. True indicates processed + AstUser1InUse m_inuser1; // STATE - ChangedState* m_statep; // Shared state across visitors + ChangedState* m_statep; // Shared state across visitors // METHODS VL_DEBUG_FUNC; // Declare debug() void genChangeDet(AstVarScope* vscp) { - vscp->v3warn(IMPERFECTSCH,"Imperfect scheduling of variable: "<v3warn(IMPERFECTSCH, "Imperfect scheduling of variable: "<isTop()) { - m_statep->m_topModp = nodep; - } + UINFO(4," MOD "<isTop()) { + m_statep->m_topModp = nodep; + } iterateChildren(nodep); } virtual void visit(AstTopScope* nodep) { - UINFO(4," TS "<scopep(); - if (!scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?"); - m_statep->m_scopetopp = scopep; + UINFO(4," TS "<scopep(); + if (!scopep) nodep->v3fatalSrc("No scope found on top level," + " perhaps you have no statements?"); + m_statep->m_scopetopp = scopep; - // Create a wrapper change detection function that calls each change detection function - m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(), "_change_request", scopep, "QData"); - m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar()); - m_statep->m_tlChgFuncp->symProlog(true); - m_statep->m_tlChgFuncp->declPrivate(true); - m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp); - // Each change detection function needs at least one AstChangeDet - // to ensure that V3EmitC outputs the necessary code. - m_statep->maybeCreateChgFuncp(); - m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false)); + // Create a wrapper change detection function that calls each change detection function + m_statep->m_tlChgFuncp = new AstCFunc(nodep->fileline(), + "_change_request", scopep, "QData"); + m_statep->m_tlChgFuncp->argTypes(EmitCBaseVisitor::symClassVar()); + m_statep->m_tlChgFuncp->symProlog(true); + m_statep->m_tlChgFuncp->declPrivate(true); + m_statep->m_scopetopp->addActivep(m_statep->m_tlChgFuncp); + // Each change detection function needs at least one AstChangeDet + // to ensure that V3EmitC outputs the necessary code. + m_statep->maybeCreateChgFuncp(); + m_statep->m_chgFuncp->addStmtsp(new AstChangeDet(nodep->fileline(), NULL, NULL, false)); iterateChildren(nodep); } virtual void visit(AstVarScope* nodep) { - if (nodep->isCircular()) { - UINFO(8," CIRC "<user1SetOnce()) { - genChangeDet(nodep); - } - } + if (nodep->isCircular()) { + UINFO(8," CIRC "<user1SetOnce()) { + genChangeDet(nodep); + } + } } virtual void visit(AstNodeMath* nodep) { - // Short-circuit + // Short-circuit } //-------------------- // Default: Just iterate @@ -288,7 +298,7 @@ private: public: // CONSTUCTORS ChangedVisitor(AstNetlist* nodep, ChangedState* statep) { - m_statep = statep; + m_statep = statep; iterate(nodep); } virtual ~ChangedVisitor() {} diff --git a/src/V3Changed.h b/src/V3Changed.h index 50f9ad313..f470fde0c 100644 --- a/src/V3Changed.h +++ b/src/V3Changed.h @@ -34,4 +34,4 @@ public: static void changedAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index f24e203f4..9330ff134 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -20,10 +20,10 @@ // V3Clean's Transformations: // // Each module: -// For each math operator, if it requires a clean operand, -// and the operand is dirty, insert a CLEAN node. -// Resize operands to C++ 32/64/wide types. -// Copy all width() values to widthMin() so RANGE, etc can still see orig widths +// For each math operator, if it requires a clean operand, +// and the operand is dirty, insert a CLEAN node. +// Resize operands to C++ 32/64/wide types. +// Copy all width() values to widthMin() so RANGE, etc can still see orig widths // //************************************************************************* @@ -44,12 +44,12 @@ class CleanVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: - // AstNode::user() -> CleanState. For this node, 0==UNKNOWN - // AstNode::user2() -> bool. True indicates widthMin has been propagated - // AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; + // AstNode::user() -> CleanState. For this node, 0==UNKNOWN + // AstNode::user2() -> bool. True indicates widthMin has been propagated + // AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; // TYPES enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY }; @@ -62,153 +62,153 @@ private: // Width resetting int cppWidth(AstNode* nodep) { - if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE; - else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE; - else return nodep->widthWords()*VL_WORDSIZE; + if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE; + else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE; + else return nodep->widthWords()*VL_WORDSIZE; } void setCppWidth(AstNode* nodep) { - nodep->user2(true); // Don't resize it again - AstNodeDType* old_dtypep = nodep->dtypep(); - int width=cppWidth(nodep); // widthMin is unchanged - if (old_dtypep->width() != width) { - // Since any given dtype's cppWidth() is the same, we can just - // remember one convertion for each, and reuse it + nodep->user2(true); // Don't resize it again + AstNodeDType* old_dtypep = nodep->dtypep(); + int width = cppWidth(nodep); // widthMin is unchanged + if (old_dtypep->width() != width) { + // Since any given dtype's cppWidth() is the same, we can just + // remember one convertion for each, and reuse it if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) { - nodep->dtypep(new_dtypep); - } else { - nodep->dtypeChgWidth(width, nodep->widthMin()); - AstNodeDType* new_dtypep2 = nodep->dtypep(); - if (new_dtypep2 == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed"); - old_dtypep->user3p(new_dtypep2); // Remember for next time - } - } + nodep->dtypep(new_dtypep); + } else { + nodep->dtypeChgWidth(width, nodep->widthMin()); + AstNodeDType* new_dtypep2 = nodep->dtypep(); + if (new_dtypep2 == old_dtypep) nodep->v3fatalSrc("Dtype didn't change when width changed"); + old_dtypep->user3p(new_dtypep2); // Remember for next time + } + } } void computeCppWidth(AstNode* nodep) { - if (!nodep->user2() && nodep->hasDType()) { + if (!nodep->user2() && nodep->hasDType()) { if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths! || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays - } else { - setCppWidth(nodep); - } - } + } else { + setCppWidth(nodep); + } + } } // Store the clean state in the userp on each node void setCleanState(AstNode* nodep, CleanState clean) { - nodep->user1(clean); + nodep->user1(clean); } CleanState getCleanState(AstNode* nodep) { return static_cast(nodep->user1()); } bool isClean(AstNode* nodep) { - CleanState clstate = getCleanState(nodep); - if (clstate==CS_CLEAN) return true; - if (clstate==CS_DIRTY) return false; - nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName()); - return false; + CleanState clstate = getCleanState(nodep); + if (clstate==CS_CLEAN) return true; + if (clstate==CS_DIRTY) return false; + nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName()); + return false; } void setClean(AstNode* nodep, bool isClean) { computeCppWidth(nodep); // Just to be sure it's in widthMin - bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); //32,64,... - setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY)); + bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); // 32,64,... + setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY)); } // Operate on nodes void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node - UINFO(4," NeedClean "<unlinkFrBack(&relinkHandle); - // + UINFO(4," NeedClean "<unlinkFrBack(&relinkHandle); + // computeCppWidth(nodep); V3Number mask (nodep, cppWidth(nodep)); mask.setMask(nodep->widthMin()); AstNode* cleanp = new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(), mask), nodep); - cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS - relinkHandle.relink(cleanp); + cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS + relinkHandle.relink(cleanp); } void insureClean(AstNode* nodep) { - computeCppWidth(nodep); - if (!isClean(nodep)) insertClean(nodep); + computeCppWidth(nodep); + if (!isClean(nodep)) insertClean(nodep); } void insureCleanAndNext(AstNode* nodep) { - // Editing list, careful looping! - for (AstNode* exprp = nodep; exprp; ) { - AstNode* nextp = exprp->nextp(); - insureClean(exprp); - exprp = nextp; - } + // Editing list, careful looping! + for (AstNode* exprp = nodep; exprp; ) { + AstNode* nextp = exprp->nextp(); + insureClean(exprp); + exprp = nextp; + } } // Base type handling methods void operandBiop(AstNodeBiop* nodep) { iterateChildren(nodep); - computeCppWidth(nodep); - if (nodep->cleanLhs()) { - insureClean(nodep->lhsp()); - } - if (nodep->cleanRhs()) { - insureClean(nodep->rhsp()); - } - //no setClean.. must do it in each user routine. + computeCppWidth(nodep); + if (nodep->cleanLhs()) { + insureClean(nodep->lhsp()); + } + if (nodep->cleanRhs()) { + insureClean(nodep->rhsp()); + } + //no setClean.. must do it in each user routine. } void operandTriop(AstNodeTriop* nodep) { iterateChildren(nodep); - computeCppWidth(nodep); - if (nodep->cleanLhs()) { - insureClean(nodep->lhsp()); - } - if (nodep->cleanRhs()) { - insureClean(nodep->rhsp()); - } - if (nodep->cleanThs()) { - insureClean(nodep->thsp()); - } - //no setClean.. must do it in each user routine. + computeCppWidth(nodep); + if (nodep->cleanLhs()) { + insureClean(nodep->lhsp()); + } + if (nodep->cleanRhs()) { + insureClean(nodep->rhsp()); + } + if (nodep->cleanThs()) { + insureClean(nodep->thsp()); + } + //no setClean.. must do it in each user routine. } // VISITORS virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; + m_modp = nodep; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstNodeUniop* nodep) { iterateChildren(nodep); - computeCppWidth(nodep); - if (nodep->cleanLhs()) { - insureClean(nodep->lhsp()); - } + computeCppWidth(nodep); + if (nodep->cleanLhs()) { + insureClean(nodep->lhsp()); + } setClean(nodep, nodep->cleanOut()); } virtual void visit(AstNodeBiop* nodep) { - operandBiop(nodep); + operandBiop(nodep); setClean(nodep, nodep->cleanOut()); } virtual void visit(AstAnd* nodep) { - operandBiop(nodep); + operandBiop(nodep); setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp())); } virtual void visit(AstXor* nodep) { - operandBiop(nodep); + operandBiop(nodep); setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp())); } virtual void visit(AstOr* nodep) { - operandBiop(nodep); + operandBiop(nodep); setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp())); } virtual void visit(AstNodeMath* nodep) { iterateChildren(nodep); - computeCppWidth(nodep); + computeCppWidth(nodep); setClean(nodep, nodep->cleanOut()); } virtual void visit(AstNodeAssign* nodep) { iterateChildren(nodep); - computeCppWidth(nodep); - if (nodep->cleanRhs()) { - insureClean(nodep->rhsp()); - } + computeCppWidth(nodep); + if (nodep->cleanRhs()) { + insureClean(nodep->rhsp()); + } } virtual void visit(AstText* nodep) { setClean(nodep, true); @@ -217,17 +217,17 @@ private: setClean(nodep, true); } virtual void visit(AstSel* nodep) { - operandTriop(nodep); + operandTriop(nodep); setClean(nodep, nodep->cleanOut()); } virtual void visit(AstUCFunc* nodep) { iterateChildren(nodep); - computeCppWidth(nodep); + computeCppWidth(nodep); setClean(nodep, false); - // We always clean, as we don't trust those pesky users. + // We always clean, as we don't trust those pesky users. if (!VN_IS(nodep->backp(), And)) { - insertClean(nodep); - } + insertClean(nodep); + } insureCleanAndNext(nodep->bodysp()); } virtual void visit(AstTraceDecl* nodep) { @@ -239,32 +239,32 @@ private: insureCleanAndNext(nodep->valuep()); } virtual void visit(AstTypedef* nodep) { - // No cleaning, or would loose pointer to enum + // No cleaning, or would loose pointer to enum iterateChildren(nodep); } virtual void visit(AstParamTypeDType* nodep) { - // No cleaning, or would loose pointer to enum + // No cleaning, or would loose pointer to enum iterateChildren(nodep); } // Control flow operators virtual void visit(AstNodeCond* nodep) { iterateChildren(nodep); - insureClean(nodep->condp()); - setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p())); + insureClean(nodep->condp()); + setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p())); } virtual void visit(AstWhile* nodep) { iterateChildren(nodep); - insureClean(nodep->condp()); + insureClean(nodep->condp()); } virtual void visit(AstNodeIf* nodep) { iterateChildren(nodep); - insureClean(nodep->condp()); + insureClean(nodep->condp()); } virtual void visit(AstSFormatF* nodep) { iterateChildren(nodep); insureCleanAndNext(nodep->exprsp()); - setClean(nodep, true); // generates a string, so not relevant + setClean(nodep, true); // generates a string, so not relevant } virtual void visit(AstUCStmt* nodep) { iterateChildren(nodep); @@ -280,7 +280,7 @@ private: // Default: Just iterate virtual void visit(AstNode* nodep) { iterateChildren(nodep); - computeCppWidth(nodep); + computeCppWidth(nodep); } public: diff --git a/src/V3Clean.h b/src/V3Clean.h index aec0b242a..6e6086c87 100644 --- a/src/V3Clean.h +++ b/src/V3Clean.h @@ -34,4 +34,4 @@ public: static void cleanAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3ClkGater.cpp b/src/V3ClkGater.cpp index 7b8256f3c..dec3b6126 100644 --- a/src/V3ClkGater.cpp +++ b/src/V3ClkGater.cpp @@ -21,14 +21,14 @@ // // Look for clock gaters // Note there's overlap between this process and V3Split's -// ALWAYS: Build graph -// IF: Form vertex pointing to any IFs or ALWAYS above -// VARREF: Form vertex pointing to IFs it is under -// ASSIGN: If under normal assignment, disable optimization +// ALWAYS: Build graph +// IF: Form vertex pointing to any IFs or ALWAYS above +// VARREF: Form vertex pointing to IFs it is under +// ASSIGN: If under normal assignment, disable optimization // FUTURE OPTIMIZE: If signal is set to itself, consider that OK for gating. -// !splitable: Mark all VARREFs in this statement as comming from it -// Optimize graph so if signal is referenced under multiple IF branches it moves up -// Make ALWAYS for each new gating term, and move statements there +// !splitable: Mark all VARREFs in this statement as comming from it +// Optimize graph so if signal is referenced under multiple IF branches it moves up +// Make ALWAYS for each new gating term, and move statements there //************************************************************************* #include "config_build.h" @@ -62,12 +62,12 @@ class GaterVertex : public V3GraphVertex { static uint32_t s_rankNum; public: explicit GaterVertex(V3Graph* graphp) - : V3GraphVertex(graphp) { - s_rankNum++; rank(s_rankNum); + : V3GraphVertex(graphp) { + s_rankNum++; rank(s_rankNum); } virtual ~GaterVertex() {} virtual int typeNum() const = 0; - static void clearRank() { s_rankNum=0; } + static void clearRank() { s_rankNum = 0; } virtual int sortCmp(const V3GraphVertex* rhsp) const; }; uint32_t GaterVertex::s_rankNum = 0; @@ -75,7 +75,7 @@ uint32_t GaterVertex::s_rankNum = 0; class GaterHeadVertex : public GaterVertex { public: explicit GaterHeadVertex(V3Graph* graphp) - : GaterVertex(graphp) {} + : GaterVertex(graphp) {} virtual ~GaterHeadVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { return "*HEAD*"; } @@ -85,7 +85,7 @@ public: class GaterPliVertex : public GaterVertex { public: explicit GaterPliVertex(V3Graph* graphp) - : GaterVertex(graphp) {} + : GaterVertex(graphp) {} virtual ~GaterPliVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { return "*PLI*"; } @@ -93,11 +93,11 @@ public: }; class GaterIfVertex : public GaterVertex { - AstNodeIf* m_nodep; + AstNodeIf* m_nodep; public: AstNodeIf* nodep() const { return m_nodep; } GaterIfVertex(V3Graph* graphp, AstNodeIf* nodep) - : GaterVertex(graphp), m_nodep(nodep) { } + : GaterVertex(graphp), m_nodep(nodep) { } virtual ~GaterIfVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { @@ -105,11 +105,11 @@ public: }; class GaterVarVertex : public GaterVertex { - AstVarScope* m_nodep; + AstVarScope* m_nodep; public: AstVarScope* nodep() const { return m_nodep; } GaterVarVertex(V3Graph* graphp, AstVarScope* nodep) - : GaterVertex(graphp), m_nodep(nodep) { } + : GaterVertex(graphp), m_nodep(nodep) { } virtual ~GaterVarVertex() {} virtual int typeNum() const { return __LINE__; } // C++ typeof() equivelent virtual string name() const { return nodep()->name(); } @@ -120,7 +120,7 @@ public: // Edge types class GaterEdge : public V3GraphEdge { - uint32_t m_ifelse; // True branch of if + uint32_t m_ifelse; // True branch of if public: // These are used as shift amounts into node's user() #define VU_DEFINE enum VarUsage { VU_NONE=0, VU_IF=1, VU_ELSE=2, VU_PLI=4, VU_MADE=8} @@ -131,23 +131,23 @@ public: bool ifelseFalse() const { return m_ifelse & VU_ELSE; } bool ifelseBoth() const { return ifelseTrue() && ifelseFalse(); } GaterEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, uint32_t ifelse) - : V3GraphEdge(graphp, fromp, top, 10, false), m_ifelse(ifelse) {} + : V3GraphEdge(graphp, fromp, top, 10, false), m_ifelse(ifelse) {} virtual ~GaterEdge() {} virtual string dotColor() const { return ((ifelse() & VU_PLI) ? "red" - : (ifelseBoth() ? "blue" - : (ifelseTrue() ? "green" - : "darkgreen"))); } + : (ifelseBoth() ? "blue" + : (ifelseTrue() ? "green" + : "darkgreen"))); } virtual int sortCmp(const V3GraphEdge* rEdgep) const { - // Our master sort sorts by edges first, so we don't want to - // consider the upstream vertex::sortCmp when sorting by edges. - // We do want to order by something though; rank works - if (fromp()->rank() < rEdgep->fromp()->rank()) return -1; - if (fromp()->rank() > rEdgep->fromp()->rank()) return 1; - // If same, resolve by ifelse - const GaterEdge* crEdgep = static_cast(rEdgep); - if (m_ifelse < crEdgep->m_ifelse) return -1; - if (m_ifelse > crEdgep->m_ifelse) return 1; - return 0; + // Our master sort sorts by edges first, so we don't want to + // consider the upstream vertex::sortCmp when sorting by edges. + // We do want to order by something though; rank works + if (fromp()->rank() < rEdgep->fromp()->rank()) return -1; + if (fromp()->rank() > rEdgep->fromp()->rank()) return 1; + // If same, resolve by ifelse + const GaterEdge* crEdgep = static_cast(rEdgep); + if (m_ifelse < crEdgep->m_ifelse) return -1; + if (m_ifelse > crEdgep->m_ifelse) return 1; + return 0; } }; @@ -160,22 +160,22 @@ int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const { // If variable, group by same input fanin // (know they're the same type based on above compare) if (dynamic_cast(this)) { - // We've already sorted by edges, so just see if same tree - // If this gets too slow, we could compute a hash up front - V3GraphEdge* lEdgep = this->inBeginp(); - V3GraphEdge* rEdgep = rhsp->inBeginp(); - while (lEdgep && rEdgep) { - const GaterEdge* clEdgep = static_cast(lEdgep); - const GaterEdge* crEdgep = static_cast(rEdgep); - if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1; - if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1; - if (clEdgep->ifelse() < crEdgep->ifelse()) return -1; - if (clEdgep->ifelse() > crEdgep->ifelse()) return 1; - lEdgep = lEdgep->inNextp(); - rEdgep = rEdgep->inNextp(); - } - if (!lEdgep && !rEdgep) return 0; - return lEdgep ? -1 : 1; + // We've already sorted by edges, so just see if same tree + // If this gets too slow, we could compute a hash up front + V3GraphEdge* lEdgep = this->inBeginp(); + V3GraphEdge* rEdgep = rhsp->inBeginp(); + while (lEdgep && rEdgep) { + const GaterEdge* clEdgep = static_cast(lEdgep); + const GaterEdge* crEdgep = static_cast(rEdgep); + if (lEdgep->fromp()->rank() < rEdgep->fromp()->rank()) return -1; + if (lEdgep->fromp()->rank() > rEdgep->fromp()->rank()) return 1; + if (clEdgep->ifelse() < crEdgep->ifelse()) return -1; + if (clEdgep->ifelse() > crEdgep->ifelse()) return 1; + lEdgep = lEdgep->inNextp(); + rEdgep = rEdgep->inNextp(); + } + if (!lEdgep && !rEdgep) return 0; + return lEdgep ? -1 : 1; } // Finally by rank of this vertex if (rank() < rhsp->rank()) return -1; @@ -189,32 +189,32 @@ int GaterVertex::sortCmp(const V3GraphVertex* rhsp) const { class GaterCondVisitor : public GaterBaseVisitor { private: // RETURN STATE - bool m_isSimple; // Set false when we know it isn't simple + bool m_isSimple; // Set false when we know it isn't simple // METHODS inline void okIterate(AstNode* nodep) { if (m_isSimple) iterateChildren(nodep); } // VISITORS - virtual void visit(AstOr* nodep) { okIterate(nodep); } - virtual void visit(AstAnd* nodep) { okIterate(nodep); } - virtual void visit(AstNot* nodep) { okIterate(nodep); } - virtual void visit(AstLogOr* nodep) { okIterate(nodep); } - virtual void visit(AstLogAnd* nodep) { okIterate(nodep); } - virtual void visit(AstLogNot* nodep) { okIterate(nodep); } - virtual void visit(AstVarRef* nodep) { okIterate(nodep); } + virtual void visit(AstOr* nodep) { okIterate(nodep); } + virtual void visit(AstAnd* nodep) { okIterate(nodep); } + virtual void visit(AstNot* nodep) { okIterate(nodep); } + virtual void visit(AstLogOr* nodep) { okIterate(nodep); } + virtual void visit(AstLogAnd* nodep) { okIterate(nodep); } + virtual void visit(AstLogNot* nodep) { okIterate(nodep); } + virtual void visit(AstVarRef* nodep) { okIterate(nodep); } // Other possibilities are equals, etc // But, we don't want to get too complicated or it will take too much // effort to calculate the gater virtual void visit(AstNode* nodep) { - m_isSimple = false; + m_isSimple = false; //iterateChildren(nodep); } public: // CONSTUCTORS explicit GaterCondVisitor(AstNode* nodep) { - m_isSimple = true; + m_isSimple = true; iterate(nodep); } virtual ~GaterCondVisitor() {} @@ -228,44 +228,44 @@ public: class GaterBodyVisitor : public GaterBaseVisitor { // NODE STATE // Input state - // AstVarScope::user2p() -> AstAlways* moving this variable to + // AstVarScope::user2p() -> AstAlways* moving this variable to enum State { - // This is used as a bitmask - STATE_UNKNOWN=0, - STATE_KEEP=1, // Seen a variable we need, keep this statement - STATE_DELETE=2 // Seen a variable we need, delete this statement - // 3=keep & delete + // This is used as a bitmask + STATE_UNKNOWN = 0, + STATE_KEEP = 1, // Seen a variable we need, keep this statement + STATE_DELETE = 2 // Seen a variable we need, delete this statement + // 3=keep & delete }; - bool m_original; // Deleting original statements, vs deleting new var statements - AstNode* m_exprp; // New gater expression we are building - bool m_cloning; // Clone this object 0=not sure yet, 1=do - uint32_t m_state; // Parsing state + bool m_original; // Deleting original statements, vs deleting new var statements + AstNode* m_exprp; // New gater expression we are building + bool m_cloning; // Clone this object 0=not sure yet, 1=do + uint32_t m_state; // Parsing state // VISITORS virtual void visit(AstVarRef* nodep) { - if (nodep->lvalue()) { - AstVarScope* vscp = nodep->varScopep(); - if (vscp->user2p() == m_exprp) { - // This variable's block needs to move to the new always - if (m_original) { - UINFO(9," VARREF delete in old: "<lvalue()) { + AstVarScope* vscp = nodep->varScopep(); + if (vscp->user2p() == m_exprp) { + // This variable's block needs to move to the new always + if (m_original) { + UINFO(9," VARREF delete in old: "<unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - // Pass upwards we did delete - m_state |= STATE_DELETE; - } else { - // Pass upwards we must keep - m_state |= STATE_KEEP; - } + if ((childstate & STATE_DELETE) && !(childstate & STATE_KEEP)) { + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // Pass upwards we did delete + m_state |= STATE_DELETE; + } else { + // Pass upwards we must keep + m_state |= STATE_KEEP; + } } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -310,15 +310,15 @@ class GaterBodyVisitor : public GaterBaseVisitor { public: // CONSTUCTORS GaterBodyVisitor(AstAlways* nodep, AstNode* exprp, bool original) { - m_exprp = exprp; - m_original = original; - m_state = STATE_UNKNOWN; - m_cloning = false; - if (debug()>=9) nodep->dumpTree(cout," GateBodyIn: "); + m_exprp = exprp; + m_original = original; + m_state = STATE_UNKNOWN; + m_cloning = false; + if (debug()>=9) nodep->dumpTree(cout, " GateBodyIn: "); iterateAndNextNull(nodep->bodysp()); - if (debug()>=9) nodep->dumpTree(cout," GateBodyOut: "); - // If there's no statements we shouldn't have had a resulting graph - // vertex asking for this creation + if (debug()>=9) nodep->dumpTree(cout, " GateBodyOut: "); + // If there's no statements we shouldn't have had a resulting graph + // vertex asking for this creation } virtual ~GaterBodyVisitor() {} }; @@ -329,581 +329,584 @@ public: class GaterVisitor : public GaterBaseVisitor { // NODE STATE // Cleared on Always - // AstVarScope::user1p() -> GaterVarVertex* - // AstAlways::user4() -> bool. True indicates processed + // AstVarScope::user1p() -> GaterVarVertex* + // AstAlways::user4() -> bool. True indicates processed // Cleared on each new Always - // AstVarScope::user2p() -> AstAlways* moving this variable to - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser4InUse m_inuser4; + // AstVarScope::user2p() -> AstAlways* moving this variable to + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser4InUse m_inuser4; // GRAPH STATE // Cleared on simplify - // Vertex::user() -> VarUsage: Mark of which if/else edges are hit + // Vertex::user() -> VarUsage: Mark of which if/else edges are hit // TYPES VU_DEFINE; enum MiscConsts { - IF_DEPTH_MAX = 4, // IFs deep we bother to analyze - DOMAINS_MAX = 32 // Clock domains before avoiding O(N^2) blowup + IF_DEPTH_MAX = 4, // IFs deep we bother to analyze + DOMAINS_MAX = 32 // Clock domains before avoiding O(N^2) blowup }; // MEMBERS - string m_nonopt; // Reason block is not optimizable - V3Double0 m_statGaters; // Statistic tracking - V3Double0 m_statBits; // Statistic tracking - bool m_directlyUnderAlw; // Immediately under Always or If - int m_ifDepth; // Depth of IF statements - int m_numIfs; // Number of IF statements - V3Graph m_graph; // Scoreboard of var usages/dependencies - GaterPliVertex* m_pliVertexp; // Element specifying PLI ordering - GaterHeadVertex* m_headVertexp; // Top vertex - V3GraphVertex* m_aboveVertexp; // Vertex above this point in tree - uint32_t m_aboveTrue; // Vertex above this point is true branch - AstVarScope* m_stmtVscp; // Current statement had variable assigned - bool m_stmtInPli; // Current statement has PLI + string m_nonopt; // Reason block is not optimizable + V3Double0 m_statGaters; // Statistic tracking + V3Double0 m_statBits; // Statistic tracking + bool m_directlyUnderAlw; // Immediately under Always or If + int m_ifDepth; // Depth of IF statements + int m_numIfs; // Number of IF statements + V3Graph m_graph; // Scoreboard of var usages/dependencies + GaterPliVertex* m_pliVertexp; // Element specifying PLI ordering + GaterHeadVertex* m_headVertexp; // Top vertex + V3GraphVertex* m_aboveVertexp; // Vertex above this point in tree + uint32_t m_aboveTrue; // Vertex above this point is true branch + AstVarScope* m_stmtVscp; // Current statement had variable assigned + bool m_stmtInPli; // Current statement has PLI // METHODS void nonOptimizable(AstNode* nodep, const char* reasonp) { - if (m_nonopt=="") { - UINFO(9," Nonopt: "<(m_stmtVscp->user1p()); - new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI); - } - m_stmtInPli = true; // Mark all followon variables too + new GaterEdge(&m_graph, m_pliVertexp, varVtxp, VU_PLI); + } + m_stmtInPli = true; // Mark all followon variables too } // METHODS -- GRAPH stuff void simplifyGraph() { - if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_init", false); + if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_init", false); - // We now have all PLI variables with edges FROM the pli vertex - simplifyPli(); - if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_pli", false); + // We now have all PLI variables with edges FROM the pli vertex + simplifyPli(); + if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_pli", false); - // Any vertex that points along both true & false path to variable - // can be simplfied so parent points to that vertex. Any vertex - // that points to a (great...) grandparent of a variable can just - // point to the edge. - m_graph.userClearVertices(); // user() will contain VarUsage - simplifyIfElseRecurse(m_headVertexp); - if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_ifelse", false); + // Any vertex that points along both true & false path to variable + // can be simplfied so parent points to that vertex. Any vertex + // that points to a (great...) grandparent of a variable can just + // point to the edge. + m_graph.userClearVertices(); // user() will contain VarUsage + simplifyIfElseRecurse(m_headVertexp); + if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_ifelse", false); - m_graph.userClearVertices(); // user() will contain VarUsage - simplifyGrandRecurse(m_headVertexp, 1); - if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_grand", false); + m_graph.userClearVertices(); // user() will contain VarUsage + simplifyGrandRecurse(m_headVertexp, 1); + if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_grand", false); - simplifyRemoveUngated(m_headVertexp); + simplifyRemoveUngated(m_headVertexp); - // Give all signals with same gating term same color - graphColorSameFeeder(); + // Give all signals with same gating term same color + graphColorSameFeeder(); - if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_done", false); + if (debug()>=9) m_graph.dumpDotFilePrefixed("gater_done", false); } void simplifyPli() { - // For now, we'll not gate any logic with PLI lvariables in it. In - // the future we may move PLI statements. One way to do so is to - // all below pli VARs to go IfVertex -> PliVertex -> VarVertex then - // they all must get colored the same. There may be a lot of - // duplicated edges to the PLI; they'll need cleanup. All of the - // later optimizations need to deal, and the GaterBodyVisitor needs - // to know how to move them. - if (m_pliVertexp) { - // Follow PLI out edges to find all relevant variables - for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) { - nextp = edgep->outNextp(); // We may edit the list - if (GaterVarVertex* vVxp = dynamic_cast(edgep->top())) { - vVxp->unlinkDelete(&m_graph); VL_DANGLING(vVxp); VL_DANGLING(edgep); - } else { - m_graph.dump(); - v3fatalSrc("PLI vertex points to non-signal"); - } - } - m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL; - } + // For now, we'll not gate any logic with PLI lvariables in it. In + // the future we may move PLI statements. One way to do so is to + // all below pli VARs to go IfVertex -> PliVertex -> VarVertex then + // they all must get colored the same. There may be a lot of + // duplicated edges to the PLI; they'll need cleanup. All of the + // later optimizations need to deal, and the GaterBodyVisitor needs + // to know how to move them. + if (m_pliVertexp) { + // Follow PLI out edges to find all relevant variables + for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) { + nextp = edgep->outNextp(); // We may edit the list + if (GaterVarVertex* vVxp = dynamic_cast(edgep->top())) { + vVxp->unlinkDelete(&m_graph); VL_DANGLING(vVxp); VL_DANGLING(edgep); + } else { + m_graph.dump(); + v3fatalSrc("PLI vertex points to non-signal"); + } + } + m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL; + } } void simplifyIfElseRecurse(V3GraphVertex* vertexp) { - // From bottom-up, propagate duplicate IF/ELSE branches to grandparent - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - simplifyIfElseRecurse(edgep->top()); - } - //UINFO(9,"IERecurse "<(vertexp)) { - // Clear indications - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - edgep->top()->user(VU_NONE); - } - // Mark nodes on to/from side - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - V3GraphVertex* toVxp = edgep->top(); - GaterEdge* cedgep = static_cast(edgep); - // We may mark this twice in one pass - if so it's duplicated; no worries - if (cedgep->ifelseTrue()) toVxp->user(toVxp->user() | VU_IF); - if (cedgep->ifelseFalse()) toVxp->user(toVxp->user() | VU_ELSE); - //UINFO(9," mark "<outBeginp(); edgep; edgep = nextp) { - nextp = edgep->outNextp(); // We may edit the list - V3GraphVertex* toVxp = edgep->top(); - //UINFO(9," to "<user()<<" "<user() & VU_IF) && (toVxp->user() & VU_ELSE)) { - edgep->unlinkDelete(); VL_DANGLING(edgep); - if (!(toVxp->user() & VU_MADE)) { // Make an edge only once - toVxp->user(toVxp->user() | VU_MADE); - GaterEdge* inedgep = static_cast(vertexp->inBeginp()); - V3GraphVertex* grandparent = inedgep->fromp(); - new GaterEdge(&m_graph, grandparent, toVxp, inedgep->ifelse()); - } - } - } - } + // From bottom-up, propagate duplicate IF/ELSE branches to grandparent + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + simplifyIfElseRecurse(edgep->top()); + } + //UINFO(9,"IERecurse "<(vertexp)) { + // Clear indications + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + edgep->top()->user(VU_NONE); + } + // Mark nodes on to/from side + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + V3GraphVertex* toVxp = edgep->top(); + GaterEdge* cedgep = static_cast(edgep); + // We may mark this twice in one pass - if so it's duplicated; no worries + if (cedgep->ifelseTrue()) toVxp->user(toVxp->user() | VU_IF); + if (cedgep->ifelseFalse()) toVxp->user(toVxp->user() | VU_ELSE); + //UINFO(9," mark "<outBeginp(); edgep; edgep = nextp) { + nextp = edgep->outNextp(); // We may edit the list + V3GraphVertex* toVxp = edgep->top(); + //UINFO(9," to "<user()<<" "<user() & VU_IF) && (toVxp->user() & VU_ELSE)) { + edgep->unlinkDelete(); VL_DANGLING(edgep); + if (!(toVxp->user() & VU_MADE)) { // Make an edge only once + toVxp->user(toVxp->user() | VU_MADE); + GaterEdge* inedgep = static_cast(vertexp->inBeginp()); + V3GraphVertex* grandparent = inedgep->fromp(); + new GaterEdge(&m_graph, grandparent, toVxp, inedgep->ifelse()); + } + } + } + } } void simplifyGrandRecurse(V3GraphVertex* vertexp, uint32_t depth) { - // From top-down delete any vars that grandparents source - // IE A -> B -> C -> VAR - // \----------^ - //UINFO(9,"GRecurse "<outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); // We may edit the list - if (GaterVarVertex* toVxp = dynamic_cast(edgep->top())) { - if (toVxp->user() && toVxp->user() < depth) { - // A recursion "above" us marked it, - // Remove this edge, it's redundant with the upper edge - edgep->unlinkDelete(); VL_DANGLING(edgep); - } else { - GaterEdge* cedgep = static_cast(edgep); - if (cedgep->ifelseBoth()) { - toVxp->user(depth); - } - } - } - } - // Recurse - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - simplifyGrandRecurse(edgep->top(), depth+1); - } - // Clean our marks - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - V3GraphVertex* toVxp = edgep->top(); - if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it; don't mess with it - } else { - toVxp->user(0); // We marked it originally, so unmark now - } - } - // Delete any If nodes with no children - // Last, as want bottom-up cleanup - for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); // We may edit the list - if (GaterIfVertex* toVxp = dynamic_cast(edgep->top())) { - if (!toVxp->outBeginp()) { - if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop - toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep); - } - } - } - } + // From top-down delete any vars that grandparents source + // IE A -> B -> C -> VAR + // \----------^ + //UINFO(9,"GRecurse "<outBeginp(); edgep; edgep=nextp) { + nextp = edgep->outNextp(); // We may edit the list + if (GaterVarVertex* toVxp = dynamic_cast(edgep->top())) { + if (toVxp->user() && toVxp->user() < depth) { + // A recursion "above" us marked it, + // Remove this edge, it's redundant with the upper edge + edgep->unlinkDelete(); VL_DANGLING(edgep); + } else { + GaterEdge* cedgep = static_cast(edgep); + if (cedgep->ifelseBoth()) { + toVxp->user(depth); + } + } + } + } + // Recurse + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + simplifyGrandRecurse(edgep->top(), depth+1); + } + // Clean our marks + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + V3GraphVertex* toVxp = edgep->top(); + if (toVxp->user() && toVxp->user() < depth) { // A recursion "above" us marked it; don't mess with it + } else { + toVxp->user(0); // We marked it originally, so unmark now + } + } + // Delete any If nodes with no children + // Last, as want bottom-up cleanup + for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { + nextp = edgep->outNextp(); // We may edit the list + if (GaterIfVertex* toVxp = dynamic_cast(edgep->top())) { + if (!toVxp->outBeginp()) { + if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop + toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep); + } + } + } + } } void simplifyRemoveUngated(V3GraphVertex* vertexp) { - // Remove variables that are ungated - // At this point, any variable under the head is ungated - for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); // We may edit the list - if (GaterVarVertex* toVxp = dynamic_cast(edgep->top())) { - if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop - toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep); - } - } - } + // Remove variables that are ungated + // At this point, any variable under the head is ungated + for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { + nextp = edgep->outNextp(); // We may edit the list + if (GaterVarVertex* toVxp = dynamic_cast(edgep->top())) { + if (!nextp || nextp->top() != edgep->top()) { // Else next would disappear; we'll do it next loop + toVxp->unlinkDelete(&m_graph); VL_DANGLING(toVxp); VL_DANGLING(edgep); + } + } + } } void graphColorSameFeeder() { - // Assign same color to all destination vertices that have same - // subgraph feeding into them - // (I.E. all "from" nodes are common within each color) - // We could hash, but instead it's faster to sort edges, so we know - // the same edge list is always adjacent. The result is a - // O(vertices*edges) loop, but we'd need that to hash too. - if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); } + // Assign same color to all destination vertices that have same + // subgraph feeding into them + // (I.E. all "from" nodes are common within each color) + // We could hash, but instead it's faster to sort edges, so we know + // the same edge list is always adjacent. The result is a + // O(vertices*edges) loop, but we'd need that to hash too. + if (debug()>9) { cout<<"PreColor:\n"; m_graph.dump(); } - m_graph.sortEdges(); + m_graph.sortEdges(); - // Now sort vertices, so same inbound edge set ends up adjacent - m_graph.sortVertices(); + // Now sort vertices, so same inbound edge set ends up adjacent + m_graph.sortVertices(); - // Now walk and assign colors; same color is adjacent - m_graph.clearColors(); - m_graph.userClearEdges(); // Used by newExprFromGraph - uint32_t color = 1; - GaterVarVertex* lastVxp = NULL; - for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { - if (!vVxp->inBeginp()) { - // At this point, any variable not linked is an error - // (It should have at least landed under the Head node) - vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection"); - } - if (!lastVxp || vVxp->sortCmp(lastVxp)) { - // Different sources for this new node - color++; - } - vVxp->color(color); - lastVxp = vVxp; - } - } + // Now walk and assign colors; same color is adjacent + m_graph.clearColors(); + m_graph.userClearEdges(); // Used by newExprFromGraph + uint32_t color = 1; + GaterVarVertex* lastVxp = NULL; + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { + if (!vVxp->inBeginp()) { + // At this point, any variable not linked is an error + // (It should have at least landed under the Head node) + vVxp->nodep()->v3fatalSrc("Variable became stranded in clk gate detection"); + } + if (!lastVxp || vVxp->sortCmp(lastVxp)) { + // Different sources for this new node + color++; + } + vVxp->color(color); + lastVxp = vVxp; + } + } - if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); } + if (debug()>9) { cout<<"PostColor:\n"; m_graph.dump(); } } AstNode* newExprFromGraph(GaterVarVertex* vertexp) { - // Recurse backwards, then form equation on return path - // We could use user()!=0, but zeroing it is slow, so instead we'll mark with a generation - // We get equations like "a | (!a & b)" which obviously could be reduced here, - // instead out of generality, there's a V3Const::matchOrAndNot that'll clean it up - static uint32_t s_generation = 0; - ++s_generation; - nafgMarkRecurse(vertexp, s_generation); - AstNode* nodep = nafgCreateRecurse(m_headVertexp, s_generation); - if (debug()>=9) nodep->dumpTree(cout," GateExpr: "); - return nodep; + // Recurse backwards, then form equation on return path + // We could use user()!=0, but zeroing it is slow, so instead we'll mark with a generation + // We get equations like "a | (!a & b)" which obviously could be reduced here, + // instead out of generality, there's a V3Const::matchOrAndNot that'll clean it up + static uint32_t s_generation = 0; + ++s_generation; + nafgMarkRecurse(vertexp, s_generation); + AstNode* nodep = nafgCreateRecurse(m_headVertexp, s_generation); + if (debug()>=9) nodep->dumpTree(cout, " GateExpr: "); + return nodep; } void nafgMarkRecurse(V3GraphVertex* vertexp, uint32_t generation) { - // Backwards mark user() on the path we recurse + // Backwards mark user() on the path we recurse //UINFO(9," nafgMark: v "<name()<inBeginp(); edgep; edgep = edgep->inNextp()) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { //UINFO(9," nafgMark: "<name()<user(generation); - nafgMarkRecurse(edgep->fromp(), generation); - } + edgep->user(generation); + nafgMarkRecurse(edgep->fromp(), generation); + } } AstNode* nafgCreateRecurse(V3GraphVertex* vertexp, uint32_t generation) { - // Forewards follow user() marked previously and build tree - AstNode* nodep = NULL; - // OR across all edges found at this level + // Forewards follow user() marked previously and build tree + AstNode* nodep = NULL; + // OR across all edges found at this level //UINFO(9," nafgEnter: v "<name()<outBeginp(); edgep; edgep = edgep->outNextp()) { - if (edgep->user() == generation) { - GaterEdge* cedgep = static_cast(edgep); - AstNode* eqnp = NULL; + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { + if (edgep->user() == generation) { + GaterEdge* cedgep = static_cast(edgep); + AstNode* eqnp = NULL; //UINFO(9," nafgFollow: "<name()<(edgep->fromp())) { - // Just OR in all lower terms - eqnp = nafgCreateRecurse(edgep->top(), generation); - } else if (GaterIfVertex* cVxp = dynamic_cast(edgep->fromp())) { - // Edges from IFs represent a real IF branch in the equation tree + if (dynamic_cast(edgep->fromp())) { + // Just OR in all lower terms + eqnp = nafgCreateRecurse(edgep->top(), generation); + } else if (GaterIfVertex* cVxp = dynamic_cast(edgep->fromp())) { + // Edges from IFs represent a real IF branch in the equation tree //UINFO(9," ifver "<dotColor()<nodep()->condp()->cloneTree(true); - if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition"); - if (cedgep->ifelseFalse()) { - eqnp = new AstNot(eqnp->fileline(),eqnp); - } - // We need to AND this term onto whatever was found below it - AstNode* belowp = nafgCreateRecurse(edgep->top(), generation); - if (belowp) eqnp = new AstAnd(eqnp->fileline(),eqnp,belowp); - } - // Top level we could choose to make multiple gaters, or ORs under the gater - // Right now we'll put OR lower down and let other optimizations deal - if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp); - else nodep = eqnp; - //if (debug()>=9) nodep->dumpTree(cout," followExpr: "); - } - } + eqnp = cVxp->nodep()->condp()->cloneTree(true); + if (!eqnp) cVxp->nodep()->v3fatalSrc("null condition"); + if (cedgep->ifelseFalse()) { + eqnp = new AstNot(eqnp->fileline(), eqnp); + } + // We need to AND this term onto whatever was found below it + AstNode* belowp = nafgCreateRecurse(edgep->top(), generation); + if (belowp) eqnp = new AstAnd(eqnp->fileline(), eqnp, belowp); + } + // Top level we could choose to make multiple gaters, or ORs under the gater + // Right now we'll put OR lower down and let other optimizations deal + if (nodep) nodep = new AstOr(nodep->fileline(), nodep, eqnp); + else nodep = eqnp; + //if (debug()>=9) nodep->dumpTree(cout, " followExpr: "); + } + } //UINFO(9," nafgExit: "<name()<verticesNextp()) { - if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { - if (!lastExprp || lastColor != vVxp->color()) { - lastColor = vVxp->color(); - // Create the block we've just finished - if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate below - // New expression for this color - lastExprp = newExprFromGraph(vVxp); - } - // Mark variable to move - if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters"); - vVxp->nodep()->user2p(lastExprp); - m_statBits += vVxp->nodep()->width(); // Moving a wide bus counts more! - // There shouldn't be two possibilities we want to - // move to, IE {A,B} <= Z because we marked such - // things as unoptimizable - } - } - // Create the final block we've just finished - if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate above + // Across all variables we're moving + uint32_t lastColor = 0; + AstNode* lastExprp = NULL; + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { + if (!lastExprp || lastColor != vVxp->color()) { + lastColor = vVxp->color(); + // Create the block we've just finished + if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate below + // New expression for this color + lastExprp = newExprFromGraph(vVxp); + } + // Mark variable to move + if (vVxp->nodep()->user2p()) vVxp->nodep()->v3fatalSrc("One variable got marked under two gaters"); + vVxp->nodep()->user2p(lastExprp); + m_statBits += vVxp->nodep()->width(); // Moving a wide bus counts more! + // There shouldn't be two possibilities we want to + // move to, IE {A,B} <= Z because we marked such + // things as unoptimizable + } + } + // Create the final block we've just finished + if (lastExprp) newAlwaysTree(nodep, lastExprp); // Duplicate above } void newAlwaysTree(AstAlways* nodep, AstNode* exprp) { - // Create new always with specified gating expression - // Relevant vars are already marked with user2p + // Create new always with specified gating expression + // Relevant vars are already marked with user2p - // Should we put the gater under the always itself, - // or make a new signal? - // New signal: May cause replicated logic until we can combine - // Need way to toggle signal on complicated pos/negedge - // Under Always: More complicated, may not propagate logic with gateer - // Untill we get better gate optimization, we'll go this route. + // Should we put the gater under the always itself, + // or make a new signal? + // New signal: May cause replicated logic until we can combine + // Need way to toggle signal on complicated pos/negedge + // Under Always: More complicated, may not propagate logic with gateer + // Untill we get better gate optimization, we'll go this route. //#define GATER_NOP #ifdef GATER_NOP - // For testing only, don't clock gate but still make new always - AstSenTree* sensesp = nodep->sensesp()->cloneTree(true); + // For testing only, don't clock gate but still make new always + AstSenTree* sensesp = nodep->sensesp()->cloneTree(true); #else - // Make a SenGate + // Make a SenGate AstSenItem* oldsenitemsp = VN_CAST(nodep->sensesp()->sensesp(), SenItem); - if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it"); + if (!oldsenitemsp) nodep->v3fatalSrc("SenTree doesn't have any SenItem under it"); - AstSenTree* sensesp = new AstSenTree(nodep->fileline(), - new AstSenGate(nodep->fileline(), - oldsenitemsp->cloneTree(true), - exprp)); + AstSenTree* sensesp = new AstSenTree(nodep->fileline(), + new AstSenGate(nodep->fileline(), + oldsenitemsp->cloneTree(true), + exprp)); #endif - // Make new body; note clone will preserve user2p() indicating moving vars - AstNode* bodyp = nodep->bodysp()->cloneTree(true); + // Make new body; note clone will preserve user2p() indicating moving vars + AstNode* bodyp = nodep->bodysp()->cloneTree(true); - AstAlways* alwp = new AstAlways(nodep->fileline(), - nodep->keyword(), - sensesp, - bodyp); + AstAlways* alwp = new AstAlways(nodep->fileline(), + nodep->keyword(), + sensesp, + bodyp); - alwp->user4(1); // No need to process the new always again! - nodep->addNextHere(alwp); + alwp->user4(1); // No need to process the new always again! + nodep->addNextHere(alwp); - // Blow moved statements from old body - { GaterBodyVisitor vis(nodep,exprp,true); } - // Blow old statements from new body - { GaterBodyVisitor vis(alwp,exprp,false); } + // Blow moved statements from old body + { GaterBodyVisitor vis(nodep, exprp, true); } + // Blow old statements from new body + { GaterBodyVisitor vis(alwp, exprp, false); } - ++m_statGaters; - if (debug()>=9) alwp->dumpTree(cout," new: "); + ++m_statGaters; + if (debug()>=9) alwp->dumpTree(cout, " new: "); } // VISITORS virtual void visit(AstAlways* nodep) { - if (debug()>=9) cout<user4SetOnce()) return; + if (debug()>=9) cout<user4SetOnce()) return; - clear(); + clear(); - if (debug()>=9) nodep->dumpTree(cout," Alwin: "); + if (debug()>=9) nodep->dumpTree(cout, " Alwin: "); - // Look for constructs we can't optimize - // Form graph with Vertices at each IF, and each Variable - m_aboveTrue = VU_IF | VU_ELSE; - iterateChildrenAlw(nodep, true); + // Look for constructs we can't optimize + // Form graph with Vertices at each IF, and each Variable + m_aboveTrue = VU_IF | VU_ELSE; + iterateChildrenAlw(nodep, true); - // Other reasons to not optimize - if (!m_numIfs) nonOptimizable(nodep, "No if statements"); + // Other reasons to not optimize + if (!m_numIfs) nonOptimizable(nodep, "No if statements"); - // Something to optimize, oh my! - if (m_nonopt!="") { - UINFO(5, " Gater non-opt: "<verticesNextp()) { - if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { - if (lastColor < vVxp->color()) { - lastColor = vVxp->color(); - } - } - } - if (lastColor == 0) { // Nothing we moved! - nonOptimizable(nodep, "Nothing moved"); - } - else if (lastColor > DOMAINS_MAX) { - // Our move algorithm is fairly slow and if we're splitting - // up too much it'll get really nasty. It's probably a bad - // move for performance to split too much, anyhow, as the - // number of gaters will result in calling many small c functions. - nonOptimizable(nodep, "Too much moved"); - } - if (m_nonopt=="") { - newAlwaysTrees(nodep); - if (debug()>=9) nodep->dumpTree(cout," Gaterout: "); - } - } - UINFO(5, " Gater done"<verticesNextp()) { + if (GaterVarVertex* vVxp = dynamic_cast(vertexp)) { + if (lastColor < vVxp->color()) { + lastColor = vVxp->color(); + } + } + } + if (lastColor == 0) { // Nothing we moved! + nonOptimizable(nodep, "Nothing moved"); + } + else if (lastColor > DOMAINS_MAX) { + // Our move algorithm is fairly slow and if we're splitting + // up too much it'll get really nasty. It's probably a bad + // move for performance to split too much, anyhow, as the + // number of gaters will result in calling many small c functions. + nonOptimizable(nodep, "Too much moved"); + } + if (m_nonopt=="") { + newAlwaysTrees(nodep); + if (debug()>=9) nodep->dumpTree(cout, " Gaterout: "); + } + } + UINFO(5, " Gater done"<lvalue()) { - AstVarScope* vscp = nodep->varScopep(); - if (nodep->varp()->isSigPublic()) { - // Public signals shouldn't be changed, pli code might be messing with them - scoreboardPli(nodep); - } - // If another lvalue in this node, give up optimizing. - // We could just not optimize this variable, but we've already marked the - // other variable as optimizable, so we can instead pretend it's a PLI node. - if (m_stmtVscp) { - UINFO(5, " Multiple lvalues in one statement: "<lvalue()) { + AstVarScope* vscp = nodep->varScopep(); + if (nodep->varp()->isSigPublic()) { + // Public signals shouldn't be changed, pli code might be messing with them + scoreboardPli(nodep); + } + // If another lvalue in this node, give up optimizing. + // We could just not optimize this variable, but we've already marked the + // other variable as optimizable, so we can instead pretend it's a PLI node. + if (m_stmtVscp) { + UINFO(5, " Multiple lvalues in one statement: "<(vscp->user1p()); - if (!vertexp) { - vertexp = new GaterVarVertex(&m_graph, vscp); - vscp->user1p(vertexp); - } - new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue); - if (m_stmtInPli) { - new GaterEdge(&m_graph, m_pliVertexp, vertexp, VU_PLI); - } - } + if (!vertexp) { + vertexp = new GaterVarVertex(&m_graph, vscp); + vscp->user1p(vertexp); + } + new GaterEdge(&m_graph, m_aboveVertexp, vertexp, m_aboveTrue); + if (m_stmtInPli) { + new GaterEdge(&m_graph, m_pliVertexp, vertexp, VU_PLI); + } + } } virtual void visit(AstNodeIf* nodep) { - m_ifDepth++; - bool allowGater = m_directlyUnderAlw && m_ifDepth <= IF_DEPTH_MAX; - if (allowGater) { - GaterCondVisitor condVisitor(nodep->condp()); - if (!condVisitor.isSimple()) { - // Don't clear optimization, simply drop this IF as part of the gating - UINFO(5," IFnon-simple-condition: "<condp()); + if (!condVisitor.isSimple()) { + // Don't clear optimization, simply drop this IF as part of the gating + UINFO(5," IFnon-simple-condition: "<condp()); // directlyUnder stays as-is - } - { - m_aboveVertexp = vertexp; // Vars will point at this edge - m_aboveTrue = VU_IF; + } + { + m_aboveVertexp = vertexp; // Vars will point at this edge + m_aboveTrue = VU_IF; iterateAndNextNull(nodep->ifsp()); // directlyUnder stays as-is (true) - } - { - m_aboveVertexp = vertexp; // Vars will point at this edge - m_aboveTrue = VU_ELSE; + } + { + m_aboveVertexp = vertexp; // Vars will point at this edge + m_aboveTrue = VU_ELSE; iterateAndNextNull(nodep->elsesp()); // directlyUnder stays as-is (true) - } - m_aboveVertexp = lastabovep; - m_aboveTrue = lasttrue; - } - m_ifDepth--; + } + m_aboveVertexp = lastabovep; + m_aboveTrue = lasttrue; + } + m_ifDepth--; } virtual void visit(AstAssignDly* nodep) { - // iterateChildrenAlw will detect this is a statement for us - iterateChildrenAlw(nodep, false); + // iterateChildrenAlw will detect this is a statement for us + iterateChildrenAlw(nodep, false); } virtual void visit(AstNodeAssign* nodep) { - // Note NOT AssignDly; handled above, We'll just mark this block as - // not optimizable. - // - // A future alternative is to look for any lvalues that are also - // variables in the sensitivity list, or rvalues in this block. If - // any hit, disable optimization. Unlikely to be useful (For loops - // being an exception, but they're already unrolled.) - nonOptimizable(nodep, "Non-delayed assignment"); - // No reason to iterate. + // Note NOT AssignDly; handled above, We'll just mark this block as + // not optimizable. + // + // A future alternative is to look for any lvalues that are also + // variables in the sensitivity list, or rvalues in this block. If + // any hit, disable optimization. Unlikely to be useful (For loops + // being an exception, but they're already unrolled.) + nonOptimizable(nodep, "Non-delayed assignment"); + // No reason to iterate. } virtual void visit(AstSenItem* nodep) { - if (!nodep->isClocked()) { - nonOptimizable(nodep, "Non-clocked sensitivity"); - } - iterateChildrenAlw(nodep, false); + if (!nodep->isClocked()) { + nonOptimizable(nodep, "Non-clocked sensitivity"); + } + iterateChildrenAlw(nodep, false); } //-------------------- virtual void visit(AstNode* nodep) { - if (m_nonopt=="") { // Else accelerate - iterateChildrenAlw(nodep, false); - } + if (m_nonopt=="") { // Else accelerate + iterateChildrenAlw(nodep, false); + } } inline void iterateChildrenAlw(AstNode* nodep, bool under) { - // **** USE THIS INSTEAD OF iterateChildren! - // Note If visitor doesn't call here; does it its own way - bool lastdua = m_directlyUnderAlw; - AstVarScope* lastvscp = m_stmtVscp; - bool lastpli = m_stmtInPli; - m_directlyUnderAlw = under; + // **** USE THIS INSTEAD OF iterateChildren! + // Note If visitor doesn't call here; does it its own way + bool lastdua = m_directlyUnderAlw; + AstVarScope* lastvscp = m_stmtVscp; + bool lastpli = m_stmtInPli; + m_directlyUnderAlw = under; if (VN_IS(nodep, NodeStmt)) { // Restored below - UINFO(9," Stmt: "<isPure() || nodep->isBrancher()) { - // May also be a new statement (above if); if so we mark it immediately - UINFO(9," NotPure "<isPure() || nodep->isBrancher()) { + // May also be a new statement (above if); if so we mark it immediately + UINFO(9," NotPure "< AstVarScope*. Temporary signal that was created. - AstUser1InUse m_inuser1; + // AstVarScope::user1p() -> AstVarScope*. Temporary signal that was created. + AstUser1InUse m_inuser1; // TYPES - enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best + enum { DOUBLE_OR_RATE = 10 }; // How many | per ||, Determined experimentally as best // STATE - AstNodeModule* m_modp; // Current module - AstTopScope* m_topScopep; // Current top scope - AstScope* m_scopep; // Current scope - AstCFunc* m_evalFuncp; // Top eval function we are creating - AstCFunc* m_initFuncp; // Top initial function we are creating - AstCFunc* m_finalFuncp; // Top final function we are creating - AstCFunc* m_settleFuncp; // Top settlement function we are creating - AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates. - AstIf* m_lastIfp; // Last sensitivity if active to add more under + AstNodeModule* m_modp; // Current module + AstTopScope* m_topScopep; // Current top scope + AstScope* m_scopep; // Current scope + AstCFunc* m_evalFuncp; // Top eval function we are creating + AstCFunc* m_initFuncp; // Top initial function we are creating + AstCFunc* m_finalFuncp; // Top final function we are creating + AstCFunc* m_settleFuncp; // Top settlement function we are creating + AstSenTree* m_lastSenp; // Last sensitivity match, so we can detect duplicates. + AstIf* m_lastIfp; // Last sensitivity if active to add more under AstMTaskBody* m_mtaskBodyp; // Current mtask body // METHODS @@ -72,15 +72,18 @@ private: AstVarScope* getCreateLastClk(AstVarScope* vscp) { if (vscp->user1p()) return static_cast(vscp->user1p()); - AstVar* varp = vscp->varp(); - if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: "<prettyName()); - string newvarname = (string("__Vclklast__")+vscp->scopep()->nameDotless()+"__"+varp->name()); - AstVar* newvarp = new AstVar(vscp->fileline(), AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1); + AstVar* varp = vscp->varp(); + if (!varp->width1()) varp->v3error("Unsupported: Clock edge on non-single bit signal: " + <prettyName()); + string newvarname = (string("__Vclklast__") + +vscp->scopep()->nameDotless()+"__"+varp->name()); + AstVar* newvarp = new AstVar(vscp->fileline(), + AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1); newvarp->noReset(true); // Reset by below assign - m_modp->addStmtp(newvarp); - AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp); - vscp->user1p(newvscp); - m_scopep->addVarp(newvscp); + m_modp->addStmtp(newvarp); + AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopep, newvarp); + vscp->user1p(newvscp); + m_scopep->addVarp(newvscp); // Add init AstNode* fromp = new AstVarRef(newvarp->fileline(), vscp, false); if (v3Global.opt.xInitialEdge()) fromp = new AstNot(fromp->fileline(), fromp); @@ -88,243 +91,245 @@ private: new AstVarRef(newvarp->fileline(), newvscp, true), fromp); addToInitial(newinitp); - // At bottom, assign them - AstAssign* finalp + // At bottom, assign them + AstAssign* finalp = new AstAssign(vscp->fileline(), new AstVarRef(vscp->fileline(), newvscp, true), new AstVarRef(vscp->fileline(), vscp, false)); - m_evalFuncp->addFinalsp(finalp); - // - UINFO(4,"New Last: "<addFinalsp(finalp); + // + UINFO(4,"New Last: "<edgeType()==AstEdgeType::ET_ILLEGAL) { - if (!v3Global.opt.bboxUnsup()) { - nodep->v3error("Unsupported: Complicated event expression in sensitive activity list"); - } - return NULL; - } - AstVarScope* clkvscp = nodep->varrefp()->varScopep(); - if (nodep->edgeType()==AstEdgeType::ET_POSEDGE) { - AstVarScope* lastVscp = getCreateLastClk(clkvscp); - newp = new AstAnd(nodep->fileline(), - new AstVarRef(nodep->fileline(), - nodep->varrefp()->varScopep(), false), - new AstNot(nodep->fileline(), - new AstVarRef(nodep->fileline(), - lastVscp, false))); - } else if (nodep->edgeType()==AstEdgeType::ET_NEGEDGE) { - AstVarScope* lastVscp = getCreateLastClk(clkvscp); - newp = new AstAnd(nodep->fileline(), - new AstNot(nodep->fileline(), - new AstVarRef(nodep->fileline(), - nodep->varrefp()->varScopep(), false)), - new AstVarRef(nodep->fileline(), lastVscp, false)); - } else if (nodep->edgeType()==AstEdgeType::ET_BOTHEDGE) { - AstVarScope* lastVscp = getCreateLastClk(clkvscp); - newp = new AstXor(nodep->fileline(), - new AstVarRef(nodep->fileline(), - nodep->varrefp()->varScopep(), false), - new AstVarRef(nodep->fileline(), lastVscp, false)); - } else if (nodep->edgeType()==AstEdgeType::ET_HIGHEDGE) { - newp = new AstVarRef(nodep->fileline(), - clkvscp, false); - } else if (nodep->edgeType()==AstEdgeType::ET_LOWEDGE) { - newp = new AstNot(nodep->fileline(), - new AstVarRef(nodep->fileline(), - clkvscp, false)); - } else { - nodep->v3fatalSrc("Bad edge type"); - } - return newp; + // We know the var is clean, and one bit, so we use binary ops + // for speed instead of logical ops. + // POSEDGE: var & ~var_last + // NEGEDGE: ~var & var_last + // BOTHEDGE: var ^ var_last + // HIGHEDGE: var + // LOWEDGE: ~var + AstNode* newp = NULL; + if (nodep->edgeType()==AstEdgeType::ET_ILLEGAL) { + if (!v3Global.opt.bboxUnsup()) { + nodep->v3error("Unsupported: Complicated event expression in sensitive activity list"); + } + return NULL; + } + AstVarScope* clkvscp = nodep->varrefp()->varScopep(); + if (nodep->edgeType()==AstEdgeType::ET_POSEDGE) { + AstVarScope* lastVscp = getCreateLastClk(clkvscp); + newp = new AstAnd(nodep->fileline(), + new AstVarRef(nodep->fileline(), + nodep->varrefp()->varScopep(), false), + new AstNot(nodep->fileline(), + new AstVarRef(nodep->fileline(), + lastVscp, false))); + } else if (nodep->edgeType()==AstEdgeType::ET_NEGEDGE) { + AstVarScope* lastVscp = getCreateLastClk(clkvscp); + newp = new AstAnd(nodep->fileline(), + new AstNot(nodep->fileline(), + new AstVarRef(nodep->fileline(), + nodep->varrefp()->varScopep(), false)), + new AstVarRef(nodep->fileline(), lastVscp, false)); + } else if (nodep->edgeType()==AstEdgeType::ET_BOTHEDGE) { + AstVarScope* lastVscp = getCreateLastClk(clkvscp); + newp = new AstXor(nodep->fileline(), + new AstVarRef(nodep->fileline(), + nodep->varrefp()->varScopep(), false), + new AstVarRef(nodep->fileline(), lastVscp, false)); + } else if (nodep->edgeType()==AstEdgeType::ET_HIGHEDGE) { + newp = new AstVarRef(nodep->fileline(), + clkvscp, false); + } else if (nodep->edgeType()==AstEdgeType::ET_LOWEDGE) { + newp = new AstNot(nodep->fileline(), + new AstVarRef(nodep->fileline(), + clkvscp, false)); + } else { + nodep->v3fatalSrc("Bad edge type"); + } + return newp; } AstNode* createSenGateEquation(AstSenGate* nodep) { - AstNode* newp = new AstAnd(nodep->fileline(), - createSenseEquation(nodep->sensesp()), - nodep->rhsp()->cloneTree(true)); - return newp; + AstNode* newp = new AstAnd(nodep->fileline(), + createSenseEquation(nodep->sensesp()), + nodep->rhsp()->cloneTree(true)); + return newp; } AstNode* createSenseEquation(AstNodeSenItem* nodesp) { - // Nodep may be a list of elements; we need to walk it - AstNode* senEqnp = NULL; - for (AstNodeSenItem* senp = nodesp; senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { - AstNode* senOnep = NULL; + // Nodep may be a list of elements; we need to walk it + AstNode* senEqnp = NULL; + for (AstNodeSenItem* senp = nodesp; senp; senp = VN_CAST(senp->nextp(), NodeSenItem)) { + AstNode* senOnep = NULL; if (AstSenItem* itemp = VN_CAST(senp, SenItem)) { - senOnep = createSenItemEquation(itemp); + senOnep = createSenItemEquation(itemp); } else if (AstSenGate* itemp = VN_CAST(senp, SenGate)) { - senOnep = createSenGateEquation(itemp); - } else { - senp->v3fatalSrc("Strange node under sentree"); - } - if (senEqnp) { - // Add new OR to the sensitivity list equation - senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep); - } else { - senEqnp = senOnep; - } - } - return senEqnp; + senOnep = createSenGateEquation(itemp); + } else { + senp->v3fatalSrc("Strange node under sentree"); + } + if (senEqnp) { + // Add new OR to the sensitivity list equation + senEqnp = new AstOr(senp->fileline(), senEqnp, senOnep); + } else { + senEqnp = senOnep; + } + } + return senEqnp; } AstIf* makeActiveIf(AstSenTree* sensesp) { - AstNode* senEqnp = createSenseEquation(sensesp->sensesp()); - if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation."); + AstNode* senEqnp = createSenseEquation(sensesp->sensesp()); + if (!senEqnp) sensesp->v3fatalSrc("No sense equation, shouldn't be in sequent activation."); AstIf* newifp = new AstIf(sensesp->fileline(), senEqnp, NULL, NULL); - return (newifp); + return (newifp); } void clearLastSen() { - m_lastSenp = NULL; - m_lastIfp = NULL; + m_lastSenp = NULL; + m_lastIfp = NULL; } // VISITORS virtual void visit(AstTopScope* nodep) { - UINFO(4," TOPSCOPE "<scopep(); - if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?"); - //VV***** We reset all user1p() - AstNode::user1ClearTree(); - // Make top functions - { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep); - funcp->argTypes(EmitCBaseVisitor::symClassVar()); - funcp->dontCombine(true); - funcp->symProlog(true); - funcp->isStatic(true); - funcp->entryPoint(true); - m_scopep->addActivep(funcp); - m_evalFuncp = funcp; - } - { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep); - funcp->argTypes(EmitCBaseVisitor::symClassVar()); - funcp->dontCombine(true); - funcp->slow(true); - funcp->symProlog(true); - funcp->isStatic(true); - funcp->entryPoint(true); - m_scopep->addActivep(funcp); - m_initFuncp = funcp; - } - { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep); - funcp->skipDecl(true); - funcp->dontCombine(true); - funcp->slow(true); - funcp->isStatic(false); - funcp->entryPoint(true); - funcp->addInitsp(new AstCStmt(nodep->fileline(), - EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); - funcp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); - m_scopep->addActivep(funcp); - m_finalFuncp = funcp; - } - { - AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep); - funcp->argTypes(EmitCBaseVisitor::symClassVar()); - funcp->dontCombine(true); - funcp->slow(true); - funcp->isStatic(true); - funcp->symProlog(true); - funcp->entryPoint(true); - m_scopep->addActivep(funcp); - m_settleFuncp = funcp; - } - // Process the activates + UINFO(4," TOPSCOPE "<scopep(); + if (!m_scopep) nodep->v3fatalSrc("No scope found on top level, perhaps you have no statements?"); + //VV***** We reset all user1p() + AstNode::user1ClearTree(); + // Make top functions + { + AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval", m_scopep); + funcp->argTypes(EmitCBaseVisitor::symClassVar()); + funcp->dontCombine(true); + funcp->symProlog(true); + funcp->isStatic(true); + funcp->entryPoint(true); + m_scopep->addActivep(funcp); + m_evalFuncp = funcp; + } + { + AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_initial", m_scopep); + funcp->argTypes(EmitCBaseVisitor::symClassVar()); + funcp->dontCombine(true); + funcp->slow(true); + funcp->symProlog(true); + funcp->isStatic(true); + funcp->entryPoint(true); + m_scopep->addActivep(funcp); + m_initFuncp = funcp; + } + { + AstCFunc* funcp = new AstCFunc(nodep->fileline(), "final", m_scopep); + funcp->skipDecl(true); + funcp->dontCombine(true); + funcp->slow(true); + funcp->isStatic(false); + funcp->entryPoint(true); + funcp->addInitsp(new AstCStmt + (nodep->fileline(), + EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); + funcp->addInitsp(new AstCStmt(nodep->fileline(), + EmitCBaseVisitor::symTopAssign()+"\n")); + m_scopep->addActivep(funcp); + m_finalFuncp = funcp; + } + { + AstCFunc* funcp = new AstCFunc(nodep->fileline(), "_eval_settle", m_scopep); + funcp->argTypes(EmitCBaseVisitor::symClassVar()); + funcp->dontCombine(true); + funcp->slow(true); + funcp->isStatic(true); + funcp->symProlog(true); + funcp->entryPoint(true); + m_scopep->addActivep(funcp); + m_settleFuncp = funcp; + } + // Process the activates iterateChildren(nodep); - // Done, clear so we can detect errors - UINFO(4," TOPSCOPEDONE "<finalClksp()) { - if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope"); - movep->unlinkFrBackWithNext(); - m_evalFuncp->addFinalsp(movep); - } - m_scopep = NULL; + if (AstNode* movep = nodep->finalClksp()) { + if (!m_topScopep) nodep->v3fatalSrc("Final clocks under non-top scope"); + movep->unlinkFrBackWithNext(); + m_evalFuncp->addFinalsp(movep); + } + m_scopep = NULL; } virtual void visit(AstAlways* nodep) { - AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName()); - nodep->replaceWith(cmtp); - if (AstNode* stmtsp = nodep->bodysp()) { - stmtsp->unlinkFrBackWithNext(); - cmtp->addNextHere(stmtsp); - } - nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName()); + nodep->replaceWith(cmtp); + if (AstNode* stmtsp = nodep->bodysp()) { + stmtsp->unlinkFrBackWithNext(); + cmtp->addNextHere(stmtsp); + } + nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstAlwaysPost* nodep) { - AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName()); - nodep->replaceWith(cmtp); - if (AstNode* stmtsp = nodep->bodysp()) { - stmtsp->unlinkFrBackWithNext(); - cmtp->addNextHere(stmtsp); - } - nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName()); + nodep->replaceWith(cmtp); + if (AstNode* stmtsp = nodep->bodysp()) { + stmtsp->unlinkFrBackWithNext(); + cmtp->addNextHere(stmtsp); + } + nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstCoverToggle* nodep) { - //nodep->dumpTree(cout,"ct:"); - //COVERTOGGLE(INC, ORIG, CHANGE) -> - // IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; } - AstNode* incp = nodep->incp()->unlinkFrBack(); - AstNode* origp = nodep->origp()->unlinkFrBack(); - AstNode* changep = nodep->changep()->unlinkFrBack(); - AstIf* newp = new AstIf(nodep->fileline(), - new AstXor(nodep->fileline(), - origp, - changep), - incp, NULL); - // We could add another IF to detect posedges, and only increment if so. - // It's another whole branch though verus a potential memory miss. - // We'll go with the miss. - newp->addIfsp(new AstAssign(nodep->fileline(), - changep->cloneTree(false), - origp->cloneTree(false))); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + //nodep->dumpTree(cout, "ct:"); + //COVERTOGGLE(INC, ORIG, CHANGE) -> + // IF(ORIG ^ CHANGE) { INC; CHANGE = ORIG; } + AstNode* incp = nodep->incp()->unlinkFrBack(); + AstNode* origp = nodep->origp()->unlinkFrBack(); + AstNode* changep = nodep->changep()->unlinkFrBack(); + AstIf* newp = new AstIf(nodep->fileline(), + new AstXor(nodep->fileline(), + origp, + changep), + incp, NULL); + // We could add another IF to detect posedges, and only increment if so. + // It's another whole branch though verus a potential memory miss. + // We'll go with the miss. + newp->addIfsp(new AstAssign(nodep->fileline(), + changep->cloneTree(false), + origp->cloneTree(false))); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstInitial* nodep) { - AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName()); - nodep->replaceWith(cmtp); - if (AstNode* stmtsp = nodep->bodysp()) { - stmtsp->unlinkFrBackWithNext(); - cmtp->addNextHere(stmtsp); - } - nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* cmtp = new AstComment(nodep->fileline(), nodep->typeName()); + nodep->replaceWith(cmtp); + if (AstNode* stmtsp = nodep->bodysp()) { + stmtsp->unlinkFrBackWithNext(); + cmtp->addNextHere(stmtsp); + } + nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstCFunc* nodep) { iterateChildren(nodep); - // Link to global function - if (nodep->formCallTree()) { - UINFO(4, " formCallTree "<fileline(), nodep); - callp->argTypes("vlSymsp"); - m_finalFuncp->addStmtsp(callp); - } + // Link to global function + if (nodep->formCallTree()) { + UINFO(4, " formCallTree "<fileline(), nodep); + callp->argTypes("vlSymsp"); + m_finalFuncp->addStmtsp(callp); + } } virtual void visit(AstSenTree* nodep) { - // Delete it later; Actives still pointing to it - nodep->unlinkFrBack(); - pushDeletep(nodep); + // Delete it later; Actives still pointing to it + nodep->unlinkFrBack(); + pushDeletep(nodep); } void addToEvalLoop(AstNode* stmtsp) { m_evalFuncp->addStmtsp(stmtsp); // add to top level function @@ -336,13 +341,13 @@ private: m_initFuncp->addStmtsp(stmtsp); // add to top level function } virtual void visit(AstActive* nodep) { - // Careful if adding variables here, ACTIVES can be under other ACTIVES - // Need to save and restore any member state in AstUntilStable block - if (!m_topScopep || !nodep->stmtsp()) { - // Not at the top or empty block... - // Only empty blocks should be leftover on the non-top. Killem. - if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active"); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // Careful if adding variables here, ACTIVES can be under other ACTIVES + // Need to save and restore any member state in AstUntilStable block + if (!m_topScopep || !nodep->stmtsp()) { + // Not at the top or empty block... + // Only empty blocks should be leftover on the non-top. Killem. + if (nodep->stmtsp()) nodep->v3fatalSrc("Non-empty lower active"); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } else if (m_mtaskBodyp) { UINFO(4," TR ACTIVE "<stmtsp()->unlinkFrBackWithNext(); @@ -367,39 +372,39 @@ private: m_mtaskBodyp->addStmtsp(stmtsp); } nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } else { - UINFO(4," ACTIVE "<stmtsp()->unlinkFrBackWithNext(); - if (nodep->hasClocked()) { - // Remember the latest sensitivity so we can compare it next time - if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity"); - if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) { - UINFO(4," sameSenseTree\n"); - } else { - clearLastSen(); - m_lastSenp = nodep->sensesp(); - // Make a new if statement - m_lastIfp = makeActiveIf(m_lastSenp); - addToEvalLoop(m_lastIfp); - } - // Move statements to if - m_lastIfp->addIfsp(stmtsp); - } else if (nodep->hasInitial()) { - // Don't need to: clearLastSen();, as we're adding it to different cfunc - // Move statements to function - addToInitial(stmtsp); - } else if (nodep->hasSettle()) { - // Don't need to: clearLastSen();, as we're adding it to different cfunc - // Move statements to function - addToSettleLoop(stmtsp); - } else { - // Combo - clearLastSen(); - // Move statements to function - addToEvalLoop(stmtsp); - } - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } + } else { + UINFO(4," ACTIVE "<stmtsp()->unlinkFrBackWithNext(); + if (nodep->hasClocked()) { + // Remember the latest sensitivity so we can compare it next time + if (nodep->hasInitial()) nodep->v3fatalSrc("Initial block should not have clock sensitivity"); + if (m_lastSenp && nodep->sensesp()->sameTree(m_lastSenp)) { + UINFO(4," sameSenseTree\n"); + } else { + clearLastSen(); + m_lastSenp = nodep->sensesp(); + // Make a new if statement + m_lastIfp = makeActiveIf(m_lastSenp); + addToEvalLoop(m_lastIfp); + } + // Move statements to if + m_lastIfp->addIfsp(stmtsp); + } else if (nodep->hasInitial()) { + // Don't need to: clearLastSen();, as we're adding it to different cfunc + // Move statements to function + addToInitial(stmtsp); + } else if (nodep->hasSettle()) { + // Don't need to: clearLastSen();, as we're adding it to different cfunc + // Move statements to function + addToSettleLoop(stmtsp); + } else { + // Combo + clearLastSen(); + // Move statements to function + addToEvalLoop(stmtsp); + } + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } } virtual void visit(AstExecGraph* nodep) { for (m_mtaskBodyp = VN_CAST(nodep->op1p(), MTaskBody); @@ -426,16 +431,16 @@ public: // CONSTUCTORS explicit ClockVisitor(AstNetlist* nodep) { m_modp = NULL; - m_evalFuncp = NULL; + m_evalFuncp = NULL; m_initFuncp = NULL; m_finalFuncp = NULL; m_settleFuncp = NULL; m_topScopep = NULL; m_lastSenp = NULL; - m_lastIfp = NULL; - m_scopep = NULL; + m_lastIfp = NULL; + m_scopep = NULL; m_mtaskBodyp = NULL; - // + // iterate(nodep); // Allow downstream modules to find _eval() // easily without iterating through the tree. diff --git a/src/V3Clock.h b/src/V3Clock.h index 453eb2f9b..91445e139 100644 --- a/src/V3Clock.h +++ b/src/V3Clock.h @@ -34,4 +34,4 @@ public: static void clockAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Combine.cpp b/src/V3Combine.cpp index 39e9fbdf3..6fd1f1b2c 100644 --- a/src/V3Combine.cpp +++ b/src/V3Combine.cpp @@ -19,18 +19,18 @@ //************************************************************************* // V3Combine's Transformations: // -// For every function that we spit out -// Examine code to find largest common blocks -// Hash each node depth first -// Hash includes varp name and operator type, and constants -// Form lookup table based on hash of each statement w/ nodep and next nodep -// GO through table -// Lookup in hash, while next of each statement match, grow that common block -// Foreach common block -// If common block large enough (> 20 statements) & used 2x or more -// Make new function -// Move common block to function -// Replace each common block ref with funccall +// For every function that we spit out +// Examine code to find largest common blocks +// Hash each node depth first +// Hash includes varp name and operator type, and constants +// Form lookup table based on hash of each statement w/ nodep and next nodep +// GO through table +// Lookup in hash, while next of each statement match, grow that common block +// Foreach common block +// If common block large enough (> 20 statements) & used 2x or more +// Make new function +// Move common block to function +// Replace each common block ref with funccall // //************************************************************************* @@ -50,7 +50,7 @@ //###################################################################### -#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function +#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function //###################################################################### @@ -79,57 +79,59 @@ class CombCallVisitor : CombBaseVisitor { // Find all CCALLS of each CFUNC, so that we can later rename them private: // NODE STATE - bool m_find; // Find mode vs. delete mode + bool m_find; // Find mode vs. delete mode typedef std::multimap CallMmap; - CallMmap m_callMmap; // Associative array of {function}{call} + CallMmap m_callMmap; // Associative array of {function}{call} // METHODS public: void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) { - if (oldfuncp==newfuncp) return; - if (newfuncp) { - UINFO(4, " Replace "< "< eqrange = m_callMmap.equal_range(oldfuncp); - for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) { - CallMmap::iterator eqit = nextit++; - AstCCall* callp = eqit->second; - if (!callp->user3()) { // !already done - UINFO(4, " Called "<funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func"); - if (newfuncp) { - AstCCall* newp = new AstCCall(callp, newfuncp); - // Special new AstCCall form above transfers children of callp to newfuncp - callp->replaceWith(newp); - addCall(newp); // Fix the table - } else { // Just deleting empty function - callp->unlinkFrBack(); - } - callp->user3(true); // Dead now - pushDeletep(callp); VL_DANGLING(callp); - m_callMmap.erase(eqit); // Fix the table - } - } + if (oldfuncp==newfuncp) return; + if (newfuncp) { + UINFO(4, " Replace "< "< eqrange + = m_callMmap.equal_range(oldfuncp); + for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) { + CallMmap::iterator eqit = nextit++; + AstCCall* callp = eqit->second; + if (!callp->user3()) { // !already done + UINFO(4, " Called "<funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func"); + if (newfuncp) { + AstCCall* newp = new AstCCall(callp, newfuncp); + // Special new AstCCall form above transfers children of callp to newfuncp + callp->replaceWith(newp); + addCall(newp); // Fix the table + } else { // Just deleting empty function + callp->unlinkFrBack(); + } + callp->user3(true); // Dead now + pushDeletep(callp); VL_DANGLING(callp); + m_callMmap.erase(eqit); // Fix the table + } + } } // METHODS void addCall(AstCCall* nodep) { - m_callMmap.insert(make_pair(nodep->funcp(), nodep)); + m_callMmap.insert(make_pair(nodep->funcp(), nodep)); } void deleteCall(AstCCall* nodep) { - std::pair eqrange = m_callMmap.equal_range(nodep->funcp()); - for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) { - CallMmap::iterator eqit = nextit++; - AstCCall* callp = eqit->second; - if (callp==nodep) { - m_callMmap.erase(eqit); - return; - } - } - nodep->v3fatalSrc("deleteCall node not found in table"); + std::pair eqrange + = m_callMmap.equal_range(nodep->funcp()); + for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) { + CallMmap::iterator eqit = nextit++; + AstCCall* callp = eqit->second; + if (callp==nodep) { + m_callMmap.erase(eqit); + return; + } + } + nodep->v3fatalSrc("deleteCall node not found in table"); } private: // VISITORS virtual void visit(AstCCall* nodep) { - addCall(nodep); + addCall(nodep); } // Speed things up virtual void visit(AstNodeAssign* nodep) {} @@ -140,7 +142,7 @@ private: public: // CONSTRUCTORS CombCallVisitor() { - m_find = false; + m_find = false; } virtual ~CombCallVisitor() {} void main(AstNetlist* nodep) { @@ -155,10 +157,10 @@ class CombMarkVisitor : CombBaseVisitor { // Mark all nodes under specified one. private: // OUTPUT: - // AstNode::user3() -> bool. True to indicate duplicated + // AstNode::user3() -> bool. True to indicate duplicated // VISITORS virtual void visit(AstNode* nodep) { - nodep->user3(true); + nodep->user3(true); iterateChildren(nodep); } public: @@ -176,273 +178,276 @@ class CombineVisitor : CombBaseVisitor { private: // NODE STATE // Entire netlist: - // AstNodeStmt::user() -> bool. True if iterated already - // AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func - // AstNodeStmt::user3() -> AstNode*. True if to ignore this cell - // AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal) - AstUser1InUse m_inuser1; - AstUser3InUse m_inuser3; - //AstUser4InUse part of V3Hashed + // AstNodeStmt::user() -> bool. True if iterated already + // AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func + // AstNodeStmt::user3() -> AstNode*. True if to ignore this cell + // AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is illegal) + AstUser1InUse m_inuser1; + AstUser3InUse m_inuser3; + //AstUser4InUse part of V3Hashed // STATE typedef enum {STATE_IDLE, STATE_HASH, STATE_DUP} CombineState; - V3Double0 m_statCombs; // Statistic tracking - CombineState m_state; // Major state - AstNodeModule* m_modp; // Current module - AstCFunc* m_funcp; // Current function - V3Hash m_lowerHash; // Hash of the statement we're building - CombCallVisitor m_call; // Tracking of function call users - int m_modNFuncs; // Number of functions made - AstNode* m_walkLast1p; // Final node that is the same in duplicate list - AstNode* m_walkLast2p; // Final node that is the same in duplicate list - V3Hashed m_hashed; // Hash for every node in module + V3Double0 m_statCombs; // Statistic tracking + CombineState m_state; // Major state + AstNodeModule* m_modp; // Current module + AstCFunc* m_funcp; // Current function + V3Hash m_lowerHash; // Hash of the statement we're building + CombCallVisitor m_call; // Tracking of function call users + int m_modNFuncs; // Number of functions made + AstNode* m_walkLast1p; // Final node that is the same in duplicate list + AstNode* m_walkLast2p; // Final node that is the same in duplicate list + V3Hashed m_hashed; // Hash for every node in module // METHODS void hashStatement(AstNode* nodep) { - // Compute hash on entire tree of this statement - m_hashed.hashAndInsert(nodep); - //UINFO(9," stmthash "<user4()<<" "<user4()<<" "<second; + for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) { + AstNode* node1p = it->second; AstCFunc* oldfuncp = VN_CAST(node1p, CFunc); - if (oldfuncp - && oldfuncp->emptyBody() - && !oldfuncp->dontCombine()) { - UINFO(5," EmptyFunc "<user4p())<<" "<unlinkFrBack(); - pushDeletep(oldfuncp); VL_DANGLING(oldfuncp); - } - } + if (oldfuncp + && oldfuncp->emptyBody() + && !oldfuncp->dontCombine()) { + UINFO(5," EmptyFunc "<user4p()) + <<" "<unlinkFrBack(); + pushDeletep(oldfuncp); VL_DANGLING(oldfuncp); + } + } } void walkDupFuncs() { - for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) { - V3Hash hashval = it->first; - AstNode* node1p = it->second; + for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) { + V3Hash hashval = it->first; + AstNode* node1p = it->second; if (!VN_IS(node1p, CFunc)) continue; - if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes"); - for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) { - AstNode* node2p = eqit->second; - if (!(eqit->first == hashval)) break; - if (node1p==node2p) continue; // Identical iterator - if (node1p->user3p() || node2p->user3p()) continue; // Already merged - if (node1p->sameTree(node2p)) { // walk of tree has same comparison - // Replace AstCCall's that point here + if (hashval.isIllegal()) node1p->v3fatalSrc("Illegal (unhashed) nodes"); + for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) { + AstNode* node2p = eqit->second; + if (!(eqit->first == hashval)) break; + if (node1p==node2p) continue; // Identical iterator + if (node1p->user3p() || node2p->user3p()) continue; // Already merged + if (node1p->sameTree(node2p)) { // walk of tree has same comparison + // Replace AstCCall's that point here replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc)); - // Replacement may promote a slow routine to fast path + // Replacement may promote a slow routine to fast path if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false); - } - } - } + } + } + } } void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) { UINFO(5," DupFunc "<user4p())<<" "<user4p())<<" "<unlinkFrBack(); - pushDeletep(oldfuncp); VL_DANGLING(oldfuncp); + // Mark user3p on entire old tree, so we don't process it more + ++m_statCombs; + CombMarkVisitor visitor(oldfuncp); + m_call.replaceFunc(oldfuncp, newfuncp); + oldfuncp->unlinkFrBack(); + pushDeletep(oldfuncp); VL_DANGLING(oldfuncp); } void replaceOnlyCallFunc(AstCCall* nodep) { if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) { - //oldfuncp->dumpTree(cout,"MAYDEL: "); - if (nodep->nextp()==NULL - && oldfuncp->initsp()==NULL - && oldfuncp->stmtsp()==nodep - && oldfuncp->finalsp()==NULL) { - UINFO(9," Function only has call "<funcp()); VL_DANGLING(nodep); - } - } + //oldfuncp->dumpTree(cout, "MAYDEL: "); + if (nodep->nextp()==NULL + && oldfuncp->initsp()==NULL + && oldfuncp->stmtsp()==nodep + && oldfuncp->finalsp()==NULL) { + UINFO(9," Function only has call "<funcp()); VL_DANGLING(nodep); + } + } } void walkDupCodeStart(AstNode* node1p) { V3Hash hashval(node1p->user4p()); - //UINFO(4," STMT "< eqrange = m_hashed.mmap().equal_range(hashval); - for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) { - AstNode* node2p = eqit->second; - if (node1p==node2p) continue; - // - // We need to mark iteration to prevent matching code inside code (abab matching in ababab) - AstNode::user1ClearTree(); // user1p() used on entire tree - m_walkLast1p = NULL; - m_walkLast2p = NULL; - int depth = walkDupCodeNext(node1p, node2p, 1); - if (depth>COMBINE_MIN_STATEMENTS - && depth>bestDepth) { - bestDepth = depth; - bestNode2p = node2p; - bestLast1p = m_walkLast1p; - bestLast2p = m_walkLast2p; - } - } - if (bestDepth) { - // Found a replacement - UINFO(5," Duplicate of depth "< eqrange + = m_hashed.mmap().equal_range(hashval); + for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) { + AstNode* node2p = eqit->second; + if (node1p==node2p) continue; + // + // We need to mark iteration to prevent matching code inside + // code (abab matching in ababab) + AstNode::user1ClearTree(); // user1p() used on entire tree + m_walkLast1p = NULL; + m_walkLast2p = NULL; + int depth = walkDupCodeNext(node1p, node2p, 1); + if (depth>COMBINE_MIN_STATEMENTS + && depth>bestDepth) { + bestDepth = depth; + bestNode2p = node2p; + bestLast1p = m_walkLast1p; + bestLast2p = m_walkLast2p; + } + } + if (bestDepth) { + // Found a replacement + UINFO(5," Duplicate of depth "<user1p() || node2p->user1p()) return 0; // Already iterated - if (node1p->user3p() || node2p->user3p()) return 0; // Already merged - if (!m_hashed.sameNodes(node1p,node2p)) return 0; // walk of tree has same comparison - V3Hash hashval(node1p->user4p()); - //UINFO(9," wdup1 "<user4p())<<" "<user4p())<<" "<user1(true); - node2p->user1(true); - if (node1p->nextp() && node2p->nextp()) { - return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1); - } - return hashval.depth(); + // Find number of common statements between the two node1p_nextp's... + if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated + if (node1p->user3p() || node2p->user3p()) return 0; // Already merged + if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison + V3Hash hashval(node1p->user4p()); + //UINFO(9," wdup1 "<user4p())<<" "<user4p())<<" "<user1(true); + node2p->user1(true); + if (node1p->nextp() && node2p->nextp()) { + return hashval.depth()+walkDupCodeNext(node1p->nextp(), node2p->nextp(), level+1); + } + return hashval.depth(); } void walkReplace(AstNode* node1p, AstNode* node2p, - AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed - // Make new function - string oldname = m_funcp->name(); - string::size_type pos; - if ((pos=oldname.find("_common")) != string::npos) { - oldname.erase(pos); - } - if ((pos=oldname.find("__")) != string::npos) { - oldname.erase(pos); - } - AstCFunc* newfuncp = new AstCFunc(node1p->fileline(), - oldname+"_common"+cvtToStr(++m_modNFuncs), - NULL); - m_modp->addStmtp(newfuncp); - // Create calls - AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp); - AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp); - // Grab statement bodies - AstNRelinker relink1Handle; - AstNRelinker relink2Handle; - for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) { - nextp = walkp->nextp(); - if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle); - else { walkp->unlinkFrBack(); node1p->addNext(walkp); } - if (walkp==last1p) break; - } - for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) { - nextp = walkp->nextp(); - if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle); - else { walkp->unlinkFrBack(); node2p->addNext(walkp); } - if (walkp==last2p) break; - } - // Move node1 statements to new function - newfuncp->addStmtsp(node1p); - //newfuncp->dumpTree(cout," newfunctree: "); - // Mark node2 statements as dead - CombMarkVisitor visitor(node2p); - pushDeletep(node2p); // Delete later - // Link in new function - relink1Handle.relink(call1p); - relink2Handle.relink(call2p); - // Hash the new function - hashFunctions(newfuncp); - m_call.addCall(call1p); - m_call.addCall(call2p); - // If either new statement makes a func with only a single call, replace - // the above callers to call it directly - replaceOnlyCallFunc(call1p); VL_DANGLING(call1p); - replaceOnlyCallFunc(call2p); VL_DANGLING(call2p); + AstNode* last1p, AstNode* last2p) { // Final node in linked list, maybe null if all statements to be grabbed + // Make new function + string oldname = m_funcp->name(); + string::size_type pos; + if ((pos = oldname.find("_common")) != string::npos) { + oldname.erase(pos); + } + if ((pos = oldname.find("__")) != string::npos) { + oldname.erase(pos); + } + AstCFunc* newfuncp = new AstCFunc(node1p->fileline(), + oldname+"_common"+cvtToStr(++m_modNFuncs), + NULL); + m_modp->addStmtp(newfuncp); + // Create calls + AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp); + AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp); + // Grab statement bodies + AstNRelinker relink1Handle; + AstNRelinker relink2Handle; + for (AstNode* nextp, *walkp = node1p; 1; walkp = nextp) { + nextp = walkp->nextp(); + if (walkp==node1p) walkp->unlinkFrBack(&relink1Handle); + else { walkp->unlinkFrBack(); node1p->addNext(walkp); } + if (walkp==last1p) break; + } + for (AstNode* nextp, *walkp = node2p; 1; walkp = nextp) { + nextp = walkp->nextp(); + if (walkp==node2p) walkp->unlinkFrBack(&relink2Handle); + else { walkp->unlinkFrBack(); node2p->addNext(walkp); } + if (walkp==last2p) break; + } + // Move node1 statements to new function + newfuncp->addStmtsp(node1p); + //newfuncp->dumpTree(cout, " newfunctree: "); + // Mark node2 statements as dead + CombMarkVisitor visitor(node2p); + pushDeletep(node2p); // Delete later + // Link in new function + relink1Handle.relink(call1p); + relink2Handle.relink(call2p); + // Hash the new function + hashFunctions(newfuncp); + m_call.addCall(call1p); + m_call.addCall(call2p); + // If either new statement makes a func with only a single call, replace + // the above callers to call it directly + replaceOnlyCallFunc(call1p); VL_DANGLING(call1p); + replaceOnlyCallFunc(call2p); VL_DANGLING(call2p); } // VISITORS virtual void visit(AstNetlist* nodep) { - // Track all callers of each function - m_call.main(nodep); - // - //In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree - // Iterate modules backwards, in bottom-up order. - // Required so that a module instantiating another can benefit from collapsing. + // Track all callers of each function + m_call.main(nodep); + // + //In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree + // Iterate modules backwards, in bottom-up order. + // Required so that a module instantiating another can benefit from collapsing. iterateChildrenBackwards(nodep); } virtual void visit(AstNodeModule* nodep) { - UINFO(4," MOD "<=9) { - m_hashed.dumpFilePrefixed("combine"); - } - // Walk the hashes removing empty functions - if (emptyFunctionDeletion()) { - walkEmptyFuncs(); - } - // Walk the hashes looking for duplicate functions - if (duplicateFunctionCombine()) { - walkDupFuncs(); - } - // Walk the statements looking for large replicated code sections - if (statementCombine()) { - m_state = STATE_DUP; + m_state = STATE_IDLE; + if (debug()>=9) { + m_hashed.dumpFilePrefixed("combine"); + } + // Walk the hashes removing empty functions + if (emptyFunctionDeletion()) { + walkEmptyFuncs(); + } + // Walk the hashes looking for duplicate functions + if (duplicateFunctionCombine()) { + walkDupFuncs(); + } + // Walk the statements looking for large replicated code sections + if (statementCombine()) { + m_state = STATE_DUP; iterateChildren(nodep); - m_state = STATE_IDLE; - } - m_modp = NULL; + m_state = STATE_IDLE; + } + m_modp = NULL; } virtual void visit(AstCFunc* nodep) { - m_funcp = nodep; - if (!nodep->dontCombine()) { - if (m_state == STATE_HASH) { - hashStatement(nodep); // Hash the entire function - it might be identical - } else if (m_state == STATE_DUP) { + m_funcp = nodep; + if (!nodep->dontCombine()) { + if (m_state == STATE_HASH) { + hashStatement(nodep); // Hash the entire function - it might be identical + } else if (m_state == STATE_DUP) { iterateChildren(nodep); - } - } - m_funcp = NULL; + } + } + m_funcp = NULL; } virtual void visit(AstNodeStmt* nodep) { if (!nodep->isStatement()) { iterateChildren(nodep); return; } - if (m_state == STATE_HASH && m_funcp) { - hashStatement(nodep); - } - else if (m_state == STATE_DUP && m_funcp) { - walkDupCodeStart(nodep); - } + if (m_state == STATE_HASH && m_funcp) { + hashStatement(nodep); + } + else if (m_state == STATE_DUP && m_funcp) { + walkDupCodeStart(nodep); + } } //-------------------- @@ -457,7 +462,7 @@ private: public: // CONSTUCTORS explicit CombineVisitor(AstNetlist* nodep) { - m_state = STATE_IDLE; + m_state = STATE_IDLE; m_modp = NULL; m_funcp = NULL; m_modNFuncs = 0; @@ -466,7 +471,7 @@ public: iterate(nodep); } virtual ~CombineVisitor() { - V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs); + V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs); } }; diff --git a/src/V3Combine.h b/src/V3Combine.h index d18ce21c0..ae907bcc6 100644 --- a/src/V3Combine.h +++ b/src/V3Combine.h @@ -34,4 +34,4 @@ public: static void combineAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Config.cpp b/src/V3Config.cpp index 829d7599f..b46d0458b 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -33,110 +33,113 @@ class V3ConfigLine { public: - int m_lineno; // Line number to make change at - V3ErrorCode m_code; // Error code - bool m_on; // True to enaable message + int m_lineno; // Line number to make change at + V3ErrorCode m_code; // Error code + bool m_on; // True to enaable message V3ConfigLine(V3ErrorCode code, int lineno, bool on) - : m_lineno(lineno), m_code(code), m_on(on) {} + : m_lineno(lineno), m_code(code), m_on(on) {} ~V3ConfigLine() {} inline bool operator< (const V3ConfigLine& rh) const { - if (m_linenorh.m_lineno) return false; - if (m_coderh.m_code) return false; - // Always turn "on" before "off" so that overlapping lines will end up finally with the error "off" - return (m_on>rh.m_on); + if (m_linenorh.m_lineno) return false; + if (m_coderh.m_code) return false; + // Always turn "on" before "off" so that overlapping lines will end + // up finally with the error "off" + return (m_on>rh.m_on); } }; -std::ostream& operator<<(std::ostream& os, V3ConfigLine rhs) { return os< IgnLines; // list of {line,code,on} typedef std::map IgnFiles; // {filename} => list of {line,code,on} // MEMBERS - string m_lastFilename; // Last filename looked up - int m_lastLineno; // Last linenumber looked up + string m_lastFilename; // Last filename looked up + int m_lastLineno; // Last linenumber looked up - IgnLines::const_iterator m_lastIt; // Point with next linenumber > current line number - IgnLines::const_iterator m_lastEnd; // Point with end() + IgnLines::const_iterator m_lastIt; // Point with next linenumber > current line number + IgnLines::const_iterator m_lastEnd; // Point with end() - IgnFiles m_ignWilds; // Ignores for each wildcarded filename - IgnFiles m_ignFiles; // Ignores for each non-wildcarded filename + IgnFiles m_ignWilds; // Ignores for each wildcarded filename + IgnFiles m_ignFiles; // Ignores for each non-wildcarded filename - static V3ConfigIgnores s_singleton; // Singleton (not via local static, as that's slow) + static V3ConfigIgnores s_singleton; // Singleton (not via local static, as that's slow) V3ConfigIgnores() { m_lastLineno = -1; } ~V3ConfigIgnores() {} // METHODS inline IgnLines* findWilds(const string& wildname) { - IgnFiles::iterator it = m_ignWilds.find(wildname); - if (it != m_ignWilds.end()) { - return &(it->second); - } else { - m_ignWilds.insert(make_pair(wildname, IgnLines())); - it = m_ignWilds.find(wildname); - return &(it->second); - } + IgnFiles::iterator it = m_ignWilds.find(wildname); + if (it != m_ignWilds.end()) { + return &(it->second); + } else { + m_ignWilds.insert(make_pair(wildname, IgnLines())); + it = m_ignWilds.find(wildname); + return &(it->second); + } } inline void absBuild(const string& filename) { - // Given a filename, find all wildcard matches against it and build - // hash with the specific filename. This avoids having to wildmatch - // more than once against any filename. - IgnFiles::iterator it = m_ignFiles.find(filename); - if (it == m_ignFiles.end()) { - // Haven't seen this filename before - m_ignFiles.insert(make_pair(filename, IgnLines())); - it = m_ignFiles.find(filename); - // Make new list for this file of all matches - for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) { - if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) { - for (IgnLines::iterator lit = fnit->second.begin(); lit != fnit->second.end(); ++lit) { - it->second.insert(*lit); - } - } - } - } - m_lastIt = it->second.begin(); - m_lastEnd = it->second.end(); + // Given a filename, find all wildcard matches against it and build + // hash with the specific filename. This avoids having to wildmatch + // more than once against any filename. + IgnFiles::iterator it = m_ignFiles.find(filename); + if (it == m_ignFiles.end()) { + // Haven't seen this filename before + m_ignFiles.insert(make_pair(filename, IgnLines())); + it = m_ignFiles.find(filename); + // Make new list for this file of all matches + for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) { + if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) { + for (IgnLines::iterator lit = fnit->second.begin(); + lit != fnit->second.end(); ++lit) { + it->second.insert(*lit); + } + } + } + } + m_lastIt = it->second.begin(); + m_lastEnd = it->second.end(); } public: inline static V3ConfigIgnores& singleton() { return s_singleton; } void addIgnore(V3ErrorCode code, const string& wildname, int lineno, bool on) { - // Insert - IgnLines* linesp = findWilds(wildname); - UINFO(9,"config addIgnore "<insert(V3ConfigLine(code, lineno, on)); - // Flush the match cache, due to a change in the rules. - m_ignFiles.clear(); - m_lastFilename = " "; + // Insert + IgnLines* linesp = findWilds(wildname); + UINFO(9,"config addIgnore "<insert(V3ConfigLine(code, lineno, on)); + // Flush the match cache, due to a change in the rules. + m_ignFiles.clear(); + m_lastFilename = " "; } inline void applyIgnores(FileLine* filelinep) { - // HOT routine, called each parsed token line - if (m_lastLineno != filelinep->lineno() - || m_lastFilename != filelinep->filename()) { - //UINFO(9," ApplyIgnores for "<ascii()<filename())) { - absBuild(filelinep->filename()); - m_lastFilename = filelinep->filename(); - } - // Process all on/offs for lines up to and including the current line - int curlineno = filelinep->lineno(); - for (; m_lastIt != m_lastEnd; ++m_lastIt) { - if (m_lastIt->m_lineno > curlineno) break; - //UINFO(9," Hit "<<*m_lastIt<warnOn(m_lastIt->m_code, m_lastIt->m_on); - } - if (0 && debug() >= 9) { - for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) { - UINFO(9," NXT "<<*it<lineno(); - } + // HOT routine, called each parsed token line + if (m_lastLineno != filelinep->lineno() + || m_lastFilename != filelinep->filename()) { + //UINFO(9," ApplyIgnores for "<ascii()<filename())) { + absBuild(filelinep->filename()); + m_lastFilename = filelinep->filename(); + } + // Process all on/offs for lines up to and including the current line + int curlineno = filelinep->lineno(); + for (; m_lastIt != m_lastEnd; ++m_lastIt) { + if (m_lastIt->m_lineno > curlineno) break; + //UINFO(9," Hit "<<*m_lastIt<warnOn(m_lastIt->m_code, m_lastIt->m_on); + } + if (0 && debug() >= 9) { + for (IgnLines::const_iterator it=m_lastIt; it != m_lastEnd; ++it) { + UINFO(9," NXT "<<*it<lineno(); + } } }; @@ -147,10 +150,10 @@ V3ConfigIgnores V3ConfigIgnores::s_singleton; void V3Config::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) { if (filename=="*") { - FileLine::globalWarnOff(code,!on); + FileLine::globalWarnOff(code,!on); } else { - V3ConfigIgnores::singleton().addIgnore(code, filename, min, on); - if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on); + V3ConfigIgnores::singleton().addIgnore(code, filename, min, on); + if (max) V3ConfigIgnores::singleton().addIgnore(code, filename, max, !on); } } diff --git a/src/V3Config.h b/src/V3Config.h index 7a030641b..f1641d4f8 100644 --- a/src/V3Config.h +++ b/src/V3Config.h @@ -35,4 +35,4 @@ public: static void applyIgnores(FileLine* filelinep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 64e35d35e..5ed24f1e4 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -19,9 +19,9 @@ //************************************************************************* // CONST TRANSFORMATIONS: // Call on one node for PARAM values, or netlist for overall constant folding: -// Bottom up traversal: -// Attempt to convert operands to constants -// If operands are constant, replace this node with constant. +// Bottom up traversal: +// Attempt to convert operands to constants +// If operands are constant, replace this node with constant. //************************************************************************* #include "config_build.h" @@ -43,11 +43,11 @@ class ConstVarMarkVisitor : public AstNVisitor { // NODE STATE - // AstVar::user4p -> bool, Var marked, 0=not set yet + // AstVar::user4p -> bool, Var marked, 0=not set yet private: // VISITORS virtual void visit(AstVarRef* nodep) { - if (nodep->varp()) nodep->varp()->user4(1); + if (nodep->varp()) nodep->varp()->user4(1); } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -55,7 +55,7 @@ private: public: // CONSTUCTORS explicit ConstVarMarkVisitor(AstNode* nodep) { - AstNode::user4ClearTree(); // Check marked InUse before we're called + AstNode::user4ClearTree(); // Check marked InUse before we're called iterate(nodep); } virtual ~ConstVarMarkVisitor() {} @@ -63,13 +63,13 @@ public: class ConstVarFindVisitor : public AstNVisitor { // NODE STATE - // AstVar::user4p -> bool, input from ConstVarMarkVisitor + // AstVar::user4p -> bool, input from ConstVarMarkVisitor // MEMBERS bool m_found; private: // VISITORS virtual void visit(AstVarRef* nodep) { - if (nodep->varp() && nodep->varp()->user4()) m_found = true; + if (nodep->varp() && nodep->varp()->user4()) m_found = true; } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -77,7 +77,7 @@ private: public: // CONSTUCTORS explicit ConstVarFindVisitor(AstNode* nodep) { - m_found = false; + m_found = false; iterateAndNextNull(nodep); } virtual ~ConstVarFindVisitor() {} @@ -93,24 +93,24 @@ private: // NODE STATE // ** 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 ConstVarMarkVisitor/ConstVarFindVisitor - // AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label + // AstVar::user4p -> Used by ConstVarMarkVisitor/ConstVarFindVisitor + // AstJumpLabel::user4 -> bool. Set when AstJumpGo uses this label // STATE - bool m_params; // If true, propogate parameterized and true numbers only - bool m_required; // If true, must become a constant - bool m_wremove; // Inside scope, no assignw removal - bool m_warn; // Output warnings - bool m_doExpensive; // Enable computationally expensive optimizations - bool m_doNConst; // Enable non-constant-child simplifications - bool m_doShort; // Remove expressions that short circuit - bool m_doV; // Verilog, not C++ conversion - bool m_doGenerate; // Postpone width checking inside generate + bool m_params; // If true, propogate parameterized and true numbers only + bool m_required; // If true, must become a constant + bool m_wremove; // Inside scope, no assignw removal + bool m_warn; // Output warnings + bool m_doExpensive; // Enable computationally expensive optimizations + bool m_doNConst; // Enable non-constant-child simplifications + bool m_doShort; // Remove expressions that short circuit + bool m_doV; // Verilog, not C++ conversion + bool m_doGenerate; // Postpone width checking inside generate bool m_hasJumpGo; // JumpGo under this while - AstNodeModule* m_modp; // Current module - AstArraySel* m_selp; // Current select - AstNode* m_scopep; // Current scope - AstAttrOf* m_attrp; // Current attribute + AstNodeModule* m_modp; // Current module + AstArraySel* m_selp; // Current select + AstNode* m_scopep; // Current scope + AstAttrOf* m_attrp; // Current attribute // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -119,144 +119,144 @@ private: return VN_IS(nodep, Const); } bool operandAsvConst(const AstNode* nodep) { - // BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...) + // BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...) const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv); - if (!bnodep) return false; + if (!bnodep) return false; if (!VN_IS(bnodep->lhsp(), Const)) return false; const AstNodeBiComAsv* rnodep = VN_CAST_CONST(bnodep->rhsp(), NodeBiComAsv); - if (!rnodep) return false; - if (rnodep->type() != bnodep->type()) return false; - if (rnodep->width() != bnodep->width()) return false; - if (rnodep->lhsp()->width() != bnodep->lhsp()->width()) return false; + if (!rnodep) return false; + if (rnodep->type() != bnodep->type()) return false; + if (rnodep->width() != bnodep->width()) return false; + if (rnodep->lhsp()->width() != bnodep->lhsp()->width()) return false; if (!VN_IS(rnodep->lhsp(), Const)) return false; - return true; + return true; } bool operandAsvSame(const AstNode* nodep) { - // BIASV(SAMEa, BIASV(SAMEb,...)) -> BIASV( BIASV(SAMEa,SAMEb), ...) + // BIASV(SAMEa, BIASV(SAMEb,...)) -> BIASV( BIASV(SAMEa,SAMEb), ...) const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv); - if (!bnodep) return false; + if (!bnodep) return false; const AstNodeBiComAsv* rnodep = VN_CAST_CONST(bnodep->rhsp(), NodeBiComAsv); - if (!rnodep) return false; - if (rnodep->type() != bnodep->type()) return false; - if (rnodep->width() != bnodep->width()) return false; - return operandsSame(bnodep->lhsp(), rnodep->lhsp()); + if (!rnodep) return false; + if (rnodep->type() != bnodep->type()) return false; + if (rnodep->width() != bnodep->width()) return false; + return operandsSame(bnodep->lhsp(), rnodep->lhsp()); } bool operandAsvLUp(const AstNode* nodep) { // BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r)) ? - // - // Example of how this is useful: - // BIASV(BIASV(CONSTa,b...),BIASV(CONSTc,d...)) // hits operandAsvUp - // BIASV(CONSTa,BIASV(b...,BIASV(CONSTc,d...))) // hits operandAsvUp - // BIASV(CONSTa,BIASV(CONSTc,BIASV(c...,d...))) // hits operandAsvConst - // BIASV(BIASV(CONSTa,CONSTc),BIASV(c...,d...))) // hits normal constant propagation - // BIASV(CONST_a_c,BIASV(c...,d...))) - // - // Idea for the future: All BiComAsvs could be lists, sorted by if they're constant + // + // Example of how this is useful: + // BIASV(BIASV(CONSTa,b...),BIASV(CONSTc,d...)) // hits operandAsvUp + // BIASV(CONSTa,BIASV(b...,BIASV(CONSTc,d...))) // hits operandAsvUp + // BIASV(CONSTa,BIASV(CONSTc,BIASV(c...,d...))) // hits operandAsvConst + // BIASV(BIASV(CONSTa,CONSTc),BIASV(c...,d...))) // hits normal constant propagation + // BIASV(CONST_a_c,BIASV(c...,d...))) + // + // Idea for the future: All BiComAsvs could be lists, sorted by if they're constant const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv); - if (!bnodep) return false; + if (!bnodep) return false; const AstNodeBiComAsv* lnodep = VN_CAST_CONST(bnodep->lhsp(), NodeBiComAsv); - if (!lnodep) return false; - if (lnodep->type() != bnodep->type()) return false; - if (lnodep->width() != bnodep->width()) return false; + if (!lnodep) return false; + if (lnodep->type() != bnodep->type()) return false; + if (lnodep->width() != bnodep->width()) return false; return VN_IS(lnodep->lhsp(), Const); } bool operandAsvRUp(const AstNode* nodep) { // BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr)) ? const AstNodeBiComAsv* bnodep = VN_CAST_CONST(nodep, NodeBiComAsv); - if (!bnodep) return false; + if (!bnodep) return false; const AstNodeBiComAsv* rnodep = VN_CAST_CONST(bnodep->rhsp(), NodeBiComAsv); - if (!rnodep) return false; - if (rnodep->type() != bnodep->type()) return false; - if (rnodep->width() != bnodep->width()) return false; + if (!rnodep) return false; + if (rnodep->type() != bnodep->type()) return false; + if (rnodep->width() != bnodep->width()) return false; return VN_IS(rnodep->lhsp(), Const); } static bool operandSubAdd(const AstNode* nodep) { - // SUB( ADD(CONSTx,y), CONSTz) -> ADD(SUB(CONSTx,CONSTz), y) + // SUB( ADD(CONSTx,y), CONSTz) -> ADD(SUB(CONSTx,CONSTz), y) const AstNodeBiop* np = VN_CAST_CONST(nodep, NodeBiop); const AstNodeBiop* lp = VN_CAST_CONST(np->lhsp(), NodeBiop); - return (lp + return (lp && VN_IS(lp->lhsp(), Const) && VN_IS(np->rhsp(), Const) - && lp->width()==np->width()); + && lp->width()==np->width()); } static bool operandAndOrSame(const AstNode* nodep) { - // OR( AND(VAL,x), AND(VAL,y)) -> AND(VAL,OR(x,y)) - // OR( AND(x,VAL), AND(y,VAL)) -> AND(OR(x,y),VAL) + // OR( AND(VAL,x), AND(VAL,y)) -> AND(VAL,OR(x,y)) + // OR( AND(x,VAL), AND(y,VAL)) -> AND(OR(x,y),VAL) const AstNodeBiop* np = VN_CAST_CONST(nodep, NodeBiop); const AstNodeBiop* lp = VN_CAST_CONST(np->lhsp(), NodeBiop); const AstNodeBiop* rp = VN_CAST_CONST(np->rhsp(), NodeBiop); - return (lp && rp - && lp->width()==rp->width() - && lp->type()==rp->type() - && (operandsSame(lp->lhsp(),rp->lhsp()) - || operandsSame(lp->rhsp(),rp->rhsp()))); + return (lp && rp + && lp->width()==rp->width() + && lp->type()==rp->type() + && (operandsSame(lp->lhsp(), rp->lhsp()) + || operandsSame(lp->rhsp(), rp->rhsp()))); } static bool matchOrAndNot(AstNodeBiop* nodep) { - // AstOr{$a, AstAnd{AstNot{$b}, $c}} if $a.width1, $a==$b => AstOr{$a,$c} - // Someday we'll sort the biops completely and this can be simplified - // This often results from our simplified clock generation: - // if (rst) ... else if (enable)... -> OR(rst,AND(!rst,enable)) - AstNode* ap; - AstNodeBiop* andp; - if (VN_IS(nodep->lhsp(), And)) { andp=VN_CAST(nodep->lhsp(), And); ap=nodep->rhsp(); } - else if (VN_IS(nodep->rhsp(), And)) { andp=VN_CAST(nodep->rhsp(), And); ap=nodep->lhsp(); } - else return false; - AstNodeUniop* notp; - AstNode* cp; - if (VN_IS(andp->lhsp(), Not)) { notp=VN_CAST(andp->lhsp(), Not); cp=andp->rhsp(); } - else if (VN_IS(andp->rhsp(), Not)) { notp=VN_CAST(andp->rhsp(), Not); cp=andp->lhsp(); } - else return false; - AstNode* bp = notp->lhsp(); - if (!operandsSame(ap, bp)) return false; - // Do it - cp->unlinkFrBack(); - andp->unlinkFrBack()->deleteTree(); VL_DANGLING(andp); VL_DANGLING(notp); - // Replace whichever branch is now dangling - if (nodep->rhsp()) nodep->lhsp(cp); - else nodep->rhsp(cp); - return true; + // AstOr{$a, AstAnd{AstNot{$b}, $c}} if $a.width1, $a==$b => AstOr{$a,$c} + // Someday we'll sort the biops completely and this can be simplified + // This often results from our simplified clock generation: + // if (rst) ... else if (enable)... -> OR(rst,AND(!rst,enable)) + AstNode* ap; + AstNodeBiop* andp; + if (VN_IS(nodep->lhsp(), And)) { andp = VN_CAST(nodep->lhsp(), And); ap = nodep->rhsp(); } + else if (VN_IS(nodep->rhsp(), And)) { andp = VN_CAST(nodep->rhsp(), And); ap = nodep->lhsp(); } + else return false; + AstNodeUniop* notp; + AstNode* cp; + if (VN_IS(andp->lhsp(), Not)) { notp = VN_CAST(andp->lhsp(), Not); cp=andp->rhsp(); } + else if (VN_IS(andp->rhsp(), Not)) { notp = VN_CAST(andp->rhsp(), Not); cp=andp->lhsp(); } + else return false; + AstNode* bp = notp->lhsp(); + if (!operandsSame(ap, bp)) return false; + // Do it + cp->unlinkFrBack(); + andp->unlinkFrBack()->deleteTree(); VL_DANGLING(andp); VL_DANGLING(notp); + // Replace whichever branch is now dangling + if (nodep->rhsp()) nodep->lhsp(cp); + else nodep->rhsp(cp); + return true; } static bool operandShiftSame(const AstNode* nodep) { const AstNodeBiop* np = VN_CAST_CONST(nodep, NodeBiop); - { + { const AstShiftL* lp = VN_CAST_CONST(np->lhsp(), ShiftL); const AstShiftL* rp = VN_CAST_CONST(np->rhsp(), ShiftL); - if (lp && rp) { - return (lp->width() == rp->width() - && lp->lhsp()->width() == rp->lhsp()->width() - && operandsSame(lp->rhsp(), rp->rhsp())); - } - } - { + if (lp && rp) { + return (lp->width() == rp->width() + && lp->lhsp()->width() == rp->lhsp()->width() + && operandsSame(lp->rhsp(), rp->rhsp())); + } + } + { const AstShiftR* lp = VN_CAST_CONST(np->lhsp(), ShiftR); const AstShiftR* rp = VN_CAST_CONST(np->rhsp(), ShiftR); - if (lp && rp) { - return (lp->width() == rp->width() - && lp->lhsp()->width() == rp->lhsp()->width() - && operandsSame(lp->rhsp(), rp->rhsp())); - } - } - return false; + if (lp && rp) { + return (lp->width() == rp->width() + && lp->lhsp()->width() == rp->lhsp()->width() + && operandsSame(lp->rhsp(), rp->rhsp())); + } + } + return false; } bool operandHugeShiftL(const AstNodeBiop* nodep) { return (VN_IS(nodep->rhsp(), Const) && !VN_CAST_CONST(nodep->rhsp(), Const)->num().isFourState() && (VN_CAST_CONST(nodep->rhsp(), Const)->toUInt() >= static_cast(nodep->width())) - && isTPure(nodep->lhsp())); + && isTPure(nodep->lhsp())); } bool operandHugeShiftR(const AstNodeBiop* nodep) { return (VN_IS(nodep->rhsp(), Const) && !VN_CAST_CONST(nodep->rhsp(), Const)->num().isFourState() && (VN_CAST_CONST(nodep->rhsp(), Const)->toUInt() >= static_cast(nodep->lhsp()->width())) - && isTPure(nodep->lhsp())); + && isTPure(nodep->lhsp())); } bool operandIsTwo(const AstNode* nodep) { return (VN_IS(nodep, Const) && !VN_CAST_CONST(nodep, Const)->num().isFourState() - && nodep->width() <= VL_QUADSIZE + && nodep->width() <= VL_QUADSIZE && VN_CAST_CONST(nodep, Const)->toUQuad()==2); } bool operandIsTwostate(const AstNode* nodep) { @@ -264,35 +264,35 @@ private: && !VN_CAST_CONST(nodep, Const)->num().isFourState()); } bool operandIsPowTwo(const AstNode* nodep) { - if (!operandIsTwostate(nodep)) return false; + if (!operandIsTwostate(nodep)) return false; return (1==VN_CAST_CONST(nodep, Const)->num().countOnes()); } bool operandShiftOp(const AstNodeBiop* nodep) { if (!VN_IS(nodep->rhsp(), Const)) return false; const AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop); if (!lhsp || !(VN_IS(lhsp, And) || VN_IS(lhsp, Or) || VN_IS(lhsp, Xor))) return false; - if (nodep->width()!=lhsp->width()) return false; - if (nodep->width()!=lhsp->lhsp()->width()) return false; - if (nodep->width()!=lhsp->rhsp()->width()) return false; - return true; + if (nodep->width()!=lhsp->width()) return false; + if (nodep->width()!=lhsp->lhsp()->width()) return false; + if (nodep->width()!=lhsp->rhsp()->width()) return false; + return true; } bool operandShiftShift(const AstNodeBiop* nodep) { - // We could add a AND though. + // We could add a AND though. const AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop); if (!lhsp || !(VN_IS(lhsp, ShiftL) || VN_IS(lhsp, ShiftR))) return false; - // We can only get rid of a<>c or a<>c or a<rhsp(), Const) && VN_IS(lhsp->rhsp(), Const))) return false; if (VN_CAST_CONST(nodep->rhsp(), Const)->num().isFourState() || VN_CAST_CONST(lhsp->rhsp(), Const)->num().isFourState()) return false; - if (nodep->width()!=lhsp->width()) return false; - if (nodep->width()!=lhsp->lhsp()->width()) return false; - return true; + if (nodep->width()!=lhsp->width()) return false; + if (nodep->width()!=lhsp->lhsp()->width()) return false; + return true; } bool operandWordOOB(const AstWordSel* nodep) { - // V3Expand may make a arraysel that exceeds the bounds of the array - // It was an expression, then got constified. In reality, the WordSel - // must be wrapped in a Cond, that will be false. + // V3Expand may make a arraysel that exceeds the bounds of the array + // It was an expression, then got constified. In reality, the WordSel + // must be wrapped in a Cond, that will be false. return (VN_IS(nodep->rhsp(), Const) && VN_IS(nodep->fromp(), NodeVarRef) && !VN_CAST_CONST(nodep->fromp(), NodeVarRef)->lvalue() @@ -302,199 +302,202 @@ private: bool operandSelFull(const AstSel* nodep) { return (VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) - && nodep->lsbConst()==0 + && nodep->lsbConst()==0 && static_cast(nodep->widthConst()) == nodep->fromp()->width()); } bool operandSelExtend(AstSel* nodep) { - // A pattern created by []'s after offsets have been removed - // SEL(EXTEND(any,width,...),(width-1),0) -> ... - // Since select's return unsigned, this is always an extend + // A pattern created by []'s after offsets have been removed + // SEL(EXTEND(any,width,...),(width-1),0) -> ... + // Since select's return unsigned, this is always an extend AstExtend* extendp = VN_CAST(nodep->fromp(), Extend); - if (!(m_doV - && extendp + if (!(m_doV + && extendp && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) - && nodep->lsbConst()==0 + && nodep->lsbConst()==0 && static_cast(nodep->widthConst()) == extendp->lhsp()->width() - )) return false; - replaceWChild(nodep, extendp->lhsp()); VL_DANGLING(nodep); - return true; + )) return false; + replaceWChild(nodep, extendp->lhsp()); VL_DANGLING(nodep); + return true; } bool operandSelBiLower(AstSel* nodep) { - // SEL(ADD(a,b),(width-1),0) -> ADD(SEL(a),SEL(b)) - // Add or any operation which doesn't care if we discard top bits + // SEL(ADD(a,b),(width-1),0) -> ADD(SEL(a),SEL(b)) + // Add or any operation which doesn't care if we discard top bits AstNodeBiop* bip = VN_CAST(nodep->fromp(), NodeBiop); - if (!(m_doV - && bip + if (!(m_doV + && bip && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) - && nodep->lsbConst()==0 - )) return false; - if (debug()>=9) nodep->dumpTree(cout,"SEL(BI)-in:"); - AstNode* bilhsp = bip->lhsp()->unlinkFrBack(); - AstNode* birhsp = bip->rhsp()->unlinkFrBack(); - bip->lhsp(new AstSel(nodep->fileline(), bilhsp, 0, nodep->widthConst())); - bip->rhsp(new AstSel(nodep->fileline(), birhsp, 0, nodep->widthConst())); - if (debug()>=9) bip->dumpTree(cout,"SEL(BI)-ou:"); - replaceWChild(nodep, bip); VL_DANGLING(nodep); - return true; + && nodep->lsbConst()==0 + )) return false; + if (debug()>=9) nodep->dumpTree(cout, "SEL(BI)-in:"); + AstNode* bilhsp = bip->lhsp()->unlinkFrBack(); + AstNode* birhsp = bip->rhsp()->unlinkFrBack(); + bip->lhsp(new AstSel(nodep->fileline(), bilhsp, 0, nodep->widthConst())); + bip->rhsp(new AstSel(nodep->fileline(), birhsp, 0, nodep->widthConst())); + if (debug()>=9) bip->dumpTree(cout, "SEL(BI)-ou:"); + replaceWChild(nodep, bip); VL_DANGLING(nodep); + return true; } bool operandSelShiftLower(AstSel* nodep) { - // AND({a}, SHIFTR({b}, {c})) is often shorthand in C for Verilog {b}[{c} :+ {a}] - // becomes thought other optimizations - // SEL(SHIFTR({a},{b}),{lsb},{width}) -> SEL({a},{lsb+b},{width}) + // AND({a}, SHIFTR({b}, {c})) is often shorthand in C for Verilog {b}[{c} :+ {a}] + // becomes thought other optimizations + // SEL(SHIFTR({a},{b}),{lsb},{width}) -> SEL({a},{lsb+b},{width}) AstShiftR* shiftp = VN_CAST(nodep->fromp(), ShiftR); - if (!(m_doV - && shiftp + if (!(m_doV + && shiftp && VN_IS(shiftp->rhsp(), Const) && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) - )) return false; - AstNode* ap = shiftp->lhsp(); + )) return false; + AstNode* ap = shiftp->lhsp(); AstConst* bp = VN_CAST(shiftp->rhsp(), Const); AstConst* lp = VN_CAST(nodep->lsbp(), Const); - if (bp->isWide() || bp->num().isFourState() || bp->num().isNegative() - || lp->isWide() || lp->num().isFourState() || lp->num().isNegative()) return false; - int newLsb = lp->toSInt() + bp->toSInt(); - if (newLsb + nodep->widthConst() > ap->width()) return false; - // - UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)\n"); - if (debug()>=9) nodep->dumpTree(cout,"SEL(SH)-in:"); - AstSel* newp = new AstSel(nodep->fileline(), ap->unlinkFrBack(), newLsb, nodep->widthConst()); - newp->dtypeFrom(nodep); - if (debug()>=9) newp->dumpTree(cout,"SEL(SH)-ou:"); - nodep->replaceWith(newp); VL_DANGLING(nodep); - return true; + if (bp->isWide() || bp->num().isFourState() || bp->num().isNegative() + || lp->isWide() || lp->num().isFourState() || lp->num().isNegative()) return false; + int newLsb = lp->toSInt() + bp->toSInt(); + if (newLsb + nodep->widthConst() > ap->width()) return false; + // + UINFO(9, "SEL(SHIFTR(a,b),l,w) -> SEL(a,l+b,w)\n"); + if (debug()>=9) nodep->dumpTree(cout, "SEL(SH)-in:"); + AstSel* newp = new AstSel(nodep->fileline(), ap->unlinkFrBack(), + newLsb, nodep->widthConst()); + newp->dtypeFrom(nodep); + if (debug()>=9) newp->dumpTree(cout, "SEL(SH)-ou:"); + nodep->replaceWith(newp); VL_DANGLING(nodep); + return true; } bool operandBiExtendConstShrink(AstNodeBiop* nodep) { - // Loop unrolling favors standalone compares - // EQ(const{width32}, EXTEND(xx{width3})) -> EQ(const{3}, xx{3}) - // The constant must have zero bits (+ 1 if signed) or compare - // would be incorrect. See also operandBiExtendConst + // Loop unrolling favors standalone compares + // EQ(const{width32}, EXTEND(xx{width3})) -> EQ(const{3}, xx{3}) + // The constant must have zero bits (+ 1 if signed) or compare + // would be incorrect. See also operandBiExtendConst AstExtend* extendp = VN_CAST(nodep->rhsp(), Extend); - if (!extendp) return false; - AstNode* smallerp = extendp->lhsp(); - int subsize = smallerp->width(); + if (!extendp) return false; + AstNode* smallerp = extendp->lhsp(); + int subsize = smallerp->width(); AstConst* constp = VN_CAST(nodep->lhsp(), Const); - if (!constp) return false; - if (!constp->num().isBitsZero(constp->width()-1, subsize)) return false; - // - if (debug()>=9) nodep->dumpTree(cout,"BI(EXTEND)-in:"); - smallerp->unlinkFrBack(); - extendp->unlinkFrBack()->deleteTree(); // aka nodep->lhsp. - nodep->rhsp(smallerp); + if (!constp) return false; + if (!constp->num().isBitsZero(constp->width()-1, subsize)) return false; + // + if (debug()>=9) nodep->dumpTree(cout, "BI(EXTEND)-in:"); + smallerp->unlinkFrBack(); + extendp->unlinkFrBack()->deleteTree(); // aka nodep->lhsp. + nodep->rhsp(smallerp); constp->unlinkFrBack(); V3Number num (constp, subsize); num.opAssign(constp->num()); - nodep->lhsp(new AstConst(constp->fileline(), num)); - constp->deleteTree(); VL_DANGLING(constp); - if (debug()>=9) nodep->dumpTree(cout,"BI(EXTEND)-ou:"); - return true; + nodep->lhsp(new AstConst(constp->fileline(), num)); + constp->deleteTree(); VL_DANGLING(constp); + if (debug()>=9) nodep->dumpTree(cout, "BI(EXTEND)-ou:"); + return true; } bool operandBiExtendConstOver(const AstNodeBiop* nodep) { - // EQ(const{width32}, EXTEND(xx{width3})) -> constant - // When the constant has non-zero bits above the extend it's a constant. - // Avoids compiler warning + // EQ(const{width32}, EXTEND(xx{width3})) -> constant + // When the constant has non-zero bits above the extend it's a constant. + // Avoids compiler warning const AstExtend* extendp = VN_CAST_CONST(nodep->rhsp(), Extend); - if (!extendp) return false; - AstNode* smallerp = extendp->lhsp(); - int subsize = smallerp->width(); + if (!extendp) return false; + AstNode* smallerp = extendp->lhsp(); + int subsize = smallerp->width(); const AstConst* constp = VN_CAST_CONST(nodep->lhsp(), Const); - if (!constp) return false; - if (constp->num().isBitsZero(constp->width()-1, subsize)) return false; - return true; + if (!constp) return false; + if (constp->num().isBitsZero(constp->width()-1, subsize)) return false; + return true; } AstNode* afterComment(AstNode* nodep) { - // Ignore comments, such as to determine if a AstIf is empty. - // nodep may be null, if so return null. + // Ignore comments, such as to determine if a AstIf is empty. + // nodep may be null, if so return null. while (nodep && VN_IS(nodep, Comment)) { nodep = nodep->nextp(); } - return nodep; + return nodep; } bool isTPure(AstNode* nodep) { - // Pure checks - if this node and all nodes under it are free of - // side effects can do this optimization - // Eventually we'll recurse through tree when unknown, memoizing results so far, - // but for now can disable en-mass until V3Purify takes effect. + // Pure checks - if this node and all nodes under it are free of + // side effects can do this optimization + // Eventually we'll recurse through tree when unknown, memoizing results so far, + // but for now can disable en-mass until V3Purify takes effect. return m_doShort || VN_IS(nodep, VarRef) || VN_IS(nodep, Const); } // Extraction checks bool warnSelect(AstSel* nodep) { - if (m_doGenerate) { - // Never checked yet - V3Width::widthParamsEdit(nodep); + if (m_doGenerate) { + // Never checked yet + V3Width::widthParamsEdit(nodep); iterateChildren(nodep); // May need "constifying" - } - // Find range of dtype we are selecting from - // Similar code in V3Unknown::AstSel - bool doit = true; - if (m_warn + } + // Find range of dtype we are selecting from + // Similar code in V3Unknown::AstSel + bool doit = true; + if (m_warn && VN_IS(nodep->lsbp(), Const) && VN_IS(nodep->widthp(), Const) - && doit) { - int maxDeclBit = nodep->declRange().hiMaxSelect()*nodep->declElWidth() + (nodep->declElWidth()-1); + && doit) { + int maxDeclBit = nodep->declRange().hiMaxSelect()*nodep->declElWidth() + + (nodep->declElWidth()-1); if (VN_CAST(nodep->lsbp(), Const)->num().isFourState() || VN_CAST(nodep->widthp(), Const)->num().isFourState()) { - nodep->v3error("Selection index is constantly unknown or tristated: " - "lsb="<lsbp()->name()<<" width="<widthp()->name()); - // Replacing nodep will make a mess above, so we replace the offender - replaceZero(nodep->lsbp()); - } - else if (nodep->declRange().ranged() - && (nodep->msbConst() > maxDeclBit - || nodep->lsbConst() > maxDeclBit)) { - // See also warning in V3Width - // Must adjust by element width as declRange() is in number of elements - nodep->v3warn(SELRANGE, "Selection index out of range: " - <<(nodep->msbConst()/nodep->declElWidth()) - <<":"<<(nodep->lsbConst()/nodep->declElWidth()) - <<" outside "<declRange().hiMaxSelect()<<":0" - <<(nodep->declRange().lo()>=0 ? "" - :(" (adjusted +"+cvtToStr(-nodep->declRange().lo()) - +" to account for negative lsb)"))); - UINFO(1," Related Raw index is "<msbConst()<<":"<lsbConst()<v3error("Selection index is constantly unknown or tristated: " + "lsb="<lsbp()->name()<<" width="<widthp()->name()); + // Replacing nodep will make a mess above, so we replace the offender + replaceZero(nodep->lsbp()); + } + else if (nodep->declRange().ranged() + && (nodep->msbConst() > maxDeclBit + || nodep->lsbConst() > maxDeclBit)) { + // See also warning in V3Width + // Must adjust by element width as declRange() is in number of elements + nodep->v3warn(SELRANGE, "Selection index out of range: " + <<(nodep->msbConst()/nodep->declElWidth()) + <<":"<<(nodep->lsbConst()/nodep->declElWidth()) + <<" outside "<declRange().hiMaxSelect()<<":0" + <<(nodep->declRange().lo()>=0 ? "" + :(" (adjusted +"+cvtToStr(-nodep->declRange().lo()) + +" to account for negative lsb)"))); + UINFO(1," Related Raw index is "<msbConst() + <<":"<lsbConst()<sameGateTree(node2p); - } + return node1p->sameGateTree(node2p); + } else if (VN_IS(node1p, VarRef) && VN_IS(node2p, VarRef)) { - // Avoid comparing widthMin's, which results in lost optimization attempts - // If cleanup sameGateTree to be smarter, this can be restored. - //return node1p->sameGateTree(node2p); - return node1p->same(node2p); - } else { - return false; - } + // Avoid comparing widthMin's, which results in lost optimization attempts + // If cleanup sameGateTree to be smarter, this can be restored. + //return node1p->sameGateTree(node2p); + return node1p->same(node2p); + } else { + return false; + } } bool ifSameAssign(const AstNodeIf* nodep) { const AstNodeAssign* ifp = VN_CAST_CONST(nodep->ifsp(), NodeAssign); const AstNodeAssign* elsep = VN_CAST_CONST(nodep->elsesp(), NodeAssign); - if (!ifp || ifp->nextp()) return false; // Must be SINGLE statement - if (!elsep || elsep->nextp()) return false; - if (ifp->type() != elsep->type()) return false; // Can't mix an assigndly and an assign - if (!ifp->lhsp()->sameGateTree(elsep->lhsp())) return false; - if (!ifp->rhsp()->gateTree()) return false; - if (!elsep->rhsp()->gateTree()) return false; - return true; + if (!ifp || ifp->nextp()) return false; // Must be SINGLE statement + if (!elsep || elsep->nextp()) return false; + if (ifp->type() != elsep->type()) return false; // Can't mix an assigndly and an assign + if (!ifp->lhsp()->sameGateTree(elsep->lhsp())) return false; + if (!ifp->rhsp()->gateTree()) return false; + if (!elsep->rhsp()->gateTree()) return false; + return true; } bool operandIfIf(const AstNodeIf* nodep) { - if (nodep->elsesp()) return false; + if (nodep->elsesp()) return false; const AstNodeIf* lowerIfp = VN_CAST_CONST(nodep->ifsp(), NodeIf); - if (!lowerIfp || lowerIfp->nextp()) return false; - if (nodep->type() != lowerIfp->type()) return false; - if (afterComment(lowerIfp->elsesp())) return false; - return true; + if (!lowerIfp || lowerIfp->nextp()) return false; + if (nodep->type() != lowerIfp->type()) return false; + if (afterComment(lowerIfp->elsesp())) return false; + return true; } bool ifConcatMergeableBiop(const AstNode* nodep) { return (VN_IS(nodep, And) @@ -503,80 +506,80 @@ private: || VN_IS(nodep, Xnor)); } bool ifAdjacentSel(const AstSel* lhsp, const AstSel* rhsp) { - if (!v3Global.opt.oAssemble()) return false; // opt disabled - if (!lhsp || !rhsp) return false; + if (!v3Global.opt.oAssemble()) return false; // opt disabled + if (!lhsp || !rhsp) return false; const AstNode* lfromp = lhsp->fromp(); const AstNode* rfromp = rhsp->fromp(); - if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false; + if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false; const AstConst* lstart = VN_CAST_CONST(lhsp->lsbp(), Const); const AstConst* rstart = VN_CAST_CONST(rhsp->lsbp(), Const); const AstConst* lwidth = VN_CAST_CONST(lhsp->widthp(), Const); const AstConst* rwidth = VN_CAST_CONST(rhsp->widthp(), Const); - if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated - int rend = (rstart->toSInt() + rwidth->toSInt()); + if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated + int rend = (rstart->toSInt() + rwidth->toSInt()); return (rend == lstart->toSInt()); } bool ifMergeAdjacent(AstNode* lhsp, AstNode* rhsp) { - // called by concatmergeable to determine if {lhsp, rhsp} make sense - if (!v3Global.opt.oAssemble()) return false; // opt disabled - // two same varref - if (operandsSame(lhsp, rhsp)) return true; + // called by concatmergeable to determine if {lhsp, rhsp} make sense + if (!v3Global.opt.oAssemble()) return false; // opt disabled + // two same varref + if (operandsSame(lhsp, rhsp)) return true; AstSel* lselp = VN_CAST(lhsp, Sel); AstSel* rselp = VN_CAST(rhsp, Sel); - // a[i:0] a - if (lselp && !rselp && rhsp->sameGateTree(lselp->fromp())) - rselp = new AstSel(rhsp->fileline(), rhsp->cloneTree(false), 0, rhsp->width()); - // a[i:j] {a[j-1:k], b} + // a[i:0] a + if (lselp && !rselp && rhsp->sameGateTree(lselp->fromp())) + rselp = new AstSel(rhsp->fileline(), rhsp->cloneTree(false), 0, rhsp->width()); + // a[i:j] {a[j-1:k], b} if (lselp && !rselp && VN_IS(rhsp, Concat)) return ifMergeAdjacent(lhsp, VN_CAST(rhsp, Concat)->lhsp()); - // a a[msb:j] - if (rselp && !lselp && lhsp->sameGateTree(rselp->fromp())) - lselp = new AstSel(lhsp->fileline(), lhsp->cloneTree(false), 0, lhsp->width()); - // {b, a[j:k]} a[k-1:i] + // a a[msb:j] + if (rselp && !lselp && lhsp->sameGateTree(rselp->fromp())) + lselp = new AstSel(lhsp->fileline(), lhsp->cloneTree(false), 0, lhsp->width()); + // {b, a[j:k]} a[k-1:i] if (rselp && !lselp && VN_IS(lhsp, Concat)) return ifMergeAdjacent(VN_CAST(lhsp, Concat)->rhsp(), rhsp); - if (!lselp || !rselp) return false; + if (!lselp || !rselp) return false; - // a[a:b] a[b-1:c] are adjacent - AstNode* lfromp = lselp->fromp(); - AstNode* rfromp = rselp->fromp(); - if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false; + // a[a:b] a[b-1:c] are adjacent + AstNode* lfromp = lselp->fromp(); + AstNode* rfromp = rselp->fromp(); + if (!lfromp || !rfromp || !lfromp->sameGateTree(rfromp)) return false; AstConst* lstart = VN_CAST(lselp->lsbp(), Const); AstConst* rstart = VN_CAST(rselp->lsbp(), Const); AstConst* lwidth = VN_CAST(lselp->widthp(), Const); AstConst* rwidth = VN_CAST(rselp->widthp(), Const); - if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated - int rend = (rstart->toSInt() + rwidth->toSInt()); - // a[i:j] a[j-1:k] - if (rend == lstart->toSInt()) return true; - // a[i:0] a[msb:j] - if (rend == rfromp->width() && lstart->toSInt() == 0) return true; - return false; + if (!lstart || !rstart || !lwidth || !rwidth) return false; // too complicated + int rend = (rstart->toSInt() + rwidth->toSInt()); + // a[i:j] a[j-1:k] + if (rend == lstart->toSInt()) return true; + // a[i:0] a[msb:j] + if (rend == rfromp->width() && lstart->toSInt() == 0) return true; + return false; } bool concatMergeable(const AstNode* lhsp, const AstNode* rhsp) { // determine if {a OP b, c OP d} => {a, c} OP {b, d} is advantagous - if (!v3Global.opt.oAssemble()) return false; // opt disabled - if (lhsp->type() != rhsp->type()) return false; - if (!ifConcatMergeableBiop(lhsp)) return false; + if (!v3Global.opt.oAssemble()) return false; // opt disabled + if (lhsp->type() != rhsp->type()) return false; + if (!ifConcatMergeableBiop(lhsp)) return false; const AstNodeBiop* lp = VN_CAST_CONST(lhsp, NodeBiop); const AstNodeBiop* rp = VN_CAST_CONST(rhsp, NodeBiop); - if (!lp || !rp) return false; - // {a[]&b[], a[]&b[]} - bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp()); - bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp()); - if (lad && rad) return true; - // {a[] & b[]&c[], a[] & b[]&c[]} - else if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true; - // {a[]&b[] & c[], a[]&b[] & c[]} - else if (rad && concatMergeable(lp->lhsp(), rp->lhsp())) return true; - else { - // {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])} - if (concatMergeable(lp->lhsp(), rp->lhsp()) - && concatMergeable(lp->rhsp(), rp->rhsp())) - return true; - } - return false; + if (!lp || !rp) return false; + // {a[]&b[], a[]&b[]} + bool lad = ifMergeAdjacent(lp->lhsp(), rp->lhsp()); + bool rad = ifMergeAdjacent(lp->rhsp(), rp->rhsp()); + if (lad && rad) return true; + // {a[] & b[]&c[], a[] & b[]&c[]} + else if (lad && concatMergeable(lp->rhsp(), rp->rhsp())) return true; + // {a[]&b[] & c[], a[]&b[] & c[]} + else if (rad && concatMergeable(lp->lhsp(), rp->lhsp())) return true; + else { + // {(a[]&b[])&(c[]&d[]), (a[]&b[])&(c[]&d[])} + if (concatMergeable(lp->lhsp(), rp->lhsp()) + && concatMergeable(lp->rhsp(), rp->rhsp())) + return true; + } + return false; } //---------------------------------------- @@ -584,51 +587,52 @@ private: // These all take a node, delete its tree, and replaces it with a constant void replaceNum(AstNode* oldp, const V3Number& num) { - // Replace oldp node with a constant set to specified value + // Replace oldp node with a constant set to specified value UASSERT(oldp, "Null old"); if (VN_IS(oldp, Const) && !VN_CAST(oldp, Const)->num().isFourState()) { - oldp->v3fatalSrc("Already constant??"); - } - AstNode* newp = new AstConst(oldp->fileline(), num); - newp->dtypeFrom(oldp); - if (debug()>5) oldp->dumpTree(cout," const_old: "); - if (debug()>5) newp->dumpTree(cout," _new: "); - oldp->replaceWith(newp); - oldp->deleteTree(); VL_DANGLING(oldp); + oldp->v3fatalSrc("Already constant??"); + } + AstNode* newp = new AstConst(oldp->fileline(), num); + newp->dtypeFrom(oldp); + if (debug()>5) oldp->dumpTree(cout, " const_old: "); + if (debug()>5) newp->dumpTree(cout, " _new: "); + oldp->replaceWith(newp); + oldp->deleteTree(); VL_DANGLING(oldp); } void replaceNum(AstNode* nodep, uint32_t val) { V3Number num (nodep, nodep->width(), val); replaceNum(nodep, num); VL_DANGLING(nodep); } void replaceNumSigned(AstNodeBiop* nodep, uint32_t val) { - // We allow both sides to be constant, as one may have come from parameter propagation, etc. + // We allow both sides to be constant, as one may have come from + // parameter propagation, etc. if (m_warn && !(VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const))) { - nodep->v3warn(UNSIGNED,"Comparison is constant due to unsigned arithmetic"); - } - replaceNum(nodep, val); VL_DANGLING(nodep); + nodep->v3warn(UNSIGNED, "Comparison is constant due to unsigned arithmetic"); + } + replaceNum(nodep, val); VL_DANGLING(nodep); } void replaceNumLimited(AstNodeBiop* nodep, uint32_t val) { - // Avoids gcc warning about same - if (m_warn) nodep->v3warn(CMPCONST,"Comparison is constant due to limited range"); - replaceNum(nodep, val); VL_DANGLING(nodep); + // Avoids gcc warning about same + if (m_warn) nodep->v3warn(CMPCONST, "Comparison is constant due to limited range"); + replaceNum(nodep, val); VL_DANGLING(nodep); } void replaceZero(AstNode* nodep) { - replaceNum(nodep, 0); VL_DANGLING(nodep); + replaceNum(nodep, 0); VL_DANGLING(nodep); } void replaceZeroChkPure(AstNode* nodep, AstNode* checkp) { - // For example, "0 * n" -> 0 if n has no side effects - // Else strength reduce it to 0 & n. - // If ever change the operation note AstAnd rule specially ignores this created pattern - if (isTPure(checkp)) { - replaceNum(nodep, 0); VL_DANGLING(nodep); - } else { - AstNode* newp = new AstAnd(nodep->fileline(), - new AstConst(nodep->fileline(), 0), - checkp->unlinkFrBack()); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); - } + // For example, "0 * n" -> 0 if n has no side effects + // Else strength reduce it to 0 & n. + // If ever change the operation note AstAnd rule specially ignores this created pattern + if (isTPure(checkp)) { + replaceNum(nodep, 0); VL_DANGLING(nodep); + } else { + AstNode* newp = new AstAnd(nodep->fileline(), + new AstConst(nodep->fileline(), 0), + checkp->unlinkFrBack()); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); + } } void replaceAllOnes(AstNode* nodep) { V3Number ones (nodep, nodep->width(), 0); @@ -643,7 +647,8 @@ private: } void replaceConst(AstNodeBiop* nodep) { V3Number num (nodep, nodep->width()); - nodep->numberOperate(num, VN_CAST(nodep->lhsp(), Const)->num(), VN_CAST(nodep->rhsp(), Const)->num()); + nodep->numberOperate(num, VN_CAST(nodep->lhsp(), Const)->num(), + VN_CAST(nodep->rhsp(), Const)->num()); UINFO(4,"BICONST -> "<numberOperate(num, VN_CAST(nodep->lhsp(), Const)->num(), VN_CAST(nodep->rhsp(), Const)->num(), VN_CAST(nodep->thsp(), Const)->num()); - UINFO(4,"TRICONST -> "< "<fileline(), AstConst::String(), num); - if (debug()>5) oldp->dumpTree(cout," const_old: "); - if (debug()>5) newp->dumpTree(cout," _new: "); - oldp->replaceWith(newp); - oldp->deleteTree(); VL_DANGLING(oldp); + AstNode* newp = new AstConst(oldp->fileline(), AstConst::String(), num); + if (debug()>5) oldp->dumpTree(cout, " const_old: "); + if (debug()>5) newp->dumpTree(cout, " _new: "); + oldp->replaceWith(newp); + oldp->deleteTree(); VL_DANGLING(oldp); } //---------------------------------------- // Replacement functions. // These all take a node and replace it with something else void replaceWChild(AstNode* nodep, AstNode* childp) { - // NODE(..., CHILD(...)) -> CHILD(...) - childp->unlinkFrBackWithNext(); - // If replacing a SEL for example, the data type comes from the parent (is less wide). - // This may adversly affect the operation of the node being replaced. - childp->dtypeFrom(nodep); - nodep->replaceWith(childp); - nodep->deleteTree(); VL_DANGLING(nodep); + // NODE(..., CHILD(...)) -> CHILD(...) + childp->unlinkFrBackWithNext(); + // If replacing a SEL for example, the data type comes from the parent (is less wide). + // This may adversly affect the operation of the node being replaced. + childp->dtypeFrom(nodep); + nodep->replaceWith(childp); + nodep->deleteTree(); VL_DANGLING(nodep); } //! Replace a ternary node with its RHS after iterating //! Used with short-circuting, where the RHS has not yet been iterated. void replaceWIteratedRhs(AstNodeTriop* nodep) { if (AstNode* rhsp = nodep->rhsp()) iterateAndNextNull(rhsp); - replaceWChild(nodep, nodep->rhsp()); // May have changed + replaceWChild(nodep, nodep->rhsp()); // May have changed } //! Replace a ternary node with its THS after iterating //! Used with short-circuting, where the THS has not yet been iterated. void replaceWIteratedThs(AstNodeTriop* nodep) { if (AstNode* thsp = nodep->thsp()) iterateAndNextNull(thsp); - replaceWChild(nodep, nodep->thsp()); // May have changed + replaceWChild(nodep, nodep->thsp()); // May have changed } void replaceWLhs(AstNodeUniop* nodep) { - // Keep LHS, remove RHS - replaceWChild(nodep, nodep->lhsp()); + // Keep LHS, remove RHS + replaceWChild(nodep, nodep->lhsp()); } void replaceWLhs(AstNodeBiop* nodep) { - // Keep LHS, remove RHS - replaceWChild(nodep, nodep->lhsp()); + // Keep LHS, remove RHS + replaceWChild(nodep, nodep->lhsp()); } void replaceWRhs(AstNodeBiop* nodep) { - // Keep RHS, remove LHS - replaceWChild(nodep, nodep->rhsp()); + // Keep RHS, remove LHS + replaceWChild(nodep, nodep->rhsp()); } void replaceAsv(AstNodeBiop* nodep) { - // BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c) - // BIASV(SAMEa, BIASV(SAMEb, c)) -> BIASV( BIASV(SAMEa,SAMEb), c) - //nodep->dumpTree(cout, " repAsvConst_old: "); - AstNode* ap = nodep->lhsp(); + // BIASV(CONSTa, BIASV(CONSTb, c)) -> BIASV( BIASV_CONSTED(a,b), c) + // BIASV(SAMEa, BIASV(SAMEb, c)) -> BIASV( BIASV(SAMEa,SAMEb), c) + //nodep->dumpTree(cout, " repAsvConst_old: "); + AstNode* ap = nodep->lhsp(); AstNodeBiop* rp = VN_CAST(nodep->rhsp(), NodeBiop); - AstNode* bp = rp->lhsp(); - AstNode* cp = rp->rhsp(); - ap->unlinkFrBack(); - bp->unlinkFrBack(); - cp->unlinkFrBack(); - rp->unlinkFrBack(); - nodep->lhsp(rp); - nodep->rhsp(cp); - rp->lhsp(ap); - rp->rhsp(bp); + AstNode* bp = rp->lhsp(); + AstNode* cp = rp->rhsp(); + ap->unlinkFrBack(); + bp->unlinkFrBack(); + cp->unlinkFrBack(); + rp->unlinkFrBack(); + nodep->lhsp(rp); + nodep->rhsp(cp); + rp->lhsp(ap); + rp->rhsp(bp); if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp); - //nodep->dumpTree(cout, " repAsvConst_new: "); + //nodep->dumpTree(cout, " repAsvConst_new: "); } void replaceAsvLUp(AstNodeBiop* nodep) { - // BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r)) + // BIASV(BIASV(CONSTll,lr),r) -> BIASV(CONSTll,BIASV(lr,r)) AstNodeBiop* lp = VN_CAST(nodep->lhsp()->unlinkFrBack(), NodeBiop); - AstNode* llp = lp->lhsp()->unlinkFrBack(); - AstNode* lrp = lp->rhsp()->unlinkFrBack(); - AstNode* rp = nodep->rhsp()->unlinkFrBack(); - nodep->lhsp(llp); - nodep->rhsp(lp); - lp->lhsp(lrp); - lp->rhsp(rp); - //nodep->dumpTree(cout, " repAsvLUp_new: "); + AstNode* llp = lp->lhsp()->unlinkFrBack(); + AstNode* lrp = lp->rhsp()->unlinkFrBack(); + AstNode* rp = nodep->rhsp()->unlinkFrBack(); + nodep->lhsp(llp); + nodep->rhsp(lp); + lp->lhsp(lrp); + lp->rhsp(rp); + //nodep->dumpTree(cout, " repAsvLUp_new: "); } void replaceAsvRUp(AstNodeBiop* nodep) { - // BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr)) - AstNode* lp = nodep->lhsp()->unlinkFrBack(); + // BIASV(l,BIASV(CONSTrl,rr)) -> BIASV(CONSTrl,BIASV(l,rr)) + AstNode* lp = nodep->lhsp()->unlinkFrBack(); AstNodeBiop* rp = VN_CAST(nodep->rhsp()->unlinkFrBack(), NodeBiop); - AstNode* rlp = rp->lhsp()->unlinkFrBack(); - AstNode* rrp = rp->rhsp()->unlinkFrBack(); - nodep->lhsp(rlp); - nodep->rhsp(rp); - rp->lhsp(lp); - rp->rhsp(rrp); - //nodep->dumpTree(cout, " repAsvRUp_new: "); + AstNode* rlp = rp->lhsp()->unlinkFrBack(); + AstNode* rrp = rp->rhsp()->unlinkFrBack(); + nodep->lhsp(rlp); + nodep->rhsp(rp); + rp->lhsp(lp); + rp->rhsp(rrp); + //nodep->dumpTree(cout, " repAsvRUp_new: "); } void replaceAndOr(AstNodeBiop* nodep) { - // OR (AND (CONSTll,lr), AND(CONSTrl==ll,rr)) -> AND (CONSTll, OR(lr,rr)) - // OR (AND (CONSTll,lr), AND(CONSTrl, rr=lr)) -> AND (OR(ll,rl), rr) - // nodep ^lp ^llp ^lrp ^rp ^rlp ^rrp - // (Or/And may also be reversed) + // OR (AND (CONSTll,lr), AND(CONSTrl==ll,rr)) -> AND (CONSTll, OR(lr,rr)) + // OR (AND (CONSTll,lr), AND(CONSTrl, rr=lr)) -> AND (OR(ll,rl), rr) + // nodep ^lp ^llp ^lrp ^rp ^rlp ^rrp + // (Or/And may also be reversed) AstNodeBiop* lp = VN_CAST(nodep->lhsp()->unlinkFrBack(), NodeBiop); - AstNode* llp = lp->lhsp()->unlinkFrBack(); - AstNode* lrp = lp->rhsp()->unlinkFrBack(); + AstNode* llp = lp->lhsp()->unlinkFrBack(); + AstNode* lrp = lp->rhsp()->unlinkFrBack(); AstNodeBiop* rp = VN_CAST(nodep->rhsp()->unlinkFrBack(), NodeBiop); - AstNode* rlp = rp->lhsp()->unlinkFrBack(); - AstNode* rrp = rp->rhsp()->unlinkFrBack(); - nodep->replaceWith(lp); - if (operandsSame(llp,rlp)) { - lp->lhsp(llp); - lp->rhsp(nodep); - nodep->lhsp(lrp); - nodep->rhsp(rrp); - rp->deleteTree(); - rlp->deleteTree(); - } else if (operandsSame(lrp, rrp)) { - lp->lhsp(nodep); - lp->rhsp(rrp); - nodep->lhsp(llp); - nodep->rhsp(rlp); - rp->deleteTree(); - lrp->deleteTree(); - } else { - nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched"); - } - //nodep->dumpTree(cout, " repAndOr_new: "); + AstNode* rlp = rp->lhsp()->unlinkFrBack(); + AstNode* rrp = rp->rhsp()->unlinkFrBack(); + nodep->replaceWith(lp); + if (operandsSame(llp, rlp)) { + lp->lhsp(llp); + lp->rhsp(nodep); + nodep->lhsp(lrp); + nodep->rhsp(rrp); + rp->deleteTree(); + rlp->deleteTree(); + } else if (operandsSame(lrp, rrp)) { + lp->lhsp(nodep); + lp->rhsp(rrp); + nodep->lhsp(llp); + nodep->rhsp(rlp); + rp->deleteTree(); + lrp->deleteTree(); + } else { + nodep->v3fatalSrc("replaceAndOr on something operandAndOrSame shouldn't have matched"); + } + //nodep->dumpTree(cout, " repAndOr_new: "); } void replaceShiftSame(AstNodeBiop* nodep) { - // Or(Shift(ll,CONSTlr),Shift(rl,CONSTrr==lr)) -> Shift(Or(ll,rl),CONSTlr) - // (Or/And may also be reversed) + // Or(Shift(ll,CONSTlr),Shift(rl,CONSTrr==lr)) -> Shift(Or(ll,rl),CONSTlr) + // (Or/And may also be reversed) AstNodeBiop* lp = VN_CAST(nodep->lhsp()->unlinkFrBack(), NodeBiop); - AstNode* llp = lp->lhsp()->unlinkFrBack(); - AstNode* lrp = lp->rhsp()->unlinkFrBack(); + AstNode* llp = lp->lhsp()->unlinkFrBack(); + AstNode* lrp = lp->rhsp()->unlinkFrBack(); AstNodeBiop* rp = VN_CAST(nodep->rhsp()->unlinkFrBack(), NodeBiop); - AstNode* rlp = rp->lhsp()->unlinkFrBack(); - AstNode* rrp = rp->rhsp()->unlinkFrBack(); - nodep->replaceWith(lp); - lp->lhsp(nodep); - lp->rhsp(lrp); - nodep->lhsp(llp); - nodep->rhsp(rlp); - rp->deleteTree(); - rrp->deleteTree(); - //nodep->dumpTree(cout, " repShiftSame_new: "); + AstNode* rlp = rp->lhsp()->unlinkFrBack(); + AstNode* rrp = rp->rhsp()->unlinkFrBack(); + nodep->replaceWith(lp); + lp->lhsp(nodep); + lp->rhsp(lrp); + nodep->lhsp(llp); + nodep->rhsp(rlp); + rp->deleteTree(); + rrp->deleteTree(); + //nodep->dumpTree(cout, " repShiftSame_new: "); } void replaceConcatSel(AstConcat* nodep) { - // {a[1], a[0]} -> a[1:0] + // {a[1], a[0]} -> a[1:0] AstSel* lselp = VN_CAST(nodep->lhsp()->unlinkFrBack(), Sel); AstSel* rselp = VN_CAST(nodep->rhsp()->unlinkFrBack(), Sel); - int lstart = lselp->lsbConst(); - int lwidth = lselp->widthConst(); - int rstart = rselp->lsbConst(); - int rwidth = rselp->widthConst(); + int lstart = lselp->lsbConst(); + int lwidth = lselp->widthConst(); + int rstart = rselp->lsbConst(); + int rwidth = rselp->widthConst(); - if ((rstart + rwidth) != lstart) nodep->v3fatalSrc("tried to merge two selects which are not adjacent"); - AstSel* newselp = new AstSel(lselp->fromp()->fileline(), rselp->fromp()->cloneTree(false), rstart, lwidth+rwidth); - UINFO(5, "merged two adjacent sel "<v3fatalSrc("tried to merge two selects which are not adjacent"); + AstSel* newselp = new AstSel(lselp->fromp()->fileline(), + rselp->fromp()->cloneTree(false), rstart, lwidth+rwidth); + UINFO(5, "merged two adjacent sel "<replaceWith(newselp); - lselp->deleteTree(); VL_DANGLING(lselp); - rselp->deleteTree(); VL_DANGLING(rselp); - nodep->deleteTree(); VL_DANGLING(nodep); + nodep->replaceWith(newselp); + lselp->deleteTree(); VL_DANGLING(lselp); + rselp->deleteTree(); VL_DANGLING(rselp); + nodep->deleteTree(); VL_DANGLING(nodep); } void replaceConcatMerge(AstConcat* nodep) { AstNodeBiop* lp = VN_CAST(nodep->lhsp(), NodeBiop); AstNodeBiop* rp = VN_CAST(nodep->rhsp(), NodeBiop); - AstNode* llp = lp->lhsp()->cloneTree(false); - AstNode* lrp = lp->rhsp()->cloneTree(false); - AstNode* rlp = rp->lhsp()->cloneTree(false); - AstNode* rrp = rp->rhsp()->cloneTree(false); - if (concatMergeable(lp, rp)) { - AstConcat* newlp = new AstConcat(rlp->fileline(), llp, rlp); - AstConcat* newrp = new AstConcat(rrp->fileline(), lrp, rrp); - // use the lhs to replace the parent concat - lp->lhsp()->replaceWith(newlp); - lp->rhsp()->replaceWith(newrp); - lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), AstNumeric::fromBool(true)); - UINFO(5, "merged "<< nodep <unlinkFrBack()->deleteTree(); VL_DANGLING(rp); - nodep->replaceWith(lp->unlinkFrBack()); nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* llp = lp->lhsp()->cloneTree(false); + AstNode* lrp = lp->rhsp()->cloneTree(false); + AstNode* rlp = rp->lhsp()->cloneTree(false); + AstNode* rrp = rp->rhsp()->cloneTree(false); + if (concatMergeable(lp, rp)) { + AstConcat* newlp = new AstConcat(rlp->fileline(), llp, rlp); + AstConcat* newrp = new AstConcat(rrp->fileline(), lrp, rrp); + // use the lhs to replace the parent concat + lp->lhsp()->replaceWith(newlp); + lp->rhsp()->replaceWith(newrp); + lp->dtypeChgWidthSigned(newlp->width(), newlp->width(), AstNumeric::fromBool(true)); + UINFO(5, "merged "<< nodep <unlinkFrBack()->deleteTree(); VL_DANGLING(rp); + nodep->replaceWith(lp->unlinkFrBack()); nodep->deleteTree(); VL_DANGLING(nodep); iterate(lp->lhsp()); iterate(lp->rhsp()); - } else nodep->v3fatalSrc("tried to merge two Concat which are not adjacent"); + } else nodep->v3fatalSrc("tried to merge two Concat which are not adjacent"); } void replaceExtend(AstNode* nodep, AstNode* arg0p) { - // -> EXTEND(nodep) - // like a AstExtend{$rhsp}, but we need to set the width correctly from base node - arg0p->unlinkFrBack(); + // -> EXTEND(nodep) + // like a AstExtend{$rhsp}, but we need to set the width correctly from base node + arg0p->unlinkFrBack(); AstNode* newp = (VN_IS(nodep, ExtendS) - ? static_cast(new AstExtendS(nodep->fileline(), arg0p)) - : static_cast(new AstExtend (nodep->fileline(), arg0p))); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + ? static_cast(new AstExtendS(nodep->fileline(), arg0p)) + : static_cast(new AstExtend (nodep->fileline(), arg0p))); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); } void replacePowShift(AstNodeBiop* nodep) { // Pow or PowS - UINFO(5,"POW(2,b)->SHIFTL(1,b) "<rhsp()->unlinkFrBack(); - AstShiftL* newp = new AstShiftL(nodep->fileline(), - new AstConst(nodep->fileline(), 1), - rhsp); - newp->dtypeFrom(nodep); - newp->lhsp()->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + UINFO(5,"POW(2,b)->SHIFTL(1,b) "<rhsp()->unlinkFrBack(); + AstShiftL* newp = new AstShiftL(nodep->fileline(), + new AstConst(nodep->fileline(), 1), + rhsp); + newp->dtypeFrom(nodep); + newp->lhsp()->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); } void replaceMulShift(AstMul* nodep) { // Mul, but not MulS as not simple shift - UINFO(5,"MUL(2^n,b)->SHIFTL(b,n) "<SHIFTL(b,n) "<lhsp(), Const)->num().mostSetBitP1()-1; // 2^n->n+1 - AstNode* opp = nodep->rhsp()->unlinkFrBack(); - AstShiftL* newp = new AstShiftL(nodep->fileline(), - opp, new AstConst(nodep->fileline(), amount)); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* opp = nodep->rhsp()->unlinkFrBack(); + AstShiftL* newp = new AstShiftL(nodep->fileline(), + opp, new AstConst(nodep->fileline(), amount)); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); } void replaceDivShift(AstDiv* nodep) { // Mul, but not MulS as not simple shift - UINFO(5,"DIV(b,2^n)->SHIFTR(b,n) "<SHIFTR(b,n) "<rhsp(), Const)->num().mostSetBitP1()-1; // 2^n->n+1 - AstNode* opp = nodep->lhsp()->unlinkFrBack(); - AstShiftR* newp = new AstShiftR(nodep->fileline(), - opp, new AstConst(nodep->fileline(), amount)); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* opp = nodep->lhsp()->unlinkFrBack(); + AstShiftR* newp = new AstShiftR(nodep->fileline(), + opp, new AstConst(nodep->fileline(), amount)); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); } void replaceShiftOp(AstNodeBiop* nodep) { - UINFO(5,"SHIFT(AND(a,b),CONST)->AND(SHIFT(a,CONST),SHIFT(b,CONST)) "<unlinkFrBack(&handle); + UINFO(5,"SHIFT(AND(a,b),CONST)->AND(SHIFT(a,CONST),SHIFT(b,CONST)) "<unlinkFrBack(&handle); AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop); lhsp->unlinkFrBack(); - AstNode* shiftp = nodep->rhsp()->unlinkFrBack(); - AstNode* ap = lhsp->lhsp()->unlinkFrBack(); - AstNode* bp = lhsp->rhsp()->unlinkFrBack(); - AstNodeBiop* shift1p = nodep; - AstNodeBiop* shift2p = nodep->cloneTree(true); - shift1p->lhsp(ap); shift1p->rhsp(shiftp->cloneTree(true)); - shift2p->lhsp(bp); shift2p->rhsp(shiftp); - AstNodeBiop* newp = lhsp; - newp->lhsp(shift1p); newp->rhsp(shift2p); - handle.relink(newp); + AstNode* shiftp = nodep->rhsp()->unlinkFrBack(); + AstNode* ap = lhsp->lhsp()->unlinkFrBack(); + AstNode* bp = lhsp->rhsp()->unlinkFrBack(); + AstNodeBiop* shift1p = nodep; + AstNodeBiop* shift2p = nodep->cloneTree(true); + shift1p->lhsp(ap); shift1p->rhsp(shiftp->cloneTree(true)); + shift2p->lhsp(bp); shift2p->rhsp(shiftp); + AstNodeBiop* newp = lhsp; + newp->lhsp(shift1p); newp->rhsp(shift2p); + handle.relink(newp); iterate(newp); // Further reduce, either node may have more reductions. } void replaceShiftShift(AstNodeBiop* nodep) { - UINFO(4,"SHIFT(SHIFT(a,s1),s2)->SHIFT(a,ADD(s1,s2)) "<=9) nodep->dumpTree(cout, " repShiftShift_old: "); + UINFO(4,"SHIFT(SHIFT(a,s1),s2)->SHIFT(a,ADD(s1,s2)) "<=9) nodep->dumpTree(cout, " repShiftShift_old: "); AstNodeBiop* lhsp = VN_CAST(nodep->lhsp(), NodeBiop); lhsp->unlinkFrBack(); - AstNode* ap = lhsp->lhsp()->unlinkFrBack(); - AstNode* shift1p = lhsp->rhsp()->unlinkFrBack(); - AstNode* shift2p = nodep->rhsp()->unlinkFrBack(); - // Shift1p and shift2p may have different sizes, both are self-determined so sum with infinite width - if (nodep->type()==lhsp->type()) { + AstNode* ap = lhsp->lhsp()->unlinkFrBack(); + AstNode* shift1p = lhsp->rhsp()->unlinkFrBack(); + AstNode* shift2p = nodep->rhsp()->unlinkFrBack(); + // Shift1p and shift2p may have different sizes, both are + // self-determined so sum with infinite width + if (nodep->type()==lhsp->type()) { int shift1 = VN_CAST(shift1p, Const)->toUInt(); int shift2 = VN_CAST(shift2p, Const)->toUInt(); - int newshift = shift1+shift2; - shift1p->deleteTree(); VL_DANGLING(shift1p); - shift2p->deleteTree(); VL_DANGLING(shift2p); - nodep->lhsp(ap); - nodep->rhsp(new AstConst(nodep->fileline(), newshift)); + int newshift = shift1+shift2; + shift1p->deleteTree(); VL_DANGLING(shift1p); + shift2p->deleteTree(); VL_DANGLING(shift2p); + nodep->lhsp(ap); + nodep->rhsp(new AstConst(nodep->fileline(), newshift)); iterate(nodep); // Further reduce, either node may have more reductions. - } else { - // We know shift amounts are constant, but might be a mixed left/right shift - int shift1 = VN_CAST(shift1p, Const)->toUInt(); if (VN_IS(lhsp, ShiftR)) shift1=-shift1; - int shift2 = VN_CAST(shift2p, Const)->toUInt(); if (VN_IS(nodep, ShiftR)) shift2=-shift2; - int newshift = shift1+shift2; - shift1p->deleteTree(); VL_DANGLING(shift1p); - shift2p->deleteTree(); VL_DANGLING(shift2p); - AstNode* newp; + } else { + // We know shift amounts are constant, but might be a mixed left/right shift + int shift1 = VN_CAST(shift1p, Const)->toUInt(); + if (VN_IS(lhsp, ShiftR)) shift1=-shift1; + int shift2 = VN_CAST(shift2p, Const)->toUInt(); + if (VN_IS(nodep, ShiftR)) shift2=-shift2; + int newshift = shift1+shift2; + shift1p->deleteTree(); VL_DANGLING(shift1p); + shift2p->deleteTree(); VL_DANGLING(shift2p); + AstNode* newp; V3Number mask1 (nodep, nodep->width()); V3Number ones (nodep, nodep->width()); ones.setMask(nodep->width()); @@ -929,83 +938,83 @@ private: } else { mask.opShiftL(mask1, V3Number(nodep, VL_WORDSIZE, shift2)); } - if (newshift<0) { - newp = new AstShiftR(nodep->fileline(), ap, - new AstConst(nodep->fileline(), -newshift)); - } else { - newp = new AstShiftL(nodep->fileline(), ap, - new AstConst(nodep->fileline(), newshift)); - } - newp->dtypeFrom(nodep); + if (newshift<0) { + newp = new AstShiftR(nodep->fileline(), ap, + new AstConst(nodep->fileline(), -newshift)); + } else { + newp = new AstShiftL(nodep->fileline(), ap, + new AstConst(nodep->fileline(), newshift)); + } + newp->dtypeFrom(nodep); newp = new AstAnd(nodep->fileline(), newp, new AstConst(nodep->fileline(), mask)); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - //newp->dumpTree(cout, " repShiftShift_new: "); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + //newp->dumpTree(cout, " repShiftShift_new: "); iterate(newp); // Further reduce, either node may have more reductions. - } - lhsp->deleteTree(); VL_DANGLING(lhsp); + } + lhsp->deleteTree(); VL_DANGLING(lhsp); } bool replaceAssignMultiSel(AstNodeAssign* nodep) { - // Multiple assignments to sequential bits can be concated - // ASSIGN(SEL(a),aq), ASSIGN(SEL(a+1),bq) -> ASSIGN(SEL(a:b),CONCAT(aq,bq) - // ie. assign var[2]=a, assign var[3]=b -> assign var[3:2]={b,a} - if (!m_modp) return false; // Skip if we're not const'ing an entire module (IE doing only one assign, etc) + // Multiple assignments to sequential bits can be concated + // ASSIGN(SEL(a),aq), ASSIGN(SEL(a+1),bq) -> ASSIGN(SEL(a:b),CONCAT(aq,bq) + // ie. assign var[2]=a, assign var[3]=b -> assign var[3:2]={b,a} + if (!m_modp) return false; // Skip if we're not const'ing an entire module (IE doing only one assign, etc) AstSel* sel1p = VN_CAST(nodep->lhsp(), Sel); if (!sel1p) return false; AstNodeAssign* nextp = VN_CAST(nodep->nextp(), NodeAssign); if (!nextp) return false; - if (nodep->type() != nextp->type()) return false; + if (nodep->type() != nextp->type()) return false; AstSel* sel2p = VN_CAST(nextp->lhsp(), Sel); if (!sel2p) return false; AstVarRef* varref1p = VN_CAST(sel1p->fromp(), VarRef); if (!varref1p) return false; AstVarRef* varref2p = VN_CAST(sel2p->fromp(), VarRef); if (!varref2p) return false; - if (!varref1p->sameGateTree(varref2p)) return false; + if (!varref1p->sameGateTree(varref2p)) return false; AstConst* con1p = VN_CAST(sel1p->lsbp(), Const); if (!con1p) return false; AstConst* con2p = VN_CAST(sel2p->lsbp(), Const); if (!con2p) return false; - // We need to make sure there's no self-references involved in either - // assignment. For speed, we only look 3 deep, then give up. - if (!varNotReferenced(nodep->rhsp(), varref1p->varp())) return false; - if (!varNotReferenced(nextp->rhsp(), varref2p->varp())) return false; - // Swap? - if (( con1p->toSInt() != con2p->toSInt() + sel2p->width()) - &&(con2p->toSInt() != con1p->toSInt() + sel1p->width())) return false; - bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt()); - // If the user already has nice 32-bit divisions, keep them to aid later subdivision - //if (VL_BITBIT_I(con1p->toUInt()) == 0) return false; - UINFO(4,"replaceAssignMultiSel "<dumpTree(cout, "comb1: "); - //nextp->dumpTree(cout, "comb2: "); - AstNode* rhs1p = nodep->rhsp()->unlinkFrBack(); - AstNode* rhs2p = nextp->rhsp()->unlinkFrBack(); - AstNode* newp; - if (lsbFirstAssign) { + // We need to make sure there's no self-references involved in either + // assignment. For speed, we only look 3 deep, then give up. + if (!varNotReferenced(nodep->rhsp(), varref1p->varp())) return false; + if (!varNotReferenced(nextp->rhsp(), varref2p->varp())) return false; + // Swap? + if (( con1p->toSInt() != con2p->toSInt() + sel2p->width()) + &&(con2p->toSInt() != con1p->toSInt() + sel1p->width())) return false; + bool lsbFirstAssign = (con1p->toUInt() < con2p->toUInt()); + // If the user already has nice 32-bit divisions, keep them to aid later subdivision + //if (VL_BITBIT_I(con1p->toUInt()) == 0) return false; + UINFO(4,"replaceAssignMultiSel "<dumpTree(cout, "comb1: "); + //nextp->dumpTree(cout, "comb2: "); + AstNode* rhs1p = nodep->rhsp()->unlinkFrBack(); + AstNode* rhs2p = nextp->rhsp()->unlinkFrBack(); + AstNode* newp; + if (lsbFirstAssign) { newp = nodep->cloneType(new AstSel(sel1p->fileline(), varref1p->unlinkFrBack(), sel1p->lsbConst(), sel1p->width() + sel2p->width()), new AstConcat(rhs1p->fileline(), rhs2p, rhs1p)); - } else { + } else { newp = nodep->cloneType(new AstSel(sel1p->fileline(), varref1p->unlinkFrBack(), sel2p->lsbConst(), sel1p->width() + sel2p->width()), new AstConcat(rhs1p->fileline(), rhs1p, rhs2p)); - } - //pnewp->dumpTree(cout, "conew: "); - nodep->replaceWith(newp); nodep->deleteTree(); - nextp->unlinkFrBack()->deleteTree(); - return true; + } + //pnewp->dumpTree(cout, "conew: "); + nodep->replaceWith(newp); nodep->deleteTree(); + nextp->unlinkFrBack()->deleteTree(); + return true; } bool varNotReferenced(AstNode* nodep, AstVar* varp, int level=0) { - // Return true if varp never referenced under node. - // Return false if referenced, or tree too deep to be worth it, or side effects - if (!nodep) return true; - if (level>2) return false; - if (nodep->isPure()) return false; // For example a $fgetc can't be reordered + // Return true if varp never referenced under node. + // Return false if referenced, or tree too deep to be worth it, or side effects + if (!nodep) return true; + if (level>2) return false; + if (nodep->isPure()) return false; // For example a $fgetc can't be reordered if (VN_IS(nodep, NodeVarRef) && VN_CAST(nodep, NodeVarRef)->varp()==varp) return false; - return (varNotReferenced (nodep->nextp(),varp,level+1) - && varNotReferenced(nodep->op1p(),varp,level+1) - && varNotReferenced(nodep->op2p(),varp,level+1) - && varNotReferenced(nodep->op3p(),varp,level+1) - && varNotReferenced(nodep->op4p(),varp,level+1)); + return (varNotReferenced(nodep->nextp(), varp, level+1) + && varNotReferenced(nodep->op1p(), varp, level+1) + && varNotReferenced(nodep->op2p(), varp, level+1) + && varNotReferenced(nodep->op3p(), varp, level+1) + && varNotReferenced(nodep->op4p(), varp, level+1)); } bool replaceNodeAssign(AstNodeAssign* nodep) { @@ -1013,173 +1022,177 @@ private: && VN_IS(nodep->rhsp(), VarRef) && VN_CAST(nodep->lhsp(), VarRef)->sameNoLvalue(VN_CAST(nodep->rhsp(), VarRef)) && !VN_IS(nodep, AssignDly)) { - // X = X. Quite pointless, though X <= X may override another earlier assignment + // X = X. Quite pointless, though X <= X may override another earlier assignment if (VN_IS(nodep, AssignW)) { - nodep->v3error("Wire inputs its own output, creating circular logic (wire x=x)"); - return false; // Don't delete the assign, or V3Gate will freak out - } else { - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - return true; - } - } + nodep->v3error("Wire inputs its own output, creating circular logic (wire x=x)"); + return false; // Don't delete the assign, or V3Gate will freak out + } else { + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + return true; + } + } else if (m_doV && VN_IS(nodep->lhsp(), Concat)) { - bool need_temp = false; + bool need_temp = false; if (m_warn && !VN_IS(nodep, AssignDly)) { // Is same var on LHS and RHS? - // Note only do this (need user4) when m_warn, which is - // done as unique visitor - AstUser4InUse m_inuser4; - ConstVarMarkVisitor mark(nodep->lhsp()); - ConstVarFindVisitor find(nodep->rhsp()); - if (find.found()) need_temp = true; - } - if (need_temp) { - // The first time we constify, there may be the same variable on the LHS - // and RHS. In that case, we must use temporaries, or {a,b}={b,a} will break. - UINFO(4," ASSITEMP "< ASSIGN(temp1,SEL(rhs,{size})), - // ASSIGN(temp2,SEL(newrhs,{size})) - // ASSIGN(lc1,temp1), - // ASSIGN(lc2,temp2) - } else { - UINFO(4," ASSI "< ASSIGN(lc1,SEL(rhs,{size})), - // ASSIGN(lc2,SEL(newrhs,{size})) - } - if (debug()>=9) nodep->dumpTree(cout," Ass_old: "); - // Unlink the stuff + // Note only do this (need user4) when m_warn, which is + // done as unique visitor + AstUser4InUse m_inuser4; + ConstVarMarkVisitor mark(nodep->lhsp()); + ConstVarFindVisitor find(nodep->rhsp()); + if (find.found()) need_temp = true; + } + if (need_temp) { + // The first time we constify, there may be the same variable on the LHS + // and RHS. In that case, we must use temporaries, or {a,b}={b,a} will break. + UINFO(4," ASSITEMP "< ASSIGN(temp1,SEL(rhs,{size})), + // ASSIGN(temp2,SEL(newrhs,{size})) + // ASSIGN(lc1,temp1), + // ASSIGN(lc2,temp2) + } else { + UINFO(4," ASSI "< ASSIGN(lc1,SEL(rhs,{size})), + // ASSIGN(lc2,SEL(newrhs,{size})) + } + if (debug()>=9) nodep->dumpTree(cout, " Ass_old: "); + // Unlink the stuff AstNode* lc1p = VN_CAST(nodep->lhsp(), Concat)->lhsp()->unlinkFrBack(); AstNode* lc2p = VN_CAST(nodep->lhsp(), Concat)->rhsp()->unlinkFrBack(); AstNode* conp = VN_CAST(nodep->lhsp(), Concat)->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* rhs2p = rhsp->cloneTree(false); - // Calc widths - int lsb2 = 0; - int msb2 = lsb2+lc2p->width()-1; - int lsb1 = msb2+1; - int msb1 = lsb1+lc1p->width()-1; - if (msb1!=(conp->width()-1)) nodep->v3fatalSrc("Width calc mismatch"); - // Form ranges - AstSel* sel1p = new AstSel(conp->fileline(), rhsp, lsb1, msb1-lsb1+1); - AstSel* sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2-lsb2+1); - // Make new assigns of same flavor as old one - //*** Not cloneTree; just one node. - AstNode* newp = NULL; - if (!need_temp) { + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* rhs2p = rhsp->cloneTree(false); + // Calc widths + int lsb2 = 0; + int msb2 = lsb2+lc2p->width()-1; + int lsb1 = msb2+1; + int msb1 = lsb1+lc1p->width()-1; + if (msb1!=(conp->width()-1)) nodep->v3fatalSrc("Width calc mismatch"); + // Form ranges + AstSel* sel1p = new AstSel(conp->fileline(), rhsp, lsb1, msb1-lsb1+1); + AstSel* sel2p = new AstSel(conp->fileline(), rhs2p, lsb2, msb2-lsb2+1); + // Make new assigns of same flavor as old one + //*** Not cloneTree; just one node. + AstNode* newp = NULL; + if (!need_temp) { AstNodeAssign* asn1ap = VN_CAST(nodep->cloneType(lc1p, sel1p), NodeAssign); AstNodeAssign* asn2ap = VN_CAST(nodep->cloneType(lc2p, sel2p), NodeAssign); - asn1ap->dtypeFrom(sel1p); - asn2ap->dtypeFrom(sel2p); - newp = AstNode::addNext(newp, asn1ap); - newp = AstNode::addNext(newp, asn2ap); - } else { - if (!m_modp) nodep->v3fatalSrc("Not under module"); - // We could create just one temp variable, but we'll get better optimization - // if we make one per term. + asn1ap->dtypeFrom(sel1p); + asn2ap->dtypeFrom(sel2p); + newp = AstNode::addNext(newp, asn1ap); + newp = AstNode::addNext(newp, asn2ap); + } else { + if (!m_modp) nodep->v3fatalSrc("Not under module"); + // We could create just one temp variable, but we'll get better optimization + // if we make one per term. string name1 = (string("__Vconcswap")+cvtToStr(m_modp->varNumGetInc())); string name2 = (string("__Vconcswap")+cvtToStr(m_modp->varNumGetInc())); - AstVar* temp1p = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, name1, - VFlagLogicPacked(), msb1-lsb1+1); - AstVar* temp2p = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, name2, - VFlagLogicPacked(), msb2-lsb2+1); - m_modp->addStmtp(temp1p); - m_modp->addStmtp(temp2p); - AstNodeAssign* asn1ap = VN_CAST(nodep->cloneType - (new AstVarRef(sel1p->fileline(), temp1p, true), - sel1p), NodeAssign); - AstNodeAssign* asn2ap = VN_CAST(nodep->cloneType - (new AstVarRef(sel2p->fileline(), temp2p, true), - sel2p), NodeAssign); - AstNodeAssign* asn1bp = VN_CAST(nodep->cloneType - (lc1p, new AstVarRef(sel1p->fileline(), temp1p, false)), - NodeAssign); - AstNodeAssign* asn2bp = VN_CAST(nodep->cloneType - (lc2p, new AstVarRef(sel2p->fileline(), temp2p, false)), - NodeAssign); - asn1ap->dtypeFrom(temp1p); - asn1bp->dtypeFrom(temp1p); - asn2ap->dtypeFrom(temp2p); - asn2bp->dtypeFrom(temp2p); - // This order matters - newp = AstNode::addNext(newp, asn1ap); - newp = AstNode::addNext(newp, asn2ap); - newp = AstNode::addNext(newp, asn1bp); - newp = AstNode::addNext(newp, asn2bp); - } - if (debug()>=9 && newp) newp->dumpTreeAndNext(cout," _new: "); - nodep->addNextHere(newp); - // Cleanup - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - conp->deleteTree(); VL_DANGLING(conp); - // Further reduce, either node may have more reductions. - return true; - } + AstVar* temp1p = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, name1, + VFlagLogicPacked(), msb1-lsb1+1); + AstVar* temp2p = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, name2, + VFlagLogicPacked(), msb2-lsb2+1); + m_modp->addStmtp(temp1p); + m_modp->addStmtp(temp2p); + AstNodeAssign* asn1ap + = VN_CAST(nodep->cloneType + (new AstVarRef(sel1p->fileline(), temp1p, true), + sel1p), NodeAssign); + AstNodeAssign* asn2ap + = VN_CAST(nodep->cloneType + (new AstVarRef(sel2p->fileline(), temp2p, true), + sel2p), NodeAssign); + AstNodeAssign* asn1bp + = VN_CAST(nodep->cloneType + (lc1p, new AstVarRef(sel1p->fileline(), temp1p, false)), + NodeAssign); + AstNodeAssign* asn2bp + = VN_CAST(nodep->cloneType + (lc2p, new AstVarRef(sel2p->fileline(), temp2p, false)), + NodeAssign); + asn1ap->dtypeFrom(temp1p); + asn1bp->dtypeFrom(temp1p); + asn2ap->dtypeFrom(temp2p); + asn2bp->dtypeFrom(temp2p); + // This order matters + newp = AstNode::addNext(newp, asn1ap); + newp = AstNode::addNext(newp, asn2ap); + newp = AstNode::addNext(newp, asn1bp); + newp = AstNode::addNext(newp, asn2bp); + } + if (debug()>=9 && newp) newp->dumpTreeAndNext(cout, " _new: "); + nodep->addNextHere(newp); + // Cleanup + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + conp->deleteTree(); VL_DANGLING(conp); + // Further reduce, either node may have more reductions. + return true; + } else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) { - // The right-streaming operator on rhs of assignment does not - // change the order of bits. Eliminate stream but keep its lhsp - // Unlink the stuff + // The right-streaming operator on rhs of assignment does not + // change the order of bits. Eliminate stream but keep its lhsp + // Unlink the stuff AstNode* srcp = VN_CAST(nodep->rhsp(), StreamR)->lhsp()->unlinkFrBack(); AstNode* sizep = VN_CAST(nodep->rhsp(), StreamR)->rhsp()->unlinkFrBack(); AstNode* streamp = VN_CAST(nodep->rhsp(), StreamR)->unlinkFrBack(); - nodep->rhsp(srcp); - // Cleanup - sizep->deleteTree(); VL_DANGLING(sizep); - streamp->deleteTree(); VL_DANGLING(streamp); - // Further reduce, any of the nodes may have more reductions. - return true; - } + nodep->rhsp(srcp); + // Cleanup + sizep->deleteTree(); VL_DANGLING(sizep); + streamp->deleteTree(); VL_DANGLING(streamp); + // Further reduce, any of the nodes may have more reductions. + return true; + } else if (m_doV && VN_IS(nodep->lhsp(), StreamL)) { - // Push the stream operator to the rhs of the assignment statement + // Push the stream operator to the rhs of the assignment statement int dWidth = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->width(); - int sWidth = nodep->rhsp()->width(); - // Unlink the stuff + int sWidth = nodep->rhsp()->width(); + // Unlink the stuff AstNode* dstp = VN_CAST(nodep->lhsp(), StreamL)->lhsp()->unlinkFrBack(); AstNode* streamp = VN_CAST(nodep->lhsp(), StreamL)->unlinkFrBack(); - AstNode* srcp = nodep->rhsp()->unlinkFrBack(); - // Connect the rhs to the stream operator and update its width + AstNode* srcp = nodep->rhsp()->unlinkFrBack(); + // Connect the rhs to the stream operator and update its width VN_CAST(streamp, StreamL)->lhsp(srcp); - streamp->dtypeSetLogicSized((srcp->width()), - (srcp->widthMin()), - AstNumeric::UNSIGNED); - // Shrink the RHS if necessary - if (sWidth > dWidth) { - streamp = new AstSel(streamp->fileline(), streamp, sWidth-dWidth, dWidth); - } - // Link the nodes back in - nodep->lhsp(dstp); - nodep->rhsp(streamp); - return true; - } + streamp->dtypeSetLogicSized((srcp->width()), + (srcp->widthMin()), + AstNumeric::UNSIGNED); + // Shrink the RHS if necessary + if (sWidth > dWidth) { + streamp = new AstSel(streamp->fileline(), streamp, sWidth-dWidth, dWidth); + } + // Link the nodes back in + nodep->lhsp(dstp); + nodep->rhsp(streamp); + return true; + } else if (m_doV && VN_IS(nodep->lhsp(), StreamR)) { - // The right stream operator on lhs of assignment statement does - // not reorder bits. However, if the rhs is wider than the lhs, - // then we select bits from the left-most, not the right-most. + // The right stream operator on lhs of assignment statement does + // not reorder bits. However, if the rhs is wider than the lhs, + // then we select bits from the left-most, not the right-most. int dWidth = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->width(); - int sWidth = nodep->rhsp()->width(); - // Unlink the stuff + int sWidth = nodep->rhsp()->width(); + // Unlink the stuff AstNode* dstp = VN_CAST(nodep->lhsp(), StreamR)->lhsp()->unlinkFrBack(); AstNode* sizep = VN_CAST(nodep->lhsp(), StreamR)->rhsp()->unlinkFrBack(); AstNode* streamp = VN_CAST(nodep->lhsp(), StreamR)->unlinkFrBack(); - AstNode* srcp = nodep->rhsp()->unlinkFrBack(); - if (sWidth > dWidth) { - srcp = new AstSel(streamp->fileline(), srcp, sWidth-dWidth, dWidth); - } - nodep->lhsp(dstp); - nodep->rhsp(srcp); - // Cleanup - sizep->deleteTree(); VL_DANGLING(sizep); - streamp->deleteTree(); VL_DANGLING(streamp); - // Further reduce, any of the nodes may have more reductions. - return true; - } - else if (replaceAssignMultiSel(nodep)) { - return true; - } - return false; + AstNode* srcp = nodep->rhsp()->unlinkFrBack(); + if (sWidth > dWidth) { + srcp = new AstSel(streamp->fileline(), srcp, sWidth-dWidth, dWidth); + } + nodep->lhsp(dstp); + nodep->rhsp(srcp); + // Cleanup + sizep->deleteTree(); VL_DANGLING(sizep); + streamp->deleteTree(); VL_DANGLING(streamp); + // Further reduce, any of the nodes may have more reductions. + return true; + } + else if (replaceAssignMultiSel(nodep)) { + return true; + } + return false; } // Boolean replacements bool operandBoolShift(const AstNode* nodep) { - // boolean test of AND(const,SHIFTR(x,const)) -> test of AND(SHIFTL(x,const), x) + // boolean test of AND(const,SHIFTR(x,const)) -> test of AND(SHIFTL(x,const), x) if (!VN_IS(nodep, And)) return false; if (!VN_IS(VN_CAST_CONST(nodep, And)->lhsp(), Const)) return false; if (!VN_IS(VN_CAST_CONST(nodep, And)->rhsp(), ShiftR)) return false; @@ -1187,422 +1200,430 @@ private: if (!VN_IS(shiftp->rhsp(), Const)) return false; if (static_cast(nodep->width()) <= VN_CAST_CONST(shiftp->rhsp(), Const)->toUInt()) return false; - return true; + return true; } void replaceBoolShift(AstNode* nodep) { - if (debug()>=9) nodep->dumpTree(cout," bshft_old: "); + if (debug()>=9) nodep->dumpTree(cout, " bshft_old: "); AstConst* andConstp = VN_CAST(VN_CAST(nodep, And)->lhsp(), Const); AstNode* fromp = VN_CAST(VN_CAST(nodep, And)->rhsp(), ShiftR)->lhsp()->unlinkFrBack(); - AstConst* shiftConstp = VN_CAST(VN_CAST(VN_CAST(nodep, And)->rhsp(), ShiftR)->rhsp(), Const); + AstConst* shiftConstp = VN_CAST(VN_CAST(VN_CAST(nodep, And)->rhsp(), ShiftR)->rhsp(), + Const); V3Number val (andConstp, andConstp->width()); val.opShiftL(andConstp->num(), shiftConstp->num()); AstAnd* newp = new AstAnd(nodep->fileline(), - new AstConst(nodep->fileline(), val), - fromp); + new AstConst(nodep->fileline(), val), + fromp); // widthMin no longer applicable if different C-expanded width newp->dtypeSetLogicSized(nodep->width(), nodep->width(), AstNumeric::UNSIGNED); - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); - if (debug()>=9) newp->dumpTree(cout," _new: "); + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); + if (debug()>=9) newp->dumpTree(cout, " _new: "); } void replaceWithSimulation(AstNode* nodep) { - SimulateVisitor simvis; - // Run it - may be unoptimizable due to large for loop, etc - simvis.mainParamEmulate(nodep); - if (!simvis.optimizable()) { - AstNode* errorp = simvis.whyNotNodep(); if (!errorp) errorp = nodep; - nodep->v3error("Expecting expression to be constant, but can't determine constant for " - <prettyTypeName()<warnMore()<<"... Location of non-constant " - <prettyTypeName()<<": "<v3fatalSrc("No number returned from simulation"); - // Replace it - replaceNum(nodep,*outnump); VL_DANGLING(nodep); - } + SimulateVisitor simvis; + // Run it - may be unoptimizable due to large for loop, etc + simvis.mainParamEmulate(nodep); + if (!simvis.optimizable()) { + AstNode* errorp = simvis.whyNotNodep(); if (!errorp) errorp = nodep; + nodep->v3error("Expecting expression to be constant, but can't determine constant for " + <prettyTypeName()<warnMore()<<"... Location of non-constant " + <prettyTypeName()<<": "<v3fatalSrc("No number returned from simulation"); + // Replace it + replaceNum(nodep,*outnump); VL_DANGLING(nodep); + } } //---------------------------------------- // VISITORS virtual void visit(AstNetlist* nodep) { - // Iterate modules backwards, in bottom-up order. That's faster + // Iterate modules backwards, in bottom-up order. That's faster iterateChildrenBackwards(nodep); } virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; + m_modp = nodep; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstCFunc* nodep) { - // No ASSIGNW removals under funcs, we've long eliminated INITIALs - // (We should perhaps rename the assignw's to just assigns) - m_wremove = false; + // No ASSIGNW removals under funcs, we've long eliminated INITIALs + // (We should perhaps rename the assignw's to just assigns) + m_wremove = false; iterateChildren(nodep); - m_wremove = true; + m_wremove = true; } virtual void visit(AstScope* nodep) { - // No ASSIGNW removals under scope, we've long eliminated INITIALs - m_scopep = nodep; - m_wremove = false; + // No ASSIGNW removals under scope, we've long eliminated INITIALs + m_scopep = nodep; + m_wremove = false; iterateChildren(nodep); - m_wremove = true; - m_scopep = NULL; + m_wremove = true; + m_scopep = NULL; } void swapSides(AstNodeBiCom* nodep) { - // COMMUNATIVE({a},CONST) -> COMMUNATIVE(CONST,{a}) - // This simplifies later optimizations - AstNode* lhsp = nodep->lhsp()->unlinkFrBackWithNext(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBackWithNext(); - nodep->lhsp(rhsp); - nodep->rhsp(lhsp); + // COMMUNATIVE({a},CONST) -> COMMUNATIVE(CONST,{a}) + // This simplifies later optimizations + AstNode* lhsp = nodep->lhsp()->unlinkFrBackWithNext(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBackWithNext(); + nodep->lhsp(rhsp); + nodep->rhsp(lhsp); iterate(nodep); // Again? } int operandConcatMove(AstConcat* nodep) { - // CONCAT under concat (See moveConcat) - // Return value: true indicates to do it; 2 means move to LHS + // CONCAT under concat (See moveConcat) + // Return value: true indicates to do it; 2 means move to LHS AstConcat* abConcp = VN_CAST(nodep->lhsp(), Concat); AstConcat* bcConcp = VN_CAST(nodep->rhsp(), Concat); - if (!abConcp && !bcConcp) return 0; - if (bcConcp) { - AstNode* ap = nodep->lhsp(); - AstNode* bp = bcConcp->lhsp(); - // If a+b == 32,64,96 etc, then we want to have a+b together on LHS - if (VL_BITBIT_I(ap->width()+bp->width())==0) return 2; // Transform 2: to abConc - } - else { //abConcp - // Unless lhs is already 32 bits due to above, reorder it - if (VL_BITBIT_I(nodep->lhsp()->width())!=0) return 1; // Transform 1: to bcConc - } - return 0; // ok + if (!abConcp && !bcConcp) return 0; + if (bcConcp) { + AstNode* ap = nodep->lhsp(); + AstNode* bp = bcConcp->lhsp(); + // If a+b == 32,64,96 etc, then we want to have a+b together on LHS + if (VL_BITBIT_I(ap->width()+bp->width())==0) return 2; // Transform 2: to abConc + } + else { // abConcp + // Unless lhs is already 32 bits due to above, reorder it + if (VL_BITBIT_I(nodep->lhsp()->width())!=0) return 1; // Transform 1: to bcConc + } + return 0; // ok } void moveConcat(AstConcat* nodep) { - // 1: CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b}, {c})) - // or 2: CONCAT({a}, CONCAT({b},{c})) -> CONCAT(CONCAT({a},{b}),{c}) - // Because the lhs of a concat needs a shift but the rhs doesn't, - // putting additional CONCATs on the RHS leads to fewer assembler operations. - // However, we'll end up with lots of wide moves if we make huge trees - // like that, so on 32 bit boundaries, we'll do the opposite form. - UINFO(4,"Move concat: "<1) { - AstNode* ap = nodep->lhsp()->unlinkFrBack(); + // 1: CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b}, {c})) + // or 2: CONCAT({a}, CONCAT({b},{c})) -> CONCAT(CONCAT({a},{b}),{c}) + // Because the lhs of a concat needs a shift but the rhs doesn't, + // putting additional CONCATs on the RHS leads to fewer assembler operations. + // However, we'll end up with lots of wide moves if we make huge trees + // like that, so on 32 bit boundaries, we'll do the opposite form. + UINFO(4,"Move concat: "<1) { + AstNode* ap = nodep->lhsp()->unlinkFrBack(); AstConcat* bcConcp = VN_CAST(nodep->rhsp(), Concat); bcConcp->unlinkFrBack(); - AstNode* bp = bcConcp->lhsp()->unlinkFrBack(); - AstNode* cp = bcConcp->rhsp()->unlinkFrBack(); - AstConcat* abConcp = new AstConcat(bcConcp->fileline(), - ap, bp); - nodep->lhsp(abConcp); - nodep->rhsp(cp); - // If bp was a concat, then we have this exact same form again! - // Recurse rather then calling node->iterate to prevent 2^n recursion! - if (operandConcatMove(abConcp)) moveConcat(abConcp); - bcConcp->deleteTree(); VL_DANGLING(bcConcp); - } else { + AstNode* bp = bcConcp->lhsp()->unlinkFrBack(); + AstNode* cp = bcConcp->rhsp()->unlinkFrBack(); + AstConcat* abConcp = new AstConcat(bcConcp->fileline(), + ap, bp); + nodep->lhsp(abConcp); + nodep->rhsp(cp); + // If bp was a concat, then we have this exact same form again! + // Recurse rather then calling node->iterate to prevent 2^n recursion! + if (operandConcatMove(abConcp)) moveConcat(abConcp); + bcConcp->deleteTree(); VL_DANGLING(bcConcp); + } else { AstConcat* abConcp = VN_CAST(nodep->lhsp(), Concat); abConcp->unlinkFrBack(); - AstNode* ap = abConcp->lhsp()->unlinkFrBack(); - AstNode* bp = abConcp->rhsp()->unlinkFrBack(); - AstNode* cp = nodep->rhsp()->unlinkFrBack(); - AstConcat* bcConcp = new AstConcat(abConcp->fileline(), - bp, cp); - nodep->lhsp(ap); - nodep->rhsp(bcConcp); - if (operandConcatMove(bcConcp)) moveConcat(bcConcp); - abConcp->deleteTree(); VL_DANGLING(abConcp); - } + AstNode* ap = abConcp->lhsp()->unlinkFrBack(); + AstNode* bp = abConcp->rhsp()->unlinkFrBack(); + AstNode* cp = nodep->rhsp()->unlinkFrBack(); + AstConcat* bcConcp = new AstConcat(abConcp->fileline(), + bp, cp); + nodep->lhsp(ap); + nodep->rhsp(bcConcp); + if (operandConcatMove(bcConcp)) moveConcat(bcConcp); + abConcp->deleteTree(); VL_DANGLING(abConcp); + } } // Special cases - virtual void visit(AstConst* nodep) {} // Already constant + virtual void visit(AstConst* nodep) {} // Already constant virtual void visit(AstCell* nodep) { - if (m_params) { + if (m_params) { iterateAndNextNull(nodep->paramsp()); - } else { + } else { iterateChildren(nodep); - } + } } virtual void visit(AstPin* nodep) { iterateChildren(nodep); } void replaceSelSel(AstSel* nodep) { - // SEL(SEL({x},a,b),c,d) => SEL({x},a+c,d) + // SEL(SEL({x},a,b),c,d) => SEL({x},a+c,d) AstSel* belowp = VN_CAST(nodep->fromp(), Sel); - AstNode* fromp = belowp->fromp()->unlinkFrBack(); - AstNode* widthp = nodep->widthp()->unlinkFrBack(); - AstNode* lsb1p = nodep->lsbp()->unlinkFrBack(); - AstNode* lsb2p = belowp->lsbp()->unlinkFrBack(); - // Eliminate lower range - UINFO(4,"Elim Lower range: "<fromp()->unlinkFrBack(); + AstNode* widthp = nodep->widthp()->unlinkFrBack(); + AstNode* lsb1p = nodep->lsbp()->unlinkFrBack(); + AstNode* lsb2p = belowp->lsbp()->unlinkFrBack(); + // Eliminate lower range + UINFO(4,"Elim Lower range: "<fileline(), + newlsbp = new AstConst(lsb1p->fileline(), VN_CAST(lsb1p, Const)->toUInt() + VN_CAST(lsb2p, Const)->toUInt()); - lsb1p->deleteTree(); VL_DANGLING(lsb1p); - lsb2p->deleteTree(); VL_DANGLING(lsb2p); - } else { - // Width is important, we need the width of the fromp's - // expression, not the potentially smaller lsb1p's width - newlsbp = new AstAdd(lsb1p->fileline(), - lsb2p, new AstExtend(lsb1p->fileline(), lsb1p)); - newlsbp->dtypeFrom(lsb2p); // Unsigned + lsb1p->deleteTree(); VL_DANGLING(lsb1p); + lsb2p->deleteTree(); VL_DANGLING(lsb2p); + } else { + // Width is important, we need the width of the fromp's + // expression, not the potentially smaller lsb1p's width + newlsbp = new AstAdd(lsb1p->fileline(), + lsb2p, new AstExtend(lsb1p->fileline(), lsb1p)); + newlsbp->dtypeFrom(lsb2p); // Unsigned VN_CAST(newlsbp, Add)->rhsp()->dtypeFrom(lsb2p); - } - AstSel* newp = new AstSel(nodep->fileline(), - fromp, - newlsbp, - widthp); - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); + } + AstSel* newp = new AstSel(nodep->fileline(), + fromp, + newlsbp, + widthp); + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); } void replaceSelConcat(AstSel* nodep) { - // SEL(CONCAT(a,b),c,d) => SEL(a or b, . .) + // SEL(CONCAT(a,b),c,d) => SEL(a or b, . .) AstConcat* conp = VN_CAST(nodep->fromp(), Concat); - AstNode* conLhsp = conp->lhsp(); - AstNode* conRhsp = conp->rhsp(); + AstNode* conLhsp = conp->lhsp(); + AstNode* conRhsp = conp->rhsp(); if (static_cast(nodep->lsbConst()) >= conRhsp->width()) { - conLhsp->unlinkFrBack(); - AstSel* newp = new AstSel(nodep->fileline(), - conLhsp, - nodep->lsbConst() - conRhsp->width(), - nodep->widthConst()); - nodep->replaceWith(newp); - } + conLhsp->unlinkFrBack(); + AstSel* newp = new AstSel(nodep->fileline(), + conLhsp, + nodep->lsbConst() - conRhsp->width(), + nodep->widthConst()); + nodep->replaceWith(newp); + } else if (static_cast(nodep->msbConst()) < conRhsp->width()) { - conRhsp->unlinkFrBack(); - AstSel* newp = new AstSel(nodep->fileline(), - conRhsp, - nodep->lsbConst(), - nodep->widthConst()); - nodep->replaceWith(newp); - } - else { - // Yuk, split between the two - conRhsp->unlinkFrBack(); - conLhsp->unlinkFrBack(); - AstConcat* newp = new AstConcat - (nodep->fileline(), - new AstSel(nodep->fileline(), - conLhsp, - 0, - nodep->msbConst() - conRhsp->width() + 1), - new AstSel(nodep->fileline(), - conRhsp, - nodep->lsbConst(), - conRhsp->width()-nodep->lsbConst())); - nodep->replaceWith(newp); - } - nodep->deleteTree(); VL_DANGLING(nodep); + conRhsp->unlinkFrBack(); + AstSel* newp = new AstSel(nodep->fileline(), + conRhsp, + nodep->lsbConst(), + nodep->widthConst()); + nodep->replaceWith(newp); + } + else { + // Yuk, split between the two + conRhsp->unlinkFrBack(); + conLhsp->unlinkFrBack(); + AstConcat* newp = new AstConcat + (nodep->fileline(), + new AstSel(nodep->fileline(), + conLhsp, + 0, + nodep->msbConst() - conRhsp->width() + 1), + new AstSel(nodep->fileline(), + conRhsp, + nodep->lsbConst(), + conRhsp->width()-nodep->lsbConst())); + nodep->replaceWith(newp); + } + nodep->deleteTree(); VL_DANGLING(nodep); } bool operandSelReplicate(AstSel* nodep) { - // SEL(REPLICATE(from,rep),lsb,width) => SEL(from,0,width) as long as SEL's width <= b's width + // SEL(REPLICATE(from,rep),lsb,width) => SEL(from,0,width) as long + // as SEL's width <= b's width AstReplicate* repp = VN_CAST(nodep->fromp(), Replicate); - AstNode* fromp = repp->lhsp(); + AstNode* fromp = repp->lhsp(); AstConst* lsbp = VN_CAST(nodep->lsbp(), Const); if (!lsbp) return false; AstNode* widthp = nodep->widthp(); if (!VN_IS(widthp, Const)) return false; - if (!fromp->width()) nodep->v3fatalSrc("Not widthed"); - if ((lsbp->toUInt() / fromp->width()) - != ((lsbp->toUInt()+nodep->width()-1) / fromp->width())) return false; - // - fromp->unlinkFrBack(); - widthp->unlinkFrBack(); - AstSel* newp = new AstSel(nodep->fileline(), - fromp, - new AstConst(lsbp->fileline(), lsbp->toUInt() % fromp->width()), - widthp); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - return true; + if (!fromp->width()) nodep->v3fatalSrc("Not widthed"); + if ((lsbp->toUInt() / fromp->width()) + != ((lsbp->toUInt()+nodep->width()-1) / fromp->width())) return false; + // + fromp->unlinkFrBack(); + widthp->unlinkFrBack(); + AstSel* newp = new AstSel(nodep->fileline(), + fromp, + new AstConst(lsbp->fileline(), lsbp->toUInt() % fromp->width()), + widthp); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + return true; } bool operandRepRep(AstReplicate* nodep) { - // REPLICATE(REPLICATE2(from2,cnt2),cnt1) => REPLICATE(from2,(cnt1+cnt2)) + // REPLICATE(REPLICATE2(from2,cnt2),cnt1) => REPLICATE(from2,(cnt1+cnt2)) AstReplicate* rep2p = VN_CAST(nodep->lhsp(), Replicate); - AstNode* from2p = rep2p->lhsp(); + AstNode* from2p = rep2p->lhsp(); AstConst* cnt1p = VN_CAST(nodep->rhsp(), Const); if (!cnt1p) return false; AstConst* cnt2p = VN_CAST(rep2p->rhsp(), Const); if (!cnt2p) return false; - // - from2p->unlinkFrBack(); - cnt1p->unlinkFrBack(); - cnt2p->unlinkFrBack(); - AstReplicate* newp = new AstReplicate(nodep->fileline(), - from2p, cnt1p->toUInt()*cnt2p->toUInt()); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - return true; + // + from2p->unlinkFrBack(); + cnt1p->unlinkFrBack(); + cnt2p->unlinkFrBack(); + AstReplicate* newp = new AstReplicate(nodep->fileline(), + from2p, cnt1p->toUInt()*cnt2p->toUInt()); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + return true; } bool operandConcatSame(AstConcat* nodep) { - // CONCAT(fromp,fromp) -> REPLICATE(fromp,1+1) - // CONCAT(REP(fromp,cnt1),fromp) -> REPLICATE(fromp,cnt1+1) - // CONCAT(fromp,REP(fromp,cnt1)) -> REPLICATE(fromp,1+cnt1) - // CONCAT(REP(fromp,cnt1),REP(fromp,cnt2)) -> REPLICATE(fromp,cnt1+cnt2) - AstNode* from1p = nodep->lhsp(); uint32_t cnt1 = 1; - AstNode* from2p = nodep->rhsp(); uint32_t cnt2 = 1; + // CONCAT(fromp,fromp) -> REPLICATE(fromp,1+1) + // CONCAT(REP(fromp,cnt1),fromp) -> REPLICATE(fromp,cnt1+1) + // CONCAT(fromp,REP(fromp,cnt1)) -> REPLICATE(fromp,1+cnt1) + // CONCAT(REP(fromp,cnt1),REP(fromp,cnt2)) -> REPLICATE(fromp,cnt1+cnt2) + AstNode* from1p = nodep->lhsp(); uint32_t cnt1 = 1; + AstNode* from2p = nodep->rhsp(); uint32_t cnt2 = 1; if (VN_IS(from1p, Replicate)) { - AstConst* cnt1p = VN_CAST(VN_CAST(from1p, Replicate)->rhsp(), Const); if (!cnt1p) return false; + AstConst* cnt1p = VN_CAST(VN_CAST(from1p, Replicate)->rhsp(), Const); + if (!cnt1p) return false; from1p = VN_CAST(from1p, Replicate)->lhsp(); - cnt1 = cnt1p->toUInt(); - } + cnt1 = cnt1p->toUInt(); + } if (VN_IS(from2p, Replicate)) { - AstConst* cnt2p = VN_CAST(VN_CAST(from2p, Replicate)->rhsp(), Const); if (!cnt2p) return false; + AstConst* cnt2p = VN_CAST(VN_CAST(from2p, Replicate)->rhsp(), Const); + if (!cnt2p) return false; from2p = VN_CAST(from2p, Replicate)->lhsp(); - cnt2 = cnt2p->toUInt(); - } - if (!operandsSame(from1p,from2p)) return false; - // - from1p->unlinkFrBack(); - AstReplicate* newp = new AstReplicate(nodep->fileline(), from1p, cnt1+cnt2); - newp->dtypeFrom(nodep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - return true; + cnt2 = cnt2p->toUInt(); + } + if (!operandsSame(from1p, from2p)) return false; + // + from1p->unlinkFrBack(); + AstReplicate* newp = new AstReplicate(nodep->fileline(), from1p, cnt1+cnt2); + newp->dtypeFrom(nodep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + return true; } void replaceSelIntoBiop(AstSel* nodep) { - // SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit)) + // SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit)) AstNodeBiop* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), NodeBiop); - if (!fromp) nodep->v3fatalSrc("Called on non biop"); - AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); - AstNode* widthp = nodep->widthp()->unlinkFrBack(); - // - AstNode* bilhsp = fromp->lhsp()->unlinkFrBack(); - AstNode* birhsp = fromp->rhsp()->unlinkFrBack(); - // - fromp->lhsp(new AstSel(nodep->fileline(), - bilhsp, lsbp->cloneTree(true), widthp->cloneTree(true))); - fromp->rhsp(new AstSel(nodep->fileline(), - birhsp, lsbp, widthp)); - fromp->dtypeFrom(nodep); - nodep->replaceWith(fromp); nodep->deleteTree(); VL_DANGLING(nodep); + if (!fromp) nodep->v3fatalSrc("Called on non biop"); + AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); + AstNode* widthp = nodep->widthp()->unlinkFrBack(); + // + AstNode* bilhsp = fromp->lhsp()->unlinkFrBack(); + AstNode* birhsp = fromp->rhsp()->unlinkFrBack(); + // + fromp->lhsp(new AstSel(nodep->fileline(), + bilhsp, lsbp->cloneTree(true), widthp->cloneTree(true))); + fromp->rhsp(new AstSel(nodep->fileline(), + birhsp, lsbp, widthp)); + fromp->dtypeFrom(nodep); + nodep->replaceWith(fromp); nodep->deleteTree(); VL_DANGLING(nodep); } void replaceSelIntoUniop(AstSel* nodep) { - // SEL(NOT(a),1,bit) => NOT(SEL(a,bit)) + // SEL(NOT(a),1,bit) => NOT(SEL(a,bit)) AstNodeUniop* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), NodeUniop); - if (!fromp) nodep->v3fatalSrc("Called on non biop"); - AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); - AstNode* widthp = nodep->widthp()->unlinkFrBack(); - // - AstNode* bilhsp = fromp->lhsp()->unlinkFrBack(); - // - fromp->lhsp(new AstSel(nodep->fileline(), - bilhsp, lsbp, widthp)); - fromp->dtypeFrom(nodep); - nodep->replaceWith(fromp); nodep->deleteTree(); VL_DANGLING(nodep); + if (!fromp) nodep->v3fatalSrc("Called on non biop"); + AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); + AstNode* widthp = nodep->widthp()->unlinkFrBack(); + // + AstNode* bilhsp = fromp->lhsp()->unlinkFrBack(); + // + fromp->lhsp(new AstSel(nodep->fileline(), + bilhsp, lsbp, widthp)); + fromp->dtypeFrom(nodep); + nodep->replaceWith(fromp); nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstAttrOf* nodep) { - AstAttrOf* oldAttr = m_attrp; - m_attrp = nodep; + AstAttrOf* oldAttr = m_attrp; + m_attrp = nodep; iterateChildren(nodep); - m_attrp = oldAttr; + m_attrp = oldAttr; } virtual void visit(AstArraySel* nodep) { iterateAndNextNull(nodep->bitp()); if (VN_IS(nodep->bitp(), Const) && VN_IS(nodep->fromp(), VarRef) - // Need to make sure it's an array object so don't mis-allow a constant (bug509.) + // Need to make sure it's an array object so don't mis-allow a constant (bug509.) && VN_CAST(nodep->fromp(), VarRef)->varp() && VN_IS(VN_CAST(nodep->fromp(), VarRef)->varp()->valuep(), InitArray)) { - m_selp = nodep; // Ask visit(AstVarRef) to replace varref with const - } + m_selp = nodep; // Ask visit(AstVarRef) to replace varref with const + } iterateAndNextNull(nodep->fromp()); if (VN_IS(nodep->fromp(), Const)) { // It did. - if (!m_selp) { - nodep->v3error("Illegal assignment of constant to unpacked array"); - } else { - AstNode* fromp = nodep->fromp()->unlinkFrBack(); - nodep->replaceWith(fromp); + if (!m_selp) { + nodep->v3error("Illegal assignment of constant to unpacked array"); + } else { + AstNode* fromp = nodep->fromp()->unlinkFrBack(); + nodep->replaceWith(fromp); if (VN_IS(fromp->dtypep()->skipRefp(), NodeArrayDType)) { - // Strip off array to find what array references - fromp->dtypeFrom(VN_CAST(fromp->dtypep()->skipRefp(), NodeArrayDType)->subDTypep()); - } - } - } - m_selp = NULL; + // Strip off array to find what array references + fromp->dtypeFrom(VN_CAST(fromp->dtypep()->skipRefp(), + NodeArrayDType)->subDTypep()); + } + } + } + m_selp = NULL; } virtual void visit(AstNodeVarRef* nodep) { iterateChildren(nodep); - if (!nodep->varp()) nodep->v3fatalSrc("Not linked"); - bool did=false; - if (m_doV && nodep->varp()->valuep() && !m_attrp) { - //if (debug()) valuep->dumpTree(cout," visitvaref: "); + if (!nodep->varp()) nodep->v3fatalSrc("Not linked"); + bool did = false; + if (m_doV && nodep->varp()->valuep() && !m_attrp) { + //if (debug()) valuep->dumpTree(cout, " visitvaref: "); iterateAndNextNull(nodep->varp()->valuep()); // May change nodep->varp()->valuep() - AstNode* valuep = nodep->varp()->valuep(); - if (!nodep->lvalue() - && ((!m_params // Can reduce constant wires into equations - && m_doNConst - && v3Global.opt.oConst() + AstNode* valuep = nodep->varp()->valuep(); + if (!nodep->lvalue() + && ((!m_params // Can reduce constant wires into equations + && m_doNConst + && v3Global.opt.oConst() // Default value, not a "known" constant for this usage && !(nodep->varp()->isFuncLocal() && nodep->varp()->isNonOutput()) && !nodep->varp()->noSubst() - && !nodep->varp()->isSigPublic()) - || nodep->varp()->isParam())) { - if (operandConst(valuep)) { + && !nodep->varp()->isSigPublic()) + || nodep->varp()->isParam())) { + if (operandConst(valuep)) { const V3Number& num = VN_CAST(valuep, Const)->num(); //UINFO(2,"constVisit "<bitConst(); - int pos = 0; - AstNode* itemp = initarp->initsp(); - for (; itemp; ++pos, itemp=itemp->nextp()) { - uint32_t index = initarp->posIndex(pos); - if (index == bit) break; - if (index > bit) { - if (initarp->defaultp()) { - itemp = initarp->defaultp(); - } else { - initarp->v3fatalSrc("Not enough values in array initalizement"); - } - } - } + uint32_t bit = m_selp->bitConst(); + int pos = 0; + AstNode* itemp = initarp->initsp(); + for (; itemp; ++pos, itemp=itemp->nextp()) { + uint32_t index = initarp->posIndex(pos); + if (index == bit) break; + if (index > bit) { + if (initarp->defaultp()) { + itemp = initarp->defaultp(); + } else { + initarp->v3fatalSrc("Not enough values in array initalizement"); + } + } + } if (VN_IS(itemp, Const)) { const V3Number& num = VN_CAST(itemp, Const)->num(); //UINFO(2,"constVisit "<backp(), Pin)) { - // Allow parameters to pass arrays - // Earlier recursion of InitArray made sure each array value is constant - // This exception is fairly fragile, i.e. doesn't support arrays of arrays or other stuff - AstNode* newp = valuep->cloneTree(false); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - did = true; - } - } - } - if (!did && m_required) { - nodep->v3error("Expecting expression to be constant, but variable isn't const: "<varp()->prettyName()); - } + // Allow parameters to pass arrays + // Earlier recursion of InitArray made sure each array value is constant + // This exception is fairly fragile, i.e. doesn't + // support arrays of arrays or other stuff + AstNode* newp = valuep->cloneTree(false); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + did = true; + } + } + } + if (!did && m_required) { + nodep->v3error("Expecting expression to be constant, but variable isn't const: " + <varp()->prettyName()); + } } virtual void visit(AstEnumItemRef* nodep) { iterateChildren(nodep); - if (!nodep->itemp()) nodep->v3fatalSrc("Not linked"); - bool did=false; - if (nodep->itemp()->valuep()) { - //if (debug()) nodep->varp()->valuep()->dumpTree(cout," visitvaref: "); + if (!nodep->itemp()) nodep->v3fatalSrc("Not linked"); + bool did = false; + if (nodep->itemp()->valuep()) { + //if (debug()) nodep->varp()->valuep()->dumpTree(cout, " visitvaref: "); iterateAndNextNull(nodep->itemp()->valuep()); if (AstConst* valuep = VN_CAST(nodep->itemp()->valuep(), Const)) { - const V3Number& num = valuep->num(); - replaceNum(nodep, num); VL_DANGLING(nodep); - did=true; - } - } - if (!did && m_required) { - nodep->v3error("Expecting expression to be constant, but variable isn't const: "<itemp()->prettyName()); - } + const V3Number& num = valuep->num(); + replaceNum(nodep, num); VL_DANGLING(nodep); + did = true; + } + } + if (!did && m_required) { + nodep->v3error("Expecting expression to be constant, but variable isn't const: " + <itemp()->prettyName()); + } } // virtual void visit(AstCvtPackString* nodep) { @@ -1610,317 +1631,325 @@ private: // Someday if lower is constant, convert to quoted "string". bool onlySenItemInSenTree(AstNodeSenItem* nodep) { - // Only one if it's not in a list - return (!nodep->nextp() && nodep->backp()->nextp() != nodep); + // Only one if it's not in a list + return (!nodep->nextp() && nodep->backp()->nextp() != nodep); } virtual void visit(AstSenItem* nodep) { iterateChildren(nodep); - if (m_doNConst + if (m_doNConst && (VN_IS(nodep->sensp(), Const) || VN_IS(nodep->sensp(), EnumItemRef) - || (nodep->varrefp() && nodep->varrefp()->varp()->isParam()))) { - // Constants in sensitivity lists may be removed (we'll simplify later) - if (nodep->isClocked()) { // A constant can never get a pos/negexge - if (onlySenItemInSenTree(nodep)) { - nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Never())); - nodep->deleteTree(); VL_DANGLING(nodep); - } else { - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - } else { // Otherwise it may compute a result that needs to settle out - nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Combo())); - nodep->deleteTree(); VL_DANGLING(nodep); - } + || (nodep->varrefp() && nodep->varrefp()->varp()->isParam()))) { + // Constants in sensitivity lists may be removed (we'll simplify later) + if (nodep->isClocked()) { // A constant can never get a pos/negexge + if (onlySenItemInSenTree(nodep)) { + nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Never())); + nodep->deleteTree(); VL_DANGLING(nodep); + } else { + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + } else { // Otherwise it may compute a result that needs to settle out + nodep->replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Combo())); + nodep->deleteTree(); VL_DANGLING(nodep); + } } else if (m_doNConst && VN_IS(nodep->sensp(), Not)) { - // V3Gate may propagate NOTs into clocks... Just deal with it - AstNode* sensp = nodep->sensp(); - AstNode* lastSensp = sensp; - bool invert = false; + // V3Gate may propagate NOTs into clocks... Just deal with it + AstNode* sensp = nodep->sensp(); + AstNode* lastSensp = sensp; + bool invert = false; while (VN_IS(lastSensp, Not)) { lastSensp = VN_CAST(lastSensp, Not)->lhsp(); - invert = !invert; - } - UINFO(8,"senItem(NOT...) "<edgeType( nodep->edgeType().invert() ); + invert = !invert; + } + UINFO(8,"senItem(NOT...) "<edgeType( nodep->edgeType().invert() ); AstNodeVarRef* senvarp = VN_CAST(lastSensp->unlinkFrBack(), NodeVarRef); - if (!senvarp) sensp->v3fatalSrc("Non-varref sensitivity variable"); - sensp->replaceWith(senvarp); - sensp->deleteTree(); VL_DANGLING(sensp); - } else if (!m_doNConst // Deal with later when doNConst missing + if (!senvarp) sensp->v3fatalSrc("Non-varref sensitivity variable"); + sensp->replaceWith(senvarp); + sensp->deleteTree(); VL_DANGLING(sensp); + } else if (!m_doNConst // Deal with later when doNConst missing && (VN_IS(nodep->sensp(), EnumItemRef) || VN_IS(nodep->sensp(), Const))) { - } else if (nodep->isIllegal()) { // Deal with later - } else { - if (nodep->hasVar() && !nodep->varrefp()) nodep->v3fatalSrc("Null sensitivity variable"); - } + } else if (nodep->isIllegal()) { // Deal with later + } else { + if (nodep->hasVar() && !nodep->varrefp()) nodep->v3fatalSrc("Null sensitivity variable"); + } } virtual void visit(AstSenGate* nodep) { iterateChildren(nodep); if (AstConst* constp = VN_CAST(nodep->rhsp(), Const)) { - if (constp->isZero()) { - UINFO(4,"SENGATE(...,0)->NEVER"<replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Never())); - nodep->deleteTree(); VL_DANGLING(nodep); - } else { - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - } else { - UINFO(4,"SENGATE(SENITEM,0)->ALWAYS SENITEM"<sensesp()->unlinkFrBack(); - nodep->replaceWith(senitemp); - nodep->deleteTree(); VL_DANGLING(nodep); - } - } + if (constp->isZero()) { + UINFO(4,"SENGATE(...,0)->NEVER"<replaceWith(new AstSenItem(nodep->fileline(), AstSenItem::Never())); + nodep->deleteTree(); VL_DANGLING(nodep); + } else { + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + } else { + UINFO(4,"SENGATE(SENITEM,0)->ALWAYS SENITEM"<sensesp()->unlinkFrBack(); + nodep->replaceWith(senitemp); + nodep->deleteTree(); VL_DANGLING(nodep); + } + } } struct SenItemCmp { inline bool operator() (AstNodeSenItem* lhsp, AstNodeSenItem* rhsp) const { - if (lhsp->type() < rhsp->type()) return true; - if (lhsp->type() > rhsp->type()) return false; + if (lhsp->type() < rhsp->type()) return true; + if (lhsp->type() > rhsp->type()) return false; const AstSenItem* litemp = VN_CAST_CONST(lhsp, SenItem); const AstSenItem* ritemp = VN_CAST_CONST(rhsp, SenItem); - if (litemp && ritemp) { - // Looks visually better if we keep sorted by name - if (!litemp->varrefp() && ritemp->varrefp()) return true; - if ( litemp->varrefp() && !ritemp->varrefp()) return false; - if (litemp->varrefp() && ritemp->varrefp()) { - if (litemp->varrefp()->name() < ritemp->varrefp()->name()) return true; - if (litemp->varrefp()->name() > ritemp->varrefp()->name()) return false; - // But might be same name with different scopes - if (litemp->varrefp()->varScopep() < ritemp->varrefp()->varScopep()) return true; - if (litemp->varrefp()->varScopep() > ritemp->varrefp()->varScopep()) return false; - // Or rarely, different data types - if (litemp->varrefp()->dtypep() < ritemp->varrefp()->dtypep()) return true; - if (litemp->varrefp()->dtypep() > ritemp->varrefp()->dtypep()) return false; - } - // Sort by edge, AFTER variable, as we want multiple edges for same var adjacent - // note the SenTree optimizer requires this order (more general first, less general last) - if (litemp->edgeType() < ritemp->edgeType()) return true; - if (litemp->edgeType() > ritemp->edgeType()) return false; - } - return false; - } + if (litemp && ritemp) { + // Looks visually better if we keep sorted by name + if (!litemp->varrefp() && ritemp->varrefp()) return true; + if ( litemp->varrefp() && !ritemp->varrefp()) return false; + if (litemp->varrefp() && ritemp->varrefp()) { + if (litemp->varrefp()->name() < ritemp->varrefp()->name()) return true; + if (litemp->varrefp()->name() > ritemp->varrefp()->name()) return false; + // But might be same name with different scopes + if (litemp->varrefp()->varScopep() + < ritemp->varrefp()->varScopep()) return true; + if (litemp->varrefp()->varScopep() + > ritemp->varrefp()->varScopep()) return false; + // Or rarely, different data types + if (litemp->varrefp()->dtypep() < ritemp->varrefp()->dtypep()) return true; + if (litemp->varrefp()->dtypep() > ritemp->varrefp()->dtypep()) return false; + } + // Sort by edge, AFTER variable, as we want multiple edges for same var adjacent. + // note the SenTree optimizer requires this order (more + // general first, less general last) + if (litemp->edgeType() < ritemp->edgeType()) return true; + if (litemp->edgeType() > ritemp->edgeType()) return false; + } + return false; + } }; virtual void visit(AstSenTree* nodep) { iterateChildren(nodep); - if (m_doExpensive) { - //cout<dumpTree(cout,"ssin: "); - // Optimize ideas for the future: - // SENTREE(... SENGATE(x,a), SENGATE(SENITEM(x),b) ...) => SENGATE(x,OR(a,b)) + if (m_doExpensive) { + //cout<dumpTree(cout, "ssin: "); + // Optimize ideas for the future: + // SENTREE(... SENGATE(x,a), SENGATE(SENITEM(x),b) ...) => SENGATE(x,OR(a,b)) - // SENTREE(... SENITEM(x), SENGATE(SENITEM(x),*) ...) => SENITEM(x) - // Do we need the SENITEM's to be identical? No because we're - // ORing between them; we just need to insure that the result is at - // least as frequently activating. So we simply - // SENGATE(SENITEM(x)) -> SENITEM(x), then let it collapse with the - // other SENITEM(x). - { - AstUser4InUse m_inuse4; - // Mark x in SENITEM(x) + // SENTREE(... SENITEM(x), SENGATE(SENITEM(x),*) ...) => SENITEM(x) + // Do we need the SENITEM's to be identical? No because we're + // ORing between them; we just need to insure that the result is at + // least as frequently activating. So we simply + // SENGATE(SENITEM(x)) -> SENITEM(x), then let it collapse with the + // other SENITEM(x). + { + AstUser4InUse m_inuse4; + // Mark x in SENITEM(x) for (AstNodeSenItem* senp = VN_CAST(nodep->sensesp(), NodeSenItem); - senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { + senp; senp = VN_CAST(senp->nextp(), NodeSenItem)) { if (AstSenItem* itemp = VN_CAST(senp, SenItem)) { - if (itemp->varrefp() && itemp->varrefp()->varScopep()) { - itemp->varrefp()->varScopep()->user4(1); - } - } - } - // Find x in SENTREE(SENITEM(x)) + if (itemp->varrefp() && itemp->varrefp()->varScopep()) { + itemp->varrefp()->varScopep()->user4(1); + } + } + } + // Find x in SENTREE(SENITEM(x)) for (AstNodeSenItem* nextp, * senp = VN_CAST(nodep->sensesp(), NodeSenItem); - senp; senp=nextp) { - nextp=VN_CAST(senp->nextp(), NodeSenItem); + senp; senp = nextp) { + nextp = VN_CAST(senp->nextp(), NodeSenItem); if (AstSenGate* gatep = VN_CAST(senp, SenGate)) { if (AstSenItem* itemp = VN_CAST(gatep->sensesp(), SenItem)) { - if (itemp->varrefp() && itemp->varrefp()->varScopep()) { - if (itemp->varrefp()->varScopep()->user4()) { - // Found, push this item up to the top - itemp->unlinkFrBack(); - nodep->addSensesp(itemp); - gatep->unlinkFrBack()->deleteTree(); VL_DANGLING(gatep); VL_DANGLING(senp); - } - } - } - } - } - } + if (itemp->varrefp() && itemp->varrefp()->varScopep()) { + if (itemp->varrefp()->varScopep()->user4()) { + // Found, push this item up to the top + itemp->unlinkFrBack(); + nodep->addSensesp(itemp); + gatep->unlinkFrBack()->deleteTree(); VL_DANGLING(gatep); VL_DANGLING(senp); + } + } + } + } + } + } - // Sort the sensitivity names so "posedge a or b" and "posedge b or a" end up together. - // Also, remove duplicate assignments, and fold POS&NEGs into ANYEDGEs - // Make things a little faster; check first if we need a sort + // Sort the sensitivity names so "posedge a or b" and "posedge b or a" end up together. + // Also, remove duplicate assignments, and fold POS&NEGs into ANYEDGEs + // Make things a little faster; check first if we need a sort for (AstNodeSenItem* nextp, * senp = VN_CAST(nodep->sensesp(), NodeSenItem); - senp; senp=nextp) { - nextp=VN_CAST(senp->nextp(), NodeSenItem); - // cppcheck-suppress unassignedVariable // cppcheck bug - SenItemCmp cmp; - if (nextp && !cmp(senp, nextp)) { - // Something's out of order, sort it - senp = NULL; + senp; senp = nextp) { + nextp = VN_CAST(senp->nextp(), NodeSenItem); + // cppcheck-suppress unassignedVariable // cppcheck bug + SenItemCmp cmp; + if (nextp && !cmp(senp, nextp)) { + // Something's out of order, sort it + senp = NULL; std::vector vec; for (AstNodeSenItem* senp = VN_CAST(nodep->sensesp(), NodeSenItem); - senp; senp=VN_CAST(senp->nextp(), NodeSenItem)) { - vec.push_back(senp); - } - stable_sort(vec.begin(), vec.end(), SenItemCmp()); - for (std::vector::iterator it=vec.begin(); it!=vec.end(); ++it) { - (*it)->unlinkFrBack(); - } - for (std::vector::iterator it=vec.begin(); it!=vec.end(); ++it) { - nodep->addSensesp(*it); - } - break; - } - } + senp; senp = VN_CAST(senp->nextp(), NodeSenItem)) { + vec.push_back(senp); + } + stable_sort(vec.begin(), vec.end(), SenItemCmp()); + for (std::vector::iterator it=vec.begin(); + it!=vec.end(); ++it) { + (*it)->unlinkFrBack(); + } + for (std::vector::iterator it=vec.begin(); + it!=vec.end(); ++it) { + nodep->addSensesp(*it); + } + break; + } + } - // Pass2, remove dup edges + // Pass2, remove dup edges for (AstNodeSenItem* nextp, * senp = VN_CAST(nodep->sensesp(), NodeSenItem); - senp; senp=nextp) { - nextp=VN_CAST(senp->nextp(), NodeSenItem); - AstNodeSenItem* cmpp = nextp; + senp; senp = nextp) { + nextp = VN_CAST(senp->nextp(), NodeSenItem); + AstNodeSenItem* cmpp = nextp; AstSenItem* litemp = VN_CAST(senp, SenItem); AstSenItem* ritemp = VN_CAST(cmpp, SenItem); - if (litemp && ritemp) { - if ((litemp->varrefp() && ritemp->varrefp() && litemp->varrefp()->sameGateTree(ritemp->varrefp())) - || (!litemp->varrefp() && !ritemp->varrefp())) { - // We've sorted in the order ANY, BOTH, POS, NEG, so we don't need to try opposite orders - if (( litemp->edgeType()==AstEdgeType::ET_ANYEDGE) // ANY or {BOTH|POS|NEG} -> ANY - || (litemp->edgeType()==AstEdgeType::ET_BOTHEDGE) // BOTH or {POS|NEG} -> BOTH - || (litemp->edgeType()==AstEdgeType::ET_POSEDGE // POS or NEG -> BOTH - && ritemp->edgeType()==AstEdgeType::ET_NEGEDGE) - || (litemp->edgeType()==ritemp->edgeType()) // Identical edges - ) { - // Fix edge of old node - if (litemp->edgeType()==AstEdgeType::ET_POSEDGE - && ritemp->edgeType()==AstEdgeType::ET_NEGEDGE) - litemp->edgeType(AstEdgeType::ET_BOTHEDGE); - // Remove redundant node - ritemp->unlinkFrBack()->deleteTree(); VL_DANGLING(ritemp); VL_DANGLING(cmpp); - // Try to collapse again - nextp=litemp; - } - } - } - } - //nodep->dumpTree(cout,"ssou: "); - } + if (litemp && ritemp) { + if ((litemp->varrefp() && ritemp->varrefp() + && litemp->varrefp()->sameGateTree(ritemp->varrefp())) + || (!litemp->varrefp() && !ritemp->varrefp())) { + // We've sorted in the order ANY, BOTH, POS, NEG, + // so we don't need to try opposite orders + if (( litemp->edgeType()==AstEdgeType::ET_ANYEDGE) // ANY or {BOTH|POS|NEG} -> ANY + || (litemp->edgeType()==AstEdgeType::ET_BOTHEDGE) // BOTH or {POS|NEG} -> BOTH + || (litemp->edgeType()==AstEdgeType::ET_POSEDGE // POS or NEG -> BOTH + && ritemp->edgeType()==AstEdgeType::ET_NEGEDGE) + || (litemp->edgeType()==ritemp->edgeType()) // Identical edges + ) { + // Fix edge of old node + if (litemp->edgeType()==AstEdgeType::ET_POSEDGE + && ritemp->edgeType()==AstEdgeType::ET_NEGEDGE) + litemp->edgeType(AstEdgeType::ET_BOTHEDGE); + // Remove redundant node + ritemp->unlinkFrBack()->deleteTree(); VL_DANGLING(ritemp); VL_DANGLING(cmpp); + // Try to collapse again + nextp = litemp; + } + } + } + } + //nodep->dumpTree(cout, "ssou: "); + } } //----- // Zero elimination virtual void visit(AstNodeAssign* nodep) { iterateChildren(nodep); - if (m_doNConst && replaceNodeAssign(nodep)) return; + if (m_doNConst && replaceNodeAssign(nodep)) return; } virtual void visit(AstAssignAlias* nodep) { - // Don't perform any optimizations, keep the alias around + // Don't perform any optimizations, keep the alias around } virtual void visit(AstAssignVarScope* nodep) { - // Don't perform any optimizations, the node won't be linked yet + // Don't perform any optimizations, the node won't be linked yet } virtual void visit(AstAssignW* nodep) { iterateChildren(nodep); - if (m_doNConst && replaceNodeAssign(nodep)) return; + if (m_doNConst && replaceNodeAssign(nodep)) return; AstNodeVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef); // Not VarXRef, as different refs may set different values to each hierarchy - if (m_wremove && !m_params && m_doNConst - && m_modp && operandConst(nodep->rhsp()) + if (m_wremove && !m_params && m_doNConst + && m_modp && operandConst(nodep->rhsp()) && !VN_CAST(nodep->rhsp(), Const)->num().isFourState() - && varrefp // Don't do messes with BITREFs/ARRAYREFs - && !varrefp->varp()->valuep() // Not already constified - && !varrefp->varScopep()) { // Not scoped (or each scope may have different initial value) - // ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) ) - UINFO(4,"constAssignW "<rhsp()->unlinkFrBack(); - varrefp->unlinkFrBack(); - AstInitial* newinitp - = new AstInitial(nodep->fileline(), - new AstAssign(nodep->fileline(), - varrefp, exprp)); - m_modp->addStmtp(newinitp); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - // Set the initial value right in the variable so we can constant propagate - AstNode* initvaluep = exprp->cloneTree(false); - varrefp->varp()->valuep(initvaluep); - } + && varrefp // Don't do messes with BITREFs/ARRAYREFs + && !varrefp->varp()->valuep() // Not already constified + && !varrefp->varScopep()) { // Not scoped (or each scope may have different initial value) + // ASSIGNW (VARREF, const) -> INITIAL ( ASSIGN (VARREF, const) ) + UINFO(4,"constAssignW "<rhsp()->unlinkFrBack(); + varrefp->unlinkFrBack(); + AstInitial* newinitp + = new AstInitial(nodep->fileline(), + new AstAssign(nodep->fileline(), + varrefp, exprp)); + m_modp->addStmtp(newinitp); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // Set the initial value right in the variable so we can constant propagate + AstNode* initvaluep = exprp->cloneTree(false); + varrefp->varp()->valuep(initvaluep); + } } virtual void visit(AstNodeIf* nodep) { iterateChildren(nodep); - if (m_doNConst) { + if (m_doNConst) { if (const AstConst* constp = VN_CAST(nodep->condp(), Const)) { - AstNode* keepp = NULL; - if (constp->isZero()) { - UINFO(4,"IF(0,{any},{x}) => {x}: "<elsesp(); - } else { - UINFO(4,"IF(!0,{x},{any}) => {x}: "<ifsp(); - } - if (keepp) { - keepp->unlinkFrBackWithNext(); - nodep->replaceWith(keepp); - } else { - nodep->unlinkFrBack(); - } - nodep->deleteTree(); VL_DANGLING(nodep); - } - else if (!afterComment(nodep->ifsp()) && !afterComment(nodep->elsesp())) { - // Empty block, remove it - // Note if we support more C++ then there might be side effects in the condition itself - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (!afterComment(nodep->ifsp())) { - UINFO(4,"IF({x}) NULL {...} => IF(NOT{x}}: "<condp(); - AstNode* elsesp = nodep->elsesp(); - condp->unlinkFrBackWithNext(); - elsesp->unlinkFrBackWithNext(); - if (nodep->ifsp()) { nodep->ifsp()->unlinkFrBackWithNext()->deleteTree(); } // Must have been comment - nodep->condp(new AstLogNot(condp->fileline(), condp)); // LogNot, as C++ optimization also possible - nodep->addIfsp(elsesp); - } + AstNode* keepp = NULL; + if (constp->isZero()) { + UINFO(4,"IF(0,{any},{x}) => {x}: "<elsesp(); + } else { + UINFO(4,"IF(!0,{x},{any}) => {x}: "<ifsp(); + } + if (keepp) { + keepp->unlinkFrBackWithNext(); + nodep->replaceWith(keepp); + } else { + nodep->unlinkFrBack(); + } + nodep->deleteTree(); VL_DANGLING(nodep); + } + else if (!afterComment(nodep->ifsp()) && !afterComment(nodep->elsesp())) { + // Empty block, remove it + // Note if we support more C++ then there might be side + // effects in the condition itself + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (!afterComment(nodep->ifsp())) { + UINFO(4,"IF({x}) NULL {...} => IF(NOT{x}}: "<condp(); + AstNode* elsesp = nodep->elsesp(); + condp->unlinkFrBackWithNext(); + elsesp->unlinkFrBackWithNext(); + if (nodep->ifsp()) { nodep->ifsp()->unlinkFrBackWithNext()->deleteTree(); } // Must have been comment + nodep->condp(new AstLogNot(condp->fileline(), condp)); // LogNot, as C++ optimization also possible + nodep->addIfsp(elsesp); + } else if (((VN_IS(nodep->condp(), Not) && nodep->condp()->width()==1) || VN_IS(nodep->condp(), LogNot)) - && nodep->ifsp() && nodep->elsesp()) { - UINFO(4,"IF(NOT {x}) => IF(x) swapped if/else"<ifsp() && nodep->elsesp()) { + UINFO(4,"IF(NOT {x}) => IF(x) swapped if/else"<condp(), Not)->lhsp()->unlinkFrBackWithNext(); - AstNode* ifsp = nodep->ifsp()->unlinkFrBackWithNext(); - AstNode* elsesp = nodep->elsesp()->unlinkFrBackWithNext(); - AstIf* ifp = new AstIf(nodep->fileline(), condp, elsesp, ifsp); - ifp->branchPred(nodep->branchPred().invert()); - nodep->replaceWith(ifp); - nodep->deleteTree(); VL_DANGLING(nodep); - } - else if (ifSameAssign(nodep)) { - UINFO(4,"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})"<ifsp()->unlinkFrBackWithNext(); + AstNode* elsesp = nodep->elsesp()->unlinkFrBackWithNext(); + AstIf* ifp = new AstIf(nodep->fileline(), condp, elsesp, ifsp); + ifp->branchPred(nodep->branchPred().invert()); + nodep->replaceWith(ifp); + nodep->deleteTree(); VL_DANGLING(nodep); + } + else if (ifSameAssign(nodep)) { + UINFO(4,"IF({a}) ASSIGN({b},{c}) else ASSIGN({b},{d}) => ASSIGN({b}, {a}?{c}:{d})"<ifsp(), NodeAssign); AstNodeAssign* elsep = VN_CAST(nodep->elsesp(), NodeAssign); - ifp->unlinkFrBack(); - AstNode* condp = nodep->condp()->unlinkFrBack(); - AstNode* truep = ifp->rhsp()->unlinkFrBack(); - AstNode* falsep = elsep->rhsp()->unlinkFrBack(); - ifp->rhsp(new AstCond(truep->fileline(), - condp, truep, falsep)); - nodep->replaceWith(ifp); - nodep->deleteTree(); VL_DANGLING(nodep); - } - else if (0 // Disabled, as vpm assertions are faster without due to short-circuiting - && operandIfIf(nodep)) { - UINFO(9,"IF({a}) IF({b}) => IF({a} && {b})"<unlinkFrBack(); + AstNode* condp = nodep->condp()->unlinkFrBack(); + AstNode* truep = ifp->rhsp()->unlinkFrBack(); + AstNode* falsep = elsep->rhsp()->unlinkFrBack(); + ifp->rhsp(new AstCond(truep->fileline(), + condp, truep, falsep)); + nodep->replaceWith(ifp); + nodep->deleteTree(); VL_DANGLING(nodep); + } + else if (0 // Disabled, as vpm assertions are faster without due to short-circuiting + && operandIfIf(nodep)) { + UINFO(9,"IF({a}) IF({b}) => IF({a} && {b})"<ifsp(), NodeIf); - AstNode* condp = nodep->condp()->unlinkFrBack(); - AstNode* lowerIfsp = lowerIfp->ifsp()->unlinkFrBackWithNext(); - AstNode* lowerCondp = lowerIfp->condp()->unlinkFrBackWithNext(); - nodep->condp(new AstLogAnd(lowerIfp->fileline(), - condp, lowerCondp)); - lowerIfp->replaceWith(lowerIfsp); - lowerIfp->deleteTree(); VL_DANGLING(lowerIfp); - } - else if (operandBoolShift(nodep->condp())) { - replaceBoolShift(nodep->condp()); - } - } + AstNode* condp = nodep->condp()->unlinkFrBack(); + AstNode* lowerIfsp = lowerIfp->ifsp()->unlinkFrBackWithNext(); + AstNode* lowerCondp = lowerIfp->condp()->unlinkFrBackWithNext(); + nodep->condp(new AstLogAnd(lowerIfp->fileline(), + condp, lowerCondp)); + lowerIfp->replaceWith(lowerIfsp); + lowerIfp->deleteTree(); VL_DANGLING(lowerIfp); + } + else if (operandBoolShift(nodep->condp())) { + replaceBoolShift(nodep->condp()); + } + } } virtual void visit(AstDisplay* nodep) { @@ -1969,76 +1998,76 @@ private: return true; } virtual void visit(AstSFormatF* nodep) { - // Substitute constants into displays. The main point of this is to - // simplify assertion methodologies which call functions with display's. - // This eliminates a pile of wide temps, and makes the C a whole lot more readable. + // Substitute constants into displays. The main point of this is to + // simplify assertion methodologies which call functions with display's. + // This eliminates a pile of wide temps, and makes the C a whole lot more readable. iterateChildren(nodep); - bool anyconst = false; - for (AstNode* argp = nodep->exprsp(); argp; argp=argp->nextp()) { - if (VN_IS(argp, Const)) { anyconst=true; break; } - } - if (m_doNConst && anyconst) { - //UINFO(9," Display in "<text()<exprsp(); argp; argp=argp->nextp()) { + if (VN_IS(argp, Const)) { anyconst = true; break; } + } + if (m_doNConst && anyconst) { + //UINFO(9," Display in "<text()<exprsp(); - string text = nodep->text(); - for (string::const_iterator it = text.begin(); it!=text.end(); ++it) { - char ch = *it; - if (!inPct && ch=='%') { - inPct = true; - fmt = ch; - } else if (inPct && (isdigit(ch) || ch=='.')) { - fmt += ch; - } else if (inPct) { - inPct = false; - fmt += ch; - switch (tolower(ch)) { - case '%': break; // %% - just output a % - case 'm': break; // %m - auto insert "name" - case 'l': break; // %l - auto insert "library" - default: // Most operators, just move to next argument - if (argp) { - AstNode* nextp=argp->nextp(); + bool inPct = false; + AstNode* argp = nodep->exprsp(); + string text = nodep->text(); + for (string::const_iterator it = text.begin(); it!=text.end(); ++it) { + char ch = *it; + if (!inPct && ch=='%') { + inPct = true; + fmt = ch; + } else if (inPct && (isdigit(ch) || ch=='.')) { + fmt += ch; + } else if (inPct) { + inPct = false; + fmt += ch; + switch (tolower(ch)) { + case '%': break; // %% - just output a % + case 'm': break; // %m - auto insert "name" + case 'l': break; // %l - auto insert "library" + default: // Most operators, just move to next argument + if (argp) { + AstNode* nextp = argp->nextp(); if (argp && VN_IS(argp, Const)) { // Convert it string out = VN_CAST(argp, Const)->num().displayed(nodep, fmt); UINFO(9," DispConst: "< "<unlinkFrBack()->deleteTree(); VL_DANGLING(argp); - } - argp=nextp; - } - break; - } // switch - newFormat += fmt; - } else { - newFormat += ch; - } - } - if (newFormat != nodep->text()) { - nodep->text(newFormat); - UINFO(9," Display out "<exprsp() + fmt = VString::quotePercent(out); + argp->unlinkFrBack()->deleteTree(); VL_DANGLING(argp); + } + argp = nextp; + } + break; + } // switch + newFormat += fmt; + } else { + newFormat += ch; + } + } + if (newFormat != nodep->text()) { + nodep->text(newFormat); + UINFO(9," Display out "<exprsp() && nodep->name().find('%') == string::npos - && !nodep->hidden()) { - // Just a simple constant string - the formatting is pointless - replaceConstString(nodep, nodep->name()); VL_DANGLING(nodep); - } + && !nodep->hidden()) { + // Just a simple constant string - the formatting is pointless + replaceConstString(nodep, nodep->name()); VL_DANGLING(nodep); + } } virtual void visit(AstFuncRef* nodep) { iterateChildren(nodep); - if (m_params) { // Only parameters force us to do constant function call propagation - replaceWithSimulation(nodep); - } + if (m_params) { // Only parameters force us to do constant function call propagation + replaceWithSimulation(nodep); + } } virtual void visit(AstArg* nodep) { - // replaceWithSimulation on the Arg's parent FuncRef replaces these + // replaceWithSimulation on the Arg's parent FuncRef replaces these iterateChildren(nodep); } virtual void visit(AstWhile* nodep) { @@ -2049,45 +2078,45 @@ private: } bool thisWhileHasJumpGo = m_hasJumpGo; m_hasJumpGo = thisWhileHasJumpGo || oldHasJumpGo; - if (m_doNConst) { - if (nodep->condp()->isZero()) { - UINFO(4,"WHILE(0) => nop "<precondsp()) nodep->replaceWith(nodep->precondsp()); - else nodep->unlinkFrBack(); - nodep->deleteTree(); VL_DANGLING(nodep); - } + if (m_doNConst) { + if (nodep->condp()->isZero()) { + UINFO(4,"WHILE(0) => nop "<precondsp()) nodep->replaceWith(nodep->precondsp()); + else nodep->unlinkFrBack(); + nodep->deleteTree(); VL_DANGLING(nodep); + } else if (nodep->condp()->isNeqZero()) { if (!thisWhileHasJumpGo) { nodep->v3warn(INFINITELOOP, "Infinite loop (condition always true)"); nodep->fileline()->modifyWarnOff(V3ErrorCode::INFINITELOOP, true); // Complain just once } } - else if (operandBoolShift(nodep->condp())) { - replaceBoolShift(nodep->condp()); - } - } + else if (operandBoolShift(nodep->condp())) { + replaceBoolShift(nodep->condp()); + } + } } virtual void visit(AstInitArray* nodep) { - // Constant if all children are constant + // Constant if all children are constant iterateChildren(nodep); } - - // These are converted by V3Param. Don't constify as we don't want the from() VARREF to disappear, if any + // These are converted by V3Param. Don't constify as we don't want the + // from() VARREF to disappear, if any. // If output of a presel didn't get consted, chances are V3Param didn't visit properly virtual void visit(AstNodePreSel* nodep) {} // Ignored, can eliminate early virtual void visit(AstSysIgnore* nodep) { iterateChildren(nodep); - if (m_doNConst) { - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } + if (m_doNConst) { + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } } // Simplify virtual void visit(AstBasicDType* nodep) { iterateChildren(nodep); - nodep->cvtRangeConst(); + nodep->cvtRangeConst(); } //----- @@ -2096,23 +2125,23 @@ private: virtual void visit(AstJumpGo* nodep) { iterateChildren(nodep); m_hasJumpGo = true; - if (m_doExpensive) { nodep->labelp()->user4(true); } + if (m_doExpensive) { nodep->labelp()->user4(true); } } virtual void visit(AstJumpLabel* nodep) { - // 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 + // 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()) { - UINFO(4,"JUMPLABEL => unused "<stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext(); - if (underp) nodep->replaceWith(underp); - else nodep->unlinkFrBack(); - nodep->deleteTree(); VL_DANGLING(nodep); - } + // AstJumpGo's below here that point to this node will set user4 + if (m_doExpensive && !nodep->user4()) { + UINFO(4,"JUMPLABEL => unused "<stmtsp()) underp = nodep->stmtsp()->unlinkFrBackWithNext(); + if (underp) nodep->replaceWith(underp); + else nodep->unlinkFrBack(); + nodep->deleteTree(); VL_DANGLING(nodep); + } } //----- @@ -2127,18 +2156,18 @@ private: // $accessor_name, ... // # .castFoo is the test VN_IS(object,Foo) // # ,, gets replaced with a , rather than && - // }" # bracket not paren + // }" # bracket not paren // ,"what to call" // // Where "what_to_call" is: - // "function to call" - // "AstREPLACEMENT_TYPE{ $accessor }" - // "! # Print line number when matches, so can see operations - // "NEVER" # Print error message - // "DONE" # Process of matching did the transform already + // "function to call" + // "AstREPLACEMENT_TYPE{ $accessor }" + // "! # Print line number when matches, so can see operations + // "NEVER" # Print error message + // "DONE" # Process of matching did the transform already // In the future maybe support more complicated match & replace: - // ("AstOr {%a, AstAnd{AstNot{%b}, %c}} if %a.width1 if %a==%b", "AstOr{%a,%c}; %b.delete"); + // ("AstOr {%a, AstAnd{AstNot{%b}, %c}} if %a.width1 if %a==%b", "AstOr{%a,%c}; %b.delete"); // Lhs/rhs would be implied; for non math operations you'd need $lhsp etc. // Lint Checks @@ -2147,70 +2176,70 @@ private: // v--- *C* This op works on all constant children, allowed in m_doConst mode // v--- *S* This op specifies a type should use short-circuting of its lhs op - TREEOP1("AstSel{warnSelect(nodep)}", "NEVER"); + TREEOP1("AstSel{warnSelect(nodep)}", "NEVER"); // Generic constants on both side. Do this first to avoid other replacements TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)"); TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)"); // Zero on one side or the other - TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure + TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure // This visit function here must allow for short-circuiting. - TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)"); - TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); + TREEOPS("AstLogAnd {$lhsp.isZero}", "replaceZero(nodep)"); + TREEOP ("AstLogAnd{$lhsp.isZero, $rhsp}", "replaceZero(nodep)"); // This visit function here must allow for short-circuiting. - TREEOPS("AstLogOr {$lhsp.isOne}", "replaceNum(nodep, 1)"); - TREEOP ("AstLogOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstDiv {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstDivS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstMul {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstMulS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstPow {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule - TREEOP ("AstPowSS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule - TREEOP ("AstPowSU {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule - TREEOP ("AstPowUS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule - TREEOP ("AstPow {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstPowUS {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); - TREEOP ("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstXnor {$lhsp.isZero, $rhsp}", "AstNot{$rhsp}"); - TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstNegate{$rhsp}"); - TREEOP ("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); - TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); - TREEOP ("AstLogOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstMul {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); - TREEOP ("AstMulS {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); - TREEOP ("AstOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstShiftL{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstShiftR{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstShiftRS{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstSub {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstXor {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOP ("AstXnor {$lhsp, $rhsp.isZero}", "AstNot{$lhsp}"); + TREEOPS("AstLogOr {$lhsp.isOne}", "replaceNum(nodep, 1)"); + TREEOP ("AstLogOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstDiv {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstDivS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstMul {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstMulS {$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstPow {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule + TREEOP ("AstPowSS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule + TREEOP ("AstPowSU {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule + TREEOP ("AstPowUS {$rhsp.isZero}", "replaceNum(nodep, 1)"); // Overrides lhs zero rule + TREEOP ("AstPow {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstPowUS {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstPowSU {$lhsp.isZero, !$rhsp.isZero}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstOr {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstShiftL{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstShiftR{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstShiftRS{$lhsp.isZero, $rhsp}", "replaceZeroChkPure(nodep,$rhsp)"); + TREEOP ("AstXor {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstXnor {$lhsp.isZero, $rhsp}", "AstNot{$rhsp}"); + TREEOP ("AstSub {$lhsp.isZero, $rhsp}", "AstNegate{$rhsp}"); + TREEOP ("AstAdd {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstAnd {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); + TREEOP ("AstLogAnd{$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); + TREEOP ("AstLogOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstMul {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); + TREEOP ("AstMulS {$lhsp, $rhsp.isZero}", "replaceZeroChkPure(nodep,$lhsp)"); + TREEOP ("AstOr {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstShiftL{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstShiftR{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstShiftRS{$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstSub {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstXor {$lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOP ("AstXnor {$lhsp, $rhsp.isZero}", "AstNot{$lhsp}"); // Non-zero on one side or the other - TREEOP ("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, isTPure($rhsp)}", "replaceWLhs(nodep)"); //->allOnes - TREEOP ("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)"); - TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); - TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)"); - TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); //->allOnes - TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); - TREEOP ("AstXnor {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)"); - TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)"); - TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)"); - TREEOP ("AstMul {operandIsPowTwo($lhsp), $rhsp}", "replaceMulShift(nodep)"); // a*2^n -> a< a>>n - TREEOP ("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<castAdd()->lhsp(),$rhsp}, $lhsp->castAdd()->rhsp()}"); // ((a+x)-y) -> (a+(x-y)) + TREEOP ("AstAnd {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstLogAnd{$lhsp.isNeqZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstOr {$lhsp.isAllOnes, $rhsp, isTPure($rhsp)}", "replaceWLhs(nodep)"); //->allOnes + TREEOP ("AstLogOr {$lhsp.isNeqZero, $rhsp}", "replaceNum(nodep,1)"); + TREEOP ("AstAnd {$lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); + TREEOP ("AstLogAnd{$lhsp, $rhsp.isNeqZero}", "replaceWLhs(nodep)"); + TREEOP ("AstOr {$lhsp, $rhsp.isAllOnes, isTPure($lhsp)}", "replaceWRhs(nodep)"); //->allOnes + TREEOP ("AstLogOr {$lhsp, $rhsp.isNeqZero, isTPure($lhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstXor {$lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); + TREEOP ("AstXnor {$lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstMul {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstMulS {$lhsp.isOne, $rhsp}", "replaceWRhs(nodep)"); + TREEOP ("AstDiv {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)"); + TREEOP ("AstDivS {$lhsp, $rhsp.isOne}", "replaceWLhs(nodep)"); + TREEOP ("AstMul {operandIsPowTwo($lhsp), $rhsp}", "replaceMulShift(nodep)"); // a*2^n -> a< a>>n + TREEOP ("AstPow {operandIsTwo($lhsp), $rhsp}", "replacePowShift(nodep)"); // 2**a == 1<castAdd()->lhsp(),$rhsp}, $lhsp->castAdd()->rhsp()}"); // ((a+x)-y) -> (a+(x-y)) // Trinary ops // Note V3Case::Sel requires Cond to always be conditionally executed in C to prevent core dump! TREEOP ("AstNodeCond{$condp.isZero, $expr1p, $expr2p}", "replaceWChild(nodep,$expr2p)"); @@ -2219,212 +2248,215 @@ private: TREEOPC("AstNodeCond{$condp.isNeqZero, $expr1p.castConst, $expr2p.castConst}", "replaceWChild(nodep,$expr1p)"); TREEOP ("AstNodeCond{$condp, operandsSame($expr1p,,$expr2p)}","replaceWChild(nodep,$expr1p)"); // This visit function here must allow for short-circuiting. - TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)"); - TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)"); + TREEOPS("AstCond {$lhsp.isZero}", "replaceWIteratedThs(nodep)"); + TREEOPS("AstCond {$lhsp.isNeqZero}", "replaceWIteratedRhs(nodep)"); TREEOP ("AstCond{$condp.castNot, $expr1p, $expr2p}", "AstCond{$condp->op1p(), $expr2p, $expr1p}"); TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isAllOnes, $expr2p}", "AstLogOr {$condp, $expr2p}"); // a?1:b == a||b TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isZero}", "AstLogAnd{$condp, $expr1p}"); // a?b:0 == a&&b TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p, $expr2p.isAllOnes}", "AstLogOr {AstNot{$condp}, $expr1p}"); // a?b:1 == ~a||b TREEOP ("AstNodeCond{$condp.width1, $expr1p.width1, $expr1p.isZero, $expr2p}", "AstLogAnd{AstNot{$condp}, $expr2p}"); // a?0:b == ~a&&b TREEOP ("AstNodeCond{!$condp.width1, operandBoolShift(nodep->condp())}", "replaceBoolShift(nodep->condp())"); - // Prefer constants on left, since that often needs a shift, it lets constant red remove the shift - TREEOP ("AstNodeBiCom{!$lhsp.castConst, $rhsp.castConst}", "swapSides(nodep)"); - TREEOP ("AstNodeBiComAsv{operandAsvConst(nodep)}", "replaceAsv(nodep)"); - TREEOP ("AstNodeBiComAsv{operandAsvSame(nodep)}", "replaceAsv(nodep)"); - TREEOP ("AstNodeBiComAsv{operandAsvLUp(nodep)}", "replaceAsvLUp(nodep)"); - TREEOP ("AstNodeBiComAsv{operandAsvRUp(nodep)}", "replaceAsvRUp(nodep)"); - TREEOP ("AstLt {!$lhsp.castConst,$rhsp.castConst}", "AstGt {$rhsp,$lhsp}"); - TREEOP ("AstLtS {!$lhsp.castConst,$rhsp.castConst}", "AstGtS {$rhsp,$lhsp}"); - TREEOP ("AstLte {!$lhsp.castConst,$rhsp.castConst}", "AstGte {$rhsp,$lhsp}"); - TREEOP ("AstLteS {!$lhsp.castConst,$rhsp.castConst}", "AstGteS{$rhsp,$lhsp}"); - TREEOP ("AstGt {!$lhsp.castConst,$rhsp.castConst}", "AstLt {$rhsp,$lhsp}"); - TREEOP ("AstGtS {!$lhsp.castConst,$rhsp.castConst}", "AstLtS {$rhsp,$lhsp}"); - TREEOP ("AstGte {!$lhsp.castConst,$rhsp.castConst}", "AstLte {$rhsp,$lhsp}"); - TREEOP ("AstGteS {!$lhsp.castConst,$rhsp.castConst}", "AstLteS{$rhsp,$lhsp}"); + // Prefer constants on left, since that often needs a shift, it lets + // constant red remove the shift + TREEOP ("AstNodeBiCom{!$lhsp.castConst, $rhsp.castConst}", "swapSides(nodep)"); + TREEOP ("AstNodeBiComAsv{operandAsvConst(nodep)}", "replaceAsv(nodep)"); + TREEOP ("AstNodeBiComAsv{operandAsvSame(nodep)}", "replaceAsv(nodep)"); + TREEOP ("AstNodeBiComAsv{operandAsvLUp(nodep)}", "replaceAsvLUp(nodep)"); + TREEOP ("AstNodeBiComAsv{operandAsvRUp(nodep)}", "replaceAsvRUp(nodep)"); + TREEOP ("AstLt {!$lhsp.castConst,$rhsp.castConst}", "AstGt {$rhsp,$lhsp}"); + TREEOP ("AstLtS {!$lhsp.castConst,$rhsp.castConst}", "AstGtS {$rhsp,$lhsp}"); + TREEOP ("AstLte {!$lhsp.castConst,$rhsp.castConst}", "AstGte {$rhsp,$lhsp}"); + TREEOP ("AstLteS {!$lhsp.castConst,$rhsp.castConst}", "AstGteS{$rhsp,$lhsp}"); + TREEOP ("AstGt {!$lhsp.castConst,$rhsp.castConst}", "AstLt {$rhsp,$lhsp}"); + TREEOP ("AstGtS {!$lhsp.castConst,$rhsp.castConst}", "AstLtS {$rhsp,$lhsp}"); + TREEOP ("AstGte {!$lhsp.castConst,$rhsp.castConst}", "AstLte {$rhsp,$lhsp}"); + TREEOP ("AstGteS {!$lhsp.castConst,$rhsp.castConst}", "AstLteS{$rhsp,$lhsp}"); // v--- *1* as These ops are always first, as we warn before replacing - TREEOP1("AstLt {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,0)"); - TREEOP1("AstGte {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,1)"); - TREEOP1("AstGt {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,0)"); - TREEOP1("AstLte {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,1)"); + TREEOP1("AstLt {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,0)"); + TREEOP1("AstGte {$lhsp, $rhsp.isZero}", "replaceNumSigned(nodep,1)"); + TREEOP1("AstGt {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,0)"); + TREEOP1("AstLte {$lhsp.isZero, $rhsp}", "replaceNumSigned(nodep,1)"); TREEOP1("AstGt {$lhsp, $rhsp.isAllOnes, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,0)"); TREEOP1("AstLte {$lhsp, $rhsp.isAllOnes, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,1)"); TREEOP1("AstLt {$lhsp.isAllOnes, $rhsp, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,0)"); TREEOP1("AstGte {$lhsp.isAllOnes, $rhsp, $lhsp->width()==$rhsp->width()}", "replaceNumLimited(nodep,1)"); // Two level bubble pushing TREEOP ("AstNot {$lhsp.castNot, $lhsp->width()==VN_CAST($lhsp,,Not)->lhsp()->width()}", "replaceWChild(nodep, $lhsp->op1p())"); // NOT(NOT(x))->x - TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->op1p())"); // LOGNOT(LOGNOT(x))->x + TREEOP ("AstLogNot{$lhsp.castLogNot}", "replaceWChild(nodep, $lhsp->op1p())"); // LOGNOT(LOGNOT(x))->x TREEOPV("AstNot {$lhsp.castEqCase, $lhsp.width1}","AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castEqCase}", "AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castEqCase}", "AstNeqCase{$lhsp->op1p(),$lhsp->op2p()}"); TREEOPV("AstNot {$lhsp.castNeqCase, $lhsp.width1}","AstEqCase {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castNeqCase}", "AstEqCase {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castNeqCase}", "AstEqCase {$lhsp->op1p(),$lhsp->op2p()}"); TREEOPV("AstNot {$lhsp.castEqWild, $lhsp.width1}","AstNeqWild{$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castEqWild}", "AstNeqWild{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castEqWild}", "AstNeqWild{$lhsp->op1p(),$lhsp->op2p()}"); TREEOPV("AstNot {$lhsp.castNeqWild, $lhsp.width1}","AstEqWild {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castNeqWild}", "AstEqWild {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castEq, $lhsp.width1}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castEq}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castNeq, $lhsp.width1}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castNeq}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castLt, $lhsp.width1}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castLt}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castLtS, $lhsp.width1}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castLtS}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castLte, $lhsp.width1}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castLte}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castLteS, $lhsp.width1}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castLteS}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castGt, $lhsp.width1}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castGt}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castGtS, $lhsp.width1}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castGtS}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castGte, $lhsp.width1}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castGte}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOPV("AstNot {$lhsp.castGteS, $lhsp.width1}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}"); - TREEOP ("AstLogNot{$lhsp.castGteS}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castNeqWild}", "AstEqWild {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castEq, $lhsp.width1}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castEq}", "AstNeq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castNeq, $lhsp.width1}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castNeq}", "AstEq {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLt, $lhsp.width1}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLt}", "AstGte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLtS, $lhsp.width1}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLtS}", "AstGteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLte, $lhsp.width1}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLte}", "AstGt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castLteS, $lhsp.width1}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castLteS}", "AstGtS {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGt, $lhsp.width1}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGt}", "AstLte {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGtS, $lhsp.width1}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGtS}", "AstLteS{$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGte, $lhsp.width1}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGte}", "AstLt {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOPV("AstNot {$lhsp.castGteS, $lhsp.width1}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}"); + TREEOP ("AstLogNot{$lhsp.castGteS}", "AstLtS {$lhsp->op1p(),$lhsp->op2p()}"); // Not common, but avoids compiler warnings about over shifting - TREEOP ("AstShiftL{operandHugeShiftL(nodep)}", "replaceZero(nodep)"); - TREEOP ("AstShiftR{operandHugeShiftR(nodep)}", "replaceZero(nodep)"); - TREEOP ("AstShiftL{operandShiftOp(nodep)}", "replaceShiftOp(nodep)"); - TREEOP ("AstShiftR{operandShiftOp(nodep)}", "replaceShiftOp(nodep)"); - TREEOP ("AstShiftL{operandShiftShift(nodep)}", "replaceShiftShift(nodep)"); - TREEOP ("AstShiftR{operandShiftShift(nodep)}", "replaceShiftShift(nodep)"); - TREEOP ("AstWordSel{operandWordOOB(nodep)}", "replaceZero(nodep)"); + TREEOP ("AstShiftL{operandHugeShiftL(nodep)}", "replaceZero(nodep)"); + TREEOP ("AstShiftR{operandHugeShiftR(nodep)}", "replaceZero(nodep)"); + TREEOP ("AstShiftL{operandShiftOp(nodep)}", "replaceShiftOp(nodep)"); + TREEOP ("AstShiftR{operandShiftOp(nodep)}", "replaceShiftOp(nodep)"); + TREEOP ("AstShiftL{operandShiftShift(nodep)}", "replaceShiftShift(nodep)"); + TREEOP ("AstShiftR{operandShiftShift(nodep)}", "replaceShiftShift(nodep)"); + TREEOP ("AstWordSel{operandWordOOB(nodep)}", "replaceZero(nodep)"); // Compress out EXTENDs to appease loop unroller - TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); - TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); - TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); - TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); - TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); - TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); - TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)"); - TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)"); - TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)"); - TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)"); - TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)"); - TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)"); + TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); + TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); + TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); + TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); + TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); + TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstShrink(nodep)}", "DONE"); + TREEOPV("AstEq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)"); + TREEOPV("AstNeq {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)"); + TREEOPV("AstGt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)"); + TREEOPV("AstGte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceNum(nodep,1)"); + TREEOPV("AstLt {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)"); + TREEOPV("AstLte {$rhsp.castExtend,operandBiExtendConstOver(nodep)}", "replaceZero(nodep)"); // Identical operands on both sides // AstLogAnd/AstLogOr already converted to AstAnd/AstOr for these rules // AstAdd->ShiftL(#,1) but uncommon - TREEOP ("AstAnd {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)"); - TREEOP ("AstChangeXor{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstDiv {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstDivS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstOr {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)"); - TREEOP ("AstSub {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstXnor {operandsSame($lhsp,,$rhsp)}", "replaceAllOnes(nodep)"); - TREEOP ("AstXor {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstEq {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. - TREEOP ("AstEqD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. - TREEOP ("AstEqN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. - TREEOP ("AstEqCase {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstEqWild {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstGt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstGtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstGtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstGtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstGte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstGteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstGteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstGteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstLt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstLtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstLtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstLtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstLte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstLteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstLteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstLteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); - TREEOP ("AstNeq {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstNeqD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstNeqN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstNeqCase{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstNeqWild{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); - TREEOP ("AstLogAnd {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)"); - TREEOP ("AstLogOr {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOP ("AstAnd {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)"); + TREEOP ("AstChangeXor{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstDiv {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstDivS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstOr {operandsSame($lhsp,,$rhsp)}", "replaceWLhs(nodep)"); + TREEOP ("AstSub {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstXnor {operandsSame($lhsp,,$rhsp)}", "replaceAllOnes(nodep)"); + TREEOP ("AstXor {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstEq {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. + TREEOP ("AstEqD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. + TREEOP ("AstEqN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); // We let X==X -> 1, although in a true 4-state sim it's X. + TREEOP ("AstEqCase {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstEqWild {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstGt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstGtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstGtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstGtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstGte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstGteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstGteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstGteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstLt {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstLtD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstLtN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstLtS {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstLte {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstLteD {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstLteN {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstLteS {operandsSame($lhsp,,$rhsp)}", "replaceNum(nodep,1)"); + TREEOP ("AstNeq {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstNeqD {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstNeqN {operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstNeqCase{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstNeqWild{operandsSame($lhsp,,$rhsp)}", "replaceZero(nodep)"); + TREEOP ("AstLogAnd {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOP ("AstLogOr {operandsSame($lhsp,,$rhsp), $lhsp.width1}", "replaceWLhs(nodep)"); ///=== Verilog operators // Comparison against 1'b0/1'b1; must be careful about widths. // These use Not, so must be Verilog only - TREEOPV("AstEq {$rhsp.width1, $lhsp.isZero, $rhsp}", "AstNot{$rhsp}"); - TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isZero}", "AstNot{$lhsp}"); - TREEOPV("AstEq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); - TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); - TREEOPV("AstNeq {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); - TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); - TREEOPV("AstNeq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); - TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "AstNot{$lhsp}"); - TREEOPV("AstLt {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); // Because not signed #s - TREEOPV("AstGt {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); // Because not signed #s + TREEOPV("AstEq {$rhsp.width1, $lhsp.isZero, $rhsp}", "AstNot{$rhsp}"); + TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isZero}", "AstNot{$lhsp}"); + TREEOPV("AstEq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "replaceWRhs(nodep)"); + TREEOPV("AstEq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "replaceWLhs(nodep)"); + TREEOPV("AstNeq {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); + TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); + TREEOPV("AstNeq {$rhsp.width1, $lhsp.isAllOnes, $rhsp}", "AstNot{$rhsp}"); + TREEOPV("AstNeq {$lhsp.width1, $lhsp, $rhsp.isAllOnes}", "AstNot{$lhsp}"); + TREEOPV("AstLt {$rhsp.width1, $lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); // Because not signed #s + TREEOPV("AstGt {$lhsp.width1, $lhsp, $rhsp.isZero}", "replaceWLhs(nodep)"); // Because not signed #s // Useful for CONDs added around ARRAYSEL's in V3Case step TREEOPV("AstLte {$lhsp->width()==$rhsp->width(), $rhsp.isAllOnes}", "replaceNum(nodep,1)"); // Simplify reduction operators // This also gets &{...,0,....} => const 0 (Common for unused_ok signals) - TREEOPV("AstRedAnd{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); - TREEOPV("AstRedOr {$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); - TREEOPV("AstRedXor{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); - TREEOPV("AstRedAnd{$lhsp.castConcat}", "AstAnd{AstRedAnd{$lhsp->castConcat()->lhsp()}, AstRedAnd{$lhsp->castConcat()->rhsp()}}"); // &{a,b} => {&a}&{&b} - TREEOPV("AstRedOr {$lhsp.castConcat}", "AstOr {AstRedOr {$lhsp->castConcat()->lhsp()}, AstRedOr {$lhsp->castConcat()->rhsp()}}"); // |{a,b} => {|a}|{|b} - TREEOPV("AstRedXor{$lhsp.castConcat}", "AstXor{AstRedXor{$lhsp->castConcat()->lhsp()}, AstRedXor{$lhsp->castConcat()->rhsp()}}"); // ^{a,b} => {^a}^{^b} + TREEOPV("AstRedAnd{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstRedOr {$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstRedXor{$lhsp, $lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstRedAnd{$lhsp.castConcat}", "AstAnd{AstRedAnd{$lhsp->castConcat()->lhsp()}, AstRedAnd{$lhsp->castConcat()->rhsp()}}"); // &{a,b} => {&a}&{&b} + TREEOPV("AstRedOr {$lhsp.castConcat}", "AstOr {AstRedOr {$lhsp->castConcat()->lhsp()}, AstRedOr {$lhsp->castConcat()->rhsp()}}"); // |{a,b} => {|a}|{|b} + TREEOPV("AstRedXor{$lhsp.castConcat}", "AstXor{AstRedXor{$lhsp->castConcat()->lhsp()}, AstRedXor{$lhsp->castConcat()->rhsp()}}"); // ^{a,b} => {^a}^{^b} TREEOPV("AstRedAnd{$lhsp.castExtend, $lhsp->width() > VN_CAST($lhsp,,Extend)->lhsp()->width()}", "replaceZero(nodep)"); // &{0,...} => 0 Prevents compiler limited range error - TREEOPV("AstRedOr {$lhsp.castExtend}", "AstRedOr {$lhsp->castExtend()->lhsp()}"); - TREEOPV("AstRedXor{$lhsp.castExtend}", "AstRedXor{$lhsp->castExtend()->lhsp()}"); - TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)"); - TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)"); + TREEOPV("AstRedOr {$lhsp.castExtend}", "AstRedOr {$lhsp->castExtend()->lhsp()}"); + TREEOPV("AstRedXor{$lhsp.castExtend}", "AstRedXor{$lhsp->castExtend()->lhsp()}"); + TREEOPV("AstOneHot{$lhsp.width1}", "replaceWLhs(nodep)"); + TREEOPV("AstOneHot0{$lhsp.width1}", "replaceNum(nodep,1)"); // Binary AND/OR is faster than logical and/or (usually) TREEOPV("AstLogAnd{$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstAnd{$lhsp,$rhsp}"); TREEOPV("AstLogOr {$lhsp.width1, $rhsp.width1, isTPure($lhsp), isTPure($rhsp)}", "AstOr{$lhsp,$rhsp}"); - TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}"); + TREEOPV("AstLogNot{$lhsp.width1, isTPure($lhsp)}", "AstNot{$lhsp}"); // CONCAT(CONCAT({a},{b}),{c}) -> CONCAT({a},CONCAT({b},{c})) // CONCAT({const},CONCAT({const},{c})) -> CONCAT((constifiedCONC{const|const},{c})) - TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)"); - TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())"); + TREEOPV("AstConcat{operandConcatMove(nodep)}", "moveConcat(nodep)"); + TREEOPV("AstConcat{$lhsp.isZero, $rhsp}", "replaceExtend(nodep, nodep->rhsp())"); // CONCAT(a[1],a[0]) -> a[1:0] TREEOPV("AstConcat{$lhsp.castSel, $rhsp.castSel, ifAdjacentSel(VN_CAST($lhsp,,Sel),,VN_CAST($rhsp,,Sel))}", "replaceConcatSel(nodep)"); TREEOPV("AstConcat{ifConcatMergeableBiop($lhsp), concatMergeable($lhsp,,$rhsp)}", "replaceConcatMerge(nodep)"); // Common two-level operations that can be simplified - TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); - TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); - TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE"); - TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); - TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); - TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); - // "AstXnor{operandShiftSame(nodep)}", // Cannot ShiftSame as the shifted-in zeros might create a one - // Note can't simplify a extend{extends}, extends{extend}, as the sign bits end up in the wrong places + TREEOP ("AstAnd {$lhsp.castOr, $rhsp.castOr, operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); + TREEOP ("AstOr {$lhsp.castAnd,$rhsp.castAnd,operandAndOrSame(nodep)}", "replaceAndOr(nodep)"); + TREEOP ("AstOr {matchOrAndNot(nodep)}", "DONE"); + TREEOP ("AstAnd {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); + TREEOP ("AstOr {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); + TREEOP ("AstXor {operandShiftSame(nodep)}", "replaceShiftSame(nodep)"); + // "AstXnor{operandShiftSame(nodep)}", // Cannot ShiftSame as the shifted-in + // zeros might create a one + // Note can't simplify a extend{extends}, extends{extend}, as the sign + // bits end up in the wrong places TREEOPV("AstExtend {$lhsp.castExtend}", "replaceExtend(nodep, VN_CAST(nodep->lhsp(), Extend)->lhsp())"); TREEOPV("AstExtendS{$lhsp.castExtendS}", "replaceExtend(nodep, VN_CAST(nodep->lhsp(), ExtendS)->lhsp())"); - TREEOPV("AstReplicate{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs + TREEOPV("AstReplicate{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs TREEOPV("AstReplicateN{$lhsp, $rhsp.isOne, $lhsp->width()==nodep->width()}", "replaceWLhs(nodep)"); // {1{lhs}}->lhs TREEOPV("AstReplicate{$lhsp.castReplicate, operandRepRep(nodep)}", "DONE"); // {2{3{lhs}}}->{6{lhs}} TREEOPV("AstConcat{operandConcatSame(nodep)}", "DONE"); // {a,a}->{2{a}}, {a,2{a}}->{3{a}, etc // Next rule because AUTOINST puts the width of bits in // to pins, even when the widths are exactly the same across the hierarchy. - TREEOPV("AstSel{operandSelExtend(nodep)}", "DONE"); - TREEOPV("AstSel{operandSelFull(nodep)}", "replaceWChild(nodep, nodep->fromp())"); - TREEOPV("AstSel{$fromp.castSel}", "replaceSelSel(nodep)"); - TREEOPV("AstSel{$fromp.castAdd, operandSelBiLower(nodep)}", "DONE"); - TREEOPV("AstSel{$fromp.castAnd, operandSelBiLower(nodep)}", "DONE"); - TREEOPV("AstSel{$fromp.castOr, operandSelBiLower(nodep)}", "DONE"); - TREEOPV("AstSel{$fromp.castSub, operandSelBiLower(nodep)}", "DONE"); - TREEOPV("AstSel{$fromp.castXnor,operandSelBiLower(nodep)}", "DONE"); - TREEOPV("AstSel{$fromp.castXor, operandSelBiLower(nodep)}", "DONE"); - TREEOPV("AstSel{$fromp.castShiftR, operandSelShiftLower(nodep)}", "DONE"); - TREEOPC("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)"); - TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)"); - TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.castConst, operandSelReplicate(nodep) }", "DONE"); + TREEOPV("AstSel{operandSelExtend(nodep)}", "DONE"); + TREEOPV("AstSel{operandSelFull(nodep)}", "replaceWChild(nodep, nodep->fromp())"); + TREEOPV("AstSel{$fromp.castSel}", "replaceSelSel(nodep)"); + TREEOPV("AstSel{$fromp.castAdd, operandSelBiLower(nodep)}", "DONE"); + TREEOPV("AstSel{$fromp.castAnd, operandSelBiLower(nodep)}", "DONE"); + TREEOPV("AstSel{$fromp.castOr, operandSelBiLower(nodep)}", "DONE"); + TREEOPV("AstSel{$fromp.castSub, operandSelBiLower(nodep)}", "DONE"); + TREEOPV("AstSel{$fromp.castXnor,operandSelBiLower(nodep)}", "DONE"); + TREEOPV("AstSel{$fromp.castXor, operandSelBiLower(nodep)}", "DONE"); + TREEOPV("AstSel{$fromp.castShiftR, operandSelShiftLower(nodep)}", "DONE"); + TREEOPC("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)"); + TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)"); + TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.castConst, operandSelReplicate(nodep) }", "DONE"); // V3Tristate requires selects below BufIf1. // Also do additional operators that are bit-independent, but only definite // win if bit select is a constant (otherwise we may need to compute bit index several times) - TREEOPV("AstSel{$fromp.castBufIf1}", "replaceSelIntoBiop(nodep)"); - TREEOPV("AstSel{$fromp.castNot}", "replaceSelIntoUniop(nodep)"); - TREEOPV("AstSel{$fromp.castAnd,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); - TREEOPV("AstSel{$fromp.castOr,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); - TREEOPV("AstSel{$fromp.castXor,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); - TREEOPV("AstSel{$fromp.castXnor,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); + TREEOPV("AstSel{$fromp.castBufIf1}", "replaceSelIntoBiop(nodep)"); + TREEOPV("AstSel{$fromp.castNot}", "replaceSelIntoUniop(nodep)"); + TREEOPV("AstSel{$fromp.castAnd,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); + TREEOPV("AstSel{$fromp.castOr,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); + TREEOPV("AstSel{$fromp.castXor,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); + TREEOPV("AstSel{$fromp.castXnor,$lhsp.castConst}", "replaceSelIntoUniop(nodep)"); // Conversions - TREEOPV("AstRedXnor{$lhsp}", "AstNot{AstRedXor{$lhsp}}"); // Just eliminate XNOR's + TREEOPV("AstRedXnor{$lhsp}", "AstNot{AstRedXor{$lhsp}}"); // Just eliminate XNOR's // This visit function here must allow for short-circuiting. - TREEOPS("AstLogIf {$lhsp.isZero}", "replaceNum(nodep, 1)"); - TREEOPV("AstLogIf {$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}"); - TREEOPV("AstLogIff{$lhsp, $rhsp}", "AstLogNot{AstXor{$lhsp,$rhsp}}"); + TREEOPS("AstLogIf {$lhsp.isZero}", "replaceNum(nodep, 1)"); + TREEOPV("AstLogIf {$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}"); + TREEOPV("AstLogIff{$lhsp, $rhsp}", "AstLogNot{AstXor{$lhsp,$rhsp}}"); // Strings TREEOPC("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_CAST(nodep->lhsp(), Const)->num().toString())"); @@ -2439,66 +2471,68 @@ private: //----- virtual void visit(AstNode* nodep) { - // Default: Just iterate - if (m_required) { + // Default: Just iterate + if (m_required) { if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range)) { - // Ignore dtypes for parameter type pins - } else { - nodep->v3error("Expecting expression to be constant, but can't convert a " - <prettyTypeName()<<" to constant."); - } - } else { - // Calculate the width of this operation - if (m_params && !nodep->width()) { - nodep = V3Width::widthParamsEdit(nodep); - } + // Ignore dtypes for parameter type pins + } else { + nodep->v3error("Expecting expression to be constant, but can't convert a " + <prettyTypeName()<<" to constant."); + } + } else { + // Calculate the width of this operation + if (m_params && !nodep->width()) { + nodep = V3Width::widthParamsEdit(nodep); + } iterateChildren(nodep); - } + } } public: // Processing Mode Enum enum ProcMode { - PROC_PARAMS, - PROC_GENERATE, - PROC_LIVE, - PROC_V_WARN, - PROC_V_NOWARN, - PROC_V_EXPENSIVE, - PROC_CPP + PROC_PARAMS, + PROC_GENERATE, + PROC_LIVE, + PROC_V_WARN, + PROC_V_NOWARN, + PROC_V_EXPENSIVE, + PROC_CPP }; // CONSTUCTORS explicit ConstVisitor(ProcMode pmode) { - m_params = false; - m_required = false; - m_doExpensive = false; - m_doNConst = false; - m_doShort = true; // Presently always done - m_doV = false; - m_doGenerate = false; // Inside generate conditionals + m_params = false; + m_required = false; + m_doExpensive = false; + m_doNConst = false; + m_doShort = true; // Presently always done + m_doV = false; + m_doGenerate = false; // Inside generate conditionals m_hasJumpGo = false; - m_warn = false; - m_wremove = true; // Overridden in visitors - m_modp = NULL; - m_selp = NULL; - m_scopep = NULL; - m_attrp = NULL; - // - switch (pmode) { - case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true; m_required = true; break; - case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true; m_required = true; m_doGenerate = true; break; - case PROC_LIVE: break; - case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break; - case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break; - case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break; - case PROC_CPP: m_doV = false; m_doNConst = true; break; - default: v3fatalSrc("Bad case"); break; - } + m_warn = false; + m_wremove = true; // Overridden in visitors + m_modp = NULL; + m_selp = NULL; + m_scopep = NULL; + m_attrp = NULL; + // + switch (pmode) { + case PROC_PARAMS: m_doV = true; m_doNConst = true; m_params = true; + m_required = true; break; + case PROC_GENERATE: m_doV = true; m_doNConst = true; m_params = true; + m_required = true; m_doGenerate = true; break; + case PROC_LIVE: break; + case PROC_V_WARN: m_doV = true; m_doNConst = true; m_warn = true; break; + case PROC_V_NOWARN: m_doV = true; m_doNConst = true; break; + case PROC_V_EXPENSIVE: m_doV = true; m_doNConst = true; m_doExpensive = true; break; + case PROC_CPP: m_doV = false; m_doNConst = true; break; + default: v3fatalSrc("Bad case"); break; + } } virtual ~ConstVisitor() {} AstNode* mainAcceptEdit(AstNode* nodep) { - // Operate starting at a random place + // Operate starting at a random place return iterateSubtreeReturnEdits(nodep); } }; @@ -2509,23 +2543,23 @@ public: //! Force this cell node's parameter list to become a constant //! @return Pointer to the edited node. AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { - //if (debug()>0) nodep->dumpTree(cout," forceConPRE : "); + //if (debug()>0) nodep->dumpTree(cout, " forceConPRE : "); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized yet. // Make sure we've sized everything first nodep = V3Width::widthParamsEdit(nodep); ConstVisitor visitor(ConstVisitor::PROC_PARAMS); - if (AstVar* varp=VN_CAST(nodep, Var)) { - // If a var wants to be constified, it's really a param, and - // we want the value to be constant. We aren't passed just the - // init value because we need widthing above to handle the var's type. - if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep()); + if (AstVar* varp = VN_CAST(nodep, Var)) { + // If a var wants to be constified, it's really a param, and + // we want the value to be constant. We aren't passed just the + // init value because we need widthing above to handle the var's type. + if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep()); } else { - nodep = visitor.mainAcceptEdit(nodep); + nodep = visitor.mainAcceptEdit(nodep); } // Because we do edits, nodep links may get trashed and core dump this. - //if (debug()>0) nodep->dumpTree(cout," forceConDONE: "); + //if (debug()>0) nodep->dumpTree(cout, " forceConDONE: "); return nodep; } @@ -2538,7 +2572,7 @@ AstNode* V3Const::constifyParamsEdit(AstNode* nodep) { //! width check. //! @return Pointer to the edited node. AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { - //if (debug()>0) nodep->dumpTree(cout," forceConPRE : "); + //if (debug()>0) nodep->dumpTree(cout, " forceConPRE : "); // Resize even if the node already has a width, because buried in the tree // we may have a node we just created with signing, etc, that isn't sized // yet. @@ -2546,16 +2580,16 @@ AstNode* V3Const::constifyGenerateParamsEdit(AstNode* nodep) { // Make sure we've sized everything first nodep = V3Width::widthGenerateParamsEdit(nodep); ConstVisitor visitor(ConstVisitor::PROC_GENERATE); - if (AstVar* varp=VN_CAST(nodep, Var)) { - // If a var wants to be constified, it's really a param, and - // we want the value to be constant. We aren't passed just the - // init value because we need widthing above to handle the var's type. - if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep()); + if (AstVar* varp = VN_CAST(nodep, Var)) { + // If a var wants to be constified, it's really a param, and + // we want the value to be constant. We aren't passed just the + // init value because we need widthing above to handle the var's type. + if (varp->valuep()) visitor.mainAcceptEdit(varp->valuep()); } else { - nodep = visitor.mainAcceptEdit(nodep); + nodep = visitor.mainAcceptEdit(nodep); } // Because we do edits, nodep links may get trashed and core dump this. - //if (debug()>0) nodep->dumpTree(cout," forceConDONE: "); + //if (debug()>0) nodep->dumpTree(cout, " forceConDONE: "); return nodep; } diff --git a/src/V3Const.h b/src/V3Const.h index a5869eb05..d6a3c6784 100644 --- a/src/V3Const.h +++ b/src/V3Const.h @@ -49,4 +49,4 @@ public: static AstNode* constifyExpensiveEdit(AstNode* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index f356f894f..75eb407ec 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -18,13 +18,13 @@ // //************************************************************************* // COVERAGE TRANSFORMATIONS: -// At each IF/(IF else)/CASEITEM, -// If there's no coverage off on the block below it, -// or a $stop -// Insert a COVERDECL node in the module. -// (V3Emit reencodes into per-module numbers for emitting.) -// Insert a COVERINC node at the end of the statement list -// for that if/else/case. +// At each IF/(IF else)/CASEITEM, +// If there's no coverage off on the block below it, +// or a $stop +// Insert a COVERDECL node in the module. +// (V3Emit reencodes into per-module numbers for emitting.) +// Insert a COVERINC node at the end of the statement list +// for that if/else/case. // //************************************************************************* @@ -47,334 +47,347 @@ private: typedef std::map FileMap; struct ToggleEnt { - string m_comment; // Comment for coverage dump - AstNode* m_varRefp; // How to get to this element - AstNode* m_chgRefp; // How to get to this element - ToggleEnt(const string& comment, AstNode* vp, AstNode* cp) - : m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {} - ~ToggleEnt() {} - void cleanup() { - m_varRefp->deleteTree(); m_varRefp=NULL; - m_chgRefp->deleteTree(); m_chgRefp=NULL; - } + string m_comment; // Comment for coverage dump + AstNode* m_varRefp; // How to get to this element + AstNode* m_chgRefp; // How to get to this element + ToggleEnt(const string& comment, AstNode* vp, AstNode* cp) + : m_comment(comment), m_varRefp(vp), m_chgRefp(cp) {} + ~ToggleEnt() {} + void cleanup() { + m_varRefp->deleteTree(); m_varRefp = NULL; + m_chgRefp->deleteTree(); m_chgRefp = NULL; + } }; // NODE STATE // Entire netlist: - // AstIf::user1() -> bool. True indicates ifelse processed - AstUser1InUse m_inuser1; + // AstIf::user1() -> bool. True indicates ifelse processed + AstUser1InUse m_inuser1; // STATE - bool m_checkBlock; // Should this block get covered? - AstNodeModule* m_modp; // Current module to add statement to - bool m_inToggleOff; // In function/task etc - bool m_inModOff; // In module with no coverage - FileMap m_fileps; // Column counts for each fileline - string m_beginHier; // AstBegin hier name for user coverage points + bool m_checkBlock; // Should this block get covered? + AstNodeModule* m_modp; // Current module to add statement to + bool m_inToggleOff; // In function/task etc + bool m_inModOff; // In module with no coverage + FileMap m_fileps; // Column counts for each fileline + string m_beginHier; // AstBegin hier name for user coverage points // METHODS VL_DEBUG_FUNC; // Declare debug() const char* varIgnoreToggle(AstVar* nodep) { - // Return true if this shouldn't be traced - // See also similar rule in V3TraceDecl::varIgnoreTrace - if (!nodep->isToggleCoverable()) - return "Not relevant signal type"; - if (!v3Global.opt.coverageUnderscore()) { - string prettyName = nodep->prettyName(); - if (prettyName[0] == '_') - return "Leading underscore"; - if (prettyName.find("._") != string::npos) - return "Inlined leading underscore"; - } - if ((nodep->width()*nodep->dtypep()->arrayUnpackedElements()) > 256) return "Wide bus/array > 256 bits"; - // We allow this, though tracing doesn't - // if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array"; - return NULL; + // Return true if this shouldn't be traced + // See also similar rule in V3TraceDecl::varIgnoreTrace + if (!nodep->isToggleCoverable()) + return "Not relevant signal type"; + if (!v3Global.opt.coverageUnderscore()) { + string prettyName = nodep->prettyName(); + if (prettyName[0] == '_') + return "Leading underscore"; + if (prettyName.find("._") != string::npos) + return "Inlined leading underscore"; + } + if ((nodep->width()*nodep->dtypep()->arrayUnpackedElements()) > 256) { + return "Wide bus/array > 256 bits"; + } + // We allow this, though tracing doesn't + // if (nodep->arrayp(1)) return "Unsupported: Multi-dimensional array"; + return NULL; } AstCoverInc* newCoverInc(FileLine* fl, const string& hier, - const string& page_prefix, const string& comment) { - // For line coverage, we may have multiple if's on one line, so disambiguate if - // everything is otherwise identical - // (Don't set column otherwise as it may result in making bins not match up with - // different types of coverage enabled.) - string key = fl->filename()+"\001"+cvtToStr(fl->lineno())+"\001"+hier+"\001"+page_prefix+"\001"+comment; - int column = 0; - FileMap::iterator it = m_fileps.find(key); - if (it == m_fileps.end()) { - m_fileps.insert(make_pair(key,column+1)); - } else { - column = (it->second)++; - } + const string& page_prefix, const string& comment) { + // For line coverage, we may have multiple if's on one line, so disambiguate if + // everything is otherwise identical + // (Don't set column otherwise as it may result in making bins not match up with + // different types of coverage enabled.) + string key = fl->filename()+"\001"+cvtToStr(fl->lineno()) + +"\001"+hier+"\001"+page_prefix+"\001"+comment; + int column = 0; + FileMap::iterator it = m_fileps.find(key); + if (it == m_fileps.end()) { + m_fileps.insert(make_pair(key, column+1)); + } else { + column = (it->second)++; + } - // We could use the basename of the filename to the page, but seems better for code - // from an include file to be listed under the module using it rather than the include file. - // Note the module name could have parameters appended, we'll consider this - // a feature as it allows for each parameterized block to be counted separately. - // Someday the user might be allowed to specify a different page suffix - string page = page_prefix + "/" + m_modp->prettyName(); + // We could use the basename of the filename to the page, but seems + // better for code from an include file to be listed under the + // module using it rather than the include file. + // Note the module name could have parameters appended, we'll consider this + // a feature as it allows for each parameterized block to be counted separately. + // Someday the user might be allowed to specify a different page suffix + string page = page_prefix + "/" + m_modp->prettyName(); - AstCoverDecl* declp = new AstCoverDecl(fl, column, page, comment); - declp->hier(hier); - m_modp->addStmtp(declp); + AstCoverDecl* declp = new AstCoverDecl(fl, column, page, comment); + declp->hier(hier); + m_modp->addStmtp(declp); - return new AstCoverInc(fl, declp); + return new AstCoverInc(fl, declp); } // VISITORS - BOTH virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; - m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created - m_fileps.clear(); + m_modp = nodep; + m_inModOff = nodep->isTop(); // Ignore coverage on top module; it's a shell we created + m_fileps.clear(); iterateChildren(nodep); - m_modp = NULL; - m_inModOff = true; + m_modp = NULL; + m_inModOff = true; } // VISITORS - TOGGLE COVERAGE virtual void visit(AstNodeFTask* nodep) { - bool oldtog = m_inToggleOff; - { - m_inToggleOff = true; + bool oldtog = m_inToggleOff; + { + m_inToggleOff = true; iterateChildren(nodep); - } - m_inToggleOff = oldtog; + } + m_inToggleOff = oldtog; } virtual void visit(AstVar* nodep) { iterateChildren(nodep); - if (m_modp && !m_inModOff && !m_inToggleOff - && nodep->fileline()->coverageOn() && v3Global.opt.coverageToggle()) { - const char* disablep = varIgnoreToggle(nodep); - if (disablep) { - UINFO(4, " Disable Toggle: "<fileline()->coverageOn() && v3Global.opt.coverageToggle()) { + const char* disablep = varIgnoreToggle(nodep); + if (disablep) { + UINFO(4, " Disable Toggle: "<shortName(); - AstVar* chgVarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, newvarname, nodep); - chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); - m_modp->addStmtp(chgVarp); + AstVar* chgVarp = new AstVar(nodep->fileline(), + AstVarType::MODULETEMP, newvarname, nodep); + chgVarp->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); + m_modp->addStmtp(chgVarp); - // Create bucket for each dimension * bit. - // This is necessarily an O(n^2) expansion, which is why - // we limit coverage to signals with < 256 bits. + // Create bucket for each dimension * bit. + // This is necessarily an O(n^2) expansion, which is why + // we limit coverage to signals with < 256 bits. - ToggleEnt newvec (string(""), - new AstVarRef(nodep->fileline(), nodep, false), - new AstVarRef(nodep->fileline(), chgVarp, true)); - toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec, - nodep, chgVarp); - newvec.cleanup(); - } - } + ToggleEnt newvec (string(""), + new AstVarRef(nodep->fileline(), nodep, false), + new AstVarRef(nodep->fileline(), chgVarp, true)); + toggleVarRecurse(nodep->dtypeSkipRefp(), 0, newvec, + nodep, chgVarp); + newvec.cleanup(); + } + } } void toggleVarBottom(const ToggleEnt& above, const AstVar* varp) { - AstCoverToggle* newp + AstCoverToggle* newp = new AstCoverToggle(varp->fileline(), newCoverInc(varp->fileline(), "", "v_toggle", varp->name()+above.m_comment), above.m_varRefp->cloneTree(true), above.m_chgRefp->cloneTree(true)); - m_modp->addStmtp(newp); + m_modp->addStmtp(newp); } - void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration - const ToggleEnt& above, - AstVar* varp, AstVar* chgVarp) { // Constant + void toggleVarRecurse(AstNodeDType* dtypep, int depth, // per-iteration + const ToggleEnt& above, + AstVar* varp, AstVar* chgVarp) { // Constant if (const AstBasicDType* bdtypep = VN_CAST(dtypep, BasicDType)) { - if (bdtypep->isRanged()) { - for (int index_docs=bdtypep->lsb(); index_docsmsb()+1; index_docs++) { - int index_code = index_docs - bdtypep->lsb(); - ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", - new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code, 1), - new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code, 1)); + if (bdtypep->isRanged()) { + for (int index_docs=bdtypep->lsb(); index_docsmsb()+1; index_docs++) { + int index_code = index_docs - bdtypep->lsb(); + ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", + new AstSel(varp->fileline(), + above.m_varRefp->cloneTree(true), index_code, 1), + new AstSel(varp->fileline(), + above.m_chgRefp->cloneTree(true), index_code, 1)); toggleVarBottom(newent, varp); - newent.cleanup(); - } - } else { + newent.cleanup(); + } + } else { toggleVarBottom(above, varp); - } - } + } + } else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) { - for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) { - int index_code = index_docs - adtypep->lsb(); - ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", - new AstArraySel(varp->fileline(), above.m_varRefp->cloneTree(true), index_code), - new AstArraySel(varp->fileline(), above.m_chgRefp->cloneTree(true), index_code)); - toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1, - newent, - varp, chgVarp); - newent.cleanup(); - } - } + for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) { + int index_code = index_docs - adtypep->lsb(); + ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", + new AstArraySel(varp->fileline(), + above.m_varRefp->cloneTree(true), index_code), + new AstArraySel(varp->fileline(), + above.m_chgRefp->cloneTree(true), index_code)); + toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1, + newent, + varp, chgVarp); + newent.cleanup(); + } + } else if (AstPackArrayDType* adtypep = VN_CAST(dtypep, PackArrayDType)) { - for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) { - AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp(); - int index_code = index_docs - adtypep->lsb(); - ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", - new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), - index_code*subtypep->width(), subtypep->width()), - new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), - index_code*subtypep->width(), subtypep->width())); - toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1, - newent, - varp, chgVarp); - newent.cleanup(); - } - } + for (int index_docs=adtypep->lsb(); index_docs<=adtypep->msb(); ++index_docs) { + AstNodeDType* subtypep = adtypep->subDTypep()->skipRefp(); + int index_code = index_docs - adtypep->lsb(); + ToggleEnt newent (above.m_comment+string("[")+cvtToStr(index_docs)+"]", + new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), + index_code*subtypep->width(), subtypep->width()), + new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), + index_code*subtypep->width(), subtypep->width())); + toggleVarRecurse(adtypep->subDTypep()->skipRefp(), depth+1, + newent, + varp, chgVarp); + newent.cleanup(); + } + } else if (AstStructDType* adtypep = VN_CAST(dtypep, StructDType)) { - // For now it's packed, so similar to array - for (AstMemberDType* itemp = adtypep->membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { - AstNodeDType* subtypep = itemp->subDTypep()->skipRefp(); - int index_code = itemp->lsb(); - ToggleEnt newent (above.m_comment+string(".")+itemp->name(), - new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), - index_code, subtypep->width()), - new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), - index_code, subtypep->width())); - toggleVarRecurse(subtypep, depth+1, - newent, - varp, chgVarp); - newent.cleanup(); - } - } + // For now it's packed, so similar to array + for (AstMemberDType* itemp = adtypep->membersp(); + itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { + AstNodeDType* subtypep = itemp->subDTypep()->skipRefp(); + int index_code = itemp->lsb(); + ToggleEnt newent (above.m_comment+string(".")+itemp->name(), + new AstSel(varp->fileline(), above.m_varRefp->cloneTree(true), + index_code, subtypep->width()), + new AstSel(varp->fileline(), above.m_chgRefp->cloneTree(true), + index_code, subtypep->width())); + toggleVarRecurse(subtypep, depth+1, + newent, + varp, chgVarp); + newent.cleanup(); + } + } else if (AstUnionDType* adtypep = VN_CAST(dtypep, UnionDType)) { - // Arbitrarially handle only the first member of the union - if (AstMemberDType* itemp = adtypep->membersp()) { - AstNodeDType* subtypep = itemp->subDTypep()->skipRefp(); - ToggleEnt newent (above.m_comment+string(".")+itemp->name(), - above.m_varRefp->cloneTree(true), - above.m_chgRefp->cloneTree(true)); - toggleVarRecurse(subtypep, depth+1, - newent, - varp, chgVarp); - newent.cleanup(); - } - } - else { - dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: "<prettyTypeName()); - } + // Arbitrarially handle only the first member of the union + if (AstMemberDType* itemp = adtypep->membersp()) { + AstNodeDType* subtypep = itemp->subDTypep()->skipRefp(); + ToggleEnt newent (above.m_comment+string(".")+itemp->name(), + above.m_varRefp->cloneTree(true), + above.m_chgRefp->cloneTree(true)); + toggleVarRecurse(subtypep, depth+1, + newent, + varp, chgVarp); + newent.cleanup(); + } + } + else { + dtypep->v3fatalSrc("Unexpected node data type in toggle coverage generation: " + <prettyTypeName()); + } } // VISITORS - LINE COVERAGE - virtual void visit(AstIf* nodep) { // Note not AstNodeIf; other types don't get covered - UINFO(4," IF: "<elsesp(), If) && !VN_CAST(nodep->elsesp(), If)->nextp()); if (elsif) VN_CAST(nodep->elsesp(), If)->user1(true); - // + // iterateAndNextNull(nodep->ifsp()); - if (m_checkBlock && !m_inModOff - && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it - UINFO(4," COVER: "<user1()) { - nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif")); - } else { - nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if")); - } - } - // Don't do empty else's, only empty if/case's - if (nodep->elsesp()) { - m_checkBlock = true; + if (m_checkBlock && !m_inModOff + && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "if" branch didn't disable it + UINFO(4," COVER: "<user1()) { + nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "elsif")); + } else { + nodep->addIfsp(newCoverInc(nodep->fileline(), "", "v_line", "if")); + } + } + // Don't do empty else's, only empty if/case's + if (nodep->elsesp()) { + m_checkBlock = true; iterateAndNextNull(nodep->elsesp()); - if (m_checkBlock && !m_inModOff - && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it - UINFO(4," COVER: "<addElsesp(newCoverInc(nodep->elsesp()->fileline(), "", "v_line", "else")); - } - } - } - m_checkBlock = true; // Reset as a child may have cleared it - } + if (m_checkBlock && !m_inModOff + && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine()) { // if a "else" branch didn't disable it + UINFO(4," COVER: "<addElsesp(newCoverInc(nodep->elsesp()->fileline(), + "", "v_line", "else")); + } + } + } + m_checkBlock = true; // Reset as a child may have cleared it + } } virtual void visit(AstCaseItem* nodep) { - UINFO(4," CASEI: "<fileline()->coverageOn() && v3Global.opt.coverageLine()) { + UINFO(4," CASEI: "<fileline()->coverageOn() && v3Global.opt.coverageLine()) { iterateAndNextNull(nodep->bodysp()); - if (m_checkBlock) { // if the case body didn't disable it - UINFO(4," COVER: "<addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case")); - } - m_checkBlock = true; // Reset as a child may have cleared it - } + if (m_checkBlock) { // if the case body didn't disable it + UINFO(4," COVER: "<addBodysp(newCoverInc(nodep->fileline(), "", "v_line", "case")); + } + m_checkBlock = true; // Reset as a child may have cleared it + } } virtual void visit(AstPslCover* nodep) { - UINFO(4," PSLCOVER: "<coverincp()) { - // Note the name may be overridden by V3Assert processing - nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover")); - } - m_checkBlock = true; // Reset as a child may have cleared it + if (!nodep->coverincp()) { + // Note the name may be overridden by V3Assert processing + nodep->coverincp(newCoverInc(nodep->fileline(), m_beginHier, "v_user", "cover")); + } + m_checkBlock = true; // Reset as a child may have cleared it } virtual void visit(AstStop* nodep) { - UINFO(4," STOP: "<pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { - // Skip all NEXT nodes under this block, and skip this if/case branch - UINFO(4," OFF: "<unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } else { + if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { + // Skip all NEXT nodes under this block, and skip this if/case branch + UINFO(4," OFF: "<unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } else { if (m_checkBlock) iterateChildren(nodep); - } + } } virtual void visit(AstBegin* nodep) { - // Record the hierarchy of any named begins, so we can apply to user - // coverage points. This is because there may be cov points inside - // generate blocks; each point should get separate consideration. - // (Currently ignored for line coverage, since any generate iteration - // covers the code in that line.) - string oldHier = m_beginHier; - bool oldtog = m_inToggleOff; - { - m_inToggleOff = true; - if (nodep->name()!="") { - m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name(); - } + // Record the hierarchy of any named begins, so we can apply to user + // coverage points. This is because there may be cov points inside + // generate blocks; each point should get separate consideration. + // (Currently ignored for line coverage, since any generate iteration + // covers the code in that line.) + string oldHier = m_beginHier; + bool oldtog = m_inToggleOff; + { + m_inToggleOff = true; + if (nodep->name()!="") { + m_beginHier = m_beginHier + (m_beginHier!=""?".":"") + nodep->name(); + } iterateChildren(nodep); - } - m_beginHier = oldHier; - m_inToggleOff = oldtog; + } + m_beginHier = oldHier; + m_inToggleOff = oldtog; } // VISITORS - BOTH virtual void visit(AstNode* nodep) { - // Default: Just iterate - if (m_checkBlock) { + // Default: Just iterate + if (m_checkBlock) { iterateChildren(nodep); - m_checkBlock = true; // Reset as a child may have cleared it - } + m_checkBlock = true; // Reset as a child may have cleared it + } } public: // CONSTUCTORS explicit CoverageVisitor(AstNetlist* rootp) { - // Operate on all modules - m_checkBlock = true; + // Operate on all modules + m_checkBlock = true; m_modp = NULL; - m_beginHier = ""; - m_inToggleOff = false; - m_inModOff = true; + m_beginHier = ""; + m_inToggleOff = false; + m_inModOff = true; iterateChildren(rootp); } virtual ~CoverageVisitor() {} diff --git a/src/V3Coverage.h b/src/V3Coverage.h index fcac9e54f..e3867da03 100644 --- a/src/V3Coverage.h +++ b/src/V3Coverage.h @@ -35,4 +35,4 @@ public: static void coverage(AstNetlist* rootp); }; -#endif // Guard +#endif // Guard diff --git a/src/V3CoverageJoin.cpp b/src/V3CoverageJoin.cpp index 427679fdc..41549f032 100644 --- a/src/V3CoverageJoin.cpp +++ b/src/V3CoverageJoin.cpp @@ -18,7 +18,7 @@ // //************************************************************************* // COVERAGEJOIN TRANSFORMATIONS: -// If two COVERTOGGLEs have same VARSCOPE, combine them +// If two COVERTOGGLEs have same VARSCOPE, combine them //************************************************************************* @@ -40,72 +40,76 @@ class CoverageJoinVisitor : public AstNVisitor { private: // NODE STATE // V3Hashed - // AstCoverToggle->VarRef::user4() // V3Hashed calculation + // AstCoverToggle->VarRef::user4() // V3Hashed calculation - //AstUser4InUse In V3Hashed + //AstUser4InUse In V3Hashed // TYPES typedef std::vector ToggleList; // STATE - ToggleList m_toggleps; // List of of all AstCoverToggle's + ToggleList m_toggleps; // List of of all AstCoverToggle's - V3Double0 m_statToggleJoins; // Statistic tracking + V3Double0 m_statToggleJoins; // Statistic tracking // METHODS VL_DEBUG_FUNC; // Declare debug() void detectDuplicates() { - UINFO(9,"Finding duplicates\n"); - // Note uses user4 - V3Hashed hashed; // Duplicate code detection - // Hash all of the original signals we toggle cover - for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) { - AstCoverToggle* nodep = *it; - hashed.hashAndInsert(nodep->origp()); - } - // Find if there are any duplicates - for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) { - AstCoverToggle* nodep = *it; - if (nodep->backp()) { // nodep->backp() is null if we already detected it's a duplicate and unlinked it - // Want to choose a base node, and keep finding duplicates that are identical - // This prevents making chains where a->b, then c->d, then b->c, as we'll find a->b, a->c, a->d directly. - while (1) { - V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp()); - if (dupit == hashed.end()) break; - // - AstNode* duporigp = hashed.iteratorNodep(dupit); - // Note hashed will point to the original variable (what's duplicated), not the covertoggle, - // but we need to get back to the covertoggle which is immediately above, so: + UINFO(9,"Finding duplicates\n"); + // Note uses user4 + V3Hashed hashed; // Duplicate code detection + // Hash all of the original signals we toggle cover + for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) { + AstCoverToggle* nodep = *it; + hashed.hashAndInsert(nodep->origp()); + } + // Find if there are any duplicates + for (ToggleList::iterator it = m_toggleps.begin(); it != m_toggleps.end(); ++it) { + AstCoverToggle* nodep = *it; + // nodep->backp() is null if we already detected it's a duplicate and unlinked it. + if (nodep->backp()) { + // Want to choose a base node, and keep finding duplicates that are identical. + // This prevents making chains where a->b, then c->d, then b->c, as we'll + // find a->b, a->c, a->d directly. + while (1) { + V3Hashed::iterator dupit = hashed.findDuplicate(nodep->origp()); + if (dupit == hashed.end()) break; + // + AstNode* duporigp = hashed.iteratorNodep(dupit); + // Note hashed will point to the original variable (what's + // duplicated), not the covertoggle, but we need to get back to the + // covertoggle which is immediately above, so: AstCoverToggle* removep = VN_CAST(duporigp->backp(), CoverToggle); - if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type"); - UINFO(8," Orig "<> "<incp()->declp()<> "<incp()->declp()<incp()->declp()->dataDeclThisp(); + if (!removep) nodep->v3fatalSrc("CoverageJoin duplicate of wrong type"); + UINFO(8," Orig "<> "<incp()->declp()<> "<incp()->declp()<incp()->declp()->dataDeclThisp(); removep->incp()->declp()->dataDeclp(datadeclp); - UINFO(8," new "<incp()->declp()<unlinkFrBack(); pushDeletep(removep); VL_DANGLING(removep); - // Remove node from comparison so don't hit it again - hashed.erase(dupit); - ++m_statToggleJoins; - } - } - } + UINFO(8," new "<incp()->declp()<unlinkFrBack(); pushDeletep(removep); VL_DANGLING(removep); + // Remove node from comparison so don't hit it again + hashed.erase(dupit); + ++m_statToggleJoins; + } + } + } } // VISITORS virtual void visit(AstNetlist* nodep) { - // Find all Coverage's + // Find all Coverage's iterateChildren(nodep); - // Simplify - detectDuplicates(); + // Simplify + detectDuplicates(); } virtual void visit(AstCoverToggle* nodep) { - m_toggleps.push_back(nodep); + m_toggleps.push_back(nodep); iterateChildren(nodep); } //-------------------- @@ -120,7 +124,7 @@ public: iterate(nodep); } virtual ~CoverageJoinVisitor() { - V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins); + V3Stats::addStat("Coverage, Toggle points joined", m_statToggleJoins); } }; diff --git a/src/V3CoverageJoin.h b/src/V3CoverageJoin.h index b61a6a7d4..b91db7cad 100644 --- a/src/V3CoverageJoin.h +++ b/src/V3CoverageJoin.h @@ -35,4 +35,4 @@ public: static void coverageJoin(AstNetlist* rootp); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index d1c5829e9..9277de7e9 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -18,8 +18,8 @@ // //************************************************************************* // DEAD TRANSFORMATIONS: -// Remove any unreferenced modules -// Remove any unreferenced variables +// Remove any unreferenced modules +// Remove any unreferenced variables // // TODO: A graph would make the process of circular and interlinked // dependencies easier to resolve. @@ -59,7 +59,7 @@ private: // VISITORS virtual void visit(AstCell* nodep) { iterateChildren(nodep); - nodep->modp()->user1Inc(-1); + nodep->modp()->user1Inc(-1); } //----- virtual void visit(AstNodeMath* nodep) {} // Accelerate @@ -81,337 +81,341 @@ class DeadVisitor : public AstNVisitor { private: // NODE STATE // Entire Netlist: - // AstNodeModule::user1() -> int. Count of number of cells referencing this module. - // AstVar::user1() -> int. Count of number of references - // AstVarScope::user1() -> int. Count of number of references - // AstNodeDType::user1() -> int. Count of number of references - AstUser1InUse m_inuser1; + // AstNodeModule::user1() -> int. Count of number of cells referencing this module. + // AstVar::user1() -> int. Count of number of references + // AstVarScope::user1() -> int. Count of number of references + // AstNodeDType::user1() -> int. Count of number of references + AstUser1InUse m_inuser1; // TYPES - typedef std::multimap AssignMap; + typedef std::multimap AssignMap; // STATE - AstNodeModule* m_modp; // Current module + AstNodeModule* m_modp; // Current module std::vector m_varsp; // List of all encountered to avoid another loop through tree std::vector m_dtypesp; // List of all encountered to avoid another loop through tree std::vector m_vscsp; // List of all encountered to avoid another loop through tree std::vector m_scopesp; // List of all encountered to avoid another loop through tree std::vector m_cellsp; // List of all encountered to avoid another loop through tree - AssignMap m_assignMap; // List of all simple assignments for each variable - bool m_elimUserVars; // Allow removal of user's vars - bool m_elimDTypes; // Allow removal of DTypes - bool m_elimScopes; // Allow removal of Scopes - bool m_elimCells; // Allow removal of Cells - bool m_sideEffect; // Side effects discovered in assign RHS + AssignMap m_assignMap; // List of all simple assignments for each variable + bool m_elimUserVars; // Allow removal of user's vars + bool m_elimDTypes; // Allow removal of DTypes + bool m_elimScopes; // Allow removal of Scopes + bool m_elimCells; // Allow removal of Cells + bool m_sideEffect; // Side effects discovered in assign RHS // METHODS VL_DEBUG_FUNC; // Declare debug() void checkAll(AstNode* nodep) { - if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves - if (AstNode* subnodep = nodep->dtypep()) { - subnodep->user1Inc(); - } - } - if (AstNode* subnodep = nodep->getChildDTypep()) { - subnodep->user1Inc(); - } + if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves + if (AstNode* subnodep = nodep->dtypep()) { + subnodep->user1Inc(); + } + } + if (AstNode* subnodep = nodep->getChildDTypep()) { + subnodep->user1Inc(); + } } void checkDType(AstNodeDType* nodep) { - if (!nodep->generic() // Don't remove generic types - && m_elimDTypes // dtypes stick around until post-widthing + if (!nodep->generic() // Don't remove generic types + && m_elimDTypes // dtypes stick around until post-widthing && !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists - ) { - m_dtypesp.push_back(nodep); - } - if (AstNode* subnodep = nodep->virtRefDTypep()) { - subnodep->user1Inc(); - } + ) { + m_dtypesp.push_back(nodep); + } + if (AstNode* subnodep = nodep->virtRefDTypep()) { + subnodep->user1Inc(); + } } // VISITORS virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; - if (!nodep->dead()) { + m_modp = nodep; + if (!nodep->dead()) { iterateChildren(nodep); - checkAll(nodep); - } - m_modp = NULL; + checkAll(nodep); + } + m_modp = NULL; } virtual void visit(AstCFunc* nodep) { iterateChildren(nodep); - checkAll(nodep); - if (nodep->scopep()) nodep->scopep()->user1Inc(); + checkAll(nodep); + if (nodep->scopep()) nodep->scopep()->user1Inc(); } virtual void visit(AstScope* nodep) { iterateChildren(nodep); - checkAll(nodep); - if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc(); + checkAll(nodep); + if (nodep->aboveScopep()) nodep->aboveScopep()->user1Inc(); - if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp() && !nodep->finalClksp()) { - m_scopesp.push_back(nodep); - } + if (!nodep->isTop() && !nodep->varsp() && !nodep->blocksp() && !nodep->finalClksp()) { + m_scopesp.push_back(nodep); + } } virtual void visit(AstCell* nodep) { iterateChildren(nodep); - checkAll(nodep); - m_cellsp.push_back(nodep); - nodep->modp()->user1Inc(); + checkAll(nodep); + m_cellsp.push_back(nodep); + nodep->modp()->user1Inc(); } virtual void visit(AstNodeVarRef* nodep) { iterateChildren(nodep); - checkAll(nodep); - if (nodep->varScopep()) { - nodep->varScopep()->user1Inc(); - nodep->varScopep()->varp()->user1Inc(); - } - if (nodep->varp()) { - nodep->varp()->user1Inc(); - } - if (nodep->packagep()) { - if (m_elimCells) nodep->packagep(NULL); - else nodep->packagep()->user1Inc(); - } + checkAll(nodep); + if (nodep->varScopep()) { + nodep->varScopep()->user1Inc(); + nodep->varScopep()->varp()->user1Inc(); + } + if (nodep->varp()) { + nodep->varp()->user1Inc(); + } + if (nodep->packagep()) { + if (m_elimCells) nodep->packagep(NULL); + else nodep->packagep()->user1Inc(); + } } virtual void visit(AstNodeFTaskRef* nodep) { iterateChildren(nodep); - checkAll(nodep); - if (nodep->packagep()) { - if (m_elimCells) nodep->packagep(NULL); - else nodep->packagep()->user1Inc(); - } + checkAll(nodep); + if (nodep->packagep()) { + if (m_elimCells) nodep->packagep(NULL); + else nodep->packagep()->user1Inc(); + } } virtual void visit(AstRefDType* nodep) { iterateChildren(nodep); - checkDType(nodep); - checkAll(nodep); - if (nodep->packagep()) { - if (m_elimCells) nodep->packagep(NULL); - else nodep->packagep()->user1Inc(); - } + checkDType(nodep); + checkAll(nodep); + if (nodep->packagep()) { + if (m_elimCells) nodep->packagep(NULL); + else nodep->packagep()->user1Inc(); + } } virtual void visit(AstNodeDType* nodep) { iterateChildren(nodep); - checkDType(nodep); - checkAll(nodep); + checkDType(nodep); + checkAll(nodep); } virtual void visit(AstEnumItemRef* nodep) { iterateChildren(nodep); - checkAll(nodep); - if (nodep->packagep()) { - if (m_elimCells) nodep->packagep(NULL); - else nodep->packagep()->user1Inc(); - } - checkAll(nodep); + checkAll(nodep); + if (nodep->packagep()) { + if (m_elimCells) nodep->packagep(NULL); + else nodep->packagep()->user1Inc(); + } + checkAll(nodep); } virtual void visit(AstModport* nodep) { iterateChildren(nodep); - if (m_elimCells) { - if (!nodep->varsp()) { - pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); - return; - } - } - checkAll(nodep); + if (m_elimCells) { + if (!nodep->varsp()) { + pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); + return; + } + } + checkAll(nodep); } virtual void visit(AstTypedef* nodep) { iterateChildren(nodep); - if (m_elimCells && !nodep->attrPublic()) { - pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); - return; - } - checkAll(nodep); - // Don't let packages with only public variables disappear - // Normal modules may disappear, e.g. if they are parameterized then removed + if (m_elimCells && !nodep->attrPublic()) { + pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); + return; + } + checkAll(nodep); + // Don't let packages with only public variables disappear + // Normal modules may disappear, e.g. if they are parameterized then removed if (nodep->attrPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc(); } virtual void visit(AstVarScope* nodep) { iterateChildren(nodep); - checkAll(nodep); - if (nodep->scopep()) nodep->scopep()->user1Inc(); - if (mightElimVar(nodep->varp())) { - m_vscsp.push_back(nodep); - } + checkAll(nodep); + if (nodep->scopep()) nodep->scopep()->user1Inc(); + if (mightElimVar(nodep->varp())) { + m_vscsp.push_back(nodep); + } } virtual void visit(AstVar* nodep) { iterateChildren(nodep); - checkAll(nodep); + checkAll(nodep); if (nodep->isSigPublic() && m_modp && VN_IS(m_modp, Package)) m_modp->user1Inc(); - if (mightElimVar(nodep)) { - m_varsp.push_back(nodep); - } + if (mightElimVar(nodep)) { + m_varsp.push_back(nodep); + } } virtual void visit(AstNodeAssign* nodep) { - // See if simple assignments to variables may be eliminated because that variable is never used. - // Similar code in V3Life - m_sideEffect = false; + // See if simple assignments to variables may be eliminated because + // that variable is never used. + // Similar code in V3Life + m_sideEffect = false; iterateAndNextNull(nodep->rhsp()); - checkAll(nodep); - // Has to be direct assignment without any EXTRACTing. + checkAll(nodep); + // Has to be direct assignment without any EXTRACTing. AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef); - if (varrefp && !m_sideEffect - && varrefp->varScopep()) { // For simplicity, we only remove post-scoping - m_assignMap.insert(make_pair(varrefp->varScopep(), nodep)); - checkAll(varrefp); // Must track reference to dtype() - } else { // Track like any other statement + if (varrefp && !m_sideEffect + && varrefp->varScopep()) { // For simplicity, we only remove post-scoping + m_assignMap.insert(make_pair(varrefp->varScopep(), nodep)); + checkAll(varrefp); // Must track reference to dtype() + } else { // Track like any other statement iterateAndNextNull(nodep->lhsp()); - } - checkAll(nodep); + } + checkAll(nodep); } //----- virtual void visit(AstNode* nodep) { - if (nodep->isOutputter()) m_sideEffect=true; + if (nodep->isOutputter()) m_sideEffect = true; iterateChildren(nodep); - checkAll(nodep); + checkAll(nodep); } // METHODS void deadCheckMod() { - // Kill any unused modules - // V3LinkCells has a graph that is capable of this too, but we need to do it - // after we've done all the generate blocks - for (bool retry=true; retry; ) { - retry=false; - AstNodeModule* nextmodp; - for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) { + // Kill any unused modules + // V3LinkCells has a graph that is capable of this too, but we need to do it + // after we've done all the generate blocks + for (bool retry=true; retry; ) { + retry = false; + AstNodeModule* nextmodp; + for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) { nextmodp = VN_CAST(modp->nextp(), NodeModule); - if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) { - // > 2 because L1 is the wrapper, L2 is the top user module - UINFO(4," Dead module "<dead()) { // If was dead didn't increment user1's - DeadModVisitor visitor(modp); - } - modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp); - retry = true; - } - } - } + if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) { + // > 2 because L1 is the wrapper, L2 is the top user module + UINFO(4," Dead module "<dead()) { // If was dead didn't increment user1's + DeadModVisitor visitor(modp); + } + modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp); + retry = true; + } + } + } } bool mightElimVar(AstVar* nodep) { - return (!nodep->isSigPublic() // Can't elim publics! - && !nodep->isIO() - && (nodep->isTemp() - || (nodep->isParam() && !nodep->isTrace()) - || m_elimUserVars)); // Post-Trace can kill most anything + return (!nodep->isSigPublic() // Can't elim publics! + && !nodep->isIO() + && (nodep->isTemp() + || (nodep->isParam() && !nodep->isTrace()) + || m_elimUserVars)); // Post-Trace can kill most anything } void deadCheckScope() { - for (bool retry=true; retry; ) { - retry = false; - for (std::vector::iterator it = m_scopesp.begin(); it != m_scopesp.end();++it) { - AstScope* scp = *it; - if (!scp) - continue; - if (scp->user1() == 0) { + for (bool retry=true; retry; ) { + retry = false; + for (std::vector::iterator it = m_scopesp.begin(); + it != m_scopesp.end();++it) { + AstScope* scp = *it; + if (!scp) + continue; + if (scp->user1() == 0) { UINFO(4, " Dead AstScope " << scp << endl); - scp->aboveScopep()->user1Inc(-1); - if (scp->dtypep()) { - scp->dtypep()->user1Inc(-1); - } - scp->unlinkFrBack()->deleteTree(); VL_DANGLING(scp); - *it = NULL; - retry = true; - } - } - } + scp->aboveScopep()->user1Inc(-1); + if (scp->dtypep()) { + scp->dtypep()->user1Inc(-1); + } + scp->unlinkFrBack()->deleteTree(); VL_DANGLING(scp); + *it = NULL; + retry = true; + } + } + } } void deadCheckCells() { for (std::vector::iterator it = m_cellsp.begin(); it!=m_cellsp.end(); ++it) { - AstCell* cellp = *it; - if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) { - cellp->modp()->user1Inc(-1); - cellp->unlinkFrBack()->deleteTree(); VL_DANGLING(cellp); - } - } + AstCell* cellp = *it; + if (cellp->user1() == 0 && !cellp->modp()->stmtsp()) { + cellp->modp()->user1Inc(-1); + cellp->unlinkFrBack()->deleteTree(); VL_DANGLING(cellp); + } + } } void deadCheckVar() { - // Delete any unused varscopes + // Delete any unused varscopes for (std::vector::iterator it = m_vscsp.begin(); it!=m_vscsp.end(); ++it) { - AstVarScope* vscp = *it; - if (vscp->user1() == 0) { - UINFO(4," Dead "< eqrange = m_assignMap.equal_range(vscp); - for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) { - AstNodeAssign* assp = itr->second; + AstVarScope* vscp = *it; + if (vscp->user1() == 0) { + UINFO(4," Dead "< eqrange + = m_assignMap.equal_range(vscp); + for (AssignMap::iterator itr = eqrange.first; itr != eqrange.second; ++itr) { + AstNodeAssign* assp = itr->second; UINFO(4," Dead assign "<dtypep()->user1Inc(-1); - assp->unlinkFrBack()->deleteTree(); VL_DANGLING(assp); - } - if (vscp->scopep()) vscp->scopep()->user1Inc(-1); - vscp->dtypep()->user1Inc(-1); - vscp->unlinkFrBack()->deleteTree(); VL_DANGLING(vscp); - } - } - for (bool retry=true; retry; ) { - retry = false; + assp->dtypep()->user1Inc(-1); + assp->unlinkFrBack()->deleteTree(); VL_DANGLING(assp); + } + if (vscp->scopep()) vscp->scopep()->user1Inc(-1); + vscp->dtypep()->user1Inc(-1); + vscp->unlinkFrBack()->deleteTree(); VL_DANGLING(vscp); + } + } + for (bool retry=true; retry; ) { + retry = false; for (std::vector::iterator it = m_varsp.begin(); it != m_varsp.end();++it) { - AstVar* varp = *it; - if (!varp) - continue; - if (varp->user1() == 0) { + AstVar* varp = *it; + if (!varp) + continue; + if (varp->user1() == 0) { UINFO(4, " Dead " << varp << endl); - if (varp->dtypep()) { - varp->dtypep()->user1Inc(-1); - } - varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp); - *it = NULL; - retry = true; - } - } - } + if (varp->dtypep()) { + varp->dtypep()->user1Inc(-1); + } + varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp); + *it = NULL; + retry = true; + } + } + } for (std::vector::iterator it = m_dtypesp.begin(); it != m_dtypesp.end();++it) { - if ((*it)->user1() == 0) { - AstNodeClassDType *classp; - // It's possible that there if a reference to each individual member, but - // not to the dtype itself. Check and don't remove the parent dtype if - // members are still alive. + if ((*it)->user1() == 0) { + AstNodeClassDType *classp; + // It's possible that there if a reference to each individual member, but + // not to the dtype itself. Check and don't remove the parent dtype if + // members are still alive. if ((classp = VN_CAST((*it), NodeClassDType))) { - bool cont = true; + bool cont = true; for (AstMemberDType *memberp = classp->membersp(); memberp; memberp = VN_CAST(memberp->nextp(), MemberDType)) { - if (memberp->user1() != 0) { - cont = false; - break; - } - } - if (!cont) - continue; - } - (*it)->unlinkFrBack()->deleteTree(); VL_DANGLING(*it); - } - } + if (memberp->user1() != 0) { + cont = false; + break; + } + } + if (!cont) + continue; + } + (*it)->unlinkFrBack()->deleteTree(); VL_DANGLING(*it); + } + } } public: // CONSTRUCTORS - DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes, bool elimCells) { - m_modp = NULL; - m_elimCells = elimCells; - m_elimUserVars = elimUserVars; - m_elimDTypes = elimDTypes; - m_elimScopes = elimScopes; - m_sideEffect = false; - // Prepare to remove some datatypes - nodep->typeTablep()->clearCache(); - // Operate on whole netlist + DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, + bool elimScopes, bool elimCells) { + m_modp = NULL; + m_elimCells = elimCells; + m_elimUserVars = elimUserVars; + m_elimDTypes = elimDTypes; + m_elimScopes = elimScopes; + m_sideEffect = false; + // Prepare to remove some datatypes + nodep->typeTablep()->clearCache(); + // Operate on whole netlist iterate(nodep); - deadCheckVar(); - // We only elimate scopes when in a flattened structure - // Otherwise we have no easy way to know if a scope is used - if (elimScopes) deadCheckScope(); - if (elimCells) deadCheckCells(); - // Modules after vars, because might be vars we delete inside a mod we delete - deadCheckMod(); + deadCheckVar(); + // We only elimate scopes when in a flattened structure + // Otherwise we have no easy way to know if a scope is used + if (elimScopes) deadCheckScope(); + if (elimCells) deadCheckCells(); + // Modules after vars, because might be vars we delete inside a mod we delete + deadCheckMod(); - // We may have removed some datatypes, cleanup - nodep->typeTablep()->repairCache(); + // We may have removed some datatypes, cleanup + nodep->typeTablep()->repairCache(); } virtual ~DeadVisitor() {} }; diff --git a/src/V3Dead.h b/src/V3Dead.h index f272a8a24..a74328f60 100644 --- a/src/V3Dead.h +++ b/src/V3Dead.h @@ -41,4 +41,4 @@ public: static void deadifyAllScoped(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 0dea2804a..52829304c 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -20,34 +20,35 @@ // V3Delayed's Transformations: // // Each module: -// Replace ASSIGNDLY var, exp -// With ASSIGNDLY newvar, exp -// At top of block: VAR newvar -// At bottom of block: ASSIGNW var newvar -// Need _x_dly = x at top of active if "x" is not always set -// For now we'll say it's set if at top of block (not under IF, etc) -// Need x = _x_dly at bottom of active if "x" is never referenced on LHS -// in the active, and above rule applies too. (If so, use x on LHS, not _x_dly.) +// Replace ASSIGNDLY var, exp +// With ASSIGNDLY newvar, exp +// At top of block: VAR newvar +// At bottom of block: ASSIGNW var newvar +// Need _x_dly = x at top of active if "x" is not always set +// For now we'll say it's set if at top of block (not under IF, etc) +// Need x = _x_dly at bottom of active if "x" is never referenced on LHS +// in the active, and above rule applies too. +// (If so, use x on LHS, not _x_dly.) // -// If a signal is set in multiple always blocks, we need a dly read & set with -// multiple clock sensitivities. We have 3 options: -// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there -// 2. Form unique ACTIVE for every multiple clocked assignment -// 3. Predetect signals from multiple always blocks and do #2 on them -// Since all 3 require a top activation cleanup, we do #2 which is easiest. +// If a signal is set in multiple always blocks, we need a dly read & set with +// multiple clock sensitivities. We have 3 options: +// 1. When detected, make a new ACTIVE and move earlier created delayed assignment there +// 2. Form unique ACTIVE for every multiple clocked assignment +// 3. Predetect signals from multiple always blocks and do #2 on them +// Since all 3 require a top activation cleanup, we do #2 which is easiest. // // ASSIGNDLY (BITSEL(ARRAYSEL (VARREF(v), bits), selbits), rhs) -// -> VAR __Vdlyvset_x -// VAR __Vdlyvval_x -// VAR __Vdlyvdim_x -// VAR __Vdlyvlsb_x -// ASSIGNW (__Vdlyvset_x,0) -// ... -// ASSIGNW (VARREF(__Vdlyvval_x), rhs) -// ASSIGNW (__Vdlyvdim_x, dimension_number) -// ASSIGNW (__Vdlyvset_x, 1) -// ... -// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x) +// -> VAR __Vdlyvset_x +// VAR __Vdlyvval_x +// VAR __Vdlyvdim_x +// VAR __Vdlyvlsb_x +// ASSIGNW (__Vdlyvset_x,0) +// ... +// ASSIGNW (VARREF(__Vdlyvval_x), rhs) +// ASSIGNW (__Vdlyvdim_x, dimension_number) +// ASSIGNW (__Vdlyvset_x, 1) +// ... +// ASSIGNW (BITSEL(ARRAYSEL(VARREF(x), __Vdlyvdim_x), __Vdlyvlsb_x), __Vdlyvval_x) // //************************************************************************* @@ -71,76 +72,81 @@ class DelayedVisitor : public AstNVisitor { private: // NODE STATE // Cleared each module: - // AstVarScope::user1p() -> AstVarScope*. Points to temp var created. - // AstVarScope::user2p() -> AstActive*. Points to activity block of signal (valid when AstVarScope::user1p is valid) - // AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable - // AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage - // AstVar::user2() -> bool. Set true if already made warning - // AstVarRef::user2() -> bool. Set true if already processed - // AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal (valid when AstAlwaysPost::user4p is valid) - // AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost + // AstVarScope::user1p() -> AstVarScope*. Points to temp var created. + // AstVarScope::user2p() -> AstActive*. Points to activity block of signal + // (valid when AstVarScope::user1p is valid) + // AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable + // AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage + // AstVar::user2() -> bool. Set true if already made warning + // AstVarRef::user2() -> bool. Set true if already processed + // AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal + // (valid when AstAlwaysPost::user4p is valid) + // AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost // Cleared each scope/active: - // AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign - // AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; - AstUser4InUse m_inuser4; - AstUser5InUse m_inuser5; + // AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign + // AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; + AstUser4InUse m_inuser4; + AstUser5InUse m_inuser5; enum VarUsage { VU_NONE=0, VU_DLY=1, VU_NONDLY=2 }; // STATE - AstActive* m_activep; // Current activate - AstCFunc* m_cfuncp; // Current public C Function - AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments - bool m_inDly; // True in delayed assignments - bool m_inLoop; // True in for loops - bool m_inInitial; // True in intial blocks + AstActive* m_activep; // Current activate + AstCFunc* m_cfuncp; // Current public C Function + AstAssignDly* m_nextDlyp; // Next delayed assignment in a list of assignments + bool m_inDly; // True in delayed assignments + bool m_inLoop; // True in for loops + bool m_inInitial; // True in intial blocks typedef std::map,AstVar*> VarMap; - VarMap m_modVarMap; // Table of new var names created under module - V3Double0 m_statSharedSet;// Statistic tracking + VarMap m_modVarMap; // Table of new var names created under module + V3Double0 m_statSharedSet;// Statistic tracking typedef std::map ScopeVecMap; - ScopeVecMap m_scopeVecMap; // Next var number for each scope + ScopeVecMap m_scopeVecMap; // Next var number for each scope // METHODS VL_DEBUG_FUNC; // Declare debug() void markVarUsage(AstVarScope* nodep, uint32_t flags) { - //UINFO(4," MVU "<user5( nodep->user5() | flags ); - if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) { - nodep->v3warn(BLKANDNBLK,"Unsupported: Blocked and non-blocking assignments to same variable: " + //UINFO(4," MVU "<user5( nodep->user5() | flags ); + if ((nodep->user5() & VU_DLY) && (nodep->user5() & VU_NONDLY)) { + nodep->v3warn(BLKANDNBLK, "Unsupported: Blocked and non-blocking assignments to same variable: " <varp()->prettyName()); - } + } } AstVarScope* createVarSc(AstVarScope* oldvarscp, const string& name, int width/*0==fromoldvar*/, AstNodeDType* newdtypep) { - // Because we've already scoped it, we may need to add both the AstVar and the AstVarScope - if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped"); - AstVar* varp; - AstNodeModule* addmodp = oldvarscp->scopep()->modp(); - // We need a new AstVar, but only one for all scopes, to match the new AstVarScope - VarMap::iterator it = m_modVarMap.find(make_pair(addmodp,name)); - if (it != m_modVarMap.end()) { - // Created module's AstVar earlier under some other scope - varp = it->second; - } else { - if (newdtypep) { - varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, newdtypep); - } else if (width==0) { - varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, oldvarscp->varp()); - varp->dtypeFrom(oldvarscp); - } else { // Used for vset and dimensions, so can zero init - varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, name, VFlagBitPacked(), width); - } - addmodp->addStmtp(varp); - m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp)); - } + // Because we've already scoped it, we may need to add both the AstVar and the AstVarScope + if (!oldvarscp->scopep()) oldvarscp->v3fatalSrc("Var unscoped"); + AstVar* varp; + AstNodeModule* addmodp = oldvarscp->scopep()->modp(); + // We need a new AstVar, but only one for all scopes, to match the new AstVarScope + VarMap::iterator it = m_modVarMap.find(make_pair(addmodp, name)); + if (it != m_modVarMap.end()) { + // Created module's AstVar earlier under some other scope + varp = it->second; + } else { + if (newdtypep) { + varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, + name, newdtypep); + } else if (width==0) { + varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, + name, oldvarscp->varp()); + varp->dtypeFrom(oldvarscp); + } else { // Used for vset and dimensions, so can zero init + varp = new AstVar(oldvarscp->fileline(), AstVarType::BLOCKTEMP, + name, VFlagBitPacked(), width); + } + addmodp->addStmtp(varp); + m_modVarMap.insert(make_pair(make_pair(addmodp, name), varp)); + } AstVarScope* varscp = new AstVarScope(oldvarscp->fileline(), oldvarscp->scopep(), varp); - oldvarscp->scopep()->addVarp(varscp); - return varscp; + oldvarscp->scopep()->addVarp(varscp); + return varscp; } AstActive* createActivePost(AstVarRef* varrefp) { @@ -148,299 +154,302 @@ private: m_activep->sensesp()); // Was addNext(), but addNextHere() avoids a linear search. m_activep->addNextHere(newactp); - return newactp; + return newactp; } void checkActivePost(AstVarRef* varrefp, AstActive* oldactivep) { - // Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree - if (!oldactivep) varrefp->v3fatalSrc("<= old dly assignment not put under sensitivity block"); - if (oldactivep->sensesp() != m_activep->sensesp()) { - if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN) - && !varrefp->varp()->user2()) { - varrefp->varp()->v3warn(MULTIDRIVEN,"Signal has multiple driving blocks with different clocking: " - <varp()->prettyName()<warnMore()<<"... Location of first driving block"<warnMore()<<"... Location of other driving block"); - varrefp->varp()->user2(true); - } - UINFO(4,"AssignDupDlyVar: "<sensesp()->sensesp()->cloneTree(true); - AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true); - AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena); - if (senb) treep->addSensesp(senb); - if (AstSenTree* storep = oldactivep->sensesStorep()) { - storep->unlinkFrBack(); - pushDeletep(storep); - } - oldactivep->sensesStorep(treep); - oldactivep->sensesp(treep); - } + // Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree + if (!oldactivep) varrefp->v3fatalSrc("<= old dly assignment not put under sensitivity block"); + if (oldactivep->sensesp() != m_activep->sensesp()) { + if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN) + && !varrefp->varp()->user2()) { + varrefp->varp()->v3warn( + MULTIDRIVEN, "Signal has multiple driving blocks with different clocking: " + <varp()->prettyName()<warnMore()<<"... Location of first driving block"<warnMore()<<"... Location of other driving block"); + varrefp->varp()->user2(true); + } + UINFO(4,"AssignDupDlyVar: "<sensesp()->sensesp()->cloneTree(true); + AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true); + AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena); + if (senb) treep->addSensesp(senb); + if (AstSenTree* storep = oldactivep->sensesStorep()) { + storep->unlinkFrBack(); + pushDeletep(storep); + } + oldactivep->sensesStorep(treep); + oldactivep->sensesp(treep); + } } AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) { - // Create delayed assignment - // See top of this file for transformation - // Return the new LHS for the assignment, Null = unlink - // Find selects - AstNode* newlhsp = NULL; // NULL = unlink old assign - AstSel* bitselp = NULL; - AstArraySel* arrayselp = NULL; + // Create delayed assignment + // See top of this file for transformation + // Return the new LHS for the assignment, Null = unlink + // Find selects + AstNode* newlhsp = NULL; // NULL = unlink old assign + AstSel* bitselp = NULL; + AstArraySel* arrayselp = NULL; if (VN_IS(lhsp, Sel)) { bitselp = VN_CAST(lhsp, Sel); arrayselp = VN_CAST(bitselp->fromp(), ArraySel); - } else { + } else { arrayselp = VN_CAST(lhsp, ArraySel); - } - if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?"); + } + if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?"); if (VN_IS(arrayselp->dtypep()->skipRefp(), UnpackArrayDType)) { nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice"); } - UINFO(4,"AssignDlyArray: "< dimvalp; // Assignment value for each dimension of assignment - AstNode* dimselp=arrayselp; + UINFO(4,"AssignDlyArray: "< dimvalp; // Assignment value for each dimension of assignment + AstNode* dimselp = arrayselp; for (; VN_IS(dimselp, ArraySel); dimselp=VN_CAST(dimselp, ArraySel)->fromp()) { AstNode* valp = VN_CAST(dimselp, ArraySel)->bitp()->unlinkFrBack(); - dimvalp.push_front(valp); - } + dimvalp.push_front(valp); + } AstVarRef* varrefp = VN_CAST(dimselp, VarRef); - if (!varrefp) nodep->v3fatalSrc("No var underneath arraysels"); - if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); - varrefp->unlinkFrBack(); - AstVar* oldvarp = varrefp->varp(); - int modVecNum = m_scopeVecMap[varrefp->varScopep()]++; - // - std::deque dimreadps; // Read value for each dimension of assignment - for (unsigned dimension=0; dimensionv3fatalSrc("No var underneath arraysels"); + if (!varrefp->varScopep()) varrefp->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); + varrefp->unlinkFrBack(); + AstVar* oldvarp = varrefp->varp(); + int modVecNum = m_scopeVecMap[varrefp->varScopep()]++; + // + std::deque dimreadps; // Read value for each dimension of assignment + for (unsigned dimension=0; dimensionshortName()+"__v"+cvtToStr(modVecNum)); - AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, dimp->width(), NULL); - AstAssign* bitassignp + dimreadps.push_front(dimp); + } else { + string bitvarname = (string("__Vdlyvdim")+cvtToStr(dimension) + +"__"+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); + AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), + bitvarname, dimp->width(), NULL); + AstAssign* bitassignp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, true), dimp); - nodep->addNextHere(bitassignp); - dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false)); - } - } - // - //=== Bitselect: __Vdlyvlsb__ - AstNode* bitreadp=NULL; // Code to read Vdlyvlsb - if (bitselp) { - AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack(); + nodep->addNextHere(bitassignp); + dimreadps.push_front(new AstVarRef(nodep->fileline(), bitvscp, false)); + } + } + // + //=== Bitselect: __Vdlyvlsb__ + AstNode* bitreadp = NULL; // Code to read Vdlyvlsb + if (bitselp) { + AstNode* lsbvaluep = bitselp->lsbp()->unlinkFrBack(); if (VN_IS(bitselp->fromp(), Const)) { // vlsb = constant, can just push constant into where we use it - bitreadp = lsbvaluep; - } else { - string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); - AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), bitvarname, lsbvaluep->width(), NULL); + bitreadp = lsbvaluep; + } else { + string bitvarname = (string("__Vdlyvlsb__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); + AstVarScope* bitvscp = createVarSc(varrefp->varScopep(), + bitvarname, lsbvaluep->width(), NULL); AstAssign* bitassignp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), bitvscp, true), lsbvaluep); - nodep->addNextHere(bitassignp); - bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false); - } - } - // - //=== Value: __Vdlyvval__ - AstNode* valreadp; // Code to read Vdlyvval + nodep->addNextHere(bitassignp); + bitreadp = new AstVarRef(nodep->fileline(), bitvscp, false); + } + } + // + //=== Value: __Vdlyvval__ + AstNode* valreadp; // Code to read Vdlyvval if (VN_IS(nodep->rhsp(), Const)) { // vval = constant, can just push constant into where we use it - valreadp = nodep->rhsp()->unlinkFrBack(); - } else { - string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); - AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep()); - newlhsp = new AstVarRef(nodep->fileline(), valvscp, true); - valreadp = new AstVarRef(nodep->fileline(), valvscp, false); - } - // - //=== Setting/not setting boolean: __Vdlyvset__ - AstVarScope* setvscp; - AstAssignPre* setinitp = NULL; + valreadp = nodep->rhsp()->unlinkFrBack(); + } else { + string valvarname = (string("__Vdlyvval__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); + AstVarScope* valvscp = createVarSc(varrefp->varScopep(), valvarname, 0, nodep->rhsp()->dtypep()); + newlhsp = new AstVarRef(nodep->fileline(), valvscp, true); + valreadp = new AstVarRef(nodep->fileline(), valvscp, false); + } + // + //=== Setting/not setting boolean: __Vdlyvset__ + AstVarScope* setvscp; + AstAssignPre* setinitp = NULL; - if (nodep->user3p()) { - // Simplistic optimization. If the previous statement in same scope was also a =>, - // then we told this nodep->user3 we can use its Vdlyvset rather than making a new one. - // This is good for code like: - // for (i=0; i<5; i++) vector[i] <= something; + if (nodep->user3p()) { + // Simplistic optimization. If the previous statement in same scope was also a =>, + // then we told this nodep->user3 we can use its Vdlyvset rather than making a new one. + // This is good for code like: + // for (i=0; i<5; i++) vector[i] <= something; setvscp = VN_CAST(nodep->user3p(), VarScope); - ++m_statSharedSet; - } else { // Create new one - string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); - setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL); + ++m_statSharedSet; + } else { // Create new one + string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); + setvscp = createVarSc(varrefp->varScopep(), setvarname, 1, NULL); setinitp = new AstAssignPre(nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, true), new AstConst(nodep->fileline(), 0)); - AstAssign* setassignp + AstAssign* setassignp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, true), new AstConst(nodep->fileline(), V3Number(nodep, 1, true))); nodep->addNextHere(setassignp); } - if (m_nextDlyp) { // Tell next assigndly it can share the variable - m_nextDlyp->user3p(setvscp); - } - // - // Create ALWAYSPOST for delayed variable - // We add all logic to the same block if it's for the same memory - // This ensures that multiple assignments to the same memory will result - // in correctly ordered code - the last assignment must be last. - // It also has the nice side effect of assisting cache locality. - AstNode* selectsp = varrefp; - for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) { - selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]); - } - if (bitselp) { - selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp, - bitselp->widthp()->cloneTree(false)); - } - // Build "IF (changeit) ... - UINFO(9," For "<user3p(setvscp); + } + // + // Create ALWAYSPOST for delayed variable + // We add all logic to the same block if it's for the same memory + // This ensures that multiple assignments to the same memory will result + // in correctly ordered code - the last assignment must be last. + // It also has the nice side effect of assisting cache locality. + AstNode* selectsp = varrefp; + for (int dimension=int(dimreadps.size())-1; dimension>=0; --dimension) { + selectsp = new AstArraySel(nodep->fileline(), selectsp, dimreadps[dimension]); + } + if (bitselp) { + selectsp = new AstSel(nodep->fileline(), selectsp, bitreadp, + bitselp->widthp()->cloneTree(false)); + } + // Build "IF (changeit) ... + UINFO(9," For "<varScopep()->user4p(), AlwaysPost); - if (finalp) { + if (finalp) { AstActive* oldactivep = VN_CAST(finalp->user2p(), Active); - checkActivePost(varrefp, oldactivep); - if (setinitp) oldactivep->addStmtsp(setinitp); - } else { // first time we've dealt with this memory - finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/); - UINFO(9," Created "<addStmtsp(finalp); - varrefp->varScopep()->user4p(finalp); - finalp->user2p(newactp); - if (setinitp) newactp->addStmtsp(setinitp); - } - AstIf* postLogicp; - if (finalp->user3p() == setvscp) { - // Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*, - // we can share the IF statement too + checkActivePost(varrefp, oldactivep); + if (setinitp) oldactivep->addStmtsp(setinitp); + } else { // first time we've dealt with this memory + finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/); + UINFO(9," Created "<addStmtsp(finalp); + varrefp->varScopep()->user4p(finalp); + finalp->user2p(newactp); + if (setinitp) newactp->addStmtsp(setinitp); + } + AstIf* postLogicp; + if (finalp->user3p() == setvscp) { + // Optimize as above; if sharing Vdlyvset *ON SAME VARIABLE*, + // we can share the IF statement too postLogicp = VN_CAST(finalp->user4p(), If); - if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF"); - } else { + if (!postLogicp) nodep->v3fatalSrc("Delayed assignment misoptimized; prev var found w/o associated IF"); + } else { postLogicp = new AstIf(nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, false), NULL, NULL); - UINFO(9," Created "<addBodysp(postLogicp); - finalp->user3p(setvscp); // Remember IF's vset variable - finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it - } - postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp)); - return newlhsp; + UINFO(9," Created "<addBodysp(postLogicp); + finalp->user3p(setvscp); // Remember IF's vset variable + finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it + } + postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp)); + return newlhsp; } // VISITORS virtual void visit(AstNetlist* nodep) { - //VV***** We reset all userp() on the netlist - m_modVarMap.clear(); + //VV***** We reset all userp() on the netlist + m_modVarMap.clear(); iterateChildren(nodep); } virtual void visit(AstScope* nodep) { - UINFO(4," MOD "<hasInitial(); - AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars. + m_activep = nodep; + bool oldinit = m_inInitial; + m_inInitial = nodep->hasInitial(); + AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars. iterateChildren(nodep); - m_inInitial = oldinit; + m_inInitial = oldinit; } virtual void visit(AstAssignDly* nodep) { - m_inDly = true; + m_inDly = true; m_nextDlyp = VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe NULL. - if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task"); + if (m_cfuncp) nodep->v3error("Unsupported: Delayed assignment inside public function/task"); if (VN_IS(nodep->lhsp(), ArraySel) || (VN_IS(nodep->lhsp(), Sel) && VN_IS(VN_CAST(nodep->lhsp(), Sel)->fromp(), ArraySel))) { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* newlhsp = createDlyArray(nodep, lhsp); - if (m_inLoop) nodep->v3warn(BLKLOOPINIT,"Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)"); - if (newlhsp) { - nodep->lhsp(newlhsp); - } else { - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - lhsp->deleteTree(); VL_DANGLING(lhsp); - } - else { + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* newlhsp = createDlyArray(nodep, lhsp); + if (m_inLoop) nodep->v3warn(BLKLOOPINIT, "Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)"); + if (newlhsp) { + nodep->lhsp(newlhsp); + } else { + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + lhsp->deleteTree(); VL_DANGLING(lhsp); + } + else { iterateChildren(nodep); - } - m_inDly = false; - m_nextDlyp = NULL; + } + m_inDly = false; + m_nextDlyp = NULL; } virtual void visit(AstVarRef* nodep) { - if (!nodep->user2Inc()) { // Not done yet - if (m_inDly && nodep->lvalue()) { - UINFO(4,"AssignDlyVar: "<varScopep(), VU_DLY); - if (!m_activep) nodep->v3fatalSrc("<= not under sensitivity block"); + if (!nodep->user2Inc()) { // Not done yet + if (m_inDly && nodep->lvalue()) { + UINFO(4,"AssignDlyVar: "<varScopep(), VU_DLY); + if (!m_activep) nodep->v3fatalSrc("<= not under sensitivity block"); if (!m_activep->hasClocked()) { nodep->v3error("Internal: Blocking <= assignment in non-clocked block, should have converted in V3Active"); } - AstVarScope* oldvscp = nodep->varScopep(); - if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); + AstVarScope* oldvscp = nodep->varScopep(); + if (!oldvscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); AstVarScope* dlyvscp = VN_CAST(oldvscp->user1p(), VarScope); - if (dlyvscp) { // Multiple use of delayed variable + if (dlyvscp) { // Multiple use of delayed variable AstActive* oldactivep = VN_CAST(dlyvscp->user2p(), Active); - checkActivePost(nodep, oldactivep); - } - if (!dlyvscp) { // First use of this delayed variable - string newvarname = (string("__Vdly__")+nodep->varp()->shortName()); - dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL); - AstNodeAssign* prep + checkActivePost(nodep, oldactivep); + } + if (!dlyvscp) { // First use of this delayed variable + string newvarname = (string("__Vdly__")+nodep->varp()->shortName()); + dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL); + AstNodeAssign* prep = new AstAssignPre(nodep->fileline(), new AstVarRef(nodep->fileline(), dlyvscp, true), new AstVarRef(nodep->fileline(), oldvscp, false)); - AstNodeAssign* postp + AstNodeAssign* postp = new AstAssignPost(nodep->fileline(), new AstVarRef(nodep->fileline(), oldvscp, true), new AstVarRef(nodep->fileline(), dlyvscp, false)); - postp->lhsp()->user2(true); // Don't detect this assignment - oldvscp->user1p(dlyvscp); // So we can find it later - // Make new ACTIVE with identical sensitivity tree - AstActive* newactp = createActivePost(nodep); - dlyvscp->user2p(newactp); - newactp->addStmtsp(prep); // Add to FRONT of statements - newactp->addStmtsp(postp); - } - AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true); - newrefp->user2(true); // No reason to do it again - nodep->replaceWith(newrefp); nodep->deleteTree(); VL_DANGLING(nodep); - } - else if (!m_inDly && nodep->lvalue()) { - //UINFO(9,"NBA "<varScopep(), VU_NONDLY); - } - } - } + postp->lhsp()->user2(true); // Don't detect this assignment + oldvscp->user1p(dlyvscp); // So we can find it later + // Make new ACTIVE with identical sensitivity tree + AstActive* newactp = createActivePost(nodep); + dlyvscp->user2p(newactp); + newactp->addStmtsp(prep); // Add to FRONT of statements + newactp->addStmtsp(postp); + } + AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true); + newrefp->user2(true); // No reason to do it again + nodep->replaceWith(newrefp); nodep->deleteTree(); VL_DANGLING(nodep); + } + else if (!m_inDly && nodep->lvalue()) { + //UINFO(9,"NBA "<varScopep(), VU_NONDLY); + } + } + } } virtual void visit(AstNodeFor* nodep) { - nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin"); + nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin"); } virtual void visit(AstWhile* nodep) { - bool oldloop = m_inLoop; - m_inLoop = true; + bool oldloop = m_inLoop; + m_inLoop = true; iterateChildren(nodep); - m_inLoop = oldloop; + m_inLoop = oldloop; } //-------------------- @@ -452,17 +461,17 @@ private: public: // CONSTUCTORS explicit DelayedVisitor(AstNetlist* nodep) { - m_inDly = false; - m_activep=NULL; - m_cfuncp=NULL; - m_nextDlyp=NULL; - m_inLoop = false; - m_inInitial = false; + m_inDly = false; + m_activep = NULL; + m_cfuncp = NULL; + m_nextDlyp = NULL; + m_inLoop = false; + m_inInitial = false; iterate(nodep); } virtual ~DelayedVisitor() { - V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet); + V3Stats::addStat("Optimizations, Delayed shared-sets", m_statSharedSet); } }; diff --git a/src/V3Delayed.h b/src/V3Delayed.h index 4a5abe95c..f7e70c113 100644 --- a/src/V3Delayed.h +++ b/src/V3Delayed.h @@ -34,4 +34,4 @@ public: static void delayedAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Depth.cpp b/src/V3Depth.cpp index 34ce9df63..e50220545 100644 --- a/src/V3Depth.cpp +++ b/src/V3Depth.cpp @@ -20,10 +20,10 @@ // V3Depth's Transformations: // // Each module: -// For each wide OP, assign a temporary variable. -// For each deep expression, assign expression to temporary. +// For each wide OP, assign a temporary variable. +// For each deep expression, assign expression to temporary. // Each CFunc: -// Any statements that need "this" are marked non-static +// Any statements that need "this" are marked non-static // //************************************************************************* @@ -44,62 +44,64 @@ private: // NODE STATE // STATE - AstNodeModule* m_modp; // Current module - AstCFunc* m_funcp; // Current block - AstNode* m_stmtp; // Current statement - int m_depth; // How deep in an expression - int m_maxdepth; // Maximum depth in an expression + AstNodeModule* m_modp; // Current module + AstCFunc* m_funcp; // Current block + AstNode* m_stmtp; // Current statement + int m_depth; // How deep in an expression + int m_maxdepth; // Maximum depth in an expression // METHODS VL_DEBUG_FUNC; // Declare debug() void createDeepTemp(AstNode* nodep) { - UINFO(6," Deep "<=9) nodep->dumpTree(cout,"deep:"); + UINFO(6," Deep "<=9) nodep->dumpTree(cout, "deep:"); string newvarname = (string("__Vdeeptemp")+cvtToStr(m_modp->varNumGetInc())); AstVar* varp = new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, - // Width, not widthMin, as we may be in middle of BITSEL expression which - // though it's one bit wide, needs the mask in the upper bits. - // (Someday we'll have a valid bitmask instead of widths....) + // Width, not widthMin, as we may be in + // middle of BITSEL expression which though + // it's one bit wide, needs the mask in the + // upper bits. (Someday we'll have a valid + // bitmask instead of widths....) // See t_func_crc for an example test that requires this VFlagLogicPacked(), nodep->width()); - if (!m_funcp) nodep->v3fatalSrc("Deep expression not under a function"); - m_funcp->addInitsp(varp); - // Replace node tree with reference to var + if (!m_funcp) nodep->v3fatalSrc("Deep expression not under a function"); + m_funcp->addInitsp(varp); + // Replace node tree with reference to var AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, false); - nodep->replaceWith(newp); - // Put assignment before the referencing statement + nodep->replaceWith(newp); + // Put assignment before the referencing statement AstAssign* assp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true), nodep); - AstNRelinker linker2; - m_stmtp->unlinkFrBack(&linker2); - assp->addNext(m_stmtp); - linker2.relink(assp); + AstNRelinker linker2; + m_stmtp->unlinkFrBack(&linker2); + assp->addNext(m_stmtp); + linker2.relink(assp); } // VISITORS virtual void visit(AstNodeModule* nodep) { - UINFO(4," MOD "<isStatement()) { @@ -112,20 +114,20 @@ private: virtual void visit(AstNodeTermop* nodep) { } virtual void visit(AstNodeMath* nodep) { - // We have some operator defines that use 2 parens, so += 2. - m_depth += 2; - if (m_depth>m_maxdepth) m_maxdepth=m_depth; + // We have some operator defines that use 2 parens, so += 2. + m_depth += 2; + if (m_depth>m_maxdepth) m_maxdepth = m_depth; iterateChildren(nodep); - m_depth -= 2; + m_depth -= 2; - if (m_stmtp - && (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it - && (m_maxdepth-m_depth) > v3Global.opt.compLimitParens() + if (m_stmtp + && (v3Global.opt.compLimitParens() >= 1) // Else compiler doesn't need it + && (m_maxdepth-m_depth) > v3Global.opt.compLimitParens() && !VN_IS(nodep->backp(), NodeStmt) // Not much point if we're about to use it - ) { - m_maxdepth = m_depth; - createDeepTemp(nodep); - } + ) { + m_maxdepth = m_depth; + createDeepTemp(nodep); + } } //-------------------- @@ -134,22 +136,22 @@ private: void needNonStaticFunc(AstNode* nodep) { if (!m_funcp) nodep->v3fatalSrc("Non-static accessor not under a function"); if (m_funcp->isStatic().trueU()) { - UINFO(5,"Mark non-public due to "<isStatic(false); - } + UINFO(5,"Mark non-public due to "<isStatic(false); + } } virtual void visit(AstUCFunc* nodep) { - needNonStaticFunc(nodep); + needNonStaticFunc(nodep); iterateChildren(nodep); } virtual void visit(AstUCStmt* nodep) { - needNonStaticFunc(nodep); - visitStmt(nodep); + needNonStaticFunc(nodep); + visitStmt(nodep); } //-------------------- // Default: Just iterate - virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars + virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars virtual void visit(AstNode* nodep) { iterateChildren(nodep); } @@ -157,12 +159,12 @@ private: public: // CONSTUCTORS explicit DepthVisitor(AstNetlist* nodep) { - m_modp=NULL; - m_funcp=NULL; - m_stmtp=NULL; - m_depth=0; - m_maxdepth=0; - // + m_modp = NULL; + m_funcp = NULL; + m_stmtp = NULL; + m_depth = 0; + m_maxdepth = 0; + // iterate(nodep); } virtual ~DepthVisitor() {} diff --git a/src/V3Depth.h b/src/V3Depth.h index 8d265c195..23de0b97f 100644 --- a/src/V3Depth.h +++ b/src/V3Depth.h @@ -34,4 +34,4 @@ public: static void depthAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3DepthBlock.cpp b/src/V3DepthBlock.cpp index 5dcaa954a..a0dbba2ad 100644 --- a/src/V3DepthBlock.cpp +++ b/src/V3DepthBlock.cpp @@ -20,7 +20,7 @@ // V3DepthBlock's Transformations: // // Each module: -// For each deep block, create cfunc including that block. +// For each deep block, create cfunc including that block. // //************************************************************************* @@ -42,69 +42,69 @@ private: // NODE STATE // STATE - AstNodeModule* m_modp; // Current module - AstCFunc* m_funcp; // Current function - int m_depth; // How deep in an expression - int m_deepNum; // How many functions made + AstNodeModule* m_modp; // Current module + AstCFunc* m_funcp; // Current function + int m_depth; // How deep in an expression + int m_deepNum; // How many functions made // METHODS VL_DEBUG_FUNC; // Declare debug() AstCFunc* createDeepFunc(AstNode* nodep) { - AstNRelinker relinkHandle; - nodep->unlinkFrBack(&relinkHandle); - // Create function - string name = m_funcp->name()+"__deep"+cvtToStr(++m_deepNum); - AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, NULL); - funcp->argTypes(EmitCBaseVisitor::symClassVar()); - funcp->symProlog(true); - funcp->slow(m_funcp->slow()); - funcp->addStmtsp(nodep); - m_modp->addStmtp(funcp); - // Call it at the point where the body was removed from - AstCCall* callp = new AstCCall(nodep->fileline(), funcp); - callp->argTypes("vlSymsp"); - UINFO(6," New "<unlinkFrBack(&relinkHandle); + // Create function + string name = m_funcp->name()+"__deep"+cvtToStr(++m_deepNum); + AstCFunc* funcp = new AstCFunc(nodep->fileline(), name, NULL); + funcp->argTypes(EmitCBaseVisitor::symClassVar()); + funcp->symProlog(true); + funcp->slow(m_funcp->slow()); + funcp->addStmtsp(nodep); + m_modp->addStmtp(funcp); + // Call it at the point where the body was removed from + AstCCall* callp = new AstCCall(nodep->fileline(), funcp); + callp->argTypes("vlSymsp"); + UINFO(6," New "< v3Global.opt.compLimitBlocks() + m_depth++; + if (m_depth > v3Global.opt.compLimitBlocks() && !VN_IS(nodep, CCall)) { // Already done - UINFO(4, "DeepBlocks "<backp(); // Only for debug - if (debug()>=9) backp->dumpTree(cout,"- pre : "); - AstCFunc* funcp = createDeepFunc(nodep); + UINFO(4, "DeepBlocks "<backp(); // Only for debug + if (debug()>=9) backp->dumpTree(cout, "- pre : "); + AstCFunc* funcp = createDeepFunc(nodep); iterate(funcp); - if (debug()>=9) backp->dumpTree(cout,"- post: "); - if (debug()>=9) funcp->dumpTree(cout,"- func: "); - } else { + if (debug()>=9) backp->dumpTree(cout, "- post: "); + if (debug()>=9) funcp->dumpTree(cout, "- func: "); + } else { iterateChildren(nodep); - } - m_depth--; + } + m_depth--; } virtual void visit(AstNodeStmt* nodep) { if (!nodep->isStatement()) { @@ -117,7 +117,7 @@ private: virtual void visit(AstNodeMath* nodep) {} // Accelerate //-------------------- // Default: Just iterate - virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars + virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars virtual void visit(AstNode* nodep) { iterateChildren(nodep); } @@ -129,7 +129,7 @@ public: m_funcp = NULL; m_depth = 0; m_deepNum = 0; - // + // iterate(nodep); } virtual ~DepthBlockVisitor() {} diff --git a/src/V3DepthBlock.h b/src/V3DepthBlock.h index eb36e5a71..a65261870 100644 --- a/src/V3DepthBlock.h +++ b/src/V3DepthBlock.h @@ -34,4 +34,4 @@ public: static void depthBlockAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Descope.cpp b/src/V3Descope.cpp index 48b977afc..6d3ff5880 100644 --- a/src/V3Descope.cpp +++ b/src/V3Descope.cpp @@ -18,11 +18,11 @@ // //************************************************************************* // DESCOPE TRANSFORMATIONS: -// All modules: -// Each VARREF/FUNCCALL -// Change varref name() to be relative to current module -// Remove varScopep() -// This allows for better V3Combine'ing. +// All modules: +// Each VARREF/FUNCCALL +// Change varref name() to be relative to current module +// Remove varScopep() +// This allows for better V3Combine'ing. // //************************************************************************* @@ -43,19 +43,19 @@ class DescopeVisitor : public AstNVisitor { private: // NODE STATE // Cleared entire netlist - // AstCFunc::user() // bool. Indicates processing completed - AstUser1InUse m_inuser1; + // AstCFunc::user() // bool. Indicates processing completed + AstUser1InUse m_inuser1; // TYPES - typedef std::multimap FuncMmap; + typedef std::multimap FuncMmap; // STATE - AstNodeModule* m_modp; // Current module - AstScope* m_scopep; // Current scope + AstNodeModule* m_modp; // Current module + AstScope* m_scopep; // Current scope bool m_modSingleton; // m_modp is only instanced once bool m_allowThis; // Allow function non-static bool m_needThis; // Make function non-static - FuncMmap m_modFuncs; // Name of public functions added + FuncMmap m_modFuncs; // Name of public functions added // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -84,7 +84,7 @@ private: string descopedName(const AstScope* scopep, bool& hierThisr, const AstVar* varp=NULL) { UASSERT(scopep, "Var/Func not scoped"); - hierThisr = (scopep == m_scopep); + hierThisr = (scopep == m_scopep); // It's possible to disable relative references. This is a concession // to older compilers (gcc < 4.5.x) that don't understand __restrict__ @@ -119,9 +119,9 @@ private: relativeRefOk = false; } - if (varp && varp->isFuncLocal()) { + if (varp && varp->isFuncLocal()) { hierThisr = true; - return ""; // Relative to function, not in this + return ""; // Relative to function, not in this } else if (relativeRefOk && scopep == m_scopep) { m_needThis = true; return "this->"; @@ -131,7 +131,7 @@ private: string name = scopep->name(); string::size_type pos; if ((pos = name.rfind('.')) != string::npos) { - name.erase(0,pos+1); + name.erase(0, pos+1); } m_needThis = true; return name+"->"; @@ -141,7 +141,7 @@ private: UINFO(8," Descope "<name()<name()<aboveScopep()) { // Top + if (!scopep->aboveScopep()) { // Top // We could also return "vlSymsp->TOPp->" here, but GCC would // suspect aliases. return "vlTOPp->"; @@ -152,41 +152,43 @@ private: } void makePublicFuncWrappers() { - // We recorded all public functions in m_modFuncs. - // If for any given name only one function exists, we can use that function directly. - // If multiple functions exist, we need to select the appropriate scope. - for (FuncMmap::iterator it = m_modFuncs.begin(); it!=m_modFuncs.end(); ++it) { - string name = it->first; - AstCFunc* topFuncp = it->second; - FuncMmap::iterator nextIt1 = it; ++nextIt1; - bool moreOfSame1 = (nextIt1!=m_modFuncs.end() && nextIt1->first == name); - if (moreOfSame1) { - // Multiple functions under this name, need a wrapper function - UINFO(6," Wrapping "<cloneTree(false); - if (newfuncp->initsp()) newfuncp->initsp()->unlinkFrBackWithNext()->deleteTree(); - if (newfuncp->stmtsp()) newfuncp->stmtsp()->unlinkFrBackWithNext()->deleteTree(); - if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree(); - newfuncp->name(name); - newfuncp->isStatic(false); - newfuncp->addInitsp( - new AstCStmt(newfuncp->fileline(), - EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); - newfuncp->addInitsp(new AstCStmt(newfuncp->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); - topFuncp->addNextHere(newfuncp); - // In the body, call each function if it matches the given scope - for (FuncMmap::iterator eachIt = it; eachIt!=m_modFuncs.end() && eachIt->first==name; ++eachIt) { - it = eachIt; - AstCFunc* funcp = eachIt->second; - FuncMmap::iterator nextIt2 = eachIt; ++nextIt2; - bool moreOfSame = (nextIt2!=m_modFuncs.end() && nextIt2->first == name); - if (!funcp->scopep()) funcp->v3fatalSrc("Not scoped"); + // We recorded all public functions in m_modFuncs. + // If for any given name only one function exists, we can use that function directly. + // If multiple functions exist, we need to select the appropriate scope. + for (FuncMmap::iterator it = m_modFuncs.begin(); it!=m_modFuncs.end(); ++it) { + string name = it->first; + AstCFunc* topFuncp = it->second; + FuncMmap::iterator nextIt1 = it; ++nextIt1; + bool moreOfSame1 = (nextIt1!=m_modFuncs.end() && nextIt1->first == name); + if (moreOfSame1) { + // Multiple functions under this name, need a wrapper function + UINFO(6," Wrapping "<cloneTree(false); + if (newfuncp->initsp()) newfuncp->initsp()->unlinkFrBackWithNext()->deleteTree(); + if (newfuncp->stmtsp()) newfuncp->stmtsp()->unlinkFrBackWithNext()->deleteTree(); + if (newfuncp->finalsp()) newfuncp->finalsp()->unlinkFrBackWithNext()->deleteTree(); + newfuncp->name(name); + newfuncp->isStatic(false); + newfuncp->addInitsp( + new AstCStmt(newfuncp->fileline(), + EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); + newfuncp->addInitsp(new AstCStmt(newfuncp->fileline(), + EmitCBaseVisitor::symTopAssign()+"\n")); + topFuncp->addNextHere(newfuncp); + // In the body, call each function if it matches the given scope + for (FuncMmap::iterator eachIt = it; + eachIt != m_modFuncs.end() && eachIt->first==name; ++eachIt) { + it = eachIt; + AstCFunc* funcp = eachIt->second; + FuncMmap::iterator nextIt2 = eachIt; ++nextIt2; + bool moreOfSame = (nextIt2!=m_modFuncs.end() && nextIt2->first == name); + if (!funcp->scopep()) funcp->v3fatalSrc("Not scoped"); - UINFO(6," Wrapping "<argTypes()<<" und "<argTypes()<declPrivate(true); - AstNode* argsp = NULL; - for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { + UINFO(6," Wrapping "<argTypes()<<" und "<argTypes()<declPrivate(true); + AstNode* argsp = NULL; + for (AstNode* stmtp = newfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { if (portp->isIO() && !portp->isFuncReturn()) { AstNode* newp = new AstVarRef(portp->fileline(), @@ -201,7 +203,7 @@ private: new AstCCall(funcp->fileline(), funcp, argsp)); - if (moreOfSame) { + if (moreOfSame) { AstIf* ifp = new AstIf(funcp->fileline(), new AstEq(funcp->fileline(), new AstCMath(funcp->fileline(), @@ -210,86 +212,89 @@ private: string("&(") +funcp->scopep()->nameVlSym() +")", 64)), - returnp, NULL); - newfuncp->addStmtsp(ifp); - } else { - newfuncp->addStmtsp(returnp); - } - } - // Not really any way the user could do this, and we'd need to come up with some return value - //newfuncp->addStmtsp(new AstDisplay (newfuncp->fileline(), AstDisplayType::DT_WARNING, - // string("%%Error: ")+name+"() called with bad scope", NULL)); - //newfuncp->addStmtsp(new AstStop (newfuncp->fileline())); - if (debug()>=9) newfuncp->dumpTree(cout," newfunc: "); - } else { - // Only a single function under this name, we can simply rename it - UINFO(6," Wrapping "<name(name); - } - } + returnp, NULL); + newfuncp->addStmtsp(ifp); + } else { + newfuncp->addStmtsp(returnp); + } + } + // Not really any way the user could do this, and we'd need + // to come up with some return value + //newfuncp->addStmtsp(new AstDisplay(newfuncp->fileline(), + // AstDisplayType::DT_WARNING, + // string("%%Error: ")+name+"() called with bad scope", NULL)); + //newfuncp->addStmtsp(new AstStop(newfuncp->fileline())); + if (debug()>=9) newfuncp->dumpTree(cout, " newfunc: "); + } else { + // Only a single function under this name, we can simply rename it + UINFO(6," Wrapping "<name(name); + } + } } // VISITORS virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; - m_modFuncs.clear(); + m_modp = nodep; + m_modFuncs.clear(); m_modSingleton = modIsSingleton(m_modp); iterateChildren(nodep); - makePublicFuncWrappers(); - m_modp = NULL; + makePublicFuncWrappers(); + m_modp = NULL; } virtual void visit(AstScope* nodep) { - m_scopep = nodep; + m_scopep = nodep; iterateChildren(nodep); - m_scopep = NULL; + m_scopep = NULL; } virtual void visit(AstVarScope* nodep) { - // Delete the varscope when we're finished - nodep->unlinkFrBack(); - pushDeletep(nodep); + // Delete the varscope when we're finished + nodep->unlinkFrBack(); + pushDeletep(nodep); } virtual void visit(AstNodeVarRef* nodep) { iterateChildren(nodep); - // Convert the hierch name - if (!m_scopep) nodep->v3fatalSrc("Node not under scope"); - bool hierThis; - nodep->hiername(descopedName(nodep->varScopep()->scopep(), hierThis/*ref*/, nodep->varScopep()->varp())); - nodep->hierThis(hierThis); - nodep->varScopep(NULL); + // Convert the hierch name + if (!m_scopep) nodep->v3fatalSrc("Node not under scope"); + bool hierThis; + nodep->hiername(descopedName(nodep->varScopep()->scopep(), hierThis/*ref*/, + nodep->varScopep()->varp())); + nodep->hierThis(hierThis); + nodep->varScopep(NULL); } virtual void visit(AstCCall* nodep) { - //UINFO(9," "<v3fatalSrc("Node not under scope"); - if (!nodep->funcp()->scopep()) nodep->v3fatalSrc("CFunc not under scope"); - bool hierThis; - nodep->hiername(descopedName(nodep->funcp()->scopep(), hierThis/*ref*/)); - // Can't do this, as we may have more calls later - // nodep->funcp()->scopep(NULL); + // Convert the hierch name + if (!m_scopep) nodep->v3fatalSrc("Node not under scope"); + if (!nodep->funcp()->scopep()) nodep->v3fatalSrc("CFunc not under scope"); + bool hierThis; + nodep->hiername(descopedName(nodep->funcp()->scopep(), hierThis/*ref*/)); + // Can't do this, as we may have more calls later + // nodep->funcp()->scopep(NULL); } virtual void visit(AstCFunc* nodep) { - if (!nodep->user1()) { - m_needThis = false; + if (!nodep->user1()) { + m_needThis = false; m_allowThis = nodep->isStatic().falseU(); // Non-static or unknown if static iterateChildren(nodep); nodep->user1(true); if (m_needThis) { nodep->isStatic(false); - } - // If it's under a scope, move it up to the top - if (m_scopep) { - nodep->unlinkFrBack(); - m_modp->addStmtp(nodep); + } + // If it's under a scope, move it up to the top + if (m_scopep) { + nodep->unlinkFrBack(); + m_modp->addStmtp(nodep); - if (nodep->funcPublic()) { - // There may be multiple public functions by the same name; - // record for later correction or making of shells - m_modFuncs.insert(make_pair(nodep->name(), nodep)); - nodep->name(m_scopep->nameDotless() +"__" + nodep->name()); - } - } - } + if (nodep->funcPublic()) { + // There may be multiple public functions by the same name; + // record for later correction or making of shells + m_modFuncs.insert(make_pair(nodep->name(), nodep)); + nodep->name(m_scopep->nameDotless() +"__" + nodep->name()); + } + } + } } virtual void visit(AstVar*) {} virtual void visit(AstNode* nodep) { diff --git a/src/V3Descope.h b/src/V3Descope.h index 2cc824302..d1ee6a71a 100644 --- a/src/V3Descope.h +++ b/src/V3Descope.h @@ -34,4 +34,4 @@ public: static void descopeAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index ee4776a80..456600f94 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -36,9 +36,9 @@ #include #include VL_INCLUDE_UNORDERED_SET -#define VL_VALUE_STRING_MAX_WIDTH 8192 // We use a static char array in VL_VALUE_STRING +#define VL_VALUE_STRING_MAX_WIDTH 8192 // We use a static char array in VL_VALUE_STRING -#define EMITC_NUM_CONSTW 8 // Number of VL_CONST_W_*X's in verilated.h (IE VL_CONST_W_8X is last) +#define EMITC_NUM_CONSTW 8 // Number of VL_CONST_W_*X's in verilated.h (IE VL_CONST_W_8X is last) //###################################################################### // Emit statements and math operators @@ -48,31 +48,31 @@ private: typedef std::vector VarVec; typedef std::map VarSortMap; // Map size class to VarVec - bool m_suppressSemi; - AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting + bool m_suppressSemi; + AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting VarVec m_ctorVarsVec; // All variables in constructor order - int m_splitSize; // # of cfunc nodes placed into output file - int m_splitFilenum; // File number being created, 0 = primary + int m_splitSize; // # of cfunc nodes placed into output file + int m_splitFilenum; // File number being created, 0 = primary public: // METHODS VL_DEBUG_FUNC; // Declare debug() // ACCESSORS - int splitFilenum() const { return m_splitFilenum; } - int splitFilenumInc() { m_splitSize = 0; return ++m_splitFilenum; } + int splitFilenum() const { return m_splitFilenum; } + int splitFilenumInc() { m_splitSize = 0; return ++m_splitFilenum; } int splitSize() const { return m_splitSize; } void splitSizeInc(int count) { m_splitSize += count; } void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); } bool splitNeeded() { return (splitSize() && v3Global.opt.outputSplit() - && v3Global.opt.outputSplit() < splitSize()); } + && v3Global.opt.outputSplit() < splitSize()); } // METHODS void displayNode(AstNode* nodep, AstScopeName* scopenamep, - const string& vformat, AstNode* exprsp, bool isScan); + const string& vformat, AstNode* exprsp, bool isScan); void displayEmit(AstNode* nodep, bool isScan); void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, - const string& vfmt, char fmtLetter); + const string& vfmt, char fmtLetter); void emitVarDecl(const AstVar* nodep, const string& prefixIfImp); typedef enum {EVL_CLASS_IO, EVL_CLASS_SIG, EVL_CLASS_TEMP, EVL_CLASS_PAR, EVL_CLASS_ALL, @@ -89,20 +89,22 @@ public: puts(nodep->dtypep()->charIQWN()); } void emitScIQW(AstVar* nodep) { - if (!nodep->isSc()) nodep->v3fatalSrc("emitting SystemC operator on non-SC variable"); + if (!nodep->isSc()) nodep->v3fatalSrc("emitting SystemC operator on non-SC variable"); puts(nodep->isScBigUint() ? "SB" : nodep->isScUint() ? "SU" : nodep->isScBv() ? "SW" : (nodep->isScQuad() ? "SQ" : "SI")); } void emitOpName(AstNode* nodep, const string& format, - AstNode* lhsp, AstNode* rhsp, AstNode* thsp); + AstNode* lhsp, AstNode* rhsp, AstNode* thsp); void emitDeclArrayBrackets(const AstVar* nodep) { - // This isn't very robust and may need cleanup for other data types - for (const AstUnpackArrayDType* arrayp=VN_CAST_CONST(nodep->dtypeSkipRefp(), UnpackArrayDType); arrayp; + // This isn't very robust and may need cleanup for other data types + for (const AstUnpackArrayDType* arrayp + = VN_CAST_CONST(nodep->dtypeSkipRefp(), UnpackArrayDType); + arrayp; arrayp = VN_CAST_CONST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) { - puts("["+cvtToStr(arrayp->elementsConst())+"]"); - } + puts("["+cvtToStr(arrayp->elementsConst())+"]"); + } } void emitVarCmtChg(const AstVar* varp, string* curVarCmtp) { string newVarCmt = varp->mtasksString(); @@ -112,280 +114,286 @@ public: } } void emitTypedefs(AstNode* firstp) { - bool first = true; - for (AstNode* loopp=firstp; loopp; loopp = loopp->nextp()) { + bool first = true; + for (AstNode* loopp=firstp; loopp; loopp = loopp->nextp()) { if (const AstTypedef* nodep = VN_CAST(loopp, Typedef)) { - if (nodep->attrPublic()) { - if (first) { - first = false; - puts("\n// TYPEDEFS\n"); - puts("// That were declared public\n"); - } else { - puts("\n"); - } - if (const AstEnumDType* adtypep = VN_CAST(nodep->dtypep()->skipRefToEnump(), EnumDType)) { - if (adtypep->width()>64) { - putsDecoration("// enum "+nodep->name()+" // Ignored: Too wide for C++\n"); - } else { - puts("enum "+nodep->name()+" {\n"); - for (AstEnumItem* itemp = adtypep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), EnumItem)) { - puts(itemp->name()); - puts(" = "); + if (nodep->attrPublic()) { + if (first) { + first = false; + puts("\n// TYPEDEFS\n"); + puts("// That were declared public\n"); + } else { + puts("\n"); + } + if (const AstEnumDType* adtypep + = VN_CAST(nodep->dtypep()->skipRefToEnump(), EnumDType)) { + if (adtypep->width()>64) { + putsDecoration("// enum "+nodep->name()+" // Ignored: Too wide for C++\n"); + } else { + puts("enum "+nodep->name()+" {\n"); + for (AstEnumItem* itemp = adtypep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), EnumItem)) { + puts(itemp->name()); + puts(" = "); iterateAndNextNull(itemp->valuep()); if (VN_IS(itemp->nextp(), EnumItem)) puts(","); - puts("\n"); - } - puts("};\n"); - } - } - } - } - } + puts("\n"); + } + puts("};\n"); + } + } + } + } + } } // VISITORS virtual void visit(AstNodeAssign* nodep) { - bool paren = true; bool decind = false; - if (AstSel* selp=VN_CAST(nodep->lhsp(), Sel)) { - if (selp->widthMin()==1) { - putbs("VL_ASSIGNBIT_"); - emitIQW(selp->fromp()); - if (nodep->rhsp()->isAllOnesV()) { - puts("O("); - } else { - puts("I("); - } - puts(cvtToStr(nodep->widthMin())+","); - iterateAndNextNull(selp->lsbp()); puts(", "); - iterateAndNextNull(selp->fromp()); puts(", "); - } else { - putbs("VL_ASSIGNSEL_"); + bool paren = true; bool decind = false; + if (AstSel* selp = VN_CAST(nodep->lhsp(), Sel)) { + if (selp->widthMin()==1) { + putbs("VL_ASSIGNBIT_"); emitIQW(selp->fromp()); - puts("II"); - emitIQW(nodep->rhsp()); - puts("("); - puts(cvtToStr(nodep->widthMin())+","); + if (nodep->rhsp()->isAllOnesV()) { + puts("O("); + } else { + puts("I("); + } + puts(cvtToStr(nodep->widthMin())+","); iterateAndNextNull(selp->lsbp()); puts(", "); iterateAndNextNull(selp->fromp()); puts(", "); - } - } else if (AstVar* varp = AstVar::scVarRecurse(nodep->lhsp())) { - putbs("VL_ASSIGN_"); // Set a systemC variable - emitScIQW(varp); - emitIQW(nodep); - puts("("); - puts(cvtToStr(nodep->widthMin())+","); + } else { + putbs("VL_ASSIGNSEL_"); + emitIQW(selp->fromp()); + puts("II"); + emitIQW(nodep->rhsp()); + puts("("); + puts(cvtToStr(nodep->widthMin())+","); + iterateAndNextNull(selp->lsbp()); puts(", "); + iterateAndNextNull(selp->fromp()); puts(", "); + } + } else if (AstVar* varp = AstVar::scVarRecurse(nodep->lhsp())) { + putbs("VL_ASSIGN_"); // Set a systemC variable + emitScIQW(varp); + emitIQW(nodep); + puts("("); + puts(cvtToStr(nodep->widthMin())+","); iterateAndNextNull(nodep->lhsp()); puts(", "); - } else if (AstVar* varp = AstVar::scVarRecurse(nodep->rhsp())) { - putbs("VL_ASSIGN_"); // Get a systemC variable - emitIQW(nodep); - emitScIQW(varp); - puts("("); - puts(cvtToStr(nodep->widthMin())+","); + } else if (AstVar* varp = AstVar::scVarRecurse(nodep->rhsp())) { + putbs("VL_ASSIGN_"); // Get a systemC variable + emitIQW(nodep); + emitScIQW(varp); + puts("("); + puts(cvtToStr(nodep->widthMin())+","); iterateAndNextNull(nodep->lhsp()); puts(", "); - } else if (nodep->isWide() + } else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) && !VN_IS(nodep->rhsp(), CMath) && !VN_IS(nodep->rhsp(), VarRef) && !VN_IS(nodep->rhsp(), ArraySel)) { - // Wide functions assign into the array directly, don't need separate assign statement + // Wide functions assign into the array directly, don't need separate assign statement m_wideTempRefp = VN_CAST(nodep->lhsp(), VarRef); - paren = false; - } else if (nodep->isWide()) { - putbs("VL_ASSIGN_W("); - puts(cvtToStr(nodep->widthMin())+","); + paren = false; + } else if (nodep->isWide()) { + putbs("VL_ASSIGN_W("); + puts(cvtToStr(nodep->widthMin())+","); iterateAndNextNull(nodep->lhsp()); puts(", "); - } else { - paren = false; + } else { + paren = false; iterateAndNextNull(nodep->lhsp()); - puts(" "); - ofp()->blockInc(); decind = true; + puts(" "); + ofp()->blockInc(); decind = true; if (!VN_IS(nodep->rhsp(), Const)) ofp()->putBreak(); - puts("= "); - } + puts("= "); + } iterateAndNextNull(nodep->rhsp()); - if (paren) puts(")"); - if (decind) ofp()->blockDec(); - if (!m_suppressSemi) puts(";\n"); + if (paren) puts(")"); + if (decind) ofp()->blockDec(); + if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstAlwaysPublic*) { } virtual void visit(AstCCall* nodep) { - puts(nodep->hiername()); - puts(nodep->funcp()->name()); - puts("("); - puts(nodep->argTypes()); - bool comma = (nodep->argTypes() != ""); - for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { - if (comma) puts(", "); + puts(nodep->hiername()); + puts(nodep->funcp()->name()); + puts("("); + puts(nodep->argTypes()); + bool comma = (nodep->argTypes() != ""); + for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { + if (comma) puts(", "); iterate(subnodep); - comma = true; - } + comma = true; + } if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) { - // We should have a separate CCall for math and statement usage, but... - puts(")"); - } else { - puts(");\n"); - } + // We should have a separate CCall for math and statement usage, but... + puts(")"); + } else { + puts(");\n"); + } } virtual void visit(AstNodeCase* nodep) { - // In V3Case... - nodep->v3fatalSrc("Case statements should have been reduced out"); + // In V3Case... + nodep->v3fatalSrc("Case statements should have been reduced out"); } virtual void visit(AstComment* nodep) { putsDecoration(string("// ")+nodep->name()+" at "+nodep->fileline()->ascii()+"\n"); iterateChildren(nodep); } virtual void visit(AstCoverDecl* nodep) { - puts("__vlCoverInsert("); // As Declared in emitCoverageDecl - puts("&(vlSymsp->__Vcoverage["); - puts(cvtToStr(nodep->dataDeclThisp()->binNum())); puts("])"); - // If this isn't the first instantiation of this module under this - // design, don't really count the bucket, and rely on verilator_cov to - // aggregate counts. This is because Verilator combines all - // hiearchies itself, and if verilator_cov also did it, you'd end up - // with (number-of-instant) times too many counts in this bin. - puts(", first"); // Enable, passed from __Vconfigure parameter - puts(", "); putsQuoted(nodep->fileline()->filename()); - puts(", "); puts(cvtToStr(nodep->fileline()->lineno())); - puts(", "); puts(cvtToStr(nodep->column())); - puts(", "); putsQuoted((nodep->hier()!=""?".":"")+nodep->hier()); - puts(", "); putsQuoted(nodep->page()); - puts(", "); putsQuoted(nodep->comment()); - puts(");\n"); + puts("__vlCoverInsert("); // As Declared in emitCoverageDecl + puts("&(vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->dataDeclThisp()->binNum())); puts("])"); + // If this isn't the first instantiation of this module under this + // design, don't really count the bucket, and rely on verilator_cov to + // aggregate counts. This is because Verilator combines all + // hiearchies itself, and if verilator_cov also did it, you'd end up + // with (number-of-instant) times too many counts in this bin. + puts(", first"); // Enable, passed from __Vconfigure parameter + puts(", "); putsQuoted(nodep->fileline()->filename()); + puts(", "); puts(cvtToStr(nodep->fileline()->lineno())); + puts(", "); puts(cvtToStr(nodep->column())); + puts(", "); putsQuoted((nodep->hier()!=""?".":"")+nodep->hier()); + puts(", "); putsQuoted(nodep->page()); + puts(", "); putsQuoted(nodep->comment()); + puts(");\n"); } virtual void visit(AstCoverInc* nodep) { - puts("++(vlSymsp->__Vcoverage["); - puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); - puts("]);\n"); + puts("++(vlSymsp->__Vcoverage["); + puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum())); + puts("]);\n"); } virtual void visit(AstCReturn* nodep) { - puts("return ("); + puts("return ("); iterateAndNextNull(nodep->lhsp()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstDisplay* nodep) { - string text = nodep->fmtp()->text(); - if (nodep->addNewline()) text += "\n"; - displayNode(nodep, nodep->fmtp()->scopeNamep(), text, nodep->fmtp()->exprsp(), false); + string text = nodep->fmtp()->text(); + if (nodep->addNewline()) text += "\n"; + displayNode(nodep, nodep->fmtp()->scopeNamep(), text, nodep->fmtp()->exprsp(), false); } virtual void visit(AstScopeName* nodep) { - // For use under AstCCalls for dpiImports. ScopeNames under displays are handled in AstDisplay - if (!nodep->dpiExport()) { - // this is where the DPI import context scope is set - string scope = nodep->scopeDpiName(); - putbs("(&(vlSymsp->__Vscope_"+scope+"))"); - } + // For use under AstCCalls for dpiImports. ScopeNames under + // displays are handled in AstDisplay + if (!nodep->dpiExport()) { + // this is where the DPI import context scope is set + string scope = nodep->scopeDpiName(); + putbs("(&(vlSymsp->__Vscope_"+scope+"))"); + } } virtual void visit(AstSFormat* nodep) { - displayNode(nodep, nodep->fmtp()->scopeNamep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp(), false); + displayNode(nodep, nodep->fmtp()->scopeNamep(), nodep->fmtp()->text(), + nodep->fmtp()->exprsp(), false); } virtual void visit(AstSFormatF* nodep) { - displayNode(nodep, nodep->scopeNamep(), nodep->text(), nodep->exprsp(), false); + displayNode(nodep, nodep->scopeNamep(), nodep->text(), nodep->exprsp(), false); } virtual void visit(AstFScanF* nodep) { - displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true); + displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true); } virtual void visit(AstSScanF* nodep) { - displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true); + displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true); } virtual void visit(AstValuePlusArgs* nodep) { - puts("VL_VALUEPLUSARGS_IN"); - emitIQW(nodep->outp()); - puts("("); - puts(cvtToStr(nodep->outp()->widthMin())); - puts(","); - emitCvtPackStr(nodep->searchp()); - puts(","); - putbs(""); + puts("VL_VALUEPLUSARGS_IN"); + emitIQW(nodep->outp()); + puts("("); + puts(cvtToStr(nodep->outp()->widthMin())); + puts(","); + emitCvtPackStr(nodep->searchp()); + puts(","); + putbs(""); iterateAndNextNull(nodep->outp()); - puts(")"); + puts(")"); } virtual void visit(AstTestPlusArgs* nodep) { - puts("VL_TESTPLUSARGS_I("); - putsQuoted(nodep->text()); - puts(")"); + puts("VL_TESTPLUSARGS_I("); + putsQuoted(nodep->text()); + puts(")"); } virtual void visit(AstFGetS* nodep) { - checkMaxWords(nodep); - emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL); + checkMaxWords(nodep); + emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL); } void checkMaxWords(AstNode* nodep) { - if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) { - nodep->v3error("String of "<width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h"); - } + if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) { + nodep->v3error("String of "<width() + <<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h"); + } } virtual void visit(AstFOpen* nodep) { iterateAndNextNull(nodep->filep()); - puts(" = VL_FOPEN_"); - emitIQW(nodep->filenamep()); - emitIQW(nodep->modep()); - if (nodep->modep()->width()>4*8) nodep->modep()->v3error("$fopen mode should be <= 4 characters"); - puts("("); - if (nodep->filenamep()->isWide()) { - puts(cvtToStr(nodep->filenamep()->widthWords())); - putbs(", "); - } - checkMaxWords(nodep->filenamep()); + puts(" = VL_FOPEN_"); + emitIQW(nodep->filenamep()); + emitIQW(nodep->modep()); + if (nodep->modep()->width()>4*8) nodep->modep()->v3error("$fopen mode should be <= 4 characters"); + puts("("); + if (nodep->filenamep()->isWide()) { + puts(cvtToStr(nodep->filenamep()->widthWords())); + putbs(", "); + } + checkMaxWords(nodep->filenamep()); iterateAndNextNull(nodep->filenamep()); - putbs(", "); + putbs(", "); iterateAndNextNull(nodep->modep()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstNodeReadWriteMem* nodep) { puts(nodep->cFuncPrefixp()); - emitIQW(nodep->filenamep()); + emitIQW(nodep->filenamep()); puts("("); // We take a void* rather than emitIQW(nodep->memp()); - puts(nodep->isHex()?"true":"false"); - putbs(","); - puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width - putbs(","); - uint32_t array_lsb = 0; - { + puts(nodep->isHex()?"true":"false"); + putbs(","); + puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width + putbs(","); + uint32_t array_lsb = 0; + { const AstVarRef* varrefp = VN_CAST(nodep->memp(), VarRef); if (!varrefp) { nodep->v3error(nodep->verilogKwd() << " loading non-variable"); } - else if (const AstUnpackArrayDType* adtypep = VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType)) { - puts(cvtToStr(varrefp->varp()->dtypep()->arrayUnpackedElements())); - array_lsb = adtypep->lsb(); - } - else { - nodep->v3error(nodep->verilogKwd() + else if (const AstUnpackArrayDType* adtypep + = VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType)) { + puts(cvtToStr(varrefp->varp()->dtypep()->arrayUnpackedElements())); + array_lsb = adtypep->lsb(); + } + else { + nodep->v3error(nodep->verilogKwd() << " loading other than unpacked-array variable"); - } - } - putbs(", "); - puts(cvtToStr(array_lsb)); - putbs(","); + } + } + putbs(", "); + puts(cvtToStr(array_lsb)); + putbs(","); if (!nodep->filenamep()->dtypep()->isString()) { puts(cvtToStr(nodep->filenamep()->widthWords())); checkMaxWords(nodep->filenamep()); putbs(", "); } iterateAndNextNull(nodep->filenamep()); - putbs(", "); + putbs(", "); iterateAndNextNull(nodep->memp()); putbs(","); if (nodep->lsbp()) { iterateAndNextNull(nodep->lsbp()); } - else puts(cvtToStr(array_lsb)); + else puts(cvtToStr(array_lsb)); putbs(","); if (nodep->msbp()) { iterateAndNextNull(nodep->msbp()); } else puts("~0"); - puts(");\n"); + puts(");\n"); } virtual void visit(AstFClose* nodep) { - puts("VL_FCLOSE_I("); + puts("VL_FCLOSE_I("); iterateAndNextNull(nodep->filep()); - puts("); "); + puts("); "); iterateAndNextNull(nodep->filep()); // For safety, so user doesn't later WRITE with it. - puts("=0;\n"); + puts("=0;\n"); } virtual void visit(AstFFlush* nodep) { - if (!nodep->filep()) { + if (!nodep->filep()) { puts("fflush(stdout);\n"); - } else { - puts("if ("); + } else { + puts("if ("); iterateAndNextNull(nodep->filep()); puts(") { fflush(VL_CVT_I_FP("); iterateAndNextNull(nodep->filep()); - puts(")); }\n"); - } + puts(")); }\n"); + } } virtual void visit(AstFRead* nodep) { puts("VL_FREAD_I("); @@ -432,369 +440,370 @@ public: if (!nodep->lhsp()->isWide()) puts(";"); } virtual void visit(AstSystemT* nodep) { - puts("(void)VL_SYSTEM_I"); - emitIQW(nodep->lhsp()); - puts("("); - if (nodep->lhsp()->isWide()) { - puts(cvtToStr(nodep->lhsp()->widthWords())); - putbs(", "); - } - checkMaxWords(nodep->lhsp()); + puts("(void)VL_SYSTEM_I"); + emitIQW(nodep->lhsp()); + puts("("); + if (nodep->lhsp()->isWide()) { + puts(cvtToStr(nodep->lhsp()->widthWords())); + putbs(", "); + } + checkMaxWords(nodep->lhsp()); iterateAndNextNull(nodep->lhsp()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstSystemF* nodep) { - puts("VL_SYSTEM_I"); - emitIQW(nodep->lhsp()); - puts("("); - if (nodep->lhsp()->isWide()) { - puts(cvtToStr(nodep->lhsp()->widthWords())); - putbs(", "); - } - checkMaxWords(nodep->lhsp()); + puts("VL_SYSTEM_I"); + emitIQW(nodep->lhsp()); + puts("("); + if (nodep->lhsp()->isWide()) { + puts(cvtToStr(nodep->lhsp()->widthWords())); + putbs(", "); + } + checkMaxWords(nodep->lhsp()); iterateAndNextNull(nodep->lhsp()); - puts(")"); + puts(")"); } virtual void visit(AstJumpGo* nodep) { - puts("goto __Vlabel"+cvtToStr(nodep->labelp()->labelNum())+";\n"); + puts("goto __Vlabel"+cvtToStr(nodep->labelp()->labelNum())+";\n"); } virtual void visit(AstJumpLabel* nodep) { - puts("{\n"); + puts("{\n"); iterateAndNextNull(nodep->stmtsp()); - puts("}\n"); - puts("__Vlabel"+cvtToStr(nodep->labelNum())+": ;\n"); + puts("}\n"); + puts("__Vlabel"+cvtToStr(nodep->labelNum())+": ;\n"); } virtual void visit(AstWhile* nodep) { iterateAndNextNull(nodep->precondsp()); - puts("while ("); + puts("while ("); iterateAndNextNull(nodep->condp()); - puts(") {\n"); + puts(") {\n"); iterateAndNextNull(nodep->bodysp()); iterateAndNextNull(nodep->incsp()); iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop - puts("}\n"); + puts("}\n"); } virtual void visit(AstNodeIf* nodep) { - puts("if ("); - if (nodep->branchPred() != AstBranchPred::BP_UNKNOWN) { - puts(nodep->branchPred().ascii()); puts("("); - } + puts("if ("); + if (nodep->branchPred() != AstBranchPred::BP_UNKNOWN) { + puts(nodep->branchPred().ascii()); puts("("); + } iterateAndNextNull(nodep->condp()); - if (nodep->branchPred() != AstBranchPred::BP_UNKNOWN) puts(")"); - puts(") {\n"); + if (nodep->branchPred() != AstBranchPred::BP_UNKNOWN) puts(")"); + puts(") {\n"); iterateAndNextNull(nodep->ifsp()); - if (nodep->elsesp()) { - puts("} else {\n"); + if (nodep->elsesp()) { + puts("} else {\n"); iterateAndNextNull(nodep->elsesp()); - } - puts("}\n"); + } + puts("}\n"); } virtual void visit(AstStop* nodep) { - puts("VL_STOP_MT("); - putsQuoted(nodep->fileline()->filename()); - puts(","); - puts(cvtToStr(nodep->fileline()->lineno())); - puts(",\"\");\n"); + puts("VL_STOP_MT("); + putsQuoted(nodep->fileline()->filename()); + puts(","); + puts(cvtToStr(nodep->fileline()->lineno())); + puts(",\"\");\n"); } virtual void visit(AstFinish* nodep) { - puts("VL_FINISH_MT("); - putsQuoted(nodep->fileline()->filename()); - puts(","); - puts(cvtToStr(nodep->fileline()->lineno())); - puts(",\"\");\n"); + puts("VL_FINISH_MT("); + putsQuoted(nodep->fileline()->filename()); + puts(","); + puts(cvtToStr(nodep->fileline()->lineno())); + puts(",\"\");\n"); } virtual void visit(AstText* nodep) { - if (nodep->tracking()) { - puts(nodep->text()); - } else { - ofp()->putsNoTracking(nodep->text()); - } + if (nodep->tracking()) { + puts(nodep->text()); + } else { + ofp()->putsNoTracking(nodep->text()); + } } virtual void visit(AstCStmt* nodep) { - putbs(""); + putbs(""); iterateAndNextNull(nodep->bodysp()); } virtual void visit(AstCMath* nodep) { - putbs(""); + putbs(""); iterateAndNextNull(nodep->bodysp()); } virtual void visit(AstUCStmt* nodep) { - putsDecoration("// $c statement at "+nodep->fileline()->ascii()+"\n"); + putsDecoration("// $c statement at "+nodep->fileline()->ascii()+"\n"); iterateAndNextNull(nodep->bodysp()); - puts("\n"); + puts("\n"); } virtual void visit(AstUCFunc* nodep) { - puts("\n"); - putsDecoration("// $c function at "+nodep->fileline()->ascii()+"\n"); + puts("\n"); + putsDecoration("// $c function at "+nodep->fileline()->ascii()+"\n"); iterateAndNextNull(nodep->bodysp()); - puts("\n"); + puts("\n"); } // Operators virtual void visit(AstNodeTermop* nodep) { - emitOpName(nodep, nodep->emitC(), NULL, NULL, NULL); + emitOpName(nodep, nodep->emitC(), NULL, NULL, NULL); } virtual void visit(AstNodeUniop* nodep) { - if (emitSimpleOk(nodep)) { - putbs("("); puts(nodep->emitSimpleOperator()); puts(" "); + if (emitSimpleOk(nodep)) { + putbs("("); puts(nodep->emitSimpleOperator()); puts(" "); iterateAndNextNull(nodep->lhsp()); puts(")"); - } else { - emitOpName(nodep, nodep->emitC(), nodep->lhsp(), NULL, NULL); - } + } else { + emitOpName(nodep, nodep->emitC(), nodep->lhsp(), NULL, NULL); + } } virtual void visit(AstNodeBiop* nodep) { - if (emitSimpleOk(nodep)) { + if (emitSimpleOk(nodep)) { putbs("("); iterateAndNextNull(nodep->lhsp()); - puts(" "); putbs(nodep->emitSimpleOperator()); puts(" "); + puts(" "); putbs(nodep->emitSimpleOperator()); puts(" "); iterateAndNextNull(nodep->rhsp()); puts(")"); - } else { - emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL); - } + } else { + emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL); + } } virtual void visit(AstRedXor* nodep) { - if (nodep->lhsp()->isWide()) { + if (nodep->lhsp()->isWide()) { visit(VN_CAST(nodep, NodeUniop)); - } else { - putbs("VL_REDXOR_"); - puts(cvtToStr(nodep->lhsp()->dtypep()->widthPow2())); - puts("("); + } else { + putbs("VL_REDXOR_"); + puts(cvtToStr(nodep->lhsp()->dtypep()->widthPow2())); + puts("("); iterateAndNextNull(nodep->lhsp()); - puts(")"); - } + puts(")"); + } } virtual void visit(AstMulS* nodep) { - if (nodep->widthWords() > VL_MULS_MAX_WORDS) { + if (nodep->widthWords() > VL_MULS_MAX_WORDS) { nodep->v3error("Unsupported: Signed multiply of "<width() <<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h"); - } + } visit(VN_CAST(nodep, NodeBiop)); } virtual void visit(AstPow* nodep) { - if (nodep->widthWords() > VL_MULS_MAX_WORDS) { + if (nodep->widthWords() > VL_MULS_MAX_WORDS) { nodep->v3error("Unsupported: Power of "<width() <<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h"); - } + } visit(VN_CAST(nodep, NodeBiop)); } virtual void visit(AstPowSS* nodep) { - if (nodep->widthWords() > VL_MULS_MAX_WORDS) { + if (nodep->widthWords() > VL_MULS_MAX_WORDS) { nodep->v3error("Unsupported: Power of "<width() <<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h"); - } + } visit(VN_CAST(nodep, NodeBiop)); } virtual void visit(AstPowSU* nodep) { - if (nodep->widthWords() > VL_MULS_MAX_WORDS) { + if (nodep->widthWords() > VL_MULS_MAX_WORDS) { nodep->v3error("Unsupported: Power of "<width() <<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h"); - } + } visit(VN_CAST(nodep, NodeBiop)); } virtual void visit(AstPowUS* nodep) { - if (nodep->widthWords() > VL_MULS_MAX_WORDS) { + if (nodep->widthWords() > VL_MULS_MAX_WORDS) { nodep->v3error("Unsupported: Power of "<width() <<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h"); - } + } visit(VN_CAST(nodep, NodeBiop)); } virtual void visit(AstCCast* nodep) { - // Extending a value of the same word width is just a NOP. - if (nodep->size()>VL_WORDSIZE) { - puts("(QData)("); - } else { - puts("(IData)("); - } + // Extending a value of the same word width is just a NOP. + if (nodep->size()>VL_WORDSIZE) { + puts("(QData)("); + } else { + puts("(IData)("); + } iterateAndNextNull(nodep->lhsp()); - puts(")"); + puts(")"); } virtual void visit(AstNodeCond* nodep) { - // Widths match up already, so we'll just use C++'s operator w/o any temps. - if (nodep->expr1p()->isWide()) { - emitOpName(nodep, nodep->emitC(), nodep->condp(), nodep->expr1p(), nodep->expr2p()); - } else { - putbs("("); + // Widths match up already, so we'll just use C++'s operator w/o any temps. + if (nodep->expr1p()->isWide()) { + emitOpName(nodep, nodep->emitC(), nodep->condp(), nodep->expr1p(), nodep->expr2p()); + } else { + putbs("("); iterateAndNextNull(nodep->condp()); putbs(" ? "); iterateAndNextNull(nodep->expr1p()); putbs(" : "); iterateAndNextNull(nodep->expr2p()); puts(")"); - } + } } virtual void visit(AstSel* nodep) { - // Note ASSIGN checks for this on a LHS - emitOpName(nodep, nodep->emitC(), nodep->fromp(), nodep->lsbp(), nodep->thsp()); + // Note ASSIGN checks for this on a LHS + emitOpName(nodep, nodep->emitC(), nodep->fromp(), nodep->lsbp(), nodep->thsp()); } virtual void visit(AstReplicate* nodep) { - if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) { + if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) { if ((static_cast(VN_CAST(nodep->rhsp(), Const)->toUInt()) * nodep->lhsp()->widthMin()) != nodep->widthMin()) - nodep->v3fatalSrc("Replicate non-constant or width miscomputed"); - puts("VL_REPLICATE_"); - emitIQW(nodep); - puts("OI("); - puts(cvtToStr(nodep->widthMin())); - if (nodep->lhsp()) { puts(","+cvtToStr(nodep->lhsp()->widthMin())); } - if (nodep->rhsp()) { puts(","+cvtToStr(nodep->rhsp()->widthMin())); } - puts(","); + nodep->v3fatalSrc("Replicate non-constant or width miscomputed"); + puts("VL_REPLICATE_"); + emitIQW(nodep); + puts("OI("); + puts(cvtToStr(nodep->widthMin())); + if (nodep->lhsp()) { puts(","+cvtToStr(nodep->lhsp()->widthMin())); } + if (nodep->rhsp()) { puts(","+cvtToStr(nodep->rhsp()->widthMin())); } + puts(","); iterateAndNextNull(nodep->lhsp()); puts(", "); iterateAndNextNull(nodep->rhsp()); puts(")"); - } else { - emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL); - } + } else { + emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL); + } } virtual void visit(AstStreamL* nodep) { - // Attempt to use a "fast" stream function for slice size = power of 2 - if (!nodep->isWide()) { + // Attempt to use a "fast" stream function for slice size = power of 2 + if (!nodep->isWide()) { uint32_t isPow2 = VN_CAST(nodep->rhsp(), Const)->num().countOnes() == 1; uint32_t sliceSize = VN_CAST(nodep->rhsp(), Const)->toUInt(); - if (isPow2 && sliceSize <= (nodep->isQuad() ? sizeof(uint64_t) : sizeof(uint32_t))) { - puts("VL_STREAML_FAST_"); - emitIQW(nodep); - emitIQW(nodep->lhsp()); - puts("I("); - puts(cvtToStr(nodep->widthMin())); - puts(","+cvtToStr(nodep->lhsp()->widthMin())); - puts(","+cvtToStr(nodep->rhsp()->widthMin())); - puts(","); + if (isPow2 && sliceSize <= (nodep->isQuad() ? sizeof(uint64_t) : sizeof(uint32_t))) { + puts("VL_STREAML_FAST_"); + emitIQW(nodep); + emitIQW(nodep->lhsp()); + puts("I("); + puts(cvtToStr(nodep->widthMin())); + puts(","+cvtToStr(nodep->lhsp()->widthMin())); + puts(","+cvtToStr(nodep->rhsp()->widthMin())); + puts(","); iterateAndNextNull(nodep->lhsp()); puts(", "); uint32_t rd_log2 = V3Number::log2b(VN_CAST(nodep->rhsp(), Const)->toUInt()); - puts(cvtToStr(rd_log2)+")"); - return; - } - } - emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(), nodep->rhsp(), NULL); + puts(cvtToStr(rd_log2)+")"); + return; + } + } + emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", + nodep->lhsp(), nodep->rhsp(), NULL); } // Terminals virtual void visit(AstVarRef* nodep) { - puts(nodep->hiername()); - puts(nodep->varp()->name()); + puts(nodep->hiername()); + puts(nodep->varp()->name()); } void emitCvtPackStr(AstNode* nodep) { if (const AstConst* constp = VN_CAST(nodep, Const)) { - putbs("std::string("); - putsQuoted(constp->num().toString()); - puts(")"); - } else { - putbs("VL_CVT_PACK_STR_N"); - emitIQW(nodep); - puts("("); - if (nodep->isWide()) { - puts(cvtToStr(nodep->widthWords())); // Note argument width, not node width (which is always 32) - puts(","); - } + putbs("std::string("); + putsQuoted(constp->num().toString()); + puts(")"); + } else { + putbs("VL_CVT_PACK_STR_N"); + emitIQW(nodep); + puts("("); + if (nodep->isWide()) { + puts(cvtToStr(nodep->widthWords())); // Note argument width, not node width (which is always 32) + puts(","); + } iterateAndNextNull(nodep); - puts(")"); - } + puts(")"); + } } void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString) { - // Put out constant set to the specified variable, or given variable in a string - if (nodep->num().isFourState()) { - nodep->v3error("Unsupported: 4-state numbers in this context"); - } else if (nodep->num().isString()) { - putbs("std::string("); - putsQuoted(nodep->num().toString()); - puts(")"); - } else if (nodep->isWide()) { - int upWidth = nodep->num().widthMin(); - int chunks = 0; - if (upWidth > EMITC_NUM_CONSTW*VL_WORDSIZE) { - // Output e.g. 8 words in groups of e.g. 8 - chunks = (upWidth-1) / (EMITC_NUM_CONSTW*VL_WORDSIZE); - upWidth %= (EMITC_NUM_CONSTW*VL_WORDSIZE); - if (upWidth == 0) upWidth = (EMITC_NUM_CONSTW*VL_WORDSIZE); - } - { // Upper e.g. 8 words - if (chunks) { - putbs("VL_CONSTHI_W_"); - puts(cvtToStr(VL_WORDS_I(upWidth))); - puts("X("); - puts(cvtToStr(nodep->widthMin())); - puts(","); - puts(cvtToStr(chunks*EMITC_NUM_CONSTW*VL_WORDSIZE)); - } else { - putbs("VL_CONST_W_"); - puts(cvtToStr(VL_WORDS_I(upWidth))); - puts("X("); - puts(cvtToStr(nodep->widthMin())); - } - puts(","); - if (!assigntop) { - puts(assignString); + // Put out constant set to the specified variable, or given variable in a string + if (nodep->num().isFourState()) { + nodep->v3error("Unsupported: 4-state numbers in this context"); + } else if (nodep->num().isString()) { + putbs("std::string("); + putsQuoted(nodep->num().toString()); + puts(")"); + } else if (nodep->isWide()) { + int upWidth = nodep->num().widthMin(); + int chunks = 0; + if (upWidth > EMITC_NUM_CONSTW*VL_WORDSIZE) { + // Output e.g. 8 words in groups of e.g. 8 + chunks = (upWidth-1) / (EMITC_NUM_CONSTW*VL_WORDSIZE); + upWidth %= (EMITC_NUM_CONSTW*VL_WORDSIZE); + if (upWidth == 0) upWidth = (EMITC_NUM_CONSTW*VL_WORDSIZE); + } + { // Upper e.g. 8 words + if (chunks) { + putbs("VL_CONSTHI_W_"); + puts(cvtToStr(VL_WORDS_I(upWidth))); + puts("X("); + puts(cvtToStr(nodep->widthMin())); + puts(","); + puts(cvtToStr(chunks*EMITC_NUM_CONSTW*VL_WORDSIZE)); + } else { + putbs("VL_CONST_W_"); + puts(cvtToStr(VL_WORDS_I(upWidth))); + puts("X("); + puts(cvtToStr(nodep->widthMin())); + } + puts(","); + if (!assigntop) { + puts(assignString); } else if (VN_IS(assigntop, VarRef)) { - puts(assigntop->hiername()); - puts(assigntop->varp()->name()); - } else { + puts(assigntop->hiername()); + puts(assigntop->varp()->name()); + } else { iterateAndNextNull(assigntop); - } - for (int word=VL_WORDS_I(upWidth)-1; word>=0; word--) { - // Only 32 bits - llx + long long here just to appease CPP format warning + } + for (int word=VL_WORDS_I(upWidth)-1; word>=0; word--) { + // Only 32 bits - llx + long long here just to appease CPP format warning ofp()->printf(",0x%08" VL_PRI64 "x", static_cast(nodep->num().dataWord (word+chunks*EMITC_NUM_CONSTW))); - } - puts(")"); - } - for (chunks--; chunks >= 0; chunks--) { - puts(";\n"); - putbs("VL_CONSTLO_W_"); - puts(cvtToStr(EMITC_NUM_CONSTW)); - puts("X("); - puts(cvtToStr(chunks*EMITC_NUM_CONSTW*VL_WORDSIZE)); - puts(","); - if (!assigntop) { - puts(assignString); + } + puts(")"); + } + for (chunks--; chunks >= 0; chunks--) { + puts(";\n"); + putbs("VL_CONSTLO_W_"); + puts(cvtToStr(EMITC_NUM_CONSTW)); + puts("X("); + puts(cvtToStr(chunks*EMITC_NUM_CONSTW*VL_WORDSIZE)); + puts(","); + if (!assigntop) { + puts(assignString); } else if (VN_IS(assigntop, VarRef)) { - puts(assigntop->hiername()); - puts(assigntop->varp()->name()); - } else { + puts(assigntop->hiername()); + puts(assigntop->varp()->name()); + } else { iterateAndNextNull(assigntop); - } - for (int word=EMITC_NUM_CONSTW-1; word>=0; word--) { - // Only 32 bits - llx + long long here just to appease CPP format warning + } + for (int word=EMITC_NUM_CONSTW-1; word>=0; word--) { + // Only 32 bits - llx + long long here just to appease CPP format warning ofp()->printf(",0x%08" VL_PRI64 "x", static_cast(nodep->num().dataWord (word+chunks*EMITC_NUM_CONSTW))); - } - puts(")"); - } - } else if (nodep->isDouble()) { - if (int(nodep->num().toDouble()) == nodep->num().toDouble() - && nodep->num().toDouble() < 1000 - && nodep->num().toDouble() > -1000) { - ofp()->printf("%3.1f", nodep->num().toDouble()); // Force decimal point - } else { - // Not %g as will not always put in decimal point, so not obvious to compiler - // is a real number - ofp()->printf("%.17e", nodep->num().toDouble()); - } - } else if (nodep->isQuad()) { - vluint64_t num = nodep->toUQuad(); - if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "u)", num); - else ofp()->printf("VL_ULL(0x%" VL_PRI64 "x)", num); - } else { - uint32_t num = nodep->toUInt(); - // Only 32 bits - llx + long long here just to appease CPP format warning - if (num<10) puts(cvtToStr(num)); + } + puts(")"); + } + } else if (nodep->isDouble()) { + if (int(nodep->num().toDouble()) == nodep->num().toDouble() + && nodep->num().toDouble() < 1000 + && nodep->num().toDouble() > -1000) { + ofp()->printf("%3.1f", nodep->num().toDouble()); // Force decimal point + } else { + // Not %g as will not always put in decimal point, so not obvious to compiler + // is a real number + ofp()->printf("%.17e", nodep->num().toDouble()); + } + } else if (nodep->isQuad()) { + vluint64_t num = nodep->toUQuad(); + if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "u)", num); + else ofp()->printf("VL_ULL(0x%" VL_PRI64 "x)", num); + } else { + uint32_t num = nodep->toUInt(); + // Only 32 bits - llx + long long here just to appease CPP format warning + if (num<10) puts(cvtToStr(num)); else ofp()->printf("0x%" VL_PRI64 "x", static_cast(num)); - // If signed, we'll do our own functions - // But must be here, or <= comparisons etc may end up signed - puts("U"); - } + // If signed, we'll do our own functions + // But must be here, or <= comparisons etc may end up signed + puts("U"); + } } void emitSetVarConstant(const string& assignString, AstConst* constp) { - if (!constp->isWide()) { - puts(assignString); - puts(" = "); - } - emitConstant(constp, NULL, assignString); - puts(";\n"); + if (!constp->isWide()) { + puts(assignString); + puts(" = "); + } + emitConstant(constp, NULL, assignString); + puts(";\n"); } virtual void visit(AstConst* nodep) { - if (nodep->isWide()) { - if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Constant w/ no temp"); - emitConstant(nodep, m_wideTempRefp, ""); - m_wideTempRefp = NULL; // We used it, barf if set it a second time - } else { - emitConstant(nodep, NULL, ""); - } + if (nodep->isWide()) { + if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Constant w/ no temp"); + emitConstant(nodep, m_wideTempRefp, ""); + m_wideTempRefp = NULL; // We used it, barf if set it a second time + } else { + emitConstant(nodep, NULL, ""); + } } // Just iterate @@ -810,25 +819,25 @@ public: // NOPs virtual void visit(AstTypedef*) {} virtual void visit(AstPragma*) {} - virtual void visit(AstCell*) {} // Handled outside the Visit class - virtual void visit(AstVar*) {} // Handled outside the Visit class - virtual void visit(AstNodeText*) {} // Handled outside the Visit class - virtual void visit(AstTraceDecl*) {} // Handled outside the Visit class - virtual void visit(AstTraceInc*) {} // Handled outside the Visit class - virtual void visit(AstCFile*) {} // Handled outside the Visit class + virtual void visit(AstCell*) {} // Handled outside the Visit class + virtual void visit(AstVar*) {} // Handled outside the Visit class + virtual void visit(AstNodeText*) {} // Handled outside the Visit class + virtual void visit(AstTraceDecl*) {} // Handled outside the Visit class + virtual void visit(AstTraceInc*) {} // Handled outside the Visit class + virtual void visit(AstCFile*) {} // Handled outside the Visit class // Default virtual void visit(AstNode* nodep) { puts(string("\n???? // ")+nodep->prettyTypeName()+"\n"); iterateChildren(nodep); - nodep->v3fatalSrc("Unknown node type reached emitter: "<prettyTypeName()); + nodep->v3fatalSrc("Unknown node type reached emitter: "<prettyTypeName()); } public: EmitCStmts() { - m_suppressSemi = false; - m_wideTempRefp = NULL; - m_splitSize = 0; - m_splitFilenum = 0; + m_suppressSemi = false; + m_wideTempRefp = NULL; + m_splitSize = 0; + m_splitFilenum = 0; } virtual ~EmitCStmts() {} }; @@ -882,91 +891,91 @@ unsigned EmitVarTspSorter::m_serialNext = 0; class EmitCImp : EmitCStmts { // MEMBERS - AstNodeModule* m_modp; + AstNodeModule* m_modp; std::vector m_blkChangeDetVec; // All encountered changes in block - bool m_slow; // Creating __Slow file - bool m_fast; // Creating non __Slow file (or both) + bool m_slow; // Creating __Slow file + bool m_fast; // Creating non __Slow file (or both) //--------------------------------------- // METHODS void doubleOrDetect(AstChangeDet* changep, bool& gotOne) { static int s_addDoubleOr = 10; // Determined experimentally as best - if (!changep->rhsp()) { - if (!gotOne) gotOne = true; - else puts(" | "); + if (!changep->rhsp()) { + if (!gotOne) gotOne = true; + else puts(" | "); iterateAndNextNull(changep->lhsp()); - } - else { - AstNode* lhsp = changep->lhsp(); - AstNode* rhsp = changep->rhsp(); + } + else { + AstNode* lhsp = changep->lhsp(); + AstNode* rhsp = changep->rhsp(); if (!VN_IS(lhsp, VarRef) && !VN_IS(lhsp, ArraySel)) changep->v3fatalSrc("Not ref?"); if (!VN_IS(rhsp, VarRef) && !VN_IS(rhsp, ArraySel)) changep->v3fatalSrc("Not ref?"); for (int word=0; word < (changep->lhsp()->isWide() ? changep->lhsp()->widthWords() : 1); ++word) { - if (!gotOne) { - gotOne = true; + if (!gotOne) { + gotOne = true; s_addDoubleOr = 10; - puts("("); + puts("("); } else if (--s_addDoubleOr == 0) { - puts("|| ("); + puts("|| ("); s_addDoubleOr = 10; - } else { - puts(" | ("); - } + } else { + puts(" | ("); + } iterateAndNextNull(changep->lhsp()); - if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]"); - if (changep->lhsp()->isDouble()) puts(" != "); - else puts(" ^ "); + if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]"); + if (changep->lhsp()->isDouble()) puts(" != "); + else puts(" ^ "); iterateAndNextNull(changep->rhsp()); - if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]"); - puts(")"); - } - } + if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]"); + puts(")"); + } + } } V3OutCFile* newOutCFile(AstNodeModule* modp, bool slow, bool source, int filenum=0) { - string filenameNoExt = v3Global.opt.makeDir()+"/"+ modClassName(modp); - if (filenum) filenameNoExt += "__"+cvtToStr(filenum); - filenameNoExt += (slow ? "__Slow":""); - V3OutCFile* ofp = NULL; - if (v3Global.opt.lintOnly()) { - // Unfortunately we have some lint checks here, so we can't just skip processing. - // We should move them to a different stage. - string filename = VL_DEV_NULL; - newCFile(filename, slow, source); + string filenameNoExt = v3Global.opt.makeDir()+"/"+ modClassName(modp); + if (filenum) filenameNoExt += "__"+cvtToStr(filenum); + filenameNoExt += (slow ? "__Slow":""); + V3OutCFile* ofp = NULL; + if (v3Global.opt.lintOnly()) { + // Unfortunately we have some lint checks here, so we can't just skip processing. + // We should move them to a different stage. + string filename = VL_DEV_NULL; + newCFile(filename, slow, source); ofp = new V3OutCFile(filename); - } - else if (optSystemC()) { - string filename = filenameNoExt+(source?".cpp":".h"); - newCFile(filename, slow, source); + } + else if (optSystemC()) { + string filename = filenameNoExt+(source?".cpp":".h"); + newCFile(filename, slow, source); ofp = new V3OutScFile(filename); - } - else { - string filename = filenameNoExt+(source?".cpp":".h"); - newCFile(filename, slow, source); + } + else { + string filename = filenameNoExt+(source?".cpp":".h"); + newCFile(filename, slow, source); ofp = new V3OutCFile(filename); - } + } - ofp->putsHeader(); - if (modp->isTop() && !source) { - ofp->puts("// DESCR" "IPTION: Verilator output: Primary design header\n"); - ofp->puts("//\n"); - ofp->puts("// This header should be included by all source files instantiating the design.\n"); - ofp->puts("// The class here is then constructed to instantiate the design.\n"); - ofp->puts("// See the Verilator manual for examples.\n"); - } else { - if (source) { - ofp->puts("// DESCR" "IPTION: Verilator output: Design implementation internals\n"); - } else { - ofp->puts("// DESCR" "IPTION: Verilator output: Design internal header\n"); - } - ofp->puts("// See "+v3Global.opt.prefix()+".h for the primary calling header\n"); - } - ofp->puts("\n"); + ofp->putsHeader(); + if (modp->isTop() && !source) { + ofp->puts("// DESCR" "IPTION: Verilator output: Primary design header\n"); + ofp->puts("//\n"); + ofp->puts("// This header should be included by all source files instantiating the design.\n"); + ofp->puts("// The class here is then constructed to instantiate the design.\n"); + ofp->puts("// See the Verilator manual for examples.\n"); + } else { + if (source) { + ofp->puts("// DESCR" "IPTION: Verilator output: Design implementation internals\n"); + } else { + ofp->puts("// DESCR" "IPTION: Verilator output: Design internal header\n"); + } + ofp->puts("// See "+v3Global.opt.prefix()+".h for the primary calling header\n"); + } + ofp->puts("\n"); - return ofp; + return ofp; } // Returns the number of cross-thread dependencies into mtaskp. @@ -1058,100 +1067,100 @@ class EmitCImp : EmitCStmts { // VISITORS using EmitCStmts::visit; // Suppress hidden overloaded virtual function warning virtual void visit(AstCFunc* nodep) { - // TRACE_* and DPI handled elsewhere - if (nodep->funcType().isTrace()) return; - if (nodep->dpiImport()) return; - if (!(nodep->slow() ? m_slow : m_fast)) return; + // TRACE_* and DPI handled elsewhere + if (nodep->funcType().isTrace()) return; + if (nodep->dpiImport()) return; + if (!(nodep->slow() ? m_slow : m_fast)) return; - m_blkChangeDetVec.clear(); + m_blkChangeDetVec.clear(); - splitSizeInc(nodep); + splitSizeInc(nodep); - puts("\n"); - if (nodep->ifdef()!="") puts("#ifdef "+nodep->ifdef()+"\n"); - if (nodep->isInline()) puts("VL_INLINE_OPT "); - puts(nodep->rtnTypeVoid()); puts(" "); - puts(modClassName(m_modp)+"::"+nodep->name() - +"("+cFuncArgs(nodep)+") {\n"); + puts("\n"); + if (nodep->ifdef()!="") puts("#ifdef "+nodep->ifdef()+"\n"); + if (nodep->isInline()) puts("VL_INLINE_OPT "); + puts(nodep->rtnTypeVoid()); puts(" "); + puts(modClassName(m_modp)+"::"+nodep->name() + +"("+cFuncArgs(nodep)+") {\n"); - // "+" in the debug indicates a print from the model - puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ "); - for (int i=0;ilevel();i++) { puts(" "); } - puts(modClassName(m_modp)+"::"+nodep->name() - +"\\n\"); );\n"); + // "+" in the debug indicates a print from the model + puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ "); + for (int i=0; ilevel(); ++i) { puts(" "); } + puts(modClassName(m_modp)+"::"+nodep->name() + +"\\n\"); );\n"); // Declare and set vlTOPp - if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n"); + if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n"); - if (nodep->initsp()) putsDecoration("// Variables\n"); - for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { - if (AstVar* varp=VN_CAST(subnodep, Var)) { - if (varp->isFuncReturn()) emitVarDecl(varp, ""); - } - } + if (nodep->initsp()) putsDecoration("// Variables\n"); + for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) { + if (AstVar* varp = VN_CAST(subnodep, Var)) { + if (varp->isFuncReturn()) emitVarDecl(varp, ""); + } + } emitVarList(nodep->initsp(), EVL_FUNC_ALL, ""); emitVarList(nodep->stmtsp(), EVL_FUNC_ALL, ""); iterateAndNextNull(nodep->initsp()); - if (nodep->stmtsp()) putsDecoration("// Body\n"); + if (nodep->stmtsp()) putsDecoration("// Body\n"); iterateAndNextNull(nodep->stmtsp()); - if (!m_blkChangeDetVec.empty()) emitChangeDet(); + if (!m_blkChangeDetVec.empty()) emitChangeDet(); - if (nodep->finalsp()) putsDecoration("// Final\n"); + if (nodep->finalsp()) putsDecoration("// Final\n"); iterateAndNextNull(nodep->finalsp()); - // + // - if (!m_blkChangeDetVec.empty()) puts("return __req;\n"); + if (!m_blkChangeDetVec.empty()) puts("return __req;\n"); - //puts("__Vm_activity = true;\n"); - puts("}\n"); - if (nodep->ifdef()!="") puts("#endif // "+nodep->ifdef()+"\n"); + //puts("__Vm_activity = true;\n"); + puts("}\n"); + if (nodep->ifdef()!="") puts("#endif // "+nodep->ifdef()+"\n"); } void emitChangeDet() { - putsDecoration("// Change detection\n"); - puts("QData __req = false; // Logically a bool\n"); // But not because it results in faster code - bool gotOne = false; + putsDecoration("// Change detection\n"); + puts("QData __req = false; // Logically a bool\n"); // But not because it results in faster code + bool gotOne = false; for (std::vector::iterator it = m_blkChangeDetVec.begin(); - it != m_blkChangeDetVec.end(); ++it) { - AstChangeDet* changep = *it; - if (changep->lhsp()) { - if (!gotOne) { // Not a clocked block - puts("__req |= ("); - } - else puts("\n"); - doubleOrDetect(changep, gotOne); - } - } - if (gotOne) { + it != m_blkChangeDetVec.end(); ++it) { + AstChangeDet* changep = *it; + if (changep->lhsp()) { + if (!gotOne) { // Not a clocked block + puts("__req |= ("); + } + else puts("\n"); + doubleOrDetect(changep, gotOne); + } + } + if (gotOne) { puts(");\n"); //puts("VL_DEBUG_IF( if (__req) cout<<\"- CLOCKREQ );"); for (std::vector::iterator it = m_blkChangeDetVec.begin(); - it != m_blkChangeDetVec.end(); ++it) { - AstChangeDet* nodep = *it; - if (nodep->lhsp()) { - puts("VL_DEBUG_IF( if(__req && ("); - bool gotOneIgnore = false; - doubleOrDetect(nodep, gotOneIgnore); - string varname; + it != m_blkChangeDetVec.end(); ++it) { + AstChangeDet* nodep = *it; + if (nodep->lhsp()) { + puts("VL_DEBUG_IF( if(__req && ("); + bool gotOneIgnore = false; + doubleOrDetect(nodep, gotOneIgnore); + string varname; if (VN_IS(nodep->lhsp(), VarRef)) { varname = ": "+VN_CAST(nodep->lhsp(), VarRef)->varp()->prettyName(); - } - puts(")) VL_DBG_MSGF(\" CHANGE: "+nodep->fileline()->ascii() - +varname+"\\n\"); );\n"); - } - } - } + } + puts(")) VL_DBG_MSGF(\" CHANGE: "+nodep->fileline()->ascii() + +varname+"\\n\"); );\n"); + } + } + } } virtual void visit(AstChangeDet* nodep) { - m_blkChangeDetVec.push_back(nodep); + m_blkChangeDetVec.push_back(nodep); } virtual void visit(AstCReset* nodep) { - AstVar* varp = nodep->varrefp()->varp(); - emitVarReset(varp); + AstVar* varp = nodep->varrefp()->varp(); + emitVarReset(varp); } virtual void visit(AstExecGraph* nodep) { @@ -1231,9 +1240,9 @@ class EmitCImp : EmitCStmts { public: EmitCImp() { - m_modp = NULL; - m_slow = false; - m_fast = false; + m_modp = NULL; + m_slow = false; + m_fast = false; } virtual ~EmitCImp() {} void main(AstNodeModule* modp, bool slow, bool fast); @@ -1246,10 +1255,11 @@ public: // Internal EmitCStmts void EmitCStmts::emitVarDecl(const AstVar* nodep, const string& prefixIfImp) { - AstBasicDType* basicp = nodep->basicp(); if (!basicp) nodep->v3fatalSrc("Unimplemented: Outputting this data type"); + AstBasicDType* basicp = nodep->basicp(); + if (!basicp) nodep->v3fatalSrc("Unimplemented: Outputting this data type"); if (nodep->isIO()) { - if (nodep->isSc()) { - m_ctorVarsVec.push_back(nodep); + if (nodep->isSc()) { + m_ctorVarsVec.push_back(nodep); if (nodep->attrScClocked() && nodep->isReadOnly()) { puts("sc_in_clk "); } else { @@ -1258,65 +1268,67 @@ void EmitCStmts::emitVarDecl(const AstVar* nodep, const string& prefixIfImp) { else if (nodep->isNonOutput()) puts("sc_in<"); else nodep->v3fatalSrc("Unknown type"); - puts(nodep->scType()); - puts("> "); - } - puts(nodep->name()); - emitDeclArrayBrackets(nodep); - puts(";\n"); - } else if (basicp && basicp->isOpaque()) { - // strings and other fundamental c types; no VL_ macro can be used - puts(nodep->vlArgType(true,false,false)); - emitDeclArrayBrackets(nodep); - puts(";\n"); + puts(nodep->scType()); + puts("> "); + } + puts(nodep->name()); + emitDeclArrayBrackets(nodep); + puts(";\n"); + } else if (basicp && basicp->isOpaque()) { + // strings and other fundamental c types; no VL_ macro can be used + puts(nodep->vlArgType(true, false, false)); + emitDeclArrayBrackets(nodep); + puts(";\n"); } else { // C++ signals if (nodep->isInoutish()) puts("VL_INOUT"); else if (nodep->isWritable()) puts("VL_OUT"); else if (nodep->isNonOutput()) puts("VL_IN"); else nodep->v3fatalSrc("Unknown type"); - if (nodep->isQuad()) puts("64"); - else if (nodep->widthMin() <= 8) puts("8"); - else if (nodep->widthMin() <= 16) puts("16"); - else if (nodep->isWide()) puts("W"); + if (nodep->isQuad()) puts("64"); + else if (nodep->widthMin() <= 8) puts("8"); + else if (nodep->widthMin() <= 16) puts("16"); + else if (nodep->isWide()) puts("W"); - puts("("+nodep->name()); - emitDeclArrayBrackets(nodep); - // If it's a packed struct/array then nodep->width is the whole thing, msb/lsb is just lowest dimension - puts(","+cvtToStr(basicp->lsb()+nodep->width()-1) - +","+cvtToStr(basicp->lsb())); - if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords())); - puts(");\n"); - } + puts("("+nodep->name()); + emitDeclArrayBrackets(nodep); + // If it's a packed struct/array then nodep->width is the whole + // thing, msb/lsb is just lowest dimension + puts(","+cvtToStr(basicp->lsb()+nodep->width()-1) + +","+cvtToStr(basicp->lsb())); + if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords())); + puts(");\n"); + } } else if (basicp && basicp->isOpaque()) { - // strings and other fundamental c types - puts(nodep->vlArgType(true,false,false)); - emitDeclArrayBrackets(nodep); - puts(";\n"); + // strings and other fundamental c types + puts(nodep->vlArgType(true, false, false)); + emitDeclArrayBrackets(nodep); + puts(";\n"); } else { - // Arrays need a small alignment, but may need different padding after. - // For example three VL_SIG8's needs alignment 1 but size 3. - if (nodep->isStatic() && prefixIfImp=="") puts("static "); - if (nodep->isStatic()) puts("VL_ST_"); else puts("VL_"); - if (nodep->widthMin() <= 8) { - puts("SIG8("); - } else if (nodep->widthMin() <= 16) { - puts("SIG16("); - } else if (nodep->isQuad()) { - puts("SIG64("); - } else if (!nodep->isWide()) { - puts("SIG("); - } else { - puts("SIGW("); - } - if (prefixIfImp!="") { puts(prefixIfImp); puts("::"); } - puts(nodep->name()); - emitDeclArrayBrackets(nodep); - // If it's a packed struct/array then nodep->width is the whole thing, msb/lsb is just lowest dimension - puts(","+cvtToStr(basicp->lsb()+nodep->width()-1) - +","+cvtToStr(basicp->lsb())); - if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords())); - puts(");\n"); + // Arrays need a small alignment, but may need different padding after. + // For example three VL_SIG8's needs alignment 1 but size 3. + if (nodep->isStatic() && prefixIfImp=="") puts("static "); + if (nodep->isStatic()) puts("VL_ST_"); else puts("VL_"); + if (nodep->widthMin() <= 8) { + puts("SIG8("); + } else if (nodep->widthMin() <= 16) { + puts("SIG16("); + } else if (nodep->isQuad()) { + puts("SIG64("); + } else if (!nodep->isWide()) { + puts("SIG("); + } else { + puts("SIGW("); + } + if (prefixIfImp!="") { puts(prefixIfImp); puts("::"); } + puts(nodep->name()); + emitDeclArrayBrackets(nodep); + // If it's a packed struct/array then nodep->width is the whole + // thing, msb/lsb is just lowest dimension + puts(","+cvtToStr(basicp->lsb()+nodep->width()-1) + +","+cvtToStr(basicp->lsb())); + if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords())); + puts(");\n"); } } @@ -1331,24 +1343,24 @@ void EmitCStmts::emitCtorSep(bool* firstp) { void EmitCStmts::emitVarCtors(bool* firstp) { if (!m_ctorVarsVec.empty()) { - ofp()->indentInc(); - puts("\n"); - puts("#if (SYSTEMC_VERSION>20011000)\n"); // SystemC 2.0.1 and newer + ofp()->indentInc(); + puts("\n"); + puts("#if (SYSTEMC_VERSION>20011000)\n"); // SystemC 2.0.1 and newer for (VarVec::iterator it = m_ctorVarsVec.begin(); it != m_ctorVarsVec.end(); ++it) { const AstVar* varp = *it; bool isArray = !VN_CAST(varp->dtypeSkipRefp(), BasicDType); - if (isArray) { - puts("// Skipping array: "); - puts(varp->name()); - puts("\n"); - } else { + if (isArray) { + puts("// Skipping array: "); + puts(varp->name()); + puts("\n"); + } else { emitCtorSep(firstp); - puts(varp->name()); - puts("("); putsQuoted(varp->name()); puts(")"); - } - } + puts(varp->name()); + puts("("); putsQuoted(varp->name()); puts(")"); + } + } puts("\n#endif\n"); - ofp()->indentDec(); + ofp()->indentDec(); } } @@ -1363,98 +1375,98 @@ bool EmitCStmts::emitSimpleOk(AstNodeMath* nodep) { } void EmitCStmts::emitOpName(AstNode* nodep, const string& format, - AstNode* lhsp, AstNode* rhsp, AstNode* thsp) { + AstNode* lhsp, AstNode* rhsp, AstNode* thsp) { // Look at emitOperator() format for term/uni/dual/triops, // and write out appropriate text. - // %n* node - // %nq emitIQW on the [node] - // %nw width in bits - // %nW width in words - // %ni iterate - // %l* lhsp - if appropriate, then second char as above - // %r* rhsp - if appropriate, then second char as above - // %t* thsp - if appropriate, then second char as above - // %k Potential line break - // %P Wide temporary name - // , Commas suppressed if the previous field is suppressed + // %n* node + // %nq emitIQW on the [node] + // %nw width in bits + // %nW width in words + // %ni iterate + // %l* lhsp - if appropriate, then second char as above + // %r* rhsp - if appropriate, then second char as above + // %t* thsp - if appropriate, then second char as above + // %k Potential line break + // %P Wide temporary name + // , Commas suppressed if the previous field is suppressed string nextComma; bool needComma = false; #define COMMA { if (!nextComma.empty()) { puts(nextComma); nextComma=""; } } putbs(""); for (string::const_iterator pos = format.begin(); pos != format.end(); ++pos) { - if (pos[0]==',') { - // Remember we need to add one, but don't do yet to avoid ",)" - if (needComma) { - if (pos[1]==' ') { nextComma=", "; } - else nextComma = ","; - needComma = false; - } - if (pos[1]==' ') { ++pos; } // Must do even if no nextComma - } - else if (pos[0]=='%') { - ++pos; - bool detail = false; - AstNode* detailp = NULL; - switch (pos[0]) { - case '%': puts("%"); break; - case 'k': putbs(""); break; - case 'n': detail = true; detailp = nodep; break; - case 'l': detail = true; detailp = lhsp; break; - case 'r': detail = true; detailp = rhsp; break; - case 't': detail = true; detailp = thsp; break; - case 'P': - if (nodep->isWide()) { - if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Op w/ no temp, perhaps missing op in V3EmitC?"); - COMMA; - puts(m_wideTempRefp->hiername()); - puts(m_wideTempRefp->varp()->name()); - m_wideTempRefp = NULL; - needComma = true; - } - break; - default: - nodep->v3fatalSrc("Unknown emitOperator format code: %"<widthMin())); - needComma = true; - break; - case 'W': - if (lhsp->isWide()) { - COMMA; - puts(cvtToStr(lhsp->widthWords())); - needComma = true; - } - break; - case 'i': - COMMA; - if (!detailp) { nodep->v3fatalSrc("emitOperator() references undef node"); } + if (pos[0]==',') { + // Remember we need to add one, but don't do yet to avoid ",)" + if (needComma) { + if (pos[1]==' ') { nextComma = ", "; } + else nextComma = ","; + needComma = false; + } + if (pos[1]==' ') { ++pos; } // Must do even if no nextComma + } + else if (pos[0]=='%') { + ++pos; + bool detail = false; + AstNode* detailp = NULL; + switch (pos[0]) { + case '%': puts("%"); break; + case 'k': putbs(""); break; + case 'n': detail = true; detailp = nodep; break; + case 'l': detail = true; detailp = lhsp; break; + case 'r': detail = true; detailp = rhsp; break; + case 't': detail = true; detailp = thsp; break; + case 'P': + if (nodep->isWide()) { + if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Op w/ no temp, perhaps missing op in V3EmitC?"); + COMMA; + puts(m_wideTempRefp->hiername()); + puts(m_wideTempRefp->varp()->name()); + m_wideTempRefp = NULL; + needComma = true; + } + break; + default: + nodep->v3fatalSrc("Unknown emitOperator format code: %"<widthMin())); + needComma = true; + break; + case 'W': + if (lhsp->isWide()) { + COMMA; + puts(cvtToStr(lhsp->widthWords())); + needComma = true; + } + break; + case 'i': + COMMA; + if (!detailp) { nodep->v3fatalSrc("emitOperator() references undef node"); } else iterateAndNextNull(detailp); - needComma = true; - break; - default: - nodep->v3fatalSrc("Unknown emitOperator format code: %[nlrt]"<v3fatalSrc("Unknown emitOperator format code: %[nlrt]"< m_argsChar; // Format of each argument to be printed - std::vector m_argsp; // Each argument to be printed - std::vector m_argsFunc; // Function before each argument to be printed + string m_format; // "%s" and text from user + std::vector m_argsChar; // Format of each argument to be printed + std::vector m_argsp; // Each argument to be printed + std::vector m_argsFunc; // Function before each argument to be printed EmitDispState() { clear(); } void clear() { - m_format = ""; - m_argsChar.clear(); - m_argsp.clear(); - m_argsFunc.clear(); + m_format = ""; + m_argsChar.clear(); + m_argsp.clear(); + m_argsFunc.clear(); } void pushFormat(const string& fmt) { m_format += fmt; } void pushFormat(char fmt) { m_format += fmt; } void pushArg(char fmtChar, AstNode* nodep, const string& func) { - m_argsChar.push_back(fmtChar); - m_argsp.push_back(nodep); m_argsFunc.push_back(func); + m_argsChar.push_back(fmtChar); + m_argsp.push_back(nodep); m_argsFunc.push_back(func); } } emitDispState; void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) { if (emitDispState.m_format == "" && VN_IS(nodep, Display)) { // not fscanf etc, as they need to return value - // NOP + // NOP } else { - // Format - bool isStmt = false; + // Format + bool isStmt = false; if (const AstFScanF* dispp = VN_CAST(nodep, FScanF)) { - isStmt = false; - puts("VL_FSCANF_IX("); + isStmt = false; + puts("VL_FSCANF_IX("); iterate(dispp->filep()); - puts(","); + puts(","); } else if (const AstSScanF* dispp = VN_CAST(nodep, SScanF)) { - isStmt = false; - checkMaxWords(dispp->fromp()); - puts("VL_SSCANF_I"); emitIQW(dispp->fromp()); puts("X("); - puts(cvtToStr(dispp->fromp()->widthMin())); - puts(","); + isStmt = false; + checkMaxWords(dispp->fromp()); + puts("VL_SSCANF_I"); emitIQW(dispp->fromp()); puts("X("); + puts(cvtToStr(dispp->fromp()->widthMin())); + puts(","); iterate(dispp->fromp()); - puts(","); + puts(","); } else if (const AstDisplay* dispp = VN_CAST(nodep, Display)) { - isStmt = true; - if (dispp->filep()) { - puts("VL_FWRITEF("); + isStmt = true; + if (dispp->filep()) { + puts("VL_FWRITEF("); iterate(dispp->filep()); - puts(","); - } else { - puts("VL_WRITEF("); - } + puts(","); + } else { + puts("VL_WRITEF("); + } } else if (const AstSFormat* dispp = VN_CAST(nodep, SFormat)) { - isStmt = true; - puts("VL_SFORMAT_X("); - puts(cvtToStr(dispp->lhsp()->widthMin())); - putbs(","); + isStmt = true; + puts("VL_SFORMAT_X("); + puts(cvtToStr(dispp->lhsp()->widthMin())); + putbs(","); iterate(dispp->lhsp()); - putbs(","); + putbs(","); } else if (const AstSFormatF* dispp = VN_CAST(nodep, SFormatF)) { - isStmt = false; - if (dispp) {} - puts("VL_SFORMATF_NX("); - } else { - nodep->v3fatalSrc("Unknown displayEmit node type"); - } - ofp()->putsQuoted(emitDispState.m_format); - // Arguments - for (unsigned i=0; i < emitDispState.m_argsp.size(); i++) { - puts(","); - char fmt = emitDispState.m_argsChar[i]; - AstNode* argp = emitDispState.m_argsp[i]; - string func = emitDispState.m_argsFunc[i]; - ofp()->indentInc(); - ofp()->putbs(""); - if (func!="") puts(func); - if (argp) { - if (isScan) puts("&("); - else if (fmt == '@') puts("&("); + isStmt = false; + if (dispp) {} + puts("VL_SFORMATF_NX("); + } else { + nodep->v3fatalSrc("Unknown displayEmit node type"); + } + ofp()->putsQuoted(emitDispState.m_format); + // Arguments + for (unsigned i=0; i < emitDispState.m_argsp.size(); i++) { + puts(","); + char fmt = emitDispState.m_argsChar[i]; + AstNode* argp = emitDispState.m_argsp[i]; + string func = emitDispState.m_argsFunc[i]; + ofp()->indentInc(); + ofp()->putbs(""); + if (func!="") puts(func); + if (argp) { + if (isScan) puts("&("); + else if (fmt == '@') puts("&("); iterate(argp); - if (isScan) puts(")"); - else if (fmt == '@') puts(")"); - } - ofp()->indentDec(); - } + if (isScan) puts(")"); + else if (fmt == '@') puts(")"); + } + ofp()->indentDec(); + } // End - puts(")"); - if (isStmt) puts(";\n"); - else puts(" "); - // Prep for next - emitDispState.clear(); + puts(")"); + if (isStmt) puts(";\n"); + else puts(" "); + // Prep for next + emitDispState.clear(); } } void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, - const string& vfmt, char fmtLetter) { + const string& vfmt, char fmtLetter) { // Print display argument, edits elistp AstNode* argp = *elistp; if (!argp) { - // expectDisplay() checks this first, so internal error if found here - dispp->v3error("Internal: Missing arguments for $display-like format"); - return; + // expectDisplay() checks this first, so internal error if found here + dispp->v3error("Internal: Missing arguments for $display-like format"); + return; } if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) { - dispp->v3error("Exceeded limit of "+cvtToStr(VL_VALUE_STRING_MAX_WIDTH)+" bits for any $display-like arguments"); + dispp->v3error("Exceeded limit of "+cvtToStr(VL_VALUE_STRING_MAX_WIDTH)+" bits for any $display-like arguments"); } if (argp && argp->widthMin()>8 && fmtLetter=='c') { - // Technically legal, but surely not what the user intended. - argp->v3warn(WIDTH,dispp->verilogKwd()<<"of %c format of > 8 bit value"); + // Technically legal, but surely not what the user intended. + argp->v3warn(WIDTH, dispp->verilogKwd()<<"of %c format of > 8 bit value"); } //string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter; string pfmt; if ((fmtLetter=='#' || fmtLetter=='d' || fmtLetter=='t') - && !isScan - && vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros - double mantissabits = argp->widthMin() - ((fmtLetter=='d')?1:0); - double maxval = pow(2.0, mantissabits); - double dchars = log10(maxval)+1.0; - if (fmtLetter=='d') dchars++; // space for sign - int nchars = int(dchars); - pfmt = string("%") + cvtToStr(nchars) + fmtLetter; + && !isScan + && vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros + double mantissabits = argp->widthMin() - ((fmtLetter=='d')?1:0); + double maxval = pow(2.0, mantissabits); + double dchars = log10(maxval)+1.0; + if (fmtLetter=='d') dchars++; // space for sign + int nchars = int(dchars); + pfmt = string("%") + cvtToStr(nchars) + fmtLetter; } else { - pfmt = string("%") + vfmt + fmtLetter; + pfmt = string("%") + vfmt + fmtLetter; } emitDispState.pushFormat(pfmt); - emitDispState.pushArg(' ',NULL,cvtToStr(argp->widthMin())); - emitDispState.pushArg(fmtLetter,argp,""); + emitDispState.pushArg(' ', NULL, cvtToStr(argp->widthMin())); + emitDispState.pushArg(fmtLetter, argp, ""); // Next parameter *elistp = (*elistp)->nextp(); } void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, - const string& vformat, AstNode* exprsp, - bool isScan) { + const string& vformat, AstNode* exprsp, + bool isScan) { AstNode* elistp = exprsp; // Convert Verilog display to C printf formats - // "%0t" becomes "%d" + // "%0t" becomes "%d" emitDispState.clear(); string vfmt; string::const_iterator pos = vformat.begin(); bool inPct = false; for (; pos != vformat.end(); ++pos) { //UINFO(1,"Parse '"<<*pos<<"' IP"<v3fatalSrc("Display with %m but no AstScopeName"); - string suffix = scopenamep->scopePrettySymName(); - if (suffix=="") emitDispState.pushFormat("%S"); - else emitDispState.pushFormat("%N"); // Add a . when needed - emitDispState.pushArg(' ',NULL, "vlSymsp->name()"); - emitDispState.pushFormat(suffix); - break; - } - case 'l': { + case 'v': displayArg(nodep, &elistp, isScan, vfmt, 'v'); break; + case 'm': { + if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName"); + string suffix = scopenamep->scopePrettySymName(); + if (suffix=="") emitDispState.pushFormat("%S"); + else emitDispState.pushFormat("%N"); // Add a . when needed + emitDispState.pushArg(' ', NULL, "vlSymsp->name()"); + emitDispState.pushFormat(suffix); + break; + } + case 'l': { // Better than not compiling - emitDispState.pushFormat("----"); - break; - } - default: - nodep->v3error("Unknown $display-like format code: %"<v3error("Unknown $display-like format code: %"<v3error("Internal: Extra arguments for $display-like format"); + // expectFormat also checks this, and should have found it first, so internal + elistp->v3error("Internal: Extra arguments for $display-like format"); } displayEmit(nodep, isScan); } @@ -1673,94 +1685,94 @@ void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep, void EmitCImp::emitVarReset(AstVar* varp) { if (varp->isIO() && m_modp->isTop() && optSystemC()) { - // System C top I/O doesn't need loading, as the lower level subinst code does it.} + // System C top I/O doesn't need loading, as the lower level subinst code does it.} } else if (varp->isParam()) { - if (!varp->valuep()) varp->v3fatalSrc("No init for a param?"); - // If a simple CONST value we initialize it using an enum - // If an ARRAYINIT we initialize it using an initial block similar to a signal - //puts("// parameter "+varp->name()+" = "+varp->valuep()->name()+"\n"); + if (!varp->valuep()) varp->v3fatalSrc("No init for a param?"); + // If a simple CONST value we initialize it using an enum + // If an ARRAYINIT we initialize it using an initial block similar to a signal + //puts("// parameter "+varp->name()+" = "+varp->valuep()->name()+"\n"); } else if (AstInitArray* initarp = VN_CAST(varp->valuep(), InitArray)) { if (AstUnpackArrayDType* arrayp = VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType)) { - if (initarp->defaultp()) { - // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block - puts("{ int __Vi=0;"); - puts(" for (; __Vi<"+cvtToStr(arrayp->elementsConst())); - puts("; ++__Vi) {\n"); + if (initarp->defaultp()) { + // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block + puts("{ int __Vi=0;"); + puts(" for (; __Vi<"+cvtToStr(arrayp->elementsConst())); + puts("; ++__Vi) {\n"); emitSetVarConstant(varp->name()+"[__Vi]", VN_CAST(initarp->defaultp(), Const)); - puts("}}\n"); - } - int pos = 0; - for (AstNode* itemp = initarp->initsp(); itemp; ++pos, itemp=itemp->nextp()) { - int index = initarp->posIndex(pos); - if (!initarp->defaultp() && index!=pos) initarp->v3fatalSrc("Not enough values in array initalizement"); + puts("}}\n"); + } + int pos = 0; + for (AstNode* itemp = initarp->initsp(); itemp; ++pos, itemp=itemp->nextp()) { + int index = initarp->posIndex(pos); + if (!initarp->defaultp() && index!=pos) initarp->v3fatalSrc("Not enough values in array initalizement"); emitSetVarConstant(varp->name()+"["+cvtToStr(index)+"]", VN_CAST(itemp, Const)); - } - } else { - varp->v3fatalSrc("InitArray under non-arrayed var"); - } + } + } else { + varp->v3fatalSrc("InitArray under non-arrayed var"); + } } else if (varp->basicp() && varp->basicp()->keyword() == AstBasicDTypeKwd::STRING) { - // Constructor deals with it + // Constructor deals with it } else { - int vects = 0; - // This isn't very robust and may need cleanup for other data types + int vects = 0; + // This isn't very robust and may need cleanup for other data types for (AstUnpackArrayDType* arrayp=VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType); arrayp; arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) { - int vecnum = vects++; - if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier."); - string ivar = string("__Vi")+cvtToStr(vecnum); - // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block - puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";"); - puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst())); - puts("; ++"+ivar+") {\n"); - } - bool zeroit = (varp->attrFileDescr() // Zero it out, so we don't core dump if never call $fopen - || (varp->basicp() && varp->basicp()->isZeroInit()) + int vecnum = vects++; + if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier."); + string ivar = string("__Vi")+cvtToStr(vecnum); + // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block + puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";"); + puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst())); + puts("; ++"+ivar+") {\n"); + } + bool zeroit = (varp->attrFileDescr() // Zero it out, so we don't core dump if never call $fopen + || (varp->basicp() && varp->basicp()->isZeroInit()) || (v3Global.opt.underlineZero() && !varp->name().empty() && varp->name()[0]=='_') - || (v3Global.opt.xInitial() == "fast" || v3Global.opt.xInitial() == "0")); - if (varp->isWide()) { - // DOCUMENT: We randomize everything. If the user wants a _var to be zero, - // there should be a initial statement. (Different from verilator2.) - if (zeroit) puts("VL_ZERO_RESET_W("); - else puts("VL_RAND_RESET_W("); - puts(cvtToStr(varp->widthMin())); - puts(","); - puts(varp->name()); - for (int v=0; vname()); - for (int v=0; visUsedClock())) { - puts(" = 0;\n"); - } else { - puts(" = VL_RAND_RESET_"); - emitIQW(varp); - puts("("); - puts(cvtToStr(varp->widthMin())); - puts(");\n"); - } - } - for (int v=0; visWide()) { + // DOCUMENT: We randomize everything. If the user wants a _var to be zero, + // there should be a initial statement. (Different from verilator2.) + if (zeroit) puts("VL_ZERO_RESET_W("); + else puts("VL_RAND_RESET_W("); + puts(cvtToStr(varp->widthMin())); + puts(","); + puts(varp->name()); + for (int v=0; vname()); + for (int v=0; visUsedClock())) { + puts(" = 0;\n"); + } else { + puts(" = VL_RAND_RESET_"); + emitIQW(varp); + puts("("); + puts(cvtToStr(varp->widthMin())); + puts(");\n"); + } + } + for (int v=0; vputsPrivate(true); - putsDecoration("// Coverage\n"); - puts("void __vlCoverInsert(uint32_t* countp, bool enable, const char* filenamep, int lineno, int column,\n"); - puts( "const char* hierp, const char* pagep, const char* commentp);\n"); + ofp()->putsPrivate(true); + putsDecoration("// Coverage\n"); + puts("void __vlCoverInsert(uint32_t* countp, bool enable, const char* filenamep, int lineno, int column,\n"); + puts( "const char* hierp, const char* pagep, const char* commentp);\n"); } } @@ -1798,7 +1810,7 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) { puts("\n"); bool first = true; if (optSystemC() && modp->isTop()) { - puts("VL_SC_CTOR_IMP("+modClassName(modp)+")"); + puts("VL_SC_CTOR_IMP("+modClassName(modp)+")"); } else { puts("VL_CTOR_IMP("+modClassName(modp)+")"); first = false; // VL_CTOR_IMP includes the first ':' @@ -1813,8 +1825,8 @@ void EmitCImp::emitCtorImp(AstNodeModule* modp) { putsDecoration("// Reset internal values\n"); if (modp->isTop()) { - if (v3Global.opt.inhibitSim()) puts("__Vm_inhibitSim = false;\n"); - puts("\n"); + if (v3Global.opt.inhibitSim()) puts("__Vm_inhibitSim = false;\n"); + puts("\n"); } putsDecoration("// Reset structure values\n"); puts("_ctor_var_reset();\n"); @@ -1860,7 +1872,7 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) { puts( "if (0 && first) {} // Prevent unused\n"); puts( "this->__VlSymsp = vlSymsp;\n"); // First, as later stuff needs it. if (v3Global.opt.coverage() ) { - puts("this->_configure_coverage(vlSymsp, first);\n"); + puts("this->_configure_coverage(vlSymsp, first);\n"); } puts("}\n"); splitSizeInc(10); @@ -1868,25 +1880,26 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) { void EmitCImp::emitCoverageImp(AstNodeModule* modp) { if (v3Global.opt.coverage() ) { - puts("\n// Coverage\n"); - // Rather than putting out VL_COVER_INSERT calls directly, we do it via this function - // This gets around gcc slowness constructing all of the template arguments. + puts("\n// Coverage\n"); + // Rather than putting out VL_COVER_INSERT calls directly, we do it via this function + // This gets around gcc slowness constructing all of the template arguments. puts("void "+modClassName(m_modp)+"::__vlCoverInsert(uint32_t* countp, bool enable," " const char* filenamep, int lineno, int column,\n"); - puts( "const char* hierp, const char* pagep, const char* commentp) {\n"); - puts( "static uint32_t fake_zero_count = 0;\n"); // static doesn't need save-restore as constant - puts( "if (!enable) countp = &fake_zero_count;\n"); // Used for second++ instantiation of identical bin - puts( "*countp = 0;\n"); - puts( "VL_COVER_INSERT(countp,"); - puts( " \"filename\",filenamep,"); - puts( " \"lineno\",lineno,"); - puts( " \"column\",column,\n"); - //puts( "\"hier\",std::string(__VlSymsp->name())+hierp,"); // Need to move hier into scopes and back out if do this - puts( "\"hier\",std::string(name())+hierp,"); - puts( " \"page\",pagep,"); - puts( " \"comment\",commentp);\n"); - puts("}\n"); - splitSizeInc(10); + puts( "const char* hierp, const char* pagep, const char* commentp) {\n"); + puts( "static uint32_t fake_zero_count = 0;\n"); // static doesn't need save-restore as constant + puts( "if (!enable) countp = &fake_zero_count;\n"); // Used for second++ instantiation of identical bin + puts( "*countp = 0;\n"); + puts( "VL_COVER_INSERT(countp,"); + puts( " \"filename\",filenamep,"); + puts( " \"lineno\",lineno,"); + puts( " \"column\",column,\n"); + // Need to move hier into scopes and back out if do this + //puts( "\"hier\",std::string(__VlSymsp->name())+hierp,"); + puts( "\"hier\",std::string(name())+hierp,"); + puts( " \"page\",pagep,"); + puts( " \"comment\",commentp);\n"); + puts("}\n"); + splitSizeInc(10); } } @@ -1904,76 +1917,82 @@ void EmitCImp::emitDestructorImp(AstNodeModule* modp) { void EmitCImp::emitSavableImp(AstNodeModule* modp) { if (v3Global.opt.savable() ) { - puts("\n// Savable\n"); - for (int de=0; de<2; ++de) { - string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; - string funcname = de ? "__Vdeserialize" : "__Vserialize"; - string writeread = de ? "read" : "write"; - string op = de ? ">>" : "<<"; + puts("\n// Savable\n"); + for (int de=0; de<2; ++de) { + string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + string funcname = de ? "__Vdeserialize" : "__Vserialize"; + string writeread = de ? "read" : "write"; + string op = de ? ">>" : "<<"; // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - puts("void "+modClassName(modp)+"::"+funcname+"("+classname+"& os) {\n"); - // Place a computed checksum to insure proper structure save/restore formatting - // OK if this hash includes some things we won't dump, since just looking for loading the wrong model - VHashSha1 hash; - for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + puts("void "+modClassName(modp)+"::"+funcname+"("+classname+"& os) {\n"); + // Place a computed checksum to insure proper structure save/restore formatting + // OK if this hash includes some things we won't dump, since + // just looking for loading the wrong model + VHashSha1 hash; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* varp = VN_CAST(nodep, Var)) { - hash.insert(varp->name()); - hash.insert(varp->dtypep()->width()); - } - } - ofp()->printf( "vluint64_t __Vcheckval = VL_ULL(0x%" VL_PRI64 "x);\n", + hash.insert(varp->name()); + hash.insert(varp->dtypep()->width()); + } + } + ofp()->printf( "vluint64_t __Vcheckval = VL_ULL(0x%" VL_PRI64 "x);\n", static_cast(hash.digestUInt64())); - if (de) { - puts("os.readAssert(__Vcheckval);\n"); - } else { - puts("os<<__Vcheckval;\n"); - } + if (de) { + puts("os.readAssert(__Vcheckval);\n"); + } else { + puts("os<<__Vcheckval;\n"); + } - // Save all members - if (v3Global.opt.inhibitSim()) puts("os"+op+"__Vm_inhibitSim;\n"); - for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + // Save all members + if (v3Global.opt.inhibitSim()) puts("os"+op+"__Vm_inhibitSim;\n"); + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* varp = VN_CAST(nodep, Var)) { - if (varp->isIO() && modp->isTop() && optSystemC()) { - // System C top I/O doesn't need loading, as the lower level subinst code does it. - } - else if (varp->isParam()) {} - else if (varp->isStatic() && varp->isConst()) {} - else { - int vects = 0; - // This isn't very robust and may need cleanup for other data types - for (AstUnpackArrayDType* arrayp=VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType); + if (varp->isIO() && modp->isTop() && optSystemC()) { + // System C top I/O doesn't need loading, as the + // lower level subinst code does it. + } + else if (varp->isParam()) {} + else if (varp->isStatic() && varp->isConst()) {} + else { + int vects = 0; + // This isn't very robust and may need cleanup for other data types + for (AstUnpackArrayDType* arrayp + = VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType); arrayp; arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) { - int vecnum = vects++; - if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier."); - string ivar = string("__Vi")+cvtToStr(vecnum); - // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block - puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";"); - puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst())); - puts("; ++"+ivar+") {\n"); - } - if (varp->basicp() && (varp->basicp()->keyword() == AstBasicDTypeKwd::STRING - || !varp->basicp()->isWide())) { - puts("os"+op+varp->name()); - for (int v=0; vname()); - for (int v=0; vname()); - for (int v=0; vmsb() < arrayp->lsb()) { + varp->v3fatalSrc("Should have swapped msb & lsb earlier."); + } + string ivar = string("__Vi")+cvtToStr(vecnum); + // MSVC++ pre V7 doesn't support 'for (int ...)', + // so declare in sep block + puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";"); + puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst())); + puts("; ++"+ivar+") {\n"); + } + if (varp->basicp() && (varp->basicp()->keyword() == AstBasicDTypeKwd::STRING + || !varp->basicp()->isWide())) { + puts("os"+op+varp->name()); + for (int v=0; vname()); + for (int v=0; vname()); + for (int v=0; visTop()) { // Save the children - puts( "__VlSymsp->"+funcname+"(os);\n"); - } - puts("}\n"); - } + if (modp->isTop()) { // Save the children + puts( "__VlSymsp->"+funcname+"(os);\n"); + } + puts("}\n"); + } } } @@ -1988,37 +2007,37 @@ void EmitCImp::emitTextSection(AstType type) { int last_line = -999; for (AstNode* nodep = m_modp->stmtsp(); nodep != NULL; nodep = nodep->nextp()) { if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) { - if (nodep->type() == type) { - if (last_line != nodep->fileline()->lineno()) { - if (last_line < 0) { - puts("\n//*** Below code from `systemc in Verilog file\n"); - } - ofp()->putsNoTracking("//#line "+cvtToStr(nodep->fileline()->lineno()) - +" "); - ofp()->putsQuoted(nodep->fileline()->filename()); - ofp()->putsNoTracking("\n"); - last_line = nodep->fileline()->lineno(); - } - ofp()->putsNoTracking(textp->text()); - last_line++; - } - } + if (nodep->type() == type) { + if (last_line != nodep->fileline()->lineno()) { + if (last_line < 0) { + puts("\n//*** Below code from `systemc in Verilog file\n"); + } + ofp()->putsNoTracking("//#line "+cvtToStr(nodep->fileline()->lineno()) + +" "); + ofp()->putsQuoted(nodep->fileline()->filename()); + ofp()->putsNoTracking("\n"); + last_line = nodep->fileline()->lineno(); + } + ofp()->putsNoTracking(textp->text()); + last_line++; + } + } } if (last_line > 0) { - puts("//*** Above code from `systemc in Verilog file\n\n"); + puts("//*** Above code from `systemc in Verilog file\n\n"); } } void EmitCImp::emitCellCtors(AstNodeModule* modp) { if (modp->isTop()) { - // Must be before other constructors, as __vlCoverInsert calls it - puts(EmitCBaseVisitor::symClassVar()+" = __VlSymsp = new "+symClassName()+"(this, name());\n"); - puts(EmitCBaseVisitor::symTopAssign()+"\n"); + // Must be before other constructors, as __vlCoverInsert calls it + puts(EmitCBaseVisitor::symClassVar()+" = __VlSymsp = new "+symClassName()+"(this, name());\n"); + puts(EmitCBaseVisitor::symTopAssign()+"\n"); } - for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCell* cellp=VN_CAST(nodep, Cell)) { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCell* cellp = VN_CAST(nodep, Cell)) { puts("VL_CELL("+cellp->name()+", "+modClassName(cellp->modp())+");\n"); - } + } } } @@ -2026,33 +2045,36 @@ void EmitCImp::emitSensitives() { // Create sensitivity list for when to evaluate the model. // If C++ code, the user must call this routine themself. if (m_modp->isTop() && optSystemC()) { - putsDecoration("// Sensitivities on all clocks and combo inputs\n"); - puts("SC_METHOD(eval);\n"); - for (AstNode* nodep=m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { + putsDecoration("// Sensitivities on all clocks and combo inputs\n"); + puts("SC_METHOD(eval);\n"); + for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* varp = VN_CAST(nodep, Var)) { if (varp->isNonOutput() && (varp->isScSensitive() || varp->isUsedClock())) { int vects = 0; // This isn't very robust and may need cleanup for other data types - for (AstUnpackArrayDType* arrayp=VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType); + for (AstUnpackArrayDType* arrayp + = VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType); arrayp; arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) { - int vecnum = vects++; - if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier."); - string ivar = string("__Vi")+cvtToStr(vecnum); - // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block - puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(arrayp->lsb())+";"); - puts(" for (; "+ivar+"<="+cvtToStr(arrayp->msb())); - puts("; ++"+ivar+") {\n"); - } - puts("sensitive << "+varp->name()); - for (int v=0; vmsb() < arrayp->lsb()) { + varp->v3fatalSrc("Should have swapped msb & lsb earlier."); + } + string ivar = string("__Vi")+cvtToStr(vecnum); + // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block + puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(arrayp->lsb())+";"); + puts(" for (; "+ivar+"<="+cvtToStr(arrayp->msb())); + puts("; ++"+ivar+") {\n"); + } + puts("sensitive << "+varp->name()); + for (int v=0; v__Vm_didInit)) _eval_initial_loop(vlSymsp);\n"); if (v3Global.opt.inhibitSim()) { - puts("if (VL_UNLIKELY(__Vm_inhibitSim)) return;\n"); + puts("if (VL_UNLIKELY(__Vm_inhibitSim)) return;\n"); } if (v3Global.opt.threads() == 1) { - uint32_t mtaskId = 0; - putsDecoration("// MTask "+cvtToStr(mtaskId)+" start\n"); + uint32_t mtaskId = 0; + putsDecoration("// MTask "+cvtToStr(mtaskId)+" start\n"); puts("VL_DEBUG_IF(VL_DBG_MSGF(\"MTask"+cvtToStr(mtaskId)+" starting\\n\"););\n"); - puts("Verilated::mtaskId("+cvtToStr(mtaskId)+");\n"); + puts("Verilated::mtaskId("+cvtToStr(mtaskId)+");\n"); } if (v3Global.opt.mtasks() @@ -2143,10 +2165,10 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) { + (v3Global.opt.trace() ? "vlSymsp->__Vm_activity = true;\n" : "") + "_eval(vlSymsp);"), false); if (v3Global.opt.threads() == 1) { - puts("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);\n"); + puts("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);\n"); } if (v3Global.opt.threads()) { - puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n"); + puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n"); } puts("}\n"); splitSizeInc(10); @@ -2156,7 +2178,7 @@ void EmitCImp::emitWrapEval(AstNodeModule* modp) { puts("vlSymsp->__Vm_didInit = true;\n"); puts("_eval_initial(vlSymsp);\n"); if (v3Global.opt.trace()) { - puts("vlSymsp->__Vm_activity = true;\n"); + puts("vlSymsp->__Vm_activity = true;\n"); } emitSettleLoop((string("_eval_settle(vlSymsp);\n") +"_eval(vlSymsp);"), true); @@ -2195,18 +2217,18 @@ void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& pref case EVL_FUNC_ALL: doit = true; break; default: v3fatalSrc("Bad Case"); } - if (varp->isStatic() ? !isstatic : isstatic) doit=false; + if (varp->isStatic() ? !isstatic : isstatic) doit = false; if (doit) { int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes(); int sortbytes = 9; if (varp->isUsedClock() && varp->widthMin()==1) sortbytes = 0; - else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) sortbytes=8; - else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes=7; - else if (varp->isScBv() || varp->isScBigUint()) sortbytes=6; - else if (sigbytes==8) sortbytes=5; - else if (sigbytes==4) sortbytes=4; - else if (sigbytes==2) sortbytes=2; - else if (sigbytes==1) sortbytes=1; + else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) sortbytes = 8; + else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes = 7; + else if (varp->isScBv() || varp->isScBigUint()) sortbytes = 6; + else if (sigbytes==8) sortbytes = 5; + else if (sigbytes==4) sortbytes = 4; + else if (sigbytes==2) sortbytes = 2; + else if (sigbytes==1) sortbytes = 1; bool anonOk = (v3Global.opt.compLimitMembers() != 0 // Enabled && !varp->isStatic() @@ -2343,7 +2365,7 @@ void EmitCStmts::emitSortedVarList(const VarVec& anons, struct CmpName { inline bool operator() (const AstNode* lhsp, const AstNode* rhsp) const { - return lhsp->name() < rhsp->name(); + return lhsp->name() < rhsp->name(); } }; @@ -2351,28 +2373,28 @@ void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) { typedef std::vector FuncVec; FuncVec funcsp; - for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstCFunc* funcp = VN_CAST(nodep, CFunc)) { - if (!funcp->skipDecl()) { - funcsp.push_back(funcp); - } - } + if (!funcp->skipDecl()) { + funcsp.push_back(funcp); + } + } } stable_sort(funcsp.begin(), funcsp.end(), CmpName()); for (FuncVec::iterator it = funcsp.begin(); it != funcsp.end(); ++it) { const AstCFunc* funcp = *it; - if (!funcp->dpiImport()) { // DPI is prototyped in __Dpi.h - ofp()->putsPrivate(funcp->declPrivate()); + if (!funcp->dpiImport()) { // DPI is prototyped in __Dpi.h + ofp()->putsPrivate(funcp->declPrivate()); if (funcp->ifdef()!="") puts("#ifdef "+funcp->ifdef()+"\n"); if (funcp->isStatic().trueU()) puts("static "); puts(funcp->rtnTypeVoid()); puts(" "); puts(funcp->name()); puts("("+cFuncArgs(funcp)+")"); if (funcp->slow()) puts(" VL_ATTR_COLD"); puts(";\n"); - if (funcp->ifdef()!="") puts("#endif // "+funcp->ifdef()+"\n"); - } + if (funcp->ifdef()!="") puts("#endif // "+funcp->ifdef()+"\n"); + } } if (modp->isTop() && v3Global.opt.mtasks()) { @@ -2437,70 +2459,70 @@ void EmitCImp::emitInt(AstNodeModule* modp) { ofp()->putsIntTopInclude(); if (v3Global.needHeavy()) { - puts("#include \"verilated_heavy.h\"\n"); + puts("#include \"verilated_heavy.h\"\n"); } else { - puts("#include \"verilated.h\"\n"); + puts("#include \"verilated.h\"\n"); } if (v3Global.opt.mtasks()) { puts("#include \"verilated_threads.h\"\n"); } if (v3Global.opt.savable()) { - puts("#include \"verilated_save.h\"\n"); + puts("#include \"verilated_save.h\"\n"); } if (v3Global.opt.coverage()) { - puts("#include \"verilated_cov.h\"\n"); - if (v3Global.opt.savable()) v3error("--coverage and --savable not supported together"); + puts("#include \"verilated_cov.h\"\n"); + if (v3Global.opt.savable()) v3error("--coverage and --savable not supported together"); } - if (v3Global.needHInlines()) { // Set by V3EmitCInlines; should have been called before us - puts("#include \""+topClassName()+"__Inlines.h\"\n"); + if (v3Global.needHInlines()) { // Set by V3EmitCInlines; should have been called before us + puts("#include \""+topClassName()+"__Inlines.h\"\n"); } if (v3Global.dpi()) { - // do this before including our main .h file so that any references to - // types defined in svdpi.h are available - puts("#include \""+ topClassName() +"__Dpi.h\"\n"); + // do this before including our main .h file so that any references to + // types defined in svdpi.h are available + puts("#include \""+ topClassName() +"__Dpi.h\"\n"); } puts("\n"); // Declare foreign instances up front to make C++ happy puts("class "+symClassName()+";\n"); vl_unordered_set didClassName; - for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCell* cellp=VN_CAST(nodep, Cell)) { - string className = modClassName(cellp->modp()); - if (didClassName.find(className)==didClassName.end()) { - puts("class "+className+";\n"); - didClassName.insert(className); - } - } + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCell* cellp = VN_CAST(nodep, Cell)) { + string className = modClassName(cellp->modp()); + if (didClassName.find(className)==didClassName.end()) { + puts("class "+className+";\n"); + didClassName.insert(className); + } + } } if (v3Global.opt.trace()) { - puts("class "+v3Global.opt.traceClassBase()+";\n"); + puts("class "+v3Global.opt.traceClassBase()+";\n"); } puts("\n//----------\n\n"); emitTextSection(AstType::atScHdr); if (optSystemC() && modp->isTop()) { - puts("SC_MODULE("+modClassName(modp)+") {\n"); + puts("SC_MODULE("+modClassName(modp)+") {\n"); } else { - puts("VL_MODULE("+modClassName(modp)+") {\n"); + puts("VL_MODULE("+modClassName(modp)+") {\n"); } ofp()->resetPrivate(); ofp()->putsPrivate(false); // public: { // Instantiated cells - bool did = false; - for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (AstCell* cellp=VN_CAST(nodep, Cell)) { - if (!did) { - did = true; - putsDecoration("// CELLS\n"); - if (modp->isTop()) puts("// Public to allow access to /*verilator_public*/ items;\n"); - if (modp->isTop()) puts("// otherwise the application code can consider these internals.\n"); - } - ofp()->putsCellDecl(modClassName(cellp->modp()), cellp->name()); - } - } + bool did = false; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstCell* cellp = VN_CAST(nodep, Cell)) { + if (!did) { + did = true; + putsDecoration("// CELLS\n"); + if (modp->isTop()) puts("// Public to allow access to /*verilator_public*/ items;\n"); + if (modp->isTop()) puts("// otherwise the application code can consider these internals.\n"); + } + ofp()->putsCellDecl(modClassName(cellp->modp()), cellp->name()); + } + } } emitTypedefs(modp->stmtsp()); @@ -2524,41 +2546,42 @@ void EmitCImp::emitInt(AstNodeModule* modp) { puts(symClassName()+"* __VlSymsp; // Symbol table\n"); ofp()->putsPrivate(false); // public: if (modp->isTop()) { - if (v3Global.opt.inhibitSim()) { - puts("bool __Vm_inhibitSim; ///< Set true to disable evaluation of module\n"); - } + if (v3Global.opt.inhibitSim()) { + puts("bool __Vm_inhibitSim; ///< Set true to disable evaluation of module\n"); + } } if (modp->isTop() && v3Global.opt.mtasks()) { emitMTaskState(); } - emitCoverageDecl(modp); // may flip public/private + emitCoverageDecl(modp); // may flip public/private puts("\n// PARAMETERS\n"); if (modp->isTop()) puts("// Parameters marked /*verilator public*/ for use by application code\n"); ofp()->putsPrivate(false); // public: emitVarList(modp->stmtsp(), EVL_CLASS_PAR, ""); // Only those that are non-CONST - for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* varp = VN_CAST(nodep, Var)) { - if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { - if (!varp->valuep()) nodep->v3fatalSrc("No init for a param?"); - // These should be static const values, however microsloth VC++ doesn't - // support them. They also cause problems with GDB under GCC2.95. - if (varp->isWide()) { // Unsupported for output - putsDecoration("// enum WData "+varp->name()+" //wide"); + if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { + if (!varp->valuep()) nodep->v3fatalSrc("No init for a param?"); + // These should be static const values, however microsloth VC++ doesn't + // support them. They also cause problems with GDB under GCC2.95. + if (varp->isWide()) { // Unsupported for output + putsDecoration("// enum WData "+varp->name()+" //wide"); } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output - //putsDecoration("// enum ..... "+varp->name()+" //not simple value, see variable above instead"); + //putsDecoration("// enum ..... "+varp->name() + // +"not simple value, see variable above instead"); } else if (VN_IS(varp->dtypep(), BasicDType) && VN_CAST(varp->dtypep(), BasicDType)->isOpaque()) { // Can't put out e.g. doubles - } else { - puts("enum "); - puts(varp->isQuad()?"_QData":"_IData"); - puts(""+varp->name()+" { "+varp->name()+" = "); + } else { + puts("enum "); + puts(varp->isQuad()?"_QData":"_IData"); + puts(""+varp->name()+" { "+varp->name()+" = "); iterateAndNextNull(varp->valuep()); - puts("};"); - } - puts("\n"); - } - } + puts("};"); + } + puts("\n"); + } + } } puts("\n// CONSTRUCTORS\n"); @@ -2569,49 +2592,49 @@ void EmitCImp::emitInt(AstNodeModule* modp) { ofp()->putsPrivate(false); // public: if (optSystemC() && modp->isTop()) { - puts("SC_CTOR("+modClassName(modp)+");\n"); - puts("virtual ~"+modClassName(modp)+"();\n"); + puts("SC_CTOR("+modClassName(modp)+");\n"); + puts("virtual ~"+modClassName(modp)+"();\n"); } else if (optSystemC()) { - puts("VL_CTOR("+modClassName(modp)+");\n"); - puts("~"+modClassName(modp)+"();\n"); + puts("VL_CTOR("+modClassName(modp)+");\n"); + puts("~"+modClassName(modp)+"();\n"); } else { - if (modp->isTop()) { - puts("/// Construct the model; called by application code\n"); - puts("/// The special name "" may be used to make a wrapper with a\n"); - puts("/// single model invisible with respect to DPI scope names.\n"); - } - puts(modClassName(modp)+"(const char* name=\"TOP\");\n"); - if (modp->isTop()) puts("/// Destroy the model; called (often implicitly) by application code\n"); - puts("~"+modClassName(modp)+"();\n"); + if (modp->isTop()) { + puts("/// Construct the model; called by application code\n"); + puts("/// The special name "" may be used to make a wrapper with a\n"); + puts("/// single model invisible with respect to DPI scope names.\n"); + } + puts(modClassName(modp)+"(const char* name=\"TOP\");\n"); + if (modp->isTop()) puts("/// Destroy the model; called (often implicitly) by application code\n"); + puts("~"+modClassName(modp)+"();\n"); } if (v3Global.opt.trace()) { - if (modp->isTop()) puts("/// Trace signals in the model; called by application code\n"); + if (modp->isTop()) puts("/// Trace signals in the model; called by application code\n"); puts("void trace("+v3Global.opt.traceClassBase()+"C* tfp, int levels, int options=0);\n"); - if (modp->isTop() && optSystemC()) { - puts("/// SC tracing; avoid overloaded virtual function lint warning\n"); + if (modp->isTop() && optSystemC()) { + puts("/// SC tracing; avoid overloaded virtual function lint warning\n"); puts("virtual void trace(sc_trace_file* tfp) const { ::sc_core::sc_module::trace(tfp); }\n"); - } + } } emitTextSection(AstType::atScInt); puts("\n// API METHODS\n"); if (modp->isTop()) { - if (optSystemC()) ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls. - else puts("/// Evaluate the model. Application must call when inputs change.\n"); - puts("void eval();\n"); - ofp()->putsPrivate(false); // public: - if (!optSystemC()) puts("/// Simulation complete, run final blocks. Application must call on completion.\n"); - puts("void final();\n"); - if (v3Global.opt.inhibitSim()) { - puts("void inhibitSim(bool flag) { __Vm_inhibitSim=flag; } ///< Set true to disable evaluation of module\n"); - } + if (optSystemC()) ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls. + else puts("/// Evaluate the model. Application must call when inputs change.\n"); + puts("void eval();\n"); + ofp()->putsPrivate(false); // public: + if (!optSystemC()) puts("/// Simulation complete, run final blocks. Application must call on completion.\n"); + puts("void final();\n"); + if (v3Global.opt.inhibitSim()) { + puts("void inhibitSim(bool flag) { __Vm_inhibitSim=flag; } ///< Set true to disable evaluation of module\n"); + } } puts("\n// INTERNAL METHODS\n"); if (modp->isTop()) { - ofp()->putsPrivate(true); // private: - puts("static void _eval_initial_loop("+EmitCBaseVisitor::symClassVar()+");\n"); + ofp()->putsPrivate(true); // private: + puts("static void _eval_initial_loop("+EmitCBaseVisitor::symClassVar()+");\n"); } ofp()->putsPrivate(false); // public: @@ -2620,16 +2643,19 @@ void EmitCImp::emitInt(AstNodeModule* modp) { emitIntFuncDecls(modp); if (v3Global.opt.trace()) { - ofp()->putsPrivate(false); // public: - puts("static void traceInit("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n"); - puts("static void traceFull("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n"); - puts("static void traceChg("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n"); + ofp()->putsPrivate(false); // public: + puts("static void traceInit("+v3Global.opt.traceClassBase() + +"* vcdp, void* userthis, uint32_t code);\n"); + puts("static void traceFull("+v3Global.opt.traceClassBase() + +"* vcdp, void* userthis, uint32_t code);\n"); + puts("static void traceChg("+v3Global.opt.traceClassBase() + +"* vcdp, void* userthis, uint32_t code);\n"); } if (v3Global.opt.savable()) { - ofp()->putsPrivate(false); // public: - puts("void __Vserialize(VerilatedSerialize& os);\n"); - puts("void __Vdeserialize(VerilatedDeserialize& os);\n"); - puts("\n"); + ofp()->putsPrivate(false); // public: + puts("void __Vserialize(VerilatedSerialize& os);\n"); + puts("void __Vdeserialize(VerilatedDeserialize& os);\n"); + puts("\n"); } puts("} VL_ATTR_ALIGNED(128);\n"); @@ -2637,11 +2663,13 @@ void EmitCImp::emitInt(AstNodeModule* modp) { // Save/restore if (v3Global.opt.savable() && modp->isTop()) { - puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "+modClassName(modp)+"& rhs) {\n" - "Verilated::quiesce(); rhs.__Vserialize(os); return os; }\n"); - puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "+modClassName(modp)+"& rhs) {\n" - "Verilated::quiesce(); rhs.__Vdeserialize(os); return os; }\n"); - puts("\n"); + puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, " + +modClassName(modp)+"& rhs) {\n" + "Verilated::quiesce(); rhs.__Vserialize(os); return os; }\n"); + puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, " + +modClassName(modp)+"& rhs) {\n" + "Verilated::quiesce(); rhs.__Vdeserialize(os); return os; }\n"); + puts("\n"); } // finish up h-file @@ -2655,40 +2683,40 @@ void EmitCImp::emitImp(AstNodeModule* modp) { puts("#include \""+symClassName()+".h\"\n"); if (v3Global.dpi()) { - puts("\n"); - puts("#include \"verilated_dpi.h\"\n"); + puts("\n"); + puts("#include \"verilated_dpi.h\"\n"); } puts("\n"); emitTextSection(AstType::atScImpHdr); if (m_slow && splitFilenum()==0) { - puts("\n//--------------------\n"); - puts("// STATIC VARIABLES\n\n"); + puts("\n//--------------------\n"); + puts("// STATIC VARIABLES\n\n"); emitVarList(modp->stmtsp(), EVL_CLASS_ALL, modClassName(modp)); } if (m_fast && splitFilenum()==0) { - emitTextSection(AstType::atScImp); - emitStaticDecl(modp); + emitTextSection(AstType::atScImp); + emitStaticDecl(modp); } if (m_slow && splitFilenum()==0) { - puts("\n//--------------------\n"); - emitCtorImp(modp); - emitConfigureImp(modp); - emitDestructorImp(modp); - emitSavableImp(modp); - emitCoverageImp(modp); + puts("\n//--------------------\n"); + emitCtorImp(modp); + emitConfigureImp(modp); + emitDestructorImp(modp); + emitSavableImp(modp); + emitCoverageImp(modp); } if (m_fast && splitFilenum()==0) { - if (modp->isTop()) { - emitStaticDecl(modp); - puts("\n//--------------------\n"); - puts("\n"); - emitWrapEval(modp); - } + if (modp->isTop()) { + emitStaticDecl(modp); + puts("\n//--------------------\n"); + puts("\n"); + emitWrapEval(modp); + } } // Blocks @@ -2701,7 +2729,7 @@ void EmitCImp::emitImp(AstNodeModule* modp) { void EmitCImp::maybeSplit(AstNodeModule* modp) { if (splitNeeded()) { // Close old file - delete m_ofp; m_ofp=NULL; + delete m_ofp; m_ofp = NULL; // Open a new file m_ofp = newOutCFile(modp, !m_fast, true/*source*/, splitFilenumInc()); emitImp(modp); @@ -2716,23 +2744,23 @@ void EmitCImp::main(AstNodeModule* modp, bool slow, bool fast) { m_fast = fast; if (debug()>=5) { - UINFO(0," Emitting "<stmtsp(); nodep; nodep = nodep->nextp()) { + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) { maybeSplit(modp); - mainDoFunc(funcp); - } + mainDoFunc(funcp); + } } if (fast && modp->isTop() && v3Global.opt.mtasks()) { @@ -2753,7 +2781,7 @@ void EmitCImp::main(AstNodeModule* modp, bool slow, bool fast) { } } } - delete m_ofp; m_ofp=NULL; + delete m_ofp; m_ofp = NULL; } //###################################################################### @@ -2766,8 +2794,8 @@ class EmitCTrace : EmitCStmts { AstUser1InUse m_inuser1; // MEMBERS - AstCFunc* m_funcp; // Function we're in now - bool m_slow; // Making slow file + AstCFunc* m_funcp; // Function we're in now + bool m_slow; // Making slow file int m_enumNum; // Enumeration number (whole netlist) // METHODS @@ -2778,117 +2806,117 @@ class EmitCTrace : EmitCStmts { filename += (m_slow ? "__Slow":""); filename += ".cpp"; - AstCFile* cfilep = newCFile(filename, m_slow, true/*source*/); - cfilep->support(true); + AstCFile* cfilep = newCFile(filename, m_slow, true/*source*/); + cfilep->support(true); - if (m_ofp) v3fatalSrc("Previous file not closed"); + if (m_ofp) v3fatalSrc("Previous file not closed"); m_ofp = new V3OutCFile(filename); - m_ofp->putsHeader(); - m_ofp->puts("// DESCR" "IPTION: Verilator output: Tracing implementation internals\n"); + m_ofp->putsHeader(); + m_ofp->puts("// DESCR" "IPTION: Verilator output: Tracing implementation internals\n"); - emitTraceHeader(); + emitTraceHeader(); } void emitTraceHeader() { - // Includes + // Includes puts("#include \""+v3Global.opt.traceSourceName()+"_c.h\"\n"); - puts("#include \""+ symClassName() +".h\"\n"); - puts("\n"); + puts("#include \""+ symClassName() +".h\"\n"); + puts("\n"); } void emitTraceSlow() { - puts("\n//======================\n\n"); + puts("\n//======================\n\n"); puts("void "+topClassName()+"::trace("); puts(v3Global.opt.traceClassBase()+"C* tfp, int, int) {\n"); puts( "tfp->spTrace()->addCallback(" - "&"+topClassName()+"::traceInit" - +", &"+topClassName()+"::traceFull" - +", &"+topClassName()+"::traceChg, this);\n"); - puts("}\n"); - splitSizeInc(10); + "&"+topClassName()+"::traceInit" + +", &"+topClassName()+"::traceFull" + +", &"+topClassName()+"::traceChg, this);\n"); + puts("}\n"); + splitSizeInc(10); - puts("void "+topClassName()+"::traceInit(" - +v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n"); - putsDecoration("// Callback from vcd->open()\n"); - puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); - puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); + puts("void "+topClassName()+"::traceInit(" + +v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n"); + putsDecoration("// Callback from vcd->open()\n"); + puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); + puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); puts("if (!Verilated::calcUnusedSigs()) {\n"); puts( "VL_FATAL_MT(__FILE__,__LINE__,__FILE__,\"Turning on wave traces requires Verilated::traceEverOn(true) call before time 0.\");\n"); puts("}\n"); - puts("vcdp->scopeEscape(' ');\n"); + puts("vcdp->scopeEscape(' ');\n"); puts("t->traceInitThis(vlSymsp, vcdp, code);\n"); - puts("vcdp->scopeEscape('.');\n"); // Restore so later traced files won't break - puts("}\n"); - splitSizeInc(10); + puts("vcdp->scopeEscape('.');\n"); // Restore so later traced files won't break + puts("}\n"); + splitSizeInc(10); - puts("void "+topClassName()+"::traceFull(" - +v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n"); - putsDecoration("// Callback from vcd->dump()\n"); - puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); - puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); + puts("void "+topClassName()+"::traceFull(" + +v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n"); + putsDecoration("// Callback from vcd->dump()\n"); + puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); + puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); puts("t->traceFullThis(vlSymsp, vcdp, code);\n"); - puts("}\n"); - splitSizeInc(10); + puts("}\n"); + splitSizeInc(10); - puts("\n//======================\n\n"); + puts("\n//======================\n\n"); } void emitTraceFast() { - puts("\n//======================\n\n"); + puts("\n//======================\n\n"); - puts("void "+topClassName()+"::traceChg(" - +v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n"); - putsDecoration("// Callback from vcd->dump()\n"); - puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); - puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); - puts("if (vlSymsp->getClearActivity()) {\n"); + puts("void "+topClassName()+"::traceChg(" + +v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n"); + putsDecoration("// Callback from vcd->dump()\n"); + puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n"); + puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n"); + puts("if (vlSymsp->getClearActivity()) {\n"); puts("t->traceChgThis(vlSymsp, vcdp, code);\n"); - puts("}\n"); - puts("}\n"); - splitSizeInc(10); + puts("}\n"); + puts("}\n"); + splitSizeInc(10); - puts("\n//======================\n\n"); + puts("\n//======================\n\n"); } bool emitTraceIsScBv(AstTraceInc* nodep) { const AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScBv(); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScBv(); } bool emitTraceIsScBigUint(AstTraceInc* nodep) { const AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScBigUint(); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScBigUint(); } bool emitTraceIsScUint(AstTraceInc* nodep) { const AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef); - if (!varrefp) return false; - AstVar* varp = varrefp->varp(); - return varp->isSc() && varp->isScUint(); + if (!varrefp) return false; + AstVar* varp = varrefp->varp(); + return varp->isSc() && varp->isScUint(); } void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) { - if (nodep->dtypep()->basicp()->isDouble()) { - puts("vcdp->declDouble"); - } else if (nodep->isWide()) { - puts("vcdp->declArray"); - } else if (nodep->isQuad()) { + if (nodep->dtypep()->basicp()->isDouble()) { + puts("vcdp->declDouble"); + } else if (nodep->isWide()) { + puts("vcdp->declArray"); + } else if (nodep->isQuad()) { puts("vcdp->declQuad"); - } else if (nodep->bitRange().ranged()) { + } else if (nodep->bitRange().ranged()) { puts("vcdp->declBus"); - } else { + } else { puts("vcdp->declBit"); - } + } - puts("(c+"+cvtToStr(nodep->code())); - if (nodep->arrayRange().ranged()) puts("+i*"+cvtToStr(nodep->widthWords())); - puts(","); - putsQuoted(nodep->showname()); + puts("(c+"+cvtToStr(nodep->code())); + if (nodep->arrayRange().ranged()) puts("+i*"+cvtToStr(nodep->widthWords())); + puts(","); + putsQuoted(nodep->showname()); // Direction if (v3Global.opt.traceFormat().fstFlavor()) { puts(","+cvtToStr(enumNum)); @@ -2943,16 +2971,16 @@ class EmitCTrace : EmitCStmts { puts(","+fstvt); } // Range - if (nodep->arrayRange().ranged()) { - puts(",(i+"+cvtToStr(nodep->arrayRange().lo())+")"); - } else { - puts(",-1"); - } - if (!nodep->dtypep()->basicp()->isDouble() // When float/double no longer have widths this can go - && nodep->bitRange().ranged()) { - puts(","+cvtToStr(nodep->bitRange().left())+","+cvtToStr(nodep->bitRange().right())); - } - puts(");"); + if (nodep->arrayRange().ranged()) { + puts(",(i+"+cvtToStr(nodep->arrayRange().lo())+")"); + } else { + puts(",-1"); + } + if (!nodep->dtypep()->basicp()->isDouble() // When float/double no longer have widths this can go + && nodep->bitRange().ranged()) { + puts(","+cvtToStr(nodep->bitRange().left())+","+cvtToStr(nodep->bitRange().right())); + } + puts(");"); } int emitTraceDeclDType(AstNodeDType* nodep) { @@ -2970,7 +2998,7 @@ class EmitCTrace : EmitCStmts { puts("const char* __VenumItemNames[]\n"); puts("= {"); for (AstEnumItem* itemp = enump->itemsp(); itemp; - itemp=VN_CAST(itemp->nextp(), EnumItem)) { + itemp = VN_CAST(itemp->nextp(), EnumItem)) { if (++nvals > 1) puts(", "); putbs("\""+itemp->prettyName()+"\""); } @@ -2979,7 +3007,7 @@ class EmitCTrace : EmitCStmts { puts("const char* __VenumItemValues[]\n"); puts("= {"); for (AstEnumItem* itemp = enump->itemsp(); itemp; - itemp=VN_CAST(itemp->nextp(), EnumItem)) { + itemp = VN_CAST(itemp->nextp(), EnumItem)) { AstConst* constp = VN_CAST(itemp->valuep(), Const); if (++nvals > 1) puts(", "); putbs("\""+constp->num().displayed(nodep, "%0b")+"\""); @@ -3000,16 +3028,16 @@ class EmitCTrace : EmitCStmts { void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) { iterateAndNextNull(nodep->precondsp()); - string full = ((m_funcp->funcType() == AstCFuncType::TRACE_FULL - || m_funcp->funcType() == AstCFuncType::TRACE_FULL_SUB) - ? "full":"chg"); + string full = ((m_funcp->funcType() == AstCFuncType::TRACE_FULL + || m_funcp->funcType() == AstCFuncType::TRACE_FULL_SUB) + ? "full":"chg"); bool emitWidth = false; - if (nodep->dtypep()->basicp()->isDouble()) { - puts("vcdp->"+full+"Double"); - } else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) { - puts("vcdp->"+full+"Array"); + if (nodep->dtypep()->basicp()->isDouble()) { + puts("vcdp->"+full+"Double"); + } else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) { + puts("vcdp->"+full+"Array"); emitWidth = true; - } else if (nodep->isQuad()) { + } else if (nodep->isQuad()) { puts("vcdp->"+full+"Quad"); emitWidth = true; } else if (nodep->declp()->bitRange().ranged() @@ -3017,119 +3045,119 @@ class EmitCTrace : EmitCStmts { && nodep->declp()->bitRange().elements() != 1) { puts("vcdp->"+full+"Bus"); emitWidth = true; - } else { + } else { puts("vcdp->"+full+"Bit"); - } - puts("(c+"+cvtToStr(nodep->declp()->code() - + ((arrayindex<0) ? 0 : (arrayindex*nodep->declp()->widthWords())))); - puts(","); - emitTraceValue(nodep, arrayindex); + } + puts("(c+"+cvtToStr(nodep->declp()->code() + + ((arrayindex<0) ? 0 : (arrayindex*nodep->declp()->widthWords())))); + puts(","); + emitTraceValue(nodep, arrayindex); if (emitWidth) { - puts(","+cvtToStr(nodep->declp()->widthMin())); - } - puts(");\n"); + puts(","+cvtToStr(nodep->declp()->widthMin())); + } + puts(");\n"); } void emitTraceValue(AstTraceInc* nodep, int arrayindex) { if (VN_IS(nodep->valuep(), VarRef)) { AstVarRef* varrefp = VN_CAST(nodep->valuep(), VarRef); - AstVar* varp = varrefp->varp(); - puts("("); - if (emitTraceIsScBigUint(nodep)) puts("(vluint32_t*)"); - else if (emitTraceIsScBv(nodep)) puts("VL_SC_BV_DATAP("); + AstVar* varp = varrefp->varp(); + puts("("); + if (emitTraceIsScBigUint(nodep)) puts("(vluint32_t*)"); + else if (emitTraceIsScBv(nodep)) puts("VL_SC_BV_DATAP("); iterate(varrefp); // Put var name out - // Tracing only supports 1D arrays - if (nodep->declp()->arrayRange().ranged()) { - if (arrayindex==-2) puts("[i]"); - else if (arrayindex==-1) puts("[0]"); - else puts("["+cvtToStr(arrayindex)+"]"); - } - if (varp->isSc()) puts(".read()"); - if (emitTraceIsScUint(nodep)) puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()"); - else if (emitTraceIsScBigUint(nodep)) puts(".get_raw()"); - else if (emitTraceIsScBv(nodep)) puts(")"); - puts(")"); - } else { - puts("("); + // Tracing only supports 1D arrays + if (nodep->declp()->arrayRange().ranged()) { + if (arrayindex==-2) puts("[i]"); + else if (arrayindex==-1) puts("[0]"); + else puts("["+cvtToStr(arrayindex)+"]"); + } + if (varp->isSc()) puts(".read()"); + if (emitTraceIsScUint(nodep)) puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()"); + else if (emitTraceIsScBigUint(nodep)) puts(".get_raw()"); + else if (emitTraceIsScBv(nodep)) puts(")"); + puts(")"); + } else { + puts("("); iterate(nodep->valuep()); - puts(")"); - } + puts(")"); + } } // VISITORS using EmitCStmts::visit; // Suppress hidden overloaded virtual function warnng virtual void visit(AstNetlist* nodep) { - // Top module only + // Top module only iterate(nodep->topModulep()); } virtual void visit(AstNodeModule* nodep) { iterateChildren(nodep); } virtual void visit(AstCFunc* nodep) { - if (nodep->slow() != m_slow) return; - if (nodep->funcType().isTrace()) { // TRACE_* - m_funcp = nodep; + if (nodep->slow() != m_slow) return; + if (nodep->funcType().isTrace()) { // TRACE_* + m_funcp = nodep; - if (splitNeeded()) { - // Close old file - delete m_ofp; m_ofp=NULL; - // Open a new file + if (splitNeeded()) { + // Close old file + delete m_ofp; m_ofp = NULL; + // Open a new file newOutCFile(splitFilenumInc()); - } + } - splitSizeInc(nodep); + splitSizeInc(nodep); - puts("\n"); - puts(nodep->rtnTypeVoid()); puts(" "); - puts(topClassName()+"::"+nodep->name() - +"("+cFuncArgs(nodep)+") {\n"); + puts("\n"); + puts(nodep->rtnTypeVoid()); puts(" "); + puts(topClassName()+"::"+nodep->name() + +"("+cFuncArgs(nodep)+") {\n"); - if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n"); + if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n"); - puts("int c=code;\n"); - puts("if (0 && vcdp && c) {} // Prevent unused\n"); - if (nodep->funcType() == AstCFuncType::TRACE_INIT) { - puts("vcdp->module(vlSymsp->name()); // Setup signal names\n"); - } else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) { - } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) { - } else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) { - } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) { - } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) { - } else nodep->v3fatalSrc("Bad Case"); + puts("int c=code;\n"); + puts("if (0 && vcdp && c) {} // Prevent unused\n"); + if (nodep->funcType() == AstCFuncType::TRACE_INIT) { + puts("vcdp->module(vlSymsp->name()); // Setup signal names\n"); + } else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) { + } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) { + } else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) { + } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) { + } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) { + } else nodep->v3fatalSrc("Bad Case"); - if (nodep->initsp()) putsDecoration("// Variables\n"); + if (nodep->initsp()) putsDecoration("// Variables\n"); emitVarList(nodep->initsp(), EVL_FUNC_ALL, ""); iterateAndNextNull(nodep->initsp()); - putsDecoration("// Body\n"); - puts("{\n"); + putsDecoration("// Body\n"); + puts("{\n"); iterateAndNextNull(nodep->stmtsp()); - puts("}\n"); - if (nodep->finalsp()) putsDecoration("// Final\n"); + puts("}\n"); + if (nodep->finalsp()) putsDecoration("// Final\n"); iterateAndNextNull(nodep->finalsp()); - puts("}\n"); - } - m_funcp = NULL; + puts("}\n"); + } + m_funcp = NULL; } virtual void visit(AstTraceDecl* nodep) { int enumNum = emitTraceDeclDType(nodep->dtypep()); - if (nodep->arrayRange().ranged()) { - puts("{int i; for (i=0; i<"+cvtToStr(nodep->arrayRange().elements())+"; i++) {\n"); + if (nodep->arrayRange().ranged()) { + puts("{int i; for (i=0; i<"+cvtToStr(nodep->arrayRange().elements())+"; i++) {\n"); emitTraceInitOne(nodep, enumNum); - puts("}}\n"); - } else { + puts("}}\n"); + } else { emitTraceInitOne(nodep, enumNum); - puts("\n"); - } + puts("\n"); + } } virtual void visit(AstTraceInc* nodep) { - if (nodep->declp()->arrayRange().ranged()) { - // It traces faster if we unroll the loop - for (int i=0; ideclp()->arrayRange().elements(); i++) { - emitTraceChangeOne(nodep, i); - } - } else { - emitTraceChangeOne(nodep, -1); - } + if (nodep->declp()->arrayRange().ranged()) { + // It traces faster if we unroll the loop + for (int i=0; ideclp()->arrayRange().elements(); i++) { + emitTraceChangeOne(nodep, i); + } + } else { + emitTraceChangeOne(nodep, -1); + } } virtual void visit(AstCoverDecl* nodep) { } @@ -3138,21 +3166,21 @@ class EmitCTrace : EmitCStmts { public: explicit EmitCTrace(bool slow) { - m_funcp = NULL; - m_slow = slow; + m_funcp = NULL; + m_slow = slow; m_enumNum = 0; } virtual ~EmitCTrace() {} void main() { - // Put out the file - newOutCFile(0); + // Put out the file + newOutCFile(0); - if (m_slow) emitTraceSlow(); - else emitTraceFast(); + if (m_slow) emitTraceSlow(); + else emitTraceFast(); iterate(v3Global.rootp()); - delete m_ofp; m_ofp=NULL; + delete m_ofp; m_ofp = NULL; } }; @@ -3162,7 +3190,8 @@ public: void V3EmitC::emitc() { UINFO(2,__FUNCTION__<<": "<modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { + for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); + nodep; nodep = VN_CAST(nodep->nextp(), NodeModule)) { if (v3Global.opt.outputSplit()) { { EmitCImp fast; fast.main(nodep, false, true); } { EmitCImp slow; slow.main(nodep, true, false); } diff --git a/src/V3EmitC.h b/src/V3EmitC.h index 047ea716f..c7b878814 100644 --- a/src/V3EmitC.h +++ b/src/V3EmitC.h @@ -37,4 +37,4 @@ public: static void emitcTrace(); }; -#endif // Guard +#endif // Guard diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index 46a50d81b..62be3b384 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -37,9 +37,9 @@ class EmitCBaseVisitor : public AstNVisitor { public: // STATE - V3OutCFile* m_ofp; + V3OutCFile* m_ofp; // METHODS - V3OutCFile* ofp() const { return m_ofp; } + V3OutCFile* ofp() const { return m_ofp; } void puts(const string& str) { ofp()->puts(str); } void putbs(const string& str) { ofp()->putbs(str); } void putsDecoration(const string& str) { if (v3Global.opt.decoration()) puts(str); } @@ -47,46 +47,47 @@ public: bool optSystemC() { return v3Global.opt.systemC(); } static string symClassName() { return v3Global.opt.prefix()+"__Syms"; } static string symClassVar() { return symClassName()+"* __restrict vlSymsp"; } - static string symTopAssign() { return v3Global.opt.prefix()+"* __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;"; } - static string modClassName(AstNodeModule* modp) { // Return name of current module being processed - if (modp->isTop()) { - return v3Global.opt.prefix(); - } else { - return v3Global.opt.modPrefix() + "_" + modp->name(); - } + static string symTopAssign() { + return v3Global.opt.prefix()+"* __restrict vlTOPp VL_ATTR_UNUSED = vlSymsp->TOPp;"; } + static string modClassName(AstNodeModule* modp) { // Return name of current module being processed + if (modp->isTop()) { + return v3Global.opt.prefix(); + } else { + return v3Global.opt.modPrefix() + "_" + modp->name(); + } } - static string topClassName() { // Return name of top wrapper module - return v3Global.opt.prefix(); + static string topClassName() { // Return name of top wrapper module + return v3Global.opt.prefix(); } static AstCFile* newCFile(const string& filename, bool slow, bool source) { - AstCFile* cfilep = new AstCFile(v3Global.rootp()->fileline(), filename); - cfilep->slow(slow); - cfilep->source(source); - v3Global.rootp()->addFilesp(cfilep); - return cfilep; + AstCFile* cfilep = new AstCFile(v3Global.rootp()->fileline(), filename); + cfilep->slow(slow); + cfilep->source(source); + v3Global.rootp()->addFilesp(cfilep); + return cfilep; } string cFuncArgs(const AstCFunc* nodep) { - // Return argument list for given C function - string args = nodep->argTypes(); - // Might be a user function with argument list. + // Return argument list for given C function + string args = nodep->argTypes(); + // Might be a user function with argument list. for (const AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) { if (const AstVar* portp = VN_CAST_CONST(stmtp, Var)) { - if (portp->isIO() && !portp->isFuncReturn()) { - if (args != "") args+= ", "; - if (nodep->dpiImport() || nodep->dpiExportWrapper()) - args += portp->dpiArgType(true,false); - else if (nodep->funcPublic()) - args += portp->cPubArgType(true,false); - else args += portp->vlArgType(true,false,true); - } - } - } - return args; + if (portp->isIO() && !portp->isFuncReturn()) { + if (args != "") args+= ", "; + if (nodep->dpiImport() || nodep->dpiExportWrapper()) + args += portp->dpiArgType(true, false); + else if (nodep->funcPublic()) + args += portp->cPubArgType(true, false); + else args += portp->vlArgType(true, false, true); + } + } + } + return args; } // CONSTRUCTORS EmitCBaseVisitor() { - m_ofp = NULL; + m_ofp = NULL; } virtual ~EmitCBaseVisitor() {} }; @@ -96,21 +97,21 @@ public: class EmitCBaseCounterVisitor : public AstNVisitor { private: - // STATE - int m_count; // Number of statements + // MEMBERS + int m_count; // Number of statements // VISITORS virtual void visit(AstNode* nodep) { - m_count++; + m_count++; iterateChildren(nodep); } public: // CONSTUCTORS explicit EmitCBaseCounterVisitor(AstNode* nodep) { - m_count = 0; + m_count = 0; iterate(nodep); } virtual ~EmitCBaseCounterVisitor() {} int count() const { return m_count; } }; -#endif // guard +#endif // guard diff --git a/src/V3EmitCInlines.cpp b/src/V3EmitCInlines.cpp index 4c5157e15..84c1ec437 100644 --- a/src/V3EmitCInlines.cpp +++ b/src/V3EmitCInlines.cpp @@ -84,7 +84,7 @@ void EmitCInlines::emitInt() { // Placeholder - v3Global.needHInlines(true) currently not used puts("//======================\n\n"); - puts("#endif // guard\n"); + puts("#endif // guard\n"); } //###################################################################### diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 2d6d60c27..0636cfed4 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -39,22 +39,24 @@ class EmitCSyms : EmitCBaseVisitor { // NODE STATE // Cleared on Netlist - // AstNodeModule::user1() -> bool. Set true __Vconfigure called - AstUser1InUse m_inuser1; + // AstNodeModule::user1() -> bool. Set true __Vconfigure called + AstUser1InUse m_inuser1; // TYPES struct ScopeNameData { string m_symName; string m_prettyName; - ScopeNameData(const string& symName, const string& prettyName) - : m_symName(symName), m_prettyName(prettyName) {} + ScopeNameData(const string& symName, const string& prettyName) + : m_symName(symName), m_prettyName(prettyName) {} }; struct ScopeFuncData { AstScopeName* m_scopep; AstCFunc* m_funcp; AstNodeModule* m_modp; - ScopeFuncData(AstScopeName* scopep, AstCFunc* funcp, AstNodeModule* modp) - : m_scopep(scopep), m_funcp(funcp), m_modp(modp) {} + ScopeFuncData(AstScopeName* scopep, AstCFunc* funcp, AstNodeModule* modp) + : m_scopep(scopep), m_funcp(funcp), m_modp(modp) {} }; struct ScopeVarData { string m_scopeName; string m_varBasePretty; AstVar* m_varp; - AstNodeModule* m_modp; AstScope* m_scopep; - ScopeVarData(const string& scopeName, const string& varBasePretty, AstVar* varp, AstNodeModule* modp, AstScope* scopep) - : m_scopeName(scopeName), m_varBasePretty(varBasePretty), m_varp(varp), m_modp(modp), m_scopep(scopep) {} + AstNodeModule* m_modp; AstScope* m_scopep; + ScopeVarData(const string& scopeName, const string& varBasePretty, + AstVar* varp, AstNodeModule* modp, AstScope* scopep) + : m_scopeName(scopeName), m_varBasePretty(varBasePretty) + , m_varp(varp), m_modp(modp), m_scopep(scopep) {} }; typedef std::map ScopeFuncs; typedef std::map ScopeVars; @@ -63,31 +65,31 @@ class EmitCSyms : EmitCBaseVisitor { typedef std::pair ModVarPair; struct CmpName { inline bool operator() (const ScopeModPair& lhsp, const ScopeModPair& rhsp) const { - return lhsp.first->name() < rhsp.first->name(); - } + return lhsp.first->name() < rhsp.first->name(); + } }; struct CmpDpi { inline bool operator() (const AstCFunc* lhsp, const AstCFunc* rhsp) const { - if (lhsp->dpiImport() != rhsp->dpiImport()) { - // cppcheck-suppress comparisonOfFuncReturningBoolError - return lhsp->dpiImport() < rhsp->dpiImport(); - } - return lhsp->name() < rhsp->name(); - } + if (lhsp->dpiImport() != rhsp->dpiImport()) { + // cppcheck-suppress comparisonOfFuncReturningBoolError + return lhsp->dpiImport() < rhsp->dpiImport(); + } + return lhsp->name() < rhsp->name(); + } }; // STATE - AstCFunc* m_funcp; // Current function - AstNodeModule* m_modp; // Current module + AstCFunc* m_funcp; // Current function + AstNodeModule* m_modp; // Current module std::vector m_scopes; // Every scope by module std::vector m_dpis; // DPI functions std::vector m_modVars; // Each public {mod,var} - ScopeNames m_scopeNames; // Each unique AstScopeName - ScopeFuncs m_scopeFuncs; // Each {scope,dpi-export-func} - ScopeVars m_scopeVars; // Each {scope,public-var} - V3LanguageWords m_words; // Reserved word detector - int m_coverBins; // Coverage bin number - int m_labelNum; // Next label number + ScopeNames m_scopeNames; // Each unique AstScopeName + ScopeFuncs m_scopeFuncs; // Each {scope,dpi-export-func} + ScopeVars m_scopeVars; // Each {scope,public-var} + V3LanguageWords m_words; // Reserved word detector + int m_coverBins; // Coverage bin number + int m_labelNum; // Next label number // METHODS void emitSymHdr(); @@ -96,143 +98,151 @@ class EmitCSyms : EmitCBaseVisitor { void emitDpiImp(); void nameCheck(AstNode* nodep) { - // Prevent GCC compile time error; name check all things that reach C++ code - if (nodep->name() != "") { - string rsvd = m_words.isKeyword(nodep->name()); - if (rsvd != "") { - // Generally V3Name should find all of these and throw SYMRSVDWORD. - // We'll still check here because the compiler errors resulting if we miss this warning are SO nasty + // Prevent GCC compile time error; name check all things that reach C++ code + if (nodep->name() != "") { + string rsvd = m_words.isKeyword(nodep->name()); + if (rsvd != "") { + // Generally V3Name should find all of these and throw SYMRSVDWORD. + // We'll still check here because the compiler errors + // resulting if we miss this warning are SO nasty nodep->v3error("Symbol matching "+rsvd+" reserved word reached emitter," " should have hit SYMRSVDWORD: '"<prettyName()<<"'"); - } - } + } + } } void varsExpand() { - // We didn'e have all m_scopes loaded when we encountered variables, so expand them now - // It would be less code if each module inserted its own variables. - // Someday. For now public isn't common. - for (std::vector::iterator itsc = m_scopes.begin(); itsc != m_scopes.end(); ++itsc) { - AstScope* scopep = itsc->first; AstNodeModule* smodp = itsc->second; - for (std::vector::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) { - AstNodeModule* modp = it->first; - AstVar* varp = it->second; - if (modp == smodp) { - // Need to split the module + var name into the original-ish full scope and variable name under that scope. - // The module instance name is included later, when we know the scopes this module is under - string whole = scopep->name()+"__DOT__"+varp->name(); - string scpName; - string varBase; - if (whole.substr(0,10) == "__DOT__TOP") whole.replace(0,10,""); - string::size_type dpos = whole.rfind("__DOT__"); - if (dpos != string::npos) { - scpName = whole.substr(0,dpos); - varBase = whole.substr(dpos+strlen("__DOT__")); - } else { - varBase = whole; - } - //UINFO(9,"For "<name()<<" - "<name()<<" Scp "<::iterator itsc = m_scopes.begin(); + itsc != m_scopes.end(); ++itsc) { + AstScope* scopep = itsc->first; AstNodeModule* smodp = itsc->second; + for (std::vector::iterator it = m_modVars.begin(); + it != m_modVars.end(); ++it) { + AstNodeModule* modp = it->first; + AstVar* varp = it->second; + if (modp == smodp) { + // Need to split the module + var name into the + // original-ish full scope and variable name under that scope. + // The module instance name is included later, when we + // know the scopes this module is under + string whole = scopep->name()+"__DOT__"+varp->name(); + string scpName; + string varBase; + if (whole.substr(0, 10) == "__DOT__TOP") whole.replace(0, 10, ""); + string::size_type dpos = whole.rfind("__DOT__"); + if (dpos != string::npos) { + scpName = whole.substr(0, dpos); + varBase = whole.substr(dpos+strlen("__DOT__")); + } else { + varBase = whole; + } + //UINFO(9,"For "<name()<<" - "<name()<<" Scp "<name(), - ScopeVarData(scpSym, varBasePretty, varp, modp, scopep))); - } - } - } + out.replace(pos, 1, "__"); + } + while ((pos = out.find("__DOT__")) != string::npos) { + out.replace(pos, 7, "__"); + } + scpSym = out; + } + //UINFO(9," scnameins sp "<name(), + ScopeVarData(scpSym, varBasePretty, varp, modp, scopep))); + } + } + } } // VISITORS virtual void visit(AstNetlist* nodep) { - // Collect list of scopes + // Collect list of scopes iterateChildren(nodep); - varsExpand(); + varsExpand(); - // Sort by names, so line/process order matters less - stable_sort(m_scopes.begin(), m_scopes.end(), CmpName()); - stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi()); + // Sort by names, so line/process order matters less + stable_sort(m_scopes.begin(), m_scopes.end(), CmpName()); + stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi()); - // Output - emitSymHdr(); - emitSymImp(); - if (v3Global.dpi()) { - emitDpiHdr(); - emitDpiImp(); - } + // Output + emitSymHdr(); + emitSymImp(); + if (v3Global.dpi()) { + emitDpiHdr(); + emitDpiImp(); + } } virtual void visit(AstNodeModule* nodep) { - nameCheck(nodep); - m_modp = nodep; - m_labelNum = 0; + nameCheck(nodep); + m_modp = nodep; + m_labelNum = 0; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstScope* nodep) { - nameCheck(nodep); - m_scopes.push_back(make_pair(nodep, m_modp)); + nameCheck(nodep); + m_scopes.push_back(make_pair(nodep, m_modp)); } virtual void visit(AstScopeName* nodep) { - string name = nodep->scopeSymName(); - //UINFO(9,"scnameins sp "<name()<<" sp "<scopePrettySymName()<<" ss "<scopePrettySymName()))); - } - if (nodep->dpiExport()) { - if (!m_funcp) nodep->v3fatalSrc("ScopeName not under DPI function"); - m_scopeFuncs.insert(make_pair(name + " " + m_funcp->name(), - ScopeFuncData(nodep, m_funcp, m_modp))); - } else { - if (m_scopeNames.find(nodep->scopeDpiName()) == m_scopeNames.end()) { - m_scopeNames.insert(make_pair(nodep->scopeDpiName(), ScopeNameData(nodep->scopeDpiName(), nodep->scopePrettyDpiName()))); - } - } + string name = nodep->scopeSymName(); + //UINFO(9,"scnameins sp "<name()<<" sp "<scopePrettySymName()<<" ss "<scopePrettySymName()))); + } + if (nodep->dpiExport()) { + if (!m_funcp) nodep->v3fatalSrc("ScopeName not under DPI function"); + m_scopeFuncs.insert(make_pair(name + " " + m_funcp->name(), + ScopeFuncData(nodep, m_funcp, m_modp))); + } else { + if (m_scopeNames.find(nodep->scopeDpiName()) == m_scopeNames.end()) { + m_scopeNames.insert(make_pair(nodep->scopeDpiName(), + ScopeNameData(nodep->scopeDpiName(), + nodep->scopePrettyDpiName()))); + } + } } virtual void visit(AstVar* nodep) { - nameCheck(nodep); + nameCheck(nodep); iterateChildren(nodep); - if (nodep->isSigUserRdPublic() - && !nodep->isParam()) { // The VPI functions require a pointer to allow modification, but parameters are constants - m_modVars.push_back(make_pair(m_modp, nodep)); - } + if (nodep->isSigUserRdPublic() + && !nodep->isParam()) { // The VPI functions require a pointer to allow modification, but parameters are constants + m_modVars.push_back(make_pair(m_modp, nodep)); + } } virtual void visit(AstCoverDecl* nodep) { - // Assign numbers to all bins, so we know how big of an array to use - if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for - nodep->binNum(m_coverBins++); - } + // Assign numbers to all bins, so we know how big of an array to use + if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for + nodep->binNum(m_coverBins++); + } } virtual void visit(AstJumpLabel* nodep) { - nodep->labelNum(++m_labelNum); + nodep->labelNum(++m_labelNum); iterateChildren(nodep); } virtual void visit(AstCFunc* nodep) { - nameCheck(nodep); - if (nodep->dpiImport() || nodep->dpiExportWrapper()) { - m_dpis.push_back(nodep); - } - m_funcp = nodep; + nameCheck(nodep); + if (nodep->dpiImport() || nodep->dpiExportWrapper()) { + m_dpis.push_back(nodep); + } + m_funcp = nodep; iterateChildren(nodep); - m_funcp = NULL; + m_funcp = NULL; } // NOPs virtual void visit(AstConst*) {} @@ -244,10 +254,10 @@ class EmitCSyms : EmitCBaseVisitor { // ACCESSORS public: explicit EmitCSyms(AstNetlist* nodep) { - m_funcp = NULL; - m_modp = NULL; - m_coverBins = 0; - m_labelNum = 0; + m_funcp = NULL; + m_modp = NULL; + m_coverBins = 0; + m_labelNum = 0; iterate(nodep); } }; @@ -271,30 +281,31 @@ void EmitCSyms::emitSymHdr() { ofp()->putsIntTopInclude(); if (v3Global.needHeavy()) { - puts("#include \"verilated_heavy.h\"\n"); + puts("#include \"verilated_heavy.h\"\n"); } else { - puts("#include \"verilated.h\"\n"); + puts("#include \"verilated.h\"\n"); } // for puts("\n// INCLUDE MODULE CLASSES\n"); - for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { - puts("#include \""+modClassName(nodep)+".h\"\n"); + for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); + nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { + puts("#include \""+modClassName(nodep)+".h\"\n"); } if (v3Global.dpi()) { puts("\n// DPI TYPES for DPI Export callbacks (Internal use)\n"); std::map types; // Remove duplicates and sort - for (ScopeFuncs::iterator it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) { - AstCFunc* funcp = it->second.m_funcp; - if (funcp->dpiExport()) { - string cbtype = v3Global.opt.prefix()+"__Vcb_"+funcp->cname()+"_t"; - types["typedef void (*"+cbtype+") ("+cFuncArgs(funcp)+");\n"] = 1; - } - } + for (ScopeFuncs::iterator it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) { + AstCFunc* funcp = it->second.m_funcp; + if (funcp->dpiExport()) { + string cbtype = v3Global.opt.prefix()+"__Vcb_"+funcp->cname()+"_t"; + types["typedef void (*"+cbtype+") ("+cFuncArgs(funcp)+");\n"] = 1; + } + } for (std::map::iterator it = types.begin(); it != types.end(); ++it) { - puts(it->first); - } + puts(it->first); + } } puts("\n// SYMS CLASS\n"); @@ -302,39 +313,39 @@ void EmitCSyms::emitSymHdr() { ofp()->putsPrivate(false); // public: puts("\n// LOCAL STATE\n"); - puts("const char* __Vm_namep;\n"); // Must be before subcells, as constructor order needed before _vlCoverInsert. + puts("const char* __Vm_namep;\n"); // Must be before subcells, as constructor order needed before _vlCoverInsert. if (v3Global.opt.trace()) { - puts("bool __Vm_activity; ///< Used by trace routines to determine change occurred\n"); + puts("bool __Vm_activity; ///< Used by trace routines to determine change occurred\n"); } puts("bool __Vm_didInit;\n"); puts("\n// SUBCELL STATE\n"); for (std::vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { - AstScope* scopep = it->first; AstNodeModule* modp = it->second; - if (modp->isTop()) { - ofp()->printf("%-30s ", (modClassName(modp)+"*").c_str()); - puts(scopep->nameDotless()+"p;\n"); - } - else { - ofp()->printf("%-30s ", (modClassName(modp)+"").c_str()); - puts(scopep->nameDotless()+";\n"); - } + AstScope* scopep = it->first; AstNodeModule* modp = it->second; + if (modp->isTop()) { + ofp()->printf("%-30s ", (modClassName(modp)+"*").c_str()); + puts(scopep->nameDotless()+"p;\n"); + } + else { + ofp()->printf("%-30s ", (modClassName(modp)+"").c_str()); + puts(scopep->nameDotless()+";\n"); + } } if (m_coverBins) { - puts("\n// COVERAGE\n"); - puts("uint32_t __Vcoverage["); puts(cvtToStr(m_coverBins)); puts("];\n"); + puts("\n// COVERAGE\n"); + puts("uint32_t __Vcoverage["); puts(cvtToStr(m_coverBins)); puts("];\n"); } { // Scope names - bool did = false; - for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { - if (!did) { - did = true; - puts("\n// SCOPE NAMES\n"); - } - puts("VerilatedScope __Vscope_"+it->second.m_symName+";\n"); - } + bool did = false; + for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { + if (!did) { + did = true; + puts("\n// SCOPE NAMES\n"); + } + puts("VerilatedScope __Vscope_"+it->second.m_symName+";\n"); + } } puts("\n// CREATORS\n"); @@ -344,16 +355,16 @@ void EmitCSyms::emitSymHdr() { puts("\n// METHODS\n"); puts("inline const char* name() { return __Vm_namep; }\n"); if (v3Global.opt.trace()) { - puts("inline bool getClearActivity() { bool r=__Vm_activity; __Vm_activity=false; return r; }\n"); + puts("inline bool getClearActivity() { bool r=__Vm_activity; __Vm_activity=false; return r; }\n"); } if (v3Global.opt.savable() ) { - puts("void __Vserialize(VerilatedSerialize& os);\n"); - puts("void __Vdeserialize(VerilatedDeserialize& os);\n"); + puts("void __Vserialize(VerilatedSerialize& os);\n"); + puts("void __Vdeserialize(VerilatedDeserialize& os);\n"); } puts("\n"); puts("} VL_ATTR_ALIGNED(64);\n"); puts("\n"); - puts("#endif // guard\n"); + puts("#endif // guard\n"); } void EmitCSyms::emitSymImp() { @@ -369,8 +380,9 @@ void EmitCSyms::emitSymImp() { // Includes puts("#include \""+symClassName()+".h\"\n"); - for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { - puts("#include \""+modClassName(nodep)+".h\"\n"); + for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); + nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { + puts("#include \""+modClassName(nodep)+".h\"\n"); } //puts("\n// GLOBALS\n"); @@ -378,25 +390,25 @@ void EmitCSyms::emitSymImp() { puts("\n// FUNCTIONS\n"); puts(symClassName()+"::"+symClassName()+"("+topClassName()+"* topp, const char* namep)\n"); puts("\t// Setup locals\n"); - puts("\t: __Vm_namep(namep)\n"); // No leak, as we get destroyed when the top is destroyed + puts("\t: __Vm_namep(namep)\n"); // No leak, as we get destroyed when the top is destroyed if (v3Global.opt.trace()) { - puts("\t, __Vm_activity(false)\n"); + puts("\t, __Vm_activity(false)\n"); } puts("\t, __Vm_didInit(false)\n"); puts("\t// Setup submodule names\n"); char comma=','; for (std::vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { - AstScope* scopep = it->first; AstNodeModule* modp = it->second; - if (modp->isTop()) { - } else { - string nameDl = scopep->nameDotless(); - ofp()->printf("\t%c %-30s ", comma, nameDl.c_str()); - puts("(Verilated::catName(topp->name(),"); - // The "." is added by catName - putsQuoted(scopep->prettyName()); - puts("))\n"); - comma=','; - } + AstScope* scopep = it->first; AstNodeModule* modp = it->second; + if (modp->isTop()) { + } else { + string nameDl = scopep->nameDotless(); + ofp()->printf("\t%c %-30s ", comma, nameDl.c_str()); + puts("(Verilated::catName(topp->name(),"); + // The "." is added by catName + putsQuoted(scopep->prettyName()); + puts("))\n"); + comma=','; + } } puts("{\n"); @@ -404,144 +416,145 @@ void EmitCSyms::emitSymImp() { puts("TOPp = topp;\n"); puts("// Setup each module's pointers to their submodules\n"); for (std::vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { - AstScope* scopep = it->first; AstNodeModule* modp = it->second; - if (!modp->isTop()) { - string arrow = scopep->name(); - string::size_type pos; + AstScope* scopep = it->first; AstNodeModule* modp = it->second; + if (!modp->isTop()) { + string arrow = scopep->name(); + string::size_type pos; while ((pos = arrow.find('.')) != string::npos) { - arrow.replace(pos, 1, "->"); - } - if (arrow.substr(0,5) == "TOP->") arrow.replace(0,5,"TOPp->"); - ofp()->printf("%-30s ", arrow.c_str()); - puts(" = &"); - puts(scopep->nameDotless()+";\n"); - } + arrow.replace(pos, 1, "->"); + } + if (arrow.substr(0, 5) == "TOP->") arrow.replace(0, 5, "TOPp->"); + ofp()->printf("%-30s ", arrow.c_str()); + puts(" = &"); + puts(scopep->nameDotless()+";\n"); + } } puts("// Setup each module's pointer back to symbol table (for public functions)\n"); puts("TOPp->__Vconfigure(this, true);\n"); for (std::vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { - AstScope* scopep = it->first; AstNodeModule* modp = it->second; - if (!modp->isTop()) { - // first is used by AstCoverDecl's call to __vlCoverInsert - bool first = !modp->user1(); - modp->user1(true); - puts(scopep->nameDotless()+".__Vconfigure(this, " - +(first?"true":"false") - +");\n"); - } + AstScope* scopep = it->first; AstNodeModule* modp = it->second; + if (!modp->isTop()) { + // first is used by AstCoverDecl's call to __vlCoverInsert + bool first = !modp->user1(); + modp->user1(true); + puts(scopep->nameDotless()+".__Vconfigure(this, " + +(first?"true":"false") + +");\n"); + } } { // Setup scope names - bool did = false; - for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { - if (!did) { - did = true; - puts("// Setup scope names\n"); - } - puts("__Vscope_"+it->second.m_symName+".configure(this,name(),"); - putsQuoted(it->second.m_prettyName); - puts(");\n"); - } + bool did = false; + for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { + if (!did) { + did = true; + puts("// Setup scope names\n"); + } + puts("__Vscope_"+it->second.m_symName+".configure(this,name(),"); + putsQuoted(it->second.m_prettyName); + puts(");\n"); + } } if (v3Global.dpi()) { - puts("// Setup export functions\n"); - puts("for (int __Vfinal=0; __Vfinal<2; __Vfinal++) {\n"); - for (ScopeFuncs::iterator it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) { - AstScopeName* scopep = it->second.m_scopep; - AstCFunc* funcp = it->second.m_funcp; - AstNodeModule* modp = it->second.m_modp; - if (funcp->dpiExport()) { - puts("__Vscope_"+scopep->scopeSymName()+".exportInsert(__Vfinal,"); - putsQuoted(funcp->cname()); - puts(", (void*)(&"); - puts(modClassName(modp)); - puts("::"); - puts(funcp->name()); - puts("));\n"); - } - } - // It would be less code if each module inserted its own variables. - // Someday. For now public isn't common. - for (ScopeVars::iterator it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) { - AstNodeModule* modp = it->second.m_modp; - AstScope* scopep = it->second.m_scopep; - AstVar* varp = it->second.m_varp; - // - int pdim=0; - int udim=0; - string bounds; - if (AstBasicDType* basicp = varp->basicp()) { - // Range is always first, it's not in "C" order - if (basicp->isRanged()) { - bounds += " ,"; bounds += cvtToStr(basicp->msb()); - bounds += ","; bounds += cvtToStr(basicp->lsb()); - pdim++; - } - for (AstNodeDType* dtypep=varp->dtypep(); dtypep; ) { - dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + puts("// Setup export functions\n"); + puts("for (int __Vfinal=0; __Vfinal<2; __Vfinal++) {\n"); + for (ScopeFuncs::iterator it = m_scopeFuncs.begin(); it != m_scopeFuncs.end(); ++it) { + AstScopeName* scopep = it->second.m_scopep; + AstCFunc* funcp = it->second.m_funcp; + AstNodeModule* modp = it->second.m_modp; + if (funcp->dpiExport()) { + puts("__Vscope_"+scopep->scopeSymName()+".exportInsert(__Vfinal,"); + putsQuoted(funcp->cname()); + puts(", (void*)(&"); + puts(modClassName(modp)); + puts("::"); + puts(funcp->name()); + puts("));\n"); + } + } + // It would be less code if each module inserted its own variables. + // Someday. For now public isn't common. + for (ScopeVars::iterator it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) { + AstNodeModule* modp = it->second.m_modp; + AstScope* scopep = it->second.m_scopep; + AstVar* varp = it->second.m_varp; + // + int pdim = 0; + int udim = 0; + string bounds; + if (AstBasicDType* basicp = varp->basicp()) { + // Range is always first, it's not in "C" order + if (basicp->isRanged()) { + bounds += " ,"; bounds += cvtToStr(basicp->msb()); + bounds += ","; bounds += cvtToStr(basicp->lsb()); + pdim++; + } + for (AstNodeDType* dtypep = varp->dtypep(); dtypep; ) { + dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) { - bounds += " ,"; bounds += cvtToStr(adtypep->msb()); - bounds += ","; bounds += cvtToStr(adtypep->lsb()); + bounds += " ,"; bounds += cvtToStr(adtypep->msb()); + bounds += ","; bounds += cvtToStr(adtypep->lsb()); if (VN_IS(dtypep, PackArrayDType)) pdim++; else udim++; - dtypep = adtypep->subDTypep(); - } - else break; // AstBasicDType - nothing below, 1 - } - } - // - if (pdim>1 || udim>1) { - puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays - } - puts("__Vscope_"+it->second.m_scopeName+".varInsert(__Vfinal,"); - putsQuoted(it->second.m_varBasePretty); - puts(", &("); - if (modp->isTop()) { - puts(scopep->nameDotless()); - puts("p->"); - } else { - puts(scopep->nameDotless()); - puts("."); - } - puts(varp->name()); - puts("), "); - puts(varp->vlEnumType()); // VLVT_UINT32 etc - puts(","); - puts(varp->vlEnumDir()); // VLVD_IN etc - puts(","); - puts(cvtToStr(pdim+udim)); - puts(bounds); - puts(");\n"); - } - puts("}\n"); + dtypep = adtypep->subDTypep(); + } + else break; // AstBasicDType - nothing below, 1 + } + } + // + if (pdim>1 || udim>1) { + puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays + } + puts("__Vscope_"+it->second.m_scopeName+".varInsert(__Vfinal,"); + putsQuoted(it->second.m_varBasePretty); + puts(", &("); + if (modp->isTop()) { + puts(scopep->nameDotless()); + puts("p->"); + } else { + puts(scopep->nameDotless()); + puts("."); + } + puts(varp->name()); + puts("), "); + puts(varp->vlEnumType()); // VLVT_UINT32 etc + puts(","); + puts(varp->vlEnumDir()); // VLVD_IN etc + puts(","); + puts(cvtToStr(pdim+udim)); + puts(bounds); + puts(");\n"); + } + puts("}\n"); } puts("}\n"); if (v3Global.opt.savable() ) { - puts("\n"); - for (int de=0; de<2; ++de) { - string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; - string funcname = de ? "__Vdeserialize" : "__Vserialize"; - string op = de ? ">>" : "<<"; + puts("\n"); + for (int de=0; de<2; ++de) { + string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + string funcname = de ? "__Vdeserialize" : "__Vserialize"; + string op = de ? ">>" : "<<"; // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - puts("void "+symClassName()+"::"+funcname+"("+classname+"& os) {\n"); - puts( "// LOCAL STATE\n"); - // __Vm_namep presumably already correct - if (v3Global.opt.trace()) { - puts( "os"+op+"__Vm_activity;\n"); - } - puts( "os"+op+"__Vm_didInit;\n"); - puts( "// SUBCELL STATE\n"); - for (std::vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { - AstScope* scopep = it->first; AstNodeModule* modp = it->second; - if (!modp->isTop()) { - puts( scopep->nameDotless()+"."+funcname+"(os);\n"); - } - } - puts("}\n"); - } + puts("void "+symClassName()+"::"+funcname+"("+classname+"& os) {\n"); + puts( "// LOCAL STATE\n"); + // __Vm_namep presumably already correct + if (v3Global.opt.trace()) { + puts( "os"+op+"__Vm_activity;\n"); + } + puts( "os"+op+"__Vm_didInit;\n"); + puts( "// SUBCELL STATE\n"); + for (std::vector::iterator it = m_scopes.begin(); + it != m_scopes.end(); ++it) { + AstScope* scopep = it->first; AstNodeModule* modp = it->second; + if (!modp->isTop()) { + puts( scopep->nameDotless()+"."+funcname+"(os);\n"); + } + } + puts("}\n"); + } } } @@ -572,17 +585,17 @@ void EmitCSyms::emitDpiHdr() { int firstExp = 0; int firstImp = 0; for (std::vector::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) { - AstCFunc* nodep = *it; - if (nodep->dpiExportWrapper()) { - if (!firstExp++) puts("\n// DPI EXPORTS\n"); - puts("// DPI export at "+nodep->fileline()->ascii()+"\n"); - puts("extern "+nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+");\n"); - } - else if (nodep->dpiImport()) { - if (!firstImp++) puts("\n// DPI IMPORTS\n"); - puts("// DPI import at "+nodep->fileline()->ascii()+"\n"); - puts("extern "+nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+");\n"); - } + AstCFunc* nodep = *it; + if (nodep->dpiExportWrapper()) { + if (!firstExp++) puts("\n// DPI EXPORTS\n"); + puts("// DPI export at "+nodep->fileline()->ascii()+"\n"); + puts("extern "+nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+");\n"); + } + else if (nodep->dpiImport()) { + if (!firstImp++) puts("\n// DPI IMPORTS\n"); + puts("// DPI import at "+nodep->fileline()->ascii()+"\n"); + puts("extern "+nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+");\n"); + } } puts("\n"); @@ -620,27 +633,27 @@ void EmitCSyms::emitDpiImp() { puts("\n"); for (std::vector::iterator it = m_dpis.begin(); it != m_dpis.end(); ++it) { - AstCFunc* nodep = *it; - if (nodep->dpiExportWrapper()) { - puts("#ifndef _VL_DPIDECL_"+nodep->name()+"\n"); - puts("#define _VL_DPIDECL_"+nodep->name()+"\n"); - puts(nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+") {\n"); - puts("// DPI Export at "+nodep->fileline()->ascii()+"\n"); - puts("return "+topClassName()+"::"+nodep->name()+"("); - string args; - for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) { + AstCFunc* nodep = *it; + if (nodep->dpiExportWrapper()) { + puts("#ifndef _VL_DPIDECL_"+nodep->name()+"\n"); + puts("#define _VL_DPIDECL_"+nodep->name()+"\n"); + puts(nodep->rtnTypeVoid()+" "+nodep->name()+" ("+cFuncArgs(nodep)+") {\n"); + puts("// DPI Export at "+nodep->fileline()->ascii()+"\n"); + puts("return "+topClassName()+"::"+nodep->name()+"("); + string args; + for (AstNode* stmtp = nodep->argsp(); stmtp; stmtp=stmtp->nextp()) { if (const AstVar* portp = VN_CAST(stmtp, Var)) { - if (portp->isIO() && !portp->isFuncReturn()) { - if (args != "") args+= ", "; - args += portp->name(); - } - } - } - puts(args+");\n"); - puts("}\n"); - puts("#endif\n"); - puts("\n"); - } + if (portp->isIO() && !portp->isFuncReturn()) { + if (args != "") args+= ", "; + args += portp->name(); + } + } + } + puts(args+");\n"); + puts("}\n"); + puts("#endif\n"); + puts("\n"); + } } } diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 26d7f6360..de2cd7c6b 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -42,198 +42,200 @@ public: VL_DEBUG_FUNC; // Declare debug() void putMakeClassEntry(V3OutMkFile& of, const string& name) { - of.puts("\t"+V3Os::filenameNonDirExt(name)+" \\\n"); + of.puts("\t"+V3Os::filenameNonDirExt(name)+" \\\n"); } void emitClassMake() { - // Generate the makefile - V3OutMkFile of (v3Global.opt.makeDir()+"/"+ v3Global.opt.prefix() + "_classes.mk"); - of.putsHeader(); - of.puts("# DESCR" "IPTION: Verilator output: Make include file with class lists\n"); - of.puts("#\n"); - of.puts("# This file lists generated Verilated files, for including in higher level makefiles.\n"); - of.puts("# See "+v3Global.opt.prefix()+".mk"+" for the caller.\n"); + // Generate the makefile + V3OutMkFile of (v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_classes.mk"); + of.putsHeader(); + of.puts("# DESCR" "IPTION: Verilator output: Make include file with class lists\n"); + of.puts("#\n"); + of.puts("# This file lists generated Verilated files, for including in higher level makefiles.\n"); + of.puts("# See "+v3Global.opt.prefix()+".mk"+" for the caller.\n"); - of.puts("\n### Switches...\n"); - of.puts("# Coverage output mode? 0/1 (from --coverage)\n"); - of.puts("VM_COVERAGE = "); of.puts(v3Global.opt.coverage()?"1":"0"); of.puts("\n"); - of.puts("# Threaded output mode? 0/1/N threads (from --threads)\n"); - of.puts("VM_THREADS = "); of.puts(cvtToStr(v3Global.opt.threads())); of.puts("\n"); - of.puts("# Tracing output mode? 0/1 (from --trace)\n"); - of.puts("VM_TRACE = "); of.puts(v3Global.opt.trace()?"1":"0"); of.puts("\n"); + of.puts("\n### Switches...\n"); + of.puts("# Coverage output mode? 0/1 (from --coverage)\n"); + of.puts("VM_COVERAGE = "); of.puts(v3Global.opt.coverage()?"1":"0"); of.puts("\n"); + of.puts("# Threaded output mode? 0/1/N threads (from --threads)\n"); + of.puts("VM_THREADS = "); of.puts(cvtToStr(v3Global.opt.threads())); of.puts("\n"); + of.puts("# Tracing output mode? 0/1 (from --trace)\n"); + of.puts("VM_TRACE = "); of.puts(v3Global.opt.trace()?"1":"0"); of.puts("\n"); of.puts("# Tracing threadeds output mode? 0/1 (from --trace-fst-thread)\n"); of.puts("VM_TRACE_THREADED = "); of.puts(v3Global.opt.traceFormat().threaded() ?"1":"0"); of.puts("\n"); - of.puts("\n### Object file lists...\n"); - for (int support=0; support<3; support++) { - for (int slow=0; slow<2; slow++) { - if (support==2) of.puts("# Global classes, need linked once per executable"); - else if (support) of.puts("# Generated support classes"); - else of.puts("# Generated module classes"); - if (slow) of.puts(", non-fast-path, compile with low/medium optimization\n"); - else of.puts(", fast-path, compile with highest optimization\n"); - of.puts(support==2?"VM_GLOBAL":support==1?"VM_SUPPORT":"VM_CLASSES"); - of.puts(slow?"_SLOW":"_FAST"); - of.puts(" += \\\n"); - if (support==2 && !slow) { - putMakeClassEntry(of, "verilated.cpp"); - if (v3Global.dpi()) { - putMakeClassEntry(of, "verilated_dpi.cpp"); - } - if (v3Global.opt.vpi()) { - putMakeClassEntry(of, "verilated_vpi.cpp"); - } - if (v3Global.opt.savable()) { - putMakeClassEntry(of, "verilated_save.cpp"); - } - if (v3Global.opt.coverage()) { - putMakeClassEntry(of, "verilated_cov.cpp"); - } - if (v3Global.opt.trace()) { + of.puts("\n### Object file lists...\n"); + for (int support=0; support<3; ++support) { + for (int slow=0; slow<2; ++slow) { + if (support==2) of.puts("# Global classes, need linked once per executable"); + else if (support) of.puts("# Generated support classes"); + else of.puts("# Generated module classes"); + if (slow) of.puts(", non-fast-path, compile with low/medium optimization\n"); + else of.puts(", fast-path, compile with highest optimization\n"); + of.puts(support==2?"VM_GLOBAL":support==1?"VM_SUPPORT":"VM_CLASSES"); + of.puts(slow?"_SLOW":"_FAST"); + of.puts(" += \\\n"); + if (support==2 && !slow) { + putMakeClassEntry(of, "verilated.cpp"); + if (v3Global.dpi()) { + putMakeClassEntry(of, "verilated_dpi.cpp"); + } + if (v3Global.opt.vpi()) { + putMakeClassEntry(of, "verilated_vpi.cpp"); + } + if (v3Global.opt.savable()) { + putMakeClassEntry(of, "verilated_save.cpp"); + } + if (v3Global.opt.coverage()) { + putMakeClassEntry(of, "verilated_cov.cpp"); + } + if (v3Global.opt.trace()) { putMakeClassEntry(of, v3Global.opt.traceSourceName()+"_c.cpp"); - if (v3Global.opt.systemC()) { + if (v3Global.opt.systemC()) { if (v3Global.opt.traceFormat() != TraceFormat::VCD) { v3error("Unsupported: This trace format is not supported in SystemC, use VCD format."); } else { putMakeClassEntry(of, v3Global.opt.traceSourceName()+"_sc.cpp"); } - } - } + } + } if (v3Global.opt.mtasks()) { putMakeClassEntry(of, "verilated_threads.cpp"); } - } - else if (support==2 && slow) { - } - else { - for (AstCFile* nodep = v3Global.rootp()->filesp(); nodep; nodep=VN_CAST(nodep->nextp(), CFile)) { - if (nodep->source() && nodep->slow()==(slow!=0) && nodep->support()==(support!=0)) { - putMakeClassEntry(of, nodep->name()); - } - } - } - of.puts("\n"); - } - } + } + else if (support==2 && slow) { + } + else { + for (AstCFile* nodep = v3Global.rootp()->filesp(); + nodep; nodep = VN_CAST(nodep->nextp(), CFile)) { + if (nodep->source() && nodep->slow()==(slow!=0) + && nodep->support()==(support!=0)) { + putMakeClassEntry(of, nodep->name()); + } + } + } + of.puts("\n"); + } + } - of.puts("\n"); - of.putsHeader(); + of.puts("\n"); + of.putsHeader(); } void emitOverallMake() { - // Generate the makefile - V3OutMkFile of (v3Global.opt.makeDir()+"/"+ v3Global.opt.prefix() + ".mk"); - of.putsHeader(); - of.puts("# DESCR" "IPTION: Verilator output: Makefile for building Verilated archive or executable\n"); - of.puts("#\n"); - of.puts("# Execute this makefile from the object directory:\n"); - of.puts("# make -f "+v3Global.opt.prefix()+".mk"+"\n"); - of.puts("\n"); + // Generate the makefile + V3OutMkFile of (v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+".mk"); + of.putsHeader(); + of.puts("# DESCR" "IPTION: Verilator output: Makefile for building Verilated archive or executable\n"); + of.puts("#\n"); + of.puts("# Execute this makefile from the object directory:\n"); + of.puts("# make -f "+v3Global.opt.prefix()+".mk"+"\n"); + of.puts("\n"); - if (v3Global.opt.exe()) { - of.puts("default: "+v3Global.opt.exeName()+"\n"); - } else { - of.puts("default: "+v3Global.opt.prefix()+"__ALL.a\n"); - } - of.puts("\n### Constants...\n"); - of.puts("# Perl executable (from $PERL)\n"); - of.puts("PERL = "+V3Options::getenvPERL()+"\n"); - of.puts("# Path to Verilator kit (from $VERILATOR_ROOT)\n"); - of.puts("VERILATOR_ROOT = "+V3Options::getenvVERILATOR_ROOT()+"\n"); - of.puts("# SystemC include directory with systemc.h (from $SYSTEMC_INCLUDE)\n"); - of.puts(string("SYSTEMC_INCLUDE ?= ")+V3Options::getenvSYSTEMC_INCLUDE()+"\n"); - of.puts("# SystemC library directory with libsystemc.a (from $SYSTEMC_LIBDIR)\n"); - of.puts(string("SYSTEMC_LIBDIR ?= ")+V3Options::getenvSYSTEMC_LIBDIR()+"\n"); + if (v3Global.opt.exe()) { + of.puts("default: "+v3Global.opt.exeName()+"\n"); + } else { + of.puts("default: "+v3Global.opt.prefix()+"__ALL.a\n"); + } + of.puts("\n### Constants...\n"); + of.puts("# Perl executable (from $PERL)\n"); + of.puts("PERL = "+V3Options::getenvPERL()+"\n"); + of.puts("# Path to Verilator kit (from $VERILATOR_ROOT)\n"); + of.puts("VERILATOR_ROOT = "+V3Options::getenvVERILATOR_ROOT()+"\n"); + of.puts("# SystemC include directory with systemc.h (from $SYSTEMC_INCLUDE)\n"); + of.puts(string("SYSTEMC_INCLUDE ?= ")+V3Options::getenvSYSTEMC_INCLUDE()+"\n"); + of.puts("# SystemC library directory with libsystemc.a (from $SYSTEMC_LIBDIR)\n"); + of.puts(string("SYSTEMC_LIBDIR ?= ")+V3Options::getenvSYSTEMC_LIBDIR()+"\n"); - of.puts("\n### Switches...\n"); - of.puts("# SystemC output mode? 0/1 (from --sc)\n"); - of.puts(string("VM_SC = ")+((v3Global.opt.systemC())?"1":"0")+"\n"); - of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n"); - of.puts(string("VM_SP_OR_SC = $(VM_SC)\n")); - of.puts("# Deprecated\n"); - of.puts(string("VM_PCLI = ")+(v3Global.opt.systemC()?"0":"1")+"\n"); - of.puts("# Deprecated: SystemC architecture to find link library path (from $SYSTEMC_ARCH)\n"); - of.puts(string("VM_SC_TARGET_ARCH = ")+V3Options::getenvSYSTEMC_ARCH()+"\n"); + of.puts("\n### Switches...\n"); + of.puts("# SystemC output mode? 0/1 (from --sc)\n"); + of.puts(string("VM_SC = ")+((v3Global.opt.systemC())?"1":"0")+"\n"); + of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n"); + of.puts(string("VM_SP_OR_SC = $(VM_SC)\n")); + of.puts("# Deprecated\n"); + of.puts(string("VM_PCLI = ")+(v3Global.opt.systemC()?"0":"1")+"\n"); + of.puts("# Deprecated: SystemC architecture to find link library path (from $SYSTEMC_ARCH)\n"); + of.puts(string("VM_SC_TARGET_ARCH = ")+V3Options::getenvSYSTEMC_ARCH()+"\n"); - of.puts("\n### Vars...\n"); - of.puts("# Design prefix (from --prefix)\n"); - of.puts(string("VM_PREFIX = ")+v3Global.opt.prefix()+"\n"); - of.puts("# Module prefix (from --prefix)\n"); - of.puts(string("VM_MODPREFIX = ")+v3Global.opt.modPrefix()+"\n"); + of.puts("\n### Vars...\n"); + of.puts("# Design prefix (from --prefix)\n"); + of.puts(string("VM_PREFIX = ")+v3Global.opt.prefix()+"\n"); + of.puts("# Module prefix (from --prefix)\n"); + of.puts(string("VM_MODPREFIX = ")+v3Global.opt.modPrefix()+"\n"); - of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n"); - of.puts("VM_USER_CFLAGS = \\\n"); - const V3StringList& cFlags = v3Global.opt.cFlags(); - for (V3StringList::const_iterator it = cFlags.begin(); it != cFlags.end(); ++it) { - of.puts("\t"+*it+" \\\n"); - } - of.puts("\n"); + of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n"); + of.puts("VM_USER_CFLAGS = \\\n"); + const V3StringList& cFlags = v3Global.opt.cFlags(); + for (V3StringList::const_iterator it = cFlags.begin(); it != cFlags.end(); ++it) { + of.puts("\t"+*it+" \\\n"); + } + of.puts("\n"); - of.puts("# User LDLIBS (from -LDFLAGS on Verilator command line)\n"); - of.puts("VM_USER_LDLIBS = \\\n"); - const V3StringList& ldLibs = v3Global.opt.ldLibs(); - for (V3StringList::const_iterator it = ldLibs.begin(); it != ldLibs.end(); ++it) { - of.puts("\t"+*it+" \\\n"); - } - of.puts("\n"); + of.puts("# User LDLIBS (from -LDFLAGS on Verilator command line)\n"); + of.puts("VM_USER_LDLIBS = \\\n"); + const V3StringList& ldLibs = v3Global.opt.ldLibs(); + for (V3StringList::const_iterator it = ldLibs.begin(); it != ldLibs.end(); ++it) { + of.puts("\t"+*it+" \\\n"); + } + of.puts("\n"); - V3StringSet dirs; - of.puts("# User .cpp files (from .cpp's on Verilator command line)\n"); - of.puts("VM_USER_CLASSES = \\\n"); - const V3StringSet& cppFiles = v3Global.opt.cppFiles(); - for (V3StringSet::const_iterator it = cppFiles.begin(); it != cppFiles.end(); ++it) { - string cppfile = *it; - of.puts("\t"+V3Os::filenameNonExt(cppfile)+" \\\n"); - string dir = V3Os::filenameDir(cppfile); - if (dirs.find(dir) == dirs.end()) dirs.insert(dir); - } - of.puts("\n"); + V3StringSet dirs; + of.puts("# User .cpp files (from .cpp's on Verilator command line)\n"); + of.puts("VM_USER_CLASSES = \\\n"); + const V3StringSet& cppFiles = v3Global.opt.cppFiles(); + for (V3StringSet::const_iterator it = cppFiles.begin(); it != cppFiles.end(); ++it) { + string cppfile = *it; + of.puts("\t"+V3Os::filenameNonExt(cppfile)+" \\\n"); + string dir = V3Os::filenameDir(cppfile); + if (dirs.find(dir) == dirs.end()) dirs.insert(dir); + } + of.puts("\n"); - of.puts("# User .cpp directories (from .cpp's on Verilator command line)\n"); - of.puts("VM_USER_DIR = \\\n"); - for (V3StringSet::iterator it = dirs.begin(); it!=dirs.end(); ++it) { - of.puts("\t"+*it+" \\\n"); - } - of.puts("\n"); + of.puts("# User .cpp directories (from .cpp's on Verilator command line)\n"); + of.puts("VM_USER_DIR = \\\n"); + for (V3StringSet::iterator it = dirs.begin(); it!=dirs.end(); ++it) { + of.puts("\t"+*it+" \\\n"); + } + of.puts("\n"); - of.puts("\n### Default rules...\n"); - of.puts("# Include list of all generated classes\n"); - of.puts("include "+v3Global.opt.prefix()+"_classes.mk\n"); - of.puts("# Include global rules\n"); - of.puts("include $(VERILATOR_ROOT)/include/verilated.mk\n"); + of.puts("\n### Default rules...\n"); + of.puts("# Include list of all generated classes\n"); + of.puts("include "+v3Global.opt.prefix()+"_classes.mk\n"); + of.puts("# Include global rules\n"); + of.puts("include $(VERILATOR_ROOT)/include/verilated.mk\n"); - if (v3Global.opt.exe()) { - of.puts("\n### Executable rules... (from --exe)\n"); - of.puts("VPATH += $(VM_USER_DIR)\n"); - of.puts("\n"); - for (V3StringSet::const_iterator it = cppFiles.begin(); it != cppFiles.end(); ++it) { - string cppfile = *it; - string basename = V3Os::filenameNonExt(cppfile); + if (v3Global.opt.exe()) { + of.puts("\n### Executable rules... (from --exe)\n"); + of.puts("VPATH += $(VM_USER_DIR)\n"); + of.puts("\n"); + for (V3StringSet::const_iterator it = cppFiles.begin(); it != cppFiles.end(); ++it) { + string cppfile = *it; + string basename = V3Os::filenameNonExt(cppfile); // NOLINTNEXTLINE(performance-inefficient-string-concatenation) - of.puts(basename+".o: "+cppfile+"\n"); - of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<\n"); - } + of.puts(basename+".o: "+cppfile+"\n"); + of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -c -o $@ $<\n"); + } - of.puts("\n### Link rules... (from --exe)\n"); - of.puts(v3Global.opt.exeName()+": $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) $(VM_PREFIX)__ALL.a\n"); - of.puts("\t$(LINK) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) $(SC_LIBS)\n"); - of.puts("\n"); - } + of.puts("\n### Link rules... (from --exe)\n"); + of.puts(v3Global.opt.exeName()+": $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) $(VM_PREFIX)__ALL.a\n"); + of.puts("\t$(LINK) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) $(SC_LIBS)\n"); + of.puts("\n"); + } - of.puts("\n"); - of.putsHeader(); + of.puts("\n"); + of.putsHeader(); } //-------------------- virtual void visit(AstNode* nodep) { - nodep->v3fatalSrc("No visitors implemented."); + nodep->v3fatalSrc("No visitors implemented."); } public: explicit EmitMkVisitor(AstNetlist*) { - emitClassMake(); - emitOverallMake(); + emitClassMake(); + emitOverallMake(); } virtual ~EmitMkVisitor() {} }; diff --git a/src/V3EmitMk.h b/src/V3EmitMk.h index 456e4fe44..0817ea0de 100644 --- a/src/V3EmitMk.h +++ b/src/V3EmitMk.h @@ -34,4 +34,4 @@ public: static void emitmk(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 6530c6bc5..0ff07b863 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -36,8 +36,8 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { // MEMBERS - bool m_suppressSemi; - AstSenTree* m_sensesp; + bool m_suppressSemi; + AstSenTree* m_sensesp; // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -48,12 +48,12 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { virtual void putqs(AstNode* nodep, const string& str) = 0; // Fileline quiet w/o %% mark virtual void putsNoTracking(const string& str) = 0; virtual void putsQuoted(const string& str) { - // Quote \ and " for use inside C programs - // Don't use to quote a filename for #include - #include doesn't \ escape. - // Duplicate in V3File - here so we can print to string - putsNoTracking("\""); + // Quote \ and " for use inside C programs + // Don't use to quote a filename for #include - #include doesn't \ escape. + // Duplicate in V3File - here so we can print to string + putsNoTracking("\""); putsNoTracking(V3OutFormatter::quoteNameControls(str)); - putsNoTracking("\""); + putsNoTracking("\""); } // VISITORS @@ -61,199 +61,200 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { iterateChildren(nodep); } virtual void visit(AstNodeModule* nodep) { - putfs(nodep, nodep->verilogKwd()+" "+modClassName(nodep)+";\n"); + putfs(nodep, nodep->verilogKwd()+" "+modClassName(nodep)+";\n"); iterateChildren(nodep); - putqs(nodep, "end"+nodep->verilogKwd()+"\n"); + putqs(nodep, "end"+nodep->verilogKwd()+"\n"); } virtual void visit(AstNodeFTask* nodep) { - putfs(nodep, nodep->isFunction() ? "function":"task"); - puts(" "); - puts(nodep->prettyName()); - puts(";\n"); - putqs(nodep, "begin\n"); // Only putfs the first time for each visitor; later for same node is putqs + putfs(nodep, nodep->isFunction() ? "function":"task"); + puts(" "); + puts(nodep->prettyName()); + puts(";\n"); + putqs(nodep, "begin\n"); // Only putfs the first time for each visitor; later for same node is putqs iterateAndNextNull(nodep->stmtsp()); - putqs(nodep, "end\n"); + putqs(nodep, "end\n"); } virtual void visit(AstBegin* nodep) { - if (nodep->unnamed()) { - putbs("begin\n"); - } else { - putbs("begin : "+nodep->name()+"\n"); - } + if (nodep->unnamed()) { + putbs("begin\n"); + } else { + putbs("begin : "+nodep->name()+"\n"); + } iterateChildren(nodep); - puts("end\n"); + puts("end\n"); } virtual void visit(AstGenerate* nodep) { - putfs(nodep, "generate\n"); + putfs(nodep, "generate\n"); iterateChildren(nodep); - putqs(nodep, "end\n"); + putqs(nodep, "end\n"); } virtual void visit(AstFinal* nodep) { - putfs(nodep, "final begin\n"); + putfs(nodep, "final begin\n"); iterateChildren(nodep); - putqs(nodep, "end\n"); + putqs(nodep, "end\n"); } virtual void visit(AstInitial* nodep) { - putfs(nodep,"initial begin\n"); + putfs(nodep, "initial begin\n"); iterateChildren(nodep); - putqs(nodep, "end\n"); + putqs(nodep, "end\n"); } virtual void visit(AstAlways* nodep) { - putfs(nodep,"always "); + putfs(nodep, "always "); if (m_sensesp) iterateAndNextNull(m_sensesp); // In active else iterateAndNextNull(nodep->sensesp()); - putbs(" begin\n"); + putbs(" begin\n"); iterateAndNextNull(nodep->bodysp()); - putqs(nodep,"end\n"); + putqs(nodep, "end\n"); } virtual void visit(AstAlwaysPublic* nodep) { - putfs(nodep,"/*verilator public_flat_rw "); + putfs(nodep, "/*verilator public_flat_rw "); if (m_sensesp) iterateAndNextNull(m_sensesp); // In active else iterateAndNextNull(nodep->sensesp()); - putqs(nodep," "); + putqs(nodep, " "); iterateAndNextNull(nodep->bodysp()); - putqs(nodep,"*/\n"); + putqs(nodep, "*/\n"); } virtual void visit(AstNodeAssign* nodep) { iterateAndNextNull(nodep->lhsp()); - putfs(nodep," "+nodep->verilogKwd()+" "); + putfs(nodep, " "+nodep->verilogKwd()+" "); iterateAndNextNull(nodep->rhsp()); - if (!m_suppressSemi) puts(";\n"); + if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstAssignDly* nodep) { iterateAndNextNull(nodep->lhsp()); - putfs(nodep," <= "); + putfs(nodep, " <= "); iterateAndNextNull(nodep->rhsp()); - puts(";\n"); + puts(";\n"); } virtual void visit(AstAssignAlias* nodep) { - putbs("alias "); + putbs("alias "); iterateAndNextNull(nodep->lhsp()); - putfs(nodep," = "); + putfs(nodep, " = "); iterateAndNextNull(nodep->rhsp()); - if (!m_suppressSemi) puts(";\n"); + if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstAssignW* nodep) { - putfs(nodep,"assign "); + putfs(nodep, "assign "); iterateAndNextNull(nodep->lhsp()); - putbs(" = "); + putbs(" = "); iterateAndNextNull(nodep->rhsp()); - if (!m_suppressSemi) puts(";\n"); + if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstBreak* nodep) { - putbs("break"); - if (!m_suppressSemi) puts(";\n"); + putbs("break"); + if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstSenTree* nodep) { - // AstSenItem is called for dumping in isolation by V3Order - putfs(nodep,"@("); - for (AstNode* expp=nodep->sensesp(); expp; expp = expp->nextp()) { + // AstSenItem is called for dumping in isolation by V3Order + putfs(nodep, "@("); + for (AstNode* expp=nodep->sensesp(); expp; expp = expp->nextp()) { iterate(expp); - if (expp->nextp()) putqs(expp->nextp()," or "); - } - puts(")"); + if (expp->nextp()) putqs(expp->nextp(), " or "); + } + puts(")"); } virtual void visit(AstSenGate* nodep) { - emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->sensesp(), nodep->rhsp()); + emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->sensesp(), nodep->rhsp()); } virtual void visit(AstSenItem* nodep) { - putfs(nodep,""); - puts(nodep->edgeType().verilogKwd()); - if (nodep->sensp()) puts(" "); + putfs(nodep, ""); + puts(nodep->edgeType().verilogKwd()); + if (nodep->sensp()) puts(" "); iterateChildren(nodep); } virtual void visit(AstNodeCase* nodep) { - putfs(nodep,""); + putfs(nodep, ""); if (const AstCase* casep = VN_CAST(nodep, Case)) { - if (casep->priorityPragma()) puts("priority "); - if (casep->uniquePragma()) puts("unique "); - if (casep->unique0Pragma()) puts("unique0 "); - } - puts(nodep->verilogKwd()); - puts(" ("); + if (casep->priorityPragma()) puts("priority "); + if (casep->uniquePragma()) puts("unique "); + if (casep->unique0Pragma()) puts("unique0 "); + } + puts(nodep->verilogKwd()); + puts(" ("); iterateAndNextNull(nodep->exprp()); - puts(")\n"); + puts(")\n"); if (const AstCase* casep = VN_CAST(nodep, Case)) { - if (casep->fullPragma() || casep->parallelPragma()) { - puts(" // synopsys"); - if (casep->fullPragma()) puts(" full_case"); - if (casep->parallelPragma()) puts(" parallel_case"); - } - } + if (casep->fullPragma() || casep->parallelPragma()) { + puts(" // synopsys"); + if (casep->fullPragma()) puts(" full_case"); + if (casep->parallelPragma()) puts(" parallel_case"); + } + } iterateAndNextNull(nodep->itemsp()); - putqs(nodep,"endcase\n"); + putqs(nodep, "endcase\n"); } virtual void visit(AstCaseItem* nodep) { - if (nodep->condsp()) { + if (nodep->condsp()) { iterateAndNextNull(nodep->condsp()); - } else putbs("default"); - putfs(nodep,": begin "); + } else putbs("default"); + putfs(nodep, ": begin "); iterateAndNextNull(nodep->bodysp()); - putqs(nodep,"end\n"); + putqs(nodep, "end\n"); } virtual void visit(AstComment* nodep) { puts(string("// ")+nodep->name()+"\n"); iterateChildren(nodep); } virtual void visit(AstContinue* nodep) { - putbs("continue"); - if (!m_suppressSemi) puts(";\n"); + putbs("continue"); + if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstCoverDecl*) {} // N/A virtual void visit(AstCoverInc*) {} // N/A virtual void visit(AstCoverToggle*) {} // N/A - void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text, AstNode* exprsp) { - putfs(nodep,nodep->verilogKwd()); - putbs(" ("); + void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, + const string& text, AstNode* exprsp) { + putfs(nodep, nodep->verilogKwd()); + putbs(" ("); if (fileOrStrgp) { iterateAndNextNull(fileOrStrgp); putbs(","); } - putsQuoted(text); - for (AstNode* expp=exprsp; expp; expp = expp->nextp()) { - puts(","); + putsQuoted(text); + for (AstNode* expp=exprsp; expp; expp = expp->nextp()) { + puts(","); iterateAndNextNull(expp); - } - puts(");\n"); + } + puts(");\n"); } virtual void visit(AstDisable* nodep) { - putbs("disable "+nodep->name()+";\n"); + putbs("disable "+nodep->name()+";\n"); } virtual void visit(AstDisplay* nodep) { - visitNodeDisplay(nodep, nodep->filep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp()); + visitNodeDisplay(nodep, nodep->filep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp()); } virtual void visit(AstFScanF* nodep) { - visitNodeDisplay(nodep, nodep->filep(), nodep->text(), nodep->exprsp()); + visitNodeDisplay(nodep, nodep->filep(), nodep->text(), nodep->exprsp()); } virtual void visit(AstSScanF* nodep) { - visitNodeDisplay(nodep, nodep->fromp(), nodep->text(), nodep->exprsp()); + visitNodeDisplay(nodep, nodep->fromp(), nodep->text(), nodep->exprsp()); } virtual void visit(AstSFormat* nodep) { - visitNodeDisplay(nodep, nodep->lhsp(), nodep->fmtp()->text(), nodep->fmtp()->exprsp()); + visitNodeDisplay(nodep, nodep->lhsp(), nodep->fmtp()->text(), nodep->fmtp()->exprsp()); } virtual void visit(AstSFormatF* nodep) { - visitNodeDisplay(nodep, NULL, nodep->text(), nodep->exprsp()); + visitNodeDisplay(nodep, NULL, nodep->text(), nodep->exprsp()); } virtual void visit(AstFOpen* nodep) { - putfs(nodep,nodep->verilogKwd()); - putbs(" ("); + putfs(nodep, nodep->verilogKwd()); + putbs(" ("); if (nodep->filep()) iterateAndNextNull(nodep->filep()); - putbs(","); + putbs(","); if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep()); - putbs(","); + putbs(","); if (nodep->modep()) iterateAndNextNull(nodep->modep()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstFClose* nodep) { - putfs(nodep,nodep->verilogKwd()); - putbs(" ("); + putfs(nodep, nodep->verilogKwd()); + putbs(" ("); if (nodep->filep()) iterateAndNextNull(nodep->filep()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstFFlush* nodep) { - putfs(nodep,nodep->verilogKwd()); - putbs(" ("); + putfs(nodep, nodep->verilogKwd()); + putbs(" ("); if (nodep->filep()) iterateAndNextNull(nodep->filep()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstJumpGo* nodep) { putbs("disable "+cvtToHex(nodep->labelp())+";\n"); @@ -261,75 +262,75 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { virtual void visit(AstJumpLabel* nodep) { putbs("begin : "+cvtToHex(nodep)+"\n"); if (nodep->stmtsp()) iterateAndNextNull(nodep->stmtsp()); - puts("end\n"); + puts("end\n"); } virtual void visit(AstNodeReadWriteMem* nodep) { - putfs(nodep,nodep->verilogKwd()); - putbs(" ("); + putfs(nodep, nodep->verilogKwd()); + putbs(" ("); if (nodep->filenamep()) iterateAndNextNull(nodep->filenamep()); - putbs(","); + putbs(","); if (nodep->memp()) iterateAndNextNull(nodep->memp()); if (nodep->lsbp()) { putbs(","); iterateAndNextNull(nodep->lsbp()); } if (nodep->msbp()) { putbs(","); iterateAndNextNull(nodep->msbp()); } - puts(");\n"); + puts(");\n"); } virtual void visit(AstSysFuncAsTask* nodep) { iterateAndNextNull(nodep->lhsp()); puts(";\n"); } virtual void visit(AstSysIgnore* nodep) { - putfs(nodep,nodep->verilogKwd()); - putbs(" ("); + putfs(nodep, nodep->verilogKwd()); + putbs(" ("); iterateAndNextNull(nodep->exprsp()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstNodeFor* nodep) { - putfs(nodep,"for ("); - m_suppressSemi = true; + putfs(nodep, "for ("); + m_suppressSemi = true; iterateAndNextNull(nodep->initsp()); - puts(";"); + puts(";"); iterateAndNextNull(nodep->condp()); - puts(";"); + puts(";"); iterateAndNextNull(nodep->incsp()); - m_suppressSemi = false; - puts(") begin\n"); + m_suppressSemi = false; + puts(") begin\n"); iterateAndNextNull(nodep->bodysp()); - putqs(nodep,"end\n"); + putqs(nodep, "end\n"); } virtual void visit(AstRepeat* nodep) { - putfs(nodep,"repeat ("); + putfs(nodep, "repeat ("); iterateAndNextNull(nodep->countp()); - puts(") begin\n"); + puts(") begin\n"); iterateAndNextNull(nodep->bodysp()); - putfs(nodep,"end\n"); + putfs(nodep, "end\n"); } virtual void visit(AstWhile* nodep) { iterateAndNextNull(nodep->precondsp()); - putfs(nodep,"while ("); + putfs(nodep, "while ("); iterateAndNextNull(nodep->condp()); - puts(") begin\n"); + puts(") begin\n"); iterateAndNextNull(nodep->bodysp()); iterateAndNextNull(nodep->incsp()); iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop - putfs(nodep,"end\n"); + putfs(nodep, "end\n"); } virtual void visit(AstNodeIf* nodep) { - putfs(nodep,""); + putfs(nodep, ""); if (const AstIf* ifp = VN_CAST(nodep, If)) { - if (ifp->priorityPragma()) puts("priority "); - if (ifp->uniquePragma()) puts("unique "); - if (ifp->unique0Pragma()) puts("unique0 "); - } - puts("if ("); + if (ifp->priorityPragma()) puts("priority "); + if (ifp->uniquePragma()) puts("unique "); + if (ifp->unique0Pragma()) puts("unique0 "); + } + puts("if ("); iterateAndNextNull(nodep->condp()); - puts(") begin\n"); + puts(") begin\n"); iterateAndNextNull(nodep->ifsp()); - if (nodep->elsesp()) { - putqs(nodep,"end\n"); - putqs(nodep,"else begin\n"); + if (nodep->elsesp()) { + putqs(nodep, "end\n"); + putqs(nodep, "else begin\n"); iterateAndNextNull(nodep->elsesp()); - } - putqs(nodep,"end\n"); + } + putqs(nodep, "end\n"); } virtual void visit(AstPast* nodep) { putfs(nodep, "$past("); @@ -341,184 +342,184 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { puts(")"); } virtual void visit(AstReturn* nodep) { - putfs(nodep,"return "); + putfs(nodep, "return "); iterateAndNextNull(nodep->lhsp()); - puts(";\n"); + puts(";\n"); } virtual void visit(AstStop* nodep) { - putfs(nodep,"$stop;\n"); + putfs(nodep, "$stop;\n"); } virtual void visit(AstFinish* nodep) { - putfs(nodep,"$finish;\n"); + putfs(nodep, "$finish;\n"); } virtual void visit(AstText* nodep) { - putsNoTracking(nodep->text()); + putsNoTracking(nodep->text()); } virtual void visit(AstScopeName* nodep) { } virtual void visit(AstCStmt* nodep) { - putfs(nodep,"$_CSTMT("); + putfs(nodep, "$_CSTMT("); iterateAndNextNull(nodep->bodysp()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstCMath* nodep) { - putfs(nodep,"$_CMATH("); + putfs(nodep, "$_CMATH("); iterateAndNextNull(nodep->bodysp()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstUCStmt* nodep) { - putfs(nodep,"$c("); + putfs(nodep, "$c("); iterateAndNextNull(nodep->bodysp()); - puts(");\n"); + puts(");\n"); } virtual void visit(AstUCFunc* nodep) { - putfs(nodep,"$c("); + putfs(nodep, "$c("); iterateAndNextNull(nodep->bodysp()); - puts(")"); + puts(")"); } // Operators virtual void emitVerilogFormat(AstNode* nodep, const string& format, - AstNode* lhsp=NULL, AstNode* rhsp=NULL, AstNode* thsp=NULL) { - // Look at emitVerilog() format for term/uni/dual/triops, - // and write out appropriate text. - // %f Potential fileline-if-change and line break - // %l lhsp - if appropriate - // %r rhsp - if appropriate - // %t thsp - if appropriate - // %d dtypep - if appropriate - // %k Potential line break - bool inPct = false; - putbs(""); - for (string::const_iterator pos = format.begin(); pos != format.end(); ++pos) { - if (pos[0]=='%') { - inPct = true; - } else if (!inPct) { // Normal text - string s; s+=pos[0]; puts(s); - } else { // Format character - inPct = false; - switch (*pos) { - case '%': puts("%"); break; - case 'f': putfs(nodep,""); break; - case 'k': putbs(""); break; - case 'l': { - if (!lhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); } + AstNode* lhsp=NULL, AstNode* rhsp=NULL, AstNode* thsp=NULL) { + // Look at emitVerilog() format for term/uni/dual/triops, + // and write out appropriate text. + // %f Potential fileline-if-change and line break + // %l lhsp - if appropriate + // %r rhsp - if appropriate + // %t thsp - if appropriate + // %d dtypep - if appropriate + // %k Potential line break + bool inPct = false; + putbs(""); + for (string::const_iterator pos = format.begin(); pos != format.end(); ++pos) { + if (pos[0]=='%') { + inPct = true; + } else if (!inPct) { // Normal text + string s; s+=pos[0]; puts(s); + } else { // Format character + inPct = false; + switch (*pos) { + case '%': puts("%"); break; + case 'f': putfs(nodep, ""); break; + case 'k': putbs(""); break; + case 'l': { + if (!lhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); } else iterateAndNextNull(lhsp); - break; - } - case 'r': { - if (!rhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); } + break; + } + case 'r': { + if (!rhsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); } else iterateAndNextNull(rhsp); - break; - } - case 't': { - if (!thsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); } + break; + } + case 't': { + if (!thsp) { nodep->v3fatalSrc("emitVerilog() references undef node"); } else iterateAndNextNull(thsp); - break; - } - case 'd': { - if (!nodep->dtypep()) { nodep->v3fatalSrc("emitVerilog() references undef node"); } + break; + } + case 'd': { + if (!nodep->dtypep()) { nodep->v3fatalSrc("emitVerilog() references undef node"); } else iterateAndNextNull(nodep->dtypep()); - break; - } - default: - nodep->v3fatalSrc("Unknown emitVerilog format code: %"<v3fatalSrc("Unknown emitVerilog format code: %"<emitVerilog()); + emitVerilogFormat(nodep, nodep->emitVerilog()); } virtual void visit(AstNodeUniop* nodep) { - emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp()); + emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp()); } virtual void visit(AstNodeBiop* nodep) { - emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp()); + emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp()); } virtual void visit(AstNodeTriop* nodep) { - emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(), nodep->thsp()); + emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(), nodep->thsp()); } virtual void visit(AstAttrOf* nodep) { - putfs(nodep,"$_ATTROF("); + putfs(nodep, "$_ATTROF("); iterateAndNextNull(nodep->fromp()); - if (nodep->dimp()) { - putbs(","); + if (nodep->dimp()) { + putbs(","); iterateAndNextNull(nodep->dimp()); - } - puts(")"); + } + puts(")"); } virtual void visit(AstInitArray* nodep) { - putfs(nodep,"`{"); - int pos = 0; - for (AstNode* itemp = nodep->initsp(); itemp; ++pos, itemp=itemp->nextp()) { - int index = nodep->posIndex(pos); - puts(cvtToStr(index)); - puts(":"); + putfs(nodep, "`{"); + int pos = 0; + for (AstNode* itemp = nodep->initsp(); itemp; ++pos, itemp=itemp->nextp()) { + int index = nodep->posIndex(pos); + puts(cvtToStr(index)); + puts(":"); iterate(itemp); - if (itemp->nextp()) putbs(","); - } - puts("}"); + if (itemp->nextp()) putbs(","); + } + puts("}"); } virtual void visit(AstNodeCond* nodep) { - putbs("("); - iterateAndNextNull(nodep->condp()); putfs(nodep," ? "); + putbs("("); + iterateAndNextNull(nodep->condp()); putfs(nodep, " ? "); iterateAndNextNull(nodep->expr1p()); putbs(" : "); iterateAndNextNull(nodep->expr2p()); puts(")"); } virtual void visit(AstRange* nodep) { - puts("["); + puts("["); if (VN_IS(nodep->msbp(), Const) && VN_IS(nodep->lsbp(), Const)) { - // Looks nicer if we print [1:0] rather than [32'sh1:32sh0] + // Looks nicer if we print [1:0] rather than [32'sh1:32sh0] puts(cvtToStr(VN_CAST(nodep->leftp(), Const)->toSInt())); puts(":"); puts(cvtToStr(VN_CAST(nodep->rightp(), Const)->toSInt())); puts("]"); - } else { + } else { iterateAndNextNull(nodep->leftp()); puts(":"); iterateAndNextNull(nodep->rightp()); puts("]"); - } + } } virtual void visit(AstSel* nodep) { iterateAndNextNull(nodep->fromp()); puts("["); if (VN_IS(nodep->lsbp(), Const)) { - if (nodep->widthp()->isOne()) { + if (nodep->widthp()->isOne()) { if (VN_IS(nodep->lsbp(), Const)) { puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt())); - } else { + } else { iterateAndNextNull(nodep->lsbp()); - } - } else { + } + } else { puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt() + VN_CAST(nodep->widthp(), Const)->toSInt() - 1)); - puts(":"); + puts(":"); puts(cvtToStr(VN_CAST(nodep->lsbp(), Const)->toSInt())); - } - } else { - iterateAndNextNull(nodep->lsbp()); putfs(nodep,"+:"); + } + } else { + iterateAndNextNull(nodep->lsbp()); putfs(nodep, "+:"); iterateAndNextNull(nodep->widthp()); puts("]"); - } - puts("]"); + } + puts("]"); } virtual void visit(AstSliceSel* nodep) { iterateAndNextNull(nodep->fromp()); puts(cvtToStr(nodep->declRange())); } virtual void visit(AstTypedef* nodep) { - putfs(nodep,"typedef "); + putfs(nodep, "typedef "); iterateAndNextNull(nodep->dtypep()); puts(" "); - puts(nodep->prettyName()); - puts(";\n"); + puts(nodep->prettyName()); + puts(";\n"); } virtual void visit(AstBasicDType* nodep) { - if (nodep->isSigned()) putfs(nodep,"signed "); - putfs(nodep,nodep->prettyName()); + if (nodep->isSigned()) putfs(nodep, "signed "); + putfs(nodep, nodep->prettyName()); if (nodep->rangep()) { puts(" "); iterateAndNextNull(nodep->rangep()); puts(" "); } - else if (nodep->isRanged()) { puts(" ["); puts(cvtToStr(nodep->msb())); puts(":0] "); } + else if (nodep->isRanged()) { puts(" ["); puts(cvtToStr(nodep->msb())); puts(":0] "); } } virtual void visit(AstConstDType* nodep) { - putfs(nodep,"const "); + putfs(nodep, "const "); iterate(nodep->subDTypep()); } virtual void visit(AstNodeArrayDType* nodep) { @@ -526,24 +527,25 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { iterateAndNextNull(nodep->rangep()); } virtual void visit(AstNodeClassDType* nodep) { - puts(nodep->verilogKwd()+" "); - if (nodep->packed()) puts("packed "); - puts("\n"); + puts(nodep->verilogKwd()+" "); + if (nodep->packed()) puts("packed "); + puts("\n"); iterateAndNextNull(nodep->membersp()); - puts("}"); + puts("}"); } virtual void visit(AstMemberDType* nodep) { iterate(nodep->subDTypep()); - puts(" "); - puts(nodep->name()); - puts("}"); + puts(" "); + puts(nodep->name()); + puts("}"); } virtual void visit(AstNodeFTaskRef* nodep) { - if (nodep->dotted()!="") { putfs(nodep,nodep->dotted()); puts("."); puts(nodep->prettyName()); } - else { putfs(nodep,nodep->prettyName()); } - puts("("); + if (nodep->dotted()!="") { + putfs(nodep, nodep->dotted()); puts("."); puts(nodep->prettyName()); + } else { putfs(nodep, nodep->prettyName()); } + puts("("); iterateAndNextNull(nodep->pinsp()); - puts(")"); + puts(")"); } virtual void visit(AstArg* nodep) { iterateAndNextNull(nodep->exprp()); @@ -551,19 +553,19 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { // Terminals virtual void visit(AstVarRef* nodep) { if (nodep->varScopep()) { - putfs(nodep,nodep->varScopep()->prettyName()); + putfs(nodep, nodep->varScopep()->prettyName()); } else { - putfs(nodep,nodep->hiername()); - puts(nodep->varp()->prettyName()); - } + putfs(nodep, nodep->hiername()); + puts(nodep->varp()->prettyName()); + } } virtual void visit(AstVarXRef* nodep) { - putfs(nodep,nodep->dotted()); - puts("."); - puts(nodep->varp()->prettyName()); + putfs(nodep, nodep->dotted()); + puts("."); + puts(nodep->varp()->prettyName()); } virtual void visit(AstConst* nodep) { - putfs(nodep,nodep->num().ascii(true,true)); + putfs(nodep, nodep->num().ascii(true, true)); } // Just iterate @@ -574,16 +576,16 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { iterateChildren(nodep); } virtual void visit(AstVar* nodep) { - putfs(nodep,nodep->verilogKwd()); - puts(" "); + putfs(nodep, nodep->verilogKwd()); + puts(" "); iterate(nodep->dtypep()); puts(" "); - puts(nodep->prettyName()); - puts(";\n"); + puts(nodep->prettyName()); + puts(";\n"); } virtual void visit(AstActive* nodep) { - m_sensesp = nodep->sensesp(); + m_sensesp = nodep->sensesp(); iterateAndNextNull(nodep->stmtsp()); - m_sensesp = NULL; + m_sensesp = NULL; } virtual void visit(AstVarScope*) {} virtual void visit(AstNodeText*) {} @@ -591,19 +593,20 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { virtual void visit(AstTraceInc*) {} // NOPs virtual void visit(AstPragma*) {} - virtual void visit(AstCell*) {} // Handled outside the Visit class + virtual void visit(AstCell*) {} // Handled outside the Visit class // Default virtual void visit(AstNode* nodep) { puts(string("\n???? // ")+nodep->prettyTypeName()+"\n"); iterateChildren(nodep); - // Not v3fatalSrc so we keep processing - nodep->v3error("Internal: Unknown node type reached emitter: "<prettyTypeName()); + // Not v3fatalSrc so we keep processing + nodep->v3error("Internal: Unknown node type reached emitter: "<prettyTypeName()); } public: - explicit EmitVBaseVisitor(AstSenTree* domainp=NULL) { // Domain for printing one a ALWAYS under a ACTIVE - m_suppressSemi = false; - m_sensesp = domainp; + explicit EmitVBaseVisitor(AstSenTree* domainp=NULL) { + // Domain for printing one a ALWAYS under a ACTIVE + m_suppressSemi = false; + m_sensesp = domainp; } virtual ~EmitVBaseVisitor() {} }; @@ -613,9 +616,9 @@ public: class EmitVFileVisitor : public EmitVBaseVisitor { // MEMBERS - V3OutFile* m_ofp; + V3OutFile* m_ofp; // METHODS - V3OutFile* ofp() const { return m_ofp; } + V3OutFile* ofp() const { return m_ofp; } virtual void puts(const string& str) { ofp()->puts(str); } virtual void putbs(const string& str) { ofp()->putbs(str); } virtual void putfs(AstNode*, const string& str) { putbs(str); } @@ -623,7 +626,7 @@ class EmitVFileVisitor : public EmitVBaseVisitor { virtual void putsNoTracking(const string& str) { ofp()->putsNoTracking(str); } public: EmitVFileVisitor(AstNode* nodep, V3OutFile* ofp) { - m_ofp = ofp; + m_ofp = ofp; iterate(nodep); } virtual ~EmitVFileVisitor() {} @@ -643,7 +646,7 @@ class EmitVStreamVisitor : public EmitVBaseVisitor { virtual void putqs(AstNode*, const string& str) { putbs(str); } public: EmitVStreamVisitor(AstNode* nodep, std::ostream& os) - : m_os(os) { + : m_os(os) { iterate(nodep); } virtual ~EmitVStreamVisitor() {} @@ -654,67 +657,67 @@ public: class EmitVPrefixedFormatter : public V3OutFormatter { std::ostream& m_os; - string m_prefix; // What to print at beginning of each line - int m_flWidth; // Padding of fileline - int m_column; // Rough location; need just zero or non-zero - FileLine* m_prefixFl; + string m_prefix; // What to print at beginning of each line + int m_flWidth; // Padding of fileline + int m_column; // Rough location; need just zero or non-zero + FileLine* m_prefixFl; // METHODS virtual void putcOutput(char chr) { - if (chr == '\n') { - m_column = 0; - m_os<ascii()+":"; - m_os<ascii().length()+1)); - m_os<<" "; - m_os<ascii()+":"; + m_os<ascii().length()+1)); + m_os<<" "; + m_os<fileline(); // NETLIST's fileline instead of NULL to avoid NULL checks + : V3OutFormatter("__STREAM", V3OutFormatter::LA_VERILOG) + , m_os(os), m_prefix(prefix), m_flWidth(flWidth) { + m_column = 0; + m_prefixFl = v3Global.rootp()->fileline(); // NETLIST's fileline instead of NULL to avoid NULL checks } virtual ~EmitVPrefixedFormatter() { - if (m_column) puts("\n"); + if (m_column) puts("\n"); } }; class EmitVPrefixedVisitor : public EmitVBaseVisitor { // MEMBERS - EmitVPrefixedFormatter m_formatter; // Special verilog formatter (Way down the inheritance is another unused V3OutFormatter) + EmitVPrefixedFormatter m_formatter; // Special verilog formatter (Way down the inheritance is another unused V3OutFormatter) // METHODS virtual void putsNoTracking(const string& str) { m_formatter.putsNoTracking(str); } virtual void puts(const string& str) { m_formatter.puts(str); } // We don't use m_formatter's putbs because the tokens will change filelines // and insert returns at the proper locations virtual void putbs(const string& str) { m_formatter.puts(str); } - virtual void putfs(AstNode* nodep, const string& str) { putfsqs(nodep,str,false); } - virtual void putqs(AstNode* nodep, const string& str) { putfsqs(nodep,str,true); } + virtual void putfs(AstNode* nodep, const string& str) { putfsqs(nodep, str, false); } + virtual void putqs(AstNode* nodep, const string& str) { putfsqs(nodep, str, true); } void putfsqs(AstNode* nodep, const string& str, bool quiet) { - if (m_formatter.prefixFl() != nodep->fileline()) { - m_formatter.prefixFl(nodep->fileline()); - if (m_formatter.column()) puts("\n"); // This in turn will print the m_prefixFl - } - if (!quiet && nodep->user3()) puts("%%"); - putbs(str); + if (m_formatter.prefixFl() != nodep->fileline()) { + m_formatter.prefixFl(nodep->fileline()); + if (m_formatter.column()) puts("\n"); // This in turn will print the m_prefixFl + } + if (!quiet && nodep->user3()) puts("%%"); + putbs(str); } public: EmitVPrefixedVisitor(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, - AstSenTree* domainp, bool user3mark) - : EmitVBaseVisitor(domainp), m_formatter(os, prefix, flWidth) { - if (user3mark) { AstUser3InUse::check(); } + AstSenTree* domainp, bool user3mark) + : EmitVBaseVisitor(domainp), m_formatter(os, prefix, flWidth) { + if (user3mark) { AstUser3InUse::check(); } iterate(nodep); } virtual ~EmitVPrefixedVisitor() {} @@ -726,19 +729,20 @@ public: void V3EmitV::emitv() { UINFO(2,__FUNCTION__<<": "<modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) { - V3OutVFile of (v3Global.opt.makeDir() - +"/"+EmitCBaseVisitor::modClassName(modp)+"__Vout.v"); - of.putsHeader(); - EmitVFileVisitor visitor (modp, &of); - } + // Process each module in turn + for (AstNodeModule* modp = v3Global.rootp()->modulesp(); + modp; modp=VN_CAST(modp->nextp(), NodeModule)) { + V3OutVFile of (v3Global.opt.makeDir() + +"/"+EmitCBaseVisitor::modClassName(modp)+"__Vout.v"); + of.putsHeader(); + EmitVFileVisitor visitor (modp, &of); + } } } @@ -746,7 +750,8 @@ void V3EmitV::verilogForTree(AstNode* nodep, std::ostream& os) { EmitVStreamVisitor(nodep, os); } -void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, - AstSenTree* domainp, bool user3mark) { +void V3EmitV::verilogPrefixedTree(AstNode* nodep, std::ostream& os, + const string& prefix, int flWidth, + AstSenTree* domainp, bool user3mark) { EmitVPrefixedVisitor(nodep, os, prefix, flWidth, domainp, user3mark); } diff --git a/src/V3EmitV.h b/src/V3EmitV.h index 9be741ca3..d031945ef 100644 --- a/src/V3EmitV.h +++ b/src/V3EmitV.h @@ -33,8 +33,9 @@ class V3EmitV { public: static void emitv(); static void verilogForTree(AstNode* nodep, std::ostream& os=std::cout); - static void verilogPrefixedTree(AstNode* nodep, std::ostream& os, const string& prefix, int flWidth, + static void verilogPrefixedTree(AstNode* nodep, std::ostream& os, + const string& prefix, int flWidth, AstSenTree* domainp, bool user3mark); }; -#endif // Guard +#endif // Guard diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index ff76d44be..79f7d86cf 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -41,78 +41,80 @@ class EmitXmlFileVisitor : public AstNVisitor { // AstNode::user1 -> uint64_t, number to connect crossrefs // MEMBERS - V3OutFile* m_ofp; - uint64_t m_id; + V3OutFile* m_ofp; + uint64_t m_id; // METHODS VL_DEBUG_FUNC; // Declare debug() // Outfile methods - V3OutFile* ofp() const { return m_ofp; } + V3OutFile* ofp() const { return m_ofp; } virtual void puts(const string& str) { ofp()->puts(str); } virtual void putbs(const string& str) { ofp()->putbs(str); } virtual void putfs(AstNode*, const string& str) { putbs(str); } virtual void putqs(AstNode*, const string& str) { putbs(str); } virtual void putsNoTracking(const string& str) { ofp()->putsNoTracking(str); } virtual void putsQuoted(const string& str) { - // Quote \ and " for use inside C programs - // Don't use to quote a filename for #include - #include doesn't \ escape. - // Duplicate in V3File - here so we can print to string - putsNoTracking("\""); + // Quote \ and " for use inside C programs + // Don't use to quote a filename for #include - #include doesn't \ escape. + // Duplicate in V3File - here so we can print to string + putsNoTracking("\""); putsNoTracking(V3OutFormatter::quoteNameControls(str)); - putsNoTracking("\""); + putsNoTracking("\""); } // XML methods void outputId(AstNode* nodep) { - if (!nodep->user1()) { nodep->user1(++m_id); } - puts("\""+cvtToStr(nodep->user1())+"\""); + if (!nodep->user1()) { nodep->user1(++m_id); } + puts("\""+cvtToStr(nodep->user1())+"\""); } void outputTag(AstNode* nodep, string tag) { - if (tag=="") tag = VString::downcase(nodep->typeName()); - puts("<"+tag+" "+nodep->fileline()->xml()); + if (tag=="") tag = VString::downcase(nodep->typeName()); + puts("<"+tag+" "+nodep->fileline()->xml()); if (VN_IS(nodep, NodeDType)) { puts(" id="); outputId(nodep); } - if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); } - if (nodep->tag()!="") { puts(" tag="); putsQuoted(nodep->tag()); } + if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); } + if (nodep->tag()!="") { puts(" tag="); putsQuoted(nodep->tag()); } if (AstNodeDType* dtp = VN_CAST(nodep, NodeDType)) { - if (dtp->subDTypep()) { puts(" sub_dtype_id="); outputId(dtp->subDTypep()->skipRefp()); } - } else { - if (nodep->dtypep()) { puts(" dtype_id="); outputId(nodep->dtypep()->skipRefp()); } - } + if (dtp->subDTypep()) { + puts(" sub_dtype_id="); outputId(dtp->subDTypep()->skipRefp()); + } + } else { + if (nodep->dtypep()) { puts(" dtype_id="); outputId(nodep->dtypep()->skipRefp()); } + } } void outputChildrenEnd(AstNode* nodep, string tag) { - if (tag=="") tag = VString::downcase(nodep->typeName()); - if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) { - puts(">\n"); + if (tag=="") tag = VString::downcase(nodep->typeName()); + if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) { + puts(">\n"); iterateChildren(nodep); - puts("\n"); - } else { - puts("/>\n"); - } + puts("\n"); + } else { + puts("/>\n"); + } } // VISITORS virtual void visit(AstAssignW* nodep) { - outputTag(nodep, "contassign"); // IEEE: vpiContAssign + outputTag(nodep, "contassign"); // IEEE: vpiContAssign outputChildrenEnd(nodep, "contassign"); } virtual void visit(AstCell* nodep) { - outputTag(nodep, "instance"); // IEEE: vpiInstance + outputTag(nodep, "instance"); // IEEE: vpiInstance puts(" defName="); putsQuoted(nodep->modName()); // IEEE vpiDefName puts(" origName="); putsQuoted(nodep->origName()); outputChildrenEnd(nodep, "instance"); } virtual void visit(AstNetlist* nodep) { - puts("\n"); + puts("\n"); iterateChildren(nodep); - puts("\n"); + puts("\n"); } virtual void visit(AstNodeModule* nodep) { - outputTag(nodep, ""); - puts(" origName="); putsQuoted(nodep->origName()); - if (nodep->level()==1 || nodep->level()==2) // ==2 because we don't add wrapper when in XML mode - puts(" topModule=\"1\""); // IEEE vpiTopModule - outputChildrenEnd(nodep, ""); + outputTag(nodep, ""); + puts(" origName="); putsQuoted(nodep->origName()); + if (nodep->level()==1 || nodep->level()==2) // ==2 because we don't add wrapper when in XML mode + puts(" topModule=\"1\""); // IEEE vpiTopModule + outputChildrenEnd(nodep, ""); } virtual void visit(AstVar* nodep) { AstVarType typ = nodep->varType(); @@ -126,8 +128,8 @@ class EmitXmlFileVisitor : public AstNVisitor { } else { puts(" vartype="); putsQuoted(!vt.empty() ? vt : kw); } - puts(" origName="); putsQuoted(nodep->origName()); - outputChildrenEnd(nodep, ""); + puts(" origName="); putsQuoted(nodep->origName()); + outputChildrenEnd(nodep, ""); } virtual void visit(AstPin* nodep) { // What we call a pin in verilator is a port in the IEEE spec. @@ -159,12 +161,12 @@ class EmitXmlFileVisitor : public AstNVisitor { // Data types virtual void visit(AstBasicDType* nodep) { - outputTag(nodep, "basicdtype "); - if (nodep->isRanged()) { - puts(" left=\""+cvtToStr(nodep->left())+"\""); - puts(" right=\""+cvtToStr(nodep->right())+"\""); - } - puts("/>\n"); + outputTag(nodep, "basicdtype "); + if (nodep->isRanged()) { + puts(" left=\""+cvtToStr(nodep->left())+"\""); + puts(" right=\""+cvtToStr(nodep->right())+"\""); + } + puts("/>\n"); } virtual void visit(AstIfaceRefDType* nodep) { string mpn; @@ -193,13 +195,13 @@ class EmitXmlFileVisitor : public AstNVisitor { // Default virtual void visit(AstNode* nodep) { - outputTag(nodep, ""); - outputChildrenEnd(nodep, ""); + outputTag(nodep, ""); + outputChildrenEnd(nodep, ""); } public: EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) { - m_ofp = ofp; - m_id = 0; + m_ofp = ofp; + m_id = 0; iterate(nodep); } virtual ~EmitXmlFileVisitor() {} @@ -336,9 +338,9 @@ void V3EmitXml::emitxml() { of.puts("\n"); of.puts("\n"); { - std::stringstream sstr; - FileLine::fileNameNumMapDumpXml(sstr); - of.puts(sstr.str()); + std::stringstream sstr; + FileLine::fileNameNumMapDumpXml(sstr); + of.puts(sstr.str()); } { std::stringstream sstr; diff --git a/src/V3EmitXml.h b/src/V3EmitXml.h index 8ca2e4225..e28be4762 100644 --- a/src/V3EmitXml.h +++ b/src/V3EmitXml.h @@ -34,4 +34,4 @@ public: static void emitxml(); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Error.cpp b/src/V3Error.cpp index ffcb249e5..bd9b423a5 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -57,9 +57,9 @@ V3ErrorCode::V3ErrorCode(const char* msgp) { // Return error encoding for given string, or ERROR, which is a bad code for (int codei=V3ErrorCode::EC_MIN; codei=V3ErrorCode::EC_MIN) { #ifndef _V3ERROR_NO_GLOBAL_ - V3Stats::addStatSum(string("Warnings, Suppressed ")+s_errorCode.ascii(), 1); + V3Stats::addStatSum(string("Warnings, Suppressed ")+s_errorCode.ascii(), 1); #endif - s_errorSuppressed = true; + s_errorSuppressed = true; } } @@ -166,8 +167,8 @@ void V3Error::v3errorEnd(std::ostringstream& sstr) { #endif // Skip suppressed messages if (s_errorSuppressed - // On debug, show only non default-off warning to prevent pages of warnings - && (!debug() || s_errorCode.defaultsOff())) return; + // On debug, show only non default-off warning to prevent pages of warnings + && (!debug() || s_errorCode.defaultsOff())) return; string msg = msgPrefix()+sstr.str(); if (msg[msg.length()-1] != '\n') msg += '\n'; // Suppress duplicates @@ -176,57 +177,57 @@ void V3Error::v3errorEnd(std::ostringstream& sstr) { // Output std::cerr<=V3ErrorCode::EC_FIRST_WARN && !s_describedWarnings) { + || s_errorCode==V3ErrorCode::USERINFO)) { + if (!s_describedEachWarn[s_errorCode] + && !s_pretendError[s_errorCode]) { + s_describedEachWarn[s_errorCode] = true; + if (s_errorCode>=V3ErrorCode::EC_FIRST_WARN && !s_describedWarnings) { std::cerr<dumpTreeFile(v3Global.debugFilename("final.tree",990)); - if (s_errorExitCb) s_errorExitCb(); - V3Stats::statsFinalAll(v3Global.rootp()); - V3Stats::statsReport(); - } + if (debug()) { + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree", 990)); + if (s_errorExitCb) s_errorExitCb(); + V3Stats::statsFinalAll(v3Global.rootp()); + V3Stats::statsReport(); + } #endif - } + } - vlAbort(); - } - else if (isError(s_errorCode, s_errorSuppressed)) { - // We don't dump tree on any error because a Visitor may be in middle of - // a tree cleanup and cause a false broken problem. - if (s_errorExitCb) s_errorExitCb(); - } + vlAbort(); + } + else if (isError(s_errorCode, s_errorSuppressed)) { + // We don't dump tree on any error because a Visitor may be in middle of + // a tree cleanup and cause a false broken problem. + if (s_errorExitCb) s_errorExitCb(); + } } } diff --git a/src/V3Error.h b/src/V3Error.h index d1685d92c..426e2d2b3 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -36,86 +36,86 @@ class V3ErrorCode { public: enum en { - EC_MIN=0, // Keep first - // - EC_INFO, // General information out - EC_FATAL, // Kill the program - EC_FATALSRC, // Kill the program, for internal source errors - EC_ERROR, // General error out, can't suppress - // Boolean information we track per-line, but aren't errors - I_COVERAGE, // Coverage is on/off from /*verilator coverage_on/off*/ - I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/ - I_LINT, // All lint messages - I_DEF_NETTYPE_WIRE, // `default_nettype is WIRE (false=NONE) - // Error codes: - E_DETECTARRAY, // Error: Unsupported: Can't detect changes on arrayed variable + EC_MIN=0, // Keep first + // + EC_INFO, // General information out + EC_FATAL, // Kill the program + EC_FATALSRC, // Kill the program, for internal source errors + EC_ERROR, // General error out, can't suppress + // Boolean information we track per-line, but aren't errors + I_COVERAGE, // Coverage is on/off from /*verilator coverage_on/off*/ + I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/ + I_LINT, // All lint messages + I_DEF_NETTYPE_WIRE, // `default_nettype is WIRE (false=NONE) + // Error codes: + E_DETECTARRAY, // Error: Unsupported: Can't detect changes on arrayed variable E_MULTITOP, // Error: Multiple top level modules E_PORTSHORT, // Error: Output port is connected to a constant, electrical short E_TASKNSVAR, // Error: Task I/O not simple - // - // Warning codes: - EC_FIRST_WARN, // Just a code so the program knows where to start warnings - // - ALWCOMBORDER, // Always_comb with unordered statements - ASSIGNDLY, // Assignment delays - ASSIGNIN, // Assigning to input - BLKANDNBLK, // Blocked and non-blocking assignments to same variable - BLKLOOPINIT, // Delayed assignment to array inside for loops - BLKSEQ, // Blocking assignments in sequential block - BSSPACE, // Backslash space - CASEINCOMPLETE, // Case statement has missing values - CASEOVERLAP, // Case statements overlap - CASEWITHX, // Case with X values - CASEX, // Casex - CDCRSTLOGIC, // Logic in async reset path - CLKDATA, // Clock used as data - CMPCONST, // Comparison is constant due to limited range - COLONPLUS, // :+ instead of +: + // + // Warning codes: + EC_FIRST_WARN, // Just a code so the program knows where to start warnings + // + ALWCOMBORDER, // Always_comb with unordered statements + ASSIGNDLY, // Assignment delays + ASSIGNIN, // Assigning to input + BLKANDNBLK, // Blocked and non-blocking assignments to same variable + BLKLOOPINIT, // Delayed assignment to array inside for loops + BLKSEQ, // Blocking assignments in sequential block + BSSPACE, // Backslash space + CASEINCOMPLETE, // Case statement has missing values + CASEOVERLAP, // Case statements overlap + CASEWITHX, // Case with X values + CASEX, // Casex + CDCRSTLOGIC, // Logic in async reset path + CLKDATA, // Clock used as data + CMPCONST, // Comparison is constant due to limited range + COLONPLUS, // :+ instead of +: COMBDLY, // Combinatorial delayed assignment CONTASSREG, // Continuous assignment on reg DEFPARAM, // Style: Defparam DECLFILENAME, // Declaration doesn't match filename - ENDLABEL, // End lable name mismatch - GENCLK, // Generated Clock - IFDEPTH, // If statements too deep + ENDLABEL, // End lable name mismatch + GENCLK, // Generated Clock + IFDEPTH, // If statements too deep IGNOREDRETURN, // Ignoring return value (funcation as task) IMPERFECTSCH, // Imperfect schedule (disabled by default) IMPLICIT, // Implicit wire IMPORTSTAR, // Import::* in $unit IMPURE, // Impure function not being inlined - INCABSPATH, // Include has absolute path + INCABSPATH, // Include has absolute path INFINITELOOP, // Infinite loop - INITIALDLY, // Initial delayed statement - LITENDIAN, // Little bit endian vector - MODDUP, // Duplicate module - MULTIDRIVEN, // Driven from multiple blocks - PINMISSING, // Cell pin not specified - PINNOCONNECT, // Cell pin not connected + INITIALDLY, // Initial delayed statement + LITENDIAN, // Little bit endian vector + MODDUP, // Duplicate module + MULTIDRIVEN, // Driven from multiple blocks + PINMISSING, // Cell pin not specified + PINNOCONNECT, // Cell pin not connected PINCONNECTEMPTY,// Cell pin connected by name with empty reference PROCASSWIRE, // Procedural assignment on wire REALCVT, // Real conversion - REDEFMACRO, // Redefining existing define macro - SELRANGE, // Selection index out of range - STMTDLY, // Delayed statement - SYMRSVDWORD, // Symbol is Reserved Word - SYNCASYNCNET, // Mixed sync + async reset + REDEFMACRO, // Redefining existing define macro + SELRANGE, // Selection index out of range + STMTDLY, // Delayed statement + SYMRSVDWORD, // Symbol is Reserved Word + SYNCASYNCNET, // Mixed sync + async reset TICKCOUNT, // Too large tick count - UNDRIVEN, // No drivers - UNOPT, // Unoptimizable block - UNOPTFLAT, // Unoptimizable block after flattening + UNDRIVEN, // No drivers + UNOPT, // Unoptimizable block + UNOPTFLAT, // Unoptimizable block after flattening UNOPTTHREADS, // Thread partitioner unable to fill all requested threads - UNPACKED, // Unsupported unpacked - UNSIGNED, // Comparison is constant due to unsigned arithmetic - UNUSED, // No receivers - USERERROR, // Elaboration time $error - USERFATAL, // Elaboration time $fatal - USERINFO, // Elaboration time $info - USERWARN, // Elaboration time $warning - VARHIDDEN, // Hiding variable - WIDTH, // Width mismatch - WIDTHCONCAT, // Unsized numbers/parameters in concatenations - _ENUM_MAX - // ***Add new elements below also*** + UNPACKED, // Unsupported unpacked + UNSIGNED, // Comparison is constant due to unsigned arithmetic + UNUSED, // No receivers + USERERROR, // Elaboration time $error + USERFATAL, // Elaboration time $fatal + USERINFO, // Elaboration time $info + USERWARN, // Elaboration time $warning + VARHIDDEN, // Hiding variable + WIDTH, // Width mismatch + WIDTHCONCAT, // Unsized numbers/parameters in concatenations + _ENUM_MAX + // ***Add new elements below also*** }; enum en m_e; inline V3ErrorCode() : m_e(EC_MIN) {} @@ -125,17 +125,17 @@ public: explicit inline V3ErrorCode(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } const char* ascii() const { - const char* names[] = { - // Leading spaces indicate it can't be disabled. - " MIN", " INFO", " FATAL", " FATALSRC", " ERROR", - // Boolean - " I_COVERAGE", " I_TRACING", " I_LINT", " I_DEF_NETTYPE_WIRE", + const char* names[] = { + // Leading spaces indicate it can't be disabled. + " MIN", " INFO", " FATAL", " FATALSRC", " ERROR", + // Boolean + " I_COVERAGE", " I_TRACING", " I_LINT", " I_DEF_NETTYPE_WIRE", // Errors "DETECTARRAY", "MULTITOP", "PORTSHORT", "TASKNSVAR", // Warnings - " EC_FIRST_WARN", - "ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN", - "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", + " EC_FIRST_WARN", + "ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN", + "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CLKDATA", "CMPCONST", "COLONPLUS", "COMBDLY", "CONTASSREG", "DEFPARAM", "DECLFILENAME", @@ -146,16 +146,16 @@ public: "LITENDIAN", "MODDUP", "MULTIDRIVEN", "PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY", "PROCASSWIRE", - "REALCVT", "REDEFMACRO", - "SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", + "REALCVT", "REDEFMACRO", + "SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSED", - "USERERROR", "USERFATAL", "USERINFO", "USERWARN", - "VARHIDDEN", "WIDTH", "WIDTHCONCAT", - " MAX" - }; - return names[m_e]; + "USERERROR", "USERFATAL", "USERINFO", "USERWARN", + "VARHIDDEN", "WIDTH", "WIDTHCONCAT", + " MAX" + }; + return names[m_e]; } // Warnings that default to off bool defaultsOff() const { return ( m_e==IMPERFECTSCH || styleError()); } @@ -170,39 +170,39 @@ public: || m_e==PROCASSWIRE); } // Warnings to mention manual bool mentionManual() const { return ( m_e==EC_FATALSRC || m_e==SYMRSVDWORD - || pretendError() ); } + || pretendError() ); } // Warnings that are lint only bool lintError() const { return ( m_e==ALWCOMBORDER - || m_e==BSSPACE - || m_e==CASEINCOMPLETE || m_e==CASEOVERLAP - || m_e==CASEWITHX || m_e==CASEX - || m_e==CMPCONST - || m_e==COLONPLUS - || m_e==ENDLABEL - || m_e==IMPLICIT - || m_e==LITENDIAN - || m_e==PINMISSING - || m_e==REALCVT - || m_e==UNSIGNED - || m_e==WIDTH); } + || m_e==BSSPACE + || m_e==CASEINCOMPLETE || m_e==CASEOVERLAP + || m_e==CASEWITHX || m_e==CASEX + || m_e==CMPCONST + || m_e==COLONPLUS + || m_e==ENDLABEL + || m_e==IMPLICIT + || m_e==LITENDIAN + || m_e==PINMISSING + || m_e==REALCVT + || m_e==UNSIGNED + || m_e==WIDTH); } // Warnings that are style only bool styleError() const { return ( m_e==ASSIGNDLY // More than style, but for backward compatibility - || m_e==BLKSEQ - || m_e==DEFPARAM - || m_e==DECLFILENAME + || m_e==BLKSEQ + || m_e==DEFPARAM + || m_e==DECLFILENAME || m_e==IMPORTSTAR || m_e==INCABSPATH || m_e==PINCONNECTEMPTY || m_e==PINNOCONNECT - || m_e==SYNCASYNCNET - || m_e==UNDRIVEN - || m_e==UNUSED - || m_e==VARHIDDEN ); } + || m_e==SYNCASYNCNET + || m_e==UNDRIVEN + || m_e==UNUSED + || m_e==VARHIDDEN ); } }; - inline bool operator== (V3ErrorCode lhs, V3ErrorCode rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (V3ErrorCode lhs, V3ErrorCode::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (V3ErrorCode::en lhs, V3ErrorCode rhs) { return (lhs == rhs.m_e); } + inline bool operator==(V3ErrorCode lhs, V3ErrorCode rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(V3ErrorCode lhs, V3ErrorCode::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(V3ErrorCode::en lhs, V3ErrorCode rhs) { return (lhs == rhs.m_e); } inline std::ostream& operator<<(std::ostream& os, V3ErrorCode rhs) { return os< bool. Processed - AstUser1InUse m_inuser1; + // AstNode::user1() -> bool. Processed + AstUser1InUse m_inuser1; // STATE - AstNode* m_stmtp; // Current statement + AstNode* m_stmtp; // Current statement // METHODS VL_DEBUG_FUNC; // Declare debug() int longOrQuadWidth(AstNode* nodep) { - // Return 32 or 64... - return (nodep->width()+(VL_WORDSIZE-1)) & ~(VL_WORDSIZE-1); + // Return 32 or 64... + return (nodep->width()+(VL_WORDSIZE-1)) & ~(VL_WORDSIZE-1); } V3Number notWideMask(AstNode* nodep) { return V3Number(nodep, VL_WORDSIZE, ~VL_MASK_I(nodep->widthMin())); @@ -66,22 +66,22 @@ private: return V3Number(nodep, VL_WORDSIZE, VL_MASK_I(nodep->widthMin())); } else { V3Number mask (nodep, longOrQuadWidth(nodep)); - mask.setMask(nodep->widthMin()); - return mask; - } + mask.setMask(nodep->widthMin()); + return mask; + } } void insertBefore(AstNode* placep, AstNode* newp) { - newp->user1(1); // Already processed, don't need to re-iterate - AstNRelinker linker; - placep->unlinkFrBack(&linker); - newp->addNext(placep); - linker.relink(newp); + newp->user1(1); // Already processed, don't need to re-iterate + AstNRelinker linker; + placep->unlinkFrBack(&linker); + newp->addNext(placep); + linker.relink(newp); } void replaceWithDelete(AstNode* nodep, AstNode* newp) { - newp->user1(1); // Already processed, don't need to re-iterate - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); + newp->user1(1); // Already processed, don't need to re-iterate + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); } AstNode* newWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { AstAssign* newp = new AstAssign(placep->fileline(), @@ -90,7 +90,7 @@ private: new AstConst(placep->fileline(), word)), rhsp); - return newp; + return newp; } void addWordAssign(AstNodeAssign* placep, int word, AstNode* lhsp, AstNode* rhsp) { insertBefore(placep, newWordAssign(placep, word, lhsp, rhsp)); @@ -100,321 +100,330 @@ private: } void fixCloneLvalue(AstNode* nodep) { - // In AstSel transforms, we call clone() on VarRefs that were lvalues, - // but are now being used on the RHS of the assignment + // In AstSel transforms, we call clone() on VarRefs that were lvalues, + // but are now being used on the RHS of the assignment if (VN_IS(nodep, VarRef)) VN_CAST(nodep, VarRef)->lvalue(false); - // Iterate - if (nodep->op1p()) fixCloneLvalue(nodep->op1p()); - if (nodep->op2p()) fixCloneLvalue(nodep->op2p()); - if (nodep->op3p()) fixCloneLvalue(nodep->op3p()); - if (nodep->op4p()) fixCloneLvalue(nodep->op4p()); + // Iterate + if (nodep->op1p()) fixCloneLvalue(nodep->op1p()); + if (nodep->op2p()) fixCloneLvalue(nodep->op2p()); + if (nodep->op3p()) fixCloneLvalue(nodep->op3p()); + if (nodep->op4p()) fixCloneLvalue(nodep->op4p()); } AstNode* newAstWordSelClone(AstNode* nodep, int word) { - // Get the specified word number from a wide array - // Or, if it's a long/quad, do appropriate conversion to wide - // Concat may pass negative word numbers, that means it wants a zero - if (nodep->isWide() && word>=0 && wordwidthWords()) { + // Get the specified word number from a wide array + // Or, if it's a long/quad, do appropriate conversion to wide + // Concat may pass negative word numbers, that means it wants a zero + if (nodep->isWide() && word>=0 && wordwidthWords()) { return new AstWordSel(nodep->fileline(), nodep->cloneTree(true), new AstConst(nodep->fileline(), word)); - } else if (nodep->isQuad() && word==0) { - AstNode* quadfromp = nodep->cloneTree(true); - quadfromp->dtypeSetBitSized(VL_QUADSIZE,quadfromp->widthMin(),AstNumeric::UNSIGNED); + } else if (nodep->isQuad() && word==0) { + AstNode* quadfromp = nodep->cloneTree(true); + quadfromp->dtypeSetBitSized(VL_QUADSIZE, quadfromp->widthMin(), AstNumeric::UNSIGNED); return new AstCCast(nodep->fileline(), quadfromp, VL_WORDSIZE); - } else if (nodep->isQuad() && word==1) { - AstNode* quadfromp = nodep->cloneTree(true); - quadfromp->dtypeSetBitSized(VL_QUADSIZE,quadfromp->widthMin(),AstNumeric::UNSIGNED); + } else if (nodep->isQuad() && word==1) { + AstNode* quadfromp = nodep->cloneTree(true); + quadfromp->dtypeSetBitSized(VL_QUADSIZE, quadfromp->widthMin(), AstNumeric::UNSIGNED); return new AstCCast(nodep->fileline(), new AstShiftR(nodep->fileline(), quadfromp, new AstConst(nodep->fileline(), VL_WORDSIZE), VL_WORDSIZE), VL_WORDSIZE); - } else if (!nodep->isWide() && !nodep->isQuad() && word==0) { - return nodep->cloneTree(true); - } else { // Out of bounds + } else if (!nodep->isWide() && !nodep->isQuad() && word==0) { + return nodep->cloneTree(true); + } else { // Out of bounds return new AstConst(nodep->fileline(), 0); - } + } } AstNode* newWordGrabShift(FileLine* fl, int word, AstNode* lhsp, int shift) { - // Extract the expression to grab the value for the specified word, if it's the shift - // of shift bits from lhsp - AstNode* newp; - // Negative word numbers requested for lhs when it's "before" what we want. - // We get a 0 then. - int othword = word - shift/VL_WORDSIZE; + // Extract the expression to grab the value for the specified word, if it's the shift + // of shift bits from lhsp + AstNode* newp; + // Negative word numbers requested for lhs when it's "before" what we want. + // We get a 0 then. + int othword = word - shift/VL_WORDSIZE; AstNode* llowp = newAstWordSelClone(lhsp, othword); - if (int loffset = VL_BITBIT_I(shift)) { + if (int loffset = VL_BITBIT_I(shift)) { AstNode* lhip = newAstWordSelClone(lhsp, othword-1); - int nbitsonright = VL_WORDSIZE-loffset; // bits that end up in lword - newp = new AstOr - (fl, - new AstAnd(fl, + int nbitsonright = VL_WORDSIZE-loffset; // bits that end up in lword + newp = new AstOr + (fl, + new AstAnd(fl, new AstConst(fl, VL_MASK_I(loffset)), new AstShiftR(fl, lhip, new AstConst(fl, nbitsonright), VL_WORDSIZE)), - new AstAnd(fl, + new AstAnd(fl, new AstConst(fl, ~VL_MASK_I(loffset)), - new AstShiftL(fl, - llowp, - new AstConst(fl, loffset), - VL_WORDSIZE))); - } else { - newp = llowp; - } - return newp; + new AstShiftL(fl, + llowp, + new AstConst(fl, loffset), + VL_WORDSIZE))); + } else { + newp = llowp; + } + return newp; } AstNode* newSelBitWord(AstNode* lsbp, int wordAdder) { - // Return equation to get the VL_BITWORD of a constant or non-constant + // Return equation to get the VL_BITWORD of a constant or non-constant if (VN_IS(lsbp, Const)) { return new AstConst(lsbp->fileline(), wordAdder + VL_BITWORD_I(VN_CAST(lsbp, Const)->toUInt())); - } else { + } else { AstNode* shiftp = new AstShiftR(lsbp->fileline(), lsbp->cloneTree(true), new AstConst(lsbp->fileline(), VL_WORDSIZE_LOG2), VL_WORDSIZE); - if (wordAdder != 0) { + if (wordAdder != 0) { shiftp = new AstAdd(lsbp->fileline(), // This is indexing a arraysel, so a 32 bit constant is fine new AstConst(lsbp->fileline(), wordAdder), shiftp); - } - return shiftp; - } + } + return shiftp; + } } AstNode* dropCondBound(AstNode* nodep) { - // Experimental only... - // If there's a CONDBOUND safety to keep arrays in bounds, - // we're going to AND it to a value that always fits inside a - // word, so we don't need it. + // Experimental only... + // If there's a CONDBOUND safety to keep arrays in bounds, + // we're going to AND it to a value that always fits inside a + // word, so we don't need it. //if (VN_IS(nodep, CondBound) && VN_IS(VN_CAST(nodep, CondBound)->lhsp(), Lte)) { // nodep = VN_CAST(nodep, CondBound)->rhsp(); - //} - return nodep; + //} + return nodep; } AstNode* newSelBitBit(AstNode* lsbp) { - // Return equation to get the VL_BITBIT of a constant or non-constant + // Return equation to get the VL_BITBIT of a constant or non-constant if (VN_IS(lsbp, Const)) { return new AstConst(lsbp->fileline(), VL_BITBIT_I(VN_CAST(lsbp, Const)->toUInt())); - } else { + } else { return new AstAnd(lsbp->fileline(), new AstConst(lsbp->fileline(), VL_WORDSIZE-1), dropCondBound(lsbp)->cloneTree(true)); - } + } } //==================== bool expandWide(AstNodeAssign* nodep, AstConst* rhsp) { - UINFO(8," Wordize ASSIGN(CONST) "< {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}} - if (rhsp->num().isFourState()) { - rhsp->v3error("Unsupported: 4-state numbers in this context"); - } + UINFO(8," Wordize ASSIGN(CONST) "< {for each_word{ ASSIGN(WORDSEL(wide,#),WORDSEL(CONST,#))}} + if (rhsp->num().isFourState()) { + rhsp->v3error("Unsupported: 4-state numbers in this context"); + } for (int w=0; wwidthWords(); w++) { V3Number num (nodep, VL_WORDSIZE, rhsp->num().dataWord(w)); addWordAssign(nodep, w, new AstConst(nodep->fileline(), num)); - } - return true; + } + return true; } //-------- Uniops bool expandWide(AstNodeAssign* nodep, AstVarRef* rhsp) { - UINFO(8," Wordize ASSIGN(VARREF) "<widthWords(); w++) { + UINFO(8," Wordize ASSIGN(VARREF) "<widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); - } - return true; + } + return true; } bool expandWide(AstNodeAssign* nodep, AstArraySel* rhsp) { - UINFO(8," Wordize ASSIGN(ARRAYSEL) "<dtypep()->skipRefp(), UnpackArrayDType)) { nodep->v3fatalSrc("ArraySel with unpacked arrays should have been removed in V3Slice"); } - for (int w=0; wwidthWords(); w++) { + for (int w=0; wwidthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone(rhsp, w)); - } - return true; + } + return true; } bool expandWide(AstNodeAssign* nodep, AstNot* rhsp) { - UINFO(8," Wordize ASSIGN(NOT) "< {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} - for (int w=0; wwidthWords(); w++) { + UINFO(8," Wordize ASSIGN(NOT) "< {for each_word{ ASSIGN(WORDSEL(wide,#),NOT(WORDSEL(lhs,#))) }} + for (int w=0; wwidthWords(); w++) { addWordAssign(nodep, w, new AstNot(rhsp->fileline(), newAstWordSelClone(rhsp->lhsp(), w))); - } - return true; + } + return true; } //-------- Biops bool expandWide(AstNodeAssign* nodep, AstAnd* rhsp) { - UINFO(8," Wordize ASSIGN(AND) "<widthWords(); w++) { + UINFO(8," Wordize ASSIGN(AND) "<widthWords(); w++) { addWordAssign(nodep, w, new AstAnd(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), newAstWordSelClone(rhsp->rhsp(), w))); - } - return true; + } + return true; } bool expandWide(AstNodeAssign* nodep, AstOr* rhsp) { - UINFO(8," Wordize ASSIGN(OR) "<widthWords(); w++) { + UINFO(8," Wordize ASSIGN(OR) "<widthWords(); w++) { addWordAssign(nodep, w, new AstOr(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), newAstWordSelClone(rhsp->rhsp(), w))); - } - return true; + } + return true; } bool expandWide(AstNodeAssign* nodep, AstXor* rhsp) { - UINFO(8," Wordize ASSIGN(XOR) "<widthWords(); w++) { + UINFO(8," Wordize ASSIGN(XOR) "<widthWords(); w++) { addWordAssign(nodep, w, new AstXor(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), newAstWordSelClone(rhsp->rhsp(), w))); - } - return true; + } + return true; } bool expandWide(AstNodeAssign* nodep, AstXnor* rhsp) { - UINFO(8," Wordize ASSIGN(XNOR) "<widthWords(); w++) { + UINFO(8," Wordize ASSIGN(XNOR) "<widthWords(); w++) { addWordAssign(nodep, w, new AstXnor(nodep->fileline(), newAstWordSelClone(rhsp->lhsp(), w), newAstWordSelClone(rhsp->rhsp(), w))); - } - return true; + } + return true; } //-------- Triops bool expandWide(AstNodeAssign* nodep, AstNodeCond* rhsp) { - UINFO(8," Wordize ASSIGN(COND) "<widthWords(); w++) { + UINFO(8," Wordize ASSIGN(COND) "<widthWords(); w++) { addWordAssign(nodep, w, new AstCond(nodep->fileline(), rhsp->condp()->cloneTree(true), newAstWordSelClone(rhsp->expr1p(), w), newAstWordSelClone(rhsp->expr2p(), w))); - } - return true; + } + return true; } // VISITORS virtual void visit(AstExtend* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (nodep->isWide()) { - // See under ASSIGN(EXTEND) - } else { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* newp = lhsp; - if (nodep->isQuad()) { - if (lhsp->isQuad()) { - lhsp->dtypeFrom(nodep); // Just mark it, else nop - } else if (lhsp->isWide()) { - nodep->v3fatalSrc("extending larger thing into smaller?"); - } else { - UINFO(8," EXTEND(q<-l) "<isWide()) { + // See under ASSIGN(EXTEND) + } else { + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* newp = lhsp; + if (nodep->isQuad()) { + if (lhsp->isQuad()) { + lhsp->dtypeFrom(nodep); // Just mark it, else nop + } else if (lhsp->isWide()) { + nodep->v3fatalSrc("extending larger thing into smaller?"); + } else { + UINFO(8," EXTEND(q<-l) "<fileline(), lhsp, nodep); - } - } else { // Long - if (lhsp->isQuad() || lhsp->isWide()) { - nodep->v3fatalSrc("extending larger thing into smaller?"); - } else { - lhsp->dtypeFrom(nodep); // Just mark it, else nop - } - } - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } + } + } else { // Long + if (lhsp->isQuad() || lhsp->isWide()) { + nodep->v3fatalSrc("extending larger thing into smaller?"); + } else { + lhsp->dtypeFrom(nodep); // Just mark it, else nop + } + } + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } } bool expandWide(AstNodeAssign* nodep, AstExtend* rhsp) { - UINFO(8," Wordize ASSIGN(EXTEND) "<lhsp()->widthWords(); w++) { + UINFO(8," Wordize ASSIGN(EXTEND) "<lhsp()->widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone(rhsp->lhsp(), w)); - } - for (; wwidthWords(); w++) { + } + for (; wwidthWords(); w++) { addWordAssign(nodep, w, new AstConst(rhsp->fileline(), 0)); - } - return true; + } + return true; } virtual void visit(AstSel* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - // Remember, Sel's may have non-integer rhs, so need to optimize for that! + // Remember, Sel's may have non-integer rhs, so need to optimize for that! if (nodep->widthMin() != nodep->widthConst()) nodep->v3fatalSrc("Width mismatch"); - if (VN_IS(nodep->backp(), NodeAssign) && nodep==VN_CAST(nodep->backp(), NodeAssign)->lhsp()) { - // Sel is an LHS assignment select - } else if (nodep->isWide()) { - // See under ASSIGN(WIDE) - } - else if (nodep->fromp()->isWide()) { - UINFO(8," SEL(wide) "<backp(), NodeAssign) + && nodep==VN_CAST(nodep->backp(), NodeAssign)->lhsp()) { + // Sel is an LHS assignment select + } else if (nodep->isWide()) { + // See under ASSIGN(WIDE) + } + else if (nodep->fromp()->isWide()) { + UINFO(8," SEL(wide) "<fromp()->fileline(), nodep->fromp()->cloneTree(true), newSelBitWord(nodep->lsbp(), 0)); - if (nodep->isQuad() && !lowwordp->isQuad()) lowwordp = new AstCCast(nodep->fileline(), lowwordp, nodep); + if (nodep->isQuad() && !lowwordp->isQuad()) { + lowwordp = new AstCCast(nodep->fileline(), lowwordp, nodep); + } AstNode* lowp = new AstShiftR(nodep->fileline(), lowwordp, newSelBitBit(nodep->lsbp()), nodep->width()); - // If > 1 bit, we might be crossing the word boundary - AstNode* midp=NULL; + // If > 1 bit, we might be crossing the word boundary + AstNode* midp = NULL; V3Number zero (nodep, longOrQuadWidth(nodep)); if (nodep->widthConst() > 1) { - AstNode* midwordp = // SEL(from,[1+wordnum]) + AstNode* midwordp = // SEL(from,[1+wordnum]) new AstWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), newSelBitWord(nodep->lsbp(), 1)); - if (nodep->isQuad() && !midwordp->isQuad()) midwordp = new AstCCast(nodep->fileline(), midwordp, nodep); - // If we're selecting bit zero, then all 32 bits in word 1 get shifted << by 32 bits - // else we need to form the lower word, so we << by 31 or less - // nbitsfromlow <= (lsb==0) ? 64-bitbit(lsb) : 32-bitbit(lsb) + if (nodep->isQuad() && !midwordp->isQuad()) { + midwordp = new AstCCast(nodep->fileline(), midwordp, nodep); + } + // If we're selecting bit zero, then all 32 bits in word 1 + // get shifted << by 32 bits + // else we need to form the lower word, so we << by 31 or less + // nbitsfromlow <= (lsb==0) ? 64-bitbit(lsb) : 32-bitbit(lsb) AstNode* midshiftp = new AstSub(nodep->lsbp()->fileline(), - new AstConst(nodep->lsbp()->fileline(), VL_WORDSIZE), + new AstConst(nodep->lsbp()->fileline(), + VL_WORDSIZE), newSelBitBit(nodep->lsbp())); - if (nodep->isQuad()) { - midshiftp = + if (nodep->isQuad()) { + midshiftp = new AstCond(nodep->fileline(), new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), newSelBitBit(nodep->lsbp())), new AstConst(nodep->lsbp()->fileline(), VL_WORDSIZE), midshiftp); - } + } AstNode* midmayp = new AstShiftL(nodep->fileline(), midwordp, midshiftp, nodep->width()); - if (nodep->isQuad()) { - midp = midmayp; // Always grab from two words - } else { + if (nodep->isQuad()) { + midp = midmayp; // Always grab from two words + } else { midp = new AstCond(nodep->fileline(), new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), newSelBitBit(nodep->lsbp())), new AstConst(nodep->fileline(), zero), midmayp); - } - } - // If > 32 bits, we might be crossing the second word boundary - AstNode* hip=NULL; - if (nodep->widthConst() > VL_WORDSIZE) { - AstNode* hiwordp = // SEL(from,[2+wordnum]) + } + } + // If > 32 bits, we might be crossing the second word boundary + AstNode* hip = NULL; + if (nodep->widthConst() > VL_WORDSIZE) { + AstNode* hiwordp = // SEL(from,[2+wordnum]) new AstWordSel(nodep->fromp()->fileline(), nodep->fromp()->cloneTree(true), newSelBitWord(nodep->lsbp(), 2)); - if (nodep->isQuad() && !hiwordp->isQuad()) hiwordp = new AstCCast(nodep->fileline(), hiwordp, nodep); - AstNode* himayp = + if (nodep->isQuad() && !hiwordp->isQuad()) { + hiwordp = new AstCCast(nodep->fileline(), hiwordp, nodep); + } + AstNode* himayp = new AstShiftL(nodep->fileline(), hiwordp, // nbitsfromlow_and_mid <= 64-bitbit(lsb) @@ -422,52 +431,54 @@ private: new AstConst(nodep->lsbp()->fileline(), 64), newSelBitBit(nodep->lsbp())), nodep->width()); - // if (frombit==0) then ignore, else use it + // if (frombit==0) then ignore, else use it hip = new AstCond(nodep->fileline(), new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), newSelBitBit(nodep->lsbp())), new AstConst(nodep->fileline(), zero), himayp); - } + } - AstNode* newp = lowp; + AstNode* newp = lowp; if (midp) newp = new AstOr(nodep->fileline(), midp, newp); if (hip) newp = new AstOr(nodep->fileline(), hip, newp); - newp->dtypeFrom(nodep); - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } - else { // Long/Quad from Long/Quad - UINFO(8," SEL->SHIFT "<fromp()->unlinkFrBack(); - AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); - if (nodep->isQuad() && !fromp->isQuad()) fromp = new AstCCast(nodep->fileline(), fromp, nodep); + newp->dtypeFrom(nodep); + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } + else { // Long/Quad from Long/Quad + UINFO(8," SEL->SHIFT "<fromp()->unlinkFrBack(); + AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); + if (nodep->isQuad() && !fromp->isQuad()) { + fromp = new AstCCast(nodep->fileline(), fromp, nodep); + } AstNode* newp = new AstShiftR(nodep->fileline(), fromp, dropCondBound(lsbp), fromp->width()); // {large}>>32 requires 64-bit shift operation; then cast - newp->dtypeFrom(fromp); - if (!nodep->isQuad() && fromp->isQuad()) { + newp->dtypeFrom(fromp); + if (!nodep->isQuad() && fromp->isQuad()) { newp = new AstCCast(newp->fileline(), newp, nodep); - } - newp->dtypeFrom(nodep); - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } + } + newp->dtypeFrom(nodep); + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } } bool expandWide(AstNodeAssign* nodep, AstSel* rhsp) { if (nodep->widthMin() != rhsp->widthConst()) nodep->v3fatalSrc("Width mismatch"); if (VN_IS(rhsp->lsbp(), Const) && VL_BITBIT_I(rhsp->lsbConst())==0) { - int lsb = rhsp->lsbConst(); - UINFO(8," Wordize ASSIGN(SEL,align) "<widthWords(); w++) { + int lsb = rhsp->lsbConst(); + UINFO(8," Wordize ASSIGN(SEL,align) "<widthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone(rhsp->fromp(), w + VL_BITWORD_I(lsb))); - } - return true; - } else { - UINFO(8," Wordize ASSIGN(EXTRACT,misalign) "<widthWords(); w++) { - // Grab lowest bits + } + return true; + } else { + UINFO(8," Wordize ASSIGN(EXTRACT,misalign) "<widthWords(); w++) { + // Grab lowest bits AstNode* lowwordp = new AstWordSel(rhsp->fileline(), rhsp->fromp()->cloneTree(true), newSelBitWord(rhsp->lsbp(), w)); @@ -477,7 +488,7 @@ private: VL_WORDSIZE); // Upper bits V3Number zero (nodep, VL_WORDSIZE, 0); - AstNode* midwordp = // SEL(from,[1+wordnum]) + AstNode* midwordp = // SEL(from,[1+wordnum]) new AstWordSel(rhsp->fromp()->fileline(), rhsp->fromp()->cloneTree(true), newSelBitWord(rhsp->lsbp(), w+1)); @@ -495,67 +506,67 @@ private: new AstConst(rhsp->fileline(), zero), midmayp); AstNode* newp = new AstOr(nodep->fileline(), midp, lowp); - addWordAssign(nodep, w, newp); - } - return true; - } + addWordAssign(nodep, w, newp); + } + return true; + } } bool expandLhs(AstNodeAssign* nodep, AstSel* lhsp) { - // Possibilities - // destp: wide or narrow - // rhsp: wide (destp must be wide), narrow, or 1 bit wide - // rhsp: may be allones and can remove AND NOT gate - // lsbp: constant or variable - // Yuk. - bool destwide = lhsp->fromp()->isWide(); - bool ones = nodep->rhsp()->isAllOnesV(); + // Possibilities + // destp: wide or narrow + // rhsp: wide (destp must be wide), narrow, or 1 bit wide + // rhsp: may be allones and can remove AND NOT gate + // lsbp: constant or variable + // Yuk. + bool destwide = lhsp->fromp()->isWide(); + bool ones = nodep->rhsp()->isAllOnesV(); if (VN_IS(lhsp->lsbp(), Const)) { - // The code should work without this constant test, but it won't - // constify as nicely as we'd like. - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); - int lsb = lhsp->lsbConst(); - int msb = lhsp->msbConst(); + // The code should work without this constant test, but it won't + // constify as nicely as we'd like. + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* destp = lhsp->fromp()->unlinkFrBack(); + int lsb = lhsp->lsbConst(); + int msb = lhsp->msbConst(); V3Number maskset (nodep, destp->widthMin()); for (int bit=lsb; bit<(msb+1); bit++) maskset.setBit(bit, 1); V3Number maskold (nodep, destp->widthMin()); maskold.opNot(maskset); - if (destwide) { - UINFO(8," ASSIGNSEL(const,wide) "<widthWords(); w++) { - if (w>=VL_BITWORD_I(lsb) && w<=VL_BITWORD_I(msb)) { - // else we would just be setting it to the same exact value + if (destwide) { + UINFO(8," ASSIGNSEL(const,wide) "<widthWords(); w++) { + if (w>=VL_BITWORD_I(lsb) && w<=VL_BITWORD_I(msb)) { + // else we would just be setting it to the same exact value AstNode* oldvalp = newAstWordSelClone(destp, w); - fixCloneLvalue(oldvalp); + fixCloneLvalue(oldvalp); if (!ones) { oldvalp = new AstAnd(lhsp->fileline(), new AstConst(lhsp->fileline(), maskold.dataWord(w)), oldvalp); } - addWordAssign(nodep, w, - destp, + addWordAssign(nodep, w, + destp, new AstOr(lhsp->fileline(), oldvalp, newWordGrabShift(lhsp->fileline(), w, rhsp, lsb))); - } - } - rhsp->deleteTree(); VL_DANGLING(rhsp); - destp->deleteTree(); VL_DANGLING(destp); - } else { - UINFO(8," ASSIGNSEL(const,narrow) "<deleteTree(); VL_DANGLING(rhsp); + destp->deleteTree(); VL_DANGLING(destp); + } else { + UINFO(8," ASSIGNSEL(const,narrow) "<isQuad() && !rhsp->isQuad()) { rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); } - AstNode* oldvalp = destp->cloneTree(true); - fixCloneLvalue(oldvalp); + AstNode* oldvalp = destp->cloneTree(true); + fixCloneLvalue(oldvalp); if (!ones) { oldvalp = new AstAnd(lhsp->fileline(), new AstConst(lhsp->fileline(), maskold), oldvalp); } - AstNode* newp + AstNode* newp = new AstOr(lhsp->fileline(), oldvalp, new AstShiftL(lhsp->fileline(), @@ -563,34 +574,36 @@ private: new AstConst(lhsp->fileline(), lsb), destp->width())); newp = new AstAssign(nodep->fileline(), destp, newp); - insertBefore(nodep,newp); - } - return true; - } - else { // non-const RHS - if (destwide && lhsp->widthConst()==1) { - UINFO(8," ASSIGNSEL(varlsb,wide,1bit) "<rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); + insertBefore(nodep, newp); + } + return true; + } + else { // non-const RHS + if (destwide && lhsp->widthConst()==1) { + UINFO(8," ASSIGNSEL(varlsb,wide,1bit) "<rhsp()->unlinkFrBack(); + AstNode* destp = lhsp->fromp()->unlinkFrBack(); AstNode* oldvalp = new AstWordSel(lhsp->fileline(), destp->cloneTree(true), newSelBitWord(lhsp->lsbp(), 0)); - fixCloneLvalue(oldvalp); + fixCloneLvalue(oldvalp); if (!ones) { - oldvalp = new AstAnd(lhsp->fileline(), - new AstNot(lhsp->fileline(), - new AstShiftL(lhsp->fileline(), - new AstConst(nodep->fileline(),1), - // newSelBitBit may exceed the MSB of this variable. - // That's ok as we'd just AND with a larger value, - // but oldval would clip the upper bits to sanity - newSelBitBit(lhsp->lsbp()), - VL_WORDSIZE)), - oldvalp); + oldvalp = new AstAnd + (lhsp->fileline(), + new AstNot(lhsp->fileline(), + new AstShiftL + (lhsp->fileline(), + new AstConst(nodep->fileline(), 1), + // newSelBitBit may exceed the MSB of this variable. + // That's ok as we'd just AND with a larger value, + // but oldval would clip the upper bits to sanity + newSelBitBit(lhsp->lsbp()), + VL_WORDSIZE)), + oldvalp); } - // Restrict the shift amount to 0-31, see bug804. - AstNode* shiftp = new AstAnd(nodep->fileline(), lhsp->lsbp()->cloneTree(true), - new AstConst(nodep->fileline(), VL_WORDSIZE-1)); + // Restrict the shift amount to 0-31, see bug804. + AstNode* shiftp = new AstAnd(nodep->fileline(), lhsp->lsbp()->cloneTree(true), + new AstConst(nodep->fileline(), VL_WORDSIZE-1)); AstNode* newp = new AstOr(lhsp->fileline(), oldvalp, new AstShiftL(lhsp->fileline(), @@ -601,33 +614,36 @@ private: new AstWordSel(nodep->fileline(), destp, newSelBitWord(lhsp->lsbp(), 0)), - newp); - insertBefore(nodep,newp); - return true; - } - else if (destwide) { - UINFO(8," ASSIGNSEL(varlsb,wide) -- NoOp -- "<dumpTree(cout,"- old: "); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* destp = lhsp->fromp()->unlinkFrBack(); - AstNode* oldvalp = destp->cloneTree(true); - fixCloneLvalue(oldvalp); + newp); + insertBefore(nodep, newp); + return true; + } + else if (destwide) { + UINFO(8," ASSIGNSEL(varlsb,wide) -- NoOp -- "<dumpTree(cout, "- old: "); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* destp = lhsp->fromp()->unlinkFrBack(); + AstNode* oldvalp = destp->cloneTree(true); + fixCloneLvalue(oldvalp); V3Number maskwidth (nodep, destp->widthMin()); - for (int bit=0; bit < lhsp->widthConst(); bit++) maskwidth.setBit(bit,1); + for (int bit=0; bit < lhsp->widthConst(); bit++) maskwidth.setBit(bit, 1); - if (destp->isQuad() && !rhsp->isQuad()) rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); + if (destp->isQuad() && !rhsp->isQuad()) { + rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); + } if (!ones) { oldvalp = new AstAnd(lhsp->fileline(), new AstNot(lhsp->fileline(), @@ -636,9 +652,9 @@ private: maskwidth), lhsp->lsbp()->cloneTree(true), destp->width())), - oldvalp); + oldvalp); } - AstNode* newp + AstNode* newp = new AstOr(lhsp->fileline(), oldvalp, new AstShiftL(lhsp->fileline(), @@ -646,286 +662,293 @@ private: lhsp->lsbp()->cloneTree(true), destp->width())); newp = new AstAssign(nodep->fileline(), destp, newp); - //newp->dumpTree(cout,"- new: "); - insertBefore(nodep,newp); - return true; - } - } + //newp->dumpTree(cout, "- new: "); + insertBefore(nodep, newp); + return true; + } + } } virtual void visit(AstConcat* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (nodep->isWide()) { - // See under ASSIGN(WIDE) - } else { - UINFO(8," CONCAT "<lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - int rhsshift = rhsp->widthMin(); - if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); - if (nodep->isQuad() && !rhsp->isQuad()) rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); + if (nodep->isWide()) { + // See under ASSIGN(WIDE) + } else { + UINFO(8," CONCAT "<lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + int rhsshift = rhsp->widthMin(); + if (nodep->isQuad() && !lhsp->isQuad()) { + lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); + } + if (nodep->isQuad() && !rhsp->isQuad()) { + rhsp = new AstCCast(nodep->fileline(), rhsp, nodep); + } AstNode* newp = new AstOr(nodep->fileline(), new AstShiftL(nodep->fileline(), lhsp, new AstConst(nodep->fileline(), rhsshift), nodep->width()), - rhsp); - newp->dtypeFrom(nodep); // Unsigned - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } + rhsp); + newp->dtypeFrom(nodep); // Unsigned + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } } bool expandWide(AstNodeAssign* nodep, AstConcat* rhsp) { - UINFO(8," Wordize ASSIGN(CONCAT) "<rhsp()->widthMin(); - // Sometimes doing the words backwards is preferrable. - // When we have x={x,foo} backwards is better, when x={foo,x} forward is better - // However V3Subst tends to rip this up, so not worth optimizing now. - for (int w=0; wwidthWords(); w++) { - addWordAssign(nodep, w, + UINFO(8," Wordize ASSIGN(CONCAT) "<rhsp()->widthMin(); + // Sometimes doing the words backwards is preferrable. + // When we have x={x,foo} backwards is better, when x={foo,x} forward is better + // However V3Subst tends to rip this up, so not worth optimizing now. + for (int w=0; wwidthWords(); w++) { + addWordAssign(nodep, w, new AstOr(rhsp->fileline(), newWordGrabShift(rhsp->fileline(), w, rhsp->lhsp(), rhsshift), newAstWordSelClone(rhsp->rhsp(), w))); - } - return true; + } + return true; } virtual void visit(AstReplicate* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (nodep->isWide()) { - // See under ASSIGN(WIDE) - } else { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* newp; - int lhswidth = lhsp->widthMin(); - if (lhswidth==1) { - UINFO(8," REPLICATE(w1) "<isWide()) { + // See under ASSIGN(WIDE) + } else { + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* newp; + int lhswidth = lhsp->widthMin(); + if (lhswidth==1) { + UINFO(8," REPLICATE(w1) "<fileline(), lhsp); - } else { - UINFO(8," REPLICATE "<rhsp(), Const); - if (!constp) nodep->v3fatalSrc("Replication value isn't a constant. Checked earlier!"); - uint32_t times = constp->toUInt(); - if (nodep->isQuad() && !lhsp->isQuad()) lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); - newp = lhsp->cloneTree(true); - for (unsigned repnum=1; repnumv3fatalSrc("Replication value isn't a constant. Checked earlier!"); + uint32_t times = constp->toUInt(); + if (nodep->isQuad() && !lhsp->isQuad()) { + lhsp = new AstCCast(nodep->fileline(), lhsp, nodep); + } + newp = lhsp->cloneTree(true); + for (unsigned repnum=1; repnumfileline(), new AstShiftL(nodep->fileline(), lhsp->cloneTree(true), new AstConst(nodep->fileline(), rhsshift), nodep->width()), - newp); - newp->dtypeFrom(nodep); // Unsigned - } - lhsp->deleteTree(); // Never used - } - newp->dtypeFrom(nodep); // Unsigned - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } + newp); + newp->dtypeFrom(nodep); // Unsigned + } + lhsp->deleteTree(); // Never used + } + newp->dtypeFrom(nodep); // Unsigned + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } } bool expandWide(AstNodeAssign* nodep, AstReplicate* rhsp) { - UINFO(8," Wordize ASSIGN(REPLICATE) "<lhsp(); - int lhswidth = lhsp->widthMin(); + UINFO(8," Wordize ASSIGN(REPLICATE) "<lhsp(); + int lhswidth = lhsp->widthMin(); const AstConst* constp = VN_CAST(rhsp->rhsp(), Const); - if (!constp) rhsp->v3fatalSrc("Replication value isn't a constant. Checked earlier!"); - uint32_t times = constp->toUInt(); - for (int w=0; wwidthWords(); w++) { - AstNode* newp; - if (lhswidth==1) { + if (!constp) rhsp->v3fatalSrc("Replication value isn't a constant. Checked earlier!"); + uint32_t times = constp->toUInt(); + for (int w=0; wwidthWords(); w++) { + AstNode* newp; + if (lhswidth==1) { newp = new AstNegate(nodep->fileline(), lhsp->cloneTree(true)); - newp->dtypeSetLogicSized(VL_WORDSIZE,VL_WORDSIZE,AstNumeric::UNSIGNED); // Replicate always unsigned - } else { + newp->dtypeSetLogicSized(VL_WORDSIZE, VL_WORDSIZE, AstNumeric::UNSIGNED); // Replicate always unsigned + } else { newp = newAstWordSelClone(lhsp, w); - for (unsigned repnum=1; repnumfileline(), newWordGrabShift(rhsp->fileline(), w, lhsp, lhswidth*repnum), newp); - } - } - addWordAssign(nodep, w, newp); - } - return true; + } + } + addWordAssign(nodep, w, newp); + } + return true; } virtual void visit(AstChangeXor* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - UINFO(8," Wordize ChangeXor "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} - AstNode* newp = NULL; - for (int w=0; wlhsp()->widthWords(); w++) { + UINFO(8," Wordize ChangeXor "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = new AstXor(nodep->fileline(), newAstWordSelClone(nodep->lhsp(), w), newAstWordSelClone(nodep->rhsp(), w)); newp = (newp==NULL) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); - } - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); + } + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); } void visitEqNeq(AstNodeBiop* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (nodep->lhsp()->isWide()) { - UINFO(8," Wordize EQ/NEQ "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} - AstNode* newp = NULL; - for (int w=0; wlhsp()->widthWords(); w++) { + if (nodep->lhsp()->isWide()) { + UINFO(8," Wordize EQ/NEQ "< (0=={or{for each_word{WORDSEL(lhs,#)^WORDSEL(rhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = new AstXor(nodep->fileline(), newAstWordSelClone(nodep->lhsp(), w), newAstWordSelClone(nodep->rhsp(), w)); newp = (newp==NULL) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); - } + } if (VN_IS(nodep, Neq)) { newp = new AstNeq(nodep->fileline(), new AstConst(nodep->fileline(), 0), newp); - } else { + } else { newp = new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), 0), newp); - } - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } + } + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } } virtual void visit(AstEq* nodep) { visitEqNeq(nodep); } virtual void visit(AstNeq* nodep) { visitEqNeq(nodep); } virtual void visit(AstRedOr* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (nodep->lhsp()->isWide()) { - UINFO(8," Wordize REDOR "< (0!={or{for each_word{WORDSEL(lhs,#)}}} - AstNode* newp = NULL; - for (int w=0; wlhsp()->widthWords(); w++) { + if (nodep->lhsp()->isWide()) { + UINFO(8," Wordize REDOR "< (0!={or{for each_word{WORDSEL(lhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); newp = (newp==NULL) ? eqp : (new AstOr(nodep->fileline(), newp, eqp)); - } + } newp = new AstNeq(nodep->fileline(), new AstConst(nodep->fileline(), 0), newp); - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } else { - UINFO(8," REDOR->EQ "<EQ "<lhsp()->unlinkFrBack(); V3Number zero (nodep, longOrQuadWidth(nodep)); AstNode* newp = new AstNeq(nodep->fileline(), new AstConst(nodep->fileline(), zero), lhsp); - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } } virtual void visit(AstRedAnd* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (nodep->lhsp()->isWide()) { - UINFO(8," Wordize REDAND "< (0!={and{for each_word{WORDSEL(lhs,#)}}} - AstNode* newp = NULL; - for (int w=0; wlhsp()->widthWords(); w++) { + if (nodep->lhsp()->isWide()) { + UINFO(8," Wordize REDAND "< (0!={and{for each_word{WORDSEL(lhs,#)}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); - if (w==nodep->lhsp()->widthWords()-1) { - // Rather than doing a (slowish) ==##, we OR in the bits that aren't part of the mask + if (w==nodep->lhsp()->widthWords()-1) { + // Rather than doing a (slowish) ==##, we OR in the + // bits that aren't part of the mask eqp = new AstOr(nodep->fileline(), new AstConst(nodep->fileline(), notWideMask(nodep->lhsp())), // Bug in cppcheck // cppcheck-suppress memleak eqp); - } + } newp = (newp==NULL) ? eqp : (new AstAnd(nodep->fileline(), newp, eqp)); - } + } newp = new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), ~0), newp); - replaceWithDelete(nodep, newp); VL_DANGLING(nodep); - } else { - UINFO(8," REDAND->EQ "<lhsp()->unlinkFrBack(); + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } else { + UINFO(8," REDAND->EQ "<lhsp()->unlinkFrBack(); AstNode* newp = new AstEq(nodep->fileline(), new AstConst(nodep->fileline(), wordMask(lhsp)), lhsp); - replaceWithDelete(nodep,newp); VL_DANGLING(nodep); - } + replaceWithDelete(nodep, newp); VL_DANGLING(nodep); + } } virtual void visit(AstRedXor* nodep) { - if (nodep->user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once iterateChildren(nodep); - if (nodep->lhsp()->isWide()) { - UINFO(8," Wordize REDXOR "< (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}} - AstNode* newp = NULL; - for (int w=0; wlhsp()->widthWords(); w++) { + if (nodep->lhsp()->isWide()) { + UINFO(8," Wordize REDXOR "< (0!={redxor{for each_word{XOR(WORDSEL(lhs,#))}}} + AstNode* newp = NULL; + for (int w=0; wlhsp()->widthWords(); w++) { AstNode* eqp = newAstWordSelClone(nodep->lhsp(), w); newp = (newp==NULL) ? eqp : (new AstXor(nodep->fileline(), newp, eqp)); - } + } newp = new AstRedXor(nodep->fileline(), newp); - UINFO(8," Wordize REDXORnew "<user1SetOnce()) return; // Process once + if (nodep->user1SetOnce()) return; // Process once if (!nodep->isStatement()) { iterateChildren(nodep); return; } - m_stmtp = nodep; + m_stmtp = nodep; iterateChildren(nodep); - m_stmtp = NULL; + m_stmtp = NULL; } virtual void visit(AstNodeAssign* nodep) { - if (nodep->user1SetOnce()) return; // Process once - m_stmtp = nodep; + if (nodep->user1SetOnce()) return; // Process once + m_stmtp = nodep; iterateChildren(nodep); - bool did = false; + bool did = false; if (nodep->isWide() && ((VN_IS(nodep->lhsp(), VarRef) || VN_IS(nodep->lhsp(), ArraySel))) - && !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC - && !AstVar::scVarRecurse(nodep->rhsp())) { + && !AstVar::scVarRecurse(nodep->lhsp()) // Need special function for SC + && !AstVar::scVarRecurse(nodep->rhsp())) { if (AstConst* rhsp = VN_CAST(nodep->rhsp(), Const)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstVarRef* rhsp = VN_CAST(nodep->rhsp(), VarRef)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstSel* rhsp = VN_CAST(nodep->rhsp(), Sel)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstArraySel* rhsp = VN_CAST(nodep->rhsp(), ArraySel)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstConcat* rhsp = VN_CAST(nodep->rhsp(), Concat)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstReplicate* rhsp = VN_CAST(nodep->rhsp(), Replicate)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstAnd* rhsp = VN_CAST(nodep->rhsp(), And)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstOr* rhsp = VN_CAST(nodep->rhsp(), Or)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstNot* rhsp = VN_CAST(nodep->rhsp(), Not)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstXor* rhsp = VN_CAST(nodep->rhsp(), Xor)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstXnor* rhsp = VN_CAST(nodep->rhsp(), Xnor)) { - did = expandWide(nodep,rhsp); + did = expandWide(nodep, rhsp); } else if (AstNodeCond* rhsp = VN_CAST(nodep->rhsp(), NodeCond)) { - did = expandWide(nodep,rhsp); - } + did = expandWide(nodep, rhsp); + } } else if (AstSel* lhsp = VN_CAST(nodep->lhsp(), Sel)) { - did = expandLhs(nodep,lhsp); - } - // Cleanup common code - if (did) { - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - m_stmtp = NULL; + did = expandLhs(nodep, lhsp); + } + // Cleanup common code + if (did) { + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + m_stmtp = NULL; } //-------------------- // Default: Just iterate - virtual void visit(AstVar*) {} // Don't hit varrefs under vars + virtual void visit(AstVar*) {} // Don't hit varrefs under vars virtual void visit(AstNode* nodep) { iterateChildren(nodep); } @@ -933,7 +956,7 @@ private: public: // CONSTUCTORS explicit ExpandVisitor(AstNetlist* nodep) { - m_stmtp=NULL; + m_stmtp = NULL; iterate(nodep); } virtual ~ExpandVisitor() {} diff --git a/src/V3Expand.h b/src/V3Expand.h index 779515e76..7348600c6 100644 --- a/src/V3Expand.h +++ b/src/V3Expand.h @@ -34,4 +34,4 @@ public: static void expandAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3File.cpp b/src/V3File.cpp index 56f2ab95f..116a7d0b4 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -40,9 +40,9 @@ # define INFILTER_PIPE // Allow pipe filtering. Needs fork() #endif -#ifdef HAVE_STAT_NSEC // i.e. Linux 2.6, from configure -# define VL_STAT_CTIME_NSEC(stat) ((stat).st_ctim.tv_nsec) // Nanoseconds -# define VL_STAT_MTIME_NSEC(stat) ((stat).st_mtim.tv_nsec) // Nanoseconds +#ifdef HAVE_STAT_NSEC // i.e. Linux 2.6, from configure +# define VL_STAT_CTIME_NSEC(stat) ((stat).st_ctim.tv_nsec) // Nanoseconds +# define VL_STAT_MTIME_NSEC(stat) ((stat).st_mtim.tv_nsec) // Nanoseconds #else # define VL_STAT_CTIME_NSEC(stat) (0) # define VL_STAT_MTIME_NSEC(stat) (0) @@ -63,38 +63,38 @@ class V3FileDependImp { // TYPES class DependFile { - // A single file - bool m_target; // True if write, else read - string m_filename; // Filename - struct stat m_stat; // Stat information + // A single file + bool m_target; // True if write, else read + string m_filename; // Filename + struct stat m_stat; // Stat information public: - DependFile(const string& filename, bool target) - : m_target(target), m_filename(filename) { - m_stat.st_ctime = 0; - m_stat.st_mtime = 0; - } - ~DependFile() {} - const string& filename() const { return m_filename; } - bool target() const { return m_target; } - off_t size() const { return m_stat.st_size; } - ino_t ino() const { return m_stat.st_ino; } - time_t cstime() const { return m_stat.st_ctime; } // Seconds - time_t cnstime() const { return VL_STAT_CTIME_NSEC(m_stat); } // Nanoseconds - time_t mstime() const { return m_stat.st_mtime; } // Seconds - time_t mnstime() const { return VL_STAT_MTIME_NSEC(m_stat); } // Nanoseconds - void loadStats() { - if (!m_stat.st_mtime) { - string fn = filename(); - int err = stat(fn.c_str(), &m_stat); + DependFile(const string& filename, bool target) + : m_target(target), m_filename(filename) { + m_stat.st_ctime = 0; + m_stat.st_mtime = 0; + } + ~DependFile() {} + const string& filename() const { return m_filename; } + bool target() const { return m_target; } + off_t size() const { return m_stat.st_size; } + ino_t ino() const { return m_stat.st_ino; } + time_t cstime() const { return m_stat.st_ctime; } // Seconds + time_t cnstime() const { return VL_STAT_CTIME_NSEC(m_stat); } // Nanoseconds + time_t mstime() const { return m_stat.st_mtime; } // Seconds + time_t mnstime() const { return VL_STAT_MTIME_NSEC(m_stat); } // Nanoseconds + void loadStats() { + if (!m_stat.st_mtime) { + string fn = filename(); + int err = stat(fn.c_str(), &m_stat); if (err!=0) { memset(&m_stat, 0, sizeof(m_stat)); m_stat.st_mtime = 1; // Not an error... This can occur due to `line directives in the .vpp files - UINFO(1,"-Info: File not statable: "< m_filenameList; // Files sourced/generated static string stripQuotes(const string& in) { - string pretty = in; - string::size_type pos; + string pretty = in; + string::size_type pos; while ((pos = pretty.find('\"')) != string::npos) pretty.replace(pos, 1, "_"); while ((pos = pretty.find('\n')) != string::npos) pretty.replace(pos, 1, "_"); - return pretty; + return pretty; } public: // ACCESSOR METHODS void addSrcDepend(const string& filename) { - if (m_filenameSet.find(filename) == m_filenameSet.end()) { - m_filenameSet.insert(filename); - DependFile df (filename, false); - df.loadStats(); // Get size now, in case changes during the run - m_filenameList.insert(df); - } + if (m_filenameSet.find(filename) == m_filenameSet.end()) { + m_filenameSet.insert(filename); + DependFile df (filename, false); + df.loadStats(); // Get size now, in case changes during the run + m_filenameList.insert(df); + } } void addTgtDepend(const string& filename) { - if (m_filenameSet.find(filename) == m_filenameSet.end()) { - m_filenameSet.insert(filename); + if (m_filenameSet.find(filename) == m_filenameSet.end()) { + m_filenameSet.insert(filename); m_filenameList.insert(DependFile(filename, true)); - } + } } void writeDepend(const string& filename); void writeTimes(const string& filename, const string& cmdlineIn); bool checkTimes(const string& filename, const string& cmdlineIn); }; -V3FileDependImp dependImp; // Depend implementation class +V3FileDependImp dependImp; // Depend implementation class //###################################################################### // V3FileDependImp @@ -139,10 +139,10 @@ inline void V3FileDependImp::writeDepend(const string& filename) { if (ofp->fail()) v3fatal("Can't write "<::iterator iter=m_filenameList.begin(); - iter!=m_filenameList.end(); ++iter) { - if (iter->target()) { - *ofp<filename()<<" "; - } + iter!=m_filenameList.end(); ++iter) { + if (iter->target()) { + *ofp<filename()<<" "; + } } *ofp<<" : "; *ofp<::iterator iter=m_filenameList.begin(); - iter!=m_filenameList.end(); ++iter) { - if (!iter->target()) { - *ofp<filename()<<" "; - } + iter!=m_filenameList.end(); ++iter) { + if (!iter->target()) { + *ofp<filename()<<" "; + } } *ofp<::iterator iter=m_filenameList.begin(); - iter!=m_filenameList.end(); ++iter) { - if (!iter->target()) { - *ofp<filename()<<":"<target()) { + *ofp<filename()<<":"<::iterator iter=m_filenameList.begin(); - iter!=m_filenameList.end(); ++iter) { - // Read stats of files we create after we're done making them (execpt for this file, of course) + iter!=m_filenameList.end(); ++iter) { + // Read stats of files we create after we're done making them + // (execpt for this file, of course) DependFile* dfp = const_cast(&(*iter)); - V3Options::fileNfsFlush(dfp->filename()); - dfp->loadStats(); - off_t showSize = iter->size(); - ino_t showIno = iter->ino(); - if (dfp->filename() == filename) { showSize=0; showIno=0; } // We're writing it, so need to ignore it + V3Options::fileNfsFlush(dfp->filename()); + dfp->loadStats(); + off_t showSize = iter->size(); + ino_t showIno = iter->ino(); + if (dfp->filename() == filename) { + showSize = 0; showIno = 0; // We're writing it, so need to ignore it + } - *ofp<<(iter->target()?"T":"S")<<" "; + *ofp<<(iter->target()?"T":"S")<<" "; *ofp<<" "<cstime(); *ofp<<" "<cnstime(); *ofp<<" "<mstime(); *ofp<<" "<mnstime(); - *ofp<<" \""<filename()<<"\""; - *ofp<filename()<<"\""; + *ofp< ifp (V3File::new_ifstream_nodepend(filename)); if (ifp->fail()) { - UINFO(2," --check-times failed: no input "<>chkDir; char quote; *ifp>>quote; string chkCmdline = V3Os::getline(*ifp, '"'); - string cmdline = stripQuotes(cmdlineIn); - if (cmdline != chkCmdline) { - UINFO(2," --check-times failed: different command line\n"); - return false; - } + string cmdline = stripQuotes(cmdlineIn); + if (cmdline != chkCmdline) { + UINFO(2," --check-times failed: different command line\n"); + return false; + } } while (!ifp->eof()) { - char chkDir; *ifp>>chkDir; - off_t chkSize; *ifp>>chkSize; - ino_t chkIno; *ifp>>chkIno; - if (ifp->eof()) break; // Needed to read final whitespace before found eof - time_t chkCstime; *ifp>>chkCstime; - time_t chkCnstime; *ifp>>chkCnstime; - time_t chkMstime; *ifp>>chkMstime; - time_t chkMnstime; *ifp>>chkMnstime; + char chkDir; *ifp>>chkDir; + off_t chkSize; *ifp>>chkSize; + ino_t chkIno; *ifp>>chkIno; + if (ifp->eof()) break; // Needed to read final whitespace before found eof + time_t chkCstime; *ifp>>chkCstime; + time_t chkCnstime; *ifp>>chkCnstime; + time_t chkMstime; *ifp>>chkMstime; + time_t chkMnstime; *ifp>>chkMnstime; char quote; *ifp>>quote; string chkFilename = V3Os::getline(*ifp, '"'); - V3Options::fileNfsFlush(chkFilename); + V3Options::fileNfsFlush(chkFilename); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) - struct stat chkStat; - int err = stat(chkFilename.c_str(), &chkStat); - if (err!=0) { - UINFO(2," --check-times failed: missing "< FileContentsMap; typedef V3InFilter::StrList StrList; - FileContentsMap m_contentsMap; // Cache of file contents - bool m_readEof; // Received EOF on read + FileContentsMap m_contentsMap; // Cache of file contents + bool m_readEof; // Received EOF on read #ifdef INFILTER_PIPE - pid_t m_pid; // fork() process id + pid_t m_pid; // fork() process id #else - int m_pid; // fork() process id - always zero as disabled + int m_pid; // fork() process id - always zero as disabled #endif - bool m_pidExited; - int m_pidStatus; - int m_writeFd; // File descriptor TO filter - int m_readFd; // File descriptor FROM filter + bool m_pidExited; + int m_pidStatus; + int m_writeFd; // File descriptor TO filter + int m_readFd; // File descriptor FROM filter private: // METHODS VL_DEBUG_FUNC; // Declare debug() bool readContents(const string& filename, StrList& outl) { - if (m_pid) return readContentsFilter(filename,outl); - else return readContentsFile(filename,outl); + if (m_pid) return readContentsFilter(filename, outl); + else return readContentsFile(filename, outl); } bool readContentsFile(const string& filename, StrList& outl) { int fd = open(filename.c_str(), O_RDONLY); - if (fd<0) return false; - m_readEof = false; - readBlocks(fd, -1, outl); - close(fd); - return true; + if (fd<0) return false; + m_readEof = false; + readBlocks(fd, -1, outl); + close(fd); + return true; } bool readContentsFilter(const string& filename, StrList& outl) { - if (filename!="" || outl.empty()) {} // Prevent unused + if (filename!="" || outl.empty()) {} // Prevent unused #ifdef INFILTER_PIPE - writeFilter("read \""+filename+"\"\n"); - string line = readFilterLine(); - if (line.find("Content-Length") != string::npos) { - int len = 0; - sscanf(line.c_str(), "Content-Length: %d\n", &len); - readBlocks(m_readFd, len, outl); - return true; - } else { - if (line!="") v3error("--pipe-filter protocol error, unexpected: "<sizegot)) { - ssize_t todo = INFILTER_IPC_BUFSIZ; - if (size>0 && sizesizegot)) { + ssize_t todo = INFILTER_IPC_BUFSIZ; + if (size>0 && size0) { - outl.push_back(string(buf, got)); - sizegot += got; - } - else if (errno == EINTR || errno == EAGAIN + //UINFO(9,"RD GOT g "<< got<<" e "<0) { + outl.push_back(string(buf, got)); + sizegot += got; + } + else if (errno == EINTR || errno == EAGAIN #ifdef EWOULDBLOCK - || errno == EWOULDBLOCK + || errno == EWOULDBLOCK #endif - ) { - // cppcheck-suppress obsoleteFunctionsusleep - checkFilter(false); usleep(1000); continue; - } else { m_readEof = true; break; } - } - return out; + ) { + // cppcheck-suppress obsoleteFunctionsusleep + checkFilter(false); usleep(1000); continue; + } else { m_readEof = true; break; } + } + return out; } // cppcheck-suppress unusedFunction unusedPrivateFunction string readFilterLine() { - // Slow, but we don't need it much - UINFO(9,"readFilterLine\n"); - string line; - while (!m_readEof) { - StrList outl; - readBlocks(m_readFd, 1, outl); - string onechar = listString(outl); - line += onechar; - if (onechar == "\n") { - if (line == "\n") { line=""; continue; } - else break; - } - } - UINFO(6,"filter-line-in: "<=6) { UINFO(6,"filter-out: "<offset) { - errno = 0; + if (debug()>=6) { UINFO(6,"filter-out: "<offset) { + errno = 0; int got = write(m_writeFd, (out.c_str())+offset, out.length()-offset); - //UINFO(9,"WR GOT g "<< got<<" e "<0) offset += got; - else if (errno == EINTR || errno == EAGAIN + //UINFO(9,"WR GOT g "<< got<<" e "<0) offset += got; + else if (errno == EINTR || errno == EAGAIN #ifdef EWOULDBLOCK - || errno == EWOULDBLOCK + || errno == EWOULDBLOCK #endif - ) { - // cppcheck-suppress obsoleteFunctionsusleep - checkFilter(false); usleep(1000); continue; - } - else break; - } + ) { + // cppcheck-suppress obsoleteFunctionsusleep + checkFilter(false); usleep(1000); continue; + } + else break; + } } // Start the filter void start(const string& command) { - if (command=="") { - m_pid = 0; // Disabled - } else { - startFilter(command); - } + if (command=="") { + m_pid = 0; // Disabled + } else { + startFilter(command); + } } void startFilter(const string& command) { - if (command=="") {} // Prevent Unused + if (command=="") {} // Prevent Unused #ifdef INFILTER_PIPE - int fd_stdin[2], fd_stdout[2]; - static const int P_RD = 0; - static const int P_WR = 1; + int fd_stdin[2], fd_stdout[2]; + static const int P_RD = 0; + static const int P_WR = 1; - if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) { - v3fatal("--pipe-filter: Can't pipe: "<(NULL)); - // Don't use v3fatal, we don't share the common structures any more - fprintf(stderr,"--pipe-filter: exec failed: %s\n",strerror(errno)); - _exit(10); - } - else { // Parent - UINFO(6,"In parent, child pid "<"<"<"<"<second); - return true; - } - if (!readContents(filename, outl)) return false; - if (listSize(outl) < INFILTER_CACHE_MAX) { - // Cache small files (only to save space) - // It's quite common to `include "timescale" thousands of times - // This isn't so important if it's just a open(), but filtering can be slow - m_contentsMap.insert(make_pair(filename,listString(outl))); - } - return true; + FileContentsMap::iterator it = m_contentsMap.find(filename); + if (it != m_contentsMap.end()) { + outl.push_back(it->second); + return true; + } + if (!readContents(filename, outl)) return false; + if (listSize(outl) < INFILTER_CACHE_MAX) { + // Cache small files (only to save space) + // It's quite common to `include "timescale" thousands of times + // This isn't so important if it's just a open(), but filtering can be slow + m_contentsMap.insert(make_pair(filename, listString(outl))); + } + return true; } size_t listSize(StrList& sl) { - size_t out = 0; - for (StrList::iterator it=sl.begin(); it!=sl.end(); ++it) { - out += it->length(); - } - return out; + size_t out = 0; + for (StrList::iterator it=sl.begin(); it!=sl.end(); ++it) { + out += it->length(); + } + return out; } string listString(StrList& sl) { - string out; - for (StrList::iterator it=sl.begin(); it!=sl.end(); ++it) { - out += *it; - } - return out; + string out; + for (StrList::iterator it=sl.begin(); it!=sl.end(); ++it) { + out += *it; + } + return out; } // CONSTRUCTORS explicit V3InFilterImp(const string& command) { - m_readEof = false; - m_pid = 0; - m_pidExited = false; - m_pidStatus = 0; - m_writeFd = 0; - m_readFd = 0; - start(command); + m_readEof = false; + m_pid = 0; + m_pidExited = false; + m_pidStatus = 0; + m_writeFd = 0; + m_readFd = 0; + start(command); } ~V3InFilterImp() { stop(); } }; @@ -565,7 +569,7 @@ protected: // Just dispatch to the implementation V3InFilter::V3InFilter(const string& command) { m_impp = new V3InFilterImp(command); } -V3InFilter::~V3InFilter() { if (m_impp) delete m_impp; m_impp=NULL; } +V3InFilter::~V3InFilter() { if (m_impp) delete m_impp; m_impp = NULL; } bool V3InFilter::readWholefile(const string& filename, V3InFilter::StrList& outl) { if (!m_impp) v3fatalSrc("readWholefile on invalid filter"); @@ -589,16 +593,16 @@ const char* V3OutFormatter::indentStr(int num) { // Indent the specified number of spaces. Use tabs as possible. static char str[MAXSPACE+20]; char* cp = str; - if (num>MAXSPACE) num=MAXSPACE; + if (num>MAXSPACE) num = MAXSPACE; if (m_lang!=LA_VERILOG && m_lang!=LA_XML) { // verilogPrefixedTree doesn't want tabs - while (num>=8) { - *cp++ = '\t'; - num -= 8; - } + while (num>=8) { + *cp++ = '\t'; + num -= 8; + } } while (num>0) { - *cp++ = ' '; - num --; + *cp++ = ' '; + num --; } *cp++ = '\0'; return (str); @@ -608,10 +612,10 @@ const string V3OutFormatter::indentSpaces(int num) { // Indent the specified number of spaces. Use spaces. static char str[MAXSPACE+20]; char* cp = str; - if (num>MAXSPACE) num=MAXSPACE; + if (num>MAXSPACE) num = MAXSPACE; while (num>0) { - *cp++ = ' '; - num --; + *cp++ = ' '; + num --; } *cp++ = '\0'; string st (str); @@ -626,153 +630,153 @@ bool V3OutFormatter::tokenStart(const char* cp, const char* cmp) { } bool V3OutFormatter::tokenEnd(const char* cp) { - return (tokenStart(cp,"end") - || tokenStart(cp,"endcase") - || tokenStart(cp,"endmodule")); + return (tokenStart(cp, "end") + || tokenStart(cp, "endcase") + || tokenStart(cp, "endmodule")); } int V3OutFormatter::endLevels(const char *strg) { - int levels=m_indentLevel; + int levels = m_indentLevel; { - const char* cp=strg; - while (isspace(*cp)) cp++; - switch (*cp) { - case '\n': // Newlines.. No need for whitespace before it - return (0); - case '#': // Preproc directive - return (0); - } - { - // label/public/private: Deindent by 2 spaces - const char* mp=cp; - for (; isalnum(*mp); mp++) ; - if (mp[0]==':' && mp[1]!=':') return (levels-m_blockIndent/2); - } + const char* cp = strg; + while (isspace(*cp)) cp++; + switch (*cp) { + case '\n': // Newlines.. No need for whitespace before it + return (0); + case '#': // Preproc directive + return (0); + } + { + // label/public/private: Deindent by 2 spaces + const char* mp = cp; + for (; isalnum(*mp); mp++) ; + if (mp[0]==':' && mp[1]!=':') return (levels-m_blockIndent/2); + } } // We want "} else {" to be one level to the left of normal for (const char* cp=strg; *cp; cp++) { - switch (*cp) { - case '}': - case ')': - levels-=m_blockIndent; - break; - case '<': - if (m_lang==LA_XML) { - if (cp[1] == '/') levels-=m_blockIndent; - } - break; - case 'e': - if (m_lang==LA_VERILOG && tokenEnd(cp)) { - levels-=m_blockIndent; - } - break; - case '\t': - case ' ': - break; // Continue - default: - return (levels); // Letter - } + switch (*cp) { + case '}': + case ')': + levels -= m_blockIndent; + break; + case '<': + if (m_lang==LA_XML) { + if (cp[1] == '/') levels -= m_blockIndent; + } + break; + case 'e': + if (m_lang==LA_VERILOG && tokenEnd(cp)) { + levels -= m_blockIndent; + } + break; + case '\t': + case ' ': + break; // Continue + default: + return (levels); // Letter + } } return (levels); } void V3OutFormatter::puts(const char *strg) { if (m_prependIndent) { - putsNoTracking(indentStr(endLevels(strg))); - m_prependIndent = false; + putsNoTracking(indentStr(endLevels(strg))); + m_prependIndent = false; } bool wordstart = true; bool equalsForBracket = false; // Looking for "= {" for (const char* cp=strg; *cp; cp++) { putcNoTracking(*cp); - switch (*cp) { - case '\n': - m_lineno++; - wordstart = true; - if (cp[1]=='\0') { - m_prependIndent = true; // Add the indent later, may be a indentInc/indentDec called between now and then - } else { - m_prependIndent = false; - putsNoTracking(indentStr(endLevels(cp+1))); - } - break; - case ' ': - wordstart = true; - break; - case '\t': - wordstart = true; - break; - case '{': + switch (*cp) { + case '\n': + m_lineno++; + wordstart = true; + if (cp[1]=='\0') { + m_prependIndent = true; // Add the indent later, may be a indentInc/indentDec called between now and then + } else { + m_prependIndent = false; + putsNoTracking(indentStr(endLevels(cp+1))); + } + break; + case ' ': + wordstart = true; + break; + case '\t': + wordstart = true; + break; + case '{': if (m_lang==LA_C && (equalsForBracket || m_bracketLevel)) { // Break up large code inside "= { ..." m_parenVec.push(m_indentLevel*m_blockIndent); // Line up continuation with block+1 ++m_bracketLevel; } - indentInc(); - break; - case '}': + indentInc(); + break; + case '}': if (m_bracketLevel>0) { m_parenVec.pop(); --m_bracketLevel; } - indentDec(); - break; - case '(': - indentInc(); - if (v3Global.opt.decoration()) { - m_parenVec.push(m_column); // Line up continuation with open paren, plus one indent - } else { - m_parenVec.push(m_indentLevel*m_blockIndent); // Line up continuation with block+1 - } - break; - case ')': - if (!m_parenVec.empty()) m_parenVec.pop(); - indentDec(); - break; - case '<': - if (m_lang==LA_XML) { - if (cp[1] == '/') {} // Zero as the > will result in net decrease by one - else if (cp[1] == '!' || cp[1] == '?') { indentInc(); } // net same indent - else { indentInc(); indentInc(); } // net increase by one - } - break; - case '>': - if (m_lang==LA_XML) { - indentDec(); - if (cp>strg && cp[-1]=='/') indentDec(); // < ..... /> stays same level - } - break; - case 'b': - if (wordstart && m_lang==LA_VERILOG && tokenStart(cp,"begin")) { - indentInc(); - } - wordstart = false; - break; - case 'c': - if (wordstart && m_lang==LA_VERILOG - && (tokenStart(cp,"case") - || tokenStart(cp,"casex") - || tokenStart(cp,"casez"))) { - indentInc(); - } - wordstart = false; - break; - case 'e': - if (wordstart && m_lang==LA_VERILOG && tokenEnd(cp)) { - indentDec(); - } - wordstart = false; - break; - case 'm': - if (wordstart && m_lang==LA_VERILOG && tokenStart(cp,"module")) { - indentInc(); - } - wordstart = false; - break; - default: - wordstart = false; - break; - } + indentDec(); + break; + case '(': + indentInc(); + if (v3Global.opt.decoration()) { + m_parenVec.push(m_column); // Line up continuation with open paren, plus one indent + } else { + m_parenVec.push(m_indentLevel*m_blockIndent); // Line up continuation with block+1 + } + break; + case ')': + if (!m_parenVec.empty()) m_parenVec.pop(); + indentDec(); + break; + case '<': + if (m_lang==LA_XML) { + if (cp[1] == '/') {} // Zero as the > will result in net decrease by one + else if (cp[1] == '!' || cp[1] == '?') { indentInc(); } // net same indent + else { indentInc(); indentInc(); } // net increase by one + } + break; + case '>': + if (m_lang==LA_XML) { + indentDec(); + if (cp>strg && cp[-1]=='/') indentDec(); // < ..... /> stays same level + } + break; + case 'b': + if (wordstart && m_lang==LA_VERILOG && tokenStart(cp, "begin")) { + indentInc(); + } + wordstart = false; + break; + case 'c': + if (wordstart && m_lang==LA_VERILOG + && (tokenStart(cp, "case") + || tokenStart(cp, "casex") + || tokenStart(cp, "casez"))) { + indentInc(); + } + wordstart = false; + break; + case 'e': + if (wordstart && m_lang==LA_VERILOG && tokenEnd(cp)) { + indentDec(); + } + wordstart = false; + break; + case 'm': + if (wordstart && m_lang==LA_VERILOG && tokenStart(cp, "module")) { + indentInc(); + } + wordstart = false; + break; + default: + wordstart = false; + break; + } switch (*cp) { case '=': @@ -794,11 +798,11 @@ void V3OutFormatter::putBreakExpr() { // Add a line break if too wide void V3OutFormatter::putBreak() { if (!m_nobreak) { - //char s[1000]; sprintf(s,"{%d,%d}",m_column,m_parenVec.top()); putsNoTracking(s); - if (exceededWidth()) { - putcNoTracking('\n'); - if (!m_parenVec.empty()) putsNoTracking(indentStr(m_parenVec.top())); - } + //char s[1000]; sprintf(s, "{%d,%d}", m_column, m_parenVec.top()); putsNoTracking(s); + if (exceededWidth()) { + putcNoTracking('\n'); + if (!m_parenVec.empty()) putsNoTracking(indentStr(m_parenVec.top())); + } } } @@ -822,23 +826,23 @@ void V3OutFormatter::putsNoTracking(const string& strg) { void V3OutFormatter::putcNoTracking(char chr) { switch (chr) { case '\n': - m_lineno++; - m_column=0; - m_nobreak=true; - break; + m_lineno++; + m_column = 0; + m_nobreak = true; + break; case '\t': - m_column = ((m_column + 9)/8)*8; - break; + m_column = ((m_column + 9)/8)*8; + break; case ' ': case '(': case '|': case '&': - m_column++; - break; + m_column++; + break; default: - m_column++; - m_nobreak=false; - break; + m_column++; + m_nobreak = false; + break; } putcOutput(chr); } @@ -874,8 +878,8 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L void V3OutFormatter::printf(const char *fmt...) { char sbuff[5000]; va_list ap; - va_start(ap,fmt); - vsprintf(sbuff,fmt,ap); + va_start(ap, fmt); + vsprintf(sbuff, fmt, ap); va_end(ap); this->puts(sbuff); } @@ -886,7 +890,7 @@ void V3OutFormatter::printf(const char *fmt...) { V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang) : V3OutFormatter(filename, lang) { if ((m_fp = V3File::new_fopen_w(filename)) == NULL) { - v3fatal("Cannot write "< m_parenVec; // Stack of columns where last ( was int m_bracketLevel; // Intenting = { block, indicates number of {'s seen. - int endLevels(const char* strg); + int endLevels(const char* strg); const char* indentStr(int num); void putcNoTracking(char chr); @@ -138,7 +138,7 @@ public: // ACCESSORS int column() const { return m_column; } int blockIndent() const { return m_blockIndent; } - void blockIndent(int flag) { m_blockIndent=flag; } + void blockIndent(int flag) { m_blockIndent = flag; } // METHODS void printf(const char* fmt...) VL_ATTR_PRINTF(2); void puts(const char* strg); @@ -154,7 +154,7 @@ public: bool tokenEnd(const char* cp); void indentInc() { m_indentLevel += m_blockIndent; } void indentDec() { - m_indentLevel -= m_blockIndent; + m_indentLevel -= m_blockIndent; UASSERT(m_indentLevel>=0, ": "<printf("%-19s\t%s;\n", classStar.c_str(), cellname.c_str()); + string classStar = classname + "*"; + this->printf("%-19s\t%s;\n", classStar.c_str(), cellname.c_str()); } virtual void putsHeader() { puts("// Verilated -*- C++ -*-\n"); } virtual void putsIntTopInclude() { - putsForceIncs(); + putsForceIncs(); } // Print out public/privates void resetPrivate() { m_private = 0; } void putsPrivate(bool setPrivate) { - if (setPrivate && m_private!=1) { - puts("private:\n"); - m_private = 1; - } else if (!setPrivate && m_private!=2) { - puts("public:\n"); - m_private = 2; - } + if (setPrivate && m_private!=1) { + puts("private:\n"); + m_private = 1; + } else if (!setPrivate && m_private!=2) { + puts("public:\n"); + m_private = 2; + } } }; @@ -220,23 +220,25 @@ public: virtual ~V3OutScFile() {} virtual void putsHeader() { puts("// Verilated -*- SystemC -*-\n"); } virtual void putsIntTopInclude() { - putsForceIncs(); - puts("#include \"systemc.h\"\n"); - puts("#include \"verilated_sc.h\"\n"); + putsForceIncs(); + puts("#include \"systemc.h\"\n"); + puts("#include \"verilated_sc.h\"\n"); } }; class V3OutVFile : public V3OutFile { public: - explicit V3OutVFile(const string& filename) : V3OutFile(filename, V3OutFormatter::LA_VERILOG) {} + explicit V3OutVFile(const string& filename) + : V3OutFile(filename, V3OutFormatter::LA_VERILOG) {} virtual ~V3OutVFile() {} virtual void putsHeader() { puts("// Verilated -*- Verilog -*-\n"); } }; class V3OutXmlFile : public V3OutFile { public: - explicit V3OutXmlFile(const string& filename) : V3OutFile(filename, V3OutFormatter::LA_XML) { - blockIndent(2); + explicit V3OutXmlFile(const string& filename) + : V3OutFile(filename, V3OutFormatter::LA_XML) { + blockIndent(2); } virtual ~V3OutXmlFile() {} virtual void putsHeader() { puts("\n"); } @@ -244,7 +246,8 @@ public: class V3OutMkFile : public V3OutFile { public: - explicit V3OutMkFile(const string& filename) : V3OutFile(filename, V3OutFormatter::LA_MK) {} + explicit V3OutMkFile(const string& filename) + : V3OutFile(filename, V3OutFormatter::LA_MK) {} virtual ~V3OutMkFile() {} virtual void putsHeader() { puts("# Verilated -*- Makefile -*-\n"); } // No automatic indentation yet. @@ -252,4 +255,4 @@ public: void puts(const string& strg) { putsNoTracking(strg); } }; -#endif // Guard +#endif // Guard diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index e269e051e..83dbdf05f 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -63,7 +63,7 @@ int FileLineSingleton::nameToNumber(const string& filename) { int num = m_names.size(); m_names.push_back(filename); m_languages.push_back(V3LangCode::mostRecent()); - m_namemap.insert(make_pair(filename,num)); + m_namemap.insert(make_pair(filename, num)); return num; } @@ -73,9 +73,9 @@ int FileLineSingleton::nameToNumber(const string& filename) { void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) { os<<"\n"; for (FileNameNumMap::const_iterator it = m_namemap.begin(); it != m_namemap.end(); ++it) { - os<<"second) - <<"\" filename=\""<first - <<"\" language=\""<second).ascii()<<"\"/>\n"; + os<<"second) + <<"\" filename=\""<first + <<"\" language=\""<second).ascii()<<"\"/>\n"; } os<<"\n"; } @@ -85,10 +85,10 @@ void FileLineSingleton::fileNameNumMapDumpXml(std::ostream& os) { FileLine::FileLine(FileLine::EmptySecret) { // Sort of a singleton - m_lineno=0; - m_filenameno=singleton().nameToNumber("AstRoot"); + m_lineno = 0; + m_filenameno = singleton().nameToNumber("AstRoot"); - m_warnOn=0; + m_warnOn = 0; for (int codei=V3ErrorCode::EC_MIN; codei(objp); FileLineCheckSet::iterator it = fileLineLeakChecks.find(flp); if (it != fileLineLeakChecks.end()) { - fileLineLeakChecks.erase(it); + fileLineLeakChecks.erase(it); } else { - flp->v3fatalSrc("Deleting FileLine object that was never tracked"); + flp->v3fatalSrc("Deleting FileLine object that was never tracked"); } ::operator delete(objp); } @@ -279,11 +280,11 @@ void FileLine::deleteAllRemaining() { // that way. Unfortunately this makes our leak checking a big mess, so // only when leak checking we'll track them all and cleanup. while (1) { - FileLineCheckSet::iterator it=fileLineLeakChecks.begin(); - if (it==fileLineLeakChecks.end()) break; - delete *it; - // Operator delete will remove the iterated object from the list. - // Eventually the list will be empty and terminate the loop. + FileLineCheckSet::iterator it = fileLineLeakChecks.begin(); + if (it==fileLineLeakChecks.end()) break; + delete *it; + // Operator delete will remove the iterated object from the list. + // Eventually the list will be empty and terminate the loop. } fileLineLeakChecks.clear(); singleton().clear(); diff --git a/src/V3FileLine.h b/src/V3FileLine.h index e52dc5b4c..a8b82ca5d 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -46,9 +46,9 @@ class FileLineSingleton { typedef std::map FileNameNumMap; typedef std::map FileLangNumMap; // MEMBERS - FileNameNumMap m_namemap; // filenameno for each filename + FileNameNumMap m_namemap; // filenameno for each filename std::deque m_names; // filename text for each filenameno - std::deque m_languages; // language for each filenameno + std::deque m_languages; // language for each filenameno // COSNTRUCTORS FileLineSingleton() { } ~FileLineSingleton() { } @@ -70,19 +70,19 @@ protected: //! millions). To save space, per-file information (e.g. filename, source //! language is held in tables in the FileLineSingleton class. class FileLine { - int m_lineno; - int m_filenameno; + int m_lineno; + int m_filenameno; std::bitset m_warnOn; private: struct EmptySecret {}; inline static FileLineSingleton& singleton() { - static FileLineSingleton s; - return s; + static FileLineSingleton s; + return s; } inline static FileLine& defaultFileLine() { - static FileLine* defFilelinep = new FileLine(FileLine::EmptySecret()); - return *defFilelinep; + static FileLine* defFilelinep = new FileLine(FileLine::EmptySecret()); + return *defFilelinep; } protected: // User routines should never need to change line numbers @@ -101,13 +101,18 @@ protected: FileLine* copyOrSameFileLine(); public: FileLine(const string& filename, int lineno) { - m_lineno=lineno; m_filenameno = singleton().nameToNumber(filename); - m_warnOn=defaultFileLine().m_warnOn; } + m_lineno = lineno; + m_filenameno = singleton().nameToNumber(filename); + m_warnOn = defaultFileLine().m_warnOn; + } explicit FileLine(FileLine* fromp) { - m_lineno=fromp->m_lineno; m_filenameno = fromp->m_filenameno; m_warnOn=fromp->m_warnOn; } + m_lineno = fromp->m_lineno; + m_filenameno = fromp->m_filenameno; + m_warnOn = fromp->m_warnOn; + } explicit FileLine(EmptySecret); ~FileLine() { } - FileLine* create(const string& filename, int lineno) { return new FileLine(filename,lineno); } + FileLine* create(const string& filename, int lineno) { return new FileLine(filename, lineno); } FileLine* create(int lineno) { return create(filename(), lineno); } static void deleteAllRemaining(); #ifdef VL_LEAK_CHECKS @@ -125,32 +130,33 @@ public: const string profileFuncname() const; const string xml() const { return "fl=\""+filenameLetters()+cvtToStr(lineno())+"\""; } string lineDirectiveStrg(int enterExit) const; - void warnOn(V3ErrorCode code, bool flag) { m_warnOn.set(code,flag); } // Turn on/off warning messages on this line. - void warnOff(V3ErrorCode code, bool flag) { warnOn(code,!flag); } + // Turn on/off warning messages on this line. + void warnOn(V3ErrorCode code, bool flag) { m_warnOn.set(code, flag); } + void warnOff(V3ErrorCode code, bool flag) { warnOn(code, !flag); } bool warnOff(const string& msg, bool flag); // Returns 1 if ok bool warnIsOff(V3ErrorCode code) const; void warnLintOff(bool flag); void warnStyleOff(bool flag); - void warnStateFrom(const FileLine& from) { m_warnOn=from.m_warnOn; } + void warnStateFrom(const FileLine& from) { m_warnOn = from.m_warnOn; } void warnResetDefault() { warnStateFrom(defaultFileLine()); } // Specific flag ACCESSORS/METHODS bool coverageOn() const { return m_warnOn.test(V3ErrorCode::I_COVERAGE); } - void coverageOn(bool flag) { warnOn(V3ErrorCode::I_COVERAGE,flag); } + void coverageOn(bool flag) { warnOn(V3ErrorCode::I_COVERAGE, flag); } bool tracingOn() const { return m_warnOn.test(V3ErrorCode::I_TRACING); } - void tracingOn(bool flag) { warnOn(V3ErrorCode::I_TRACING,flag); } + void tracingOn(bool flag) { warnOn(V3ErrorCode::I_TRACING, flag); } // METHODS - Global static void globalWarnLintOff(bool flag) { - defaultFileLine().warnLintOff(flag); } + defaultFileLine().warnLintOff(flag); } static void globalWarnStyleOff(bool flag) { - defaultFileLine().warnStyleOff(flag); } + defaultFileLine().warnStyleOff(flag); } static void globalWarnOff(V3ErrorCode code, bool flag) { - defaultFileLine().warnOff(code, flag); } + defaultFileLine().warnOff(code, flag); } static bool globalWarnOff(const string& code, bool flag) { - return defaultFileLine().warnOff(code, flag); } + return defaultFileLine().warnOff(code, flag); } static void fileNameNumMapDumpXml(std::ostream& os) { - singleton().fileNameNumMapDumpXml(os); } + singleton().fileNameNumMapDumpXml(os); } // METHODS - Called from netlist // Merge warning disables from another fileline @@ -158,14 +164,15 @@ public: // Change the current fileline due to actions discovered after parsing // and may have side effects on other nodes sharing this FileLine. // Use only when this is intended - void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code,flag); } + void modifyWarnOff(V3ErrorCode code, bool flag) { warnOff(code, flag); } // OPERATORS void v3errorEnd(std::ostringstream& str); void v3errorEndFatal(std::ostringstream& str); string warnMore() const; inline bool operator==(FileLine rhs) const { - return (m_lineno==rhs.m_lineno && m_filenameno==rhs.m_filenameno && m_warnOn==rhs.m_warnOn); + return (m_lineno==rhs.m_lineno && m_filenameno==rhs.m_filenameno + && m_warnOn==rhs.m_warnOn); } private: void v3errorEndFatalGuts(std::ostringstream& str); diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index bef52573e..4e17bc6f2 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -61,8 +61,8 @@ class GateLogicVertex; class GateVarVertex; class GateGraphBaseVisitor { public: - virtual VNUser visit(GateLogicVertex* vertexp, VNUser vu=VNUser(0)) =0; - virtual VNUser visit(GateVarVertex* vertexp, VNUser vu=VNUser(0)) =0; + virtual VNUser visit(GateLogicVertex* vertexp, VNUser vu=VNUser(0)) = 0; + virtual VNUser visit(GateVarVertex* vertexp, VNUser vu=VNUser(0)) = 0; virtual ~GateGraphBaseVisitor() {} }; @@ -70,13 +70,14 @@ public: // Support classes class GateEitherVertex : public V3GraphVertex { - AstScope* m_scopep; - bool m_reducible; // True if this node should be able to be eliminated - bool m_dedupable; // True if this node should be able to be deduped - bool m_consumed; // Output goes to something meaningful + AstScope* m_scopep; // Scope vertex refers to + bool m_reducible; // True if this node should be able to be eliminated + bool m_dedupable; // True if this node should be able to be deduped + bool m_consumed; // Output goes to something meaningful public: GateEitherVertex(V3Graph* graphp, AstScope* scopep) - : V3GraphVertex(graphp), m_scopep(scopep), m_reducible(true), m_dedupable(true), m_consumed(false) {} + : V3GraphVertex(graphp), m_scopep(scopep), m_reducible(true) + , m_dedupable(true), m_consumed(false) {} virtual ~GateEitherVertex() {} // ACCESSORS virtual string dotStyle() const { return m_consumed?"":"dotted"; } @@ -84,57 +85,57 @@ public: bool reducible() const { return m_reducible; } bool dedupable() const { return m_dedupable; } void setConsumed(const char* consumedReason) { - m_consumed = true; - //UINFO(0,"\t\tSetConsumed "<inNextp()) { - ret = dynamic_cast(edgep->fromp())->accept(v, vu); - } - return ret; + VNUser ret = VNUser(0); + for (V3GraphEdge* edgep = inBeginp(); edgep; edgep = edgep->inNextp()) { + ret = dynamic_cast(edgep->fromp())->accept(v, vu); + } + return ret; } // Returns only the result from the LAST vertex iterated over // Note: This behaves differently than iterateInEdges() in that it will traverse - // all edges that exist when it is initially called, whereas - // iterateInEdges() will stop traversing edges if one is deleted + // all edges that exist when it is initially called, whereas + // iterateInEdges() will stop traversing edges if one is deleted VNUser iterateCurrentOutEdges(GateGraphBaseVisitor& v, VNUser vu=VNUser(0)) { - VNUser ret = VNUser(0); - V3GraphEdge* next_edgep = NULL; - for (V3GraphEdge* edgep = outBeginp(); edgep; edgep = next_edgep) { - // Need to find the next edge before visiting in case the edge is deleted - next_edgep = edgep->outNextp(); - ret = dynamic_cast(edgep->top())->accept(v, vu); - } - return ret; + VNUser ret = VNUser(0); + V3GraphEdge* next_edgep = NULL; + for (V3GraphEdge* edgep = outBeginp(); edgep; edgep = next_edgep) { + // Need to find the next edge before visiting in case the edge is deleted + next_edgep = edgep->outNextp(); + ret = dynamic_cast(edgep->top())->accept(v, vu); + } + return ret; } }; class GateVarVertex : public GateEitherVertex { AstVarScope* m_varScp; - bool m_isTop; - bool m_isClock; - AstNode* m_rstSyncNodep; // Used as reset and not in SenItem, in clocked always - AstNode* m_rstAsyncNodep; // Used as reset and in SenItem, in clocked always + bool m_isTop; + bool m_isClock; + AstNode* m_rstSyncNodep; // Used as reset and not in SenItem, in clocked always + AstNode* m_rstAsyncNodep; // Used as reset and in SenItem, in clocked always public: GateVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : GateEitherVertex(graphp, scopep), m_varScp(varScp), m_isTop(false) - , m_isClock(false), m_rstSyncNodep(NULL), m_rstAsyncNodep(NULL) {} + : GateEitherVertex(graphp, scopep), m_varScp(varScp), m_isTop(false) + , m_isClock(false), m_rstSyncNodep(NULL), m_rstAsyncNodep(NULL) {} virtual ~GateVarVertex() {} // ACCESSORS AstVarScope* varScp() const { return m_varScp; } @@ -145,28 +146,29 @@ public: bool isClock() const { return m_isClock; } void setIsClock() { m_isClock = true; setConsumed("isclk"); } AstNode* rstSyncNodep() const { return m_rstSyncNodep; } - void rstSyncNodep(AstNode* nodep) { m_rstSyncNodep=nodep; } + void rstSyncNodep(AstNode* nodep) { m_rstSyncNodep = nodep; } AstNode* rstAsyncNodep() const { return m_rstAsyncNodep; } - void rstAsyncNodep(AstNode* nodep) { m_rstAsyncNodep=nodep; } + void rstAsyncNodep(AstNode* nodep) { m_rstAsyncNodep = nodep; } // METHODS void propagateAttrClocksFrom(GateVarVertex* fromp) { - // Propagate clock and general attribute onto this node - varScp()->varp()->propagateAttrFrom(fromp->varScp()->varp()); - if (fromp->isClock()) { - varScp()->varp()->usedClock(true); - setIsClock(); - } + // Propagate clock and general attribute onto this node + varScp()->varp()->propagateAttrFrom(fromp->varScp()->varp()); + if (fromp->isClock()) { + varScp()->varp()->usedClock(true); + setIsClock(); + } } - VNUser accept(GateGraphBaseVisitor& v, VNUser vu=VNUser(0)) { return v.visit(this,vu); } + VNUser accept(GateGraphBaseVisitor& v, VNUser vu=VNUser(0)) { return v.visit(this, vu); } }; class GateLogicVertex : public GateEitherVertex { - AstNode* m_nodep; - AstActive* m_activep; // Under what active; NULL is ok (under cfunc or such) - bool m_slow; // In slow block + AstNode* m_nodep; + AstActive* m_activep; // Under what active; NULL is ok (under cfunc or such) + bool m_slow; // In slow block public: - GateLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstActive* activep, bool slow) - : GateEitherVertex(graphp,scopep), m_nodep(nodep), m_activep(activep), m_slow(slow) {} + GateLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, + AstActive* activep, bool slow) + : GateEitherVertex(graphp ,scopep), m_nodep(nodep), m_activep(activep), m_slow(slow) {} virtual ~GateLogicVertex() {} // ACCESSORS virtual string name() const { return (cvtToHex(m_nodep)+"@"+scopep()->prettyName()); } @@ -174,8 +176,8 @@ public: virtual FileLine* fileline() const { return nodep()->fileline(); } AstNode* nodep() const { return m_nodep; } AstActive* activep() const { return m_activep; } - bool slow() const { return m_slow; } - VNUser accept(GateGraphBaseVisitor& v, VNUser vu=VNUser(0)) { return v.visit(this,vu); } + bool slow() const { return m_slow; } + VNUser accept(GateGraphBaseVisitor& v, VNUser vu=VNUser(0)) { return v.visit(this, vu); } }; //###################################################################### @@ -184,118 +186,119 @@ public: class GateOkVisitor : public GateBaseVisitor { private: // RETURN STATE - bool m_isSimple; // Set false when we know it isn't simple - GateVarRefList m_rhsVarRefs; // VarRefs on rhs of assignment - AstNode* m_substTreep; // What to replace the variable with + bool m_isSimple; // Set false when we know it isn't simple + GateVarRefList m_rhsVarRefs; // VarRefs on rhs of assignment + AstNode* m_substTreep; // What to replace the variable with // STATE - bool m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks) - AstNodeVarRef* m_lhsVarRef; // VarRef on lhs of assignment (what we're replacing) - bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable + bool m_buffersOnly; // Set when we only allow simple buffering, no equations (for clocks) + AstNodeVarRef* m_lhsVarRef; // VarRef on lhs of assignment (what we're replacing) + bool m_dedupe; // Set when we use isGateDedupable instead of isGateOptimizable int m_ops; // Operation count // METHODS void clearSimple(const char* because) { - if (m_isSimple) { - m_isSimple = false; - UINFO(9, "Clear simple "<varScopep()->varp()->isSc()) { - clearSimple("SystemC sig"); // Don't want to eliminate the VL_ASSIGN_SI's - } - if (nodep->lvalue()) { - if (m_lhsVarRef) clearSimple(">1 lhs varRefs"); - m_lhsVarRef = nodep; - } else { - if (m_rhsVarRefs.size()>1) { - AstNodeVarRef* lastRefp = m_rhsVarRefs.back(); - if (0) { // Diable the multiple-input optimization - clearSimple(">1 rhs varRefs"); - } else { - if (m_buffersOnly) clearSimple(">1 rhs varRefs"); - if (!nodep->varScopep()->varp()->gateMultiInputOptimizable() - // We didn't check multiInput on the first varref, so check it here - || !lastRefp->varScopep()->varp()->gateMultiInputOptimizable()) { - clearSimple("!gateMultiInputOptimizable"); - } - } - } - m_rhsVarRefs.push_back(nodep); - } + // We only allow a LHS ref for the var being set, and a RHS ref for + // something else being read. + if (nodep->varScopep()->varp()->isSc()) { + clearSimple("SystemC sig"); // Don't want to eliminate the VL_ASSIGN_SI's + } + if (nodep->lvalue()) { + if (m_lhsVarRef) clearSimple(">1 lhs varRefs"); + m_lhsVarRef = nodep; + } else { + if (m_rhsVarRefs.size()>1) { + AstNodeVarRef* lastRefp = m_rhsVarRefs.back(); + if (0) { // Diable the multiple-input optimization + clearSimple(">1 rhs varRefs"); + } else { + if (m_buffersOnly) clearSimple(">1 rhs varRefs"); + if (!nodep->varScopep()->varp()->gateMultiInputOptimizable() + // We didn't check multiInput on the first varref, so check it here + || !lastRefp->varScopep()->varp()->gateMultiInputOptimizable()) { + clearSimple("!gateMultiInputOptimizable"); + } + } + } + m_rhsVarRefs.push_back(nodep); + } } virtual void visit(AstNodeAssign* nodep) { - m_substTreep = nodep->rhsp(); + m_substTreep = nodep->rhsp(); if (!VN_IS(nodep->lhsp(), NodeVarRef)) { clearSimple("ASSIGN(non-VARREF)"); } else { iterateChildren(nodep); } - // We don't push logic other then assignments/NOTs into SenItems - // This avoids a mess in computing what exactly a POSEDGE is - // V3Const cleans up any NOTs by flipping the edges for us - if (m_buffersOnly + // We don't push logic other then assignments/NOTs into SenItems + // This avoids a mess in computing what exactly a POSEDGE is + // V3Const cleans up any NOTs by flipping the edges for us + if (m_buffersOnly && !(VN_IS(nodep->rhsp(), VarRef) - // Avoid making non-clocked logic into clocked, - // as it slows down the verilator_sim_benchmark + // Avoid making non-clocked logic into clocked, + // as it slows down the verilator_sim_benchmark || (VN_IS(nodep->rhsp(), Not) && VN_IS(VN_CAST(nodep->rhsp(), Not)->lhsp(), VarRef) && VN_CAST(VN_CAST(nodep->rhsp(), Not)->lhsp(), VarRef)->varp()->isUsedClock()) - )) { - clearSimple("Not a buffer (goes to a clock)"); - } + )) { + clearSimple("Not a buffer (goes to a clock)"); + } } //-------------------- // Default virtual void visit(AstNode* nodep) { - // *** Special iterator - if (!m_isSimple) return; // Fastpath + // *** Special iterator + if (!m_isSimple) return; // Fastpath if (++m_ops > v3Global.opt.gateStmts()) { clearSimple("--gate-stmts exceeded"); } - if (!(m_dedupe ? nodep->isGateDedupable() : nodep->isGateOptimizable()) - || !nodep->isPure() - || nodep->isBrancher()) { - UINFO(5, "Non optimizable type: "<isGateDedupable() : nodep->isGateOptimizable()) + || !nodep->isPure() + || nodep->isBrancher()) { + UINFO(5, "Non optimizable type: "<varScopep() == (*it)->varScopep()) { - clearSimple("Circular logic\n"); // Oh my, we'll get a UNOPTFLAT much later. - } - } - if (debug()>=9 && !m_isSimple) { - nodep->dumpTree(cout,"\tgate!Ok: "); - } + // Check results + if (!m_substTreep) { + clearSimple("No assignment found\n"); + } + for (GateVarRefList::const_iterator it = m_rhsVarRefs.begin(); + it != m_rhsVarRefs.end(); ++it) { + if (m_lhsVarRef && m_lhsVarRef->varScopep() == (*it)->varScopep()) { + clearSimple("Circular logic\n"); // Oh my, we'll get a UNOPTFLAT much later. + } + } + if (debug()>=9 && !m_isSimple) { + nodep->dumpTree(cout, "\tgate!Ok: "); + } } virtual ~GateOkVisitor() {} // PUBLIC METHODS bool isSimple() const { return m_isSimple; } AstNode* substTree() const { return m_substTreep; } const GateVarRefList& rhsVarRefs() const { - return m_rhsVarRefs; + return m_rhsVarRefs; } }; @@ -306,66 +309,66 @@ class GateVisitor : public GateBaseVisitor { private: // NODE STATE //Entire netlist: - // AstVarScope::user1p -> GateVarVertex* for usage var, 0=not set yet - // {statement}Node::user1p -> GateLogicVertex* for this statement - // AstVarScope::user2 -> bool: Signal used in SenItem in *this* always statement - // AstVar::user2 -> bool: Warned about SYNCASYNCNET + // AstVarScope::user1p -> GateVarVertex* for usage var, 0=not set yet + // {statement}Node::user1p -> GateLogicVertex* for this statement + // AstVarScope::user2 -> bool: Signal used in SenItem in *this* always statement + // AstVar::user2 -> bool: Warned about SYNCASYNCNET // AstNodeVarRef::user2 -> bool: ConcatOffset visited - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // STATE - V3Graph m_graph; // Scoreboard of var usages/dependencies - GateLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored - AstScope* m_scopep; // Current scope being processed - AstNodeModule* m_modp; // Current module - AstActive* m_activep; // Current active - bool m_activeReducible; // Is activation block reducible? - bool m_inSenItem; // Underneath AstSenItem; any varrefs are clocks - bool m_inSlow; // Inside a slow structure - V3Double0 m_statSigs; // Statistic tracking - V3Double0 m_statRefs; // Statistic tracking - V3Double0 m_statDedupLogic; // Statistic tracking - V3Double0 m_statAssignMerged; // Statistic tracking + V3Graph m_graph; // Scoreboard of var usages/dependencies + GateLogicVertex* m_logicVertexp; // Current statement being tracked, NULL=ignored + AstScope* m_scopep; // Current scope being processed + AstNodeModule* m_modp; // Current module + AstActive* m_activep; // Current active + bool m_activeReducible; // Is activation block reducible? + bool m_inSenItem; // Underneath AstSenItem; any varrefs are clocks + bool m_inSlow; // Inside a slow structure + V3Double0 m_statSigs; // Statistic tracking + V3Double0 m_statRefs; // Statistic tracking + V3Double0 m_statDedupLogic; // Statistic tracking + V3Double0 m_statAssignMerged; // Statistic tracking // METHODS void iterateNewStmt(AstNode* nodep, const char* nonReducibleReason, const char* consumeReason) { - if (m_scopep) { + if (m_scopep) { UINFO(5," STMT "<clearReducibleAndDedupable(nonReducibleReason); - } else if (!m_activeReducible) { - m_logicVertexp->clearReducible("Block Unreducible"); // Sequential logic is dedupable - } - if (consumeReason) m_logicVertexp->setConsumed(consumeReason); + // m_activep is null under AstCFunc's, that's ok. + m_logicVertexp = new GateLogicVertex(&m_graph, m_scopep, nodep, m_activep, m_inSlow); + if (nonReducibleReason) { + m_logicVertexp->clearReducibleAndDedupable(nonReducibleReason); + } else if (!m_activeReducible) { + m_logicVertexp->clearReducible("Block Unreducible"); // Sequential logic is dedupable + } + if (consumeReason) m_logicVertexp->setConsumed(consumeReason); if (VN_IS(nodep, SenItem)) m_logicVertexp->setConsumed("senItem"); iterateChildren(nodep); - m_logicVertexp = NULL; - } + m_logicVertexp = NULL; + } } GateVarVertex* makeVarVertex(AstVarScope* varscp) { GateVarVertex* vertexp = reinterpret_cast(varscp->user1p()); - if (!vertexp) { - UINFO(6,"New vertex "<user1p(vertexp); - if (varscp->varp()->isSigPublic()) { - // Public signals shouldn't be changed, pli code might be messing with them - vertexp->clearReducibleAndDedupable("SigPublic"); - vertexp->setConsumed("SigPublic"); - } - if (varscp->varp()->isIO() && varscp->scopep()->isTop()) { - // We may need to convert to/from sysc/reg sigs - vertexp->setIsTop(); - vertexp->clearReducibleAndDedupable("isTop"); - vertexp->setConsumed("isTop"); - } - if (varscp->varp()->isUsedClock()) vertexp->setConsumed("clock"); - } - return vertexp; + if (!vertexp) { + UINFO(6,"New vertex "<user1p(vertexp); + if (varscp->varp()->isSigPublic()) { + // Public signals shouldn't be changed, pli code might be messing with them + vertexp->clearReducibleAndDedupable("SigPublic"); + vertexp->setConsumed("SigPublic"); + } + if (varscp->varp()->isIO() && varscp->scopep()->isTop()) { + // We may need to convert to/from sysc/reg sigs + vertexp->setIsTop(); + vertexp->clearReducibleAndDedupable("isTop"); + vertexp->setConsumed("isTop"); + } + if (varscp->varp()->isUsedClock()) vertexp->setConsumed("clock"); + } + return vertexp; } void optimizeSignals(bool allowMultiIn); @@ -383,132 +386,133 @@ private: // VISITORS virtual void visit(AstNetlist* nodep) { iterateChildren(nodep); - //if (debug()>6) m_graph.dump(); - if (debug()>6) m_graph.dumpDotFilePrefixed("gate_pre"); - warnSignals(); // Before loss of sync/async pointers - // Decompose clock vectors -- need to do this before removing redundant edges - decomposeClkVectors(); - m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue); - m_graph.dumpDotFilePrefixed("gate_simp"); - // Find gate interconnect and optimize - m_graph.userClearVertices(); // vertex->user(): bool. True indicates we've set it as consumed - // Get rid of buffers first, - optimizeSignals(false); - // Then propagate more complicated equations - optimizeSignals(true); - // Remove redundant logic - if (v3Global.opt.oDedupe()) dedupe(); - if (v3Global.opt.oAssemble()) mergeAssigns(); - // Consumption warnings - consumedMark(); - m_graph.dumpDotFilePrefixed("gate_opt"); - // Rewrite assignments - consumedMove(); - replaceAssigns(); + //if (debug()>6) m_graph.dump(); + if (debug()>6) m_graph.dumpDotFilePrefixed("gate_pre"); + warnSignals(); // Before loss of sync/async pointers + // Decompose clock vectors -- need to do this before removing redundant edges + decomposeClkVectors(); + m_graph.removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue); + m_graph.dumpDotFilePrefixed("gate_simp"); + // Find gate interconnect and optimize + m_graph.userClearVertices(); // vertex->user(): bool. True indicates we've set it as consumed + // Get rid of buffers first, + optimizeSignals(false); + // Then propagate more complicated equations + optimizeSignals(true); + // Remove redundant logic + if (v3Global.opt.oDedupe()) dedupe(); + if (v3Global.opt.oAssemble()) mergeAssigns(); + // Consumption warnings + consumedMark(); + m_graph.dumpDotFilePrefixed("gate_opt"); + // Rewrite assignments + consumedMove(); + replaceAssigns(); } virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; - m_activeReducible = true; + m_modp = nodep; + m_activeReducible = true; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstScope* nodep) { - UINFO(4," SCOPE "<hasClocked()); // Seq logic outputs aren't reducible - m_activep = nodep; - AstNode::user2ClearTree(); + // Create required blocks and add to module + UINFO(4," BLOCK "<hasClocked()); // Seq logic outputs aren't reducible + m_activep = nodep; + AstNode::user2ClearTree(); iterateChildren(nodep); - AstNode::user2ClearTree(); - m_activep = NULL; - m_activeReducible = true; + AstNode::user2ClearTree(); + m_activep = NULL; + m_activeReducible = true; } virtual void visit(AstNodeVarRef* nodep) { - if (m_scopep) { - if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block"); - AstVarScope* varscp = nodep->varScopep(); - if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); - GateVarVertex* vvertexp = makeVarVertex(varscp); - UINFO(5," VARREF to "<setIsClock(); - // For SYNCASYNCNET - if (m_inSenItem) varscp->user2(true); - else if (m_activep && m_activep->hasClocked() && !nodep->lvalue()) { - if (varscp->user2()) { - if (!vvertexp->rstAsyncNodep()) vvertexp->rstAsyncNodep(nodep); - } else { - if (!vvertexp->rstSyncNodep()) vvertexp->rstSyncNodep(nodep); - } - } - // We use weight of one; if we ref the var more than once, when we simplify, - // the weight will increase - if (nodep->lvalue()) { - new V3GraphEdge(&m_graph, m_logicVertexp, vvertexp, 1); - } else { - new V3GraphEdge(&m_graph, vvertexp, m_logicVertexp, 1); - } - } + if (m_scopep) { + if (!m_logicVertexp) nodep->v3fatalSrc("Var ref not under a logic block"); + AstVarScope* varscp = nodep->varScopep(); + if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); + GateVarVertex* vvertexp = makeVarVertex(varscp); + UINFO(5," VARREF to "<setIsClock(); + // For SYNCASYNCNET + if (m_inSenItem) varscp->user2(true); + else if (m_activep && m_activep->hasClocked() && !nodep->lvalue()) { + if (varscp->user2()) { + if (!vvertexp->rstAsyncNodep()) vvertexp->rstAsyncNodep(nodep); + } else { + if (!vvertexp->rstSyncNodep()) vvertexp->rstSyncNodep(nodep); + } + } + // We use weight of one; if we ref the var more than once, when we simplify, + // the weight will increase + if (nodep->lvalue()) { + new V3GraphEdge(&m_graph, m_logicVertexp, vvertexp, 1); + } else { + new V3GraphEdge(&m_graph, vvertexp, m_logicVertexp, 1); + } + } } virtual void visit(AstAlways* nodep) { - iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL); + iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL); } virtual void visit(AstAlwaysPublic* nodep) { - bool lastslow = m_inSlow; - m_inSlow = true; - iterateNewStmt(nodep, "AlwaysPublic", NULL); - m_inSlow = lastslow; + bool lastslow = m_inSlow; + m_inSlow = true; + iterateNewStmt(nodep, "AlwaysPublic", NULL); + m_inSlow = lastslow; } virtual void visit(AstCFunc* nodep) { - iterateNewStmt(nodep, "User C Function", "User C Function"); + iterateNewStmt(nodep, "User C Function", "User C Function"); } virtual void visit(AstSenItem* nodep) { - // Note we look at only AstSenItems, not AstSenGate's - // The gating term of a AstSenGate is normal logic - m_inSenItem = true; - if (m_logicVertexp) { // Already under logic; presumably a SenGate + // Note we look at only AstSenItems, not AstSenGate's + // The gating term of a AstSenGate is normal logic + m_inSenItem = true; + if (m_logicVertexp) { // Already under logic; presumably a SenGate iterateChildren(nodep); - } else { // Standalone item, probably right under a SenTree - iterateNewStmt(nodep, NULL, NULL); - } - m_inSenItem = false; + } else { // Standalone item, probably right under a SenTree + iterateNewStmt(nodep, NULL, NULL); + } + m_inSenItem = false; } virtual void visit(AstSenGate* nodep) { - // First handle the clock part will be handled in a minute by visit AstSenItem - // The logic gating term is delt with as logic - iterateNewStmt(nodep, "Clock gater", "Clock gater"); + // First handle the clock part will be handled in a minute by visit AstSenItem + // The logic gating term is delt with as logic + iterateNewStmt(nodep, "Clock gater", "Clock gater"); } virtual void visit(AstInitial* nodep) { - bool lastslow = m_inSlow; - m_inSlow = true; - iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL); - m_inSlow = lastslow; + bool lastslow = m_inSlow; + m_inSlow = true; + iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL); + m_inSlow = lastslow; } virtual void visit(AstAssignAlias* nodep) { - iterateNewStmt(nodep, NULL, NULL); + iterateNewStmt(nodep, NULL, NULL); } virtual void visit(AstAssignW* nodep) { - iterateNewStmt(nodep, NULL, NULL); + iterateNewStmt(nodep, NULL, NULL); } virtual void visit(AstCoverToggle* nodep) { - iterateNewStmt(nodep, "CoverToggle", "CoverToggle"); + iterateNewStmt(nodep, "CoverToggle", "CoverToggle"); } virtual void visit(AstTraceInc* nodep) { - bool lastslow = m_inSlow; - m_inSlow = true; - iterateNewStmt(nodep, "Tracing", "Tracing"); - m_inSlow = lastslow; + bool lastslow = m_inSlow; + m_inSlow = true; + iterateNewStmt(nodep, "Tracing", "Tracing"); + m_inSlow = lastslow; } virtual void visit(AstConcat* nodep) { - if (VN_IS(nodep->backp(), NodeAssign) && VN_CAST(nodep->backp(), NodeAssign)->lhsp()==nodep) { - nodep->v3fatalSrc("Concat on LHS of assignment; V3Const should have deleted it"); - } + if (VN_IS(nodep->backp(), NodeAssign) + && VN_CAST(nodep->backp(), NodeAssign)->lhsp()==nodep) { + nodep->v3fatalSrc("Concat on LHS of assignment; V3Const should have deleted it"); + } iterateChildren(nodep); } @@ -516,27 +520,27 @@ private: // Default virtual void visit(AstNode* nodep) { iterateChildren(nodep); - if (nodep->isOutputter() && m_logicVertexp) m_logicVertexp->setConsumed("outputter"); + if (nodep->isOutputter() && m_logicVertexp) m_logicVertexp->setConsumed("outputter"); } public: // CONSTUCTORS explicit GateVisitor(AstNode* nodep) { - AstNode::user1ClearTree(); - m_logicVertexp = NULL; - m_scopep = NULL; - m_modp = NULL; - m_activep = NULL; - m_activeReducible = true; - m_inSenItem = false; - m_inSlow = false; + AstNode::user1ClearTree(); + m_logicVertexp = NULL; + m_scopep = NULL; + m_modp = NULL; + m_activep = NULL; + m_activeReducible = true; + m_inSenItem = false; + m_inSlow = false; iterate(nodep); } virtual ~GateVisitor() { - V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs); - V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs); - V3Stats::addStat("Optimizations, Gate sigs deduped", m_statDedupLogic); - V3Stats::addStat("Optimizations, Gate assign merged", m_statAssignMerged); + V3Stats::addStat("Optimizations, Gate sigs deleted", m_statSigs); + V3Stats::addStat("Optimizations, Gate inputs replaced", m_statRefs); + V3Stats::addStat("Optimizations, Gate sigs deduped", m_statDedupLogic); + V3Stats::addStat("Optimizations, Gate assign merged", m_statAssignMerged); } }; @@ -544,193 +548,208 @@ public: void GateVisitor::optimizeSignals(bool allowMultiIn) { for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (GateVarVertex* vvertexp = dynamic_cast(itp)) { - if (vvertexp->inEmpty()) { - vvertexp->clearReducibleAndDedupable("inEmpty"); // Can't deal with no sources - if (!vvertexp->isTop() // Ok if top inputs are driverless - && !vvertexp->varScp()->varp()->valuep() - && !vvertexp->varScp()->varp()->isSigPublic()) { - UINFO(4, "No drivers "<varScp()<varScp()->varp()->v3warn(UNDRIVEN,"Signal has no drivers " - <scopep()->prettyName()<<"." - <varScp()->varp()->prettyName()); - } - } - } - else if (!vvertexp->inSize1()) { - vvertexp->clearReducibleAndDedupable("size!1"); // Can't deal with more than one src - } - // Reduce it? - if (!vvertexp->reducible()) { - UINFO(8, "SigNotRed "<name()<name()< - (vvertexp->inBeginp()->fromp()); - UINFO(8, " From "<name()<nodep(); - if (logicVertexp->reducible()) { - // Can we eliminate? - GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false); - bool multiInputs = okVisitor.rhsVarRefs().size() > 1; - // Was it ok? - bool doit = okVisitor.isSimple(); - if (doit && multiInputs) { - if (!allowMultiIn) doit = false; - // Doit if one input, or not used, or used only once, ignoring traces - int n=0; - for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - GateLogicVertex* consumeVertexp = dynamic_cast(edgep->top()); - if (!consumeVertexp->slow()) { // Not tracing or other slow path junk - if (edgep->top()->outBeginp()) { // Destination is itself used - n += edgep->weight(); - } - } - if (n>1) { - doit = false; - break; - } - } - } - // Process it - if (!doit) { - if (allowMultiIn && (debug()>=9)) { - UINFO(9, "Not ok simp"<outBeginp()<<" on"<<(vvertexp->outBeginp()?vvertexp->outBeginp()->outNextp():0) - <<" "<name() - <outBeginp(); edgep; edgep = edgep->outNextp()) { - GateLogicVertex* consumeVertexp = dynamic_cast(edgep->top()); - UINFO(9, " edge "<nodep()<inBeginp(); edgep; edgep = edgep->inNextp()) { - GateLogicVertex* consumeVertexp = dynamic_cast(edgep->fromp()); - UINFO(9, " edge "<nodep()<=5) logicp->dumpTree(cout,"\telimVar: "); - if (debug()>=5) substp->dumpTree(cout,"\t subst: "); - ++m_statSigs; - bool removedAllUsages = true; - for (V3GraphEdge* edgep = vvertexp->outBeginp(); - edgep; ) { - GateLogicVertex* consumeVertexp = dynamic_cast(edgep->top()); - AstNode* consumerp = consumeVertexp->nodep(); - if (!elimLogicOkOutputs(consumeVertexp, okVisitor/*ref*/)) { - // Cannot optimize this replacement - removedAllUsages = false; - edgep = edgep->outNextp(); - } else { - optimizeElimVar(vvertexp->varScp(), substp, consumerp); - // If the new replacement referred to a signal, - // Correct the graph to point to this new generating variable - const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs(); - for (GateVarRefList::const_iterator it = rhsVarRefs.begin(); - it != rhsVarRefs.end(); ++it) { - AstVarScope* newvarscp = (*it)->varScopep(); - UINFO(9," Point-to-new vertex "<propagateAttrClocksFrom(vvertexp); - } - // Remove the edge - edgep->unlinkDelete(); VL_DANGLING(edgep); - ++m_statRefs; - edgep = vvertexp->outBeginp(); - } - } - if (removedAllUsages) { - // Remove input links - while (V3GraphEdge* edgep = vvertexp->inBeginp()) { - edgep->unlinkDelete(); VL_DANGLING(edgep); - } - // Clone tree so we remember it for tracing, and keep the pointer - // to the "ALWAYS" part of the tree as part of this statement - // That way if a later signal optimization that retained a pointer to the always - // can optimize it further - logicp->unlinkFrBack(); - vvertexp->varScp()->valuep(logicp); - logicp = NULL; - // Mark the vertex so we don't mark it as being unconsumed in the next step - vvertexp->user(true); - logicVertexp->user(true); - } - } - } - } - } + if (GateVarVertex* vvertexp = dynamic_cast(itp)) { + if (vvertexp->inEmpty()) { + vvertexp->clearReducibleAndDedupable("inEmpty"); // Can't deal with no sources + if (!vvertexp->isTop() // Ok if top inputs are driverless + && !vvertexp->varScp()->varp()->valuep() + && !vvertexp->varScp()->varp()->isSigPublic()) { + UINFO(4, "No drivers "<varScp()<varScp()->varp()->v3warn + (UNDRIVEN, "Signal has no drivers " + <scopep()->prettyName()<<"." + <varScp()->varp()->prettyName()); + } + } + } + else if (!vvertexp->inSize1()) { + vvertexp->clearReducibleAndDedupable("size!1"); // Can't deal with more than one src + } + // Reduce it? + if (!vvertexp->reducible()) { + UINFO(8, "SigNotRed "<name()<name()< + (vvertexp->inBeginp()->fromp()); + UINFO(8, " From "<name()<nodep(); + if (logicVertexp->reducible()) { + // Can we eliminate? + GateOkVisitor okVisitor(logicp, vvertexp->isClock(), false); + bool multiInputs = okVisitor.rhsVarRefs().size() > 1; + // Was it ok? + bool doit = okVisitor.isSimple(); + if (doit && multiInputs) { + if (!allowMultiIn) doit = false; + // Doit if one input, or not used, or used only once, ignoring traces + int n = 0; + for (V3GraphEdge* edgep = vvertexp->outBeginp(); + edgep; edgep = edgep->outNextp()) { + GateLogicVertex* consumeVertexp + = dynamic_cast(edgep->top()); + if (!consumeVertexp->slow()) { // Not tracing or other slow path junk + if (edgep->top()->outBeginp()) { // Destination is itself used + n += edgep->weight(); + } + } + if (n>1) { + doit = false; + break; + } + } + } + // Process it + if (!doit) { + if (allowMultiIn && (debug()>=9)) { + UINFO(9, "Not ok simp"<outBeginp() + <<" on"<<(vvertexp->outBeginp()?vvertexp->outBeginp()->outNextp():0) + <<" "<name() + <outBeginp(); + edgep; edgep = edgep->outNextp()) { + GateLogicVertex* consumeVertexp + = dynamic_cast(edgep->top()); + UINFO(9, " edge "<nodep()<inBeginp(); + edgep; edgep = edgep->inNextp()) { + GateLogicVertex* consumeVertexp + = dynamic_cast(edgep->fromp()); + UINFO(9, " edge "<nodep()<=5) logicp->dumpTree(cout, "\telimVar: "); + if (debug()>=5) substp->dumpTree(cout, "\t subst: "); + ++m_statSigs; + bool removedAllUsages = true; + for (V3GraphEdge* edgep = vvertexp->outBeginp(); + edgep; ) { + GateLogicVertex* consumeVertexp + = dynamic_cast(edgep->top()); + AstNode* consumerp = consumeVertexp->nodep(); + if (!elimLogicOkOutputs(consumeVertexp, okVisitor/*ref*/)) { + // Cannot optimize this replacement + removedAllUsages = false; + edgep = edgep->outNextp(); + } else { + optimizeElimVar(vvertexp->varScp(), substp, consumerp); + // If the new replacement referred to a signal, + // Correct the graph to point to this new generating variable + const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs(); + for (GateVarRefList::const_iterator it = rhsVarRefs.begin(); + it != rhsVarRefs.end(); ++it) { + AstVarScope* newvarscp = (*it)->varScopep(); + UINFO(9," Point-to-new vertex "<propagateAttrClocksFrom(vvertexp); + } + // Remove the edge + edgep->unlinkDelete(); VL_DANGLING(edgep); + ++m_statRefs; + edgep = vvertexp->outBeginp(); + } + } + if (removedAllUsages) { + // Remove input links + while (V3GraphEdge* edgep = vvertexp->inBeginp()) { + edgep->unlinkDelete(); VL_DANGLING(edgep); + } + // Clone tree so we remember it for tracing, and keep the pointer + // to the "ALWAYS" part of the tree as part of this statement + // That way if a later signal optimization that + // retained a pointer to the always can + // optimize it further + logicp->unlinkFrBack(); + vvertexp->varScp()->valuep(logicp); + logicp = NULL; + // Mark the vertex so we don't mark it as being + // unconsumed in the next step + vvertexp->user(true); + logicVertexp->user(true); + } + } + } + } + } } } -bool GateVisitor::elimLogicOkOutputs(GateLogicVertex* consumeVertexp, const GateOkVisitor& okVisitor) { +bool GateVisitor::elimLogicOkOutputs(GateLogicVertex* consumeVertexp, + const GateOkVisitor& okVisitor) { // Return true if can optimize - // Return false if the consuming logic has an output signal that the replacement logic has as an input + // Return false if the consuming logic has an output signal that the + // replacement logic has as an input typedef vl_unordered_set VarScopeSet; // Use map to find duplicates between two lists VarScopeSet varscopes; // Replacement logic usually has shorter input list, so faster to build list based on it const GateVarRefList& rhsVarRefs = okVisitor.rhsVarRefs(); for (GateVarRefList::const_iterator it = rhsVarRefs.begin(); - it != rhsVarRefs.end(); ++it) { - AstVarScope* vscp = (*it)->varScopep(); - if (varscopes.find(vscp) == varscopes.end()) varscopes.insert(vscp); + it != rhsVarRefs.end(); ++it) { + AstVarScope* vscp = (*it)->varScopep(); + if (varscopes.find(vscp) == varscopes.end()) varscopes.insert(vscp); } for (V3GraphEdge* edgep = consumeVertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - GateVarVertex* consVVertexp = dynamic_cast(edgep->top()); - AstVarScope* vscp = consVVertexp->varScp(); - if (varscopes.find(vscp) != varscopes.end()) { - UINFO(9," Block-unopt, insertion generates input vscp "<(edgep->top()); + AstVarScope* vscp = consVVertexp->varScp(); + if (varscopes.find(vscp) != varscopes.end()) { + UINFO(9," Block-unopt, insertion generates input vscp "<verticesNextp()) { - if (GateVarVertex* vvertexp = dynamic_cast(itp)) { - // Take the Comments/assigns that were moved to the VarScope and change them to a - // simple value assignment - AstVarScope* vscp = vvertexp->varScp(); + if (GateVarVertex* vvertexp = dynamic_cast(itp)) { + // Take the Comments/assigns that were moved to the VarScope and change them to a + // simple value assignment + AstVarScope* vscp = vvertexp->varScp(); if (vscp->valuep() && !VN_IS(vscp->valuep(), NodeMath)) { - //if (debug()>9) vscp->dumpTree(cout, "-vscPre: "); - while (AstNode* delp=VN_CAST(vscp->valuep(), Comment)) { - delp->unlinkFrBack()->deleteTree(); VL_DANGLING(delp); - } - if (AstInitial* delp=VN_CAST(vscp->valuep(), Initial)) { - AstNode* bodyp=delp->bodysp(); - bodyp->unlinkFrBackWithNext(); - delp->replaceWith(bodyp); - delp->deleteTree(); VL_DANGLING(delp); - } - if (AstAlways* delp=VN_CAST(vscp->valuep(), Always)) { - AstNode* bodyp=delp->bodysp(); - bodyp->unlinkFrBackWithNext(); - delp->replaceWith(bodyp); - delp->deleteTree(); VL_DANGLING(delp); - } - if (AstNodeAssign* delp=VN_CAST(vscp->valuep(), NodeAssign)) { - AstNode* rhsp=delp->rhsp(); - rhsp->unlinkFrBack(); - delp->replaceWith(rhsp); - delp->deleteTree(); VL_DANGLING(delp); - } - //if (debug()>9) {vscp->dumpTree(cout, "-vscDone: "); cout<9) vscp->dumpTree(cout, "-vscPre: "); + while (AstNode* delp = VN_CAST(vscp->valuep(), Comment)) { + delp->unlinkFrBack()->deleteTree(); VL_DANGLING(delp); + } + if (AstInitial* delp = VN_CAST(vscp->valuep(), Initial)) { + AstNode* bodyp = delp->bodysp(); + bodyp->unlinkFrBackWithNext(); + delp->replaceWith(bodyp); + delp->deleteTree(); VL_DANGLING(delp); + } + if (AstAlways* delp = VN_CAST(vscp->valuep(), Always)) { + AstNode* bodyp = delp->bodysp(); + bodyp->unlinkFrBackWithNext(); + delp->replaceWith(bodyp); + delp->deleteTree(); VL_DANGLING(delp); + } + if (AstNodeAssign* delp = VN_CAST(vscp->valuep(), NodeAssign)) { + AstNode* rhsp = delp->rhsp(); + rhsp->unlinkFrBack(); + delp->replaceWith(rhsp); + delp->deleteTree(); VL_DANGLING(delp); + } + //if (debug()>9) {vscp->dumpTree(cout, "-vscDone: "); cout<valuep(), NodeMath) - || vscp->valuep()->nextp()) { + || vscp->valuep()->nextp()) { vscp->dumpTree(std::cerr, "vscStrange: "); - vscp->v3fatalSrc("Value of varscope not mathematical"); - } - } - } + vscp->v3fatalSrc("Value of varscope not mathematical"); + } + } + } } } @@ -739,44 +758,47 @@ void GateVisitor::replaceAssigns() { void GateVisitor::consumedMark() { // Propagate consumed signals backwards to all producers into a consumed node m_graph.userClearVertices(); - for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { GateEitherVertex* evertexp = static_cast(vertexp); - if (!evertexp->user() && evertexp->consumed()) { - consumedMarkRecurse(evertexp); - } + if (!evertexp->user() && evertexp->consumed()) { + consumedMarkRecurse(evertexp); + } } } void GateVisitor::consumedMarkRecurse(GateEitherVertex* vertexp) { - if (vertexp->user()) return; // Already marked + if (vertexp->user()) return; // Already marked vertexp->user(true); if (!vertexp->consumed()) vertexp->setConsumed("propagated"); // Walk sources and mark them too for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { GateEitherVertex* eFromVertexp = static_cast(edgep->fromp()); - consumedMarkRecurse(eFromVertexp); + consumedMarkRecurse(eFromVertexp); } } void GateVisitor::consumedMove() { // Remove unused logic (logic that doesn't hit a combo block or a display statement) // We need the "usually" block logic to do a better job at this - for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (GateVarVertex* vvertexp = dynamic_cast(vertexp)) { - if (!vvertexp->consumed() && !vvertexp->user()) { - UINFO(8, "Unconsumed "<varScp()<(vertexp)) { - AstNode* nodep = lvertexp->nodep(); - AstActive* oldactp = lvertexp->activep(); // NULL under cfunc - if (!lvertexp->consumed() && oldactp) { - // Eventually: Move the statement to a new active block with "tracing-on" sensitivity - UINFO(8," Remove unconsumed "<unlinkFrBack(); - pushDeletep(nodep); VL_DANGLING(nodep); - } - } + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (GateVarVertex* vvertexp = dynamic_cast(vertexp)) { + if (!vvertexp->consumed() && !vvertexp->user()) { + UINFO(8, "Unconsumed "<varScp()<(vertexp)) { + AstNode* nodep = lvertexp->nodep(); + AstActive* oldactp = lvertexp->activep(); // NULL under cfunc + if (!lvertexp->consumed() && oldactp) { + // Eventually: Move the statement to a new active block + // with "tracing-on" sensitivity + UINFO(8," Remove unconsumed "<unlinkFrBack(); + pushDeletep(nodep); VL_DANGLING(nodep); + } + } } } @@ -785,26 +807,27 @@ void GateVisitor::consumedMove() { void GateVisitor::warnSignals() { AstNode::user2ClearTree(); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (GateVarVertex* vvertexp = dynamic_cast(itp)) { - AstVarScope* vscp = vvertexp->varScp(); - AstNode* sp = vvertexp->rstSyncNodep(); - AstNode* ap = vvertexp->rstAsyncNodep(); - if (ap && sp && !vscp->varp()->user2()) { - // This is somewhat wrong, as marking one flop as ok for sync - // may mean a different flop now fails. However it's a pain to - // then report a warning in a new place - we should report them all at once. - // Instead we'll disable if any disabled - if (!vscp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) - && !ap->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) - && !sp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) - ) { - vscp->varp()->user2(true); // Warn only once per signal - vscp->v3warn(SYNCASYNCNET,"Signal flopped as both synchronous and async: "<prettyName()<warnMore()<<"... Location of async usage"<warnMore()<<"... Location of sync usage"<(itp)) { + AstVarScope* vscp = vvertexp->varScp(); + AstNode* sp = vvertexp->rstSyncNodep(); + AstNode* ap = vvertexp->rstAsyncNodep(); + if (ap && sp && !vscp->varp()->user2()) { + // This is somewhat wrong, as marking one flop as ok for sync + // may mean a different flop now fails. However it's a pain to + // then report a warning in a new place - we should report them all at once. + // Instead we'll disable if any disabled + if (!vscp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) + && !ap->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) + && !sp->fileline()->warnIsOff(V3ErrorCode::SYNCASYNCNET) + ) { + vscp->varp()->user2(true); // Warn only once per signal + vscp->v3warn(SYNCASYNCNET, "Signal flopped as both synchronous and async: " + <prettyName()<warnMore()<<"... Location of async usage"<warnMore()<<"... Location of sync usage"<varScopep() == m_elimVarScp) { - // Substitute in the new tree - // It's possible we substitute into something that will be reduced more later - // however, as we never delete the top Always/initial statement, all should be well. - m_didReplace = true; - if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var"); - AstNode* substp = m_replaceTreep->cloneTree(false); + if (nodep->varScopep() == m_elimVarScp) { + // Substitute in the new tree + // It's possible we substitute into something that will be reduced more later + // however, as we never delete the top Always/initial statement, all should be well. + m_didReplace = true; + if (nodep->lvalue()) nodep->v3fatalSrc("Can't replace lvalue assignments with const var"); + AstNode* substp = m_replaceTreep->cloneTree(false); if (VN_IS(nodep, NodeVarRef) && VN_IS(substp, NodeVarRef) - && nodep->same(substp)) { - // Prevent a infinite loop... - substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?"); - } - // Which fileline() to use? - // If replacing with logic, an error/warning is likely to want to point to the logic - // IE what we're replacing with. - // However a VARREF should point to the original as it's otherwise confusing - // to throw warnings that point to a PIN rather than where the pin us used. + && nodep->same(substp)) { + // Prevent a infinite loop... + substp->v3fatalSrc("Replacing node with itself; perhaps circular logic?"); + } + // Which fileline() to use? + // If replacing with logic, an error/warning is likely to want to point to the logic + // IE what we're replacing with. + // However a VARREF should point to the original as it's otherwise confusing + // to throw warnings that point to a PIN rather than where the pin us used. if (VN_IS(substp, VarRef)) substp->fileline(nodep->fileline()); - // Make the substp an rvalue like nodep. This facilitate the hashing in dedupe. + // Make the substp an rvalue like nodep. This facilitate the hashing in dedupe. if (AstNodeVarRef* varrefp = VN_CAST(substp, NodeVarRef)) varrefp->lvalue(false); - nodep->replaceWith(substp); - nodep->deleteTree(); VL_DANGLING(nodep); - } + nodep->replaceWith(substp); + nodep->deleteTree(); VL_DANGLING(nodep); + } } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -852,26 +875,26 @@ public: // CONSTUCTORS virtual ~GateElimVisitor() {} GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep) { - m_didReplace = false; - m_elimVarScp = varscp; - m_replaceTreep = replaceTreep; + m_didReplace = false; + m_elimVarScp = varscp; + m_replaceTreep = replaceTreep; iterate(nodep); } bool didReplace() const { return m_didReplace; } }; void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) { - if (debug()>=5) consumerp->dumpTree(cout,"\telimUsePre: "); + if (debug()>=5) consumerp->dumpTree(cout, "\telimUsePre: "); GateElimVisitor elimVisitor (consumerp, varscp, substp); if (elimVisitor.didReplace()) { - if (debug()>=9) consumerp->dumpTree(cout,"\telimUseCns: "); - //Caution: Can't let V3Const change our handle to consumerp, such as by - // optimizing away this assignment, etc. - consumerp = V3Const::constifyEdit(consumerp); - if (debug()>=5) consumerp->dumpTree(cout,"\telimUseDne: "); - // Some previous input edges may have disappeared, perhaps all of them. - // If we remove the edges we can further optimize - // See e.g t_var_overzero.v. + if (debug()>=9) consumerp->dumpTree(cout, "\telimUseCns: "); + //Caution: Can't let V3Const change our handle to consumerp, such as by + // optimizing away this assignment, etc. + consumerp = V3Const::constifyEdit(consumerp); + if (debug()>=5) consumerp->dumpTree(cout, "\telimUseDne: "); + // Some previous input edges may have disappeared, perhaps all of them. + // If we remove the edges we can further optimize + // See e.g t_var_overzero.v. } } @@ -881,55 +904,55 @@ void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* class GateDedupeHash : public V3HashedUserCheck { private: // NODE STATE - // Ast*::user2p -> parent AstNodeAssign* for this rhsp - // Ast*::user3p -> AstNode* checked in test for duplicate - // Ast*::user5p -> AstNode* checked in test for duplicate - // AstUser2InUse m_inuser2; (Allocated for use in GateVisitor) - AstUser3InUse m_inuser3; - AstUser5InUse m_inuser5; - V3Hashed m_hashed; // Hash, contains rhs of assigns + // Ast*::user2p -> parent AstNodeAssign* for this rhsp + // Ast*::user3p -> AstNode* checked in test for duplicate + // Ast*::user5p -> AstNode* checked in test for duplicate + // AstUser2InUse m_inuser2; (Allocated for use in GateVisitor) + AstUser3InUse m_inuser3; + AstUser5InUse m_inuser5; + V3Hashed m_hashed; // Hash, contains rhs of assigns void hash(AstNode* nodep) { - // !NULL && the object is hashable - if (nodep && !nodep->sameHash().isIllegal()) { - m_hashed.hash(nodep); - } + // !NULL && the object is hashable + if (nodep && !nodep->sameHash().isIllegal()) { + m_hashed.hash(nodep); + } } bool sameHash(AstNode* node1p, AstNode* node2p) { - return (node1p && node2p - && !node1p->sameHash().isIllegal() - && !node2p->sameHash().isIllegal() - && m_hashed.sameNodes(node1p,node2p)); + return (node1p && node2p + && !node1p->sameHash().isIllegal() + && !node2p->sameHash().isIllegal() + && m_hashed.sameNodes(node1p, node2p)); } bool same(AstNode* node1p, AstNode* node2p) { - return node1p == node2p || sameHash(node1p,node2p); + return node1p == node2p || sameHash(node1p, node2p); } public: - bool check(AstNode* node1p,AstNode* node2p) { - return same(node1p->user3p(),node2p->user3p()) && same(node1p->user5p(),node2p->user5p()) - && node1p->user2p()->type() == node2p->user2p()->type() - ; + bool check(AstNode* node1p, AstNode* node2p) { + return same(node1p->user3p(), node2p->user3p()) + && same(node1p->user5p(), node2p->user5p()) + && node1p->user2p()->type() == node2p->user2p()->type(); } AstNodeAssign* hashAndFindDupe(AstNodeAssign* assignp, AstNode* extra1p, AstNode* extra2p) { - AstNode *rhsp = assignp->rhsp(); - rhsp->user2p(assignp); - rhsp->user3p(extra1p); - rhsp->user5p(extra2p); + AstNode *rhsp = assignp->rhsp(); + rhsp->user2p(assignp); + rhsp->user3p(extra1p); + rhsp->user5p(extra2p); - hash(extra1p); - hash(extra2p); + hash(extra1p); + hash(extra2p); - V3Hashed::iterator inserted = m_hashed.hashAndInsert(rhsp); - V3Hashed::iterator dupit = m_hashed.findDuplicate(rhsp, this); - // Even though rhsp was just inserted, V3Hashed::findDuplicate doesn't - // return anything in the hash that has the same pointer (V3Hashed.cpp::findDuplicate) - // So dupit is either a different, duplicate rhsp, or the end of the hash. - if (dupit != m_hashed.end()) { - m_hashed.erase(inserted); + V3Hashed::iterator inserted = m_hashed.hashAndInsert(rhsp); + V3Hashed::iterator dupit = m_hashed.findDuplicate(rhsp, this); + // Even though rhsp was just inserted, V3Hashed::findDuplicate doesn't + // return anything in the hash that has the same pointer (V3Hashed.cpp::findDuplicate) + // So dupit is either a different, duplicate rhsp, or the end of the hash. + if (dupit != m_hashed.end()) { + m_hashed.erase(inserted); return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign); - } - return NULL; + } + return NULL; } }; @@ -937,7 +960,8 @@ public: // Have we seen the rhs of this assign before? class GateDedupeVarVisitor : public GateBaseVisitor { - // Given a node, it is visited to try to find the AstNodeAssign under it that can used for dedupe. + // Given a node, it is visited to try to find the AstNodeAssign under + // it that can used for dedupe. // Right now, only the following node trees are supported for dedupe. // 1. AstNodeAssign // 2. AstAlways -> AstNodeAssign @@ -948,81 +972,84 @@ class GateDedupeVarVisitor : public GateBaseVisitor { // Any other ordering or node type, except for an AstComment, makes it not dedupable private: // STATE - GateDedupeHash m_hash; // Hash used to find dupes of rhs of assign - AstNodeAssign* m_assignp; // Assign found for dedupe - AstNode* m_ifCondp; // IF condition that assign is under - bool m_always; // Assign is under an always - bool m_dedupable; // Determined the assign to be dedupable + GateDedupeHash m_hash; // Hash used to find dupes of rhs of assign + AstNodeAssign* m_assignp; // Assign found for dedupe + AstNode* m_ifCondp; // IF condition that assign is under + bool m_always; // Assign is under an always + bool m_dedupable; // Determined the assign to be dedupable // VISITORS virtual void visit(AstNodeAssign* assignp) { - if (m_dedupable) { - // I think we could safely dedupe an always block with multiple non-blocking statements, but erring on side of caution here - if (!m_assignp) { - m_assignp = assignp; - } else { - m_dedupable = false; - } - } + if (m_dedupable) { + // I think we could safely dedupe an always block with multiple + // non-blocking statements, but erring on side of caution here + if (!m_assignp) { + m_assignp = assignp; + } else { + m_dedupable = false; + } + } } virtual void visit(AstAlways* alwaysp) { - if (m_dedupable) { - if (!m_always) { - m_always = true; + if (m_dedupable) { + if (!m_always) { + m_always = true; iterateAndNextNull(alwaysp->bodysp()); - } else { - m_dedupable = false; - } - } + } else { + m_dedupable = false; + } + } } // Ugly support for latches of the specific form - // always @(...) // if (...) // foo = ...; // or foo <= ...; virtual void visit(AstNodeIf* ifp) { - if (m_dedupable) { - if (m_always && !m_ifCondp && !ifp->elsesp()) { //we're under an always, this is the first IF, and there's no else - m_ifCondp = ifp->condp(); + if (m_dedupable) { + if (m_always && !m_ifCondp && !ifp->elsesp()) { //we're under an always, this is the first IF, and there's no else + m_ifCondp = ifp->condp(); iterateAndNextNull(ifp->ifsp()); - } else { - m_dedupable = false; - } - } + } else { + m_dedupable = false; + } + } } virtual void visit(AstComment*) {} // NOP //-------------------- // Default virtual void visit(AstNode*) { - m_dedupable = false; + m_dedupable = false; } public: // CONSTUCTORS GateDedupeVarVisitor() { - m_assignp = NULL; - m_ifCondp = NULL; - m_always = false; - m_dedupable = true; + m_assignp = NULL; + m_ifCondp = NULL; + m_always = false; + m_dedupable = true; } // PUBLIC METHODS AstNodeVarRef* findDupe(AstNode* nodep, AstVarScope* consumerVarScopep, AstActive* activep) { - m_assignp = NULL; - m_ifCondp = NULL; - m_always = false; - m_dedupable = true; + m_assignp = NULL; + m_ifCondp = NULL; + m_always = false; + m_dedupable = true; iterate(nodep); - if (m_dedupable && m_assignp) { - AstNode* lhsp = m_assignp->lhsp(); - // Possible todo, handle more complex lhs expressions + if (m_dedupable && m_assignp) { + AstNode* lhsp = m_assignp->lhsp(); + // Possible todo, handle more complex lhs expressions if (AstNodeVarRef* lhsVarRefp = VN_CAST(lhsp, NodeVarRef)) { - if (lhsVarRefp->varScopep() != consumerVarScopep) consumerVarScopep->v3fatalSrc("Consumer doesn't match lhs of assign"); - if (AstNodeAssign* dup = m_hash.hashAndFindDupe(m_assignp,activep,m_ifCondp)) { + if (lhsVarRefp->varScopep() != consumerVarScopep) { + consumerVarScopep->v3fatalSrc("Consumer doesn't match lhs of assign"); + } + if (AstNodeAssign* dup = m_hash.hashAndFindDupe(m_assignp, activep, m_ifCondp)) { return static_cast(dup->lhsp()); - } - } - } - return NULL; + } + } + } + return NULL; } }; @@ -1032,86 +1059,87 @@ public: class GateDedupeGraphVisitor : public GateGraphBaseVisitor { private: // NODE STATE - // AstVarScope::user2p -> bool: already visited - // AstUser2InUse m_inuser2; (Allocated for use in GateVisitor) - V3Double0 m_numDeduped; // Statistic tracking - GateDedupeVarVisitor m_varVisitor; // Looks for a dupe of the logic - int m_depth; // Iteration depth + // AstVarScope::user2p -> bool: already visited + // AstUser2InUse m_inuser2; (Allocated for use in GateVisitor) + V3Double0 m_numDeduped; // Statistic tracking + GateDedupeVarVisitor m_varVisitor; // Looks for a dupe of the logic + int m_depth; // Iteration depth virtual VNUser visit(GateVarVertex* vvertexp, VNUser) { - // Check that we haven't been here before - if (m_depth > GATE_DEDUP_MAX_DEPTH) return VNUser(0); // Break loops; before user2 set so hit this vertex later - if (vvertexp->varScp()->user2()) return VNUser(0); - vvertexp->varScp()->user2(true); + // Check that we haven't been here before + if (m_depth > GATE_DEDUP_MAX_DEPTH) return VNUser(0); // Break loops; before user2 set so hit this vertex later + if (vvertexp->varScp()->user2()) return VNUser(0); + vvertexp->varScp()->user2(true); - m_depth++; - if (vvertexp->inSize1()) { + m_depth++; + if (vvertexp->inSize1()) { AstNodeVarRef* dupVarRefp = static_cast (vvertexp->iterateInEdges(*this, VNUser(vvertexp)).toNodep()); - if (dupVarRefp) { - V3GraphEdge* edgep = vvertexp->inBeginp(); + if (dupVarRefp) { + V3GraphEdge* edgep = vvertexp->inBeginp(); GateLogicVertex* lvertexp = static_cast(edgep->fromp()); - if (!vvertexp->dedupable()) vvertexp->varScp()->v3fatalSrc("GateLogicVertex* visit should have returned NULL if consumer var vertex is not dedupable."); - GateOkVisitor okVisitor(lvertexp->nodep(), false, true); - if (okVisitor.isSimple()) { - AstVarScope* dupVarScopep = dupVarRefp->varScopep(); + if (!vvertexp->dedupable()) vvertexp->varScp()->v3fatalSrc("GateLogicVertex* visit should have returned NULL if consumer var vertex is not dedupable."); + GateOkVisitor okVisitor(lvertexp->nodep(), false, true); + if (okVisitor.isSimple()) { + AstVarScope* dupVarScopep = dupVarRefp->varScopep(); GateVarVertex* dupVvertexp = reinterpret_cast(dupVarScopep->user1p()); - UINFO(4,"replacing " << vvertexp << " with " << dupVvertexp << endl); - ++m_numDeduped; - // Replace all of this varvertex's consumers with dupVarRefp - for (V3GraphEdge* outedgep = vvertexp->outBeginp(); outedgep; ) { - GateLogicVertex* consumeVertexp = dynamic_cast(outedgep->top()); - AstNode* consumerp = consumeVertexp->nodep(); - GateElimVisitor elimVisitor(consumerp,vvertexp->varScp(),dupVarRefp); - outedgep = outedgep->relinkFromp(dupVvertexp); - } + UINFO(4,"replacing " << vvertexp << " with " << dupVvertexp << endl); + ++m_numDeduped; + // Replace all of this varvertex's consumers with dupVarRefp + for (V3GraphEdge* outedgep = vvertexp->outBeginp(); outedgep; ) { + GateLogicVertex* consumeVertexp + = dynamic_cast(outedgep->top()); + AstNode* consumerp = consumeVertexp->nodep(); + GateElimVisitor elimVisitor(consumerp, vvertexp->varScp(), dupVarRefp); + outedgep = outedgep->relinkFromp(dupVvertexp); + } - // Propagate attributes - dupVvertexp->propagateAttrClocksFrom(vvertexp); - // Remove inputs links - while (V3GraphEdge* inedgep = vvertexp->inBeginp()) { - inedgep->unlinkDelete(); VL_DANGLING(inedgep); - } - // replaceAssigns() does the deleteTree on lvertexNodep in a later step - AstNode* lvertexNodep = lvertexp->nodep(); - lvertexNodep->unlinkFrBack(); - vvertexp->varScp()->valuep(lvertexNodep); - lvertexNodep = NULL; - vvertexp->user(true); - lvertexp->user(true); - } - } - } - m_depth--; - return VNUser(0); + // Propagate attributes + dupVvertexp->propagateAttrClocksFrom(vvertexp); + // Remove inputs links + while (V3GraphEdge* inedgep = vvertexp->inBeginp()) { + inedgep->unlinkDelete(); VL_DANGLING(inedgep); + } + // replaceAssigns() does the deleteTree on lvertexNodep in a later step + AstNode* lvertexNodep = lvertexp->nodep(); + lvertexNodep->unlinkFrBack(); + vvertexp->varScp()->valuep(lvertexNodep); + lvertexNodep = NULL; + vvertexp->user(true); + lvertexp->user(true); + } + } + } + m_depth--; + return VNUser(0); } // Given iterated logic, starting at vu which was consumer's GateVarVertex // Returns a varref that has the same logic input; or NULL if none virtual VNUser visit(GateLogicVertex* lvertexp, VNUser vu) { - lvertexp->iterateInEdges(*this); + lvertexp->iterateInEdges(*this); GateVarVertex* consumerVvertexpp = static_cast(vu.toGraphVertex()); - if (lvertexp->dedupable() && consumerVvertexpp->dedupable()) { - AstNode* nodep = lvertexp->nodep(); - AstVarScope* consumerVarScopep = consumerVvertexpp->varScp(); - // TODO: Doing a simple pointer comparison of activep won't work - // optimally for statements under generated clocks. Statements under - // different generated clocks will never compare as equal, even if the - // generated clocks are deduped into one clock. - AstActive* activep = lvertexp->activep(); - return VNUser(m_varVisitor.findDupe(nodep, consumerVarScopep, activep)); - } - return VNUser(0); + if (lvertexp->dedupable() && consumerVvertexpp->dedupable()) { + AstNode* nodep = lvertexp->nodep(); + AstVarScope* consumerVarScopep = consumerVvertexpp->varScp(); + // TODO: Doing a simple pointer comparison of activep won't work + // optimally for statements under generated clocks. Statements under + // different generated clocks will never compare as equal, even if the + // generated clocks are deduped into one clock. + AstActive* activep = lvertexp->activep(); + return VNUser(m_varVisitor.findDupe(nodep, consumerVarScopep, activep)); + } + return VNUser(0); } public: GateDedupeGraphVisitor() { - m_depth = 0; + m_depth = 0; } void dedupeTree(GateVarVertex* vvertexp) { - vvertexp->accept(*this); + vvertexp->accept(*this); } V3Double0 numDeduped() { return m_numDeduped; } }; @@ -1123,11 +1151,11 @@ void GateVisitor::dedupe() { GateDedupeGraphVisitor deduper; // Traverse starting from each of the clocks for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (GateVarVertex* vvertexp = dynamic_cast(itp)) { - if (vvertexp->isClock()) { - deduper.dedupeTree(vvertexp); - } - } + if (GateVarVertex* vvertexp = dynamic_cast(itp)) { + if (vvertexp->isClock()) { + deduper.dedupeTree(vvertexp); + } + } } // Traverse starting from each of the outputs for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { @@ -1151,19 +1179,21 @@ private: AstActive* m_activep; GateLogicVertex* m_logicvp; V3Graph* m_graphp; - V3Double0 m_numMergedAssigns; // Statistic tracking + V3Double0 m_numMergedAssigns; // Statistic tracking // assemble two Sel into one if possible AstSel* merge(AstSel* pre, AstSel* cur) { AstVarRef* preVarRefp = VN_CAST(pre->fromp(), VarRef); AstVarRef* curVarRefp = VN_CAST(cur->fromp(), VarRef); - if (!preVarRefp || !curVarRefp || !curVarRefp->same(preVarRefp)) return NULL; // not the same var + if (!preVarRefp || !curVarRefp || !curVarRefp->same(preVarRefp)) { + return NULL; // not the same var + } const AstConst* pstart = VN_CAST(pre->lsbp(), Const); const AstConst* pwidth = VN_CAST(pre->widthp(), Const); const AstConst* cstart = VN_CAST(cur->lsbp(), Const); const AstConst* cwidth = VN_CAST(cur->widthp(), Const); - if (!pstart || !pwidth || !cstart || !cwidth) return NULL; // too complicated + if (!pstart || !pwidth || !cstart || !cwidth) return NULL; // too complicated if (cur->lsbConst()+cur->widthConst() == pre->lsbConst()) { return new AstSel(curVarRefp->fileline(), curVarRefp->cloneTree(false), cur->lsbConst(), pre->widthConst()+cur->widthConst()); @@ -1171,84 +1201,92 @@ private: } virtual VNUser visit(GateVarVertex *vvertexp, VNUser) { - for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; ) { - V3GraphEdge* oldedgep = edgep; - edgep = edgep->inNextp(); // for recursive since the edge could be deleted - if (GateLogicVertex* lvertexp = dynamic_cast(oldedgep->fromp())) { + for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; ) { + V3GraphEdge* oldedgep = edgep; + edgep = edgep->inNextp(); // for recursive since the edge could be deleted + if (GateLogicVertex* lvertexp = dynamic_cast(oldedgep->fromp())) { if (AstNodeAssign* assignp = VN_CAST(lvertexp->nodep(), NodeAssign)) { //if (lvertexp->outSize1() && VN_IS(assignp->lhsp(), Sel)) { if (VN_IS(assignp->lhsp(), Sel) && lvertexp->outSize1()) { - UINFO(9, "assing to the nodep["<lhsp(), Sel)->lsbConst()<<"]"<activep(); - if (!m_logicvp) m_logicvp = lvertexp; - if (!m_assignp) m_assignp = assignp; + UINFO(9, "assing to the nodep[" + <lhsp(), Sel)->lsbConst()<<"]"<activep(); + if (!m_logicvp) m_logicvp = lvertexp; + if (!m_assignp) m_assignp = assignp; - // not under the same active - if (m_activep != lvertexp->activep()) { - m_activep = lvertexp->activep(); - m_logicvp = lvertexp; - m_assignp = assignp; - continue; - } + // not under the same active + if (m_activep != lvertexp->activep()) { + m_activep = lvertexp->activep(); + m_logicvp = lvertexp; + m_assignp = assignp; + continue; + } AstSel* preselp = VN_CAST(m_assignp->lhsp(), Sel); AstSel* curselp = VN_CAST(assignp->lhsp(), Sel); - if (!preselp || !curselp) continue; + if (!preselp || !curselp) continue; - if (AstSel* newselp = merge(preselp, curselp)) { - UINFO(5, "assemble to new sel: "<replaceWith(newselp); preselp->deleteTree(); VL_DANGLING(preselp); - // create new rhs for pre assignment - AstNode* newrhsp = new AstConcat(m_assignp->rhsp()->fileline(), m_assignp->rhsp()->cloneTree(false), assignp->rhsp()->cloneTree(false)); - AstNode* oldrhsp = m_assignp->rhsp(); - oldrhsp->replaceWith(newrhsp); oldrhsp->deleteTree(); VL_DANGLING(oldrhsp); - m_assignp->dtypeChgWidthSigned(m_assignp->width()+assignp->width(), m_assignp->width()+assignp->width(), AstNumeric::fromBool(true)); - // don't need to delete, will be handled - //assignp->unlinkFrBack(); assignp->deleteTree(); VL_DANGLING(assignp); + if (AstSel* newselp = merge(preselp, curselp)) { + UINFO(5, "assemble to new sel: "<replaceWith(newselp); + preselp->deleteTree(); VL_DANGLING(preselp); + // create new rhs for pre assignment + AstNode* newrhsp = new AstConcat( + m_assignp->rhsp()->fileline(), m_assignp->rhsp()->cloneTree(false), + assignp->rhsp()->cloneTree(false)); + AstNode* oldrhsp = m_assignp->rhsp(); + oldrhsp->replaceWith(newrhsp); + oldrhsp->deleteTree(); VL_DANGLING(oldrhsp); + m_assignp->dtypeChgWidthSigned(m_assignp->width()+assignp->width(), + m_assignp->width()+assignp->width(), + AstNumeric::fromBool(true)); + // don't need to delete, will be handled + //assignp->unlinkFrBack(); assignp->deleteTree(); VL_DANGLING(assignp); - // update the graph - { - // delete all inedges to lvertexp - if (!lvertexp->inEmpty()) { - for (V3GraphEdge* ledgep = lvertexp->inBeginp(); ledgep; ) { - V3GraphEdge* oedgep = ledgep; - ledgep = ledgep->inNextp(); - GateEitherVertex* fromvp = dynamic_cast(oedgep->fromp()); - new V3GraphEdge(m_graphp, fromvp, m_logicvp, 1); - oedgep->unlinkDelete(); VL_DANGLING(oedgep); - } - } - // delete all outedges to lvertexp, only one - oldedgep->unlinkDelete(); VL_DANGLING(oldedgep); - } - ++m_numMergedAssigns; - } else { - m_assignp = assignp; - m_logicvp = lvertexp; - } - } - } - } - } - return VNUser(0); + // update the graph + { + // delete all inedges to lvertexp + if (!lvertexp->inEmpty()) { + for (V3GraphEdge* ledgep = lvertexp->inBeginp(); ledgep; ) { + V3GraphEdge* oedgep = ledgep; + ledgep = ledgep->inNextp(); + GateEitherVertex* fromvp + = dynamic_cast(oedgep->fromp()); + new V3GraphEdge(m_graphp, fromvp, m_logicvp, 1); + oedgep->unlinkDelete(); VL_DANGLING(oedgep); + } + } + // delete all outedges to lvertexp, only one + oldedgep->unlinkDelete(); VL_DANGLING(oldedgep); + } + ++m_numMergedAssigns; + } else { + m_assignp = assignp; + m_logicvp = lvertexp; + } + } + } + } + } + return VNUser(0); } virtual VNUser visit(GateLogicVertex* lvertexp, VNUser vu) { - return VNUser(0); + return VNUser(0); } public: explicit GateMergeAssignsGraphVisitor(V3Graph* graphp) { - m_assignp = NULL; - m_activep = NULL; - m_logicvp = NULL; - m_numMergedAssigns = 0; - m_graphp = graphp; + m_assignp = NULL; + m_activep = NULL; + m_logicvp = NULL; + m_numMergedAssigns = 0; + m_graphp = graphp; } void mergeAssignsTree(GateVarVertex* vvertexp) { - vvertexp->accept(*this); + vvertexp->accept(*this); } V3Double0 numMergedAssigns() { return m_numMergedAssigns; } }; @@ -1259,9 +1297,9 @@ public: void GateVisitor::mergeAssigns() { GateMergeAssignsGraphVisitor merger(&m_graph); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (GateVarVertex* vvertexp = dynamic_cast(itp)) { - merger.mergeAssignsTree(vvertexp); - } + if (GateVarVertex* vvertexp = dynamic_cast(itp)) { + merger.mergeAssignsTree(vvertexp); + } } m_statAssignMerged += merger.numMergedAssigns(); } @@ -1272,26 +1310,26 @@ void GateVisitor::mergeAssigns() { class GateConcatVisitor : public GateBaseVisitor { private: // STATE - AstVarScope* m_vscp; // Varscope we're trying to find - int m_offset; // Current offset of varscope - int m_found_offset; // Found offset of varscope - bool m_found; // Offset found + AstVarScope* m_vscp; // Varscope we're trying to find + int m_offset; // Current offset of varscope + int m_found_offset; // Found offset of varscope + bool m_found; // Offset found // VISITORS virtual void visit(AstNodeVarRef* nodep) { - UINFO(9,"CLK DECOMP Concat search var (off = "<varScopep() == m_vscp && !nodep->user2() && !m_found) { - // A concatenation may use the same var multiple times - // But the graph will initially have an edge per instance - nodep->user2(true); - m_found_offset = m_offset; - m_found = true; - UINFO(9,"CLK DECOMP Concat found var (off = "<dtypep()->width(); + UINFO(9,"CLK DECOMP Concat search var (off = "<varScopep() == m_vscp && !nodep->user2() && !m_found) { + // A concatenation may use the same var multiple times + // But the graph will initially have an edge per instance + nodep->user2(true); + m_found_offset = m_offset; + m_found = true; + UINFO(9,"CLK DECOMP Concat found var (off = "<dtypep()->width(); } virtual void visit(AstConcat* nodep) { - UINFO(9,"CLK DECOMP Concat search (off = "<rhsp()); iterate(nodep->lhsp()); } @@ -1303,22 +1341,23 @@ private: public: // CONSTUCTORS GateConcatVisitor() { - m_vscp = NULL; - m_offset = 0; - m_found_offset = 0; - m_found = false; + m_vscp = NULL; + m_offset = 0; + m_found_offset = 0; + m_found = false; } virtual ~GateConcatVisitor() {} // PUBLIC METHODS bool concatOffset(AstConcat* concatp, AstVarScope* vscp, int& offsetr) { - m_vscp = vscp; - m_offset = 0; - m_found = false; - // Iterate + m_vscp = vscp; + m_offset = 0; + m_found = false; + // Iterate iterate(concatp); - UINFO(9,"CLK DECOMP Concat Offset (found = "< bool: already visited - V3Graph* m_graphp; - int m_seen_clk_vectors; - AstVarScope* m_clk_vsp; - GateVarVertex* m_clk_vvertexp; - GateConcatVisitor m_concat_visitor; - int m_total_seen_clk_vectors; - int m_total_decomposed_clk_vectors; + // AstVarScope::user2p -> bool: already visited + V3Graph* m_graphp; + int m_seen_clk_vectors; + AstVarScope* m_clk_vsp; + GateVarVertex* m_clk_vvertexp; + GateConcatVisitor m_concat_visitor; + int m_total_seen_clk_vectors; + int m_total_decomposed_clk_vectors; virtual VNUser visit(GateVarVertex* vvertexp, VNUser vu) { - // Check that we haven't been here before - AstVarScope* vsp = vvertexp->varScp(); - if (vsp->user2SetOnce()) return VNUser(0); - UINFO(9,"CLK DECOMP Var - "<varp()->width() > 1) { - m_seen_clk_vectors++; - m_total_seen_clk_vectors++; - } + // Check that we haven't been here before + AstVarScope* vsp = vvertexp->varScp(); + if (vsp->user2SetOnce()) return VNUser(0); + UINFO(9,"CLK DECOMP Var - "<varp()->width() > 1) { + m_seen_clk_vectors++; + m_total_seen_clk_vectors++; + } GateClkDecompState* currState = reinterpret_cast(vu.c()); - GateClkDecompState nextState(currState->m_offset, vsp); - vvertexp->iterateCurrentOutEdges(*this, VNUser(&nextState)); - if (vsp->varp()->width() > 1) { - m_seen_clk_vectors--; - } - vsp->user2(false); - return VNUser(0); + GateClkDecompState nextState(currState->m_offset, vsp); + vvertexp->iterateCurrentOutEdges(*this, VNUser(&nextState)); + if (vsp->varp()->width() > 1) { + m_seen_clk_vectors--; + } + vsp->user2(false); + return VNUser(0); } virtual VNUser visit(GateLogicVertex* lvertexp, VNUser vu) { GateClkDecompState* currState = reinterpret_cast(vu.c()); - int clk_offset = currState->m_offset; + int clk_offset = currState->m_offset; if (const AstAssignW* assignp = VN_CAST(lvertexp->nodep(), AssignW)) { - UINFO(9,"CLK DECOMP Logic (off = "<rhsp(), Sel)) { if (VN_IS(rselp->lsbp(), Const) && VN_IS(rselp->widthp(), Const)) { - if (clk_offset < rselp->lsbConst() || clk_offset > rselp->msbConst()) { - UINFO(9,"CLK DECOMP Sel [ "<msbConst()<<" : "<lsbConst()<<" ] dropped clock ("<lsbConst(); - } else { - return VNUser(0); - } + if (clk_offset < rselp->lsbConst() || clk_offset > rselp->msbConst()) { + UINFO(9,"CLK DECOMP Sel [ "<msbConst() + <<" : "<lsbConst()<<" ] dropped clock (" + <lsbConst(); + } else { + return VNUser(0); + } } else if (AstConcat* catp = VN_CAST(assignp->rhsp(), Concat)) { - UINFO(9,"CLK DECOMP Concat searching - "<lhsp()<m_last_vsp, concat_offset)) { - return VNUser(0); - } - clk_offset += concat_offset; - } + UINFO(9,"CLK DECOMP Concat searching - "<lhsp()<m_last_vsp, concat_offset)) { + return VNUser(0); + } + clk_offset += concat_offset; + } if (const AstSel* lselp = VN_CAST(assignp->lhsp(), Sel)) { if (VN_IS(lselp->lsbp(), Const) && VN_IS(lselp->widthp(), Const)) { - clk_offset += lselp->lsbConst(); - } else { - return VNUser(0); - } + clk_offset += lselp->lsbConst(); + } else { + return VNUser(0); + } } else if (const AstVarRef* vrp = VN_CAST(assignp->lhsp(), VarRef)) { - if (vrp->dtypep()->width() == 1 && m_seen_clk_vectors) { - if (clk_offset != 0) { - UINFO(9,"Should only make it here with clk_offset = 0"<lhsp()<<" <-> "<rhsp(); - rhsp->replaceWith(new AstVarRef(rhsp->fileline(), m_clk_vsp, false)); - for (V3GraphEdge* edgep = lvertexp->inBeginp(); edgep; ) { - edgep->unlinkDelete(); VL_DANGLING(edgep); - } - new V3GraphEdge(m_graphp, m_clk_vvertexp, lvertexp, 1); - m_total_decomposed_clk_vectors++; - } - } - GateClkDecompState nextState(clk_offset, currState->m_last_vsp); - return lvertexp->iterateCurrentOutEdges(*this, VNUser(&nextState)); - } - return VNUser(0); + if (vrp->dtypep()->width() == 1 && m_seen_clk_vectors) { + if (clk_offset != 0) { + UINFO(9,"Should only make it here with clk_offset = 0"<lhsp()<<" <-> "<rhsp(); + rhsp->replaceWith(new AstVarRef(rhsp->fileline(), m_clk_vsp, false)); + for (V3GraphEdge* edgep = lvertexp->inBeginp(); edgep; ) { + edgep->unlinkDelete(); VL_DANGLING(edgep); + } + new V3GraphEdge(m_graphp, m_clk_vvertexp, lvertexp, 1); + m_total_decomposed_clk_vectors++; + } + } + GateClkDecompState nextState(clk_offset, currState->m_last_vsp); + return lvertexp->iterateCurrentOutEdges(*this, VNUser(&nextState)); + } + return VNUser(0); } public: explicit GateClkDecompGraphVisitor(V3Graph* graphp) { - m_graphp = graphp; - m_seen_clk_vectors = 0; - m_clk_vsp = NULL; - m_clk_vvertexp = NULL; - m_total_seen_clk_vectors = 0; - m_total_decomposed_clk_vectors = 0; + m_graphp = graphp; + m_seen_clk_vectors = 0; + m_clk_vsp = NULL; + m_clk_vvertexp = NULL; + m_total_seen_clk_vectors = 0; + m_total_decomposed_clk_vectors = 0; } virtual ~GateClkDecompGraphVisitor() { - V3Stats::addStat("Optimizations, Clocker seen vectors", m_total_seen_clk_vectors); - V3Stats::addStat("Optimizations, Clocker decomposed vectors", m_total_decomposed_clk_vectors); + V3Stats::addStat("Optimizations, Clocker seen vectors", m_total_seen_clk_vectors); + V3Stats::addStat("Optimizations, Clocker decomposed vectors", m_total_decomposed_clk_vectors); } void clkDecomp(GateVarVertex* vvertexp) { - UINFO(9,"CLK DECOMP Starting Var - "<varScp(); - m_clk_vvertexp = vvertexp; - GateClkDecompState nextState(0, m_clk_vsp); - vvertexp->accept(*this, VNUser(&nextState)); + UINFO(9,"CLK DECOMP Starting Var - "<varScp(); + m_clk_vvertexp = vvertexp; + GateClkDecompState nextState(0, m_clk_vsp); + vvertexp->accept(*this, VNUser(&nextState)); } }; @@ -1446,17 +1488,17 @@ void GateVisitor::decomposeClkVectors() { AstNode::user2ClearTree(); GateClkDecompGraphVisitor decomposer(&m_graph); for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (GateVarVertex* vertp = dynamic_cast(itp)) { - AstVarScope* vsp = vertp->varScp(); - if (vsp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { - if (vsp->varp()->width() > 1) { - UINFO(9,"Clocker > 1 bit, not decomposing: "<(itp)) { + AstVarScope* vsp = vertp->varScp(); + if (vsp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { + if (vsp->varp()->width() > 1) { + UINFO(9,"Clocker > 1 bit, not decomposing: "<valuep(), NodeAssign)) { - UINFO(5," Removeassign "<rhsp(); - valuep->unlinkFrBack(); - assp->replaceWith(valuep); - assp->deleteTree(); VL_DANGLING(assp); - } + UINFO(5," Removeassign "<rhsp(); + valuep->unlinkFrBack(); + assp->replaceWith(valuep); + assp->deleteTree(); VL_DANGLING(assp); + } } // Speedups virtual void visit(AstVar* nodep) {} diff --git a/src/V3Gate.h b/src/V3Gate.h index 561e2f32d..36570008e 100644 --- a/src/V3Gate.h +++ b/src/V3Gate.h @@ -34,4 +34,4 @@ public: static void gateAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3GenClk.cpp b/src/V3GenClk.cpp index 316e3c5eb..a91338f8a 100644 --- a/src/V3GenClk.cpp +++ b/src/V3GenClk.cpp @@ -18,8 +18,8 @@ // //************************************************************************* // GENCLK TRANSFORMATIONS: -// Follow control-flow graph with assignments and var usages -// ASSIGNDLY to variable later used as clock requires change detect +// Follow control-flow graph with assignments and var usages +// ASSIGNDLY to variable later used as clock requires change detect // //************************************************************************* @@ -47,72 +47,73 @@ class GenClkRenameVisitor : public GenClkBaseVisitor { private: // NODE STATE // Cleared on top scope - // AstVarScope::user2() -> AstVarScope*. Signal replacing activation with - // AstVarRef::user3() -> bool. Signal is replaced activation (already done) - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; + // AstVarScope::user2() -> AstVarScope*. Signal replacing activation with + // AstVarRef::user3() -> bool. Signal is replaced activation (already done) + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; // STATE - AstActive* m_activep; // Inside activate statement - AstNodeModule* m_topModp; // Top module - AstScope* m_scopetopp; // Scope under TOPSCOPE + AstActive* m_activep; // Inside activate statement + AstNodeModule* m_topModp; // Top module + AstScope* m_scopetopp; // Scope under TOPSCOPE // METHODS AstVarScope* genInpClk(AstVarScope* vscp) { - if (vscp->user2p()) { + if (vscp->user2p()) { return VN_CAST(vscp->user2p(), VarScope); - } else { - AstVar* varp = vscp->varp(); - string newvarname = "__VinpClk__"+vscp->scopep()->nameDotless()+"__"+varp->name(); - // Create: VARREF(inpclk) - // ... - // ASSIGN(VARREF(inpclk), VARREF(var)) - AstVar* newvarp = new AstVar(varp->fileline(), AstVarType::MODULETEMP, newvarname, varp); - m_topModp->addStmtp(newvarp); - AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopetopp, newvarp); - m_scopetopp->addVarp(newvscp); + } else { + AstVar* varp = vscp->varp(); + string newvarname = "__VinpClk__"+vscp->scopep()->nameDotless()+"__"+varp->name(); + // Create: VARREF(inpclk) + // ... + // ASSIGN(VARREF(inpclk), VARREF(var)) + AstVar* newvarp = new AstVar(varp->fileline(), + AstVarType::MODULETEMP, newvarname, varp); + m_topModp->addStmtp(newvarp); + AstVarScope* newvscp = new AstVarScope(vscp->fileline(), m_scopetopp, newvarp); + m_scopetopp->addVarp(newvscp); AstAssign* asninitp = new AstAssign(vscp->fileline(), new AstVarRef(vscp->fileline(), newvscp, true), new AstVarRef(vscp->fileline(), vscp, false)); - m_scopetopp->addFinalClkp(asninitp); - // - vscp->user2p(newvscp); - return newvscp; - } + m_scopetopp->addFinalClkp(asninitp); + // + vscp->user2p(newvscp); + return newvscp; + } } // VISITORS virtual void visit(AstTopScope* nodep) { - AstNode::user2ClearTree(); // user2p() used on entire tree + AstNode::user2ClearTree(); // user2p() used on entire tree - AstScope* scopep = nodep->scopep(); - if (!scopep) nodep->v3fatalSrc("No scope found on top level"); - m_scopetopp = scopep; + AstScope* scopep = nodep->scopep(); + if (!scopep) nodep->v3fatalSrc("No scope found on top level"); + m_scopetopp = scopep; iterateChildren(nodep); } //---- virtual void visit(AstVarRef* nodep) { - // Consumption/generation of a variable, - AstVarScope* vscp = nodep->varScopep(); - if (!vscp) nodep->v3fatalSrc("Scope not assigned"); - if (m_activep && !nodep->user3()) { - nodep->user3(true); - if (vscp->isCircular()) { - UINFO(8," VarActReplace "<fileline(), newvscp, nodep->lvalue()); - nodep->replaceWith(newrefp); - pushDeletep(nodep); VL_DANGLING(nodep); - } - } + // Consumption/generation of a variable, + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Scope not assigned"); + if (m_activep && !nodep->user3()) { + nodep->user3(true); + if (vscp->isCircular()) { + UINFO(8," VarActReplace "<fileline(), newvscp, nodep->lvalue()); + nodep->replaceWith(newrefp); + pushDeletep(nodep); VL_DANGLING(nodep); + } + } } virtual void visit(AstActive* nodep) { - m_activep = nodep; + m_activep = nodep; if (!nodep->sensesp()) nodep->v3fatalSrc("Unlinked"); iterateChildren(nodep->sensesp()); // iterateAndNext? - m_activep = NULL; + m_activep = NULL; iterateChildren(nodep); } virtual void visit(AstCFunc* nodep) { @@ -126,9 +127,9 @@ private: public: // CONSTRUCTORS GenClkRenameVisitor(AstTopScope* nodep, AstNodeModule* topModp) { - m_topModp = topModp; - m_scopetopp = NULL; - m_activep = NULL; + m_topModp = topModp; + m_scopetopp = NULL; + m_activep = NULL; iterate(nodep); } virtual ~GenClkRenameVisitor() {} @@ -141,8 +142,8 @@ class GenClkReadVisitor : public GenClkBaseVisitor { private: // NODE STATE // Cleared on top scope - // AstVarScope::user() -> bool. Set when the var has been used as clock - AstUser1InUse m_inuser1; + // AstVarScope::user() -> bool. Set when the var has been used as clock + AstUser1InUse m_inuser1; // STATE AstActive* m_activep; // Inside activate statement @@ -152,20 +153,20 @@ private: // VISITORS virtual void visit(AstTopScope* nodep) { - AstNode::user1ClearTree(); // user1p() used on entire tree + AstNode::user1ClearTree(); // user1p() used on entire tree iterateChildren(nodep); - { - // Make the new clock signals and replace any activate references - // See rename, it does some AstNode::userClearTree()'s - GenClkRenameVisitor visitor (nodep, m_topModp); - } + { + // Make the new clock signals and replace any activate references + // See rename, it does some AstNode::userClearTree()'s + GenClkRenameVisitor visitor (nodep, m_topModp); + } } virtual void visit(AstNodeModule* nodep) { - // Only track the top scopes, not lower level functions - if (nodep->isTop()) { - m_topModp = nodep; + // Only track the top scopes, not lower level functions + if (nodep->isTop()) { + m_topModp = nodep; iterateChildren(nodep); - } + } } virtual void visit(AstCCall* nodep) { iterateChildren(nodep); @@ -188,37 +189,37 @@ private: //---- virtual void visit(AstVarRef* nodep) { - // Consumption/generation of a variable, - AstVarScope* vscp = nodep->varScopep(); - if (!vscp) nodep->v3fatalSrc("Scope not assigned"); - if (m_activep) { - UINFO(8," VarAct "<user1(true); - } - if (m_assignp && nodep->lvalue() && vscp->user1()) { - // Variable was previously used as a clock, and is now being set - // Thus a unordered generated clock... - UINFO(8," VarSetAct "<circular(true); - } + // Consumption/generation of a variable, + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Scope not assigned"); + if (m_activep) { + UINFO(8," VarAct "<user1(true); + } + if (m_assignp && nodep->lvalue() && vscp->user1()) { + // Variable was previously used as a clock, and is now being set + // Thus a unordered generated clock... + UINFO(8," VarSetAct "<circular(true); + } } virtual void visit(AstNodeAssign* nodep) { - //UINFO(8,"ASS "<sensesp()) nodep->v3fatalSrc("Unlinked"); iterateChildren(nodep->sensesp()); // iterateAndNext? - m_activep = NULL; + m_activep = NULL; iterateChildren(nodep); } //----- - virtual void visit(AstVar*) {} // Don't want varrefs under it + virtual void visit(AstVar*) {} // Don't want varrefs under it virtual void visit(AstNode* nodep) { iterateChildren(nodep); } diff --git a/src/V3GenClk.h b/src/V3GenClk.h index 465d7ccca..2e6a16c07 100644 --- a/src/V3GenClk.h +++ b/src/V3GenClk.h @@ -34,4 +34,4 @@ public: static void genClkAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Global.h b/src/V3Global.h index b337b533e..1813c9c25 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -45,9 +45,9 @@ class AstNetlist; class VWidthMinUsage { public: enum en { - LINT_WIDTH, - MATCHES_WIDTH, - VERILOG_WIDTH + LINT_WIDTH, + MATCHES_WIDTH, + VERILOG_WIDTH }; enum en m_e; inline VWidthMinUsage() : m_e(LINT_WIDTH) {} @@ -56,40 +56,40 @@ public: explicit inline VWidthMinUsage(int _e) : m_e(static_cast(_e)) {} operator en() const { return m_e; } }; - inline bool operator== (VWidthMinUsage lhs, VWidthMinUsage rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (VWidthMinUsage lhs, VWidthMinUsage::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (VWidthMinUsage::en lhs, VWidthMinUsage rhs) { return (lhs == rhs.m_e); } + inline bool operator==(VWidthMinUsage lhs, VWidthMinUsage rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(VWidthMinUsage lhs, VWidthMinUsage::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(VWidthMinUsage::en lhs, VWidthMinUsage rhs) { return (lhs == rhs.m_e); } //###################################################################### // V3Global - The top level class for the entire program class V3Global { // Globals - AstNetlist* m_rootp; // Root of entire netlist - VWidthMinUsage m_widthMinUsage; // What AstNode::widthMin() is used for + AstNetlist* m_rootp; // Root of entire netlist + VWidthMinUsage m_widthMinUsage; // What AstNode::widthMin() is used for - int m_debugFileNumber; // Number to append to debug files created - bool m_assertDTypesResolved; // Tree should have dtypep()'s - bool m_constRemoveXs; // Const needs to strip any Xs - bool m_needHInlines; // Need __Inlines file - bool m_needHeavy; // Need verilated_heavy.h include - bool m_dpi; // Need __Dpi include files + int m_debugFileNumber; // Number to append to debug files created + bool m_assertDTypesResolved; // Tree should have dtypep()'s + bool m_constRemoveXs; // Const needs to strip any Xs + bool m_needHInlines; // Need __Inlines file + bool m_needHeavy; // Need verilated_heavy.h include + bool m_dpi; // Need __Dpi include files public: // Options - V3Options opt; // All options; let user see them directly + V3Options opt; // All options; let user see them directly public: // CONSTRUCTORS V3Global() { - m_debugFileNumber = 0; - m_widthMinUsage = VWidthMinUsage::LINT_WIDTH; - m_assertDTypesResolved = false; - m_constRemoveXs = false; - m_needHInlines = false; - m_needHeavy = false; - m_dpi = false; - m_rootp = NULL; // created by makeInitNetlist() so static constructors run first + m_debugFileNumber = 0; + m_widthMinUsage = VWidthMinUsage::LINT_WIDTH; + m_assertDTypesResolved = false; + m_constRemoveXs = false; + m_needHInlines = false; + m_needHeavy = false; + m_dpi = false; + m_rootp = NULL; // created by makeInitNetlist() so static constructors run first } AstNetlist* makeNetlist(); void boot() { UASSERT(!m_rootp,"call once"); m_rootp = makeNetlist(); } @@ -108,10 +108,10 @@ public: bool constRemoveXs() const { return m_constRemoveXs; } void constRemoveXs(bool flag) { m_constRemoveXs = flag; } string debugFilename(const string& nameComment, int newNumber=0) { - ++m_debugFileNumber; - if (newNumber) m_debugFileNumber = newNumber; - char digits[100]; sprintf(digits, "%03d", m_debugFileNumber); - return opt.makeDir()+"/"+opt.prefix()+"_"+digits+"_"+nameComment; + ++m_debugFileNumber; + if (newNumber) m_debugFileNumber = newNumber; + char digits[100]; sprintf(digits, "%03d", m_debugFileNumber); + return opt.makeDir()+"/"+opt.prefix()+"_"+digits+"_"+nameComment; } bool needHInlines() const { return m_needHInlines; } void needHInlines(bool flag) { m_needHInlines=flag; } diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index 0322285c0..429d5f40c 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -55,14 +55,14 @@ void V3GraphVertex::verticesPushBack(V3Graph* graphp) { void V3GraphVertex::unlinkEdges(V3Graph* graphp) { for (V3GraphEdge* edgep = outBeginp(); edgep; /*BELOW*/) { - V3GraphEdge* nextp = edgep->outNextp(); - edgep->unlinkDelete(); - edgep = nextp; + V3GraphEdge* nextp = edgep->outNextp(); + edgep->unlinkDelete(); + edgep = nextp; } for (V3GraphEdge* edgep = inBeginp(); edgep; /*BELOW*/) { - V3GraphEdge* nextp = edgep->inNextp(); - edgep->unlinkDelete(); - edgep = nextp; + V3GraphEdge* nextp = edgep->inNextp(); + edgep->unlinkDelete(); + edgep = nextp; } } @@ -72,17 +72,17 @@ void V3GraphVertex::unlinkDelete(V3Graph* graphp) { // Unlink from vertex list m_vertices.unlink(graphp->m_vertices, this); // Delete - delete this; //this=NULL; + delete this; //this=NULL; } void V3GraphVertex::rerouteEdges(V3Graph* graphp) { // Make new edges for each from/to pair for (V3GraphEdge* iedgep = inBeginp(); iedgep; iedgep=iedgep->inNextp()) { - for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep=oedgep->outNextp()) { + for (V3GraphEdge* oedgep = outBeginp(); oedgep; oedgep=oedgep->outNextp()) { new V3GraphEdge(graphp, iedgep->fromp(), oedgep->top(), - std::min(iedgep->weight(),oedgep->weight()), + std::min(iedgep->weight(), oedgep->weight()), iedgep->cutable() && oedgep->cutable()); - } + } } // Remove old edges unlinkEdges(graphp); @@ -101,17 +101,17 @@ uint32_t V3GraphVertex::inHash() const { // So we need an associative operator, like XOR. // However with XOR multiple edges to the same source will cancel out, // so we use ADD. (Generally call this only after removing duplicates though) - uint32_t hash=0; + uint32_t hash = 0; for (V3GraphEdge* edgep = this->inBeginp(); edgep; edgep=edgep->inNextp()) { - hash += cvtToHash(edgep->fromp()); + hash += cvtToHash(edgep->fromp()); } return hash; } uint32_t V3GraphVertex::outHash() const { - uint32_t hash=0; + uint32_t hash = 0; for (V3GraphEdge* edgep = this->outBeginp(); edgep; edgep=edgep->outNextp()) { - hash += cvtToHash(edgep->top()); + hash += cvtToHash(edgep->top()); } return hash; } @@ -163,8 +163,8 @@ std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) { // Edges void V3GraphEdge::init(V3Graph* graphp, - V3GraphVertex* fromp, V3GraphVertex* top, int weight, - bool cutable) { + V3GraphVertex* fromp, V3GraphVertex* top, int weight, + bool cutable) { UASSERT(fromp, "Null from pointer"); UASSERT(top, "Null to pointer"); m_fromp = fromp; @@ -191,7 +191,7 @@ void V3GraphEdge::unlinkDelete() { // Unlink to side m_ins.unlink(m_top->m_ins, this); // Delete - delete this; //this=NULL; + delete this; //this=NULL; } void V3GraphEdge::outPushBack() { @@ -222,18 +222,18 @@ void V3Graph::clear() { // Empty it of all points, as if making a new object // Delete the old edges for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; /*BELOW*/) { - V3GraphEdge* nextp = edgep->outNextp(); - delete edgep; - edgep = nextp; - } - vertexp->outUnlink(); + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; /*BELOW*/) { + V3GraphEdge* nextp = edgep->outNextp(); + delete edgep; + edgep = nextp; + } + vertexp->outUnlink(); } // Delete the old vertices for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; /*BELOW*/) { - V3GraphVertex* nextp = vertexp->verticesNextp(); - delete vertexp; - vertexp = nextp; + V3GraphVertex* nextp = vertexp->verticesNextp(); + delete vertexp; + vertexp = nextp; } verticesUnlink(); } @@ -246,25 +246,25 @@ void V3Graph::userClearVertices() { // the extra code on each read of user() would probably slow things // down more than help. for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertexp->user(0); - vertexp->userp(NULL); // Its a union, but might be different size than user() + vertexp->user(0); + vertexp->userp(NULL); // Its a union, but might be different size than user() } } void V3Graph::userClearEdges() { // Clear user() in all of tree for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - edgep->user(0); - edgep->userp(NULL); // Its a union, but might be different size than user() - } + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + edgep->user(0); + edgep->userp(NULL); // Its a union, but might be different size than user() + } } } void V3Graph::clearColors() { // Reset colors for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertexp->m_color = 0; + vertexp->m_color = 0; } } @@ -285,34 +285,34 @@ void V3Graph::dump(std::ostream& os) { os<<" Graph:\n"; // Print vertices for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - os<<"\tNode: "<name(); - if (vertexp->color()) os<<" color="<color(); - os<inBeginp(); edgep; edgep=edgep->inNextp()) { + os<<"\tNode: "<name(); + if (vertexp->color()) os<<" color="<color(); + os<inBeginp(); edgep; edgep=edgep->inNextp()) { dumpEdge(os, vertexp, edgep); - } - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + } + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { dumpEdge(os, vertexp, edgep); - } + } } } void V3Graph::dumpEdge(std::ostream& os, V3GraphVertex* vertexp, V3GraphEdge* edgep) { if (edgep->weight() - && (edgep->fromp() == vertexp - || edgep->top() == vertexp)) { - os<<"\t\t"; - if (edgep->fromp() == vertexp) os << "-> "<top()->name(); - if (edgep->top() == vertexp) os << "<- "<fromp()->name(); - if (edgep->cutable()) os<<" [CUTABLE]"; - os<fromp() == vertexp + || edgep->top() == vertexp)) { + os<<"\t\t"; + if (edgep->fromp() == vertexp) os << "-> "<top()->name(); + if (edgep->top() == vertexp) os << "<- "<fromp()->name(); + if (edgep->cutable()) os<<" [CUTABLE]"; + os< SubgraphMmap; SubgraphMmap subgraphs; for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - string vertexSubgraph = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : ""; - subgraphs.insert(make_pair(vertexSubgraph, vertexp)); + string vertexSubgraph + = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : ""; + subgraphs.insert(make_pair(vertexSubgraph, vertexp)); } // We use a map here, as we don't want to corrupt anything (userp) in the graph, @@ -347,50 +348,51 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { std::map numMap; // Print vertices - int n=0; + int n = 0; string subgr; for (SubgraphMmap::iterator it = subgraphs.begin(); it!=subgraphs.end(); ++it) { - string vertexSubgraph = it->first; - V3GraphVertex* vertexp = it->second; - numMap[vertexp] = n; - if (subgr != vertexSubgraph) { - if (subgr!="") *logp<<"\t};\n"; - subgr = vertexSubgraph; - if (subgr!="") *logp<<"\tsubgraph cluster_"<dotName()<<(n++) - <<"\t[fontsize=8 " - <<"label=\""<<(vertexp->name()!="" ? vertexp->name() : "\\N"); - if (vertexp->rank()) *logp<<" r"<rank(); - if (vertexp->fanout()!=0.0) *logp<<" f"<fanout(); - if (vertexp->color()) *logp<<"\\n c"<color(); - *logp<<"\""; - *logp<<", color="<dotColor(); - if (vertexp->dotStyle()!="") *logp<<", style="<dotStyle(); - if (vertexp->dotShape()!="") *logp<<", shape="<dotShape(); - *logp<<"];\n"; + string vertexSubgraph = it->first; + V3GraphVertex* vertexp = it->second; + numMap[vertexp] = n; + if (subgr != vertexSubgraph) { + if (subgr!="") *logp<<"\t};\n"; + subgr = vertexSubgraph; + if (subgr!="") *logp<<"\tsubgraph cluster_"<dotName()<<(n++) + <<"\t[fontsize=8 " + <<"label=\""<<(vertexp->name()!="" ? vertexp->name() : "\\N"); + if (vertexp->rank()) *logp<<" r"<rank(); + if (vertexp->fanout()!=0.0) *logp<<" f"<fanout(); + if (vertexp->color()) *logp<<"\\n c"<color(); + *logp<<"\""; + *logp<<", color="<dotColor(); + if (vertexp->dotStyle()!="") *logp<<", style="<dotStyle(); + if (vertexp->dotShape()!="") *logp<<", shape="<dotShape(); + *logp<<"];\n"; } if (subgr!="") *logp<<"\t};\n"; // Print edges for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (edgep->weight()) { - int fromVnum = numMap[edgep->fromp()]; - int toVnum = numMap[edgep->top()]; - *logp<<"\tn"<fromp()->dotName()< n"<top()->dotName()<name()!="" ? edgep->name() : "\\E")<<"\"" - <<"fontsize=8 label=\""<<(edgep->dotLabel()!="" ? edgep->dotLabel() : "")<<"\"" - <<" weight="<weight() - <<" color="<dotColor(); - if (edgep->dotStyle()!="") *logp<<" style="<dotStyle(); - //if (edgep->cutable()) { *logp<<",constraint=false"; } // to rank without following edges - *logp<<"];\n"; - } - } + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (edgep->weight()) { + int fromVnum = numMap[edgep->fromp()]; + int toVnum = numMap[edgep->top()]; + *logp<<"\tn"<fromp()->dotName()< n"<top()->dotName()<name()!="" ? edgep->name() : "\\E")<<"\"" + <<"fontsize=8 label=\"" + <<(edgep->dotLabel()!="" ? edgep->dotLabel() : "")<<"\"" + <<" weight="<weight() + <<" color="<dotColor(); + if (edgep->dotStyle()!="") *logp<<" style="<dotStyle(); + //if (edgep->cutable()) { *logp<<",constraint=false"; } // to rank without following edges + *logp<<"];\n"; + } + } } // Vertex::m_user end, now unused diff --git a/src/V3Graph.h b/src/V3Graph.h index 4d6d38714..6417ac25f 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -79,8 +79,8 @@ inline bool operator==(GraphWay::en lhs, GraphWay rhs) { return (lhs == rhs.m_e) class V3Graph { private: - // STATE - V3List m_vertices; // All vertices + // MEMBERS + V3List m_vertices; // All vertices static int s_debug; protected: friend class V3GraphVertex; friend class V3GraphEdge; @@ -99,10 +99,10 @@ public: V3Graph(); virtual ~V3Graph(); static void debug(int level) { s_debug = level; } - virtual string dotRankDir() const { return "TB"; } // rankdir for dot plotting + virtual string dotRankDir() const { return "TB"; } // rankdir for dot plotting // METHODS - void clear(); // Empty it of all vertices/edges, as if making a new object + void clear(); // Empty it of all vertices/edges, as if making a new object void clearColors(); V3GraphVertex* verticesBeginp() const { return m_vertices.begin(); } @@ -191,22 +191,22 @@ class V3GraphVertex { protected: friend class V3Graph; friend class V3GraphEdge; friend class GraphAcyc; friend class GraphAlgRank; - V3ListEnt m_vertices;// All vertices, linked list - V3List m_outs; // Outbound edges,linked list - V3List m_ins; // Inbound edges, linked list - double m_fanout; // Order fanout - uint32_t m_color; // Color of the node - uint32_t m_rank; // Rank of edge + V3ListEnt m_vertices; // All vertices, linked list + V3List m_outs; // Outbound edges,linked list + V3List m_ins; // Inbound edges, linked list + double m_fanout; // Order fanout + uint32_t m_color; // Color of the node + uint32_t m_rank; // Rank of edge union { - void* m_userp; // Marker for some algorithms - uint32_t m_user; // Marker for some algorithms + void* m_userp; // Marker for some algorithms + uint32_t m_user; // Marker for some algorithms }; // METHODS void verticesPushBack(V3Graph* graphp); // ACCESSORS void fanout(double fanout) { m_fanout = fanout; } - void inUnlink() { m_ins.reset(); } // Low level; normally unlinkDelete is what you want - void outUnlink() { m_outs.reset(); } // Low level; normally unlinkDelete is what you want + void inUnlink() { m_ins.reset(); } // Low level; normally unlinkDelete is what you want + void outUnlink() { m_outs.reset(); } // Low level; normally unlinkDelete is what you want protected: // CONSTRUCTORS V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old); @@ -214,10 +214,10 @@ public: explicit V3GraphVertex(V3Graph* graphp); //! Clone copy constructor. Doesn't copy edges or user/userp. virtual V3GraphVertex* clone(V3Graph* graphp) const { - return new V3GraphVertex(graphp, *this); } + return new V3GraphVertex(graphp, *this); } virtual ~V3GraphVertex() {} - void unlinkEdges(V3Graph* graphp); - void unlinkDelete(V3Graph* graphp); + void unlinkEdges(V3Graph* graphp); + void unlinkDelete(V3Graph* graphp); // ACCESSORS virtual string name() const { return ""; } @@ -228,32 +228,32 @@ public: virtual uint32_t rankAdder() const { return 1; } virtual FileLine* fileline() const { return NULL; } // NULL for unknown virtual int sortCmp(const V3GraphVertex* rhsp) const { - // LHS goes first if of lower rank, or lower fanout - if (m_rank < rhsp->m_rank) return -1; - if (m_rank > rhsp->m_rank) return 1; - if (m_fanout < rhsp->m_fanout) return -1; - if (m_fanout > rhsp->m_fanout) return 1; - return 0; + // LHS goes first if of lower rank, or lower fanout + if (m_rank < rhsp->m_rank) return -1; + if (m_rank > rhsp->m_rank) return 1; + if (m_fanout < rhsp->m_fanout) return -1; + if (m_fanout > rhsp->m_fanout) return 1; + return 0; } - uint32_t color() const { return m_color; } - void color(uint32_t color) { m_color = color; } - uint32_t rank() const { return m_rank; } + uint32_t color() const { return m_color; } + void color(uint32_t color) { m_color = color; } + uint32_t rank() const { return m_rank; } void rank(uint32_t rank) { m_rank = rank; } - double fanout() const { return m_fanout; } - void user(uint32_t user) { m_user = user; } - uint32_t user() const { return m_user; } - void userp(void* userp) { m_userp = userp; } - void* userp() const { return m_userp; } + double fanout() const { return m_fanout; } + void user(uint32_t user) { m_user = user; } + uint32_t user() const { return m_user; } + void userp(void* userp) { m_userp = userp; } + void* userp() const { return m_userp; } // ITERATORS V3GraphVertex* verticesNextp() const { return m_vertices.nextp(); } V3GraphEdge* inBeginp() const { return m_ins.begin(); } - bool inEmpty() const { return inBeginp()==NULL; } - bool inSize1() const; - uint32_t inHash() const; + bool inEmpty() const { return inBeginp()==NULL; } + bool inSize1() const; + uint32_t inHash() const; V3GraphEdge* outBeginp() const { return m_outs.begin(); } - bool outEmpty() const { return outBeginp()==NULL; } - bool outSize1() const; - uint32_t outHash() const; + bool outEmpty() const { return outBeginp()==NULL; } + bool outSize1() const; + uint32_t outHash() const; V3GraphEdge* beginp(GraphWay way) const { return way.forward() ? outBeginp() : inBeginp(); } // METHODS @@ -275,39 +275,42 @@ class V3GraphEdge { // Wires/variables aren't edges. Edges have only a single to/from vertex public: // ENUMS - enum Cuttable { NOT_CUTABLE = false, CUTABLE = true }; // For passing to V3GraphEdge + enum Cuttable { NOT_CUTABLE = false, CUTABLE = true }; // For passing to V3GraphEdge protected: - friend class V3Graph; friend class V3GraphVertex; - friend class GraphAcyc; friend class GraphAcycEdge; - V3ListEnt m_outs; // Next Outbound edge for same vertex (linked list) - V3ListEnt m_ins; // Next Inbound edge for same vertex (linked list) + friend class V3Graph; friend class V3GraphVertex; + friend class GraphAcyc; friend class GraphAcycEdge; + V3ListEnt m_outs; // Next Outbound edge for same vertex (linked list) + V3ListEnt m_ins; // Next Inbound edge for same vertex (linked list) // - V3GraphVertex* m_fromp; // Vertices pointing to this edge - V3GraphVertex* m_top; // Vertices this edge points to - int m_weight; // Weight of the connection - bool m_cutable; // Interconnect may be broken in order sorting + V3GraphVertex* m_fromp; // Vertices pointing to this edge + V3GraphVertex* m_top; // Vertices this edge points to + int m_weight; // Weight of the connection + bool m_cutable; // Interconnect may be broken in order sorting union { - void* m_userp; // Marker for some algorithms - uint32_t m_user; // Marker for some algorithms + void* m_userp; // Marker for some algorithms + uint32_t m_user; // Marker for some algorithms }; // METHODS - void init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=false); - void cut() { m_weight = 0; } // 0 weight is same as disconnected + void init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, + int weight, bool cutable=false); + void cut() { m_weight = 0; } // 0 weight is same as disconnected void outPushBack(); void inPushBack(); // CONSTRUCTORS protected: - V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, const V3GraphEdge& old) { - init(graphp, fromp, top, old.m_weight, old.m_cutable); + V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, + const V3GraphEdge& old) { + init(graphp, fromp, top, old.m_weight, old.m_cutable); } public: //! Add DAG from one node to the specified node - V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=false) { - init(graphp, fromp, top, weight, cutable); + V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, + int weight, bool cutable=false) { + init(graphp, fromp, top, weight, cutable); } //! Clone copy constructor. Doesn't copy existing vertices or user/userp. virtual V3GraphEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { - return new V3GraphEdge(graphp, fromp, top, *this); } + return new V3GraphEdge(graphp, fromp, top, *this); } virtual ~V3GraphEdge() {} // METHODS virtual string name() const { return m_fromp->name()+"->"+m_top->name(); } @@ -315,20 +318,20 @@ public: virtual string dotColor() const { return cutable()?"yellowGreen":"red"; } virtual string dotStyle() const { return cutable()?"dashed":""; } virtual int sortCmp(const V3GraphEdge* rhsp) const { - if (!m_weight || !rhsp->m_weight) return 0; - return top()->sortCmp(rhsp->top()); + if (!m_weight || !rhsp->m_weight) return 0; + return top()->sortCmp(rhsp->top()); } - void unlinkDelete(); + void unlinkDelete(); V3GraphEdge* relinkFromp(V3GraphVertex* newFromp); // ACCESSORS - int weight() const { return m_weight; } - void weight(int weight) { m_weight=weight; } - bool cutable() const { return m_cutable; } - void cutable(bool cutable) { m_cutable=cutable; } - void userp(void* user) { m_userp = user; } - void* userp() const { return m_userp; } - void user(uint32_t user) { m_user = user; } - uint32_t user() const { return m_user; } + int weight() const { return m_weight; } + void weight(int weight) { m_weight = weight; } + bool cutable() const { return m_cutable; } + void cutable(bool cutable) { m_cutable = cutable; } + void userp(void* user) { m_userp = user; } + void* userp() const { return m_userp; } + void user(uint32_t user) { m_user = user; } + uint32_t user() const { return m_user; } V3GraphVertex* fromp() const { return m_fromp; } V3GraphVertex* top() const { return m_top; } V3GraphVertex* closerp(GraphWay way) const { return way.forward() ? fromp() : top(); } @@ -345,4 +348,4 @@ public: //============================================================================ -#endif // Guard +#endif // Guard diff --git a/src/V3GraphAcyc.cpp b/src/V3GraphAcyc.cpp index 2c9c1c3bd..e98697ccf 100644 --- a/src/V3GraphAcyc.cpp +++ b/src/V3GraphAcyc.cpp @@ -32,22 +32,22 @@ //###################################################################### //###################################################################### // Algorithms - acyclic -// Break the minimal number of backward edges to make the graph acyclic +// Break the minimal number of backward edges to make the graph acyclic class GraphAcycVertex : public V3GraphVertex { // user() is used for various sub-algorithm pieces - V3GraphVertex* m_origVertexp; // Pointer to first vertex this represents + V3GraphVertex* m_origVertexp; // Pointer to first vertex this represents protected: friend class GraphAcyc; - V3ListEnt m_work; // List of vertices with optimization work left - uint32_t m_storedRank; // Rank held until commit to edge placement - bool m_onWorkList; // True if already on list of work to do - bool m_deleted; // True if deleted + V3ListEnt m_work; // List of vertices with optimization work left + uint32_t m_storedRank; // Rank held until commit to edge placement + bool m_onWorkList; // True if already on list of work to do + bool m_deleted; // True if deleted public: GraphAcycVertex(V3Graph* graphp, V3GraphVertex* origVertexp) - : V3GraphVertex(graphp), m_origVertexp(origVertexp) - , m_storedRank(0), m_onWorkList(false), m_deleted(false) { + : V3GraphVertex(graphp), m_origVertexp(origVertexp) + , m_storedRank(0), m_onWorkList(false), m_deleted(false) { } virtual ~GraphAcycVertex() {} V3GraphVertex* origVertexp() const { return m_origVertexp; } @@ -64,14 +64,15 @@ class GraphAcycEdge : public V3GraphEdge { // userp() is always used to point to the head original graph edge private: typedef std::list OrigEdgeList; // List of orig edges, see also GraphAcyc's decl - V3GraphEdge* origEdgep() const { + V3GraphEdge* origEdgep() const { OrigEdgeList* oEListp = static_cast(userp()); - if (!oEListp) v3fatalSrc("No original edge associated with acyc edge "<front()); + if (!oEListp) v3fatalSrc("No original edge associated with acyc edge "<front()); } public: - GraphAcycEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable=false) - : V3GraphEdge(graphp, fromp, top, weight, cutable) { + GraphAcycEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, + int weight, bool cutable=false) + : V3GraphEdge(graphp, fromp, top, weight, cutable) { } virtual ~GraphAcycEdge() {} // yellow=we might still cut it, else oldEdge: yellowGreen=made uncutable, red=uncutable @@ -82,9 +83,9 @@ public: struct GraphAcycEdgeCmp { inline bool operator() (const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) const { - if (lhsp->weight() > rhsp->weight()) return 1; // LHS goes first - if (lhsp->weight() < rhsp->weight()) return 0; // RHS goes first - return 0; + if (lhsp->weight() > rhsp->weight()) return 1; // LHS goes first + if (lhsp->weight() < rhsp->weight()) return 0; // RHS goes first + return 0; } }; @@ -96,17 +97,17 @@ private: typedef std::list OrigEdgeList; // List of orig edges, see also GraphAcycEdge's decl // GRAPH USERS // origGraph - // GraphVertex::user() GraphAycVerted* New graph node + // GraphVertex::user() GraphAycVerted* New graph node // m_breakGraph - // GraphEdge::user() OrigEdgeList* Old graph edges - // GraphVertex::user bool Detection of loops in simplifyDupIterate + // GraphEdge::user() OrigEdgeList* Old graph edges + // GraphVertex::user bool Detection of loops in simplifyDupIterate // MEMBERS - V3Graph* m_origGraphp; // Original graph - V3Graph m_breakGraph; // Graph with only breakable edges represented - V3List m_work; // List of vertices with optimization work left + V3Graph* m_origGraphp; // Original graph + V3Graph m_breakGraph; // Graph with only breakable edges represented + V3List m_work; // List of vertices with optimization work left std::vector m_origEdgeDelp; // List of deletions to do when done - V3EdgeFuncP m_origEdgeFuncp; // Function that says we follow this edge (in original graph) - uint32_t m_placeStep; // Number that user() must be equal to to indicate processing + V3EdgeFuncP m_origEdgeFuncp; // Function that says we follow this edge (in original graph) + uint32_t m_placeStep; // Number that user() must be equal to to indicate processing static int debug() { return V3Graph::debug(); } @@ -126,73 +127,75 @@ private: bool placeIterate(GraphAcycVertex* vertexp, uint32_t currentRank); inline bool origFollowEdge(V3GraphEdge* edgep) { - return (edgep->weight() && (m_origEdgeFuncp)(edgep)); + return (edgep->weight() && (m_origEdgeFuncp)(edgep)); } V3GraphEdge* edgeFromEdge(V3GraphEdge* oldedgep, V3GraphVertex* fromp, V3GraphVertex* top) { - // Make new breakGraph edge, with old edge as a template + // Make new breakGraph edge, with old edge as a template GraphAcycEdge* newEdgep = new GraphAcycEdge(&m_breakGraph, fromp, top, oldedgep->weight(), oldedgep->cutable()); - newEdgep->userp(oldedgep->userp()); // Keep pointer to OrigEdgeList - return newEdgep; + newEdgep->userp(oldedgep->userp()); // Keep pointer to OrigEdgeList + return newEdgep; } void addOrigEdgep(V3GraphEdge* toEdgep, V3GraphEdge* addEdgep) { - // Add addEdge (or it's list) to list of edges that break edge represents - // Note addEdge may already have a bunch of similar linked edge representations. Yuk. - UASSERT(addEdgep, "Adding NULL"); - if (!toEdgep->userp()) { - OrigEdgeList* oep = new OrigEdgeList; - m_origEdgeDelp.push_back(oep); - toEdgep->userp(oep); - } + // Add addEdge (or it's list) to list of edges that break edge represents + // Note addEdge may already have a bunch of similar linked edge representations. Yuk. + UASSERT(addEdgep, "Adding NULL"); + if (!toEdgep->userp()) { + OrigEdgeList* oep = new OrigEdgeList; + m_origEdgeDelp.push_back(oep); + toEdgep->userp(oep); + } OrigEdgeList* oEListp = static_cast(toEdgep->userp()); if (OrigEdgeList* addListp = static_cast(addEdgep->userp())) { - for (OrigEdgeList::iterator it = addListp->begin(); it != addListp->end(); ++it) { - oEListp->push_back(*it); - } - addListp->clear(); // Done with it - } else { - oEListp->push_back(addEdgep); - } + for (OrigEdgeList::iterator it = addListp->begin(); it != addListp->end(); ++it) { + oEListp->push_back(*it); + } + addListp->clear(); // Done with it + } else { + oEListp->push_back(addEdgep); + } } void cutOrigEdge(V3GraphEdge* breakEdgep, const char* why) { - // From the break edge, cut edges in original graph it represents - UINFO(8,why<<" CUT "<fromp()<cut(); + // From the break edge, cut edges in original graph it represents + UINFO(8,why<<" CUT "<fromp()<cut(); OrigEdgeList* oEListp = static_cast(breakEdgep->userp()); - if (!oEListp) v3fatalSrc("No original edge associated with cutting edge "<begin(); it != oEListp->end(); ++it) { - V3GraphEdge* origEdgep = *it; - origEdgep->cut(); - UINFO(8," "<fromp()<<" ->"<top()<begin(); it != oEListp->end(); ++it) { + V3GraphEdge* origEdgep = *it; + origEdgep->cut(); + UINFO(8," "<fromp()<<" ->"<top()<(vertexp); - // Add vertex to list of nodes needing further optimization trials - if (!avertexp->m_onWorkList) { - avertexp->m_onWorkList = true; - avertexp->m_work.pushBack(m_work, avertexp); - } + // Add vertex to list of nodes needing further optimization trials + if (!avertexp->m_onWorkList) { + avertexp->m_onWorkList = true; + avertexp->m_work.pushBack(m_work, avertexp); + } } GraphAcycVertex* workBeginp() { return m_work.begin(); } void workPop() { - GraphAcycVertex* avertexp = workBeginp(); - avertexp->m_onWorkList = false; - avertexp->m_work.unlink(m_work, avertexp); } + GraphAcycVertex* avertexp = workBeginp(); + avertexp->m_onWorkList = false; + avertexp->m_work.unlink(m_work, avertexp); } public: // CONSTRUCTORS GraphAcyc(V3Graph* origGraphp, V3EdgeFuncP edgeFuncp) { - m_origGraphp = origGraphp; - m_origEdgeFuncp = edgeFuncp; - m_placeStep = 0; + m_origGraphp = origGraphp; + m_origEdgeFuncp = edgeFuncp; + m_placeStep = 0; } ~GraphAcyc() { - for (std::vector::iterator it = m_origEdgeDelp.begin(); it != m_origEdgeDelp.end(); ++it) { - delete (*it); - } - m_origEdgeDelp.clear(); + for (std::vector::iterator it = m_origEdgeDelp.begin(); + it != m_origEdgeDelp.end(); ++it) { + delete (*it); + } + m_origEdgeDelp.clear(); } void main(); }; @@ -206,59 +209,62 @@ void GraphAcyc::buildGraph(V3Graph* origGraphp) { // For each old node, make a new graph node for optimization origGraphp->userClearVertices(); origGraphp->userClearEdges(); - for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); overtexp; overtexp=overtexp->verticesNextp()) { - if (overtexp->color()) { - GraphAcycVertex* avertexp = new GraphAcycVertex(&m_breakGraph, overtexp); - overtexp->userp(avertexp); // Stash so can look up later - } + for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); + overtexp; overtexp = overtexp->verticesNextp()) { + if (overtexp->color()) { + GraphAcycVertex* avertexp = new GraphAcycVertex(&m_breakGraph, overtexp); + overtexp->userp(avertexp); // Stash so can look up later + } } // Build edges between logic vertices - for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); overtexp; overtexp=overtexp->verticesNextp()) { - if (overtexp->color()) { + for (V3GraphVertex* overtexp = origGraphp->verticesBeginp(); + overtexp; overtexp = overtexp->verticesNextp()) { + if (overtexp->color()) { GraphAcycVertex* avertexp = static_cast(overtexp->userp()); - buildGraphIterate(overtexp, avertexp); - } + buildGraphIterate(overtexp, avertexp); + } } } void GraphAcyc::buildGraphIterate(V3GraphVertex* overtexp, GraphAcycVertex* avertexp) { // Make new edges for (V3GraphEdge* edgep = overtexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (origFollowEdge(edgep)) { // not cut - V3GraphVertex* toVertexp = edgep->top(); - if (toVertexp->color()) { + if (origFollowEdge(edgep)) { // not cut + V3GraphVertex* toVertexp = edgep->top(); + if (toVertexp->color()) { GraphAcycVertex* toAVertexp = static_cast(toVertexp->userp()); - // Replicate the old edge into the new graph - // There may be multiple edges between same pairs of vertices - V3GraphEdge* breakEdgep = new GraphAcycEdge - (&m_breakGraph, avertexp, toAVertexp, edgep->weight(), edgep->cutable()); + // Replicate the old edge into the new graph + // There may be multiple edges between same pairs of vertices + V3GraphEdge* breakEdgep = new GraphAcycEdge + (&m_breakGraph, avertexp, toAVertexp, edgep->weight(), edgep->cutable()); addOrigEdgep(breakEdgep, edgep); // So can find original edge - } - } + } + } } } void GraphAcyc::simplify(bool allowCut) { // Add all nodes to list of work to do - for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - workPush(vertexp); + for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); + vertexp; vertexp = vertexp->verticesNextp()) { + workPush(vertexp); } // Optimize till everything finished while (GraphAcycVertex* vertexp = workBeginp()) { - workPop(); - simplifyNone(vertexp); - simplifyOne(vertexp); - simplifyOut(vertexp); - simplifyDup(vertexp); - if (allowCut) { - // The main algorithm works without these, though slower - // So if changing the main algorithm, comment these out for a test run - if (v3Global.opt.oAcycSimp()) { - cutBasic(vertexp); - cutBackward(vertexp); - } - } + workPop(); + simplifyNone(vertexp); + simplifyOne(vertexp); + simplifyOut(vertexp); + simplifyDup(vertexp); + if (allowCut) { + // The main algorithm works without these, though slower + // So if changing the main algorithm, comment these out for a test run + if (v3Global.opt.oAcycSimp()) { + cutBasic(vertexp); + cutBackward(vertexp); + } + } } deleteMarked(); } @@ -266,11 +272,11 @@ void GraphAcyc::simplify(bool allowCut) { void GraphAcyc::deleteMarked() { // Delete nodes marked for removal for (V3GraphVertex* nextp, *vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=nextp) { - nextp = vertexp->verticesNextp(); + nextp = vertexp->verticesNextp(); GraphAcycVertex* avertexp = static_cast(vertexp); - if (avertexp->isDelete()) { - avertexp->unlinkDelete(&m_breakGraph); VL_DANGLING(avertexp); - } + if (avertexp->isDelete()) { + avertexp->unlinkDelete(&m_breakGraph); VL_DANGLING(avertexp); + } } } @@ -279,21 +285,21 @@ void GraphAcyc::simplifyNone(GraphAcycVertex* avertexp) { // Likewise, vertices with no outputs if (avertexp->isDelete()) return; if (avertexp->inEmpty() || avertexp->outEmpty()) { - UINFO(9," SimplifyNoneRemove "<setDelete(); // Mark so we won't delete it twice - // Remove edges - while (V3GraphEdge* edgep = avertexp->outBeginp()) { - V3GraphVertex* otherVertexp = edgep->top(); - //UINFO(9," out "<unlinkDelete(); VL_DANGLING(edgep); - workPush(otherVertexp); - } - while (V3GraphEdge* edgep = avertexp->inBeginp()) { - V3GraphVertex* otherVertexp = edgep->fromp(); - //UINFO(9," in "<unlinkDelete(); VL_DANGLING(edgep); - workPush(otherVertexp); - } + UINFO(9," SimplifyNoneRemove "<setDelete(); // Mark so we won't delete it twice + // Remove edges + while (V3GraphEdge* edgep = avertexp->outBeginp()) { + V3GraphVertex* otherVertexp = edgep->top(); + //UINFO(9," out "<unlinkDelete(); VL_DANGLING(edgep); + workPush(otherVertexp); + } + while (V3GraphEdge* edgep = avertexp->inBeginp()) { + V3GraphVertex* otherVertexp = edgep->fromp(); + //UINFO(9," in "<unlinkDelete(); VL_DANGLING(edgep); + workPush(otherVertexp); + } } } @@ -301,32 +307,32 @@ void GraphAcyc::simplifyOne(GraphAcycVertex* avertexp) { // If a node has one input and one output, we can remove it and change the edges if (avertexp->isDelete()) return; if (avertexp->inSize1() && avertexp->outSize1()) { - V3GraphEdge* inEdgep = avertexp->inBeginp(); - V3GraphEdge* outEdgep = avertexp->outBeginp(); - V3GraphVertex* inVertexp = inEdgep->fromp(); - V3GraphVertex* outVertexp = outEdgep->top(); - // The in and out may be the same node; we'll make a loop - // The in OR out may be THIS node; we can't delete it then. - if (inVertexp!=avertexp && outVertexp!=avertexp) { - UINFO(9," SimplifyOneRemove "<setDelete(); // Mark so we won't delete it twice - // Make a new edge connecting the two vertices directly - // If both are breakable, we pick the one with less weight, else it's arbitrary - // We can forget about the origEdge list for the "non-selected" set of edges, - // as we need to break only one set or the other set of edges, not both. - // (This is why we must give preference to the cutable set.) - V3GraphEdge* templateEdgep = ( (inEdgep->cutable() - && (!outEdgep->cutable() - || inEdgep->weight()weight() )) - ? inEdgep : outEdgep); - // cppcheck-suppress leakReturnValNotUsed - edgeFromEdge(templateEdgep, inVertexp, outVertexp); - // Remove old edge - inEdgep->unlinkDelete(); VL_DANGLING(inEdgep); - outEdgep->unlinkDelete(); VL_DANGLING(outEdgep); VL_DANGLING(templateEdgep); - workPush(inVertexp); - workPush(outVertexp); - } + V3GraphEdge* inEdgep = avertexp->inBeginp(); + V3GraphEdge* outEdgep = avertexp->outBeginp(); + V3GraphVertex* inVertexp = inEdgep->fromp(); + V3GraphVertex* outVertexp = outEdgep->top(); + // The in and out may be the same node; we'll make a loop + // The in OR out may be THIS node; we can't delete it then. + if (inVertexp!=avertexp && outVertexp!=avertexp) { + UINFO(9," SimplifyOneRemove "<setDelete(); // Mark so we won't delete it twice + // Make a new edge connecting the two vertices directly + // If both are breakable, we pick the one with less weight, else it's arbitrary + // We can forget about the origEdge list for the "non-selected" set of edges, + // as we need to break only one set or the other set of edges, not both. + // (This is why we must give preference to the cutable set.) + V3GraphEdge* templateEdgep = ( (inEdgep->cutable() + && (!outEdgep->cutable() + || inEdgep->weight()weight() )) + ? inEdgep : outEdgep); + // cppcheck-suppress leakReturnValNotUsed + edgeFromEdge(templateEdgep, inVertexp, outVertexp); + // Remove old edge + inEdgep->unlinkDelete(); VL_DANGLING(inEdgep); + outEdgep->unlinkDelete(); VL_DANGLING(outEdgep); VL_DANGLING(templateEdgep); + workPush(inVertexp); + workPush(outVertexp); + } } } @@ -335,34 +341,34 @@ void GraphAcyc::simplifyOut(GraphAcycVertex* avertexp) { // to the next node in the list if (avertexp->isDelete()) return; if (avertexp->outSize1()) { - V3GraphEdge* outEdgep = avertexp->outBeginp(); - if (!outEdgep->cutable()) { - V3GraphVertex* outVertexp = outEdgep->top(); - UINFO(9," SimplifyOutRemove "<setDelete(); // Mark so we won't delete it twice - for (V3GraphEdge* nextp, *inEdgep = avertexp->inBeginp(); inEdgep; inEdgep=nextp) { - nextp = inEdgep->inNextp(); - V3GraphVertex* inVertexp = inEdgep->fromp(); - if (inVertexp == avertexp) { - if (debug()) v3error("Non-cutable edge forms a loop, vertex="<reportLoops(&V3GraphEdge::followNotCutable, - avertexp->origVertexp()); // calls OrderGraph::loopsVertexCb - // Things are unlikely to end well at this point, - // but we'll try something to get to further errors... - inEdgep->cutable(true); - return; - } - // Make a new edge connecting the two vertices directly - // cppcheck-suppress leakReturnValNotUsed - edgeFromEdge(inEdgep, inVertexp, outVertexp); - // Remove old edge - inEdgep->unlinkDelete(); VL_DANGLING(inEdgep); - workPush(inVertexp); - } - outEdgep->unlinkDelete(); VL_DANGLING(outEdgep); - workPush(outVertexp); - } + V3GraphEdge* outEdgep = avertexp->outBeginp(); + if (!outEdgep->cutable()) { + V3GraphVertex* outVertexp = outEdgep->top(); + UINFO(9," SimplifyOutRemove "<setDelete(); // Mark so we won't delete it twice + for (V3GraphEdge* nextp, *inEdgep = avertexp->inBeginp(); inEdgep; inEdgep=nextp) { + nextp = inEdgep->inNextp(); + V3GraphVertex* inVertexp = inEdgep->fromp(); + if (inVertexp == avertexp) { + if (debug()) v3error("Non-cutable edge forms a loop, vertex="<reportLoops(&V3GraphEdge::followNotCutable, + avertexp->origVertexp()); // calls OrderGraph::loopsVertexCb + // Things are unlikely to end well at this point, + // but we'll try something to get to further errors... + inEdgep->cutable(true); + return; + } + // Make a new edge connecting the two vertices directly + // cppcheck-suppress leakReturnValNotUsed + edgeFromEdge(inEdgep, inVertexp, outVertexp); + // Remove old edge + inEdgep->unlinkDelete(); VL_DANGLING(inEdgep); + workPush(inVertexp); + } + outEdgep->unlinkDelete(); VL_DANGLING(outEdgep); + workPush(outVertexp); + } } } @@ -371,37 +377,37 @@ void GraphAcyc::simplifyDup(GraphAcycVertex* avertexp) { if (avertexp->isDelete()) return; // Clear marks for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - edgep->top()->userp(NULL); + edgep->top()->userp(NULL); } // Mark edges and detect duplications for (V3GraphEdge* nextp, *edgep = avertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); - V3GraphVertex* outVertexp = edgep->top(); + nextp = edgep->outNextp(); + V3GraphVertex* outVertexp = edgep->top(); V3GraphEdge* prevEdgep = static_cast(outVertexp->userp()); - if (prevEdgep) { - if (!prevEdgep->cutable()) { - // !cutable duplicates prev !cutable: we can ignore it, redundant - // cutable duplicates prev !cutable: know it's not a relevant loop, ignore it - UINFO(8," DelDupEdge "< "<top()<unlinkDelete(); VL_DANGLING(edgep); - } else if (!edgep->cutable()) { - // !cutable duplicates prev cutable: delete the earlier cutable - UINFO(8," DelDupPrev "< "<top()<unlinkDelete(); VL_DANGLING(prevEdgep); - outVertexp->userp(edgep); - } else { - // cutable duplicates prev cutable: combine weights - UINFO(8," DelDupComb "< "<top()<cutable()) { + // !cutable duplicates prev !cutable: we can ignore it, redundant + // cutable duplicates prev !cutable: know it's not a relevant loop, ignore it + UINFO(8," DelDupEdge "< "<top()<unlinkDelete(); VL_DANGLING(edgep); + } else if (!edgep->cutable()) { + // !cutable duplicates prev cutable: delete the earlier cutable + UINFO(8," DelDupPrev "< "<top()<unlinkDelete(); VL_DANGLING(prevEdgep); + outVertexp->userp(edgep); + } else { + // cutable duplicates prev cutable: combine weights + UINFO(8," DelDupComb "< "<top()<weight(prevEdgep->weight() + edgep->weight()); addOrigEdgep(prevEdgep, edgep); - edgep->unlinkDelete(); VL_DANGLING(edgep); - } - workPush(outVertexp); - workPush(avertexp); - } else { - // No previous assignment - outVertexp->userp(edgep); - } + edgep->unlinkDelete(); VL_DANGLING(edgep); + } + workPush(outVertexp); + workPush(avertexp); + } else { + // No previous assignment + outVertexp->userp(edgep); + } } } @@ -409,12 +415,12 @@ void GraphAcyc::cutBasic(GraphAcycVertex* avertexp) { // Detect and cleanup any loops from node to itself if (avertexp->isDelete()) return; for (V3GraphEdge* nextp, *edgep = avertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); - if (edgep->cutable() && edgep->top()==avertexp) { + nextp = edgep->outNextp(); + if (edgep->cutable() && edgep->top()==avertexp) { cutOrigEdge(edgep, " Cut Basic"); - edgep->unlinkDelete(); VL_DANGLING(edgep); - workPush(avertexp); - } + edgep->unlinkDelete(); VL_DANGLING(edgep); + workPush(avertexp); + } } } @@ -423,19 +429,19 @@ void GraphAcyc::cutBackward(GraphAcycVertex* avertexp) { if (avertexp->isDelete()) return; // Clear marks for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - edgep->top()->user(false); + edgep->top()->user(false); } for (V3GraphEdge* edgep = avertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { - if (!edgep->cutable()) edgep->fromp()->user(true); + if (!edgep->cutable()) edgep->fromp()->user(true); } // Detect duplications for (V3GraphEdge* nextp, *edgep = avertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); - if (edgep->cutable() && edgep->top()->user()) { + nextp = edgep->outNextp(); + if (edgep->cutable() && edgep->top()->user()) { cutOrigEdge(edgep, " Cut A->B->A"); - edgep->unlinkDelete(); VL_DANGLING(edgep); - workPush(avertexp); - } + edgep->unlinkDelete(); VL_DANGLING(edgep); + workPush(avertexp); + } } } @@ -444,24 +450,26 @@ void GraphAcyc::place() { // Make a list of all cutable edges in the graph int numEdges = 0; - for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (edgep->weight() && edgep->cutable()) { - numEdges++; - } - } + for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); + vertexp; vertexp = vertexp->verticesNextp()) { + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (edgep->weight() && edgep->cutable()) { + numEdges++; + } + } } UINFO(4, " Cutable edges = "< edges; // List of all edges to be processed - edges.reserve(numEdges+1); // Make the vector properly sized right off the bat -- faster than reallocating - for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertexp->user(0); // Clear in prep of next step - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (edgep->weight() && edgep->cutable()) { - edges.push_back(edgep); - } - } + edges.reserve(numEdges+1); // Make the vector properly sized right off the bat -- faster than reallocating + for (V3GraphVertex* vertexp = m_breakGraph.verticesBeginp(); + vertexp; vertexp = vertexp->verticesNextp()) { + vertexp->user(0); // Clear in prep of next step + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (edgep->weight() && edgep->cutable()) { + edges.push_back(edgep); + } + } } // Sort by weight, then by vertex (so that we completely process one vertex, when possible) @@ -470,8 +478,8 @@ void GraphAcyc::place() { // Process each edge in weighted order m_placeStep = 10; for (std::vector::iterator it = edges.begin(); it!=edges.end(); ++it) { - V3GraphEdge* edgep = (*it); - placeTryEdge(edgep); + V3GraphEdge* edgep = (*it); + placeTryEdge(edgep); } } @@ -487,23 +495,23 @@ void GraphAcyc::placeTryEdge(V3GraphEdge* edgep) { bool loop = placeIterate(static_cast(edgep->top()), edgep->fromp()->rank()+1); if (!loop) { - // No loop, we can keep it as uncutable - // Commit the new ranks we calculated - // Just cleanup the list. If this is slow, we can add another set of - // user counters to avoid cleaning up the list. - while (workBeginp()) { - workPop(); - } + // No loop, we can keep it as uncutable + // Commit the new ranks we calculated + // Just cleanup the list. If this is slow, we can add another set of + // user counters to avoid cleaning up the list. + while (workBeginp()) { + workPop(); + } } else { - // Adding this edge would cause a loop, kill it - edgep->cutable(true); // So graph still looks pretty + // Adding this edge would cause a loop, kill it + edgep->cutable(true); // So graph still looks pretty cutOrigEdge(edgep, " Cut loop"); - edgep->unlinkDelete(); VL_DANGLING(edgep); - // Backout the ranks we calculated - while (GraphAcycVertex* vertexp = workBeginp()) { - workPop(); - vertexp->rank(vertexp->m_storedRank); - } + edgep->unlinkDelete(); VL_DANGLING(edgep); + // Backout the ranks we calculated + while (GraphAcycVertex* vertexp = workBeginp()) { + workPop(); + vertexp->rank(vertexp->m_storedRank); + } } } @@ -517,18 +525,18 @@ bool GraphAcyc::placeIterate(GraphAcycVertex* vertexp, uint32_t currentRank) { vertexp->user(m_placeStep); // Remember we're changing the rank of this node; might need to back out if (!vertexp->m_onWorkList) { - vertexp->m_storedRank = vertexp->rank(); - workPush(vertexp); + vertexp->m_storedRank = vertexp->rank(); + workPush(vertexp); } vertexp->rank(currentRank); // Follow all edges and increase their ranks for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (edgep->weight() && !edgep->cutable()) { + if (edgep->weight() && !edgep->cutable()) { if (placeIterate(static_cast(edgep->top()), currentRank+1)) { - // We don't need to reset user(); we'll use a different placeStep for the next edge - return true; // Loop detected - } - } + // We don't need to reset user(); we'll use a different placeStep for the next edge + return true; // Loop detected + } + } } vertexp->user(0); return false; diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp index 8680944ac..31eda865a 100644 --- a/src/V3GraphAlg.cpp +++ b/src/V3GraphAlg.cpp @@ -41,30 +41,30 @@ void V3Graph::deleteCutableOnlyEdges() { // Vertex::m_user begin: indicates can be deleted // Pass 1, mark those. Don't delete now, as we don't want to rip out whole trees for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertexp->user(true); - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { - if (!edgep->cutable()) { - vertexp->user(false); // Can't delete it - break; - } - } - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (!edgep->cutable()) { - vertexp->user(false); // Can't delete it - break; - } - } + vertexp->user(true); + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + if (!edgep->cutable()) { + vertexp->user(false); // Can't delete it + break; + } + } + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (!edgep->cutable()) { + vertexp->user(false); // Can't delete it + break; + } + } } // Pass 2, delete those marked // Rather than doing a delete() we set the weight to 0 which disconnects the edge. for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (vertexp->user()) { - //UINFO(7,"Disconnect "<name()<outBeginp(); edgep; edgep=edgep->outNextp()) { - edgep->cut(); - } - } + if (vertexp->user()) { + //UINFO(7,"Disconnect "<name()<outBeginp(); edgep; edgep=edgep->outNextp()) { + edgep->cut(); + } + } } // Vertex::m_user end, now unused @@ -75,54 +75,55 @@ void V3Graph::deleteCutableOnlyEdges() { // Algorithms - weakly connected components class GraphRemoveRedundant : GraphAlg<> { - bool m_sumWeights; ///< Sum, rather then maximize weights + bool m_sumWeights; ///< Sum, rather then maximize weights private: void main() { - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertexIterate(vertexp); - } + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + vertexIterate(vertexp); + } } void vertexIterate(V3GraphVertex* vertexp) { - // Clear marks - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - edgep->top()->userp(NULL); - } - // Mark edges and detect duplications - for (V3GraphEdge* nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); - if (followEdge(edgep)) { - V3GraphVertex* outVertexp = edgep->top(); + // Clear marks + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + edgep->top()->userp(NULL); + } + // Mark edges and detect duplications + for (V3GraphEdge* nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { + nextp = edgep->outNextp(); + if (followEdge(edgep)) { + V3GraphVertex* outVertexp = edgep->top(); V3GraphEdge* prevEdgep = static_cast(outVertexp->userp()); - if (!prevEdgep) { // No previous assignment - outVertexp->userp(edgep); - } else { // Duplicate - bool saveOld = true; - if (prevEdgep->cutable() && !edgep->cutable()) { - saveOld = false; // new !cutable more important than old - } else if (!prevEdgep->cutable() && edgep->cutable()) { - saveOld = true; // old !cutable more important than new - } else { - saveOld = true; - if (!m_sumWeights && (prevEdgep->weight() < edgep->weight())) { // Keep max weight - prevEdgep->weight(edgep->weight()); - } - } - if (saveOld) { - if (m_sumWeights) prevEdgep->weight(prevEdgep->weight() + edgep->weight()); - edgep->unlinkDelete(); VL_DANGLING(edgep); - } else { - if (m_sumWeights) edgep->weight(prevEdgep->weight() + edgep->weight()); - prevEdgep->unlinkDelete(); VL_DANGLING(prevEdgep); - outVertexp->userp(edgep); - } - } - } - } + if (!prevEdgep) { // No previous assignment + outVertexp->userp(edgep); + } else { // Duplicate + bool saveOld = true; + if (prevEdgep->cutable() && !edgep->cutable()) { + saveOld = false; // new !cutable more important than old + } else if (!prevEdgep->cutable() && edgep->cutable()) { + saveOld = true; // old !cutable more important than new + } else { + saveOld = true; + if (!m_sumWeights && (prevEdgep->weight() < edgep->weight())) { // Keep max weight + prevEdgep->weight(edgep->weight()); + } + } + if (saveOld) { + if (m_sumWeights) prevEdgep->weight(prevEdgep->weight() + edgep->weight()); + edgep->unlinkDelete(); VL_DANGLING(edgep); + } else { + if (m_sumWeights) edgep->weight(prevEdgep->weight() + edgep->weight()); + prevEdgep->unlinkDelete(); VL_DANGLING(prevEdgep); + outVertexp->userp(edgep); + } + } + } + } } public: GraphRemoveRedundant(V3Graph* graphp, V3EdgeFuncP edgeFuncp, bool sumWeights) : GraphAlg<>(graphp, edgeFuncp), m_sumWeights(sumWeights) { - main(); + main(); } ~GraphRemoveRedundant() {} }; @@ -181,36 +182,37 @@ void V3Graph::removeTransitiveEdges() { class GraphAlgWeakly : GraphAlg<> { private: void main() { - // Initialize state - m_graphp->clearColors(); - // Color graph - uint32_t currentColor = 0; - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - currentColor ++; - vertexIterate(vertexp, currentColor); - } + // Initialize state + m_graphp->clearColors(); + // Color graph + uint32_t currentColor = 0; + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + currentColor ++; + vertexIterate(vertexp, currentColor); + } } void vertexIterate(V3GraphVertex* vertexp, uint32_t currentColor) { - // Assign new color to each unvisited node - // then visit each of its edges, giving them the same color - if (vertexp->color()) return; // Already colored it - vertexp->color(currentColor); - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (followEdge(edgep)) { - vertexIterate(edgep->top(), currentColor); - } - } - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { - if (followEdge(edgep)) { - vertexIterate(edgep->fromp(), currentColor); - } - } + // Assign new color to each unvisited node + // then visit each of its edges, giving them the same color + if (vertexp->color()) return; // Already colored it + vertexp->color(currentColor); + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (followEdge(edgep)) { + vertexIterate(edgep->top(), currentColor); + } + } + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + if (followEdge(edgep)) { + vertexIterate(edgep->fromp(), currentColor); + } + } } public: GraphAlgWeakly(V3Graph* graphp, V3EdgeFuncP edgeFuncp) : GraphAlg<>(graphp, edgeFuncp) { - main(); + main(); } ~GraphAlgWeakly() {} }; @@ -225,78 +227,81 @@ void V3Graph::weaklyConnected(V3EdgeFuncP edgeFuncp) { class GraphAlgStrongly : GraphAlg<> { private: - uint32_t m_currentDfs; // DFS count + uint32_t m_currentDfs; // DFS count std::vector m_callTrace; // List of everything we hit processing so far void main() { - // Use Tarjan's algorithm to find the strongly connected subgraphs. - // Node State: - // Vertex::user // DFS number indicating possible root of subtree, 0=not iterated - // Vertex::color // Output subtree number (fully processed) + // Use Tarjan's algorithm to find the strongly connected subgraphs. + // Node State: + // Vertex::user // DFS number indicating possible root of subtree, 0=not iterated + // Vertex::color // Output subtree number (fully processed) - // Clear info - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertexp->color(0); - vertexp->user(0); - } - // Color graph - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (!vertexp->user()) { - m_currentDfs++; - vertexIterate(vertexp); - } - } - // If there's a single vertex of a color, it doesn't need a subgraph - // This simplifies the consumer's code, and reduces graph debugging clutter - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - bool onecolor = true; - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (followEdge(edgep)) { - if (vertexp->color() == edgep->top()->color()) { - onecolor = false; - break; - } - } - } - if (onecolor) vertexp->color(0); - } + // Clear info + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + vertexp->color(0); + vertexp->user(0); + } + // Color graph + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (!vertexp->user()) { + m_currentDfs++; + vertexIterate(vertexp); + } + } + // If there's a single vertex of a color, it doesn't need a subgraph + // This simplifies the consumer's code, and reduces graph debugging clutter + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + bool onecolor = true; + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (followEdge(edgep)) { + if (vertexp->color() == edgep->top()->color()) { + onecolor = false; + break; + } + } + } + if (onecolor) vertexp->color(0); + } } void vertexIterate(V3GraphVertex* vertexp) { - uint32_t thisDfsNum = m_currentDfs++; - vertexp->user(thisDfsNum); - vertexp->color(0); - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (followEdge(edgep)) { - V3GraphVertex* top = edgep->top(); - if (!top->user()) { // Dest not computed yet - vertexIterate(top); - } - if (!top->color()) { // Dest not in a component - if (vertexp->user() > top->user()) vertexp->user(top->user()); - } - } - } - if (vertexp->user() == thisDfsNum) { // New head of subtree - vertexp->color(thisDfsNum); // Mark as component - while (!m_callTrace.empty()) { - V3GraphVertex* popVertexp = m_callTrace.back(); - if (popVertexp->user() >= thisDfsNum) { // Lower node is part of this subtree - m_callTrace.pop_back(); - popVertexp->color(thisDfsNum); - } else { - break; - } - } - } else { // In another subtree (maybe...) - m_callTrace.push_back(vertexp); - } + uint32_t thisDfsNum = m_currentDfs++; + vertexp->user(thisDfsNum); + vertexp->color(0); + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (followEdge(edgep)) { + V3GraphVertex* top = edgep->top(); + if (!top->user()) { // Dest not computed yet + vertexIterate(top); + } + if (!top->color()) { // Dest not in a component + if (vertexp->user() > top->user()) vertexp->user(top->user()); + } + } + } + if (vertexp->user() == thisDfsNum) { // New head of subtree + vertexp->color(thisDfsNum); // Mark as component + while (!m_callTrace.empty()) { + V3GraphVertex* popVertexp = m_callTrace.back(); + if (popVertexp->user() >= thisDfsNum) { // Lower node is part of this subtree + m_callTrace.pop_back(); + popVertexp->color(thisDfsNum); + } else { + break; + } + } + } else { // In another subtree (maybe...) + m_callTrace.push_back(vertexp); + } } public: GraphAlgStrongly(V3Graph* graphp, V3EdgeFuncP edgeFuncp) : GraphAlg<>(graphp, edgeFuncp) { - m_currentDfs = 0; - main(); + m_currentDfs = 0; + main(); } ~GraphAlgStrongly() {} }; @@ -312,43 +317,45 @@ void V3Graph::stronglyConnected(V3EdgeFuncP edgeFuncp) { class GraphAlgRank : GraphAlg<> { private: void main() { - // Rank each vertex, ignoring cutable edges - // Vertex::m_user begin: 1 indicates processing, 2 indicates completed - // Clear existing ranks - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertexp->rank(0); - vertexp->user(0); - } - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (!vertexp->user()) { - vertexIterate(vertexp,1); - } - } + // Rank each vertex, ignoring cutable edges + // Vertex::m_user begin: 1 indicates processing, 2 indicates completed + // Clear existing ranks + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + vertexp->rank(0); + vertexp->user(0); + } + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (!vertexp->user()) { + vertexIterate(vertexp, 1); + } + } } void vertexIterate(V3GraphVertex* vertexp, uint32_t currentRank) { - // Assign rank to each unvisited node - // If larger rank is found, assign it and loop back through - // If we hit a back node make a list of all loops - if (vertexp->user() == 1) { - m_graphp->reportLoops(m_edgeFuncp, vertexp); - m_graphp->loopsMessageCb(vertexp); - return; - } - if (vertexp->rank() >= currentRank) return; // Already processed it - vertexp->user(1); - vertexp->rank(currentRank); - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (followEdge(edgep)) { - vertexIterate(edgep->top(), currentRank + vertexp->rankAdder()); - } - } - vertexp->user(2); + // Assign rank to each unvisited node + // If larger rank is found, assign it and loop back through + // If we hit a back node make a list of all loops + if (vertexp->user() == 1) { + m_graphp->reportLoops(m_edgeFuncp, vertexp); + m_graphp->loopsMessageCb(vertexp); + return; + } + if (vertexp->rank() >= currentRank) return; // Already processed it + vertexp->user(1); + vertexp->rank(currentRank); + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (followEdge(edgep)) { + vertexIterate(edgep->top(), currentRank + vertexp->rankAdder()); + } + } + vertexp->user(2); } public: GraphAlgRank(V3Graph* graphp, V3EdgeFuncP edgeFuncp) : GraphAlg<>(graphp, edgeFuncp) { - main(); + main(); } ~GraphAlgRank() {} }; @@ -368,46 +375,46 @@ void V3Graph::rank(V3EdgeFuncP edgeFuncp) { class GraphAlgRLoops : GraphAlg<> { private: std::vector m_callTrace; // List of everything we hit processing so far - bool m_done; // Exit algorithm + bool m_done; // Exit algorithm void main(V3GraphVertex* vertexp) { - // Vertex::m_user begin: 1 indicates processing, 2 indicates completed - // Clear existing ranks - m_graphp->userClearVertices(); - m_callTrace.reserve(100); - vertexIterate(vertexp, 0); + // Vertex::m_user begin: 1 indicates processing, 2 indicates completed + // Clear existing ranks + m_graphp->userClearVertices(); + m_callTrace.reserve(100); + vertexIterate(vertexp, 0); } void vertexIterate(V3GraphVertex* vertexp, uint32_t currentRank) { - // Assign rank to each unvisited node - // When we hit ourself again, return the list of all loops - if (m_done) return; + // Assign rank to each unvisited node + // When we hit ourself again, return the list of all loops + if (m_done) return; - // Can't just reserve(), unless we modify size() before setting array directly - while (m_callTrace.size() <= currentRank) m_callTrace.push_back(vertexp); - m_callTrace[currentRank++] = vertexp; + // Can't just reserve(), unless we modify size() before setting array directly + while (m_callTrace.size() <= currentRank) m_callTrace.push_back(vertexp); + m_callTrace[currentRank++] = vertexp; - if (vertexp->user() == 1) { - for (unsigned i=0; iloopsVertexCb(m_callTrace[i]); - } - m_done = true; - return; - } - if (vertexp->user() == 2) return; // Already processed it - vertexp->user(1); - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (followEdge(edgep)) { - vertexIterate(edgep->top(),currentRank); - } - } - vertexp->user(2); + if (vertexp->user() == 1) { + for (unsigned i=0; iloopsVertexCb(m_callTrace[i]); + } + m_done = true; + return; + } + if (vertexp->user() == 2) return; // Already processed it + vertexp->user(1); + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (followEdge(edgep)) { + vertexIterate(edgep->top(), currentRank); + } + } + vertexp->user(2); } public: GraphAlgRLoops(V3Graph* graphp, V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) : GraphAlg<>(graphp, edgeFuncp) { - m_done = false; - main(vertexp); + m_done = false; + main(vertexp); } ~GraphAlgRLoops() {} }; @@ -428,43 +435,43 @@ private: //! Iterate through all connected nodes of a graph with a loop or loops. V3GraphVertex* vertexIterateAll(V3GraphVertex* vertexp) { if (V3GraphVertex* newVertexp = static_cast(vertexp->userp())) { - return newVertexp; - } else { - newVertexp = vertexp->clone(m_loopGraphp); - vertexp->userp(newVertexp); + return newVertexp; + } else { + newVertexp = vertexp->clone(m_loopGraphp); + vertexp->userp(newVertexp); - for (V3GraphEdge* edgep = vertexp->outBeginp(); - edgep; edgep=edgep->outNextp()) { - if (followEdge(edgep)) { + for (V3GraphEdge* edgep = vertexp->outBeginp(); + edgep; edgep=edgep->outNextp()) { + if (followEdge(edgep)) { V3GraphEdge* newEdgep = static_cast(edgep->userp()); - if (!newEdgep) { - V3GraphVertex* newTop = vertexIterateAll(edgep->top()); - newEdgep = edgep->clone(m_loopGraphp, newVertexp, - newTop); - edgep->userp(newEdgep); - } - } - } - return newVertexp; - } + if (!newEdgep) { + V3GraphVertex* newTop = vertexIterateAll(edgep->top()); + newEdgep = edgep->clone(m_loopGraphp, newVertexp, + newTop); + edgep->userp(newEdgep); + } + } + } + return newVertexp; + } } public: GraphAlgSubtrees(V3Graph* graphp, V3Graph* loopGraphp, - V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) + V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp) : GraphAlg<>(graphp, edgeFuncp), m_loopGraphp(loopGraphp) { - // Vertex::m_userp - New vertex if we have seen this vertex already - // Edge::m_userp - New edge if we have seen this edge already - m_graphp->userClearVertices(); - m_graphp->userClearEdges(); - (void) vertexIterateAll(vertexp); + // Vertex::m_userp - New vertex if we have seen this vertex already + // Edge::m_userp - New edge if we have seen this edge already + m_graphp->userClearVertices(); + m_graphp->userClearEdges(); + (void) vertexIterateAll(vertexp); } ~GraphAlgSubtrees() {} }; //! Report the entire connected graph with a loop or loops void V3Graph::subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp, - V3Graph* loopGraphp) { + V3Graph* loopGraphp) { GraphAlgSubtrees(this, loopGraphp, edgeFuncp, vertexp); } @@ -474,12 +481,12 @@ void V3Graph::subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp, void V3Graph::makeEdgesNonCutable(V3EdgeFuncP edgeFuncp) { for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - // Only need one direction, we'll always see the other at some point... - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - if (edgep->cutable() && edgep->weight() && (edgeFuncp)(edgep)) { - edgep->cutable(false); - } - } + // Only need one direction, we'll always see the other at some point... + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + if (edgep->cutable() && edgep->weight() && (edgeFuncp)(edgep)) { + edgep->cutable(false); + } + } } } @@ -489,12 +496,12 @@ void V3Graph::makeEdgesNonCutable(V3EdgeFuncP edgeFuncp) { struct GraphSortVertexCmp { inline bool operator() (const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) const { - return lhsp->sortCmp(rhsp) < 0; + return lhsp->sortCmp(rhsp) < 0; } }; struct GraphSortEdgeCmp { inline bool operator() (const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) const { - return lhsp->sortCmp(rhsp) < 0; + return lhsp->sortCmp(rhsp) < 0; } }; @@ -502,12 +509,12 @@ void V3Graph::sortVertices() { // Sort list of vertices by rank, then fanout std::vector vertices; for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - vertices.push_back(vertexp); + vertices.push_back(vertexp); } std::stable_sort(vertices.begin(), vertices.end(), GraphSortVertexCmp()); this->verticesUnlink(); for (std::vector::iterator it = vertices.begin(); it!=vertices.end(); ++it) { - (*it)->verticesPushBack(this); + (*it)->verticesPushBack(this); } } @@ -515,32 +522,32 @@ void V3Graph::sortEdges() { // Sort edges by rank then fanout of node they point to std::vector edges; for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - // Make a vector - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - edges.push_back(edgep); - } - // Sort - std::stable_sort(edges.begin(), edges.end(), GraphSortEdgeCmp()); + // Make a vector + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { + edges.push_back(edgep); + } + // Sort + std::stable_sort(edges.begin(), edges.end(), GraphSortEdgeCmp()); - // Relink edges in specified order - // We know the vector contains all of the edges that were - // there originally (didn't delete or add) - vertexp->outUnlink(); + // Relink edges in specified order + // We know the vector contains all of the edges that were + // there originally (didn't delete or add) + vertexp->outUnlink(); for (std::vector::const_iterator it = edges.begin(); it!=edges.end(); ++it) { - (*it)->outPushBack(); - } - // Prep for next - edges.clear(); + (*it)->outPushBack(); + } + // Prep for next + edges.clear(); } } //###################################################################### //###################################################################### // Algorithms - ordering -// Compute near optimal ordering of the nodes, where: -// If a required edge is A->B, rank(A)B, rank(A)verticesNextp()) { - if (!vertexp->user()) { - orderDFSIterate(vertexp); - } + if (!vertexp->user()) { + orderDFSIterate(vertexp); + } } // Sort list of vertices by rank, then fanout. Fanout is a bit of a @@ -576,11 +583,11 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) { vertexp->user(1); double fanout = 0; for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (edgep->weight()) fanout += orderDFSIterate(edgep->m_top); + if (edgep->weight()) fanout += orderDFSIterate(edgep->m_top); } // Just count inbound edges for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - if (edgep->weight()) fanout ++; + if (edgep->weight()) fanout ++; } vertexp->fanout(fanout); vertexp->user(2); diff --git a/src/V3GraphAlg.h b/src/V3GraphAlg.h index a6f61d19c..01ff2a749 100644 --- a/src/V3GraphAlg.h +++ b/src/V3GraphAlg.h @@ -48,4 +48,4 @@ protected: //============================================================================ -#endif // Guard +#endif // Guard diff --git a/src/V3GraphDfa.cpp b/src/V3GraphDfa.cpp index 7b06fa7e4..d6b7d4811 100644 --- a/src/V3GraphDfa.cpp +++ b/src/V3GraphDfa.cpp @@ -36,15 +36,16 @@ DfaVertex* DfaGraph::findStart() { DfaVertex* startp = NULL; - for (V3GraphVertex* vertexp = this->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { - if (vvertexp->start()) { + for (V3GraphVertex* vertexp = this->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { + if (vvertexp->start()) { if (startp) vertexp->v3fatalSrc("Multiple start points in NFA graph"); - startp = vvertexp; - } - } else { + startp = vvertexp; + } + } else { vertexp->v3fatalSrc("Non DfaVertex in DfaGraph"); - } + } } if (!startp) v3fatalSrc("No start point in NFA graph"); return startp; @@ -66,8 +67,8 @@ private: typedef std::multimap HashMap; // MEMBERS - uint32_t m_step; // Processing step, so we can avoid clearUser all the time - HashMap m_hashMap; // Dfa Vertex for each set of NFA vertexes + uint32_t m_step; // Processing step, so we can avoid clearUser all the time + HashMap m_hashMap; // Dfa Vertex for each set of NFA vertexes #ifdef VL_CPPCHECK static int debug() { return 9; } @@ -83,271 +84,286 @@ private: void nextStep() { m_step++; } bool unseenNfaThisStep(V3GraphVertex* vertexp) { - // A nfa node not already seen this processing step - return (nfaState(vertexp) && !(vertexp->user()==m_step)); + // A nfa node not already seen this processing step + return (nfaState(vertexp) && !(vertexp->user()==m_step)); } DfaVertex* newDfaVertex(DfaVertex* nfaTemplatep=NULL) { DfaVertex* vertexp = new DfaVertex(graphp()); - vertexp->color(1); // Mark as dfa - if (nfaTemplatep && nfaTemplatep->start()) vertexp->start(true); - if (nfaTemplatep && nfaTemplatep->accepting()) vertexp->accepting(true); - UINFO(9, " New "<color(1); // Mark as dfa + if (nfaTemplatep && nfaTemplatep->start()) vertexp->start(true); + if (nfaTemplatep && nfaTemplatep->accepting()) vertexp->accepting(true); + UINFO(9, " New "<outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { - if (nfaState(dfaEdgep->top())) { - DfaVertex* nfaStatep = static_cast(dfaEdgep->top()); - hash ^= hashVertex(nfaStatep); - if (debug()) { + // Find the NFA states this dfa came from, + // Record a checksum, so we can search for it later by the list of nfa nodes. + // The order of the nodes is not deterministic; the hash thus must + // not depend on order of edges + uint32_t hash = 0; + // Foreach NFA state (this DFA state was formed from) + if (debug()) nextStep(); + for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); + dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { + if (nfaState(dfaEdgep->top())) { + DfaVertex* nfaStatep = static_cast(dfaEdgep->top()); + hash ^= hashVertex(nfaStatep); + if (debug()) { if (nfaStatep->user()==m_step) { nfaStatep->v3fatalSrc("DFA state points to duplicate NFA state."); } - nfaStatep->user(m_step); - } - } - } - return hash; + nfaStatep->user(m_step); + } + } + } + return hash; } uint32_t hashDfaOrigins(const DfaStates& nfasWithInput) { - // Find the NFA states this dfa came from, - uint32_t hash = 0; - for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) { - DfaVertex* nfaStatep = *nfaIt; - hash ^= hashVertex(nfaStatep); - } - return hash; + // Find the NFA states this dfa came from, + uint32_t hash = 0; + for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); + nfaIt!=nfasWithInput.end(); ++nfaIt) { + DfaVertex* nfaStatep = *nfaIt; + hash ^= hashVertex(nfaStatep); + } + return hash; } bool compareDfaOrigins(const DfaStates& nfasWithInput, DfaVertex* dfa2p) { - // Return true if the NFA nodes both DFAs came from are the same list - // Assume there are no duplicates in either input list or NFAs under dfa2 - nextStep(); - // Mark all input vertexes - int num1s = 0; - for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) { - DfaVertex* nfaStatep = *nfaIt; - nfaStatep->user(m_step); - num1s++; - } - if (!num1s) v3fatalSrc("DFA node construction that contains no NFA states"); + // Return true if the NFA nodes both DFAs came from are the same list + // Assume there are no duplicates in either input list or NFAs under dfa2 + nextStep(); + // Mark all input vertexes + int num1s = 0; + for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); + nfaIt!=nfasWithInput.end(); ++nfaIt) { + DfaVertex* nfaStatep = *nfaIt; + nfaStatep->user(m_step); + num1s++; + } + if (!num1s) v3fatalSrc("DFA node construction that contains no NFA states"); - // Check comparison; must all be marked - // (Check all in dfa2p were in dfa1p) - int num2s = 0; - for (V3GraphEdge* dfaEdgep = dfa2p->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { - if (nfaState(dfaEdgep->top())) { - if (dfaEdgep->top()->user() != m_step) return false; - num2s++; - } - } - // If we saw all of the nodes, then they have the same number of hits - // (Else something in dfa1p that wasn't in dfa2p) + // Check comparison; must all be marked + // (Check all in dfa2p were in dfa1p) + int num2s = 0; + for (V3GraphEdge* dfaEdgep = dfa2p->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { + if (nfaState(dfaEdgep->top())) { + if (dfaEdgep->top()->user() != m_step) return false; + num2s++; + } + } + // If we saw all of the nodes, then they have the same number of hits + // (Else something in dfa1p that wasn't in dfa2p) return (num1s == num2s); } void insertDfaOrigins(DfaVertex* dfaStatep) { - // Record the NFA states this dfa came from - uint32_t hash = hashDfaOrigins(dfaStatep); - m_hashMap.insert(make_pair(hash,dfaStatep)); + // Record the NFA states this dfa came from + uint32_t hash = hashDfaOrigins(dfaStatep); + m_hashMap.insert(make_pair(hash, dfaStatep)); } DfaVertex* findDfaOrigins(const DfaStates& nfasWithInput) { - // Find another DFA state which comes from the identical set of NFA states - // The order of the nodes is not deterministic; the hash thus must not depend on order of edges - uint32_t hash = hashDfaOrigins(nfasWithInput); + // Find another DFA state which comes from the identical set of NFA states + // The order of the nodes is not deterministic; the hash thus must + // not depend on order of edges + uint32_t hash = hashDfaOrigins(nfasWithInput); std::pair eqrange = m_hashMap.equal_range(hash); - for (HashMap::iterator it = eqrange.first; it != eqrange.second; ++it) { - DfaVertex* testp = it->second; - if (compareDfaOrigins(nfasWithInput, testp)) { - UINFO(9," DFA match for set: "<second; + if (compareDfaOrigins(nfasWithInput, testp)) { + UINFO(9," DFA match for set: "<outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { - if (nfaState(dfaEdgep->top())) { - DfaVertex* nfaStatep = static_cast(dfaEdgep->top()); - // Foreach input transition (on this nfaStatep) - for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { - DfaEdge* cNfaEdgep = static_cast(nfaEdgep); - if (cNfaEdgep->input().toNodep() == input.toNodep()) { - DfaVertex* nextStatep = static_cast(cNfaEdgep->top()); - if (unseenNfaThisStep(nextStatep)) { // Not processed? - nfasWithInput.push_back(nextStatep); - nextStatep->user(m_step); - UINFO(9," Reachable "<outBeginp(); + dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { + if (nfaState(dfaEdgep->top())) { + DfaVertex* nfaStatep = static_cast(dfaEdgep->top()); + // Foreach input transition (on this nfaStatep) + for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); + nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { + DfaEdge* cNfaEdgep = static_cast(nfaEdgep); + if (cNfaEdgep->input().toNodep() == input.toNodep()) { + DfaVertex* nextStatep = static_cast(cNfaEdgep->top()); + if (unseenNfaThisStep(nextStatep)) { // Not processed? + nfasWithInput.push_back(nextStatep); + nextStatep->user(m_step); + UINFO(9," Reachable "<outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { - DfaEdge* cNfaEdgep = static_cast(nfaEdgep); - if (cNfaEdgep->epsilon()) { - DfaVertex* nextStatep = static_cast(cNfaEdgep->top()); - if (unseenNfaThisStep(nextStatep)) { // Not processed? - nfasWithInput.push_back(nextStatep); - nextStatep->user(m_step); - UINFO(9," Epsilon Reachable "<outBeginp(); + nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { + DfaEdge* cNfaEdgep = static_cast(nfaEdgep); + if (cNfaEdgep->epsilon()) { + DfaVertex* nextStatep = static_cast(cNfaEdgep->top()); + if (unseenNfaThisStep(nextStatep)) { // Not processed? + nfasWithInput.push_back(nextStatep); + nextStatep->user(m_step); + UINFO(9," Epsilon Reachable "<clearColors(); - // Vertex::m_user begin: # indicates processed this m_step number - m_graphp->userClearVertices(); + UINFO(5,"Dfa to Nfa conversion...\n"); + // Vertex::color() begin: 1 indicates vertex on DFA graph, 0=NFA graph + m_graphp->clearColors(); + // Vertex::m_user begin: # indicates processed this m_step number + m_graphp->userClearVertices(); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_nfa"); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_nfa"); - // Find NFA start - DfaVertex* nfaStartp = graphp()->findStart(); + // Find NFA start + DfaVertex* nfaStartp = graphp()->findStart(); - // Create new DFA State (start state) from the NFA states - DfaVertex* dfaStartp = newDfaVertex(nfaStartp); + // Create new DFA State (start state) from the NFA states + DfaVertex* dfaStartp = newDfaVertex(nfaStartp); - DfaStates dfaUnprocps; // Unprocessed DFA nodes - dfaUnprocps.push_back(dfaStartp); + DfaStates dfaUnprocps; // Unprocessed DFA nodes + dfaUnprocps.push_back(dfaStartp); - UINFO(5,"Starting state conversion...\n"); - // Form DFA starting state from epsilon closure of NFA start - nextStep(); - DfaStates workps; workps.push_back(nfaStartp); + UINFO(5,"Starting state conversion...\n"); + // Form DFA starting state from epsilon closure of NFA start + nextStep(); + DfaStates workps; workps.push_back(nfaStartp); - while (!workps.empty()) { // While work - DfaVertex* nfaStatep = workps.back(); workps.pop_back(); - //UINFO(9," Processing "<user(m_step); // Mark as processed - // Add a edge so we can find NFAs from a given DFA. - // The NFA will never see this edge, because we only look at TO edges. - new DfaEdge(graphp(), dfaStartp, nfaStatep, DfaEdge::NA()); - // Find epsilon closure of this nfa node, and destinations to work list - for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { - DfaEdge* cNfaEdgep = static_cast(nfaEdgep); - DfaVertex* ecNfaStatep = static_cast(nfaEdgep->top()); - //UINFO(9," Consider "<top()<<" EP "<epsilon()<epsilon() - && unseenNfaThisStep(ecNfaStatep)) { // Not processed? - workps.push_back(ecNfaStatep); - } - } - } - if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_start"); - insertDfaOrigins(dfaStartp); + while (!workps.empty()) { // While work + DfaVertex* nfaStatep = workps.back(); workps.pop_back(); + //UINFO(9," Processing "<user(m_step); // Mark as processed + // Add a edge so we can find NFAs from a given DFA. + // The NFA will never see this edge, because we only look at TO edges. + new DfaEdge(graphp(), dfaStartp, nfaStatep, DfaEdge::NA()); + // Find epsilon closure of this nfa node, and destinations to work list + for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); + nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { + DfaEdge* cNfaEdgep = static_cast(nfaEdgep); + DfaVertex* ecNfaStatep = static_cast(nfaEdgep->top()); + //UINFO(9," Consider "<top()<<" EP "<epsilon()<epsilon() + && unseenNfaThisStep(ecNfaStatep)) { // Not processed? + workps.push_back(ecNfaStatep); + } + } + } + if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_start"); + insertDfaOrigins(dfaStartp); - int i=0; - UINFO(5,"Main state conversion...\n"); - while (!dfaUnprocps.empty()) { - DfaVertex* dfaStatep = dfaUnprocps.back(); dfaUnprocps.pop_back(); - UINFO(9," On dfaState "< inputs; - // Foreach NFA state (this DFA state was formed from) - for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { - if (nfaState(dfaEdgep->top())) { - DfaVertex* nfaStatep = static_cast(dfaEdgep->top()); - // Foreach input on this nfaStatep - for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { - DfaEdge* cNfaEdgep = static_cast(nfaEdgep); - if (!cNfaEdgep->epsilon()) { - if (inputs.find(cNfaEdgep->input().toInt()) == inputs.end()) { - inputs.insert(cNfaEdgep->input().toInt()); - UINFO(9," Input to "<input().toInt())<<" via "<outBeginp(); + dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) { + if (nfaState(dfaEdgep->top())) { + DfaVertex* nfaStatep = static_cast(dfaEdgep->top()); + // Foreach input on this nfaStatep + for (V3GraphEdge* nfaEdgep = nfaStatep->outBeginp(); + nfaEdgep; nfaEdgep=nfaEdgep->outNextp()) { + DfaEdge* cNfaEdgep = static_cast(nfaEdgep); + if (!cNfaEdgep->epsilon()) { + if (inputs.find(cNfaEdgep->input().toInt()) == inputs.end()) { + inputs.insert(cNfaEdgep->input().toInt()); + UINFO(9," Input to "<input().toInt())<<" via "<::const_iterator inIt=inputs.begin(); inIt!=inputs.end(); ++inIt) { - DfaInput input = *inIt; - UINFO(9," ==="<<++i<<"=======================\n"); + DfaInput input = *inIt; + UINFO(9," ==="<<++i<<"=======================\n"); UINFO(9," On input "<accepting()) toDfaStatep->accepting(true); - } - insertDfaOrigins(toDfaStatep); - } - // Add input transition + if ((*nfaIt)->accepting()) toDfaStatep->accepting(true); + } + insertDfaOrigins(toDfaStatep); + } + // Add input transition new DfaEdge(graphp(), dfaStatep, toDfaStatep, input); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("step"); - } - } + if (debug()>=6) m_graphp->dumpDotFilePrefixed("step"); + } + } - // Remove old NFA states - UINFO(5,"Removing NFA states...\n"); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_withnfa"); - for (V3GraphVertex* nextp,*vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=nextp) { - nextp = vertexp->verticesNextp(); - if (nfaState(vertexp)) { - vertexp->unlinkDelete(m_graphp); VL_DANGLING(vertexp); - } - } + // Remove old NFA states + UINFO(5,"Removing NFA states...\n"); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_withnfa"); + for (V3GraphVertex* nextp,*vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=nextp) { + nextp = vertexp->verticesNextp(); + if (nfaState(vertexp)) { + vertexp->unlinkDelete(m_graphp); VL_DANGLING(vertexp); + } + } - UINFO(5,"Done.\n"); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_done"); + UINFO(5,"Done.\n"); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("dfa_done"); } public: GraphNfaToDfa(V3Graph* graphp, V3EdgeFuncP edgeFuncp) : GraphAlg<>(graphp, edgeFuncp) { - m_step = 0; - main(); + m_step = 0; + main(); } ~GraphNfaToDfa() {} }; @@ -369,113 +385,115 @@ private: DfaGraph* graphp() { return static_cast(m_graphp); } bool isDead(DfaVertex* vertexp) { - // A state is dead if not accepting, and goes nowhere - if (vertexp->accepting() || vertexp->start()) return false; - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - if (edgep->top() != vertexp) return false; - } - return true; + // A state is dead if not accepting, and goes nowhere + if (vertexp->accepting() || vertexp->start()) return false; + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (edgep->top() != vertexp) return false; + } + return true; } void optimize_accepting_out() { - // Delete outbound edges from accepting states - // (As once we've accepted, we no longer care about anything else.) - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { - if (vvertexp->accepting()) { - for (V3GraphEdge* nextp,*edgep = vertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); - edgep->unlinkDelete(); VL_DANGLING(edgep); - } - } - } - } + // Delete outbound edges from accepting states + // (As once we've accepted, we no longer care about anything else.) + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { + if (vvertexp->accepting()) { + for (V3GraphEdge* nextp,*edgep = vertexp->outBeginp(); edgep; edgep=nextp) { + nextp = edgep->outNextp(); + edgep->unlinkDelete(); VL_DANGLING(edgep); + } + } + } + } } void optimize_orphans() { - // Remove states that don't come from start - // Presumably the previous optimization orphaned them. + // Remove states that don't come from start + // Presumably the previous optimization orphaned them. - // Vertex::m_user begin: 1 indicates on the work list, 2 processed - // (Otherwise we might have nodes on the list twice, and reference after deleting them.) - m_graphp->userClearVertices(); + // Vertex::m_user begin: 1 indicates on the work list, 2 processed + // (Otherwise we might have nodes on the list twice, and reference after deleting them.) + m_graphp->userClearVertices(); - DfaVertex* startp = graphp()->findStart(); + DfaVertex* startp = graphp()->findStart(); std::stack workps; workps.push(startp); - // Mark all nodes connected to start - while (!workps.empty()) { - V3GraphVertex* vertexp = workps.top(); workps.pop(); - vertexp->user(2); // Processed - // Add nodes from here to the work list - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { - V3GraphVertex* tovertexp = edgep->top(); - if (!tovertexp->user()) { - workps.push(tovertexp); - tovertexp->user(1); - } - } - } + // Mark all nodes connected to start + while (!workps.empty()) { + V3GraphVertex* vertexp = workps.top(); workps.pop(); + vertexp->user(2); // Processed + // Add nodes from here to the work list + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + V3GraphVertex* tovertexp = edgep->top(); + if (!tovertexp->user()) { + workps.push(tovertexp); + tovertexp->user(1); + } + } + } - // Delete all nodes not connected - for (V3GraphVertex* nextp,*vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=nextp) { - nextp = vertexp->verticesNextp(); - if (!vertexp->user()) { - vertexp->unlinkDelete(m_graphp); VL_DANGLING(vertexp); - } - } + // Delete all nodes not connected + for (V3GraphVertex* nextp,*vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=nextp) { + nextp = vertexp->verticesNextp(); + if (!vertexp->user()) { + vertexp->unlinkDelete(m_graphp); VL_DANGLING(vertexp); + } + } } void optimize_no_outbound() { - // Non-accepting states with no outbound transitions may be - // deleted. Then, any arcs feeding those states, and perhaps those - // states... + // Non-accepting states with no outbound transitions may be + // deleted. Then, any arcs feeding those states, and perhaps those + // states... - // Vertex::m_user begin: 1 indicates on the work list - // (Otherwise we might have nodes on the list twice, and reference after deleting them.) - m_graphp->userClearVertices(); + // Vertex::m_user begin: 1 indicates on the work list + // (Otherwise we might have nodes on the list twice, and reference after deleting them.) + m_graphp->userClearVertices(); - // Find all dead vertexes + // Find all dead vertexes std::stack workps; - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { - workps.push(vvertexp); - vertexp->user(1); - } else { - // If ever remove this, need dyn cast below + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { + workps.push(vvertexp); + vertexp->user(1); + } else { + // If ever remove this, need dyn cast below vertexp->v3fatalSrc("Non DfaVertex in dfa graph"); - } - } + } + } - // While deadness... Delete and find new dead nodes. - while (!workps.empty()) { - DfaVertex* vertexp = workps.top(); workps.pop(); - vertexp->user(0); - if (isDead(vertexp)) { - // Add nodes that go here to the work list - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { - DfaVertex* fromvertexp = static_cast(edgep->fromp()); - if (fromvertexp != vertexp - && !fromvertexp->user()) { - workps.push(fromvertexp); - fromvertexp->user(1); - } - } - // Transitions to this state removed by the unlink function - vertexp->unlinkDelete(m_graphp); VL_DANGLING(vertexp); - } - } + // While deadness... Delete and find new dead nodes. + while (!workps.empty()) { + DfaVertex* vertexp = workps.top(); workps.pop(); + vertexp->user(0); + if (isDead(vertexp)) { + // Add nodes that go here to the work list + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + DfaVertex* fromvertexp = static_cast(edgep->fromp()); + if (fromvertexp != vertexp + && !fromvertexp->user()) { + workps.push(fromvertexp); + fromvertexp->user(1); + } + } + // Transitions to this state removed by the unlink function + vertexp->unlinkDelete(m_graphp); VL_DANGLING(vertexp); + } + } } public: DfaGraphReduce(V3Graph* graphp, V3EdgeFuncP edgeFuncp) : GraphAlg<>(graphp, edgeFuncp) { - if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_in"); - optimize_accepting_out(); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_acc"); - optimize_orphans(); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_orph"); - optimize_no_outbound(); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_noout"); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_in"); + optimize_accepting_out(); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_acc"); + optimize_orphans(); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_orph"); + optimize_no_outbound(); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("opt_noout"); } ~DfaGraphReduce() {} }; @@ -514,62 +532,64 @@ private: DfaGraph* graphp() { return static_cast(m_graphp); } void add_complement_edges() { - // Find accepting vertex - DfaVertex* acceptp = NULL; - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { - if (vvertexp->accepting()) { - acceptp = vvertexp; - break; - } - } - } - if (!acceptp) v3fatalSrc("No accepting vertex in DFA"); + // Find accepting vertex + DfaVertex* acceptp = NULL; + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { + if (vvertexp->accepting()) { + acceptp = vvertexp; + break; + } + } + } + if (!acceptp) v3fatalSrc("No accepting vertex in DFA"); - // Remap edges - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { - if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { - //UINFO(9, " on vertex "<name()<accepting() && vvertexp != m_tempNewerReject) { - for (V3GraphEdge* nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { - nextp = edgep->outNextp(); - if (!edgep->user()) { // Not processed - // Old edges to accept now go to new reject - DfaEdge* vedgep = static_cast(edgep); - DfaVertex* tovertexp = static_cast(edgep->top()); - if (tovertexp->accepting()) { - new DfaEdge(graphp(), vvertexp, m_tempNewerReject, vedgep); - edgep->unlinkDelete(); VL_DANGLING(edgep); - } + // Remap edges + for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { + if (DfaVertex* vvertexp = dynamic_cast(vertexp)) { + //UINFO(9, " on vertex "<name()<accepting() && vvertexp != m_tempNewerReject) { + for (V3GraphEdge* nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) { + nextp = edgep->outNextp(); + if (!edgep->user()) { // Not processed + // Old edges to accept now go to new reject + DfaEdge* vedgep = static_cast(edgep); + DfaVertex* tovertexp = static_cast(edgep->top()); + if (tovertexp->accepting()) { + new DfaEdge(graphp(), vvertexp, m_tempNewerReject, vedgep); + edgep->unlinkDelete(); VL_DANGLING(edgep); + } - // NOT of all values goes to accept - // We make a edge for each value to OR, IE - // edge(complemented,a) edge(complemented,b) means !(a | b) - if (!tovertexp->accepting()) { // Note we must include edges moved above to reject + // NOT of all values goes to accept + // We make a edge for each value to OR, IE + // edge(complemented,a) edge(complemented,b) means !(a | b) + if (!tovertexp->accepting()) { // Note we must include edges moved above to reject DfaEdge* newp = new DfaEdge(graphp(), vvertexp, acceptp, vedgep); - newp->complement(!newp->complement()); - newp->user(1); - } - } - } - } - } - } + newp->complement(!newp->complement()); + newp->user(1); + } + } + } + } + } + } } public: DfaGraphComplement(V3Graph* dfagraphp, V3EdgeFuncP edgeFuncp) : GraphAlg<>(dfagraphp, edgeFuncp) { - if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_in"); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_in"); - // Vertex::m_user begin: 1 indicates new edge, no more processing - m_graphp->userClearEdges(); + // Vertex::m_user begin: 1 indicates new edge, no more processing + m_graphp->userClearEdges(); - m_tempNewerReject = new DfaVertex(graphp()); - add_complement_edges(); - if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_preswap"); + m_tempNewerReject = new DfaVertex(graphp()); + add_complement_edges(); + if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_preswap"); - m_tempNewerReject->unlinkDelete(graphp()); m_tempNewerReject=NULL; - if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_out"); + m_tempNewerReject->unlinkDelete(graphp()); m_tempNewerReject = NULL; + if (debug()>=6) m_graphp->dumpDotFilePrefixed("comp_out"); } ~DfaGraphComplement() {} }; diff --git a/src/V3GraphDfa.h b/src/V3GraphDfa.h index 0ad155653..9be2f75ae 100644 --- a/src/V3GraphDfa.h +++ b/src/V3GraphDfa.h @@ -33,10 +33,10 @@ class DfaEdge; //============================================================================= // NFA/DFA Graphs -/// The NFA graph consists of: -/// DfaVertex(START) The starting point -/// DfaVertex() Interior states -/// DfaVertex(ACCEPT) The completion point +/// The NFA graph consists of: +/// DfaVertex(START) The starting point +/// DfaVertex() Interior states +/// DfaVertex(ACCEPT) The completion point /// /// Transitions include a list of all inputs (arbitrary user pointers), /// or epsilon, represented as a empty list of inputs. @@ -48,36 +48,32 @@ class DfaEdge; /// /// Common transforms: /// -/// "*": DfaVertex(START) --> [epsilon] -->DfaVertex(ACCEPT) +/// "*": DfaVertex(START) --> [epsilon] -->DfaVertex(ACCEPT) /// -/// "L": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT) +/// "L": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT) /// -/// "LR": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT) -/// ->[ON_R]-->DfaVtx-->[epsilon]-/ +/// "LR": ...->[ON_L]-->DfaVtx-->[epsilon]-->DfaVtx(ACCEPT) +/// ->[ON_R]-->DfaVtx-->[epsilon]-/ /// -/// "L|R": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT) -/// \->[epsilon]-->DfaVtx-->[ON_R]-->DfaVtx()->[epsilon]-/ +/// "L|R": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT) +/// \->[epsilon]-->DfaVtx-->[ON_R]-->DfaVtx()->[epsilon]-/ /// -/// "L*": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT) -/// | ^\----[epsilon]<-------/ | -/// \->[epsilon]-----------------------------------------/ +/// "L*": ...->DfaVtx-->[epsilon]-->DfaVtx-->[ON_L]-->DfaVtx()->[epsilon]-->DfaVtx(ACCEPT) +/// | ^\----[epsilon]<-------/ | +/// \->[epsilon]-----------------------------------------/ class DfaGraph : public V3Graph { - // STATE public: + // CONSTRUCTORS DfaGraph() {} virtual ~DfaGraph() {} - // METHODS /// Find start node DfaVertex* findStart(); - /// Convert automata: NFA to DFA void nfaToDfa(); - /// Simplify a DFA automata void dfaReduce(); - /// Complement result (must already be dfa) void dfaComplement(); }; @@ -89,24 +85,24 @@ class DfaVertex : public V3GraphVertex { // Each DFA state is captured in this vertex. // Start and accepting are members, rather than the more intuitive // subclasses, as subclassing them would make it harder to inherit from here. - bool m_start; // Start state - bool m_accepting; // Accepting state? + bool m_start; // Start state + bool m_accepting; // Accepting state? public: // CONSTRUCTORS DfaVertex(DfaGraph* graphp, bool start=false, bool accepting=false) - : V3GraphVertex(graphp) - , m_start(start), m_accepting(accepting) {} + : V3GraphVertex(graphp) + , m_start(start), m_accepting(accepting) {} using V3GraphVertex::clone; // We are overriding, not overloading clone(V3Graph*) virtual DfaVertex* clone(DfaGraph* graphp) { - return new DfaVertex(graphp, start(), accepting()); } + return new DfaVertex(graphp, start(), accepting()); } virtual ~DfaVertex() {} // ACCESSORS virtual string dotShape() const { return (accepting()?"doublecircle":""); } virtual string dotColor() const { return start()?"blue":(color()?"red":"black"); } bool start() const { return m_start; } - void start(bool flag) { m_start=flag; } + void start(bool flag) { m_start = flag; } bool accepting() const { return m_accepting; } - void accepting(bool flag) { m_accepting=flag; } + void accepting(bool flag) { m_accepting = flag; } }; //============================================================================ @@ -118,37 +114,37 @@ typedef VNUser DfaInput; // Edge types class DfaEdge : public V3GraphEdge { - DfaInput m_input; - bool m_complement; // Invert value when doing compare + DfaInput m_input; + bool m_complement; // Invert value when doing compare public: static DfaInput EPSILON() { return VNUser::fromInt(0); } - static DfaInput NA() { return VNUser::fromInt(1); } // as in not-applicable + static DfaInput NA() { return VNUser::fromInt(1); } // as in not-applicable // CONSTRUCTORS DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaInput& input) - : V3GraphEdge(graphp, fromp, top, 1) - , m_input(input), m_complement(false) {} + : V3GraphEdge(graphp, fromp, top, 1) + , m_input(input), m_complement(false) {} DfaEdge(DfaGraph* graphp, DfaVertex* fromp, DfaVertex* top, const DfaEdge* copyfrom) - : V3GraphEdge(graphp, fromp, top, copyfrom->weight()) - , m_input(copyfrom->input()), m_complement(copyfrom->complement()) {} + : V3GraphEdge(graphp, fromp, top, copyfrom->weight()) + , m_input(copyfrom->input()), m_complement(copyfrom->complement()) {} virtual ~DfaEdge() {} // METHODS virtual string dotColor() const { - return (na() ? "yellow" - : epsilon() ? "green" - : "black"); } + return (na() ? "yellow" + : epsilon() ? "green" + : "black"); } virtual string dotLabel() const { - return (na() ? "" - : epsilon() ? "e" - : complement() ? ("not "+cvtToStr(input().toInt())) - : cvtToStr(input().toInt())); } + return (na() ? "" + : epsilon() ? "e" + : complement() ? ("not "+cvtToStr(input().toInt())) + : cvtToStr(input().toInt())); } virtual string dotStyle() const { return (na()||cutable())?"dashed":""; } bool epsilon() const { return input().toInt()==EPSILON().toInt(); } bool na() const { return input().toInt()==NA().toInt(); } bool complement() const { return m_complement; } - void complement(bool value) { m_complement=value; } - DfaInput input() const { return m_input; } + void complement(bool value) { m_complement = value; } + DfaInput input() const { return m_input; } }; //============================================================================ -#endif // Guard +#endif // Guard diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp index c5ea3635b..515325eaa 100644 --- a/src/V3GraphTest.cpp +++ b/src/V3GraphTest.cpp @@ -35,10 +35,10 @@ class V3GraphTest { public: // ***These tests only run with DEBUG ON*** static int debug() { - static int level = -1; - // Note setting just --debug will not enable this, as we exit when we run the test - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__, 0); - return level; + static int level = -1; + // Note setting just --debug will not enable this, as we exit when we run the test + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__, 0); + return level; } protected: @@ -46,21 +46,21 @@ protected: DfaGraph m_graph; // METHODS - for children - virtual void runTest() = 0; // Run the test - virtual string name() = 0; // Name of the test + virtual void runTest() = 0; // Run the test + virtual string name() = 0; // Name of the test // Utilities void dump() { - if (debug()>=9) { - m_graph.dumpDotFilePrefixed("v3graphtest_"+name()); - } + if (debug()>=9) { + m_graph.dumpDotFilePrefixed("v3graphtest_"+name()); + } } public: V3GraphTest() {} virtual ~V3GraphTest() {} void run() { - if (debug()) runTest(); + if (debug()) runTest(); } }; @@ -69,9 +69,10 @@ public: // Vertices and nodes class V3GraphTestVertex : public V3GraphVertex { - string m_name; + string m_name; public: - V3GraphTestVertex(V3Graph* graphp, const string& name) : V3GraphVertex(graphp), m_name(name) {} + V3GraphTestVertex(V3Graph* graphp, const string& name) + : V3GraphVertex(graphp), m_name(name) {} virtual ~V3GraphTestVertex() {} // ACCESSORS virtual string name() const { return m_name; } @@ -79,7 +80,8 @@ public: class V3GraphTestVarVertex : public V3GraphTestVertex { public: - V3GraphTestVarVertex(V3Graph* graphp, const string& name) : V3GraphTestVertex(graphp, name) {} + V3GraphTestVarVertex(V3Graph* graphp, const string& name) + : V3GraphTestVertex(graphp, name) {} virtual ~V3GraphTestVarVertex() {} // ACCESSORS virtual string dotColor() const { return "blue"; } @@ -93,30 +95,30 @@ class V3GraphTestStrong : public V3GraphTest { public: virtual string name() { return "strong"; } virtual void runTest() { - V3Graph* gp = &m_graph; - // Verify we break edges at a good point - // A simple alg would make 3 breaks, below only requires b->i to break - V3GraphTestVertex* i = new V3GraphTestVarVertex(gp,"*INPUTS*"); - V3GraphTestVertex* a = new V3GraphTestVarVertex(gp,"a"); - V3GraphTestVertex* b = new V3GraphTestVarVertex(gp,"b"); - V3GraphTestVertex* g1 = new V3GraphTestVarVertex(gp,"g1"); - V3GraphTestVertex* g2 = new V3GraphTestVarVertex(gp,"g2"); - V3GraphTestVertex* g3 = new V3GraphTestVarVertex(gp,"g3"); - V3GraphTestVertex* q = new V3GraphTestVarVertex(gp,"q"); - new V3GraphEdge(gp, i, a, 2, true); - new V3GraphEdge(gp, a, b, 2, true); - new V3GraphEdge(gp, b, g1, 2, true); - new V3GraphEdge(gp, b, g2, 2, true); - new V3GraphEdge(gp, b, g3, 2, true); - new V3GraphEdge(gp, g1, a, 2, true); - new V3GraphEdge(gp, g3, g2, 2, true); - new V3GraphEdge(gp, g2, g3, 2, true); - new V3GraphEdge(gp, g1, q, 2, true); - new V3GraphEdge(gp, g2, q, 2, true); - new V3GraphEdge(gp, g3, q, 2, true); + V3Graph* gp = &m_graph; + // Verify we break edges at a good point + // A simple alg would make 3 breaks, below only requires b->i to break + V3GraphTestVertex* i = new V3GraphTestVarVertex(gp, "*INPUTS*"); + V3GraphTestVertex* a = new V3GraphTestVarVertex(gp, "a"); + V3GraphTestVertex* b = new V3GraphTestVarVertex(gp, "b"); + V3GraphTestVertex* g1 = new V3GraphTestVarVertex(gp, "g1"); + V3GraphTestVertex* g2 = new V3GraphTestVarVertex(gp, "g2"); + V3GraphTestVertex* g3 = new V3GraphTestVarVertex(gp, "g3"); + V3GraphTestVertex* q = new V3GraphTestVarVertex(gp, "q"); + new V3GraphEdge(gp, i, a, 2, true); + new V3GraphEdge(gp, a, b, 2, true); + new V3GraphEdge(gp, b, g1, 2, true); + new V3GraphEdge(gp, b, g2, 2, true); + new V3GraphEdge(gp, b, g3, 2, true); + new V3GraphEdge(gp, g1, a, 2, true); + new V3GraphEdge(gp, g3, g2, 2, true); + new V3GraphEdge(gp, g2, g3, 2, true); + new V3GraphEdge(gp, g1, q, 2, true); + new V3GraphEdge(gp, g2, q, 2, true); + new V3GraphEdge(gp, g3, q, 2, true); - gp->stronglyConnected(&V3GraphEdge::followAlwaysTrue); - dump(); + gp->stronglyConnected(&V3GraphEdge::followAlwaysTrue); + dump(); UASSERT(i->color()!=a->color() && a->color() != g2->color() && g2->color() != q->color(), "SelfTest: Separate colors not assigned"); @@ -131,27 +133,27 @@ class V3GraphTestAcyc : public V3GraphTest { public: virtual string name() { return "acyc"; } virtual void runTest() { - V3Graph* gp = &m_graph; - // Verify we break edges at a good point - // A simple alg would make 3 breaks, below only requires b->i to break - V3GraphTestVertex* i = new V3GraphTestVarVertex(gp,"*INPUTS*"); - V3GraphTestVertex* a = new V3GraphTestVarVertex(gp,"a"); - V3GraphTestVertex* b = new V3GraphTestVarVertex(gp,"b"); - V3GraphTestVertex* g1 = new V3GraphTestVarVertex(gp,"g1"); - V3GraphTestVertex* g2 = new V3GraphTestVarVertex(gp,"g2"); - V3GraphTestVertex* g3 = new V3GraphTestVarVertex(gp,"g3"); - new V3GraphEdge(gp, i, a, 2, true); - new V3GraphEdge(gp, a, b, 2, true); - new V3GraphEdge(gp, b, g1, 2, true); - new V3GraphEdge(gp, b, g2, 2, true); - new V3GraphEdge(gp, b, g3, 2, true); - new V3GraphEdge(gp, g1, a, 2, true); - new V3GraphEdge(gp, g2, a, 2, true); - new V3GraphEdge(gp, g3, a, 2, true); + V3Graph* gp = &m_graph; + // Verify we break edges at a good point + // A simple alg would make 3 breaks, below only requires b->i to break + V3GraphTestVertex* i = new V3GraphTestVarVertex(gp, "*INPUTS*"); + V3GraphTestVertex* a = new V3GraphTestVarVertex(gp, "a"); + V3GraphTestVertex* b = new V3GraphTestVarVertex(gp, "b"); + V3GraphTestVertex* g1 = new V3GraphTestVarVertex(gp, "g1"); + V3GraphTestVertex* g2 = new V3GraphTestVarVertex(gp, "g2"); + V3GraphTestVertex* g3 = new V3GraphTestVarVertex(gp, "g3"); + new V3GraphEdge(gp, i, a, 2, true); + new V3GraphEdge(gp, a, b, 2, true); + new V3GraphEdge(gp, b, g1, 2, true); + new V3GraphEdge(gp, b, g2, 2, true); + new V3GraphEdge(gp, b, g3, 2, true); + new V3GraphEdge(gp, g1, a, 2, true); + new V3GraphEdge(gp, g2, a, 2, true); + new V3GraphEdge(gp, g3, a, 2, true); - gp->acyclic(&V3GraphEdge::followAlwaysTrue); - gp->order(); - dump(); + gp->acyclic(&V3GraphEdge::followAlwaysTrue); + gp->order(); + dump(); } }; @@ -159,118 +161,118 @@ class V3GraphTestVars : public V3GraphTest { public: virtual string name() { return "vars"; } virtual void runTest() { - V3Graph* gp = &m_graph; + V3Graph* gp = &m_graph; - V3GraphTestVertex* clk = new V3GraphTestVarVertex(gp,"$clk"); + V3GraphTestVertex* clk = new V3GraphTestVarVertex(gp, "$clk"); - V3GraphTestVertex* a = new V3GraphTestVarVertex(gp,"$a"); - V3GraphTestVertex* a_dly = new V3GraphTestVarVertex(gp,"$a_dly"); - V3GraphTestVertex* a_dlyblk= new V3GraphTestVarVertex(gp,"$a_dlyblk"); - V3GraphTestVertex* b = new V3GraphTestVarVertex(gp,"$b"); - V3GraphTestVertex* b_dly = new V3GraphTestVarVertex(gp,"$b_dly"); - V3GraphTestVertex* b_dlyblk= new V3GraphTestVarVertex(gp,"$b_dlyblk"); - V3GraphTestVertex* c = new V3GraphTestVarVertex(gp,"$c"); - V3GraphTestVertex* i = new V3GraphTestVarVertex(gp,"$i"); + V3GraphTestVertex* a = new V3GraphTestVarVertex(gp, "$a"); + V3GraphTestVertex* a_dly = new V3GraphTestVarVertex(gp, "$a_dly"); + V3GraphTestVertex* a_dlyblk= new V3GraphTestVarVertex(gp, "$a_dlyblk"); + V3GraphTestVertex* b = new V3GraphTestVarVertex(gp, "$b"); + V3GraphTestVertex* b_dly = new V3GraphTestVarVertex(gp, "$b_dly"); + V3GraphTestVertex* b_dlyblk= new V3GraphTestVarVertex(gp, "$b_dlyblk"); + V3GraphTestVertex* c = new V3GraphTestVarVertex(gp, "$c"); + V3GraphTestVertex* i = new V3GraphTestVarVertex(gp, "$i"); - V3GraphTestVertex* ap = new V3GraphTestVarVertex(gp,"$a_pre"); - V3GraphTestVertex* bp = new V3GraphTestVarVertex(gp,"$b_pre"); - V3GraphTestVertex* cp = new V3GraphTestVarVertex(gp,"$c_pre"); + V3GraphTestVertex* ap = new V3GraphTestVarVertex(gp, "$a_pre"); + V3GraphTestVertex* bp = new V3GraphTestVarVertex(gp, "$b_pre"); + V3GraphTestVertex* cp = new V3GraphTestVarVertex(gp, "$c_pre"); - V3GraphTestVertex* n; + V3GraphTestVertex* n; - // Logical order between clk, and posedge blocks - // implemented by special CLK prod/cons? - // Required order between first x_DLY<=x_pre and final x<=x_DLY - // implemented by producer/consumer on a_dly signals - // Required order between first x_DLY<=x_pre and x_DLY<=setters - // implemented by fake dependency on _dlyblk - // Required order between x_DLY<=setters and final x<=x_DLY - // implemented by producer/consumer on a_dly signals - // Desired order between different _DLY blocks so we can elim temporaries - // implemented by cutable "pre" signal dependencies + // Logical order between clk, and posedge blocks + // implemented by special CLK prod/cons? + // Required order between first x_DLY<=x_pre and final x<=x_DLY + // implemented by producer/consumer on a_dly signals + // Required order between first x_DLY<=x_pre and x_DLY<=setters + // implemented by fake dependency on _dlyblk + // Required order between x_DLY<=setters and final x<=x_DLY + // implemented by producer/consumer on a_dly signals + // Desired order between different _DLY blocks so we can elim temporaries + // implemented by cutable "pre" signal dependencies - n = new V3GraphTestVertex(gp,"*INPUTS*"); { - new V3GraphEdge(gp, n, clk, 2); - new V3GraphEdge(gp, n, i, 2); - } + n = new V3GraphTestVertex(gp, "*INPUTS*"); { + new V3GraphEdge(gp, n, clk, 2); + new V3GraphEdge(gp, n, i, 2); + } - V3GraphTestVertex* posedge = n = new V3GraphTestVertex(gp,"*posedge clk*"); { - new V3GraphEdge(gp, clk, n, 2); - } + V3GraphTestVertex* posedge = n = new V3GraphTestVertex(gp, "*posedge clk*"); { + new V3GraphEdge(gp, clk, n, 2); + } - // AssignPre's VarRefs on LHS: generate special BLK - // normal: VarRefs on LHS: generate normal - // underSBlock: VarRefs on RHS: consume 'pre' (required to save cutable tests) - n = new V3GraphTestVertex(gp,"a_dlyacyclic(&V3GraphEdge::followAlwaysTrue); - gp->order(); + gp->acyclic(&V3GraphEdge::followAlwaysTrue); + gp->order(); - dump(); + dump(); } }; //====================================================================== class DfaTestVertex : public DfaVertex { - string m_name; + string m_name; public: DfaTestVertex(DfaGraph* graphp, const string& name) : DfaVertex(graphp), m_name(name) {} virtual ~DfaTestVertex() {} @@ -283,43 +285,43 @@ class V3GraphTestDfa : public V3GraphTest { public: virtual string name() { return "dfa"; } virtual void runTest() { - DfaGraph* gp = &m_graph; + DfaGraph* gp = &m_graph; - // NFA Pattern for ( (LR) | (L*R)) Z - DfaTestVertex* st = new DfaTestVertex(gp,"*START*"); st->start(true); - DfaTestVertex* sl = new DfaTestVertex(gp,"sL"); - DfaTestVertex* srs = new DfaTestVertex(gp,"sR*"); - DfaTestVertex* sls = new DfaTestVertex(gp,"sL*"); - DfaTestVertex* sr = new DfaTestVertex(gp,"sR"); - DfaTestVertex* sz = new DfaTestVertex(gp,"sZ"); - DfaTestVertex* sac = new DfaTestVertex(gp,"*ACCEPT*"); sac->accepting(true); + // NFA Pattern for ( (LR) | (L*R)) Z + DfaTestVertex* st = new DfaTestVertex(gp, "*START*"); st->start(true); + DfaTestVertex* sl = new DfaTestVertex(gp, "sL"); + DfaTestVertex* srs = new DfaTestVertex(gp, "sR*"); + DfaTestVertex* sls = new DfaTestVertex(gp, "sL*"); + DfaTestVertex* sr = new DfaTestVertex(gp, "sR"); + DfaTestVertex* sz = new DfaTestVertex(gp, "sZ"); + DfaTestVertex* sac = new DfaTestVertex(gp, "*ACCEPT*"); sac->accepting(true); - VNUser L = VNUser::fromInt(0xaa); - VNUser R = VNUser::fromInt(0xbb); - VNUser Z = VNUser::fromInt(0xcc); + VNUser L = VNUser::fromInt(0xaa); + VNUser R = VNUser::fromInt(0xbb); + VNUser Z = VNUser::fromInt(0xcc); - new DfaEdge(gp, st, sl, DfaEdge::EPSILON()); - new DfaEdge(gp, sl, srs, L); - new DfaEdge(gp, srs, srs, R); - new DfaEdge(gp, srs, sz, Z); - new DfaEdge(gp, sz, sac, DfaEdge::EPSILON()); + new DfaEdge(gp, st, sl, DfaEdge::EPSILON()); + new DfaEdge(gp, sl, srs, L); + new DfaEdge(gp, srs, srs, R); + new DfaEdge(gp, srs, sz, Z); + new DfaEdge(gp, sz, sac, DfaEdge::EPSILON()); - new DfaEdge(gp, st, sls, DfaEdge::EPSILON()); - new DfaEdge(gp, sls, sls, L); - new DfaEdge(gp, sls, sr, R); - new DfaEdge(gp, sr, sz, Z); - new DfaEdge(gp, sz, sac, DfaEdge::EPSILON()); + new DfaEdge(gp, st, sls, DfaEdge::EPSILON()); + new DfaEdge(gp, sls, sls, L); + new DfaEdge(gp, sls, sr, R); + new DfaEdge(gp, sr, sz, Z); + new DfaEdge(gp, sz, sac, DfaEdge::EPSILON()); - dump(); - gp->nfaToDfa(); - dump(); - gp->dfaReduce(); - dump(); + dump(); + gp->nfaToDfa(); + dump(); + gp->dfaReduce(); + dump(); - gp->dfaComplement(); - dump(); - gp->dfaReduce(); - dump(); + gp->dfaComplement(); + dump(); + gp->dfaReduce(); + dump(); } }; @@ -336,14 +338,14 @@ class V3GraphTestImport : public V3GraphTest { public: virtual string name() { return "import"; } virtual void runTest() { - DfaGraph* gp = &m_graph; + DfaGraph* gp = &m_graph; if (V3GraphTest::debug()) DfaGraph::debug(9); - dotImport(); - dump(); - gp->acyclic(&V3GraphEdge::followAlwaysTrue); - dump(); - gp->rank(&V3GraphEdge::followAlwaysTrue); - dump(); + dotImport(); + dump(); + gp->acyclic(&V3GraphEdge::followAlwaysTrue); + dump(); + gp->rank(&V3GraphEdge::followAlwaysTrue); + dump(); } }; diff --git a/src/V3Hashed.cpp b/src/V3Hashed.cpp index ec26685f5..759984d69 100644 --- a/src/V3Hashed.cpp +++ b/src/V3Hashed.cpp @@ -19,9 +19,9 @@ //************************************************************************* // V3Hashed's Transformations: // -// Hash each node depth first -// Hash includes varp name and operator type, and constants -// Form lookup table based on hash of each statement w/ nodep and next nodep +// Hash each node depth first +// Hash includes varp name and operator type, and constants +// Form lookup table based on hash of each statement w/ nodep and next nodep // //************************************************************************* @@ -46,11 +46,11 @@ class HashedVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: - // AstNodeStmt::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) - //AstUser4InUse in V3Hashed.h + // AstNodeStmt::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) + //AstUser4InUse in V3Hashed.h // STATE - V3Hash m_lowerHash; // Hash of the statement we're building + V3Hash m_lowerHash; // Hash of the statement we're building bool m_cacheInUser4; // Use user4 to cache each V3Hash? // METHODS @@ -61,26 +61,28 @@ private: if (!m_cacheInUser4 || !nodep->user4()) { if (VN_IS(nodep->backp(), CFunc) && !(VN_IS(nodep, NodeStmt) || VN_IS(nodep, CFunc))) { - nodep->v3fatalSrc("Node "<prettyTypeName()<<" in statement position but not marked stmt (node under function)"); - } + nodep->v3fatalSrc("Node "<prettyTypeName()<<" in statement position but not marked stmt (node under function)"); + } V3Hash oldHash = m_lowerHash; - { - m_lowerHash = nodep->sameHash(); - if (m_lowerHash.isIllegal()) { - nodep->v3fatalSrc("sameHash function undefined (returns 0) for node under CFunc."); - } - // For identical nodes, the type should be the same thus dtypep should be the same too - m_lowerHash = V3Hash(m_lowerHash, V3Hash(nodep->type()<<6, V3Hash(nodep->dtypep()))); - // Now update m_lowerHash for our children's (and next children) contributions + { + m_lowerHash = nodep->sameHash(); + if (m_lowerHash.isIllegal()) { + nodep->v3fatalSrc("sameHash function undefined (returns 0) for node under CFunc."); + } + // For identical nodes, the type should be the same thus + // dtypep should be the same too + m_lowerHash = V3Hash(m_lowerHash, V3Hash(nodep->type()<<6, + V3Hash(nodep->dtypep()))); + // Now update m_lowerHash for our children's (and next children) contributions iterateChildren(nodep); - // Store the hash value - nodep->user4(m_lowerHash.fullValue()); - //UINFO(9, " hashnode "<user4(m_lowerHash.fullValue()); + //UINFO(9, " hashnode "<user4p()) { - HashedVisitor visitor (nodep); + HashedVisitor visitor (nodep); } } @@ -133,7 +135,7 @@ bool V3Hashed::sameNodes(AstNode* node1p, AstNode* node2p) { if (!node1p->user4p()) node1p->v3fatalSrc("Called isIdentical on non-hashed nodes"); if (!node2p->user4p()) node2p->v3fatalSrc("Called isIdentical on non-hashed nodes"); return (node1p->user4p() == node2p->user4p() // Same hash - && node1p->sameTree(node2p)); + && node1p->sameTree(node2p)); } void V3Hashed::erase(iterator it) { @@ -141,12 +143,12 @@ void V3Hashed::erase(iterator it) { UINFO(8," erase "<user4p()) nodep->v3fatalSrc("Called removeNode on non-hashed node"); m_hashMmap.erase(it); - nodep->user4p(NULL); // So we don't allow removeNode again + nodep->user4p(NULL); // So we don't allow removeNode again } void V3Hashed::dumpFilePrefixed(const string& nameComment, bool tree) { if (v3Global.opt.dumpTree()) { - dumpFile(v3Global.debugFilename(nameComment)+".hash", tree); + dumpFile(v3Global.debugFilename(nameComment)+".hash", tree); } } @@ -159,19 +161,19 @@ void V3Hashed::dumpFile(const string& filename, bool tree) { V3Hash lasthash; int num_in_bucket = 0; for (HashMmap::iterator it=begin(); 1; ++it) { - if (lasthash != it->first || it==end()) { - if (it!=end()) lasthash = it->first; - if (num_in_bucket) { - if (dist.find(num_in_bucket)==dist.end()) { - dist.insert(make_pair(num_in_bucket,1)); - } else { - ++dist[num_in_bucket]; - } - } - num_in_bucket = 0; - } - if (it==end()) break; - num_in_bucket++; + if (lasthash != it->first || it==end()) { + if (it!=end()) lasthash = it->first; + if (num_in_bucket) { + if (dist.find(num_in_bucket)==dist.end()) { + dist.insert(make_pair(num_in_bucket, 1)); + } else { + ++dist[num_in_bucket]; + } + } + num_in_bucket = 0; + } + if (it==end()) break; + num_in_bucket++; } *logp <<"\n*** STATS:\n"<first) { - lasthash = it->first; - *logp <<" "<first<second<second->dumpTree(*logp,"\t\t"); + if (lasthash != it->first) { + lasthash = it->first; + *logp <<" "<first<second<second->dumpTree(*logp, "\t\t"); } } @@ -197,10 +199,10 @@ V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) { if (!nodep->user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node"); std::pair eqrange = mmap().equal_range(nodeHash(nodep)); for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) { - AstNode* node2p = eqit->second; - if (nodep != node2p && sameNodes(nodep, node2p)) { - return eqit; - } + AstNode* node2p = eqit->second; + if (nodep != node2p && sameNodes(nodep, node2p)) { + return eqit; + } } return end(); } @@ -208,12 +210,13 @@ V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep) { V3Hashed::iterator V3Hashed::findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp) { UINFO(8," findD "<user4p()) nodep->v3fatalSrc("Called findDuplicate on non-hashed node"); - std::pair eqrange = mmap().equal_range(nodeHash(nodep)); + std::pair eqrange + = mmap().equal_range(nodeHash(nodep)); for (HashMmap::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) { - AstNode* node2p = eqit->second; - if (nodep != node2p && checkp->check(nodep,node2p) && sameNodes(nodep, node2p)) { - return eqit; - } + AstNode* node2p = eqit->second; + if (nodep != node2p && checkp->check(nodep, node2p) && sameNodes(nodep, node2p)) { + return eqit; + } } return end(); } diff --git a/src/V3Hashed.h b/src/V3Hashed.h index 889c467fa..d6556ff50 100644 --- a/src/V3Hashed.h +++ b/src/V3Hashed.h @@ -42,15 +42,15 @@ public: struct V3HashedUserCheck { // Functor for V3Hashed::findDuplicate - virtual bool check(AstNode*,AstNode*) =0; + virtual bool check(AstNode*, AstNode*) = 0; V3HashedUserCheck() {} virtual ~V3HashedUserCheck() {} }; class V3Hashed : public VHashedBase { // NODE STATE - // AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) - AstUser4InUse m_inuser4; + // AstNode::user4() -> V3Hash. Hash value of this node (hash of 0 is illegal) + AstUser4InUse m_inuser4; // TYPES typedef std::multimap HashMmap; @@ -58,7 +58,7 @@ public: typedef HashMmap::iterator iterator; private: // MEMBERS - HashMmap m_hashMmap; // hashvalue -> nodes with that hash + HashMmap m_hashMmap; // hashvalue -> nodes with that hash public: // CONSTRUCTORS @@ -66,18 +66,18 @@ public: ~V3Hashed() {} // ACCESSORS - HashMmap& mmap() { return m_hashMmap; } // Return map for iteration + HashMmap& mmap() { return m_hashMmap; } // Return map for iteration iterator begin() { return m_hashMmap.begin(); } iterator end() { return m_hashMmap.end(); } // METHODS void clear() { m_hashMmap.clear(); AstNode::user4ClearTree(); } - iterator hashAndInsert(AstNode* nodep); // Hash the node, and insert into map. Return iterator to inserted - void hash(AstNode* nodep); // Only hash the node - bool sameNodes(AstNode* node1p, AstNode* node2p); // After hashing, and tell if identical - void erase(iterator it); // Remove node from structures - iterator findDuplicate(AstNode* nodep); // Return duplicate in hash, if any - iterator findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp); // Extra user checks for sameness + iterator hashAndInsert(AstNode* nodep); // Hash the node, and insert into map. Return iterator to inserted + void hash(AstNode* nodep); // Only hash the node + bool sameNodes(AstNode* node1p, AstNode* node2p); // After hashing, and tell if identical + void erase(iterator it); // Remove node from structures + iterator findDuplicate(AstNode* nodep); // Return duplicate in hash, if any + iterator findDuplicate(AstNode* nodep, V3HashedUserCheck* checkp); // Extra user checks for sameness AstNode* iteratorNodep(iterator it) { return it->second; } void dumpFile(const string& filename, bool tree); void dumpFilePrefixed(const string& nameComment, bool tree=false); @@ -86,4 +86,4 @@ public: static V3Hash uncachedHash(const AstNode* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 0b19e6443..7d1d2820a 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -20,11 +20,11 @@ // V3Inline's Transformations: // // Each module: -// Look for CELL... PRAGMA INLINE_MODULE -// Replicate the cell's module -// Convert pins to wires that make assignments -// Rename vars to include cell name -// Insert cell's module statements into the upper module +// Look for CELL... PRAGMA INLINE_MODULE +// Replicate the cell's module +// Convert pins to wires that make assignments +// Rename vars to include cell name +// Insert cell's module statements into the upper module // //************************************************************************* @@ -44,7 +44,7 @@ #include VL_INCLUDE_UNORDERED_SET // CONFIG -static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it +static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it //###################################################################### // Inline state, as a visitor of each AstNode @@ -53,13 +53,13 @@ class InlineMarkVisitor : public AstNVisitor { private: // NODE STATE // Output - // AstNodeModule::user1() // OUTPUT: bool. User request to inline this module + // AstNodeModule::user1() // OUTPUT: bool. User request to inline this module // Internal state (can be cleared after this visit completes) - // AstNodeModule::user2() // CIL_*. Allowed to automatically inline module - // AstNodeModule::user3() // int. Number of cells referencing this module + // AstNodeModule::user2() // CIL_*. Allowed to automatically inline module + // AstNodeModule::user3() // int. Number of cells referencing this module // AstNodeModule::user4() // int. Statements in module - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; AstUser4InUse m_inuser4; // For the user2 field: @@ -70,10 +70,10 @@ private: // STATE AstNodeModule* m_modp; // Current module - V3Double0 m_statUnsup; // Statistic tracking + V3Double0 m_statUnsup; // Statistic tracking typedef std::vector ModVec; - ModVec m_allMods; // All modules, in top-down order. + ModVec m_allMods; // All modules, in top-down order. // Within the context of a given module, LocalInstanceMap maps // from child modules to the count of each child's local instantiations. @@ -85,35 +85,35 @@ private: // METHODS VL_DEBUG_FUNC; // Declare debug() void cantInline(const char* reason, bool hard) { - if (hard) { - if (m_modp->user2() != CIL_NOTHARD) { - UINFO(4," No inline hard: "<user2(CIL_NOTHARD); - ++m_statUnsup; - } - } else { - if (m_modp->user2() == CIL_MAYBE) { - UINFO(4," No inline soft: "<user2(CIL_NOTSOFT); - } - } + if (hard) { + if (m_modp->user2() != CIL_NOTHARD) { + UINFO(4," No inline hard: "<user2(CIL_NOTHARD); + ++m_statUnsup; + } + } else { + if (m_modp->user2() == CIL_MAYBE) { + UINFO(4," No inline soft: "<user2(CIL_NOTSOFT); + } + } } // VISITORS virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; + m_modp = nodep; m_allMods.push_back(nodep); - m_modp->user2(CIL_MAYBE); - m_modp->user4(0); // statement count + m_modp->user2(CIL_MAYBE); + m_modp->user4(0); // statement count if (VN_IS(m_modp, Iface)) { - // Inlining an interface means we no longer have a cell handle to resolve to. - // If inlining moves post-scope this can perhaps be relaxed. - cantInline("modIface",true); - } - if (m_modp->modPublic()) cantInline("modPublic",false); + // Inlining an interface means we no longer have a cell handle to resolve to. + // If inlining moves post-scope this can perhaps be relaxed. + cantInline("modIface", true); + } + if (m_modp->modPublic()) cantInline("modPublic", false); iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstCell* nodep) { nodep->modp()->user3Inc(); // Inc refs @@ -121,42 +121,42 @@ private: iterateChildren(nodep); } virtual void visit(AstPragma* nodep) { - if (nodep->pragType() == AstPragmaType::INLINE_MODULE) { - //UINFO(0,"PRAG MARK "<v3error("Inline pragma not under a module"); + if (nodep->pragType() == AstPragmaType::INLINE_MODULE) { + //UINFO(0,"PRAG MARK "<v3error("Inline pragma not under a module"); } else if (m_modp->user2() == CIL_MAYBE || m_modp->user2() == CIL_NOTSOFT) { m_modp->user2(CIL_USER); - } - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Remove so don't propagate to upper cell... - } else if (nodep->pragType() == AstPragmaType::NO_INLINE_MODULE) { - if (!m_modp) { - nodep->v3error("Inline pragma not under a module"); - } else { - cantInline("Pragma NO_INLINE_MODULE",false); - } - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Remove so don't propagate to upper cell... - } else { + } + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Remove so don't propagate to upper cell... + } else if (nodep->pragType() == AstPragmaType::NO_INLINE_MODULE) { + if (!m_modp) { + nodep->v3error("Inline pragma not under a module"); + } else { + cantInline("Pragma NO_INLINE_MODULE", false); + } + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); // Remove so don't propagate to upper cell... + } else { iterateChildren(nodep); - } + } } virtual void visit(AstVarXRef* nodep) { - // Cleanup link until V3LinkDot can correct it - nodep->varp(NULL); + // Cleanup link until V3LinkDot can correct it + nodep->varp(NULL); } virtual void visit(AstNodeFTaskRef* nodep) { - // Cleanup link until V3LinkDot can correct it - if (!nodep->packagep()) nodep->taskp(NULL); + // Cleanup link until V3LinkDot can correct it + if (!nodep->packagep()) nodep->taskp(NULL); iterateChildren(nodep); } virtual void visit(AstAlways* nodep) { iterateChildren(nodep); - m_modp->user4Inc(); // statement count + m_modp->user4Inc(); // statement count } virtual void visit(AstNodeAssign* nodep) { - // Don't count assignments, as they'll likely flatten out - // Still need to iterate though to nullify VarXRefs + // Don't count assignments, as they'll likely flatten out + // Still need to iterate though to nullify VarXRefs int oldcnt = m_modp->user4(); iterateChildren(nodep); m_modp->user4(oldcnt); @@ -208,20 +208,20 @@ private: iterateChildren(nodep); if (m_modp) { m_modp->user4Inc(); // Inc statement count - } + } } public: // CONSTUCTORS explicit InlineMarkVisitor(AstNode* nodep) { - m_modp = NULL; + m_modp = NULL; iterate(nodep); } virtual ~InlineMarkVisitor() { - V3Stats::addStat("Optimizations, Inline unsupported", m_statUnsup); - // Done with these, are not outputs - AstNode::user2ClearTree(); - AstNode::user3ClearTree(); + V3Stats::addStat("Optimizations, Inline unsupported", m_statUnsup); + // Done with these, are not outputs + AstNode::user2ClearTree(); + AstNode::user3ClearTree(); AstNode::user4ClearTree(); } }; @@ -234,14 +234,14 @@ class InlineCollectVisitor : public AstNVisitor { private: // NODE STATE // Output: - // AstCell::user4p() // AstCell* of the created clone + // AstCell::user4p() // AstCell* of the created clone // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstCell* nodep) { - nodep->user4p(nodep->clonep()); + nodep->user4p(nodep->clonep()); } // Accelerate virtual void visit(AstNodeStmt* nodep) {} @@ -271,52 +271,52 @@ private: // STATE StringSet m_renamedInterfaces; // Name of renamed interface variables - AstNodeModule* m_modp; // Current module - AstCell* m_cellp; // Cell being cloned + AstNodeModule* m_modp; // Current module + AstCell* m_cellp; // Cell being cloned // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstCellInline* nodep) { - // Inlined cell under the inline cell, need to move to avoid conflicts - nodep->unlinkFrBack(); - m_modp->addInlinesp(nodep); - // Rename - string name = m_cellp->name() + "__DOT__" + nodep->name(); - nodep->name(name); - UINFO(6, " Inline "<unlinkFrBack(); + m_modp->addInlinesp(nodep); + // Rename + string name = m_cellp->name() + "__DOT__" + nodep->name(); + nodep->name(name); + UINFO(6, " Inline "<name() + "__DOT__" + nodep->name(); - nodep->name(name); + // Cell under the inline cell, need to rename to avoid conflicts + string name = m_cellp->name() + "__DOT__" + nodep->name(); + nodep->name(name); iterateChildren(nodep); } virtual void visit(AstModule* nodep) { - m_renamedInterfaces.clear(); + m_renamedInterfaces.clear(); iterateChildren(nodep); } virtual void visit(AstVar* nodep) { - if (nodep->user2p()) { - // Make an assignment, so we'll trace it properly - // user2p is either a const or a var. + if (nodep->user2p()) { + // Make an assignment, so we'll trace it properly + // user2p is either a const or a var. AstConst* exprconstp = VN_CAST(nodep->user2p(), Const); AstVarRef* exprvarrefp = VN_CAST(nodep->user2p(), VarRef); - UINFO(8,"connectto: "<user2p()<v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up"); - } - if (exprconstp) { - m_modp->addStmtp(new AstAssignW(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, true), - exprconstp->cloneTree(true))); - } else if (nodep->user3()) { - // Public variable at the lower module end - we need to make sure we propagate - // the logic changes up and down; if we aliased, we might remove the change detection - // on the output variable. + UINFO(8,"connectto: "<user2p()<v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up"); + } + if (exprconstp) { + m_modp->addStmtp(new AstAssignW(nodep->fileline(), + new AstVarRef(nodep->fileline(), nodep, true), + exprconstp->cloneTree(true))); + } else if (nodep->user3()) { + // Public variable at the lower module end - we need to make sure we propagate + // the logic changes up and down; if we aliased, we might + // remove the change detection on the output variable. UINFO(9,"public pin assign: "<isNonOutput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias"); m_modp->addStmtp( @@ -327,82 +327,87 @@ private: m_modp->addStmtp( new AstAssignVarScope(nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, true), - new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false))); - AstNode* nodebp=exprvarrefp->varp(); + new AstVarRef(nodep->fileline(), + exprvarrefp->varp(), false))); + AstNode* nodebp = exprvarrefp->varp(); nodep ->fileline()->modifyStateInherit(nodebp->fileline()); nodebp->fileline()->modifyStateInherit(nodep ->fileline()); } else { - // Do to inlining child's variable now within the same module, so a AstVarRef not AstVarXRef below + // Do to inlining child's variable now within the same + // module, so a AstVarRef not AstVarXRef below m_modp->addStmtp( new AstAssignAlias(nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, true), - new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false))); - AstNode* nodebp=exprvarrefp->varp(); - nodep ->fileline()->modifyStateInherit(nodebp->fileline()); - nodebp->fileline()->modifyStateInherit(nodep ->fileline()); - } - } - // Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module + new AstVarRef(nodep->fileline(), + exprvarrefp->varp(), false))); + AstNode* nodebp = exprvarrefp->varp(); + nodep ->fileline()->modifyStateInherit(nodebp->fileline()); + nodebp->fileline()->modifyStateInherit(nodep ->fileline()); + } + } + // Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module if (AstIfaceRefDType* ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) { - m_renamedInterfaces.insert(nodep->name()); - // Each inlined cell that contain an interface variable need to copy the IfaceRefDType and point it to - // the newly cloned interface cell. + m_renamedInterfaces.insert(nodep->name()); + // Each inlined cell that contain an interface variable need to + // copy the IfaceRefDType and point it to the newly cloned + // interface cell. AstIfaceRefDType* newdp = VN_CAST(ifacerefp->cloneTree(false), IfaceRefDType); - nodep->dtypep(newdp); - ifacerefp->addNextHere(newdp); - // Relink to point to newly cloned cell - if (newdp->cellp()) { + nodep->dtypep(newdp); + ifacerefp->addNextHere(newdp); + // Relink to point to newly cloned cell + if (newdp->cellp()) { if (AstCell* newcellp = VN_CAST(newdp->cellp()->user4p(), Cell)) { - newdp->cellp(newcellp); - newdp->cellName(newcellp->name()); - // Tag the old ifacerefp to ensure it leaves no stale reference to the inlined cell. - newdp->user5(false); - ifacerefp->user5(true); - } - } - } - // Variable under the inline cell, need to rename to avoid conflicts - // Also clear I/O bits, as it is now local. - string name = m_cellp->name() + "__DOT__" + nodep->name(); - if (!nodep->isFuncLocal()) nodep->inlineAttrReset(name); - if (!m_cellp->isTrace()) nodep->trace(false); - if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); } - if (debug()>=9 && nodep->valuep()) { nodep->valuep()->dumpTree(cout,"varchangei:"); } + newdp->cellp(newcellp); + newdp->cellName(newcellp->name()); + // Tag the old ifacerefp to ensure it leaves no stale + // reference to the inlined cell. + newdp->user5(false); + ifacerefp->user5(true); + } + } + } + // Variable under the inline cell, need to rename to avoid conflicts + // Also clear I/O bits, as it is now local. + string name = m_cellp->name() + "__DOT__" + nodep->name(); + if (!nodep->isFuncLocal()) nodep->inlineAttrReset(name); + if (!m_cellp->isTrace()) nodep->trace(false); + if (debug()>=9) { nodep->dumpTree(cout, "varchanged:"); } + if (debug()>=9 && nodep->valuep()) { nodep->valuep()->dumpTree(cout, "varchangei:"); } iterateChildren(nodep); } virtual void visit(AstNodeFTask* nodep) { - // Function under the inline cell, need to rename to avoid conflicts - nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); + // Function under the inline cell, need to rename to avoid conflicts + nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); iterateChildren(nodep); } virtual void visit(AstTypedef* nodep) { - // Typedef under the inline cell, need to rename to avoid conflicts - nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); + // Typedef under the inline cell, need to rename to avoid conflicts + nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); iterateChildren(nodep); } virtual void visit(AstVarRef* nodep) { - if (nodep->varp()->user2p() // It's being converted to an alias. - && !nodep->varp()->user3() + if (nodep->varp()->user2p() // It's being converted to an alias. + && !nodep->varp()->user3() && !VN_IS(nodep->backp(), AssignAlias)) { // Don't constant propagate aliases (we just made) AstConst* exprconstp = VN_CAST(nodep->varp()->user2p(), Const); AstVarRef* exprvarrefp = VN_CAST(nodep->varp()->user2p(), VarRef); - if (exprconstp) { - nodep->replaceWith(exprconstp->cloneTree(true)); - nodep->deleteTree(); VL_DANGLING(nodep); - return; - } - else if (exprvarrefp) { - nodep->varp( exprvarrefp->varp() ); - } - else { - nodep->v3fatalSrc("Null connection?"); - } - } - nodep->name(nodep->varp()->name()); + if (exprconstp) { + nodep->replaceWith(exprconstp->cloneTree(true)); + nodep->deleteTree(); VL_DANGLING(nodep); + return; + } + else if (exprvarrefp) { + nodep->varp( exprvarrefp->varp() ); + } + else { + nodep->v3fatalSrc("Null connection?"); + } + } + nodep->name(nodep->varp()->name()); iterateChildren(nodep); } virtual void visit(AstVarXRef* nodep) { - // Track what scope it was originally under so V3LinkDot can resolve it + // Track what scope it was originally under so V3LinkDot can resolve it string newdots = VString::dot(m_cellp->name(), ".", nodep->inlinedDots()); nodep->inlinedDots(newdots); for (string tryname = nodep->dotted(); 1;) { @@ -421,13 +426,13 @@ private: iterateChildren(nodep); } virtual void visit(AstNodeFTaskRef* nodep) { - // Track what scope it was originally under so V3LinkDot can resolve it + // Track what scope it was originally under so V3LinkDot can resolve it string newdots = VString::dot(m_cellp->name(), ".", nodep->inlinedDots()); nodep->inlinedDots(newdots); - if (m_renamedInterfaces.count(nodep->dotted())) { - nodep->dotted(m_cellp->name() + "__DOT__" + nodep->dotted()); - } - UINFO(8," "<dotted())) { + nodep->dotted(m_cellp->name() + "__DOT__" + nodep->dotted()); + } + UINFO(8," "<scopeAttrp(); - if (afterp) afterp->unlinkFrBackWithNext(); + // If there's a %m in the display text, we add a special node that will contain the name() + // Similar code in V3Begin + // To keep correct visual order, must add before other Text's + AstNode* afterp = nodep->scopeAttrp(); + if (afterp) afterp->unlinkFrBackWithNext(); nodep->scopeAttrp(new AstText(nodep->fileline(), string("__DOT__")+m_cellp->name())); - if (afterp) nodep->scopeAttrp(afterp); - afterp = nodep->scopeEntrp(); - if (afterp) afterp->unlinkFrBackWithNext(); + if (afterp) nodep->scopeAttrp(afterp); + afterp = nodep->scopeEntrp(); + if (afterp) afterp->unlinkFrBackWithNext(); nodep->scopeEntrp(new AstText(nodep->fileline(), string("__DOT__")+m_cellp->name())); - if (afterp) nodep->scopeEntrp(afterp); + if (afterp) nodep->scopeEntrp(afterp); iterateChildren(nodep); } virtual void visit(AstCoverDecl* nodep) { - // Fix path in coverage statements + // Fix path in coverage statements nodep->hier(VString::dot(m_cellp->prettyName(), ".", nodep->hier())); iterateChildren(nodep); } @@ -460,8 +465,8 @@ private: public: // CONSTUCTORS InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp) { - m_modp = oldModp; - m_cellp = cellp; + m_modp = oldModp; + m_cellp = cellp; iterate(cloneModp); } virtual ~InlineRelinkVisitor() {} @@ -474,81 +479,83 @@ class InlineVisitor : public AstNVisitor { private: // NODE STATE // Cleared entire netlist - // AstIfaceRefDType::user5p() // Whether the cell pointed to by this AstIfaceRefDType has been inlined + // AstIfaceRefDType::user5p() // Whether the cell pointed to by this + // // AstIfaceRefDType has been inlined // Input: - // AstNodeModule::user1p() // bool. True to inline this module (from InlineMarkVisitor) + // AstNodeModule::user1p() // bool. True to inline this module (from InlineMarkVisitor) // Cleared each cell - // AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to - // AstVar::user3() // bool Don't alias the user2, keep it as signal - // AstCell::user4 // AstCell* of the created clone + // AstVar::user2p() // AstVarRef*/AstConst* Points to signal this + // // is a direct connect to + // AstVar::user3() // bool Don't alias the user2, keep it as signal + // AstCell::user4 // AstCell* of the created clone AstUser2InUse m_inuser2; AstUser3InUse m_inuser3; AstUser4InUse m_inuser4; - AstUser5InUse m_inuser5; + AstUser5InUse m_inuser5; // STATE AstNodeModule* m_modp; // Current module - V3Double0 m_statCells; // Statistic tracking + V3Double0 m_statCells; // Statistic tracking // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstNetlist* nodep) { - // Iterate modules backwards, in bottom-up order. Required! + // Iterate modules backwards, in bottom-up order. Required! iterateChildrenBackwards(nodep); } virtual void visit(AstIfaceRefDType* nodep) { - if (nodep->user5()) { - // The cell has been removed so let's make sure we don't leave a reference to it - // This dtype may still be in use by the AstAssignVarScope created earlier - // but that'll get cleared up later - nodep->cellp(NULL); - } + if (nodep->user5()) { + // The cell has been removed so let's make sure we don't leave a reference to it + // This dtype may still be in use by the AstAssignVarScope created earlier + // but that'll get cleared up later + nodep->cellp(NULL); + } } virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; + m_modp = nodep; iterateChildren(nodep); } virtual void visit(AstCell* nodep) { - if (nodep->modp()->user1()) { // Marked with inline request - UINFO(5," Inline CELL "<modp()->user1()) { // Marked with inline request + UINFO(5," Inline CELL "<pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - if (!pinp->exprp()) continue; + if (!pinp->exprp()) continue; V3Inst::pinReconnectSimple(pinp, nodep, false); - } + } - // Clone original module - if (debug()>=9) { nodep->dumpTree(cout,"inlcell:"); } - //if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); } - AstNodeModule* newmodp = nodep->modp()->cloneTree(false); - if (debug()>=9) { newmodp->dumpTree(cout,"newmod:"); } - // Clear var markings and find cell cross references - AstNode::user2ClearTree(); - AstNode::user4ClearTree(); - { InlineCollectVisitor(nodep->modp()); } // {} to destroy visitor immediately - // Create data for dotted variable resolution - AstCellInline* inlinep = new AstCellInline(nodep->fileline(), - nodep->name(), nodep->modp()->origName()); - m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells - // Create assignments to the pins + // Clone original module + if (debug()>=9) { nodep->dumpTree(cout, "inlcell:"); } + //if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); } + AstNodeModule* newmodp = nodep->modp()->cloneTree(false); + if (debug()>=9) { newmodp->dumpTree(cout, "newmod:"); } + // Clear var markings and find cell cross references + AstNode::user2ClearTree(); + AstNode::user4ClearTree(); + { InlineCollectVisitor(nodep->modp()); } // {} to destroy visitor immediately + // Create data for dotted variable resolution + AstCellInline* inlinep = new AstCellInline(nodep->fileline(), + nodep->name(), nodep->modp()->origName()); + m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells + // Create assignments to the pins for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - if (!pinp->exprp()) continue; - UINFO(6," Pin change from "<modVarp()<modVarp(); - AstVar* pinNewVarp = pinOldVarp->clonep(); - if (!pinNewVarp) pinOldVarp->v3fatalSrc("Cloning failed"); + if (!pinp->exprp()) continue; + UINFO(6," Pin change from "<modVarp()<modVarp(); + AstVar* pinNewVarp = pinOldVarp->clonep(); + if (!pinNewVarp) pinOldVarp->v3fatalSrc("Cloning failed"); AstNode* connectRefp = pinp->exprp(); if (!VN_IS(connectRefp, Const) && !VN_IS(connectRefp, VarRef)) { @@ -556,18 +563,18 @@ private: } V3Inst::checkOutputShort(pinp); - // Propagate any attributes across the interconnect - pinNewVarp->propagateAttrFrom(pinOldVarp); + // Propagate any attributes across the interconnect + pinNewVarp->propagateAttrFrom(pinOldVarp); if (VN_IS(connectRefp, VarRef)) { VN_CAST(connectRefp, VarRef)->varp()->propagateAttrFrom(pinOldVarp); - } + } - // One to one interconnect won't make a temporary variable. - // This prevents creating a lot of extra wires for clock signals. - // It will become a tracing alias. - UINFO(6,"One-to-one "<user2p(connectRefp); + // One to one interconnect won't make a temporary variable. + // This prevents creating a lot of extra wires for clock signals. + // It will become a tracing alias. + UINFO(6,"One-to-one "<user2p(connectRefp); // Public output inside the cell must go via an assign rather // than alias. Else the public logic will set the alias, losing // the value to be propagated up (InOnly isn't a problem as the @@ -575,19 +582,19 @@ private: pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->direction()==VDirection::OUTPUT); } - // Cleanup var names, etc, to not conflict - { InlineRelinkVisitor(newmodp, m_modp, nodep); } - // Move statements to top module - if (debug()>=9) { newmodp->dumpTree(cout,"fixmod:"); } - AstNode* stmtsp = newmodp->stmtsp(); - if (stmtsp) stmtsp->unlinkFrBackWithNext(); - if (stmtsp) m_modp->addStmtp(stmtsp); - // Remove the cell - newmodp->deleteTree(); VL_DANGLING(newmodp); // Clear any leftover ports, etc - nodep->unlinkFrBack(); - pushDeletep(nodep); VL_DANGLING(nodep); - if (debug()>=9) { m_modp->dumpTree(cout,"donemod:"); } - } + // Cleanup var names, etc, to not conflict + { InlineRelinkVisitor(newmodp, m_modp, nodep); } + // Move statements to top module + if (debug()>=9) { newmodp->dumpTree(cout, "fixmod:"); } + AstNode* stmtsp = newmodp->stmtsp(); + if (stmtsp) stmtsp->unlinkFrBackWithNext(); + if (stmtsp) m_modp->addStmtp(stmtsp); + // Remove the cell + newmodp->deleteTree(); VL_DANGLING(newmodp); // Clear any leftover ports, etc + nodep->unlinkFrBack(); + pushDeletep(nodep); VL_DANGLING(nodep); + if (debug()>=9) { m_modp->dumpTree(cout, "donemod:"); } + } } //-------------------- @@ -600,11 +607,11 @@ private: public: // CONSTUCTORS explicit InlineVisitor(AstNode* nodep) { - m_modp = NULL; + m_modp = NULL; iterate(nodep); } virtual ~InlineVisitor() { - V3Stats::addStat("Optimizations, Inlined cells", m_statCells); + V3Stats::addStat("Optimizations, Inlined cells", m_statCells); } }; @@ -613,8 +620,8 @@ public: void V3Inline::inlineAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<modulesp(); modp; modp=nextmodp) { nextmodp = VN_CAST(modp->nextp(), NodeModule); - if (modp->user1()) { // Was inlined - modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp); - } + if (modp->user1()) { // Was inlined + modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp); + } } V3Global::dumpCheckGlobalTree("inline", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Inline.h b/src/V3Inline.h index a9674d51e..f99c5d3f9 100644 --- a/src/V3Inline.h +++ b/src/V3Inline.h @@ -34,4 +34,4 @@ public: static void inlineAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index 4e1bf4c7e..910015802 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -20,8 +20,8 @@ // V3Inst's Transformations: // // Each module: -// Pins: -// Create a wire assign to interconnect to submodule +// Pins: +// Create a wire assign to interconnect to submodule // //************************************************************************* @@ -44,8 +44,8 @@ class InstVisitor : public AstNVisitor { private: // NODE STATE // Cleared each Cell: - // AstPin::user1p() -> bool. True if created assignment already - AstUser1InUse m_inuser1; + // AstPin::user1p() -> bool. True if created assignment already + AstUser1InUse m_inuser1; // STATE AstCell* m_cellp; // Current cell @@ -55,19 +55,19 @@ private: // VISITORS virtual void visit(AstCell* nodep) { - UINFO(4," CELL "< ASSIGNW(VARXREF(p),expr) (if sub's input) // or ASSIGNW(expr,VARXREF(p)) (if sub's output) UINFO(4," PIN "<exprp()) return; // No-connect - if (debug()>=9) nodep->dumpTree(cout," Pin_oldb: "); + if (debug()>=9) nodep->dumpTree(cout, " Pin_oldb: "); V3Inst::checkOutputShort(nodep); // Use user1p on the PIN to indicate we created an assign for this pin if (!nodep->user1SetOnce()) { @@ -81,42 +81,46 @@ private: if (nodep->modVarp()->isInoutish()) { nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator"); } else if (nodep->modVarp()->isWritable()) { - AstNode* rhsp = new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), false); + AstNode* rhsp = new AstVarXRef(exprp->fileline(), + nodep->modVarp(), m_cellp->name(), false); AstAssignW* assp = new AstAssignW(exprp->fileline(), exprp, rhsp); m_cellp->addNextHere(assp); } else if (nodep->modVarp()->isNonOutput()) { - // Don't bother moving constants now, - // we'll be pushing the const down to the cell soon enough. - AstNode* assp = new AstAssignW - (exprp->fileline(), - new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), true), - exprp); + // Don't bother moving constants now, + // we'll be pushing the const down to the cell soon enough. + AstNode* assp = new AstAssignW + (exprp->fileline(), + new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), true), + exprp); m_cellp->addNextHere(assp); - if (debug()>=9) assp->dumpTree(cout," _new: "); - } else if (nodep->modVarp()->isIfaceRef() + if (debug()>=9) assp->dumpTree(cout, " _new: "); + } else if (nodep->modVarp()->isIfaceRef() || (VN_IS(nodep->modVarp()->subDTypep(), UnpackArrayDType) - && VN_IS(VN_CAST(nodep->modVarp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType))) { - // Create an AstAssignVarScope for Vars to Cells so we can link with their scope later - AstNode* lhsp = new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), false); + && VN_IS(VN_CAST(nodep->modVarp()->subDTypep(), + UnpackArrayDType)->subDTypep(), IfaceRefDType))) { + // Create an AstAssignVarScope for Vars to Cells so we can + // link with their scope later + AstNode* lhsp = new AstVarXRef(exprp->fileline(), + nodep->modVarp(), m_cellp->name(), false); const AstVarRef* refp = VN_CAST(exprp, VarRef); const AstVarXRef* xrefp = VN_CAST(exprp, VarXRef); - if (!refp && !xrefp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef or VarXRef"); - AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, exprp); + if (!refp && !xrefp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef or VarXRef"); + AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, exprp); m_cellp->addNextHere(assp); - } else { - nodep->v3error("Assigned pin is neither input nor output"); - } - } + } else { + nodep->v3error("Assigned pin is neither input nor output"); + } + } - // We're done with the pin - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // We're done with the pin + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstUdpTable* nodep) { - if (!v3Global.opt.bboxUnsup()) { - // If we support primitives, update V3Undriven to remove special case - nodep->v3error("Unsupported: Verilog 1995 UDP Tables. Use --bbox-unsup to ignore tables."); - } + if (!v3Global.opt.bboxUnsup()) { + // If we support primitives, update V3Undriven to remove special case + nodep->v3error("Unsupported: Verilog 1995 UDP Tables. Use --bbox-unsup to ignore tables."); + } } // Save some time @@ -132,8 +136,8 @@ private: public: // CONSTUCTORS explicit InstVisitor(AstNetlist* nodep) { - m_cellp=NULL; - // + m_cellp = NULL; + // iterate(nodep); } virtual ~InstVisitor() {} @@ -146,16 +150,16 @@ class InstDeModVarVisitor : public AstNVisitor { private: // STATE typedef std::map VarNameMap; - VarNameMap m_modVarNameMap; // Per module, name of cloned variables + VarNameMap m_modVarNameMap; // Per module, name of cloned variables VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstVar* nodep) { if (VN_IS(nodep->dtypep(), IfaceRefDType)) { - UINFO(8," dm-1-VAR "<name(), nodep)); + UINFO(8," dmINSERT "<name(), nodep)); } AstVar* find(const string& name) { - VarNameMap::iterator it = m_modVarNameMap.find(name); - if (it != m_modVarNameMap.end()) { - return it->second; - } else { - return NULL; - } + VarNameMap::iterator it = m_modVarNameMap.find(name); + if (it != m_modVarNameMap.end()) { + return it->second; + } else { + return NULL; + } } void dump() { - for (VarNameMap::iterator it=m_modVarNameMap.begin(); it!=m_modVarNameMap.end(); ++it) { - cout<<"-namemap: "<first<<" -> "<second<first<<" -> "<second< VarNameMap; @@ -212,221 +216,228 @@ private: virtual void visit(AstVar* nodep) { if (VN_IS(nodep->dtypep(), UnpackArrayDType) && VN_IS(VN_CAST(nodep->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType)) { - UINFO(8," dv-vec-VAR "<dtypep(), UnpackArrayDType); - AstNode* prevp = NULL; - for (int i = arrdtype->lsb(); i <= arrdtype->msb(); ++i) { - string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__"; - UINFO(8,"VAR name insert "<subDTypep(), IfaceRefDType)->cloneTree(false); - arrdtype->addNextHere(ifaceRefp); - ifaceRefp->cellp(NULL); + AstNode* prevp = NULL; + for (int i = arrdtype->lsb(); i <= arrdtype->msb(); ++i) { + string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__"; + UINFO(8,"VAR name insert "<subDTypep(), IfaceRefDType)->cloneTree(false); + arrdtype->addNextHere(ifaceRefp); + ifaceRefp->cellp(NULL); - AstVar* varNewp = nodep->cloneTree(false); - varNewp->name(varNewName); - varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); - varNewp->dtypep(ifaceRefp); - m_deModVars.insert(varNewp); - if (!prevp) { - prevp = varNewp; - } else { - prevp->addNextHere(varNewp); - } - } - } - if (prevp) nodep->addNextHere(prevp); - if (prevp && debug()==9) { prevp->dumpTree(cout, "newintf: "); cout << endl; } - } + AstVar* varNewp = nodep->cloneTree(false); + varNewp->name(varNewName); + varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); + varNewp->dtypep(ifaceRefp); + m_deModVars.insert(varNewp); + if (!prevp) { + prevp = varNewp; + } else { + prevp->addNextHere(varNewp); + } + } + } + if (prevp) nodep->addNextHere(prevp); + if (prevp && debug()==9) { prevp->dumpTree(cout, "newintf: "); cout << endl; } + } iterateChildren(nodep); } virtual void visit(AstCell* nodep) { - UINFO(4," CELL "<modp()) nodep->v3fatalSrc("Unlinked"); - m_deModVars.main(nodep->modp()); - // - if (nodep->rangep()) { - m_cellRangep = nodep->rangep(); + UINFO(4," CELL "<modp()) nodep->v3fatalSrc("Unlinked"); + m_deModVars.main(nodep->modp()); + // + if (nodep->rangep()) { + m_cellRangep = nodep->rangep(); AstVar* ifaceVarp = VN_CAST(nodep->nextp(), Var); - bool isIface = ifaceVarp + bool isIface = ifaceVarp && VN_IS(ifaceVarp->dtypep(), UnpackArrayDType) - && VN_IS(VN_CAST(ifaceVarp->dtypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType); + && VN_IS(VN_CAST(ifaceVarp->dtypep(), + UnpackArrayDType)->subDTypep(), IfaceRefDType); - // Make all of the required clones - for (int i = 0; i < m_cellRangep->elementsConst(); i++) { - m_instSelNum = m_cellRangep->littleEndian() ? (m_cellRangep->elementsConst() - 1 - i) : i; - int instNum = m_cellRangep->lsbConst() + i; + // Make all of the required clones + for (int i = 0; i < m_cellRangep->elementsConst(); i++) { + m_instSelNum = m_cellRangep->littleEndian() ? (m_cellRangep->elementsConst() - 1 - i) : i; + int instNum = m_cellRangep->lsbConst() + i; - AstCell* newp = nodep->cloneTree(false); - nodep->addNextHere(newp); - // Remove ranging and fix name - newp->rangep()->unlinkFrBack()->deleteTree(); - // Somewhat illogically, we need to rename the orignal name of the cell too. - // as that is the name users expect for dotting - // The spec says we add [x], but that won't work in C... - newp->name(newp->name()+"__BRA__"+cvtToStr(instNum)+"__KET__"); - newp->origName(newp->origName()+"__BRA__"+cvtToStr(instNum)+"__KET__"); - UINFO(8," CELL loop "<cloneTree(false); + nodep->addNextHere(newp); + // Remove ranging and fix name + newp->rangep()->unlinkFrBack()->deleteTree(); + // Somewhat illogically, we need to rename the orignal name of the cell too. + // as that is the name users expect for dotting + // The spec says we add [x], but that won't work in C... + newp->name(newp->name()+"__BRA__"+cvtToStr(instNum)+"__KET__"); + newp->origName(newp->origName()+"__BRA__"+cvtToStr(instNum)+"__KET__"); + UINFO(8," CELL loop "<dtypep(), UnpackArrayDType); AstIfaceRefDType* origIfaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType); - origIfaceRefp->cellp(NULL); - AstVar* varNewp = ifaceVarp->cloneTree(false); - AstIfaceRefDType* ifaceRefp = VN_CAST(arrdtype->subDTypep(), IfaceRefDType)->cloneTree(false); - arrdtype->addNextHere(ifaceRefp); - ifaceRefp->cellp(newp); - ifaceRefp->cellName(newp->name()); - varNewp->name(varNewp->name() + "__BRA__" + cvtToStr(instNum) + "__KET__"); - varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(instNum) + "__KET__"); - varNewp->dtypep(ifaceRefp); - newp->addNextHere(varNewp); - if (debug()==9) { varNewp->dumpTree(cout, "newintf: "); cout << endl; } - } - // Fixup pins + origIfaceRefp->cellp(NULL); + AstVar* varNewp = ifaceVarp->cloneTree(false); + AstIfaceRefDType* ifaceRefp + = VN_CAST(arrdtype->subDTypep(), IfaceRefDType)->cloneTree(false); + arrdtype->addNextHere(ifaceRefp); + ifaceRefp->cellp(newp); + ifaceRefp->cellName(newp->name()); + varNewp->name(varNewp->name() + "__BRA__" + cvtToStr(instNum) + "__KET__"); + varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(instNum) + "__KET__"); + varNewp->dtypep(ifaceRefp); + newp->addNextHere(varNewp); + if (debug()==9) { varNewp->dumpTree(cout, "newintf: "); cout << endl; } + } + // Fixup pins iterateAndNextNull(newp->pinsp()); - if (debug()==9) { newp->dumpTree(cout,"newcell: "); cout<dumpTree(cout, "newcell: "); cout<unlinkFrBack(); pushDeletep(ifaceVarp); VL_DANGLING(ifaceVarp); - } - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } else { - m_cellRangep = NULL; + // Done. Delete original + m_cellRangep = NULL; + if (isIface) { + ifaceVarp->unlinkFrBack(); pushDeletep(ifaceVarp); VL_DANGLING(ifaceVarp); + } + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } else { + m_cellRangep = NULL; iterateChildren(nodep); - } + } } virtual void visit(AstPin* nodep) { - // Any non-direct pins need reconnection with a part-select - if (!nodep->exprp()) return; // No-connect - if (m_cellRangep) { - UINFO(4," PIN "<modVarp()->width(); - int expwidth = nodep->exprp()->width(); + // Any non-direct pins need reconnection with a part-select + if (!nodep->exprp()) return; // No-connect + if (m_cellRangep) { + UINFO(4," PIN "<modVarp()->width(); + int expwidth = nodep->exprp()->width(); std::pair pinDim = nodep->modVarp()->dtypep()->dimensions(false); std::pair expDim = nodep->exprp()->dtypep()->dimensions(false); - UINFO(4," PINVAR "<modVarp()<exprp()<exprp()->unlinkFrBack(); + UINFO(4," PINVAR "<modVarp()<exprp()<exprp()->unlinkFrBack(); exprp = new AstArraySel(exprp->fileline(), exprp, m_instSelNum); - nodep->exprp(exprp); - } else if (expwidth == pinwidth) { - // NOP: Arrayed instants: widths match so connect to each instance - } else if (expwidth == pinwidth*m_cellRangep->elementsConst()) { - // Arrayed instants: one bit for each of the instants (each assign is 1 pinwidth wide) - if (m_cellRangep->littleEndian()) { - nodep->v3warn(LITENDIAN,"Little endian cell range connecting to vector: MSB < LSB of cell range: " - <lsbConst()<<":"<msbConst()); - } + nodep->exprp(exprp); + } else if (expwidth == pinwidth) { + // NOP: Arrayed instants: widths match so connect to each instance + } else if (expwidth == pinwidth*m_cellRangep->elementsConst()) { + // Arrayed instants: one bit for each of the instants (each + // assign is 1 pinwidth wide) + if (m_cellRangep->littleEndian()) { + nodep->v3warn(LITENDIAN, "Little endian cell range connecting to vector: MSB < LSB of cell range: " + <lsbConst()<<":"<msbConst()); + } AstNode* exprp = nodep->exprp()->unlinkFrBack(); bool inputPin = nodep->modVarp()->isNonOutput(); if (!inputPin && !VN_IS(exprp, VarRef) && !VN_IS(exprp, Concat) // V3Const will collapse the SEL with the one we're about to make && !VN_IS(exprp, Sel)) { // V3Const will collapse the SEL with the one we're about to make - nodep->v3error("Unsupported: Per-bit array instantiations with output connections to non-wires."); - // Note spec allows more complicated matches such as slices and such - } + nodep->v3error("Unsupported: Per-bit array instantiations with output connections to non-wires."); + // Note spec allows more complicated matches such as slices and such + } exprp = new AstSel(exprp->fileline(), exprp, pinwidth*m_instSelNum, pinwidth); - nodep->exprp(exprp); - } else { - nodep->v3fatalSrc("Width mismatch; V3Width should have errored out."); - } + nodep->exprp(exprp); + } else { + nodep->v3fatalSrc("Width mismatch; V3Width should have errored out."); + } } else if (AstArraySel* arrselp = VN_CAST(nodep->exprp(), ArraySel)) { if (AstUnpackArrayDType* arrp = VN_CAST(arrselp->lhsp()->dtypep(), UnpackArrayDType)) { if (!VN_IS(arrp->subDTypep(), IfaceRefDType)) - return; + return; - V3Const::constifyParamsEdit(arrselp->rhsp()); + V3Const::constifyParamsEdit(arrselp->rhsp()); const AstConst* constp = VN_CAST(arrselp->rhsp(), Const); - if (!constp) { - nodep->v3error("Unsupported: Non-constant index when passing interface to module"); - return; - } - string index = AstNode::encodeNumber(constp->toSInt()); + if (!constp) { + nodep->v3error("Unsupported: Non-constant index when passing interface to module"); + return; + } + string index = AstNode::encodeNumber(constp->toSInt()); AstVarRef* varrefp = VN_CAST(arrselp->lhsp(), VarRef); - AstVarXRef* newp = new AstVarXRef(nodep->fileline(), varrefp->name()+"__BRA__"+index+"__KET__", "", true); - newp->dtypep(nodep->modVarp()->dtypep()); - newp->packagep(varrefp->packagep()); - arrselp->addNextHere(newp); - arrselp->unlinkFrBack()->deleteTree(); - } - } else { - AstVar* pinVarp = nodep->modVarp(); + AstVarXRef* newp = new AstVarXRef(nodep->fileline(), + varrefp->name()+"__BRA__"+index+"__KET__", + "", true); + newp->dtypep(nodep->modVarp()->dtypep()); + newp->packagep(varrefp->packagep()); + arrselp->addNextHere(newp); + arrselp->unlinkFrBack()->deleteTree(); + } + } else { + AstVar* pinVarp = nodep->modVarp(); AstUnpackArrayDType* pinArrp = VN_CAST(pinVarp->dtypep(), UnpackArrayDType); if (!pinArrp || !VN_IS(pinArrp->subDTypep(), IfaceRefDType)) - return; - AstNode* prevp = NULL; - AstNode* prevPinp = NULL; - // Clone the var referenced by the pin, and clone each var referenced by the varref - // Clone pin varp: - for (int i = pinArrp->lsb(); i <= pinArrp->msb(); ++i) { - string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; - AstVar* varNewp = NULL; + return; + AstNode* prevp = NULL; + AstNode* prevPinp = NULL; + // Clone the var referenced by the pin, and clone each var referenced by the varref + // Clone pin varp: + for (int i = pinArrp->lsb(); i <= pinArrp->msb(); ++i) { + string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; + AstVar* varNewp = NULL; - // Only clone the var once for all usages of a given child module - if (!pinVarp->backp()) { - varNewp = m_deModVars.find(varNewName); - } else { + // Only clone the var once for all usages of a given child module + if (!pinVarp->backp()) { + varNewp = m_deModVars.find(varNewName); + } else { AstIfaceRefDType* ifaceRefp = VN_CAST(pinArrp->subDTypep(), IfaceRefDType); - ifaceRefp->cellp(NULL); - varNewp = pinVarp->cloneTree(false); - varNewp->name(varNewName); - varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); - varNewp->dtypep(ifaceRefp); - m_deModVars.insert(varNewp); - if (!prevp) { - prevp = varNewp; - } else { - prevp->addNextHere(varNewp); - } - } - if (!varNewp) { - if (debug()>=9) m_deModVars.dump(); - nodep->v3fatalSrc("Module dearray failed for "<cellp(NULL); + varNewp = pinVarp->cloneTree(false); + varNewp->name(varNewName); + varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); + varNewp->dtypep(ifaceRefp); + m_deModVars.insert(varNewp); + if (!prevp) { + prevp = varNewp; + } else { + prevp->addNextHere(varNewp); + } + } + if (!varNewp) { + if (debug()>=9) m_deModVars.dump(); + nodep->v3fatalSrc("Module dearray failed for " + <cloneTree(false); - newp->modVarp(varNewp); - newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__"); - // And replace exprp with a new varxref + // But clone the pin for each module instance + // Now also clone the pin itself and update its varref + AstPin* newp = nodep->cloneTree(false); + newp->modVarp(varNewp); + newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__"); + // And replace exprp with a new varxref const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef); - string newname = varrefp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; + string newname = varrefp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; AstVarXRef* newVarXRefp = new AstVarXRef(nodep->fileline(), newname, "", true); - newVarXRefp->varp(newp->modVarp()); - newVarXRefp->dtypep(newp->modVarp()->dtypep()); - newp->exprp()->unlinkFrBack()->deleteTree(); - newp->exprp(newVarXRefp); - if (!prevPinp) { - prevPinp = newp; - } else { - prevPinp->addNextHere(newp); - } - } - if (prevp) { - pinVarp->replaceWith(prevp); - pushDeletep(pinVarp); - } // else pinVarp already unlinked when another instance did this step - nodep->replaceWith(prevPinp); - pushDeletep(nodep); - } + newVarXRefp->varp(newp->modVarp()); + newVarXRefp->dtypep(newp->modVarp()->dtypep()); + newp->exprp()->unlinkFrBack()->deleteTree(); + newp->exprp(newVarXRefp); + if (!prevPinp) { + prevPinp = newp; + } else { + prevPinp->addNextHere(newp); + } + } + if (prevp) { + pinVarp->replaceWith(prevp); + pushDeletep(pinVarp); + } // else pinVarp already unlinked when another instance did this step + nodep->replaceWith(prevPinp); + pushDeletep(nodep); + } } // Save some time @@ -439,9 +450,9 @@ private: public: // CONSTUCTORS explicit InstDeVisitor(AstNetlist* nodep) { - m_cellRangep=NULL; - m_instSelNum=0; - // + m_cellRangep = NULL; + m_instSelNum = 0; + // iterate(nodep); } virtual ~InstDeVisitor() {} @@ -453,64 +464,64 @@ public: class InstStatic { private: VL_DEBUG_FUNC; // Declare debug() - InstStatic() {} // Static class + InstStatic() {} // Static class static AstNode* extendOrSel(FileLine* fl, AstNode* rhsp, AstNode* cmpWidthp) { - if (cmpWidthp->width() > rhsp->width()) { - rhsp = (rhsp->isSigned() - ? static_cast(new AstExtendS(fl, rhsp)) - : static_cast(new AstExtend (fl, rhsp))); - rhsp->dtypeFrom(cmpWidthp); // Need proper widthMin, which may differ from AstSel created above - } else if (cmpWidthp->width() < rhsp->width()) { + if (cmpWidthp->width() > rhsp->width()) { + rhsp = (rhsp->isSigned() + ? static_cast(new AstExtendS(fl, rhsp)) + : static_cast(new AstExtend (fl, rhsp))); + rhsp->dtypeFrom(cmpWidthp); // Need proper widthMin, which may differ from AstSel created above + } else if (cmpWidthp->width() < rhsp->width()) { rhsp = new AstSel(fl, rhsp, 0, cmpWidthp->width()); - rhsp->dtypeFrom(cmpWidthp); // Need proper widthMin, which may differ from AstSel created above - } - // else don't change dtype, as might be e.g. array of something - return rhsp; + rhsp->dtypeFrom(cmpWidthp); // Need proper widthMin, which may differ from AstSel created above + } + // else don't change dtype, as might be e.g. array of something + return rhsp; } public: static AstAssignW* pinReconnectSimple(AstPin* pinp, AstCell* cellp, - bool forTristate, bool alwaysCvt) { - // If a pin connection is "simple" leave it as-is - // Else create a intermediate wire to perform the interconnect - // Return the new assignment, if one was made - // Note this module calles cloneTree() via new AstVar + bool forTristate, bool alwaysCvt) { + // If a pin connection is "simple" leave it as-is + // Else create a intermediate wire to perform the interconnect + // Return the new assignment, if one was made + // Note this module calles cloneTree() via new AstVar - AstVar* pinVarp = pinp->modVarp(); + AstVar* pinVarp = pinp->modVarp(); AstVarRef* connectRefp = VN_CAST(pinp->exprp(), VarRef); AstVarXRef* connectXRefp = VN_CAST(pinp->exprp(), VarXRef); AstBasicDType* pinBasicp = VN_CAST(pinVarp->dtypep(), BasicDType); // Maybe NULL - AstBasicDType* connBasicp = NULL; - AstAssignW* assignp = NULL; + AstBasicDType* connBasicp = NULL; + AstAssignW* assignp = NULL; if (connectRefp) connBasicp = VN_CAST(connectRefp->varp()->dtypep(), BasicDType); - // - if (!alwaysCvt - && connectRefp - && connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep()) - && !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types - // Done. Same data type - } else if (!alwaysCvt - && connectRefp - && connectRefp->varp()->isIfaceRef()) { - // Done. Interface - } else if (!alwaysCvt - && connectXRefp - && connectXRefp->varp() - && connectXRefp->varp()->isIfaceRef()) { - } else if (!alwaysCvt - && connBasicp - && pinBasicp - && connBasicp->width() == pinBasicp->width() - && connBasicp->lsb() == pinBasicp->lsb() - && !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types - && connBasicp->width() == pinVarp->width()) { - // Done. One to one interconnect won't need a temporary variable. + // + if (!alwaysCvt + && connectRefp + && connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep()) + && !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types + // Done. Same data type + } else if (!alwaysCvt + && connectRefp + && connectRefp->varp()->isIfaceRef()) { + // Done. Interface + } else if (!alwaysCvt + && connectXRefp + && connectXRefp->varp() + && connectXRefp->varp()->isIfaceRef()) { + } else if (!alwaysCvt + && connBasicp + && pinBasicp + && connBasicp->width() == pinBasicp->width() + && connBasicp->lsb() == pinBasicp->lsb() + && !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types + && connBasicp->width() == pinVarp->width()) { + // Done. One to one interconnect won't need a temporary variable. } else if (!alwaysCvt && !forTristate && VN_IS(pinp->exprp(), Const)) { - // Done. Constant. - } else { - // Make a new temp wire - //if (1||debug()>=9) { pinp->dumpTree(cout,"-in_pin:"); } + // Done. Constant. + } else { + // Make a new temp wire + //if (1||debug()>=9) { pinp->dumpTree(cout, "-in_pin:"); } V3Inst::checkOutputShort(pinp); AstNode* pinexprp = pinp->exprp()->unlinkFrBack(); string newvarname = (string(pinVarp->isWritable() ? "__Vcellout" : "__Vcellinp") @@ -540,12 +551,12 @@ public: new AstVarRef(pinp->fileline(), newvarp, true), pinexprp); pinp->exprp(new AstVarRef(pinexprp->fileline(), newvarp, false)); - } - if (assignp) cellp->addNextHere(assignp); - //if (debug()) { pinp->dumpTree(cout,"- out:"); } - //if (debug()) { assignp->dumpTree(cout,"- aout:"); } - } - return assignp; + } + if (assignp) cellp->addNextHere(assignp); + //if (debug()) { pinp->dumpTree(cout, "- out:"); } + //if (debug()) { assignp->dumpTree(cout, "- aout:"); } + } + return assignp; } }; diff --git a/src/V3Inst.h b/src/V3Inst.h index 0ee6e3528..77788b526 100644 --- a/src/V3Inst.h +++ b/src/V3Inst.h @@ -38,4 +38,4 @@ public: static void checkOutputShort(AstPin* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LangCode.h b/src/V3LangCode.h index ab4ffea2e..ce206720b 100644 --- a/src/V3LangCode.h +++ b/src/V3LangCode.h @@ -35,34 +35,34 @@ class V3LangCode { public: enum en { - L_ERROR, // Must be first. - L1364_1995, - L1364_2001, - L1364_2005, - L1800_2005, - L1800_2009, - L1800_2012, - L1800_2017, - // ***Add new elements below also*** - _ENUM_END + L_ERROR, // Must be first. + L1364_1995, + L1364_2001, + L1364_2005, + L1800_2005, + L1800_2009, + L1800_2012, + L1800_2017, + // ***Add new elements below also*** + _ENUM_END }; const char* ascii() const { - const char* const names[] = { - // These must match the `begin_keywords values. - " ERROR", - "1364-1995", - "1364-2001", - "1364-2005", - "1800-2005", - "1800-2009", - "1800-2012", - "1800-2017" - }; - return names[m_e]; + const char* const names[] = { + // These must match the `begin_keywords values. + " ERROR", + "1364-1995", + "1364-2001", + "1364-2005", + "1800-2005", + "1800-2009", + "1800-2012", + "1800-2017" + }; + return names[m_e]; }; static V3LangCode mostRecent() { return V3LangCode(L1800_2017); } bool systemVerilog() const { return m_e == L1800_2005 || m_e == L1800_2009 - || m_e == L1800_2012 || m_e == L1800_2017; } + || m_e == L1800_2012 || m_e == L1800_2017; } bool legal() const { return m_e != L_ERROR; } // enum en m_e; @@ -76,4 +76,4 @@ public: //###################################################################### -#endif // guard +#endif // guard diff --git a/src/V3LanguageWords.h b/src/V3LanguageWords.h index b6b5fcf8b..c486dae8c 100644 --- a/src/V3LanguageWords.h +++ b/src/V3LanguageWords.h @@ -36,153 +36,153 @@ class V3LanguageWords { KeywordMap m_kwdMap; // List of keywords, and what language applies void addKwd(const string& kwd, const string& why) { - m_kwdMap.insert(make_pair(kwd,why)); + m_kwdMap.insert(make_pair(kwd, why)); } public: string isKeyword(const string& kwd) { KeywordMap::iterator it = m_kwdMap.find(kwd); - if (it == m_kwdMap.end()) return ""; - return it->second; + if (it == m_kwdMap.end()) return ""; + return it->second; } public: V3LanguageWords() { - // C++ keywords - // C++ - addKwd("NULL", "C++ common word"); - addKwd("abort", "C++ common word"); - addKwd("alignas", "C++11 keyword"); - addKwd("alignof", "C++11 keyword"); - addKwd("and", "C++11 keyword"); - addKwd("and_eq", "C++11 keyword"); - addKwd("asm", "C++ common word"); - addKwd("atomic_cancel", "C++ TM TS keyword"); - addKwd("atomic_commit", "C++ TM TS keyword"); - addKwd("atomic_noexcept", "C++ TM TS keyword"); - addKwd("auto", "C++ keyword"); - addKwd("bit_vector", "C++ common word"); - addKwd("bitand", "C++ keyword"); - addKwd("bitor", "C++ keyword"); - addKwd("bool", "C++ keyword"); - addKwd("break", "C++ keyword"); - addKwd("case", "C++ keyword"); - addKwd("catch", "C++ keyword"); - addKwd("cdecl", "C++ common word"); - addKwd("char", "C++ keyword"); - addKwd("char16_t", "C++11 keyword"); - addKwd("char32_t", "C++11 keyword"); - addKwd("class", "C++11 keyword"); - addKwd("compl", "C++11 keyword"); - addKwd("complex", "C++ common word"); - addKwd("concept", "C++20 keyword"); - addKwd("const", "C++ keyword"); - addKwd("const_cast", "C++ common word"); - addKwd("const_iterator", "C++ common word"); - addKwd("const_reference ", "C++ common word"); - addKwd("constexpr", "C++11 keyword"); - addKwd("continue", "C++ keyword"); - addKwd("decltype", "C++11 keyword"); - addKwd("default", "C++ keyword"); - addKwd("delete", "C++ keyword"); - addKwd("deque", "C++ common word"); - addKwd("do", "C++ keyword"); - addKwd("double", "C++ keyword"); - addKwd("dynamic_cast", "C++ keyword"); - addKwd("else", "C++ keyword"); - addKwd("enum", "C++ keyword"); - addKwd("explicit", "C++ keyword"); - addKwd("export", "C++ keyword"); - addKwd("extern", "C++ keyword"); - addKwd("false", "C++ keyword"); - addKwd("far", "C++ common word"); - addKwd("float", "C++ keyword"); - addKwd("for", "C++ keyword"); - addKwd("friend", "C++ keyword"); - addKwd("goto", "C++ keyword"); - addKwd("huge", "C++ keyword"); - addKwd("if", "C++ keyword"); - addKwd("import", "C++ modules TS keyword"); - addKwd("inline", "C++ keyword"); - addKwd("int", "C++ keyword"); - addKwd("interrupt", "C++ common word"); - addKwd("iterator", "C++ common word"); - addKwd("list", "C++ common word"); - addKwd("long", "C++ keyword"); - addKwd("map", "C++ common word"); - addKwd("module", "C++ modules TS keyword"); + // C++ keywords + // C++ + addKwd("NULL", "C++ common word"); + addKwd("abort", "C++ common word"); + addKwd("alignas", "C++11 keyword"); + addKwd("alignof", "C++11 keyword"); + addKwd("and", "C++11 keyword"); + addKwd("and_eq", "C++11 keyword"); + addKwd("asm", "C++ common word"); + addKwd("atomic_cancel", "C++ TM TS keyword"); + addKwd("atomic_commit", "C++ TM TS keyword"); + addKwd("atomic_noexcept", "C++ TM TS keyword"); + addKwd("auto", "C++ keyword"); + addKwd("bit_vector", "C++ common word"); + addKwd("bitand", "C++ keyword"); + addKwd("bitor", "C++ keyword"); + addKwd("bool", "C++ keyword"); + addKwd("break", "C++ keyword"); + addKwd("case", "C++ keyword"); + addKwd("catch", "C++ keyword"); + addKwd("cdecl", "C++ common word"); + addKwd("char", "C++ keyword"); + addKwd("char16_t", "C++11 keyword"); + addKwd("char32_t", "C++11 keyword"); + addKwd("class", "C++11 keyword"); + addKwd("compl", "C++11 keyword"); + addKwd("complex", "C++ common word"); + addKwd("concept", "C++20 keyword"); + addKwd("const", "C++ keyword"); + addKwd("const_cast", "C++ common word"); + addKwd("const_iterator", "C++ common word"); + addKwd("const_reference ", "C++ common word"); + addKwd("constexpr", "C++11 keyword"); + addKwd("continue", "C++ keyword"); + addKwd("decltype", "C++11 keyword"); + addKwd("default", "C++ keyword"); + addKwd("delete", "C++ keyword"); + addKwd("deque", "C++ common word"); + addKwd("do", "C++ keyword"); + addKwd("double", "C++ keyword"); + addKwd("dynamic_cast", "C++ keyword"); + addKwd("else", "C++ keyword"); + addKwd("enum", "C++ keyword"); + addKwd("explicit", "C++ keyword"); + addKwd("export", "C++ keyword"); + addKwd("extern", "C++ keyword"); + addKwd("false", "C++ keyword"); + addKwd("far", "C++ common word"); + addKwd("float", "C++ keyword"); + addKwd("for", "C++ keyword"); + addKwd("friend", "C++ keyword"); + addKwd("goto", "C++ keyword"); + addKwd("huge", "C++ keyword"); + addKwd("if", "C++ keyword"); + addKwd("import", "C++ modules TS keyword"); + addKwd("inline", "C++ keyword"); + addKwd("int", "C++ keyword"); + addKwd("interrupt", "C++ common word"); + addKwd("iterator", "C++ common word"); + addKwd("list", "C++ common word"); + addKwd("long", "C++ keyword"); + addKwd("map", "C++ common word"); + addKwd("module", "C++ modules TS keyword"); addKwd("std::multimap", "C++ common word"); addKwd("std::multiset", "C++ common word"); - addKwd("mutable", "C++ keyword"); - addKwd("namespace", "C++ keyword"); - addKwd("near", "C++ common word"); - addKwd("new", "C++ keyword"); - addKwd("noexcept", "C++11 keyword"); - addKwd("not", "C++ keyword"); - addKwd("not_eq", "C++ keyword"); - addKwd("nullptr", "C++11 keyword"); - addKwd("operator", "C++ keyword"); - addKwd("or", "C++ keyword"); - addKwd("or_eq", "C++ keyword"); - addKwd("override", "C++ common word"); - addKwd("pascal", "C++ keyword"); - addKwd("private", "C++ keyword"); - addKwd("protected", "C++ keyword"); - addKwd("public", "C++ keyword"); - addKwd("queue", "C++ common word"); - addKwd("reference", "C++ common word"); - addKwd("register", "C++ keyword"); - addKwd("reinterpret_cast ", "C++ keyword"); - addKwd("requires", "C++20 keyword"); - addKwd("restrict", "C++ keyword"); - addKwd("return", "C++ keyword"); - addKwd("set", "C++ common word"); - addKwd("short", "C++ keyword"); - addKwd("signed", "C++ keyword"); - addKwd("sizeof", "C++ keyword"); - addKwd("stack", "C++ common word"); - addKwd("static", "C++ keyword"); - addKwd("static_assert", "C++11 keyword"); - addKwd("static_cast", "C++ keyword"); - addKwd("struct", "C++ keyword"); - addKwd("switch", "C++ keyword"); - addKwd("synchronized", "C++ TM TS keyword"); - addKwd("template", "C++ keyword"); - addKwd("this", "C++ keyword"); - addKwd("thread_local", "C++11 keyword"); - addKwd("throw", "C++ keyword"); - addKwd("transaction_safe", "C++ common word"); - addKwd("transaction_safe_dynamic", "C++ common word"); - addKwd("true", "C++ keyword"); - addKwd("try", "C++ keyword"); - addKwd("type_info", "C++ common word"); - addKwd("typedef", "C++ keyword"); - addKwd("typeid", "C++ keyword"); - addKwd("typename", "C++ keyword"); - addKwd("uint16_t", "C++ common word"); - addKwd("uint32_t", "C++ common word"); - addKwd("uint8_t", "C++ common word"); - addKwd("union", "C++ keyword"); - addKwd("unsigned", "C++ keyword"); - addKwd("using", "C++ keyword"); - addKwd("vector", "C++ common word"); - addKwd("virtual", "C++ keyword"); - addKwd("void", "C++ keyword"); - addKwd("volatile", "C++ keyword"); - addKwd("wchar_t", "C++ keyword"); - addKwd("while", "C++ keyword"); - addKwd("xor", "C++ keyword"); - addKwd("xor_eq", "C++ keyword"); - // This conflicts with header functions, so is ignored - //dKwd("final", "C++11 keyword"); // Member function or class head, otherwise not reserved - // SystemC - addKwd("sc_clock", "SystemC common word"); - addKwd("sc_in", "SystemC common word"); - addKwd("sc_inout", "SystemC common word"); - addKwd("sc_out", "SystemC common word"); - addKwd("sc_signal", "SystemC common word"); - addKwd("sensitive", "SystemC common word"); - addKwd("sensitive_neg", "SystemC common word"); - addKwd("sensitive_pos", "SystemC common word"); + addKwd("mutable", "C++ keyword"); + addKwd("namespace", "C++ keyword"); + addKwd("near", "C++ common word"); + addKwd("new", "C++ keyword"); + addKwd("noexcept", "C++11 keyword"); + addKwd("not", "C++ keyword"); + addKwd("not_eq", "C++ keyword"); + addKwd("nullptr", "C++11 keyword"); + addKwd("operator", "C++ keyword"); + addKwd("or", "C++ keyword"); + addKwd("or_eq", "C++ keyword"); + addKwd("override", "C++ common word"); + addKwd("pascal", "C++ keyword"); + addKwd("private", "C++ keyword"); + addKwd("protected", "C++ keyword"); + addKwd("public", "C++ keyword"); + addKwd("queue", "C++ common word"); + addKwd("reference", "C++ common word"); + addKwd("register", "C++ keyword"); + addKwd("reinterpret_cast ", "C++ keyword"); + addKwd("requires", "C++20 keyword"); + addKwd("restrict", "C++ keyword"); + addKwd("return", "C++ keyword"); + addKwd("set", "C++ common word"); + addKwd("short", "C++ keyword"); + addKwd("signed", "C++ keyword"); + addKwd("sizeof", "C++ keyword"); + addKwd("stack", "C++ common word"); + addKwd("static", "C++ keyword"); + addKwd("static_assert", "C++11 keyword"); + addKwd("static_cast", "C++ keyword"); + addKwd("struct", "C++ keyword"); + addKwd("switch", "C++ keyword"); + addKwd("synchronized", "C++ TM TS keyword"); + addKwd("template", "C++ keyword"); + addKwd("this", "C++ keyword"); + addKwd("thread_local", "C++11 keyword"); + addKwd("throw", "C++ keyword"); + addKwd("transaction_safe", "C++ common word"); + addKwd("transaction_safe_dynamic", "C++ common word"); + addKwd("true", "C++ keyword"); + addKwd("try", "C++ keyword"); + addKwd("type_info", "C++ common word"); + addKwd("typedef", "C++ keyword"); + addKwd("typeid", "C++ keyword"); + addKwd("typename", "C++ keyword"); + addKwd("uint16_t", "C++ common word"); + addKwd("uint32_t", "C++ common word"); + addKwd("uint8_t", "C++ common word"); + addKwd("union", "C++ keyword"); + addKwd("unsigned", "C++ keyword"); + addKwd("using", "C++ keyword"); + addKwd("vector", "C++ common word"); + addKwd("virtual", "C++ keyword"); + addKwd("void", "C++ keyword"); + addKwd("volatile", "C++ keyword"); + addKwd("wchar_t", "C++ keyword"); + addKwd("while", "C++ keyword"); + addKwd("xor", "C++ keyword"); + addKwd("xor_eq", "C++ keyword"); + // This conflicts with header functions, so is ignored + //dKwd("final", "C++11 keyword"); + // SystemC + addKwd("sc_clock", "SystemC common word"); + addKwd("sc_in", "SystemC common word"); + addKwd("sc_inout", "SystemC common word"); + addKwd("sc_out", "SystemC common word"); + addKwd("sc_signal", "SystemC common word"); + addKwd("sensitive", "SystemC common word"); + addKwd("sensitive_neg", "SystemC common word"); + addKwd("sensitive_pos", "SystemC common word"); } }; diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 64a1d3179..5b0c45921 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -18,12 +18,12 @@ // //************************************************************************* // LIFE TRANSFORMATIONS: -// Build control-flow graph with assignments and var usages -// All modules: -// ASSIGN(x,...), ASSIGN(x,...) => delete first one -// We also track across if statements: -// ASSIGN(X,...) IF( ..., ASSIGN(X,...), ASSIGN(X,...)) => deletes first -// We don't do the opposite yet though (remove assigns in if followed by outside if) +// Build control-flow graph with assignments and var usages +// All modules: +// ASSIGN(x,...), ASSIGN(x,...) => delete first one +// We also track across if statements: +// ASSIGN(X,...) IF( ..., ASSIGN(X,...), ASSIGN(X,...)) => deletes first +// We don't do the opposite yet though (remove assigns in if followed by outside if) // //************************************************************************* @@ -46,24 +46,25 @@ class LifeState { // NODE STATE // See below - AstUser1InUse m_inuser1; + AstUser1InUse m_inuser1; // STATE public: - V3Double0 m_statAssnDel; // Statistic tracking - V3Double0 m_statAssnCon; // Statistic tracking + V3Double0 m_statAssnDel; // Statistic tracking + V3Double0 m_statAssnCon; // Statistic tracking std::vector m_unlinkps; public: // CONSTRUCTORS LifeState() {} ~LifeState() { - V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel); - V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon); - for (std::vector::iterator it = m_unlinkps.begin(); it != m_unlinkps.end(); ++it) { - (*it)->unlinkFrBack(); - (*it)->deleteTree(); - } + V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel); + V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon); + for (std::vector::iterator it = m_unlinkps.begin(); + it != m_unlinkps.end(); ++it) { + (*it)->unlinkFrBack(); + (*it)->deleteTree(); + } } // METHODS void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); } @@ -73,16 +74,16 @@ public: // Structure for each variable encountered class LifeVarEntry { - AstNodeAssign* m_assignp; // Last assignment to this varscope, NULL if no longer relevant - AstConst* m_constp; // Known constant value - bool m_setBeforeUse; // First access was a set (and thus block above may have a set that can be deleted - bool m_everSet; // Was ever assigned (and thus above block may not preserve constant propagation) + AstNodeAssign* m_assignp; // Last assignment to this varscope, NULL if no longer relevant + AstConst* m_constp; // Known constant value + bool m_setBeforeUse; // First access was a set (and thus block above may have a set that can be deleted + bool m_everSet; // Was ever assigned (and thus above block may not preserve constant propagation) inline void init(bool setBeforeUse) { - m_assignp = NULL; - m_constp = NULL; - m_setBeforeUse = setBeforeUse; - m_everSet = false; + m_assignp = NULL; + m_constp = NULL; + m_setBeforeUse = setBeforeUse; + m_everSet = false; } public: class SIMPLEASSIGN {}; @@ -90,28 +91,28 @@ public: class CONSUMED {}; LifeVarEntry(SIMPLEASSIGN, AstNodeAssign* assp) { - init(true); simpleAssign(assp); + init(true); simpleAssign(assp); } explicit LifeVarEntry(COMPLEXASSIGN) { - init(false); complexAssign(); + init(false); complexAssign(); } explicit LifeVarEntry(CONSUMED) { - init(false); consumed(); + init(false); consumed(); } ~LifeVarEntry() {} - inline void simpleAssign(AstNodeAssign* assp) { // New simple A=.... assignment - m_assignp = assp; - m_constp = NULL; - m_everSet = true; + inline void simpleAssign(AstNodeAssign* assp) { // New simple A=.... assignment + m_assignp = assp; + m_constp = NULL; + m_everSet = true; if (VN_IS(assp->rhsp(), Const)) m_constp = VN_CAST(assp->rhsp(), Const); } inline void complexAssign() { // A[x]=... or some complicated assignment - m_assignp = NULL; - m_constp = NULL; - m_everSet = true; + m_assignp = NULL; + m_constp = NULL; + m_everSet = true; } inline void consumed() { // Rvalue read of A - m_assignp = NULL; + m_assignp = NULL; } AstNodeAssign* assignp() const { return m_assignp; } AstConst* constNodep() const { return m_constp; } @@ -125,152 +126,152 @@ public: class LifeBlock { // NODE STATE // Cleared each AstIf: - // AstVarScope::user1() -> int. Used in combining to detect duplicates + // AstVarScope::user1() -> int. Used in combining to detect duplicates // LIFE MAP // For each basic block, we'll make a new map of what variables that if/else is changing typedef std::map LifeMap; - LifeMap m_map; // Current active lifetime map for current scope - LifeBlock* m_aboveLifep; // Upper life, or NULL - LifeState* m_statep; // Current global state + LifeMap m_map; // Current active lifetime map for current scope + LifeBlock* m_aboveLifep; // Upper life, or NULL + LifeState* m_statep; // Current global state VL_DEBUG_FUNC; // Declare debug() public: LifeBlock(LifeBlock* aboveLifep, LifeState* statep) { - m_aboveLifep = aboveLifep; // Null if top - m_statep = statep; + m_aboveLifep = aboveLifep; // Null if top + m_statep = statep; } ~LifeBlock() {} // METHODS void checkRemoveAssign(const LifeMap::iterator& it) { - AstVar* varp = it->first->varp(); - LifeVarEntry* entp = &(it->second); - if (!varp->isSigPublic()) { - // Rather than track what sigs AstUCFunc/AstUCStmt may change, - // we just don't optimize any public sigs - // Check the var entry, and remove if appropriate - if (AstNode* oldassp = entp->assignp()) { - UINFO(7," PREV: "<4) oldassp->dumpTree(cout, " REMOVE/SAMEBLK "); - entp->complexAssign(); - m_statep->pushUnlinkDeletep(oldassp); VL_DANGLING(oldassp); - ++m_statep->m_statAssnDel; - } - } + AstVar* varp = it->first->varp(); + LifeVarEntry* entp = &(it->second); + if (!varp->isSigPublic()) { + // Rather than track what sigs AstUCFunc/AstUCStmt may change, + // we just don't optimize any public sigs + // Check the var entry, and remove if appropriate + if (AstNode* oldassp = entp->assignp()) { + UINFO(7," PREV: "<4) oldassp->dumpTree(cout, " REMOVE/SAMEBLK "); + entp->complexAssign(); + m_statep->pushUnlinkDeletep(oldassp); VL_DANGLING(oldassp); + ++m_statep->m_statAssnDel; + } + } } void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) { - // Do we have a old assignment we can nuke? - UINFO(4," ASSIGNof: "<second.simpleAssign(assp); - } else { - m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::SIMPLEASSIGN(), assp))); - } - //lifeDump(); + // Do we have a old assignment we can nuke? + UINFO(4," ASSIGNof: "<second.simpleAssign(assp); + } else { + m_map.insert(make_pair(nodep, LifeVarEntry(LifeVarEntry::SIMPLEASSIGN(), assp))); + } + //lifeDump(); } void complexAssign(AstVarScope* nodep) { - UINFO(4," clearof: "<second.complexAssign(); - } else { - m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::COMPLEXASSIGN()))); - } + UINFO(4," clearof: "<second.complexAssign(); + } else { + m_map.insert(make_pair(nodep, LifeVarEntry(LifeVarEntry::COMPLEXASSIGN()))); + } } void varUsageReplace(AstVarScope* nodep, AstVarRef* varrefp) { - // Variable rvalue. If it references a constant, we can simply replace it - LifeMap::iterator it = m_map.find(nodep); - if (it != m_map.end()) { - if (AstConst* constp = it->second.constNodep()) { - if (!varrefp->varp()->isSigPublic()) { - // Aha, variable is constant; substitute in. - // We'll later constant propagate - UINFO(4," replaceconst: "<replaceWith(constp->cloneTree(false)); - varrefp->deleteTree(); VL_DANGLING(varrefp); - ++m_statep->m_statAssnCon; - return; // **DONE, no longer a var reference** - } - } - UINFO(4," usage: "<second.consumed(); - } else { - m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::CONSUMED()))); - } + // Variable rvalue. If it references a constant, we can simply replace it + LifeMap::iterator it = m_map.find(nodep); + if (it != m_map.end()) { + if (AstConst* constp = it->second.constNodep()) { + if (!varrefp->varp()->isSigPublic()) { + // Aha, variable is constant; substitute in. + // We'll later constant propagate + UINFO(4," replaceconst: "<replaceWith(constp->cloneTree(false)); + varrefp->deleteTree(); VL_DANGLING(varrefp); + ++m_statep->m_statAssnCon; + return; // **DONE, no longer a var reference** + } + } + UINFO(4," usage: "<second.consumed(); + } else { + m_map.insert(make_pair(nodep, LifeVarEntry(LifeVarEntry::CONSUMED()))); + } } void complexAssignFind(AstVarScope* nodep) { - LifeMap::iterator it = m_map.find(nodep); - if (it != m_map.end()) { - UINFO(4," casfind: "<first<second.complexAssign(); - } else { - m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::COMPLEXASSIGN()))); - } + LifeMap::iterator it = m_map.find(nodep); + if (it != m_map.end()) { + UINFO(4," casfind: "<first<second.complexAssign(); + } else { + m_map.insert(make_pair(nodep, LifeVarEntry(LifeVarEntry::COMPLEXASSIGN()))); + } } void consumedFind(AstVarScope* nodep) { - LifeMap::iterator it = m_map.find(nodep); - if (it != m_map.end()) { - it->second.consumed(); - } else { - m_map.insert(make_pair(nodep,LifeVarEntry(LifeVarEntry::CONSUMED()))); - } + LifeMap::iterator it = m_map.find(nodep); + if (it != m_map.end()) { + it->second.consumed(); + } else { + m_map.insert(make_pair(nodep, LifeVarEntry(LifeVarEntry::CONSUMED()))); + } } void lifeToAbove() { - // Any varrefs under a if/else branch affect statements outside and after the if/else - if (!m_aboveLifep) v3fatalSrc("Pushing life when already at the top level"); - for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) { - AstVarScope* nodep = it->first; - m_aboveLifep->complexAssignFind(nodep); - if (it->second.everSet()) { - // Record there may be an assignment, so we don't constant propagate across the if. - complexAssignFind(nodep); - } else { - // Record consumption, so we don't eliminate earlier assignments - consumedFind(nodep); - } - } + // Any varrefs under a if/else branch affect statements outside and after the if/else + if (!m_aboveLifep) v3fatalSrc("Pushing life when already at the top level"); + for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) { + AstVarScope* nodep = it->first; + m_aboveLifep->complexAssignFind(nodep); + if (it->second.everSet()) { + // Record there may be an assignment, so we don't constant propagate across the if. + complexAssignFind(nodep); + } else { + // Record consumption, so we don't eliminate earlier assignments + consumedFind(nodep); + } + } } void dualBranch(LifeBlock* life1p, LifeBlock* life2p) { - // Find any common sets on both branches of IF and propagate upwards - //life1p->lifeDump(); - //life2p->lifeDump(); - AstNode::user1ClearTree(); // user1p() used on entire tree - for (LifeMap::iterator it = life1p->m_map.begin(); it!=life1p->m_map.end(); ++it) { - // When the if branch sets a var before it's used, mark that variable - if (it->second.setBeforeUse()) it->first->user1(1); - } - for (LifeMap::iterator it = life2p->m_map.begin(); it!=life2p->m_map.end(); ++it) { - // When the else branch sets a var before it's used - AstVarScope* nodep = it->first; - if (it->second.setBeforeUse() && nodep->user1()) { - // Both branches set the var, we can remove the assignment before the IF. - UINFO(4,"DUALBRANCH "<lifeDump(); + // Find any common sets on both branches of IF and propagate upwards + //life1p->lifeDump(); + //life2p->lifeDump(); + AstNode::user1ClearTree(); // user1p() used on entire tree + for (LifeMap::iterator it = life1p->m_map.begin(); it!=life1p->m_map.end(); ++it) { + // When the if branch sets a var before it's used, mark that variable + if (it->second.setBeforeUse()) it->first->user1(1); + } + for (LifeMap::iterator it = life2p->m_map.begin(); it!=life2p->m_map.end(); ++it) { + // When the else branch sets a var before it's used + AstVarScope* nodep = it->first; + if (it->second.setBeforeUse() && nodep->user1()) { + // Both branches set the var, we can remove the assignment before the IF. + UINFO(4,"DUALBRANCH "<lifeDump(); } // DEBUG void lifeDump() { - UINFO(5, " LifeMap:"<second.setBeforeUse()?"[F] ":" ") - <first<second.assignp()) { - UINFO(5, " Ass: "<second.assignp()<second.setBeforeUse()?"[F] ":" ") + <first<second.assignp()) { + UINFO(5, " Ass: "<second.assignp()< LifeMap; // cppcheck-suppress memleak // cppcheck bug - it is deleted - LifeBlock* m_lifep; // Current active lifetime map for current scope + LifeBlock* m_lifep; // Current active lifetime map for current scope // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstVarRef* nodep) { - // Consumption/generation of a variable, - // it's used so can't elim assignment before this use. - if (!nodep->varScopep()) nodep->v3fatalSrc("NULL"); - // - AstVarScope* vscp = nodep->varScopep(); - if (!vscp) nodep->v3fatalSrc("Scope not assigned"); - if (nodep->lvalue()) { - m_lifep->complexAssign(vscp); - } else { - m_lifep->varUsageReplace(vscp, nodep); VL_DANGLING(nodep); - } + // Consumption/generation of a variable, + // it's used so can't elim assignment before this use. + if (!nodep->varScopep()) nodep->v3fatalSrc("NULL"); + // + AstVarScope* vscp = nodep->varScopep(); + if (!vscp) nodep->v3fatalSrc("Scope not assigned"); + if (nodep->lvalue()) { + m_lifep->complexAssign(vscp); + } else { + m_lifep->varUsageReplace(vscp, nodep); VL_DANGLING(nodep); + } } virtual void visit(AstNodeAssign* nodep) { - // Collect any used variables first, as lhs may also be on rhs - // Similar code in V3Dead - vluint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited - m_sideEffect = false; + // Collect any used variables first, as lhs may also be on rhs + // Similar code in V3Dead + vluint64_t lastEdit = AstNode::editCountGbl(); // When it was last edited + m_sideEffect = false; iterateAndNextNull(nodep->rhsp()); - if (lastEdit != AstNode::editCountGbl()) { - // We changed something, try to constant propagate, but don't delete the - // assignment as we still need nodep to remain. - V3Const::constifyEdit(nodep->rhsp()); // rhsp may change - } - // Has to be direct assignment without any EXTRACTing. + if (lastEdit != AstNode::editCountGbl()) { + // We changed something, try to constant propagate, but don't delete the + // assignment as we still need nodep to remain. + V3Const::constifyEdit(nodep->rhsp()); // rhsp may change + } + // Has to be direct assignment without any EXTRACTing. if (VN_IS(nodep->lhsp(), VarRef) && !m_sideEffect && !m_noopt) { AstVarScope* vscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep(); - if (!vscp) nodep->v3fatalSrc("Scope lost on variable"); - m_lifep->simpleAssign(vscp, nodep); - } else { + if (!vscp) nodep->v3fatalSrc("Scope lost on variable"); + m_lifep->simpleAssign(vscp, nodep); + } else { iterateAndNextNull(nodep->lhsp()); - } + } } virtual void visit(AstAssignDly* nodep) { - // Don't treat as normal assign; V3Life doesn't understand time sense + // Don't treat as normal assign; V3Life doesn't understand time sense iterateChildren(nodep); } //---- Track control flow changes virtual void visit(AstNodeIf* nodep) { - UINFO(4," IF "<condp()); - LifeBlock* prevLifep = m_lifep; + LifeBlock* prevLifep = m_lifep; LifeBlock* ifLifep = new LifeBlock(prevLifep, m_statep); LifeBlock* elseLifep = new LifeBlock(prevLifep, m_statep); - { - m_lifep = ifLifep; + { + m_lifep = ifLifep; iterateAndNextNull(nodep->ifsp()); - m_lifep = prevLifep; - } - { - m_lifep = elseLifep; + m_lifep = prevLifep; + } + { + m_lifep = elseLifep; iterateAndNextNull(nodep->elsesp()); - m_lifep = prevLifep; - } - UINFO(4," join "<dualBranch(ifLifep, elseLifep); - // For the next assignments, clear any variables that were read or written in the block - ifLifep->lifeToAbove(); - elseLifep->lifeToAbove(); - delete ifLifep; - delete elseLifep; + // For the next assignments, clear any variables that were read or written in the block + ifLifep->lifeToAbove(); + elseLifep->lifeToAbove(); + delete ifLifep; + delete elseLifep; } virtual void visit(AstWhile* nodep) { - // While's are a problem, as we don't allow loops in the graph. We - // may go around the cond/body multiple times. Thus a - // lifelication just in the body is ok, but we can't delete an - // assignment in the body that's used in the cond. (And otherwise - // would because it only appears used after-the-fact. So, we model - // it as a IF statement, and just don't allow elimination of - // variables across the body. - LifeBlock* prevLifep = m_lifep; + // While's are a problem, as we don't allow loops in the graph. We + // may go around the cond/body multiple times. Thus a + // lifelication just in the body is ok, but we can't delete an + // assignment in the body that's used in the cond. (And otherwise + // would because it only appears used after-the-fact. So, we model + // it as a IF statement, and just don't allow elimination of + // variables across the body. + LifeBlock* prevLifep = m_lifep; LifeBlock* condLifep = new LifeBlock(prevLifep, m_statep); LifeBlock* bodyLifep = new LifeBlock(prevLifep, m_statep); - { - m_lifep = condLifep; + { + m_lifep = condLifep; iterateAndNextNull(nodep->precondsp()); iterateAndNextNull(nodep->condp()); - m_lifep = prevLifep; - } - { - m_lifep = bodyLifep; + m_lifep = prevLifep; + } + { + m_lifep = bodyLifep; iterateAndNextNull(nodep->bodysp()); iterateAndNextNull(nodep->incsp()); - m_lifep = prevLifep; - } - UINFO(4," joinfor"<lifeToAbove(); - bodyLifep->lifeToAbove(); - delete condLifep; - delete bodyLifep; + m_lifep = prevLifep; + } + UINFO(4," joinfor"<lifeToAbove(); + bodyLifep->lifeToAbove(); + delete condLifep; + delete bodyLifep; } virtual void visit(AstJumpLabel* nodep) { - // As with While's we can't predict if a JumpGo will kill us or not - // It's worse though as an IF(..., JUMPGO) may change the control flow. - // Just don't optimize blocks with labels; they're rare - so far. - LifeBlock* prevLifep = m_lifep; + // As with While's we can't predict if a JumpGo will kill us or not + // It's worse though as an IF(..., JUMPGO) may change the control flow. + // Just don't optimize blocks with labels; they're rare - so far. + LifeBlock* prevLifep = m_lifep; LifeBlock* bodyLifep = new LifeBlock(prevLifep, m_statep); - bool prev_noopt = m_noopt; - { - m_lifep = bodyLifep; - m_noopt = true; + bool prev_noopt = m_noopt; + { + m_lifep = bodyLifep; + m_noopt = true; iterateAndNextNull(nodep->stmtsp()); - m_lifep = prevLifep; - m_noopt = prev_noopt; - } - UINFO(4," joinjump"<lifeToAbove(); - delete bodyLifep; + m_lifep = prevLifep; + m_noopt = prev_noopt; + } + UINFO(4," joinjump"<lifeToAbove(); + delete bodyLifep; } virtual void visit(AstCCall* nodep) { - //UINFO(4," CCALL "<funcp()->entryPoint()) { // else is non-inline or public function we optimize separately + // Enter the function and trace it + if (!nodep->funcp()->entryPoint()) { // else is non-inline or public function we optimize separately m_tracingCall = true; iterate(nodep->funcp()); - } + } } virtual void visit(AstCFunc* nodep) { - //UINFO(4," CCALL "<entryPoint()) return; m_tracingCall = false; - if (nodep->dpiImport() && !nodep->pure()) { - m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment - } + if (nodep->dpiImport() && !nodep->pure()) { + m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment + } iterateChildren(nodep); } virtual void visit(AstUCFunc* nodep) { - m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment + m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment iterateChildren(nodep); } virtual void visit(AstCMath* nodep) { - m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment + m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment iterateChildren(nodep); } - virtual void visit(AstVar*) {} // Don't want varrefs under it + virtual void visit(AstVar*) {} // Don't want varrefs under it virtual void visit(AstNode* nodep) { iterateChildren(nodep); } @@ -445,19 +446,19 @@ private: public: // CONSTRUCTORS LifeVisitor(AstNode* nodep, LifeState* statep) { - UINFO(4," LifeVisitor on "<entryPoint()) { - // Usage model 1: Simulate all C code, doing lifetime analysis - LifeVisitor visitor (nodep, m_statep); - } + if (nodep->entryPoint()) { + // Usage model 1: Simulate all C code, doing lifetime analysis + LifeVisitor visitor (nodep, m_statep); + } } virtual void visit(AstAlways* nodep) { - // Usage model 2: Cleanup basic blocks - LifeVisitor visitor (nodep, m_statep); + // Usage model 2: Cleanup basic blocks + LifeVisitor visitor (nodep, m_statep); } virtual void visit(AstInitial* nodep) { - // Usage model 2: Cleanup basic blocks - LifeVisitor visitor (nodep, m_statep); + // Usage model 2: Cleanup basic blocks + LifeVisitor visitor (nodep, m_statep); } virtual void visit(AstFinal* nodep) { - // Usage model 2: Cleanup basic blocks - LifeVisitor visitor (nodep, m_statep); + // Usage model 2: Cleanup basic blocks + LifeVisitor visitor (nodep, m_statep); } - virtual void visit(AstVar*) {} // Accelerate - virtual void visit(AstNodeStmt*) {} // Accelerate - virtual void visit(AstNodeMath*) {} // Accelerate + virtual void visit(AstVar*) {} // Accelerate + virtual void visit(AstNodeStmt*) {} // Accelerate + virtual void visit(AstNodeMath*) {} // Accelerate virtual void visit(AstNode* nodep) { iterateChildren(nodep); } public: // CONSTRUCTORS LifeTopVisitor(AstNetlist* nodep, LifeState* statep) { - m_statep = statep; + m_statep = statep; iterate(nodep); } virtual ~LifeTopVisitor() {} diff --git a/src/V3Life.h b/src/V3Life.h index a10fbb93a..8ea4e9be1 100644 --- a/src/V3Life.h +++ b/src/V3Life.h @@ -34,4 +34,4 @@ public: static void lifeAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index 38983db3e..b9e84c42d 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -24,7 +24,7 @@ // ASSIGN(Vdly, a) // ... {no reads or writes of a after the first write to Vdly} // ... {no reads of a after the first write to Vdly} -// ASSIGNPOST(Vdly,tmp) +// ASSIGNPOST(Vdly, tmp) // //************************************************************************* @@ -51,7 +51,8 @@ private: // NODE STATE // INPUT: - // AstVarScope::user4p() -> AstVarScope*, If set, replace this varscope with specified new one + // AstVarScope::user4p() -> AstVarScope*, If set, replace this + // varscope with specified new one // STATE // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -223,7 +224,7 @@ private: // Proof (1) const std::set& dlyVarReads = m_reads[dlyVarp]; if (!dlyVarReads.empty()) { - continue; // do not scrunch, go to next LifePostLocation + continue; // do not scrunch, go to next LifePostLocation } // Proof (2) diff --git a/src/V3LifePost.h b/src/V3LifePost.h index c9fdb7148..e087daf07 100644 --- a/src/V3LifePost.h +++ b/src/V3LifePost.h @@ -34,4 +34,4 @@ public: static void lifepostAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index e63dbebff..f496eb27e 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -18,13 +18,13 @@ // //************************************************************************* // NO EDITS: Don't replace or delete nodes, as the parser symbol table -// has pointers into the ast tree. +// has pointers into the ast tree. // // LINK TRANSFORMATIONS: -// Top-down traversal -// Cells: -// Read module if needed -// Link to module that instantiates it +// Top-down traversal +// Cells: +// Read module if needed +// Link to module that instantiates it //************************************************************************* #include "config_build.h" @@ -57,31 +57,33 @@ class LinkCellsVertex : public V3GraphVertex { AstNodeModule* m_modp; public: LinkCellsVertex(V3Graph* graphp, AstNodeModule* modp) - : V3GraphVertex(graphp), m_modp(modp) {} + : V3GraphVertex(graphp), m_modp(modp) {} virtual ~LinkCellsVertex() {} AstNodeModule* modp() const { return m_modp; } virtual string name() const { return modp()->name(); } virtual FileLine* fileline() const { return modp()->fileline(); } // Recursive modules get space for maximum recursion - virtual uint32_t rankAdder() const { return m_modp->recursiveClone() ? (1+v3Global.opt.moduleRecursionDepth()) : 1; } + virtual uint32_t rankAdder() const { + return m_modp->recursiveClone() ? (1+v3Global.opt.moduleRecursionDepth()) : 1; + } }; class LibraryVertex : public V3GraphVertex { public: explicit LibraryVertex(V3Graph* graphp) - : V3GraphVertex(graphp) {} + : V3GraphVertex(graphp) {} virtual ~LibraryVertex() {} virtual string name() const { return "*LIBRARY*"; } }; void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) { if (LinkCellsVertex* vvertexp = dynamic_cast(vertexp)) { - vvertexp->modp()->v3error("Unsupported: Recursive multiple modules (module instantiates something leading back to itself): " - <modp()->prettyName()); - vvertexp->modp()->v3error("Note self-recursion (module instantiating itself directly) is supported."); - V3Error::abortIfErrors(); + vvertexp->modp()->v3error("Unsupported: Recursive multiple modules (module instantiates something leading back to itself): " + <modp()->prettyName()); + vvertexp->modp()->v3error("Note self-recursion (module instantiating itself directly) is supported."); + V3Error::abortIfErrors(); } else { // Everything should match above, but... - v3fatalSrc("Recursive instantiations"); + v3fatalSrc("Recursive instantiations"); } } @@ -92,380 +94,395 @@ class LinkCellsVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: - // AstNodeModule::user1p() // V3GraphVertex* Vertex describing this module - // AstNodeModule::user2p() // AstNodeModule* clone used for de-recursing - // AstCell::user1p() // ==V3NodeModule* if done, != if unprocessed - // AstCell::user2() // bool clone renaming completed + // AstNodeModule::user1p() // V3GraphVertex* Vertex describing this module + // AstNodeModule::user2p() // AstNodeModule* clone used for de-recursing + // AstCell::user1p() // ==V3NodeModule* if done, != if unprocessed + // AstCell::user2() // bool clone renaming completed // Allocated across all readFiles in V3Global::readFiles: - // AstNode::user4p() // VSymEnt* Package and typedef symbol names - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + // AstNode::user4p() // VSymEnt* Package and typedef symbol names + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // STATE - V3InFilter* m_filterp; // Parser filter - V3ParseSym* m_parseSymp; // Parser symbol table + V3InFilter* m_filterp; // Parser filter + V3ParseSym* m_parseSymp; // Parser symbol table // Below state needs to be preserved between each module call. - AstNodeModule* m_modp; // Current module - VSymGraph m_mods; // Symbol table of all module names - LinkCellsGraph m_graph; // Linked graph of all cell interconnects - LibraryVertex* m_libVertexp; // Vertex at root of all libraries - V3GraphVertex* m_topVertexp; // Vertex of top module + AstNodeModule* m_modp; // Current module + VSymGraph m_mods; // Symbol table of all module names + LinkCellsGraph m_graph; // Linked graph of all cell interconnects + LibraryVertex* m_libVertexp; // Vertex at root of all libraries + V3GraphVertex* m_topVertexp; // Vertex of top module vl_unordered_set m_declfnWarned; // Files we issued DECLFILENAME on VL_DEBUG_FUNC; // Declare debug() // METHODS V3GraphVertex* vertex(AstNodeModule* nodep) { - // Return corresponding vertex for this module - if (!nodep->user1p()) { - nodep->user1p(new LinkCellsVertex(&m_graph, nodep)); - } - return (nodep->user1u().toGraphVertex()); + // Return corresponding vertex for this module + if (!nodep->user1p()) { + nodep->user1p(new LinkCellsVertex(&m_graph, nodep)); + } + return (nodep->user1u().toGraphVertex()); } AstNodeModule* findModuleSym(const string& modName) { - VSymEnt* foundp = m_mods.rootp()->findIdFallback(modName); - if (!foundp) return NULL; + VSymEnt* foundp = m_mods.rootp()->findIdFallback(modName); + if (!foundp) return NULL; else return VN_CAST(foundp->nodep(), NodeModule); } AstNodeModule* resolveModule(AstNode* nodep, const string& modName) { - AstNodeModule* modp = findModuleSym(modName); - if (!modp) { - // Read-subfile - // If file not found, make AstNotFoundModule, rather than error out. - // We'll throw the error when we know the module will really be needed. - string prettyName = AstNode::prettyName(modName); - V3Parse parser (v3Global.rootp(), m_filterp, m_parseSymp); - parser.parseFile(nodep->fileline(), prettyName, false, ""); - V3Error::abortIfErrors(); - // We've read new modules, grab new pointers to their names - readModNames(); - // Check again - modp = findModuleSym(modName); - if (!modp) { - // This shouldn't throw a message as parseFile will create a AstNotFoundModule for us - nodep->v3error("Can't resolve module reference: "<fileline(), prettyName, false, ""); + V3Error::abortIfErrors(); + // We've read new modules, grab new pointers to their names + readModNames(); + // Check again + modp = findModuleSym(modName); + if (!modp) { + // This shouldn't throw a message as parseFile will create + // a AstNotFoundModule for us + nodep->v3error("Can't resolve module reference: "<verticesNextp()) { - if (LinkCellsVertex* vvertexp = dynamic_cast(itp)) { - // +1 so we leave level 1 for the new wrapper we'll make in a moment - AstNodeModule* modp = vvertexp->modp(); - modp->level(vvertexp->rank()+1); - if (vvertexp == m_topVertexp && modp->level() != 2) { - AstNodeModule* abovep = NULL; - if (V3GraphEdge* edgep = vvertexp->inBeginp()) { - if (LinkCellsVertex* eFromVertexp = dynamic_cast(edgep->fromp())) { - abovep = eFromVertexp->modp(); - } - } - v3error("Specified --top-module '"<prettyName() : "UNKNOWN")<<"'"); - } - } - } - if (v3Global.opt.topModule()!="" - && !m_topVertexp) { - v3error("Specified --top-module '"<verticesNextp()) { + if (LinkCellsVertex* vvertexp = dynamic_cast(itp)) { + // +1 so we leave level 1 for the new wrapper we'll make in a moment + AstNodeModule* modp = vvertexp->modp(); + modp->level(vvertexp->rank()+1); + if (vvertexp == m_topVertexp && modp->level() != 2) { + AstNodeModule* abovep = NULL; + if (V3GraphEdge* edgep = vvertexp->inBeginp()) { + if (LinkCellsVertex* eFromVertexp + = dynamic_cast(edgep->fromp())) { + abovep = eFromVertexp->modp(); + } + } + v3error("Specified --top-module '"<prettyName() : "UNKNOWN")<<"'"); + } + } + } + if (v3Global.opt.topModule()!="" + && !m_topVertexp) { + v3error("Specified --top-module '"<fileline()->filebasenameNoExt() != nodep->prettyName() - && !v3Global.opt.isLibraryFile(nodep->fileline()->filename()) - && !nodep->recursiveClone() - && !nodep->internal()) { - // We only complain once per file, otherwise library-like files have a huge mess of warnings - if (m_declfnWarned.find(nodep->fileline()->filename()) == m_declfnWarned.end()) { - m_declfnWarned.insert(nodep->fileline()->filename()); - nodep->v3warn(DECLFILENAME, "Filename '"<fileline()->filebasenameNoExt() - <<"' does not match "<typeName()<<" name: "<prettyName()); - } - } + // Module: Pick up modnames, so we can resolve cells later + m_modp = nodep; + UINFO(2,"Link Module: "<fileline()->filebasenameNoExt() != nodep->prettyName() + && !v3Global.opt.isLibraryFile(nodep->fileline()->filename()) + && !nodep->recursiveClone() + && !nodep->internal()) { + // We only complain once per file, otherwise library-like files + // have a huge mess of warnings + if (m_declfnWarned.find(nodep->fileline()->filename()) == m_declfnWarned.end()) { + m_declfnWarned.insert(nodep->fileline()->filename()); + nodep->v3warn(DECLFILENAME, "Filename '"<fileline()->filebasenameNoExt() + <<"' does not match "<typeName() + <<" name: "<prettyName()); + } + } if (VN_IS(nodep, Iface) || VN_IS(nodep, Package)) nodep->inLibrary(true); // Interfaces can't be at top, unless asked - bool topMatch = (v3Global.opt.topModule()==nodep->prettyName()); - if (topMatch) { - m_topVertexp = vertex(nodep); - UINFO(2,"Link --top-module: "<inLibrary(false); // Safer to make sure it doesn't disappear - } - if (v3Global.opt.topModule()=="" - ? nodep->inLibrary() // Library cells are lower - : !topMatch) { // Any non-specified module is lower - // Put under a fake vertex so that the graph ranking won't indicate - // this is a top level module - if (!m_libVertexp) m_libVertexp = new LibraryVertex(&m_graph); - new V3GraphEdge(&m_graph, m_libVertexp, vertex(nodep), 1, false); - } - // Note AstBind also has iteration on cells + bool topMatch = (v3Global.opt.topModule()==nodep->prettyName()); + if (topMatch) { + m_topVertexp = vertex(nodep); + UINFO(2,"Link --top-module: "<inLibrary(false); // Safer to make sure it doesn't disappear + } + if (v3Global.opt.topModule()=="" + ? nodep->inLibrary() // Library cells are lower + : !topMatch) { // Any non-specified module is lower + // Put under a fake vertex so that the graph ranking won't indicate + // this is a top level module + if (!m_libVertexp) m_libVertexp = new LibraryVertex(&m_graph); + new V3GraphEdge(&m_graph, m_libVertexp, vertex(nodep), 1, false); + } + // Note AstBind also has iteration on cells iterateChildren(nodep); - nodep->checkTree(); - m_modp = NULL; + nodep->checkTree(); + m_modp = NULL; } virtual void visit(AstIfaceRefDType* nodep) { - // Cell: Resolve its filename. If necessary, parse it. - UINFO(4,"Link IfaceRef: "<ifaceName()); - if (modp) { + // Cell: Resolve its filename. If necessary, parse it. + UINFO(4,"Link IfaceRef: "<ifaceName()); + if (modp) { if (VN_IS(modp, Iface)) { - // Track module depths, so can sort list from parent down to children - new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false); + // Track module depths, so can sort list from parent down to children + new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false); if (!nodep->cellp()) nodep->ifacep(VN_CAST(modp, Iface)); } else if (VN_IS(modp, NotFoundModule)) { // Will error out later - } else { - nodep->v3error("Non-interface used as an interface: "<prettyName()); - } - } - // Note cannot do modport resolution here; modports are allowed underneath generates + } else { + nodep->v3error("Non-interface used as an interface: "<prettyName()); + } + } + // Note cannot do modport resolution here; modports are allowed underneath generates } virtual void visit(AstPackageImport* nodep) { - // Package Import: We need to do the package before the use of a package + // Package Import: We need to do the package before the use of a package iterateChildren(nodep); - if (!nodep->packagep()) nodep->v3fatalSrc("Unlinked package"); // Parser should set packagep - new V3GraphEdge(&m_graph, vertex(m_modp), vertex(nodep->packagep()), 1, false); + if (!nodep->packagep()) nodep->v3fatalSrc("Unlinked package"); // Parser should set packagep + new V3GraphEdge(&m_graph, vertex(m_modp), vertex(nodep->packagep()), 1, false); } virtual void visit(AstBind* nodep) { - // Bind: Has cells underneath that need to be put into the new module, and cells which need resolution - // TODO this doesn't allow bind to dotted hier names, that would require - // this move to post param, which would mean we do not auto-read modules - // and means we cannot compute module levels until later. - UINFO(4,"Link Bind: "<name()); - if (modp) { - AstNode* cellsp = nodep->cellsp()->unlinkFrBackWithNext(); - // Module may have already linked, so need to pick up these new cells - AstNodeModule* oldModp = m_modp; - { - m_modp = modp; - modp->addStmtp(cellsp); // Important that this adds to end, as next iterate assumes does all cells + // Bind: Has cells underneath that need to be put into the new + // module, and cells which need resolution + // TODO this doesn't allow bind to dotted hier names, that would require + // this move to post param, which would mean we do not auto-read modules + // and means we cannot compute module levels until later. + UINFO(4,"Link Bind: "<name()); + if (modp) { + AstNode* cellsp = nodep->cellsp()->unlinkFrBackWithNext(); + // Module may have already linked, so need to pick up these new cells + AstNodeModule* oldModp = m_modp; + { + m_modp = modp; + modp->addStmtp(cellsp); // Important that this adds to end, as next iterate assumes does all cells iterateAndNextNull(cellsp); - } - m_modp = oldModp; - } - pushDeletep(nodep->unlinkFrBack()); + } + m_modp = oldModp; + } + pushDeletep(nodep->unlinkFrBack()); } virtual void visit(AstCell* nodep) { - // Cell: Resolve its filename. If necessary, parse it. - // Execute only once. Complication is that cloning may result in user1 being set (for pre-clone) - // so check if user1() matches the m_mod, if 0 never did it, if !=, it is an unprocessed clone - bool cloned = (nodep->user1p() && nodep->user1p()!=m_modp); - if (nodep->user1p()==m_modp) return; // AstBind and AstNodeModule may call a cell twice - nodep->user1p(m_modp); - // - if (!nodep->modp() || cloned) { - UINFO(4,"Link Cell: "<modName()); - if (cellmodp) { - if (cellmodp == m_modp - || cellmodp->user2p() == m_modp) { - UINFO(1,"Self-recursive module "<recursive(true); - nodep->recursive(true); - if (!cellmodp->recursiveClone()) { - // In the non-Vrcm, which needs to point to Vrcm flavor - // - // Make a clone which this cell points to - // Later, the clone's cells will also point clone'd name - // This lets us link the XREFs between the (uncloned) children so - // they don't point to the same module which would - // break parameter resolution. + // Cell: Resolve its filename. If necessary, parse it. + // Execute only once. Complication is that cloning may result in + // user1 being set (for pre-clone) so check if user1() matches the + // m_mod, if 0 never did it, if !=, it is an unprocessed clone + bool cloned = (nodep->user1p() && nodep->user1p()!=m_modp); + if (nodep->user1p()==m_modp) return; // AstBind and AstNodeModule may call a cell twice + nodep->user1p(m_modp); + // + if (!nodep->modp() || cloned) { + UINFO(4,"Link Cell: "<modName()); + if (cellmodp) { + if (cellmodp == m_modp + || cellmodp->user2p() == m_modp) { + UINFO(1,"Self-recursive module "<recursive(true); + nodep->recursive(true); + if (!cellmodp->recursiveClone()) { + // In the non-Vrcm, which needs to point to Vrcm flavor + // + // Make a clone which this cell points to + // Later, the clone's cells will also point clone'd name + // This lets us link the XREFs between the (uncloned) children so + // they don't point to the same module which would + // break parameter resolution. AstNodeModule* otherModp = VN_CAST(cellmodp->user2p(), NodeModule); - if (!otherModp) { - otherModp = cellmodp->cloneTree(false); - otherModp->name(otherModp->name()+"__Vrcm"); - otherModp->user1p(NULL); // Need new vertex - otherModp->user2p(cellmodp); - otherModp->recursiveClone(true); - // user1 etc will retain its pre-clone value - cellmodp->user2p(otherModp); - v3Global.rootp()->addModulep(otherModp); - new V3GraphEdge(&m_graph, vertex(cellmodp), vertex(otherModp), 1, false); - } - cellmodp = otherModp; - nodep->modp(cellmodp); - } - else { - // In the Vrcm, which needs to point back to Vrcm flavor - // The cell already has the correct resolution (to Vrcm) - nodep->modp(cellmodp); - // We don't create a V3GraphEdge (as it would be circular) - } - } - else { // Non-recursive - // Track module depths, so can sort list from parent down to children - nodep->modp(cellmodp); - new V3GraphEdge(&m_graph, vertex(m_modp), vertex(cellmodp), 1, false); - } - } - } - // Remove AstCell(AstPin("",NULL)), it's a side effect of how we parse "()" - // the empty middle is identical to the empty rule that must find pins in "(,)". - if (nodep->pinsp() && !nodep->pinsp()->nextp() - && nodep->pinsp()->name() == "" - && !nodep->pinsp()->exprp()) { - pushDeletep(nodep->pinsp()->unlinkFrBackWithNext()); - } - if (nodep->paramsp() && !nodep->paramsp()->nextp() - && nodep->paramsp()->name() == "" - && !nodep->paramsp()->exprp()) { - pushDeletep(nodep->paramsp()->unlinkFrBackWithNext()); - } - // Convert .* to list of pins - bool pinStar = false; - for (AstPin* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) { + if (!otherModp) { + otherModp = cellmodp->cloneTree(false); + otherModp->name(otherModp->name()+"__Vrcm"); + otherModp->user1p(NULL); // Need new vertex + otherModp->user2p(cellmodp); + otherModp->recursiveClone(true); + // user1 etc will retain its pre-clone value + cellmodp->user2p(otherModp); + v3Global.rootp()->addModulep(otherModp); + new V3GraphEdge(&m_graph, vertex(cellmodp), + vertex(otherModp), 1, false); + } + cellmodp = otherModp; + nodep->modp(cellmodp); + } + else { + // In the Vrcm, which needs to point back to Vrcm flavor + // The cell already has the correct resolution (to Vrcm) + nodep->modp(cellmodp); + // We don't create a V3GraphEdge (as it would be circular) + } + } + else { // Non-recursive + // Track module depths, so can sort list from parent down to children + nodep->modp(cellmodp); + new V3GraphEdge(&m_graph, vertex(m_modp), vertex(cellmodp), 1, false); + } + } + } + // Remove AstCell(AstPin("",NULL)), it's a side effect of how we parse "()" + // the empty middle is identical to the empty rule that must find pins in "(,)". + if (nodep->pinsp() && !nodep->pinsp()->nextp() + && nodep->pinsp()->name() == "" + && !nodep->pinsp()->exprp()) { + pushDeletep(nodep->pinsp()->unlinkFrBackWithNext()); + } + if (nodep->paramsp() && !nodep->paramsp()->nextp() + && nodep->paramsp()->name() == "" + && !nodep->paramsp()->exprp()) { + pushDeletep(nodep->paramsp()->unlinkFrBackWithNext()); + } + // Convert .* to list of pins + bool pinStar = false; + for (AstPin* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) { nextp = VN_CAST(pinp->nextp(), Pin); - if (pinp->dotStar()) { - if (pinStar) pinp->v3error("Duplicate .* in a cell"); - pinStar = true; - // Done with this fake pin - pinp->unlinkFrBack()->deleteTree(); VL_DANGLING(pinp); - } - } - // Convert unnamed pins to pin number based assignments + if (pinp->dotStar()) { + if (pinStar) pinp->v3error("Duplicate .* in a cell"); + pinStar = true; + // Done with this fake pin + pinp->unlinkFrBack()->deleteTree(); VL_DANGLING(pinp); + } + } + // Convert unnamed pins to pin number based assignments for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - if (pinp->name()=="") pinp->name("__pinNumber"+cvtToStr(pinp->pinNum())); - } + if (pinp->name()=="") pinp->name("__pinNumber"+cvtToStr(pinp->pinNum())); + } for (AstPin* pinp = nodep->paramsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - pinp->param(true); - if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum())); - } - if (nodep->modp()) { - nodep->modName(nodep->modp()->name()); - // Note what pins exist - vl_unordered_set ports; // Symbol table of all connected port names + pinp->param(true); + if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum())); + } + if (nodep->modp()) { + nodep->modName(nodep->modp()->name()); + // Note what pins exist + vl_unordered_set ports; // Symbol table of all connected port names for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells"); - if (!pinp->exprp()) { - if (pinp->name().substr(0, 11) == "__pinNumber") { - pinp->v3warn(PINNOCONNECT,"Cell pin is not connected: "<prettyName()); - } else { - pinp->v3warn(PINCONNECTEMPTY,"Cell pin connected by name with empty reference: "<prettyName()); - } - } - if (ports.find(pinp->name()) == ports.end()) { - ports.insert(pinp->name()); - } - } - // We search ports, rather than in/out declarations as they aren't resolved yet, - // and it's easier to do it now than in V3LinkDot when we'd need to repeat steps. - for (AstNode* portnodep = nodep->modp()->stmtsp(); portnodep; portnodep=portnodep->nextp()) { + if (pinp->name()=="") pinp->v3error("Connect by position is illegal in .* connected cells"); + if (!pinp->exprp()) { + if (pinp->name().substr(0, 11) == "__pinNumber") { + pinp->v3warn(PINNOCONNECT, "Cell pin is not connected: " + <prettyName()); + } else { + pinp->v3warn(PINCONNECTEMPTY, + "Cell pin connected by name with empty reference: " + <prettyName()); + } + } + if (ports.find(pinp->name()) == ports.end()) { + ports.insert(pinp->name()); + } + } + // We search ports, rather than in/out declarations as they aren't resolved yet, + // and it's easier to do it now than in V3LinkDot when we'd need to repeat steps. + for (AstNode* portnodep = nodep->modp()->stmtsp(); + portnodep; portnodep=portnodep->nextp()) { if (const AstPort* portp = VN_CAST(portnodep, Port)) { - if (ports.find(portp->name()) == ports.end() - && ports.find("__pinNumber"+cvtToStr(portp->pinNum())) == ports.end()) { - if (pinStar) { - UINFO(9," need .* PORT "<fileline(),0,portp->name(), - new AstParseRef(nodep->fileline(), - AstParseRefExp::PX_TEXT, portp->name(), NULL, NULL)); - newp->svImplicit(true); - nodep->addPinsp(newp); - } else { // warn on the CELL that needs it, not the port - nodep->v3warn(PINMISSING, "Cell has missing pin: "<prettyName()); - AstPin* newp = new AstPin(nodep->fileline(),0,portp->name(),NULL); - nodep->addPinsp(newp); - } - } - } - } - } + if (ports.find(portp->name()) == ports.end() + && ports.find("__pinNumber"+cvtToStr(portp->pinNum())) == ports.end()) { + if (pinStar) { + UINFO(9," need .* PORT "<fileline(), 0, portp->name(), + new AstParseRef(nodep->fileline(), + AstParseRefExp::PX_TEXT, + portp->name(), NULL, NULL)); + newp->svImplicit(true); + nodep->addPinsp(newp); + } else { // warn on the CELL that needs it, not the port + nodep->v3warn(PINMISSING, "Cell has missing pin: " + <prettyName()); + AstPin* newp = new AstPin(nodep->fileline(), 0, portp->name(), NULL); + nodep->addPinsp(newp); + } + } + } + } + } if (VN_IS(nodep->modp(), Iface)) { - // Cell really is the parent's instantiation of an interface, not a normal module - // Make sure we have a variable to refer to this cell, so can . - // in the same way that a child does. Rename though to avoid conflict with cell. - // This is quite similar to how classes work; when unpacked classes are better supported - // may remap interfaces to be more like a class. - if (!nodep->hasIfaceVar()) { - string varName = nodep->name() + "__Viftop"; // V3LinkDot looks for this naming - AstIfaceRefDType* idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(), - nodep->modp()->name()); - idtypep->ifacep(NULL); // cellp overrides - // In the case of arrayed interfaces, we replace cellp when de-arraying in V3Inst - idtypep->cellp(nodep); // Only set when real parent cell known. - AstVar* varp; - if (nodep->rangep()) { + // Cell really is the parent's instantiation of an interface, not a normal module + // Make sure we have a variable to refer to this cell, so can . + // in the same way that a child does. Rename though to avoid conflict with cell. + // This is quite similar to how classes work; when unpacked classes are better supported + // may remap interfaces to be more like a class. + if (!nodep->hasIfaceVar()) { + string varName = nodep->name() + "__Viftop"; // V3LinkDot looks for this naming + AstIfaceRefDType* idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(), + nodep->modp()->name()); + idtypep->ifacep(NULL); // cellp overrides + // In the case of arrayed interfaces, we replace cellp when de-arraying in V3Inst + idtypep->cellp(nodep); // Only set when real parent cell known. + AstVar* varp; + if (nodep->rangep()) { AstNodeArrayDType* arrp = new AstUnpackArrayDType(nodep->fileline(), VFlagChildDType(), idtypep, nodep->rangep()->cloneTree(true)); - varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, - VFlagChildDType(), arrp); - } else { - varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, - VFlagChildDType(), idtypep); - } - varp->isIfaceParent(true); - nodep->addNextHere(varp); - nodep->hasIfaceVar(true); - } - } - if (nodep->modp()) { + varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, + VFlagChildDType(), arrp); + } else { + varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, + VFlagChildDType(), idtypep); + } + varp->isIfaceParent(true); + nodep->addNextHere(varp); + nodep->hasIfaceVar(true); + } + } + if (nodep->modp()) { iterateChildren(nodep); - } - UINFO(4," Link Cell done: "<modulesp(); nodep; nodep=nextp) { + // Look at all modules, and store pointers to all module names + for (AstNodeModule* nextp,* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nextp) { nextp = VN_CAST(nodep->nextp(), NodeModule); - AstNodeModule* foundp = findModuleSym(nodep->name()); - if (foundp && foundp != nodep) { - if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP) || nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP))) { - nodep->v3warn(MODDUP,"Duplicate declaration of module: "<prettyName()<warnMore()<<"... Location of original declaration"); - } - nodep->unlinkFrBack(); - pushDeletep(nodep); VL_DANGLING(nodep); - } else if (!foundp) { - m_mods.rootp()->insert(nodep->name(), new VSymEnt(&m_mods, nodep)); - } - } - //if (debug()>=9) m_mods.dump(cout, "-syms: "); + AstNodeModule* foundp = findModuleSym(nodep->name()); + if (foundp && foundp != nodep) { + if (!(foundp->fileline()->warnIsOff(V3ErrorCode::MODDUP) + || nodep->fileline()->warnIsOff(V3ErrorCode::MODDUP))) { + nodep->v3warn(MODDUP, "Duplicate declaration of module: " + <prettyName()<warnMore()<<"... Location of original declaration"); + } + nodep->unlinkFrBack(); + pushDeletep(nodep); VL_DANGLING(nodep); + } else if (!foundp) { + m_mods.rootp()->insert(nodep->name(), new VSymEnt(&m_mods, nodep)); + } + } + //if (debug()>=9) m_mods.dump(cout, "-syms: "); } public: // CONSTUCTORS LinkCellsVisitor(AstNetlist* nodep, V3InFilter* filterp, V3ParseSym* parseSymp) : m_mods(nodep) { - m_filterp = filterp; - m_parseSymp = parseSymp; - m_modp = NULL; - m_libVertexp = NULL; - m_topVertexp = NULL; + m_filterp = filterp; + m_parseSymp = parseSymp; + m_modp = NULL; + m_libVertexp = NULL; + m_topVertexp = NULL; iterate(nodep); } virtual ~LinkCellsVisitor() {} diff --git a/src/V3LinkCells.h b/src/V3LinkCells.h index 6fb3a113c..70a90d366 100644 --- a/src/V3LinkCells.h +++ b/src/V3LinkCells.h @@ -37,4 +37,4 @@ public: static void link(AstNetlist* nodep, V3InFilter* filterp, V3ParseSym* parseSymp); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 6bf71a797..f5a0f718a 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -18,44 +18,44 @@ // //************************************************************************* // LinkDot TRANSFORMATIONS: -// Top-down traversal in LinkDotFindVisitor -// Cells: -// Make graph of cell hierarchy -// Var/Funcs's: -// Collect all names into symtable under appropriate cell -// Top-down traversal in LinkDotScopeVisitor -// Find VarScope versions of signals (well past original link) -// Top-down traversal in LinkDotParamVisitor -// Create implicit signals -// Top-down traversal in LinkDotResolveVisitor -// VarXRef/Func's: -// Find appropriate named cell and link to var they reference +// Top-down traversal in LinkDotFindVisitor +// Cells: +// Make graph of cell hierarchy +// Var/Funcs's: +// Collect all names into symtable under appropriate cell +// Top-down traversal in LinkDotScopeVisitor +// Find VarScope versions of signals (well past original link) +// Top-down traversal in LinkDotParamVisitor +// Create implicit signals +// Top-down traversal in LinkDotResolveVisitor +// VarXRef/Func's: +// Find appropriate named cell and link to var they reference //************************************************************************* // Interfaces: -// CELL (.port (ifref) -// ^--- cell -> IfaceDTypeRef(iface) -// ^--- cell.modport -> IfaceDTypeRef(iface,modport) -// ^--- varref(input_ifref) -> IfaceDTypeRef(iface) -// ^--- varref(input_ifref).modport -> IfaceDTypeRef(iface,modport) -// FindVisitor: -// #1: Insert interface Vars -// #2: Insert ModPort names -// IfaceVisitor: -// #3: Update ModPortVarRef to point at interface vars (after #1) -// #4: Create ModPortVarRef symbol table entries -// FindVisitor-insertIfaceRefs() -// #5: Resolve IfaceRefDtype modport names (after #2) -// #7: Record sym of IfaceRefDType and aliased interface and/or modport (after #4,#5) -// insertAllScopeAliases(): -// #8: Insert modport's symbols under IfaceRefDType (after #7) -// ResolveVisitor: -// #9: Resolve general variables, which may point into the interface or modport (after #8) +// CELL (.port (ifref) +// ^--- cell -> IfaceDTypeRef(iface) +// ^--- cell.modport -> IfaceDTypeRef(iface,modport) +// ^--- varref(input_ifref) -> IfaceDTypeRef(iface) +// ^--- varref(input_ifref).modport -> IfaceDTypeRef(iface,modport) +// FindVisitor: +// #1: Insert interface Vars +// #2: Insert ModPort names +// IfaceVisitor: +// #3: Update ModPortVarRef to point at interface vars (after #1) +// #4: Create ModPortVarRef symbol table entries +// FindVisitor-insertIfaceRefs() +// #5: Resolve IfaceRefDtype modport names (after #2) +// #7: Record sym of IfaceRefDType and aliased interface and/or modport (after #4,#5) +// insertAllScopeAliases(): +// #8: Insert modport's symbols under IfaceRefDType (after #7) +// ResolveVisitor: +// #9: Resolve general variables, which may point into the interface or modport (after #8) //************************************************************************* // TOP // {name-of-top-modulename} // a (VSymEnt->AstCell) -// {name-of-cell} -// {name-of-cell-module} +// {name-of-cell} +// {name-of-cell-module} // aa (VSymEnt->AstCell) // var (AstVar) -- no sub symbol table needed // beg (VSymEnt->AstBegin) -- can see "upper" a's symbol table @@ -86,22 +86,23 @@ class LinkDotState { private: // NODE STATE // Cleared on Netlist - // AstNodeModule::user1p() // VSymEnt*. Last symbol created for this node - // AstNodeModule::user2() // bool. Currently processing for recursion check - // ... Note maybe more than one, as can be multiple hierarchy places - // AstVarScope::user2p() // AstVarScope*. Base alias for AstInline of this signal - // AstVar::user2p() // AstFTask*. If a function variable, the task that links to the variable - // AstVar::user4() // bool. True if port set for this variable - // AstBegin::user4() // bool. Did name processing - // AstNodeModule::user4() // bool. Live module - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser4InUse m_inuser4; + // AstNodeModule::user1p() // VSymEnt*. Last symbol created for this node + // AstNodeModule::user2() // bool. Currently processing for recursion check + // ... Note maybe more than one, as can be multiple hierarchy places + // AstVarScope::user2p() // AstVarScope*. Base alias for AstInline of this signal + // AstVar::user2p() // AstFTask*. If a function variable, the task + // that links to the variable + // AstVar::user4() // bool. True if port set for this variable + // AstBegin::user4() // bool. Did name processing + // AstNodeModule::user4() // bool. Live module + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser4InUse m_inuser4; public: // ENUMS // In order of priority, compute first ... compute last - enum SAMNum { SAMN_MODPORT, SAMN_IFTOP, SAMN__MAX }; // Values for m_scopeAliasMap + enum SAMNum { SAMN_MODPORT, SAMN_IFTOP, SAMN__MAX }; // Values for m_scopeAliasMap private: // TYPES @@ -111,72 +112,72 @@ private: typedef std::vector IfaceVarSyms; typedef std::vector > IfaceModSyms; - static LinkDotState* s_errorThisp; // Last self, for error reporting only + static LinkDotState* s_errorThisp; // Last self, for error reporting only // MEMBERS - VSymGraph m_syms; // Symbol table - VSymEnt* m_dunitEntp; // $unit entry - NameScopeSymMap m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name - ImplicitNameSet m_implicitNameSet; // For [module][signalname] if we can implicitly create it - ScopeAliasMap m_scopeAliasMap[SAMN__MAX]; // Map of aliases - IfaceVarSyms m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported - IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed - bool m_forPrimary; // First link - bool m_forPrearray; // Compress cell__[array] refs - bool m_forScopeCreation; // Remove VarXRefs for V3Scope + VSymGraph m_syms; // Symbol table + VSymEnt* m_dunitEntp; // $unit entry + NameScopeSymMap m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name + ImplicitNameSet m_implicitNameSet; // For [module][signalname] if we can implicitly create it + ScopeAliasMap m_scopeAliasMap[SAMN__MAX]; // Map of aliases + IfaceVarSyms m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported + IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed + bool m_forPrimary; // First link + bool m_forPrearray; // Compress cell__[array] refs + bool m_forScopeCreation; // Remove VarXRefs for V3Scope public: // METHODS VL_DEBUG_FUNC; // Declare debug() void dump(const string& nameComment="linkdot", bool force=false) { - if (debug()>=6 || force) { - string filename = v3Global.debugFilename(nameComment)+".txt"; + if (debug()>=6 || force) { + string filename = v3Global.debugFilename(nameComment)+".txt"; const vl_unique_ptr logp (V3File::new_ofstream(filename)); if (logp->fail()) v3fatal("Can't write "<first<<" ("<first->nodep()->typeName() - <<") <- "<second<<" "<second->nodep()<first<<" ("<first->nodep()->typeName() + <<") <- "<second<<" "<second->nodep()<preErrorDump(); + if (s_errorThisp) s_errorThisp->preErrorDump(); } void preErrorDump() { - static bool diddump = false; - if (!diddump && v3Global.opt.dumpTree()) { - diddump = true; - dump("linkdot-preerr",true); - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot-preerr.tree")); - } + static bool diddump = false; + if (!diddump && v3Global.opt.dumpTree()) { + diddump = true; + dump("linkdot-preerr", true); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("linkdot-preerr.tree")); + } } // CONSTRUCTORS LinkDotState(AstNetlist* rootp, VLinkDotStep step) - : m_syms(rootp) { - UINFO(4,__FUNCTION__<<": "<prettyTypeName(); + else return nodep->prettyTypeName(); } VSymEnt* rootEntp() const { return m_syms.rootp(); } VSymEnt* dunitEntp() const { return m_dunitEntp; } void checkDuplicate(VSymEnt* lookupSymp, AstNode* nodep, const string& name) { - // Lookup the given name under current symbol table - // Insert if not found - // Report error if there's a duplicate - // - // Note we only check for conflicts at the same level; it's ok if one block hides another - // We also wouldn't want to not insert it even though it's lower down - VSymEnt* foundp = lookupSymp->findIdFlat(name); - AstNode* fnodep = foundp ? foundp->nodep() : NULL; - if (!fnodep) { - // Not found, will add in a moment. - } else if (nodep==fnodep) { // Already inserted. - // Good. - } else if (foundp->imported()) { // From package - // We don't throw VARHIDDEN as if the import is later the symbol table's import wouldn't warn + // Lookup the given name under current symbol table + // Insert if not found + // Report error if there's a duplicate + // + // Note we only check for conflicts at the same level; it's ok if one block hides another + // We also wouldn't want to not insert it even though it's lower down + VSymEnt* foundp = lookupSymp->findIdFlat(name); + AstNode* fnodep = foundp ? foundp->nodep() : NULL; + if (!fnodep) { + // Not found, will add in a moment. + } else if (nodep==fnodep) { // Already inserted. + // Good. + } else if (foundp->imported()) { // From package + // We don't throw VARHIDDEN as if the import is later the symbol + // table's import wouldn't warn } else if (VN_IS(nodep, Begin) && VN_IS(fnodep, Begin) && VN_CAST(nodep, Begin)->generate()) { - // Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks - // See t_gen_forif.v for an example. - } else { - UINFO(4,"name "<name - UINFO(4,"Var1 "<type() == fnodep->type()) { - nodep->v3error("Duplicate declaration of "<prettyName()<warnMore()<<"... Location of original declaration"); - } else { - nodep->v3error("Unsupported in C: "<prettyName()<warnMore()<<"... Location of original declaration"); - } - } + // Begin: ... blocks often replicate under genif/genfor, so simply + // suppress duplicate checks. See t_gen_forif.v for an example. + } else { + UINFO(4,"name "<name + UINFO(4,"Var1 "<type() == fnodep->type()) { + nodep->v3error("Duplicate declaration of "<prettyName()<warnMore()<<"... Location of original declaration"); + } else { + nodep->v3error("Unsupported in C: "<prettyName()<warnMore()<<"... Location of original declaration"); + } + } } void insertDUnit(AstNetlist* nodep) { - // $unit on top scope - VSymEnt* symp = new VSymEnt(&m_syms, nodep); + // $unit on top scope + VSymEnt* symp = new VSymEnt(&m_syms, nodep); UINFO(9," INSERTdunit se"<parentp(rootEntp()); // Needed so backward search can find name of top module - symp->fallbackp(NULL); - rootEntp()->insert("$unit ",symp); // Space so can never name conflict with user code - // - if (m_dunitEntp) nodep->v3fatalSrc("Call insertDUnit only once"); - m_dunitEntp = symp; + symp->parentp(rootEntp()); // Needed so backward search can find name of top module + symp->fallbackp(NULL); + rootEntp()->insert("$unit ", symp); // Space so can never name conflict with user code + // + if (m_dunitEntp) nodep->v3fatalSrc("Call insertDUnit only once"); + m_dunitEntp = symp; } VSymEnt* insertTopCell(AstNodeModule* nodep, const string& scopename) { - // Only called on the module at the very top of the hierarchy - VSymEnt* symp = new VSymEnt(&m_syms, nodep); + // Only called on the module at the very top of the hierarchy + VSymEnt* symp = new VSymEnt(&m_syms, nodep); UINFO(9," INSERTtop se"<parentp(rootEntp()); // Needed so backward search can find name of top module - symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff - nodep->user1p(symp); - checkDuplicate(rootEntp(), nodep, nodep->origName()); - rootEntp()->insert(nodep->origName(),symp); - if (forScopeCreation()) m_nameScopeSymMap.insert(make_pair(scopename, symp)); - return symp; + symp->parentp(rootEntp()); // Needed so backward search can find name of top module + symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff + nodep->user1p(symp); + checkDuplicate(rootEntp(), nodep, nodep->origName()); + rootEntp()->insert(nodep->origName(), symp); + if (forScopeCreation()) m_nameScopeSymMap.insert(make_pair(scopename, symp)); + return symp; } VSymEnt* insertCell(VSymEnt* abovep, VSymEnt* modSymp, - AstCell* nodep, const string& scopename) { - if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); - VSymEnt* symp = new VSymEnt(&m_syms, nodep); - UINFO(9," INSERTcel se"<v3fatalSrc("Null symbol table inserting node"); + VSymEnt* symp = new VSymEnt(&m_syms, nodep); + UINFO(9," INSERTcel se"<reinsert(nodep->name(), symp); - } - if (forScopeCreation()) m_nameScopeSymMap.insert(make_pair(scopename, symp)); - return symp; + symp->parentp(abovep); + symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff + nodep->user1p(symp); + if (nodep->modp()) nodep->modp()->user1p(symp); + checkDuplicate(abovep, nodep, nodep->origName()); + abovep->reinsert(nodep->origName(), symp); + if (forScopeCreation() && abovep != modSymp && !modSymp->findIdFlat(nodep->name())) { + // If it's foo_DOT_bar, we need to be able to find it under "foo_DOT_bar" too. + // Duplicates are possible, as until resolve generates might + // have 2 same cells under an if + modSymp->reinsert(nodep->name(), symp); + } + if (forScopeCreation()) m_nameScopeSymMap.insert(make_pair(scopename, symp)); + return symp; } VSymEnt* insertInline(VSymEnt* abovep, VSymEnt* modSymp, - AstCellInline* nodep, const string& basename) { - // A fake point in the hierarchy, corresponding to an inlined module - // This refrences to another Sym, and eventually resolves to a module with a prefix - if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); - VSymEnt* symp = new VSymEnt(&m_syms, nodep); - UINFO(9," INSERTinl se"<v3fatalSrc("Null symbol table inserting node"); + VSymEnt* symp = new VSymEnt(&m_syms, nodep); + UINFO(9," INSERTinl se"<user1p(symp); - checkDuplicate(abovep, nodep, nodep->name()); - abovep->reinsert(basename, symp); - if (abovep != modSymp && !modSymp->findIdFlat(nodep->name())) { - // If it's foo_DOT_bar, we need to be able to find it under that too. - modSymp->reinsert(nodep->name(), symp); - } - return symp; + symp->parentp(abovep); + symp->fallbackp(modSymp); + symp->symPrefix(nodep->name()+"__DOT__"); + nodep->user1p(symp); + checkDuplicate(abovep, nodep, nodep->name()); + abovep->reinsert(basename, symp); + if (abovep != modSymp && !modSymp->findIdFlat(nodep->name())) { + // If it's foo_DOT_bar, we need to be able to find it under that too. + modSymp->reinsert(nodep->name(), symp); + } + return symp; } - VSymEnt* insertBlock(VSymEnt* abovep, const string& name, AstNode* nodep, AstPackage* packagep) { - // A fake point in the hierarchy, corresponding to a begin or function/task block - // After we remove begins these will go away - // Note we fallback to the symbol table of the parent, as we want to find variables there - // However, cells walk the graph, so cells will appear under the begin/ftask itself - if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); - VSymEnt* symp = new VSymEnt(&m_syms, nodep); - UINFO(9," INSERTblk se"<reinsert(name, symp); - return symp; + VSymEnt* insertBlock(VSymEnt* abovep, const string& name, + AstNode* nodep, AstPackage* packagep) { + // A fake point in the hierarchy, corresponding to a begin or function/task block + // After we remove begins these will go away + // Note we fallback to the symbol table of the parent, as we want to find variables there + // However, cells walk the graph, so cells will appear under the begin/ftask itself + if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); + VSymEnt* symp = new VSymEnt(&m_syms, nodep); + UINFO(9," INSERTblk se"<reinsert(name, symp); + return symp; } - VSymEnt* insertSym(VSymEnt* abovep, const string& name, AstNode* nodep, AstPackage* packagep) { - if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); - VSymEnt* symp = new VSymEnt(&m_syms, nodep); + VSymEnt* insertSym(VSymEnt* abovep, const string& name, + AstNode* nodep, AstPackage* packagep) { + if (!abovep) nodep->v3fatalSrc("Null symbol table inserting node"); + VSymEnt* symp = new VSymEnt(&m_syms, nodep); UINFO(9," INSERTsym se"<user1u().toSymEnt(); + if (!symp) nodep->v3fatalSrc("Module/etc never assigned a symbol entry?"); + return symp; } VSymEnt* getScopeSym(AstScope* nodep) { - NameScopeSymMap::iterator it = m_nameScopeSymMap.find(nodep->name()); - if (it == m_nameScopeSymMap.end()) { - nodep->v3fatalSrc("Scope never assigned a symbol entry?"); - } - return it->second; + NameScopeSymMap::iterator it = m_nameScopeSymMap.find(nodep->name()); + if (it == m_nameScopeSymMap.end()) { + nodep->v3fatalSrc("Scope never assigned a symbol entry?"); + } + return it->second; } void implicitOkAdd(AstNodeModule* nodep, const string& varname) { - // Mark the given variable name as being allowed to be implicitly declared - if (nodep) { - ImplicitNameSet::iterator it = m_implicitNameSet.find(make_pair(nodep,varname)); - if (it == m_implicitNameSet.end()) { - m_implicitNameSet.insert(make_pair(nodep,varname)); - } - } + // Mark the given variable name as being allowed to be implicitly declared + if (nodep) { + ImplicitNameSet::iterator it = m_implicitNameSet.find(make_pair(nodep, varname)); + if (it == m_implicitNameSet.end()) { + m_implicitNameSet.insert(make_pair(nodep, varname)); + } + } } bool implicitOk(AstNodeModule* nodep, const string& varname) { - return nodep - && (m_implicitNameSet.find(make_pair(nodep,varname)) != m_implicitNameSet.end()); + return nodep + && (m_implicitNameSet.find(make_pair(nodep, varname)) + != m_implicitNameSet.end()); } // Track and later recurse interface modules void insertIfaceModSym(AstIface* nodep, VSymEnt* symp) { - m_ifaceModSyms.push_back(make_pair(nodep, symp)); + m_ifaceModSyms.push_back(make_pair(nodep, symp)); } void computeIfaceModSyms(); // Track and later insert interface references void insertIfaceVarSym(VSymEnt* symp) { // Where sym is for a VAR of dtype IFACEREFDTYPE - m_ifaceVarSyms.push_back(symp); + m_ifaceVarSyms.push_back(symp); } // Iface for a raw or arrayed iface static AstIfaceRefDType* ifaceRefFromArray(AstNodeDType* nodep) { AstIfaceRefDType* ifacerefp = VN_CAST(nodep, IfaceRefDType); - if (!ifacerefp) { + if (!ifacerefp) { if (AstUnpackArrayDType* arrp = VN_CAST(nodep, UnpackArrayDType)) { ifacerefp = VN_CAST(arrp->subDTypep(), IfaceRefDType); - } - } - return ifacerefp; + } + } + return ifacerefp; } void computeIfaceVarSyms() { - for (IfaceVarSyms::iterator it = m_ifaceVarSyms.begin(); it != m_ifaceVarSyms.end(); ++it) { - VSymEnt* varSymp = *it; + for (IfaceVarSyms::iterator it = m_ifaceVarSyms.begin(); it != m_ifaceVarSyms.end(); ++it) { + VSymEnt* varSymp = *it; AstVar* varp = varSymp ? VN_CAST(varSymp->nodep(), Var) : NULL; UINFO(9, " insAllIface se"<subDTypep()); - if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!"); - if (!ifacerefp->ifaceViaCellp()) { - if (!ifacerefp->cellp()) { // Probably a NotFoundModule, or a normal module if made mistake - ifacerefp->v3error("Cannot find file containing interface: "<ifaceName())); - continue; - } else { - ifacerefp->v3fatalSrc("Unlinked interface"); - } - } else if (ifacerefp->ifaceViaCellp()->dead()) { - ifacerefp->v3error("Parent cell's interface is not found: "<ifaceName())); - continue; - } - VSymEnt* ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp()); - VSymEnt* ifOrPortSymp = ifaceSymp; - // Link Modport names to the Modport Node under the Interface - if (ifacerefp->isModport()) { - VSymEnt* foundp = ifaceSymp->findIdFallback(ifacerefp->modportName()); - bool ok = false; - if (foundp) { + AstIfaceRefDType* ifacerefp = ifaceRefFromArray(varp->subDTypep()); + if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!"); + if (!ifacerefp->ifaceViaCellp()) { + if (!ifacerefp->cellp()) { // Probably a NotFoundModule, or a normal module if made mistake + ifacerefp->v3error("Cannot find file containing interface: " + <ifaceName())); + continue; + } else { + ifacerefp->v3fatalSrc("Unlinked interface"); + } + } else if (ifacerefp->ifaceViaCellp()->dead()) { + ifacerefp->v3error("Parent cell's interface is not found: " + <ifaceName())); + continue; + } + VSymEnt* ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp()); + VSymEnt* ifOrPortSymp = ifaceSymp; + // Link Modport names to the Modport Node under the Interface + if (ifacerefp->isModport()) { + VSymEnt* foundp = ifaceSymp->findIdFallback(ifacerefp->modportName()); + bool ok = false; + if (foundp) { if (AstModport* modportp = VN_CAST(foundp->nodep(), Modport)) { - UINFO(4,"Link Modport: "<modportp(modportp); - ifOrPortSymp = foundp; - ok = true; - } - } - if (!ok) ifacerefp->v3error("Modport not found under interface '" - <prettyName(ifacerefp->ifaceName()) - <<"': "<prettyName(ifacerefp->modportName())); - } - // Alias won't expand until interfaces and modport names are known; see notes at top - insertScopeAlias(SAMN_IFTOP, varSymp, ifOrPortSymp); - } - m_ifaceVarSyms.clear(); + UINFO(4,"Link Modport: "<modportp(modportp); + ifOrPortSymp = foundp; + ok = true; + } + } + if (!ok) ifacerefp->v3error("Modport not found under interface '" + <prettyName(ifacerefp->ifaceName()) + <<"': "<prettyName(ifacerefp->modportName())); + } + // Alias won't expand until interfaces and modport names are known; see notes at top + insertScopeAlias(SAMN_IFTOP, varSymp, ifOrPortSymp); + } + m_ifaceVarSyms.clear(); } void insertScopeAlias(SAMNum samn, VSymEnt* lhsp, VSymEnt* rhsp) { - // Track and later insert scope aliases; an interface referenced by a child cell connecting to that interface - // Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell + // Track and later insert scope aliases; an interface referenced by + // a child cell connecting to that interface + // Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell UINFO(9," insertScopeAlias se"<nodep(), Cell) && !VN_IS(VN_CAST(rhsp->nodep(), Cell)->modp(), Iface)) { - rhsp->nodep()->v3fatalSrc("Got a non-IFACE alias RHS"); - } - m_scopeAliasMap[samn].insert(make_pair(lhsp, rhsp)); + rhsp->nodep()->v3fatalSrc("Got a non-IFACE alias RHS"); + } + m_scopeAliasMap[samn].insert(make_pair(lhsp, rhsp)); } void computeScopeAliases() { - UINFO(9,"computeIfaceAliases\n"); - for (int samn=0; samnfirst; - VSymEnt* srcp = lhsp; - while (1) { // Follow chain of aliases up to highest level non-alias - ScopeAliasMap::iterator it2 = m_scopeAliasMap[samn].find(srcp); - if (it2 != m_scopeAliasMap[samn].end()) { srcp = it2->second; continue; } - else break; - } - UINFO(9," iiasa: Insert alias se"<nodep()->typeName() - <<") <- se"<nodep()<importFromIface(symsp(), srcp); - // Allow access to objects not permissible to be listed in a modport + UINFO(9,"computeIfaceAliases\n"); + for (int samn=0; samnfirst; + VSymEnt* srcp = lhsp; + while (1) { // Follow chain of aliases up to highest level non-alias + ScopeAliasMap::iterator it2 = m_scopeAliasMap[samn].find(srcp); + if (it2 != m_scopeAliasMap[samn].end()) { srcp = it2->second; continue; } + else break; + } + UINFO(9," iiasa: Insert alias se"<nodep()->typeName() + <<") <- se"<nodep()<importFromIface(symsp(), srcp); + // Allow access to objects not permissible to be listed in a modport if (VN_IS(srcp->nodep(), Modport)) { - lhsp->importFromIface(symsp(), srcp->parentp(), true); - } - } - //m_scopeAliasMap[samn].clear(); // Done with it, but put into debug file - } + lhsp->importFromIface(symsp(), srcp->parentp(), true); + } + } + //m_scopeAliasMap[samn].clear(); // Done with it, but put into debug file + } } private: VSymEnt* findWithAltFallback(VSymEnt* symp, const string& name, const string& altname) { - VSymEnt* findp = symp->findIdFallback(name); - if (findp) return findp; - if (altname != "") { - UINFO(8," alt fallback\n"); - findp = symp->findIdFallback(altname); - } - return findp; + VSymEnt* findp = symp->findIdFallback(name); + if (findp) return findp; + if (altname != "") { + UINFO(8," alt fallback\n"); + findp = symp->findIdFallback(altname); + } + return findp; } public: VSymEnt* findDotted(VSymEnt* lookupSymp, const string& dotname, - string& baddot, VSymEnt*& okSymp) { - // Given a dotted hierarchy name, return where in scope it is - // Note when dotname=="" we just fall through and return lookupSymp + string& baddot, VSymEnt*& okSymp) { + // Given a dotted hierarchy name, return where in scope it is + // Note when dotname=="" we just fall through and return lookupSymp UINFO(8," dottedFind se"<nodep(), Cell) : NULL; // Replicated below AstCellInline* inlinep = lookupSymp ? VN_CAST(lookupSymp->nodep(), CellInline) : NULL; // Replicated below - if (VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) { - lookupSymp = findSymp; - } - // Check this module - cur modname - else if ((cellp && cellp->modp()->origName() == ident) - || (inlinep && inlinep->origModName() == ident)) {} - // Move up and check cellname + modname - else { - bool crossedCell = false; // Crossed a cell boundary - while (lookupSymp) { - lookupSymp = lookupSymp->parentp(); + if (VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) { + lookupSymp = findSymp; + } + // Check this module - cur modname + else if ((cellp && cellp->modp()->origName() == ident) + || (inlinep && inlinep->origModName() == ident)) {} + // Move up and check cellname + modname + else { + bool crossedCell = false; // Crossed a cell boundary + while (lookupSymp) { + lookupSymp = lookupSymp->parentp(); cellp = lookupSymp ? VN_CAST(lookupSymp->nodep(), Cell) : NULL; // Replicated above inlinep = lookupSymp ? VN_CAST(lookupSymp->nodep(), CellInline) : NULL; // Replicated above - if (lookupSymp) { - UINFO(9,"\t\tUp to "<modp()->origName() == ident) - || (inlinep && inlinep->origModName() == ident)) { - break; - } - else if (VSymEnt* findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) { - lookupSymp = findSymp; + if (lookupSymp) { + UINFO(9,"\t\tUp to "<modp()->origName() == ident) + || (inlinep && inlinep->origModName() == ident)) { + break; + } + else if (VSymEnt* findSymp + = findWithAltFallback(lookupSymp, ident, altIdent)) { + lookupSymp = findSymp; if (crossedCell && VN_IS(lookupSymp->nodep(), Var)) { - UINFO(9,"\t\tNot found but matches var name in parent "<symPrefix()=="") ? "" : " as ") - <<((lookupSymp->symPrefix()=="") ? "" : lookupSymp->symPrefix()+dotname) - <<" at se"<symPrefix(); - VSymEnt* foundp = NULL; - while (!foundp) { - foundp = lookupSymp->findIdFallback(prefix + dotname); // Might be NULL - if (prefix == "") { - break; - } - prefix = removeLastInlineScope(prefix); - } - if (!foundp) baddot = dotname; - return foundp; + <<((lookupSymp->symPrefix()=="") ? "" : " as ") + <<((lookupSymp->symPrefix()=="") ? "" : lookupSymp->symPrefix()+dotname) + <<" at se"<symPrefix(); + VSymEnt* foundp = NULL; + while (!foundp) { + foundp = lookupSymp->findIdFallback(prefix + dotname); // Might be NULL + if (prefix == "") { + break; + } + prefix = removeLastInlineScope(prefix); + } + if (!foundp) baddot = dotname; + return foundp; } }; @@ -588,31 +607,31 @@ LinkDotState* LinkDotState::s_errorThisp = NULL; class LinkDotFindVisitor : public AstNVisitor { // STATE - LinkDotState* m_statep; // State to pass between visitors, including symbol table - AstPackage* m_packagep; // Current package - VSymEnt* m_modSymp; // Symbol Entry for current module - VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert - string m_scope; // Scope text - AstBegin* m_beginp; // Current Begin/end block - AstNodeFTask* m_ftaskp; // Current function/task - bool m_inGenerate; // Inside a generate - bool m_inRecursion; // Inside a recursive module - int m_paramNum; // Parameter number, for position based connection - int m_beginNum; // Begin block number, 0=none seen - int m_modBeginNum; // Begin block number in module, 0=none seen + LinkDotState* m_statep; // State to pass between visitors, including symbol table + AstPackage* m_packagep; // Current package + VSymEnt* m_modSymp; // Symbol Entry for current module + VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert + string m_scope; // Scope text + AstBegin* m_beginp; // Current Begin/end block + AstNodeFTask* m_ftaskp; // Current function/task + bool m_inGenerate; // Inside a generate + bool m_inRecursion; // Inside a recursive module + int m_paramNum; // Parameter number, for position based connection + int m_beginNum; // Begin block number, 0=none seen + int m_modBeginNum; // Begin block number in module, 0=none seen // METHODS int debug() { return LinkDotState::debug(); } virtual AstConst* parseParamLiteral(FileLine* fl, const string& literal) { - bool success = false; + bool success = false; if (literal[0] == '"') { - // This is a string + // This is a string string v = literal.substr(1, literal.find('"', 1) - 1); return new AstConst(fl, AstConst::VerilogStringLiteral(), v); } else if ((literal.find('.') != string::npos) || (literal.find('e') != string::npos)) { - // This may be a real + // This may be a real double v = V3ParseImp::parseDouble(literal.c_str(), literal.length(), &success); if (success) { return new AstConst(fl, AstConst::RealDouble(), v); @@ -634,442 +653,464 @@ class LinkDotFindVisitor : public AstNVisitor { return new AstConst(fl, AstConst::StringToParse(), literal.c_str()); } } - return NULL; + return NULL; } // VISITs virtual void visit(AstNetlist* nodep) { - // Process $unit or other packages - // Not needed - dotted references not allowed from inside packages - //for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { + // Process $unit or other packages + // Not needed - dotted references not allowed from inside packages + //for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); + // nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { // if (VN_IS(nodep, Package)) {}} - m_statep->insertDUnit(nodep); + m_statep->insertDUnit(nodep); - // First back iterate, to find all packages. Backward as must do base packages before using packages + // First back iterate, to find all packages. Backward as must do base + // packages before using packages iterateChildrenBackwards(nodep); - // The first module in the list is always the top module (sorted before this is called). - // This may not be the module with isTop() set, as early in the steps, - // wrapTop may have not been created yet. - AstNodeModule* topmodp = nodep->modulesp(); - if (!topmodp) { - nodep->v3error("No top level module found"); - } else { - UINFO(8,"Top Module: "<insertTopCell(topmodp, m_scope); - { + // The first module in the list is always the top module (sorted before this is called). + // This may not be the module with isTop() set, as early in the steps, + // wrapTop may have not been created yet. + AstNodeModule* topmodp = nodep->modulesp(); + if (!topmodp) { + nodep->v3error("No top level module found"); + } else { + UINFO(8,"Top Module: "<insertTopCell(topmodp, m_scope); + { iterate(topmodp); - } - m_scope = ""; - m_curSymp = m_modSymp = NULL; - } + } + m_scope = ""; + m_curSymp = m_modSymp = NULL; + } } virtual void visit(AstTypeTable* nodep) {} virtual void visit(AstNodeModule* nodep) { - // Called on top module from Netlist, other modules from the cell creating them, - // and packages - UINFO(8," "<forPrearray() && VN_IS(nodep, Package)); - bool doit = (m_modSymp || standalonePkg); - string oldscope = m_scope; - VSymEnt* oldModSymp = m_modSymp; - VSymEnt* oldCurSymp = m_curSymp; + bool doit = (m_modSymp || standalonePkg); + string oldscope = m_scope; + VSymEnt* oldModSymp = m_modSymp; + VSymEnt* oldCurSymp = m_curSymp; int oldParamNum = m_paramNum; int oldBeginNum = m_beginNum; int oldModBeginNum = m_modBeginNum; - if (doit && nodep->user2()) { - nodep->v3error("Unsupported: Identically recursive module (module instantiates itself, without changing parameters): " - <origName())); - } else if (doit) { - UINFO(4," Link Module: "<dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?"); - VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp(); + if (doit && nodep->user2()) { + nodep->v3error("Unsupported: Identically recursive module (module instantiates itself, without changing parameters): " + <origName())); + } else if (doit) { + UINFO(4," Link Module: "<dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?"); + VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp(); m_packagep = VN_CAST(nodep, Package); - if (standalonePkg) { - if (m_packagep->isDollarUnit()) { - m_curSymp = m_modSymp = m_statep->dunitEntp(); - nodep->user1p(m_curSymp); - } else { - m_scope = nodep->name(); - m_curSymp = m_modSymp = m_statep->insertBlock(upperSymp, nodep->name()+"::", nodep, m_packagep); - UINFO(9, "New module scope "<user2(true); + if (standalonePkg) { + if (m_packagep->isDollarUnit()) { + m_curSymp = m_modSymp = m_statep->dunitEntp(); + nodep->user1p(m_curSymp); + } else { + m_scope = nodep->name(); + m_curSymp = m_modSymp + = m_statep->insertBlock(upperSymp, nodep->name()+"::", nodep, m_packagep); + UINFO(9, "New module scope "<user2(true); iterateChildren(nodep); - nodep->user2(false); - nodep->user4(true); - // Interfaces need another pass when signals are resolved + nodep->user2(false); + nodep->user4(true); + // Interfaces need another pass when signals are resolved if (AstIface* ifacep = VN_CAST(nodep, Iface)) { - m_statep->insertIfaceModSym(ifacep, m_curSymp); - } - } else { //!doit - // Will be optimized away later - // Can't remove now, as our backwards iterator will throw up - UINFO(5, "Module not under any CELL or top - dead module: "<insertIfaceModSym(ifacep, m_curSymp); + } + } else { // !doit + // Will be optimized away later + // Can't remove now, as our backwards iterator will throw up + UINFO(5, "Module not under any CELL or top - dead module: "<forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); - // Ignored. Processed in next step + if (!m_statep->forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); + // Ignored. Processed in next step } virtual void visit(AstCell* nodep) { - UINFO(5," CELL under "<recursive() && m_inRecursion) return; + UINFO(5," CELL under "<recursive() && m_inRecursion) return; iterateChildren(nodep); - // Recurse in, preserving state - string oldscope = m_scope; - AstBegin* oldbeginp = m_beginp; - VSymEnt* oldModSymp = m_modSymp; - VSymEnt* oldCurSymp = m_curSymp; - int oldParamNum = m_paramNum; - bool oldRecursion = m_inRecursion; - // Where do we add it? - VSymEnt* aboveSymp = m_curSymp; - string origname = AstNode::dedotName(nodep->name()); - string::size_type pos; + // Recurse in, preserving state + string oldscope = m_scope; + AstBegin* oldbeginp = m_beginp; + VSymEnt* oldModSymp = m_modSymp; + VSymEnt* oldCurSymp = m_curSymp; + int oldParamNum = m_paramNum; + bool oldRecursion = m_inRecursion; + // Where do we add it? + VSymEnt* aboveSymp = m_curSymp; + string origname = AstNode::dedotName(nodep->name()); + string::size_type pos; if ((pos = origname.rfind('.')) != string::npos) { - // Flattened, find what CellInline it should live under - string scope = origname.substr(0,pos); - string baddot; - VSymEnt* okSymp; - aboveSymp = m_statep->findDotted(aboveSymp, scope, baddot, okSymp); - if (!aboveSymp) { - nodep->v3fatalSrc("Can't find cell insertion point at '"<prettyName()); - } - } - { - m_scope = m_scope+"."+nodep->name(); - m_curSymp = m_modSymp = m_statep->insertCell(aboveSymp, m_modSymp, nodep, m_scope); - m_beginp = NULL; - m_inRecursion = nodep->recursive(); - // We don't report NotFoundModule, as may be a unused module in a generate + // Flattened, find what CellInline it should live under + string scope = origname.substr(0, pos); + string baddot; + VSymEnt* okSymp; + aboveSymp = m_statep->findDotted(aboveSymp, scope, baddot, okSymp); + if (!aboveSymp) { + nodep->v3fatalSrc("Can't find cell insertion point at '" + <prettyName()); + } + } + { + m_scope = m_scope+"."+nodep->name(); + m_curSymp = m_modSymp = m_statep->insertCell(aboveSymp, m_modSymp, nodep, m_scope); + m_beginp = NULL; + m_inRecursion = nodep->recursive(); + // We don't report NotFoundModule, as may be a unused module in a generate if (nodep->modp()) iterate(nodep->modp()); - } - m_scope = oldscope; - m_beginp = oldbeginp; - m_modSymp = oldModSymp; - m_curSymp = oldCurSymp; - m_paramNum = oldParamNum; - m_inRecursion = oldRecursion; + } + m_scope = oldscope; + m_beginp = oldbeginp; + m_modSymp = oldModSymp; + m_curSymp = oldCurSymp; + m_paramNum = oldParamNum; + m_inRecursion = oldRecursion; } virtual void visit(AstCellInline* nodep) { - UINFO(5," CELLINLINE under "<name(); - string::size_type pos; - if ((pos=dottedname.rfind("__DOT__")) != string::npos) { - string dotted = dottedname.substr(0, pos); - string ident = dottedname.substr(pos+strlen("__DOT__")); - string baddot; - VSymEnt* okSymp; - aboveSymp = m_statep->findDotted(aboveSymp, dotted, baddot, okSymp); - if (!aboveSymp) { - nodep->v3fatalSrc("Can't find cellinline insertion point at '"<prettyName()); - } - m_statep->insertInline(aboveSymp, m_modSymp, nodep, ident); - } else { // No __DOT__, just directly underneath - m_statep->insertInline(aboveSymp, m_modSymp, nodep, nodep->name()); - } + UINFO(5," CELLINLINE under "<name(); + string::size_type pos; + if ((pos = dottedname.rfind("__DOT__")) != string::npos) { + string dotted = dottedname.substr(0, pos); + string ident = dottedname.substr(pos+strlen("__DOT__")); + string baddot; + VSymEnt* okSymp; + aboveSymp = m_statep->findDotted(aboveSymp, dotted, baddot, okSymp); + if (!aboveSymp) { + nodep->v3fatalSrc("Can't find cellinline insertion point at '" + <prettyName()); + } + m_statep->insertInline(aboveSymp, m_modSymp, nodep, ident); + } else { // No __DOT__, just directly underneath + m_statep->insertInline(aboveSymp, m_modSymp, nodep, nodep->name()); + } } virtual void visit(AstDefParam* nodep) { - nodep->user1p(m_curSymp); + nodep->user1p(m_curSymp); iterateChildren(nodep); } virtual void visit(AstGenerate* nodep) { - // Begin: ... blocks often replicate under genif/genfor, so simply suppress duplicate checks - // See t_gen_forif.v for an example. - bool lastInGen = m_inGenerate; - { - m_inGenerate = true; + // Begin: ... blocks often replicate under genif/genfor, so simply + // suppress duplicate checks. See t_gen_forif.v for an example. + bool lastInGen = m_inGenerate; + { + m_inGenerate = true; iterateChildren(nodep); - } - m_inGenerate = lastInGen; + } + m_inGenerate = lastInGen; } virtual void visit(AstBegin* nodep) { - UINFO(5," "<forPrimary() && !nodep->user4SetOnce()) { - if (nodep->name() == "genblk") { - ++m_beginNum; - nodep->name(nodep->name()+cvtToStr(m_beginNum)); - } - // Just for loop index, make special name. The [00] is so it will "dearray" to same - // name as after we expand the GENFOR - if (nodep->genforp()) nodep->name(nodep->name()); - } - // All blocks are numbered in the standard, IE we start with "genblk1" even if only one. - if (nodep->name()=="" && nodep->unnamed()) { - // Unnamed blocks are only important when they contain var - // decls, so search for them. (Otherwise adding all the - // unnamed#'s would just confuse tracing variables in - // places such as tasks, where "task ...; begin ... end" - // are common. - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { + UINFO(5," "<forPrimary() && !nodep->user4SetOnce()) { + if (nodep->name() == "genblk") { + ++m_beginNum; + nodep->name(nodep->name()+cvtToStr(m_beginNum)); + } + // Just for loop index, make special name. The [00] is so it will "dearray" to same + // name as after we expand the GENFOR + if (nodep->genforp()) nodep->name(nodep->name()); + } + // All blocks are numbered in the standard, IE we start with "genblk1" even if only one. + if (nodep->name()=="" && nodep->unnamed()) { + // Unnamed blocks are only important when they contain var + // decls, so search for them. (Otherwise adding all the + // unnamed#'s would just confuse tracing variables in + // places such as tasks, where "task ...; begin ... end" + // are common. + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (VN_IS(stmtp, Var)) { - ++m_modBeginNum; - nodep->name("unnamedblk"+cvtToStr(m_modBeginNum)); - break; - } - } - } - int oldNum = m_beginNum; - AstBegin* oldbegin = m_beginp; - VSymEnt* oldCurSymp = m_curSymp; - { - m_beginNum = 0; - m_beginp = nodep; - m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep); - m_curSymp->fallbackp(oldCurSymp); - // Iterate + ++m_modBeginNum; + nodep->name("unnamedblk"+cvtToStr(m_modBeginNum)); + break; + } + } + } + int oldNum = m_beginNum; + AstBegin* oldbegin = m_beginp; + VSymEnt* oldCurSymp = m_curSymp; + { + m_beginNum = 0; + m_beginp = nodep; + m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep); + m_curSymp->fallbackp(oldCurSymp); + // Iterate iterateChildren(nodep); - } - m_curSymp = oldCurSymp; - m_beginp = oldbegin; - m_beginNum = oldNum; + } + m_curSymp = oldCurSymp; + m_beginp = oldbegin; + m_beginNum = oldNum; } virtual void visit(AstNodeFTask* nodep) { - // NodeTask: Remember its name for later resolution - UINFO(5," "<v3fatalSrc("Function/Task not under module?"); - // Remember the existing symbol table scope - VSymEnt* oldCurSymp = m_curSymp; - { - // Create symbol table for the task's vars - m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep); - m_curSymp->fallbackp(oldCurSymp); - // Convert the func's range to the output variable - // This should probably be done in the Parser instead, as then we could - // just attact normal signal attributes to it. - if (nodep->fvarp() + // NodeTask: Remember its name for later resolution + UINFO(5," "<v3fatalSrc("Function/Task not under module?"); + // Remember the existing symbol table scope + VSymEnt* oldCurSymp = m_curSymp; + { + // Create symbol table for the task's vars + m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep); + m_curSymp->fallbackp(oldCurSymp); + // Convert the func's range to the output variable + // This should probably be done in the Parser instead, as then we could + // just attact normal signal attributes to it. + if (nodep->fvarp() && !VN_IS(nodep->fvarp(), Var)) { AstNodeDType* dtypep = VN_CAST(nodep->fvarp(), NodeDType); - // If unspecified, function returns one bit; however when we support NEW() it could - // also return the class reference. + // If unspecified, function returns one bit; however when we + // support NEW() it could also return the class reference. if (dtypep) dtypep->unlinkFrBack(); else dtypep = new AstBasicDType(nodep->fileline(), AstBasicDTypeKwd::LOGIC); AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::VAR, nodep->name(), VFlagChildDType(), dtypep); // Not dtype resolved yet newvarp->direction(VDirection::OUTPUT); - newvarp->funcReturn(true); - newvarp->trace(false); // Not user visible - newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); - nodep->addFvarp(newvarp); - // Explicit insert required, as the var name shadows the upper level's task name - m_statep->insertSym(m_curSymp, newvarp->name(), newvarp, NULL/*packagep*/); - } - m_ftaskp = nodep; + newvarp->funcReturn(true); + newvarp->trace(false); // Not user visible + newvarp->attrIsolateAssign(nodep->attrIsolateAssign()); + nodep->addFvarp(newvarp); + // Explicit insert required, as the var name shadows the upper level's task name + m_statep->insertSym(m_curSymp, newvarp->name(), newvarp, NULL/*packagep*/); + } + m_ftaskp = nodep; iterateChildren(nodep); - m_ftaskp = NULL; - } - m_curSymp = oldCurSymp; + m_ftaskp = NULL; + } + m_curSymp = oldCurSymp; } virtual void visit(AstVar* nodep) { - // Var: Remember its name for later resolution - if (!m_curSymp || !m_modSymp) nodep->v3fatalSrc("Var not under module?"); + // Var: Remember its name for later resolution + if (!m_curSymp || !m_modSymp) nodep->v3fatalSrc("Var not under module?"); iterateChildren(nodep); - if (!m_statep->forScopeCreation()) { - // Find under either a task or the module's vars - VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); - if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname? + if (!m_statep->forScopeCreation()) { + // Find under either a task or the module's vars + VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); + if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) { + foundp = m_modSymp; // Conflicts with modname? + } AstVar* findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : NULL; - bool ins=false; - if (!foundp) { - ins=true; - } else if (!findvarp && foundp && m_curSymp->findIdFlat(nodep->name())) { - nodep->v3error("Unsupported in C: Variable has same name as " - <nodep())<<": "<prettyName()); - } else if (findvarp != nodep) { - UINFO(4,"DupVar: "<nodep()<findIdFlat(nodep->name())) { + nodep->v3error("Unsupported in C: Variable has same name as " + <nodep()) + <<": "<prettyName()); + } else if (findvarp != nodep) { + UINFO(4,"DupVar: "<nodep()<parentp())<parentp() == m_curSymp // Only when on same level - && !foundp->imported()) { // and not from package - if ((findvarp->isIO() && nodep->isSignal()) - || (findvarp->isSignal() && nodep->isIO())) { - findvarp->combineType(nodep); - nodep->fileline()->modifyStateInherit(nodep->fileline()); + if (foundp && foundp->parentp() == m_curSymp // Only when on same level + && !foundp->imported()) { // and not from package + if ((findvarp->isIO() && nodep->isSignal()) + || (findvarp->isSignal() && nodep->isIO())) { + findvarp->combineType(nodep); + nodep->fileline()->modifyStateInherit(nodep->fileline()); AstBasicDType* bdtypep = VN_CAST(findvarp->childDTypep(), BasicDType); - if (bdtypep && bdtypep->implicit()) { - // Then have "input foo" and "real foo" so the dtype comes from the other side. - AstNodeDType* newdtypep = nodep->subDTypep(); - if (!newdtypep || !nodep->childDTypep()) findvarp->v3fatalSrc("No child type?"); - bdtypep->unlinkFrBack()->deleteTree(); - newdtypep->unlinkFrBack(); - findvarp->childDTypep(newdtypep); - } - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } else { - nodep->v3error("Duplicate declaration of signal: "<prettyName()<warnMore()<<"... Location of original declaration"); - } - } else { - // User can disable the message at either point - if (!(m_ftaskp && m_ftaskp->dpiImport()) - && (!m_ftaskp || m_ftaskp != foundp->nodep()) // Not the function's variable hiding function - && !nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) - && !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { - nodep->v3warn(VARHIDDEN,"Declaration of signal hides declaration in upper scope: "<prettyName()<nodep()->warnMore()<<"... Location of original declaration"); - } - ins = true; - } - } - if (ins) { - if (m_statep->forPrimary() && nodep->isGParam() - && (m_statep->rootEntp()->nodep() == m_modSymp->parentp()->nodep())) { - // This is the toplevel module. Check for command line overwrites of parameters - // We first search if the parameter is overwritten and then replace it with a - // new value. It will keep the same FileLine information. - if (v3Global.opt.hasParameter(nodep->name())) { - AstVar* newp = new AstVar(nodep->fileline(), AstVarType(AstVarType::GPARAM), - nodep->name(), nodep); - string svalue = v3Global.opt.parameter(nodep->name()); - if (AstNode* valuep = parseParamLiteral(nodep->fileline(), svalue)) { - newp->valuep(valuep); - UINFO(9," replace parameter "<replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - nodep = newp; - } - } - } - VSymEnt* insp = m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); - if (m_statep->forPrimary() && nodep->isGParam()) { - m_paramNum++; - VSymEnt* symp = m_statep->insertSym(m_curSymp, "__paramNumber" + cvtToStr(m_paramNum), - nodep, m_packagep); - symp->exported(false); - } - AstIfaceRefDType* ifacerefp = LinkDotState::ifaceRefFromArray(nodep->subDTypep()); - if (ifacerefp) { - // Can't resolve until interfaces and modport names are known; see notes at top - m_statep->insertIfaceVarSym(insp); - } - } - } + if (bdtypep && bdtypep->implicit()) { + // Then have "input foo" and "real foo" so the + // dtype comes from the other side. + AstNodeDType* newdtypep = nodep->subDTypep(); + if (!newdtypep || !nodep->childDTypep()) findvarp->v3fatalSrc("No child type?"); + bdtypep->unlinkFrBack()->deleteTree(); + newdtypep->unlinkFrBack(); + findvarp->childDTypep(newdtypep); + } + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } else { + nodep->v3error("Duplicate declaration of signal: " + <prettyName()<warnMore()<<"... Location of original declaration"); + } + } else { + // User can disable the message at either point + if (!(m_ftaskp && m_ftaskp->dpiImport()) + && (!m_ftaskp || m_ftaskp != foundp->nodep()) // Not the function's variable hiding function + && !nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) + && !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { + nodep->v3warn(VARHIDDEN, "Declaration of signal hides declaration in upper scope: " + <prettyName()<nodep()->warnMore() + <<"... Location of original declaration"); + } + ins = true; + } + } + if (ins) { + if (m_statep->forPrimary() && nodep->isGParam() + && (m_statep->rootEntp()->nodep() == m_modSymp->parentp()->nodep())) { + // This is the toplevel module. Check for command line overwrites of parameters + // We first search if the parameter is overwritten and then replace it with a + // new value. It will keep the same FileLine information. + if (v3Global.opt.hasParameter(nodep->name())) { + AstVar* newp = new AstVar(nodep->fileline(), + AstVarType(AstVarType::GPARAM), + nodep->name(), nodep); + string svalue = v3Global.opt.parameter(nodep->name()); + if (AstNode* valuep = parseParamLiteral(nodep->fileline(), svalue)) { + newp->valuep(valuep); + UINFO(9," replace parameter "<replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + nodep = newp; + } + } + } + VSymEnt* insp = m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); + if (m_statep->forPrimary() && nodep->isGParam()) { + m_paramNum++; + VSymEnt* symp = m_statep->insertSym(m_curSymp, + "__paramNumber"+cvtToStr(m_paramNum), + nodep, m_packagep); + symp->exported(false); + } + AstIfaceRefDType* ifacerefp = LinkDotState::ifaceRefFromArray(nodep->subDTypep()); + if (ifacerefp) { + // Can't resolve until interfaces and modport names are + // known; see notes at top + m_statep->insertIfaceVarSym(insp); + } + } + } } virtual void visit(AstTypedef* nodep) { - // Remember its name for later resolution - if (!m_curSymp) nodep->v3fatalSrc("Typedef not under module?"); + // Remember its name for later resolution + if (!m_curSymp) nodep->v3fatalSrc("Typedef not under module?"); iterateChildren(nodep); - m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); + m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); } virtual void visit(AstParamTypeDType* nodep) { - if (!m_curSymp) nodep->v3fatalSrc("Parameter type not under module?"); + if (!m_curSymp) nodep->v3fatalSrc("Parameter type not under module?"); iterateChildren(nodep); - m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); + m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); } virtual void visit(AstCFunc* nodep) { - // For dotted resolution, ignore all AstVars under functions, otherwise shouldn't exist - if (m_statep->forScopeCreation()) nodep->v3fatalSrc("No CFuncs expected in tree yet"); + // For dotted resolution, ignore all AstVars under functions, otherwise shouldn't exist + if (m_statep->forScopeCreation()) nodep->v3fatalSrc("No CFuncs expected in tree yet"); } virtual void visit(AstEnumItem* nodep) { - // EnumItem: Remember its name for later resolution + // EnumItem: Remember its name for later resolution iterateChildren(nodep); - // Find under either a task or the module's vars - VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); - if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) foundp = m_modSymp; // Conflicts with modname? + // Find under either a task or the module's vars + VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); + if (!foundp && m_modSymp && nodep->name() == m_modSymp->nodep()->name()) { + foundp = m_modSymp; // Conflicts with modname? + } AstEnumItem* findvarp = foundp ? VN_CAST(foundp->nodep(), EnumItem) : NULL; - bool ins=false; - if (!foundp) { - ins=true; - } else if (findvarp != nodep) { - UINFO(4,"DupVar: "<parentp() == m_curSymp // Only when on same level - && !foundp->imported()) { // and not from package - nodep->v3error("Duplicate declaration of enum value: "<prettyName()<warnMore()<<"... Location of original declaration"); - } else { - // User can disable the message at either point - if (!nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) - && !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { - nodep->v3warn(VARHIDDEN,"Declaration of enum value hides declaration in upper scope: "<prettyName()<nodep()->warnMore()<<"... Location of original declaration"); - } - ins = true; - } - } - if (ins) { - m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); - } + bool ins = false; + if (!foundp) { + ins = true; + } else if (findvarp != nodep) { + UINFO(4,"DupVar: "<parentp() == m_curSymp // Only when on same level + && !foundp->imported()) { // and not from package + nodep->v3error("Duplicate declaration of enum value: "<prettyName()<warnMore()<<"... Location of original declaration"); + } else { + // User can disable the message at either point + if (!nodep->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN) + && !foundp->nodep()->fileline()->warnIsOff(V3ErrorCode::VARHIDDEN)) { + nodep->v3warn(VARHIDDEN, "Declaration of enum value hides declaration in upper scope: " + <prettyName()<nodep()->warnMore() + <<"... Location of original declaration"); + } + ins = true; + } + } + if (ins) { + m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep); + } } virtual void visit(AstPackageImport* nodep) { - UINFO(4," Link: "<getNodeSym(nodep->packagep()); + UINFO(4," Link: "<getNodeSym(nodep->packagep()); if (nodep->name()=="*") { if (m_curSymp == m_statep->dunitEntp()) { - nodep->v3warn(IMPORTSTAR,"Import::* in $unit scope may pollute global namespace"); + nodep->v3warn(IMPORTSTAR, "Import::* in $unit scope may pollute global namespace"); } } else { - VSymEnt* impp = srcp->findIdFlat(nodep->name()); - if (!impp) { - nodep->v3error("Import object not found: "<packagep()->prettyName()<<"::"<prettyName()); - } - } - m_curSymp->importFromPackage(m_statep->symsp(), srcp, nodep->name()); - UINFO(9," Link Done: "<findIdFlat(nodep->name()); + if (!impp) { + nodep->v3error("Import object not found: " + <packagep()->prettyName()<<"::"<prettyName()); + } + } + m_curSymp->importFromPackage(m_statep->symsp(), srcp, nodep->name()); + UINFO(9," Link Done: "<getNodeSym(nodep->packagep()); - if (nodep->name()!="*") { - VSymEnt* impp = srcp->findIdFlat(nodep->name()); - if (!impp) { - nodep->v3error("Export object not found: "<packagep()->prettyName()<<"::"<prettyName()); - } - } - m_curSymp->exportFromPackage(m_statep->symsp(), srcp, nodep->name()); - UINFO(9," Link Done: "<getNodeSym(nodep->packagep()); + if (nodep->name()!="*") { + VSymEnt* impp = srcp->findIdFlat(nodep->name()); + if (!impp) { + nodep->v3error("Export object not found: " + <packagep()->prettyName()<<"::"<prettyName()); + } + } + m_curSymp->exportFromPackage(m_statep->symsp(), srcp, nodep->name()); + UINFO(9," Link Done: "<exportStarStar(m_statep->symsp()); - // No longer needed, but can't delete until any multi-instantiated modules are expanded + UINFO(4," Link: "<exportStarStar(m_statep->symsp()); + // No longer needed, but can't delete until any multi-instantiated modules are expanded } virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS LinkDotFindVisitor(AstNetlist* rootp, LinkDotState* statep) { - UINFO(4,__FUNCTION__<<": "< See LinkDotState - // *::user2p() -> See LinkDotState - // *::user4() -> See LinkDotState + // *::user1p() -> See LinkDotState + // *::user2p() -> See LinkDotState + // *::user4() -> See LinkDotState // STATE - LinkDotState* m_statep; // State to pass between visitors, including symbol table - AstNodeModule* m_modp; // Current module + LinkDotState* m_statep; // State to pass between visitors, including symbol table + AstNodeModule* m_modp; // Current module int debug() { return LinkDotState::debug(); } void pinImplicitExprRecurse(AstNode* nodep) { - // Under a pin, Check interconnect expression for a pin reference or a concat. - // Create implicit variable as needed + // Under a pin, Check interconnect expression for a pin reference or a concat. + // Create implicit variable as needed if (VN_IS(nodep, Dot)) { // Not creating a simple implied type, - // and implying something else would just confuse later errors - } + // and implying something else would just confuse later errors + } else if (VN_IS(nodep, VarRef) || VN_IS(nodep, ParseRef)) { - // To prevent user errors, we should only do single bit - // implicit vars, however some netlists (MIPS) expect single - // bit implicit wires to get created with range 0:0 etc. - m_statep->implicitOkAdd(m_modp, nodep->name()); - } - // These are perhaps a little too generous, as a SELect of siga[sigb] - // perhaps shouldn't create an implicit variable. But, we've warned... - else { - if (nodep->op1p()) pinImplicitExprRecurse(nodep->op1p()); - if (nodep->op2p()) pinImplicitExprRecurse(nodep->op2p()); - if (nodep->op3p()) pinImplicitExprRecurse(nodep->op3p()); - if (nodep->op4p()) pinImplicitExprRecurse(nodep->op4p()); - if (nodep->nextp()) pinImplicitExprRecurse(nodep->nextp()); - } + // To prevent user errors, we should only do single bit + // implicit vars, however some netlists (MIPS) expect single + // bit implicit wires to get created with range 0:0 etc. + m_statep->implicitOkAdd(m_modp, nodep->name()); + } + // These are perhaps a little too generous, as a SELect of siga[sigb] + // perhaps shouldn't create an implicit variable. But, we've warned... + else { + if (nodep->op1p()) pinImplicitExprRecurse(nodep->op1p()); + if (nodep->op2p()) pinImplicitExprRecurse(nodep->op2p()); + if (nodep->op3p()) pinImplicitExprRecurse(nodep->op3p()); + if (nodep->op4p()) pinImplicitExprRecurse(nodep->op4p()); + if (nodep->nextp()) pinImplicitExprRecurse(nodep->nextp()); + } } // VISITs virtual void visit(AstTypeTable* nodep) {} virtual void visit(AstNodeModule* nodep) { - UINFO(5," "<dead() || !nodep->user4()) { - UINFO(4,"Mark dead module "<forPrearray()) nodep->v3fatalSrc("Dead module persisted past where should have removed"); - // Don't remove now, because we may have a tree of parameterized modules with VARXREFs into the deleted module region - // V3Dead should cleanup. - // Downstream visitors up until V3Dead need to check for nodep->dead. - nodep->dead(true); - } else { - m_modp = nodep; + UINFO(5," "<dead() || !nodep->user4()) { + UINFO(4,"Mark dead module "<forPrearray()) nodep->v3fatalSrc("Dead module persisted past where should have removed"); + // Don't remove now, because we may have a tree of + // parameterized modules with VARXREFs into the deleted module + // region. V3Dead should cleanup. + // Downstream visitors up until V3Dead need to check for nodep->dead. + nodep->dead(true); + } else { + m_modp = nodep; iterateChildren(nodep); - m_modp = NULL; - } + m_modp = NULL; + } } virtual void visit(AstPin* nodep) { - // Pin: Link to submodule's port - // Deal with implicit definitions - do before Resolve visitor as may be referenced above declaration - if (nodep->exprp() // Else specifically unconnected pin - && !nodep->svImplicit()) { // SV 19.11.3: .name pins don't allow implicit decls - pinImplicitExprRecurse(nodep->exprp()); - } + // Pin: Link to submodule's port + // Deal with implicit definitions - do before Resolve visitor as may + // be referenced above declaration + if (nodep->exprp() // Else specifically unconnected pin + && !nodep->svImplicit()) { // SV 19.11.3: .name pins don't allow implicit decls + pinImplicitExprRecurse(nodep->exprp()); + } } virtual void visit(AstDefParam* nodep) { iterateChildren(nodep); - nodep->v3warn(DEFPARAM,"Suggest replace defparam with Verilog 2001 #(."<prettyName()<<"(...etc...))"); - VSymEnt* foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->path()); + nodep->v3warn(DEFPARAM, "Suggest replace defparam with Verilog 2001 #(." + <prettyName()<<"(...etc...))"); + VSymEnt* foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->path()); AstCell* cellp = foundp ? VN_CAST(foundp->nodep(), Cell) : NULL; - if (!cellp) { - nodep->v3error("In defparam, cell "<path()<<" never declared"); - } else { - AstNode* exprp = nodep->rhsp()->unlinkFrBack(); - UINFO(9,"Defparam cell "<path()<<"."<name() - <<" attach-to "<path()<<" never declared"); + } else { + AstNode* exprp = nodep->rhsp()->unlinkFrBack(); + UINFO(9,"Defparam cell "<path()<<"."<name() + <<" attach-to "<prettyName()); - } else if (!refp->isIO() && !refp->isIfaceRef()) { - nodep->v3error("Pin is not an in/out/inout/interface: "<prettyName()); - } else { - refp->user4(true); - VSymEnt* symp = m_statep->insertSym(m_statep->getNodeSym(m_modp), - "__pinNumber"+cvtToStr(nodep->pinNum()), refp, NULL/*packagep*/); - symp->exported(false); - } - // Ports not needed any more - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + if (!refp) { + nodep->v3error("Input/output/inout declaration not found for port: " + <prettyName()); + } else if (!refp->isIO() && !refp->isIfaceRef()) { + nodep->v3error("Pin is not an in/out/inout/interface: "<prettyName()); + } else { + refp->user4(true); + VSymEnt* symp = m_statep->insertSym(m_statep->getNodeSym(m_modp), + "__pinNumber"+cvtToStr(nodep->pinNum()), + refp, NULL/*packagep*/); + symp->exported(false); + } + // Ports not needed any more + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstAssignW* nodep) { - // Deal with implicit definitions - // We used to nodep->allowImplicit() here, but it turns out - // normal "assigns" can also make implicit wires. Yuk. - pinImplicitExprRecurse(nodep->lhsp()); + // Deal with implicit definitions + // We used to nodep->allowImplicit() here, but it turns out + // normal "assigns" can also make implicit wires. Yuk. + pinImplicitExprRecurse(nodep->lhsp()); iterateChildren(nodep); } virtual void visit(AstAssignAlias* nodep) { - // tran gates need implicit creation - // As VarRefs don't exist in forPrimary, sanity check - if (m_statep->forPrimary()) nodep->v3fatalSrc("Assign aliases unexpected pre-dot"); + // tran gates need implicit creation + // As VarRefs don't exist in forPrimary, sanity check + if (m_statep->forPrimary()) nodep->v3fatalSrc("Assign aliases unexpected pre-dot"); if (AstVarRef* forrefp = VN_CAST(nodep->lhsp(), VarRef)) { - pinImplicitExprRecurse(forrefp); - } + pinImplicitExprRecurse(forrefp); + } if (AstVarRef* forrefp = VN_CAST(nodep->rhsp(), VarRef)) { - pinImplicitExprRecurse(forrefp); - } + pinImplicitExprRecurse(forrefp); + } iterateChildren(nodep); } virtual void visit(AstImplicit* nodep) { - // Unsupported gates need implicit creation - pinImplicitExprRecurse(nodep); - // We're done with implicit gates - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // Unsupported gates need implicit creation + pinImplicitExprRecurse(nodep); + // We're done with implicit gates + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS LinkDotParamVisitor(AstNetlist* rootp, LinkDotState* statep) { - UINFO(4,__FUNCTION__<<": "<forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); - // Using the CELL names, we created all hierarchy. We now need to match this Scope - // up with the hierarchy created by the CELL names. - m_modSymp = m_statep->getScopeSym(nodep); - m_scopep = nodep; + UINFO(8," SCOPE "<forScopeCreation()) v3fatalSrc("Scopes should only exist right after V3Scope"); + // Using the CELL names, we created all hierarchy. We now need to match this Scope + // up with the hierarchy created by the CELL names. + m_modSymp = m_statep->getScopeSym(nodep); + m_scopep = nodep; iterateChildren(nodep); - m_modSymp = NULL; - m_scopep = NULL; + m_modSymp = NULL; + m_scopep = NULL; } virtual void visit(AstVarScope* nodep) { - if (!nodep->varp()->isFuncLocal()) { - VSymEnt* varSymp = m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL); - if (nodep->varp()->isIfaceRef() - && nodep->varp()->isIfaceParent()) { - UINFO(9,"Iface parent ref var "<varp()->name()<<" "<varp()->dtypep()); - if (!dtypep) nodep->v3fatalSrc("Non AstIfaceRefDType on isIfaceRef() var"); - UINFO(9,"Iface parent dtype "<cellName(); - string baddot; VSymEnt* okSymp; - VSymEnt* cellSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp); - if (!cellSymp) nodep->v3fatalSrc("No symbol for interface cell: " <prettyName(ifcellname)); - UINFO(5, " Found interface cell: se"<nodep()<modportName()!="") { - VSymEnt* mpSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp); - if (!mpSymp) { nodep->v3fatalSrc("No symbol for interface modport: " <prettyName(dtypep->modportName())); } - else cellSymp = mpSymp; - UINFO(5, " Found modport cell: se"<nodep()<insertScopeAlias(LinkDotState::SAMN_IFTOP, varSymp, cellSymp); - } - } + if (!nodep->varp()->isFuncLocal()) { + VSymEnt* varSymp = m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL); + if (nodep->varp()->isIfaceRef() + && nodep->varp()->isIfaceParent()) { + UINFO(9,"Iface parent ref var "<varp()->name()<<" "<varp()->dtypep()); + if (!dtypep) nodep->v3fatalSrc("Non AstIfaceRefDType on isIfaceRef() var"); + UINFO(9,"Iface parent dtype "<cellName(); + string baddot; VSymEnt* okSymp; + VSymEnt* cellSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp); + if (!cellSymp) nodep->v3fatalSrc("No symbol for interface cell: " + <prettyName(ifcellname)); + UINFO(5, " Found interface cell: se"<nodep()<modportName()!="") { + VSymEnt* mpSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp); + if (!mpSymp) { nodep->v3fatalSrc("No symbol for interface modport: " + <prettyName(dtypep->modportName())); } + else cellSymp = mpSymp; + UINFO(5, " Found modport cell: se" + <nodep()<insertScopeAlias(LinkDotState::SAMN_IFTOP, varSymp, cellSymp); + } + } } virtual void visit(AstNodeFTask* nodep) { - VSymEnt* symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, NULL); - symp->fallbackp(m_modSymp); - // No recursion, we don't want to pick up variables + VSymEnt* symp = m_statep->insertBlock(m_modSymp, nodep->name(), nodep, NULL); + symp->fallbackp(m_modSymp); + // No recursion, we don't want to pick up variables } virtual void visit(AstAssignAlias* nodep) { - // Track aliases created by V3Inline; if we get a VARXREF(aliased_from) - // we'll need to replace it with a VARXREF(aliased_to) - if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\talias: "); + // Track aliases created by V3Inline; if we get a VARXREF(aliased_from) + // we'll need to replace it with a VARXREF(aliased_to) + if (debug()>=9) nodep->dumpTree(cout, "-\t\t\t\talias: "); AstVarScope* fromVscp = VN_CAST(nodep->lhsp(), VarRef)->varScopep(); AstVarScope* toVscp = VN_CAST(nodep->rhsp(), VarRef)->varScopep(); - if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes"); - fromVscp->user2p(toVscp); + if (!fromVscp || !toVscp) nodep->v3fatalSrc("Bad alias scopes"); + fromVscp->user2p(toVscp); iterateChildren(nodep); } virtual void visit(AstAssignVarScope* nodep) { - UINFO(5,"ASSIGNVARSCOPE "<=9) nodep->dumpTree(cout,"-\t\t\t\tavs: "); - VSymEnt* rhsSymp; - { + UINFO(5,"ASSIGNVARSCOPE "<=9) nodep->dumpTree(cout, "-\t\t\t\tavs: "); + VSymEnt* rhsSymp; + { AstVarRef* refp = VN_CAST(nodep->rhsp(), VarRef); AstVarXRef* xrefp = VN_CAST(nodep->rhsp(), VarXRef); - if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin"); - string inl = (xrefp && xrefp->inlinedDots().size()) ? (xrefp->inlinedDots() + "__DOT__") : ""; - VSymEnt* symp = NULL; - string scopename; - while (!symp) { - scopename = refp ? refp->name() : (inl.size() ? (inl + xrefp->name()) : xrefp->name()); - string baddot; VSymEnt* okSymp; - symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp); - if (inl == "") - break; - inl = LinkDotState::removeLastInlineScope(inl); - } - if (!symp) UINFO(9,"No symbol for interface alias rhs ("<v3fatalSrc("No symbol for interface alias rhs"); - UINFO(5, " Found a linked scope RHS: "<nodep()<v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin"); + string inl = ((xrefp && xrefp->inlinedDots().size()) + ? (xrefp->inlinedDots() + "__DOT__") : ""); + VSymEnt* symp = NULL; + string scopename; + while (!symp) { + scopename = refp ? refp->name() : (inl.size() ? (inl + xrefp->name()) + : xrefp->name()); + string baddot; VSymEnt* okSymp; + symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp); + if (inl == "") + break; + inl = LinkDotState::removeLastInlineScope(inl); + } + if (!symp) UINFO(9,"No symbol for interface alias rhs (" + <v3fatalSrc("No symbol for interface alias rhs"); + UINFO(5, " Found a linked scope RHS: "<nodep()<lhsp(), VarXRef); const AstVarRef* refp = VN_CAST(nodep->lhsp(), VarRef); - if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin"); - string scopename = refp ? refp->varp()->name() : xrefp->dotted()+"."+xrefp->name(); - string baddot; VSymEnt* okSymp; - VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp); - if (!symp) nodep->v3fatalSrc("No symbol for interface alias lhs"); - UINFO(5, " Found a linked scope LHS: "<nodep()<insertScopeAlias(LinkDotState::SAMN_IFTOP, lhsSymp, rhsSymp); - // We have stored the link, we don't need these any more - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + if (!refp && !xrefp) nodep->v3fatalSrc("Unsupported: Non Var(X)Ref attached to interface pin"); + string scopename = refp ? refp->varp()->name() : xrefp->dotted()+"."+xrefp->name(); + string baddot; VSymEnt* okSymp; + VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp); + if (!symp) nodep->v3fatalSrc("No symbol for interface alias lhs"); + UINFO(5, " Found a linked scope LHS: "<nodep()<insertScopeAlias(LinkDotState::SAMN_IFTOP, lhsSymp, rhsSymp); + // We have stored the link, we don't need these any more + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } // For speed, don't recurse things that can't have scope // Note we allow AstNodeStmt's as generates may be under them @@ -1340,18 +1397,18 @@ class LinkDotScopeVisitor : public AstNVisitor { virtual void visit(AstVar*) {} virtual void visit(AstNodeMath*) {} virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS LinkDotScopeVisitor(AstNetlist* rootp, LinkDotState* statep) { - UINFO(4,__FUNCTION__<<": "<insertBlock(m_curSymp, nodep->name(), nodep, NULL); - m_curSymp->fallbackp(oldCurSymp); + // Modport: Remember its name for later resolution + UINFO(5," fiv: "<insertBlock(m_curSymp, nodep->name(), nodep, NULL); + m_curSymp->fallbackp(oldCurSymp); iterateChildren(nodep); - } - m_curSymp = oldCurSymp; + } + m_curSymp = oldCurSymp; } virtual void visit(AstModportFTaskRef* nodep) { - UINFO(5," fif: "<isExport()) nodep->v3error("Unsupported: modport export"); - VSymEnt* symp = m_curSymp->findIdFallback(nodep->name()); - if (!symp) { - nodep->v3error("Modport item not found: "<prettyName()); + if (nodep->isExport()) nodep->v3error("Unsupported: modport export"); + VSymEnt* symp = m_curSymp->findIdFallback(nodep->name()); + if (!symp) { + nodep->v3error("Modport item not found: "<prettyName()); } else if (AstNodeFTask* ftaskp = VN_CAST(symp->nodep(), NodeFTask)) { - // Make symbol under modport that points at the _interface_'s var, not the modport. - nodep->ftaskp(ftaskp); - VSymEnt* subSymp = m_statep->insertSym(m_curSymp, nodep->name(), ftaskp, NULL/*package*/); - m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp); - } else { - nodep->v3error("Modport item is not a function/task: "<prettyName()); - } - if (m_statep->forScopeCreation()) { - // Done with AstModportFTaskRef. - // Delete to prevent problems if we dead-delete pointed to ftask - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } + // Make symbol under modport that points at the _interface_'s var, not the modport. + nodep->ftaskp(ftaskp); + VSymEnt* subSymp = m_statep->insertSym(m_curSymp, nodep->name(), + ftaskp, NULL/*package*/); + m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp); + } else { + nodep->v3error("Modport item is not a function/task: "<prettyName()); + } + if (m_statep->forScopeCreation()) { + // Done with AstModportFTaskRef. + // Delete to prevent problems if we dead-delete pointed to ftask + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } } virtual void visit(AstModportVarRef* nodep) { - UINFO(5," fiv: "<findIdFallback(nodep->name()); - if (!symp) { - nodep->v3error("Modport item not found: "<prettyName()); + VSymEnt* symp = m_curSymp->findIdFallback(nodep->name()); + if (!symp) { + nodep->v3error("Modport item not found: "<prettyName()); } else if (AstVar* varp = VN_CAST(symp->nodep(), Var)) { - // Make symbol under modport that points at the _interface_'s var via the modport. - // (Need modport still to test input/output markings) - nodep->varp(varp); - m_statep->insertSym(m_curSymp, nodep->name(), nodep, NULL/*package*/); + // Make symbol under modport that points at the _interface_'s var via the modport. + // (Need modport still to test input/output markings) + nodep->varp(varp); + m_statep->insertSym(m_curSymp, nodep->name(), nodep, NULL/*package*/); } else if (AstVarScope* vscp = VN_CAST(symp->nodep(), VarScope)) { - // Make symbol under modport that points at the _interface_'s var, not the modport. - nodep->varp(vscp->varp()); - m_statep->insertSym(m_curSymp, nodep->name(), vscp, NULL/*package*/); - } else { - nodep->v3error("Modport item is not a variable: "<prettyName()); - } - if (m_statep->forScopeCreation()) { - // Done with AstModportVarRef. - // Delete to prevent problems if we dead-delete pointed to variable - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } + // Make symbol under modport that points at the _interface_'s var, not the modport. + nodep->varp(vscp->varp()); + m_statep->insertSym(m_curSymp, nodep->name(), vscp, NULL/*package*/); + } else { + nodep->v3error("Modport item is not a variable: "<prettyName()); + } + if (m_statep->forScopeCreation()) { + // Done with AstModportVarRef. + // Delete to prevent problems if we dead-delete pointed to variable + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } } virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS LinkDotIfaceVisitor(AstIface* nodep, VSymEnt* curSymp, LinkDotState* statep) { - UINFO(4,__FUNCTION__<<": "<first; - VSymEnt* symp = it->second; - LinkDotIfaceVisitor(nodep, symp, this); + AstIface* nodep = it->first; + VSymEnt* symp = it->second; + LinkDotIfaceVisitor(nodep, symp, this); } m_ifaceModSyms.clear(); } @@ -1457,78 +1515,84 @@ class LinkDotResolveVisitor : public AstNVisitor { private: // NODE STATE // Cleared on global - // *::user1p() -> See LinkDotState - // *::user2p() -> See LinkDotState - // *::user3() // bool. Processed - // *::user4() -> See LinkDotState + // *::user1p() -> See LinkDotState + // *::user2p() -> See LinkDotState + // *::user3() // bool. Processed + // *::user4() -> See LinkDotState // Cleared on Cell - // AstVar::user5() // bool. True if pin used in this cell - AstUser3InUse m_inuser3; - AstUser5InUse m_inuser5; + // AstVar::user5() // bool. True if pin used in this cell + AstUser3InUse m_inuser3; + AstUser5InUse m_inuser5; // TYPES - enum DotPosition { DP_NONE=0, // Not under a DOT - DP_PACKAGE, // {package}:: DOT - DP_SCOPE, // [DOT...] {scope-or-var} DOT - DP_FINAL, // [DOT...] {var-or-func-or-dtype} with no following dots - DP_MEMBER }; // DOT {member-name} [DOT...] + enum DotPosition { DP_NONE=0, // Not under a DOT + DP_PACKAGE, // {package}:: DOT + DP_SCOPE, // [DOT...] {scope-or-var} DOT + DP_FINAL, // [DOT...] {var-or-func-or-dtype} with no following dots + DP_MEMBER }; // DOT {member-name} [DOT...] // STATE - LinkDotState* m_statep; // State, including dotted symbol table - VSymEnt* m_curSymp; // SymEnt for current lookup point - VSymEnt* m_modSymp; // SymEnt for current module - VSymEnt* m_pinSymp; // SymEnt for pin lookups - AstCell* m_cellp; // Current cell - AstNodeModule* m_modp; // Current module - AstNodeFTask* m_ftaskp; // Current function/task - int m_modportNum; // Uniqueify modport numbers + LinkDotState* m_statep; // State, including dotted symbol table + VSymEnt* m_curSymp; // SymEnt for current lookup point + VSymEnt* m_modSymp; // SymEnt for current module + VSymEnt* m_pinSymp; // SymEnt for pin lookups + AstCell* m_cellp; // Current cell + AstNodeModule* m_modp; // Current module + AstNodeFTask* m_ftaskp; // Current function/task + int m_modportNum; // Uniqueify modport numbers struct DotStates { - DotPosition m_dotPos; // Scope part of dotted resolution - VSymEnt* m_dotSymp; // SymEnt for dotted AstParse lookup - AstDot* m_dotp; // Current dot - bool m_unresolved; // Unresolved, needs help from V3Param - AstNode* m_unlinkedScope;// Unresolved scope, needs corresponding VarXRef - bool m_dotErr; // Error found in dotted resolution, ignore upwards - string m_dotText; // String of dotted names found in below parseref - DotStates() { init(NULL); } - ~DotStates() {} - void init(VSymEnt* curSymp) { - m_dotPos = DP_NONE; m_dotSymp = curSymp; m_dotp = NULL; m_dotErr = false; m_dotText = ""; - m_unresolved = false; m_unlinkedScope = NULL; - } - string ascii() const { - static const char* const names[] = { "NONE","PACKAGE","SCOPE","FINAL","MEMBER" }; + DotPosition m_dotPos; // Scope part of dotted resolution + VSymEnt* m_dotSymp; // SymEnt for dotted AstParse lookup + AstDot* m_dotp; // Current dot + bool m_unresolved; // Unresolved, needs help from V3Param + AstNode* m_unlinkedScope;// Unresolved scope, needs corresponding VarXRef + bool m_dotErr; // Error found in dotted resolution, ignore upwards + string m_dotText; // String of dotted names found in below parseref + DotStates() { init(NULL); } + ~DotStates() {} + void init(VSymEnt* curSymp) { + m_dotPos = DP_NONE; m_dotSymp = curSymp; m_dotp = NULL; + m_dotErr = false; m_dotText = ""; + m_unresolved = false; m_unlinkedScope = NULL; + } + string ascii() const { + static const char* const names[] + = { "NONE", "PACKAGE", "SCOPE", "FINAL", "MEMBER" }; std::ostringstream sstr; - sstr<<"ds="<prettyName()); - } else { - nodep->v3warn(IMPLICIT,"Signal definition not found, creating implicitly: "<prettyName()); - } - } + void createImplicitVar(VSymEnt* lookupSymp, AstVarRef* nodep, + AstNodeModule* modp, VSymEnt* moduleSymp, bool noWarn) { + // Create implicit after warning + if (!nodep->varp()) { + if (!noWarn) { + if (nodep->fileline()->warnIsOff(V3ErrorCode::I_DEF_NETTYPE_WIRE)) { + nodep->v3error("Signal definition not found, and implicit disabled with `default_nettype: " + <prettyName()); + } else { + nodep->v3warn(IMPLICIT, "Signal definition not found, creating implicitly: " + <prettyName()); + } + } AstVar* newp = new AstVar(nodep->fileline(), AstVarType::WIRE, nodep->name(), VFlagLogicPacked(), 1); - newp->trace(modp->modTrace()); - nodep->varp(newp); - modp->addStmtp(newp); - // Link it to signal list, must add the variable under the module; current scope might be lower now - m_statep->insertSym(moduleSymp, newp->name(), newp, NULL/*packagep*/); - } + newp->trace(modp->modTrace()); + nodep->varp(newp); + modp->addStmtp(newp); + // Link it to signal list, must add the variable under the module; + // current scope might be lower now + m_statep->insertSym(moduleSymp, newp->name(), newp, NULL/*packagep*/); + } } AstVar* foundToVarp(const VSymEnt* symp, AstNode* nodep, bool lvalue) { // Return a variable if possible, auto converting a modport to variable @@ -1541,7 +1605,7 @@ private: AstVar* varp = snodep->varp(); if (lvalue && snodep->direction().isReadOnly()) { nodep->v3error("Attempt to drive input-only modport: "<prettyName()); - } // else other simulators don't warn about reading, and IEEE doesn't say illegal + } // else other simulators don't warn about reading, and IEEE doesn't say illegal return varp; } else { return NULL; @@ -1549,272 +1613,287 @@ private: } void taskFuncSwapCheck(AstNodeFTaskRef* nodep) { if (nodep->taskp() && VN_IS(nodep->taskp(), Task) - && VN_IS(nodep, FuncRef)) nodep->v3error("Illegal call of a task as a function: "<prettyName()); + && VN_IS(nodep, FuncRef)) nodep->v3error("Illegal call of a task as a function: " + <prettyName()); } inline void checkNoDot(AstNode* nodep) { - if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) { - //UINFO(9,"ds="<type()<<" under a " <backp()->type()<<" in dotted expression"); - m_ds.m_dotErr = true; - } + m_ds.m_dotErr = true; + } } - AstVar* makeIfaceModportVar(FileLine* fl, AstCell* cellp, AstIface* ifacep, AstModport* modportp) { - // Create iface variable, using duplicate var when under same module scope - string varName = ifacep->name()+"__Vmp__"+modportp->name()+"__Viftop"+cvtToStr(++m_modportNum); - AstIfaceRefDType* idtypep = new AstIfaceRefDType(fl, cellp->name(), ifacep->name(), modportp->name()); - idtypep->cellp(cellp); - AstVar* varp = new AstVar(fl, AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep); - varp->isIfaceParent(true); - m_modp->addStmtp(varp); - return varp; + AstVar* makeIfaceModportVar(FileLine* fl, AstCell* cellp, + AstIface* ifacep, AstModport* modportp) { + // Create iface variable, using duplicate var when under same module scope + string varName + = ifacep->name()+"__Vmp__"+modportp->name()+"__Viftop"+cvtToStr(++m_modportNum); + AstIfaceRefDType* idtypep + = new AstIfaceRefDType(fl, cellp->name(), ifacep->name(), modportp->name()); + idtypep->cellp(cellp); + AstVar* varp = new AstVar(fl, AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep); + varp->isIfaceParent(true); + m_modp->addStmtp(varp); + return varp; } void markAndCheckPinDup(AstNode* nodep, AstNode* refp, const char* whatp) { - if (refp->user5p() && refp->user5p()!=nodep) { - nodep->v3error("Duplicate "<prettyName()<user5p()->warnMore() - <<"... Location of original "<user5p(nodep); - } + if (refp->user5p() && refp->user5p()!=nodep) { + nodep->v3error("Duplicate "<prettyName()<user5p()->warnMore() + <<"... Location of original "<user5p(nodep); + } } // VISITs virtual void visit(AstNetlist* nodep) { - // Recurse..., backward as must do packages before using packages + // Recurse..., backward as must do packages before using packages iterateChildrenBackwards(nodep); } virtual void visit(AstTypeTable* nodep) {} virtual void visit(AstNodeModule* nodep) { - if (nodep->dead()) return; - checkNoDot(nodep); - UINFO(8," "<getNodeSym(nodep); // Until overridden by a SCOPE - m_cellp = NULL; - m_modp = nodep; - m_modportNum = 0; + if (nodep->dead()) return; + checkNoDot(nodep); + UINFO(8," "<getNodeSym(nodep); // Until overridden by a SCOPE + m_cellp = NULL; + m_modp = nodep; + m_modportNum = 0; iterateChildren(nodep); - m_modp = NULL; - m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL; + m_modp = NULL; + m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL; } virtual void visit(AstScope* nodep) { - UINFO(8," "<getScopeSym(nodep); + UINFO(8," "<getScopeSym(nodep); iterateChildren(nodep); - m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL; - m_modSymp = oldModSymp; - m_curSymp = oldCurSymp; + m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL; + m_modSymp = oldModSymp; + m_curSymp = oldCurSymp; } virtual void visit(AstCellInline* nodep) { - checkNoDot(nodep); - if (m_statep->forScopeCreation()) { - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } + checkNoDot(nodep); + if (m_statep->forScopeCreation()) { + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } } virtual void visit(AstCell* nodep) { - // Cell: Recurse inside or cleanup not founds - checkNoDot(nodep); - m_cellp = nodep; - AstNode::user5ClearTree(); - if (!nodep->modp()) { - nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out - } - else { + // Cell: Recurse inside or cleanup not founds + checkNoDot(nodep); + m_cellp = nodep; + AstNode::user5ClearTree(); + if (!nodep->modp()) { + nodep->v3fatalSrc("Cell has unlinked module"); // V3LinkCell should have errored out + } + else { if (VN_IS(nodep->modp(), NotFoundModule)) { - // Prevent warnings about missing pin connects - if (nodep->pinsp()) nodep->pinsp()->unlinkFrBackWithNext()->deleteTree(); - if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); - } - // Need to pass the module info to this cell, so we can link up the pin names - // However can't use m_curSymp as pin connections need to use the instantiator's symbols - else { - m_pinSymp = m_statep->getNodeSym(nodep->modp()); - UINFO(4,"(Backto) Link Cell: "<dumpTree(cout,"linkcell:"); } - //if (debug()) { nodep->modp()->dumpTree(cout,"linkcemd:"); } + // Prevent warnings about missing pin connects + if (nodep->pinsp()) nodep->pinsp()->unlinkFrBackWithNext()->deleteTree(); + if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); + } + // Need to pass the module info to this cell, so we can link up the pin names + // However can't use m_curSymp as pin connections need to use the + // instantiator's symbols + else { + m_pinSymp = m_statep->getNodeSym(nodep->modp()); + UINFO(4,"(Backto) Link Cell: "<dumpTree(cout, "linkcell:"); } + //if (debug()) { nodep->modp()->dumpTree(cout, "linkcemd:"); } iterateChildren(nodep); - m_pinSymp = NULL; - } - } - m_cellp = NULL; - // Parent module inherits child's publicity - // This is done bottom up in the LinkBotupVisitor stage + m_pinSymp = NULL; + } + } + m_cellp = NULL; + // Parent module inherits child's publicity + // This is done bottom up in the LinkBotupVisitor stage } virtual void visit(AstPin* nodep) { - // Pin: Link to submodule's port - checkNoDot(nodep); + // Pin: Link to submodule's port + checkNoDot(nodep); iterateChildren(nodep); - if (!nodep->modVarp()) { - if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?"); - VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name()); - const char* whatp = nodep->param() ? "parameter pin" : "pin"; - if (!foundp) { + if (!nodep->modVarp()) { + if (!m_pinSymp) nodep->v3fatalSrc("Pin not under cell?"); + VSymEnt* foundp = m_pinSymp->findIdFlat(nodep->name()); + const char* whatp = nodep->param() ? "parameter pin" : "pin"; + if (!foundp) { if (nodep->name() == "__paramNumber1" && VN_IS(m_cellp->modp(), Primitive)) { - // Primitive parameter is really a delay we can just ignore - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - return; - } - nodep->v3error(ucfirst(whatp)<<" not found: "<prettyName()); - } + // Primitive parameter is really a delay we can just ignore + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + return; + } + nodep->v3error(ucfirst(whatp)<<" not found: "<prettyName()); + } else if (AstVar* refp = VN_CAST(foundp->nodep(), Var)) { - if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) { - nodep->v3error(ucfirst(whatp)<<" is not an in/out/inout/param/interface: "<prettyName()); - } else { - nodep->modVarp(refp); - markAndCheckPinDup(nodep, refp, whatp); - } - } + if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) { + nodep->v3error(ucfirst(whatp)<<" is not an in/out/inout/param/interface: " + <prettyName()); + } else { + nodep->modVarp(refp); + markAndCheckPinDup(nodep, refp, whatp); + } + } else if (AstParamTypeDType* refp = VN_CAST(foundp->nodep(), ParamTypeDType)) { - nodep->modPTypep(refp); - markAndCheckPinDup(nodep, refp, whatp); - } - else { - nodep->v3error(ucfirst(whatp)<<" not found: "<prettyName()); - } - } - // Early return() above when deleted + nodep->modPTypep(refp); + markAndCheckPinDup(nodep, refp, whatp); + } + else { + nodep->v3error(ucfirst(whatp)<<" not found: "<prettyName()); + } + } + // Early return() above when deleted } virtual void visit(AstDot* nodep) { - // Legal under a DOT: AstDot, AstParseRef, AstPackageRef, AstNodeSel - // also a DOT can be part of an expression, but only above plus AstFTaskRef are legal children - // DOT(PACKAGEREF, PARSEREF(text)) - // DOT(DOT(DOT(PARSEREF(text), ... - if (nodep->user3SetOnce()) return; - UINFO(8," "<=9) nodep->dumpTree("-dot-in: "); - m_ds.init(m_curSymp); // Start from current point - } - m_ds.m_dotp = nodep; // Always, not just at start - m_ds.m_dotPos = DP_SCOPE; + // Legal under a DOT: AstDot, AstParseRef, AstPackageRef, AstNodeSel + // also a DOT can be part of an expression, but only above plus + // AstFTaskRef are legal children + // DOT(PACKAGEREF, PARSEREF(text)) + // DOT(DOT(DOT(PARSEREF(text), ... + if (nodep->user3SetOnce()) return; + UINFO(8," "<=9) nodep->dumpTree("-dot-in: "); + m_ds.init(m_curSymp); // Start from current point + } + m_ds.m_dotp = nodep; // Always, not just at start + m_ds.m_dotPos = DP_SCOPE; - // m_ds.m_dotText communicates the cell prefix between stages + // m_ds.m_dotText communicates the cell prefix between stages if (VN_IS(nodep->lhsp(), PackageRef)) { - //if (!start) { nodep->lhsp()->v3error("Package reference may not be embedded in dotted reference"); m_ds.m_dotErr=true; } - m_ds.m_dotPos = DP_PACKAGE; - } else { - m_ds.m_dotPos = DP_SCOPE; + //if (!start) { nodep->lhsp()->v3error("Package reference may not be embedded in dotted reference"); m_ds.m_dotErr=true; } + m_ds.m_dotPos = DP_PACKAGE; + } else { + m_ds.m_dotPos = DP_SCOPE; iterateAndNextNull(nodep->lhsp()); - //if (debug()>=9) nodep->dumpTree("-dot-lho: "); - } - if (m_ds.m_unresolved && (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) { - m_ds.m_unlinkedScope = nodep->lhsp(); - } - if (!m_ds.m_dotErr) { // Once something wrong, give up - if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_FINAL; // Top 'final' dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS + //if (debug()>=9) nodep->dumpTree("-dot-lho: "); + } + if (m_ds.m_unresolved && (VN_IS(nodep->lhsp(), CellRef) + || VN_IS(nodep->lhsp(), CellArrayRef))) { + m_ds.m_unlinkedScope = nodep->lhsp(); + } + if (!m_ds.m_dotErr) { // Once something wrong, give up + // Top 'final' dot RHS is final RHS, else it's a + // DOT(DOT(x,*here*),real-rhs) which we consider a RHS + if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_FINAL; iterateAndNextNull(nodep->rhsp()); - //if (debug()>=9) nodep->dumpTree("-dot-rho: "); - } - if (start) { - AstNode* newp; - if (m_ds.m_dotErr) { - newp = new AstConst(nodep->fileline(),AstConst::LogicFalse()); - } else { - // RHS is what we're left with - newp = nodep->rhsp()->unlinkFrBack(); - } - if (debug()>=9) newp->dumpTree("-dot-out: "); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - } else { // Dot midpoint - AstNode* newp = nodep->rhsp()->unlinkFrBack(); - if (m_ds.m_unresolved) { - AstCellRef* crp = new AstCellRef(nodep->fileline(), nodep->name(), nodep->lhsp()->unlinkFrBack(), newp); - newp = crp; - } - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - } - } - if (start) { - m_ds = lastStates; - } else { - m_ds.m_dotp = lastStates.m_dotp; - } + //if (debug()>=9) nodep->dumpTree("-dot-rho: "); + } + if (start) { + AstNode* newp; + if (m_ds.m_dotErr) { + newp = new AstConst(nodep->fileline(), AstConst::LogicFalse()); + } else { + // RHS is what we're left with + newp = nodep->rhsp()->unlinkFrBack(); + } + if (debug()>=9) newp->dumpTree("-dot-out: "); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + } else { // Dot midpoint + AstNode* newp = nodep->rhsp()->unlinkFrBack(); + if (m_ds.m_unresolved) { + AstCellRef* crp = new AstCellRef(nodep->fileline(), nodep->name(), + nodep->lhsp()->unlinkFrBack(), newp); + newp = crp; + } + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + } + } + if (start) { + m_ds = lastStates; + } else { + m_ds.m_dotp = lastStates.m_dotp; + } } virtual void visit(AstParseRef* nodep) { - if (nodep->user3SetOnce()) return; - UINFO(9," linkPARSEREF "<v3fatalSrc("NULL lookup symbol table"); - if (!m_statep->forPrimary()) nodep->v3fatalSrc("ParseRefs should no longer exist"); - DotStates lastStates = m_ds; - bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed - if (start) { - m_ds.init(m_curSymp); - // Note m_ds.m_dot remains NULL; this is a reference not under a dot - } - if (m_ds.m_dotPos == DP_MEMBER) { - // Found a Var, everything following is membership. {scope}.{var}.HERE {member} - AstNode* varEtcp = m_ds.m_dotp->lhsp()->unlinkFrBack(); - AstNode* newp = new AstMemberSel(nodep->fileline(), varEtcp, VFlagChildDType(), nodep->name()); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - } - else { - // - string expectWhat; - bool allowScope = false; - bool allowVar = false; - if (m_ds.m_dotPos == DP_PACKAGE) { - // {package}::{a} - AstPackage* packagep = NULL; - expectWhat = "scope/variable"; - allowScope = true; - allowVar = true; - if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + if (nodep->user3SetOnce()) return; + UINFO(9," linkPARSEREF "<v3fatalSrc("NULL lookup symbol table"); + if (!m_statep->forPrimary()) nodep->v3fatalSrc("ParseRefs should no longer exist"); + DotStates lastStates = m_ds; + bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed + if (start) { + m_ds.init(m_curSymp); + // Note m_ds.m_dot remains NULL; this is a reference not under a dot + } + if (m_ds.m_dotPos == DP_MEMBER) { + // Found a Var, everything following is membership. {scope}.{var}.HERE {member} + AstNode* varEtcp = m_ds.m_dotp->lhsp()->unlinkFrBack(); + AstNode* newp = new AstMemberSel(nodep->fileline(), varEtcp, + VFlagChildDType(), nodep->name()); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + } + else { + // + string expectWhat; + bool allowScope = false; + bool allowVar = false; + if (m_ds.m_dotPos == DP_PACKAGE) { + // {package}::{a} + AstPackage* packagep = NULL; + expectWhat = "scope/variable"; + allowScope = true; + allowVar = true; + if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) { + m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + } packagep = VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep(); - if (!packagep) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); - m_ds.m_dotSymp = m_statep->getNodeSym(packagep); - m_ds.m_dotPos = DP_SCOPE; - } else if (m_ds.m_dotPos == DP_SCOPE) { - // {a}.{b}, where {a} maybe a module name - // or variable, where dotting into structure member - expectWhat = "scope/variable"; - allowScope = true; - allowVar = true; - } else if (m_ds.m_dotPos == DP_NONE - || m_ds.m_dotPos == DP_FINAL) { - expectWhat = "variable"; - allowVar = true; - } else { - UINFO(1,"ds="<v3fatalSrc("Unhandled AstParseRefExp"); - } - // Lookup - VSymEnt* foundp; - string baddot; - VSymEnt* okSymp = NULL; - if (allowScope) { - foundp = m_statep->findDotted(m_ds.m_dotSymp, nodep->name(), baddot, okSymp); // Maybe NULL - } else { - foundp = m_ds.m_dotSymp->findIdFallback(nodep->name()); - } + if (!packagep) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + m_ds.m_dotSymp = m_statep->getNodeSym(packagep); + m_ds.m_dotPos = DP_SCOPE; + } else if (m_ds.m_dotPos == DP_SCOPE) { + // {a}.{b}, where {a} maybe a module name + // or variable, where dotting into structure member + expectWhat = "scope/variable"; + allowScope = true; + allowVar = true; + } else if (m_ds.m_dotPos == DP_NONE + || m_ds.m_dotPos == DP_FINAL) { + expectWhat = "variable"; + allowVar = true; + } else { + UINFO(1,"ds="<v3fatalSrc("Unhandled AstParseRefExp"); + } + // Lookup + VSymEnt* foundp; + string baddot; + VSymEnt* okSymp = NULL; + if (allowScope) { + foundp = m_statep->findDotted(m_ds.m_dotSymp, + nodep->name(), baddot, okSymp); // Maybe NULL + } else { + foundp = m_ds.m_dotSymp->findIdFallback(nodep->name()); + } if (foundp) UINFO(9," found=se"<name()); - m_ds.m_dotSymp = foundp; - m_ds.m_dotPos = DP_SCOPE; - // Upper AstDot visitor will handle it from here - } + m_ds.m_dotSymp = foundp; + m_ds.m_dotPos = DP_SCOPE; + // Upper AstDot visitor will handle it from here + } else if (VN_IS(foundp->nodep(), Cell) - && allowVar && m_cellp) { + && allowVar && m_cellp) { AstCell* cellp = VN_CAST(foundp->nodep(), Cell); if (VN_IS(cellp->modp(), Iface)) { // Interfaces can be referenced like a variable for interconnect @@ -1824,489 +1903,536 @@ private: string findName = nodep->name()+"__Viftop"; VSymEnt* ifaceSymp = parentEntp->findIdFallback(findName); AstVar* ifaceRefVarp = ifaceSymp ? VN_CAST(ifaceSymp->nodep(), Var) : NULL; - if (!ifaceRefVarp) nodep->v3fatalSrc("Can't find interface var ref: "<v3fatalSrc("Can't find interface var ref: " + <name()); m_ds.m_dotSymp = foundp; m_ds.m_dotPos = DP_SCOPE; UINFO(9," cell -> iface varref "<nodep()<fileline(), ifaceRefVarp, false); + AstNode* newp = new AstVarRef(ifaceRefVarp->fileline(), + ifaceRefVarp, false); nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); } else if (VN_IS(cellp->modp(), NotFoundModule)) { - cellp->v3error("Cannot find file containing interface: " << AstNode::prettyName(cellp->modp()->name())); + cellp->v3error("Cannot find file containing interface: " + <modp()->name())); } } - } - else if (AstVar* varp = foundToVarp(foundp, nodep, false)) { - AstIfaceRefDType* ifacerefp = LinkDotState::ifaceRefFromArray(varp->subDTypep()); - if (ifacerefp) { - if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface"); - // Really this is a scope reference into an interface - UINFO(9,"varref-ifaceref "<subDTypep()); + if (ifacerefp) { + if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface"); + // Really this is a scope reference into an interface + UINFO(9,"varref-ifaceref "<name()); - m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp()); - m_ds.m_dotPos = DP_SCOPE; - ok = true; - AstNode* newp = new AstVarRef(nodep->fileline(), varp, false); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } - else if (allowVar) { - AstNode* newp; - if (m_ds.m_dotText != "") { - AstVarXRef* refp = new AstVarXRef(nodep->fileline(), nodep->name(), - m_ds.m_dotText, false); // lvalue'ness computed later - refp->varp(varp); - m_ds.m_dotText = ""; - if (m_ds.m_unresolved && m_ds.m_unlinkedScope) { + m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp()); + m_ds.m_dotPos = DP_SCOPE; + ok = true; + AstNode* newp = new AstVarRef(nodep->fileline(), varp, false); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } + else if (allowVar) { + AstNode* newp; + if (m_ds.m_dotText != "") { + AstVarXRef* refp = new AstVarXRef(nodep->fileline(), nodep->name(), + m_ds.m_dotText, false); // lvalue'ness computed later + refp->varp(varp); + m_ds.m_dotText = ""; + if (m_ds.m_unresolved && m_ds.m_unlinkedScope) { newp = new AstUnlinkedRef(nodep->fileline(), VN_CAST(refp, VarXRef), - refp->name(), m_ds.m_unlinkedScope->unlinkFrBack()); - m_ds.m_unlinkedScope = NULL; - m_ds.m_unresolved = false; - } else { - newp = refp; - } - } else { - AstVarRef* refp = new AstVarRef(nodep->fileline(), varp, false); // lvalue'ness computed later - refp->packagep(foundp->packagep()); - newp = refp; - } - UINFO(9," new "<replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - m_ds.m_dotPos = DP_MEMBER; - ok = true; - } - } + refp->name(), + m_ds.m_unlinkedScope->unlinkFrBack()); + m_ds.m_unlinkedScope = NULL; + m_ds.m_unresolved = false; + } else { + newp = refp; + } + } else { + AstVarRef* refp = new AstVarRef(nodep->fileline(), varp, false); // lvalue'ness computed later + refp->packagep(foundp->packagep()); + newp = refp; + } + UINFO(9," new "<replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + m_ds.m_dotPos = DP_MEMBER; + ok = true; + } + } else if (AstModport* modportp = VN_CAST(foundp->nodep(), Modport)) { - // A scope reference into an interface's modport (not necessarily at a pin connection) - UINFO(9,"cell-ref-to-modport "<nodep()<nodep()<nodep(), Cell) || !VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp() || !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) { - nodep->v3error("Modport not referenced as ."<prettyName()); + nodep->v3error("Modport not referenced as ." + <prettyName()); } else if (!VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp() || !VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface)) { - nodep->v3error("Modport not referenced from underneath an interface: "<prettyName()); - } else { + nodep->v3error("Modport not referenced from underneath an interface: " + <prettyName()); + } else { AstCell* cellp = VN_CAST(m_ds.m_dotSymp->nodep(), Cell); - if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell"); + if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell"); AstIface* ifacep = VN_CAST(cellp->modp(), Iface); - //string cellName = m_ds.m_dotText; // Use cellp->name + //string cellName = m_ds.m_dotText; // Use cellp->name m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name()); - m_ds.m_dotSymp = m_statep->getNodeSym(modportp); - m_ds.m_dotPos = DP_SCOPE; - ok = true; - AstVar* varp = makeIfaceModportVar(nodep->fileline(), cellp, ifacep, modportp); - AstVarRef* refp = new AstVarRef(varp->fileline(), varp, false); - nodep->replaceWith(refp); pushDeletep(nodep); VL_DANGLING(nodep); - } - } + m_ds.m_dotSymp = m_statep->getNodeSym(modportp); + m_ds.m_dotPos = DP_SCOPE; + ok = true; + AstVar* varp = makeIfaceModportVar(nodep->fileline(), + cellp, ifacep, modportp); + AstVarRef* refp = new AstVarRef(varp->fileline(), varp, false); + nodep->replaceWith(refp); pushDeletep(nodep); VL_DANGLING(nodep); + } + } else if (AstEnumItem* valuep = VN_CAST(foundp->nodep(), EnumItem)) { - if (allowVar) { - AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep()); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - ok = true; - m_ds.m_dotText = ""; - } - } - // - if (!ok) { + if (allowVar) { + AstNode* newp = new AstEnumItemRef(nodep->fileline(), + valuep, foundp->packagep()); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + ok = true; + m_ds.m_dotText = ""; + } + } + // + if (!ok) { // Cells/interfaces can't be implicit bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false; - bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText=="" && !isCell); - bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name())); - if (err) { - if (foundp) { - nodep->v3error("Found definition of '"<prettyName() - <<"'"<<" as a "<nodep()->typeName() - <<" but expected a "<prettyName()); - } else { - nodep->v3error("Can't find definition of '"<<(baddot!=""?baddot:nodep->prettyName())<<"' in dotted " - <prettyName()); - okSymp->cellErrorScopes(nodep, AstNode::prettyName(m_ds.m_dotText)); - } - m_ds.m_dotErr = true; - } - if (checkImplicit) { // Else if a scope is allowed, making a signal won't help error cascade - // Create if implicit, and also if error (so only complain once) - AstVarRef* newp = new AstVarRef(nodep->fileline(), nodep->name(), false); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); + bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText=="" && !isCell); + bool err = !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name())); + if (err) { + if (foundp) { + nodep->v3error("Found definition of '" + <prettyName() + <<"'"<<" as a "<nodep()->typeName() + <<" but expected a "<prettyName()); + } else { + nodep->v3error("Can't find definition of '" + <<(baddot!="" ? baddot : nodep->prettyName()) + <<"' in dotted " + <prettyName()); + okSymp->cellErrorScopes(nodep, AstNode::prettyName(m_ds.m_dotText)); + } + m_ds.m_dotErr = true; + } + if (checkImplicit) { // Else if a scope is allowed, making a signal won't help error cascade + // Create if implicit, and also if error (so only complain once) + AstVarRef* newp = new AstVarRef(nodep->fileline(), nodep->name(), false); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); createImplicitVar(m_curSymp, newp, m_modp, m_modSymp, err); - } - } - } - if (start) { - m_ds = lastStates; - } + } + } + } + if (start) { + m_ds = lastStates; + } } virtual void visit(AstVarRef* nodep) { - // VarRef: Resolve its reference - // ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find - // errors here now that we have a VarRef. - // No checkNoDot; created and iterated from a parseRef + // VarRef: Resolve its reference + // ParseRefs are used the first pass (forPrimary) so we shouldn't get can't find + // errors here now that we have a VarRef. + // No checkNoDot; created and iterated from a parseRef iterateChildren(nodep); - if (!nodep->varp()) { + if (!nodep->varp()) { UINFO(9," linkVarRef se"<findIdFallback(nodep->name()); - if (AstVar* varp = foundp ? foundToVarp(foundp, nodep, nodep->lvalue()) : NULL) { - nodep->varp(varp); - nodep->packagep(foundp->packagep()); // Generally set by parse, but might be an import - } - if (!nodep->varp()) { - nodep->v3error("Can't find definition of signal, again: "<prettyName()); - } - } + if (!m_curSymp) nodep->v3fatalSrc("NULL lookup symbol table"); + VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); + if (AstVar* varp = foundp ? foundToVarp(foundp, nodep, nodep->lvalue()) : NULL) { + nodep->varp(varp); + nodep->packagep(foundp->packagep()); // Generally set by parse, but might be an import + } + if (!nodep->varp()) { + nodep->v3error("Can't find definition of signal, again: "<prettyName()); + } + } } virtual void visit(AstVarXRef* nodep) { - // VarRef: Resolve its reference - // We always link even if varp() is set, because the module we choose may change - // due to creating new modules, flattening, etc. - if (nodep->user3SetOnce()) return; - UINFO(8," "<varp(NULL); // Module that is not in hierarchy. We'll be dead code eliminating it later. - } else { - string baddot; - VSymEnt* okSymp; - VSymEnt* dotSymp = m_curSymp; // Start search at current scope - if (nodep->inlinedDots()!="") { // Correct for current scope - dotSymp = m_modSymp; // Dotted lookup is always relative to module, as maybe variable name lower down with same scope name we want to ignore (t_math_divw) - string inl = AstNode::dedotName(nodep->inlinedDots()); - dotSymp = m_statep->findDotted(dotSymp, inl, baddot, okSymp); - if (!dotSymp) { - nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); - } - } - dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL - if (!m_statep->forScopeCreation()) { - VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); - AstVar* varp = foundp ? foundToVarp(foundp, nodep, nodep->lvalue()) : NULL; - nodep->varp(varp); - UINFO(7," Resolved "<varp()) { - nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); - okSymp->cellErrorScopes(nodep); - } - // V3Inst may have expanded arrays of interfaces to AstVarXRef's even though they are in the same module - // detect this and convert to normal VarRefs - if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) { + // VarRef: Resolve its reference + // We always link even if varp() is set, because the module we choose may change + // due to creating new modules, flattening, etc. + if (nodep->user3SetOnce()) return; + UINFO(8," "<varp(NULL); // Module that is not in hierarchy. We'll be dead code eliminating it later. + } else { + string baddot; + VSymEnt* okSymp; + VSymEnt* dotSymp = m_curSymp; // Start search at current scope + if (nodep->inlinedDots()!="") { // Correct for current scope + dotSymp = m_modSymp; // Dotted lookup is always relative to module, as maybe variable name lower down with same scope name we want to ignore (t_math_divw) + string inl = AstNode::dedotName(nodep->inlinedDots()); + dotSymp = m_statep->findDotted(dotSymp, inl, baddot, okSymp); + if (!dotSymp) { + nodep->v3fatalSrc("Couldn't resolve inlined scope '" + <inlinedDots()); + } + } + dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL + if (!m_statep->forScopeCreation()) { + VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); + AstVar* varp = foundp ? foundToVarp(foundp, nodep, nodep->lvalue()) : NULL; + nodep->varp(varp); + UINFO(7," Resolved "<varp()) { + nodep->v3error("Can't find definition of '" + <dotted()+"."+nodep->prettyName()); + okSymp->cellErrorScopes(nodep); + } + // V3Inst may have expanded arrays of interfaces to + // AstVarXRef's even though they are in the same module detect + // this and convert to normal VarRefs + if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) { if (VN_IS(nodep->dtypep(), IfaceRefDType)) { - AstVarRef* newrefp = new AstVarRef(nodep->fileline(), nodep->varp(), nodep->lvalue()); - nodep->replaceWith(newrefp); - nodep->deleteTree(); VL_DANGLING(nodep); - } - } - } else { - string baddot; - VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); + AstVarRef* newrefp = new AstVarRef(nodep->fileline(), + nodep->varp(), nodep->lvalue()); + nodep->replaceWith(newrefp); + nodep->deleteTree(); VL_DANGLING(nodep); + } + } + } else { + string baddot; + VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); AstVarScope* vscp = foundp ? VN_CAST(foundp->nodep(), VarScope) : NULL; - if (!vscp) { + if (!vscp) { nodep->v3error("Can't find varpin scope of '"<dotted()+"."+nodep->prettyName()); - okSymp->cellErrorScopes(nodep); - } else { - while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal - UINFO(7," Resolved pre-alias "<dotted()+"."+nodep->prettyName()); + okSymp->cellErrorScopes(nodep); + } else { + while (vscp->user2p()) { // If V3Inline aliased it, pick up the new signal + UINFO(7," Resolved pre-alias "<user2p(), VarScope); - } - // Convert the VarXRef to a VarRef, so we don't need later optimizations to deal with VarXRef. - nodep->varp(vscp->varp()); - nodep->varScopep(vscp); - UINFO(7," Resolved "<fileline(), vscp, nodep->lvalue()); - nodep->replaceWith(newvscp); - nodep->deleteTree(); VL_DANGLING(nodep); - UINFO(9," new "<varp(vscp->varp()); + nodep->varScopep(vscp); + UINFO(7," Resolved "<fileline(), vscp, nodep->lvalue()); + nodep->replaceWith(newvscp); + nodep->deleteTree(); VL_DANGLING(nodep); + UINFO(9," new "<forPrimary() && nodep->isIO() && !m_ftaskp && !nodep->user4()) { - nodep->v3error("Input/output/inout does not appear in port list: "<prettyName()); - } + if (m_statep->forPrimary() && nodep->isIO() && !m_ftaskp && !nodep->user4()) { + nodep->v3error("Input/output/inout does not appear in port list: " + <prettyName()); + } } virtual void visit(AstNodeFTaskRef* nodep) { - if (nodep->user3SetOnce()) return; - UINFO(8," "<lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); - if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + if (nodep->user3SetOnce()) return; + UINFO(8," "<lhsp(), PackageRef)) { + m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + } + if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) { + m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + } nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()); - m_ds.m_dotPos = DP_SCOPE; - m_ds.m_dotp = NULL; - } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) { - if (m_ds.m_unresolved && m_ds.m_unlinkedScope) { - AstNodeFTaskRef* newftaskp = nodep->cloneTree(false); - newftaskp->dotted(m_ds.m_dotText); - AstNode* newp = new AstUnlinkedRef(nodep->fileline(), newftaskp, nodep->name(), m_ds.m_unlinkedScope->unlinkFrBack()); - m_ds.m_unlinkedScope = NULL; - m_ds.m_unresolved = false; - nodep->replaceWith(newp); - return; - } else { - nodep->dotted(m_ds.m_dotText); // Maybe "" - } - } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_MEMBER) { - // Found a Var, everything following is method call. {scope}.{var}.HERE {method} ( ARGS ) - AstNode* varEtcp = m_ds.m_dotp->lhsp()->unlinkFrBack(); - AstNode* argsp = NULL; if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext(); - AstNode* newp = new AstMethodSel(nodep->fileline(), varEtcp, VFlagChildDType(), nodep->name(), argsp); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - return; - } else { - checkNoDot(nodep); - } - if (nodep->packagep() && nodep->taskp()) { - // References into packages don't care about cell hierarchy. - } else if (!m_modSymp) { - UINFO(9,"Dead module for "<taskp(NULL); // Module that is not in hierarchy. We'll be dead code eliminating it later. - } else if (nodep->dotted()=="" && nodep->taskp()) { - // Earlier should have setup the links - // Might be under a BEGIN we're not processing, so don't relink it - } else { - string baddot; - VSymEnt* okSymp = NULL; - VSymEnt* dotSymp = m_curSymp; // Start search at module, as a variable of same name under a subtask isn't a relevant hit - // // however a function under a begin/end is. So we want begins, but not the function - if (nodep->packagep()) { // Look only in specified package - dotSymp = m_statep->getNodeSym(nodep->packagep()); - } else { - if (nodep->inlinedDots()!="") { // Correct for current scope - dotSymp = m_modSymp; // Dotted lookup is always relative to module, as maybe variable name lower down with same scope name we want to ignore (t_math_divw) - string inl = AstNode::dedotName(nodep->inlinedDots()); - UINFO(8,"\t\tInlined "<findDotted(dotSymp, inl, baddot, okSymp); - if (!dotSymp) { - okSymp->cellErrorScopes(nodep); - nodep->v3fatalSrc("Couldn't resolve inlined scope '"<inlinedDots()); - } - } - dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), baddot, okSymp); // Maybe NULL - } + m_ds.m_dotPos = DP_SCOPE; + m_ds.m_dotp = NULL; + } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) { + if (m_ds.m_unresolved && m_ds.m_unlinkedScope) { + AstNodeFTaskRef* newftaskp = nodep->cloneTree(false); + newftaskp->dotted(m_ds.m_dotText); + AstNode* newp = new AstUnlinkedRef(nodep->fileline(), newftaskp, + nodep->name(), + m_ds.m_unlinkedScope->unlinkFrBack()); + m_ds.m_unlinkedScope = NULL; + m_ds.m_unresolved = false; + nodep->replaceWith(newp); + return; + } else { + nodep->dotted(m_ds.m_dotText); // Maybe "" + } + } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_MEMBER) { + // Found a Var, everything following is method call. + // {scope}.{var}.HERE {method} ( ARGS ) + AstNode* varEtcp = m_ds.m_dotp->lhsp()->unlinkFrBack(); + AstNode* argsp = NULL; + if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext(); + AstNode* newp = new AstMethodSel(nodep->fileline(), varEtcp, + VFlagChildDType(), nodep->name(), argsp); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + return; + } else { + checkNoDot(nodep); + } + if (nodep->packagep() && nodep->taskp()) { + // References into packages don't care about cell hierarchy. + } else if (!m_modSymp) { + UINFO(9,"Dead module for "<taskp(NULL); // Module that is not in hierarchy. We'll be dead code eliminating it later. + } else if (nodep->dotted()=="" && nodep->taskp()) { + // Earlier should have setup the links + // Might be under a BEGIN we're not processing, so don't relink it + } else { + string baddot; + VSymEnt* okSymp = NULL; + VSymEnt* dotSymp = m_curSymp; // Start search at module, as a variable + // of same name under a subtask isn't a relevant hit however a + // function under a begin/end is. So we want begins, but not + // the function + if (nodep->packagep()) { // Look only in specified package + dotSymp = m_statep->getNodeSym(nodep->packagep()); + } else { + if (nodep->inlinedDots()!="") { // Correct for current scope + dotSymp = m_modSymp; // Dotted lookup is always relative to module, as maybe variable name lower down with same scope name we want to ignore (t_math_divw) + string inl = AstNode::dedotName(nodep->inlinedDots()); + UINFO(8,"\t\tInlined "<findDotted(dotSymp, inl, baddot, okSymp); + if (!dotSymp) { + okSymp->cellErrorScopes(nodep); + nodep->v3fatalSrc("Couldn't resolve inlined scope '" + <inlinedDots()); + } + } + dotSymp = m_statep->findDotted(dotSymp, nodep->dotted(), + baddot, okSymp); // Maybe NULL + } VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot); AstNodeFTask* taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : NULL; // Maybe NULL - if (taskp) { - nodep->taskp(taskp); - nodep->packagep(foundp->packagep()); - UINFO(7," Resolved "<taskp(taskp); + nodep->packagep(foundp->packagep()); + UINFO(7," Resolved "<v3error("Found definition of '"<prettyName() - <<"'"<<" as a "<nodep()->typeName() - <<" but expected a task/function"); - } else if (nodep->dotted() == "") { - nodep->v3error("Can't find definition of task/function: "<prettyName()); - } else { + if (foundp) { + nodep->v3error("Found definition of '" + <prettyName() + <<"'"<<" as a "<nodep()->typeName() + <<" but expected a task/function"); + } else if (nodep->dotted() == "") { + nodep->v3error("Can't find definition of task/function: " + <prettyName()); + } else { nodep->v3error("Can't find definition of '"<dotted()+"."+nodep->prettyName()); - okSymp->cellErrorScopes(nodep); - } - } - taskFuncSwapCheck(nodep); - } - DotStates lastStates = m_ds; - { - m_ds.init(m_curSymp); + <<"' in dotted task/function: " + <dotted()+"."+nodep->prettyName()); + okSymp->cellErrorScopes(nodep); + } + } + taskFuncSwapCheck(nodep); + } + DotStates lastStates = m_ds; + { + m_ds.init(m_curSymp); iterateChildren(nodep); - } - m_ds = lastStates; + } + m_ds = lastStates; } virtual void visit(AstSelBit* nodep) { - if (nodep->user3SetOnce()) return; + if (nodep->user3SetOnce()) return; iterateAndNextNull(nodep->lhsp()); - if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} - UINFO(9," deferring until after a V3Param pass: "<fromp()); - DotStates lastStates = m_ds; - { - m_ds.init(m_curSymp); + DotStates lastStates = m_ds; + { + m_ds.init(m_curSymp); iterateAndNextNull(nodep->bitp()); iterateAndNextNull(nodep->attrp()); - } - m_ds = lastStates; - if (m_ds.m_unresolved && m_ds.m_dotPos == DP_SCOPE) { - AstNode* exprp = nodep->bitp()->unlinkFrBack(); - AstCellArrayRef* newp = new AstCellArrayRef(nodep->fileline(), nodep->fromp()->name(), exprp); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } + } + m_ds = lastStates; + if (m_ds.m_unresolved && m_ds.m_dotPos == DP_SCOPE) { + AstNode* exprp = nodep->bitp()->unlinkFrBack(); + AstCellArrayRef* newp + = new AstCellArrayRef(nodep->fileline(), nodep->fromp()->name(), exprp); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } } virtual void visit(AstNodePreSel* nodep) { - // Excludes simple AstSelBit, see above - if (nodep->user3SetOnce()) return; - if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} - nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the cell part of a dotted reference"); - m_ds.m_dotErr = true; - return; - } + // Excludes simple AstSelBit, see above + if (nodep->user3SetOnce()) return; + if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} + nodep->v3error("Syntax Error: Range ':', '+:' etc are not allowed in the cell part of a dotted reference"); + m_ds.m_dotErr = true; + return; + } iterateAndNextNull(nodep->lhsp()); - DotStates lastStates = m_ds; - { - m_ds.init(m_curSymp); + DotStates lastStates = m_ds; + { + m_ds.init(m_curSymp); iterateAndNextNull(nodep->rhsp()); iterateAndNextNull(nodep->thsp()); iterateAndNextNull(nodep->attrp()); - } - m_ds = lastStates; + } + m_ds = lastStates; } virtual void visit(AstMemberSel* nodep) { - // checkNoDot not appropriate, can be under a dot + // checkNoDot not appropriate, can be under a dot iterateChildren(nodep); } virtual void visit(AstBegin* nodep) { - UINFO(5," "<getNodeSym(nodep); + UINFO(5," "<getNodeSym(nodep); UINFO(5," cur=se"<getNodeSym(nodep); + UINFO(5," "<getNodeSym(nodep); iterateChildren(nodep); - } - m_ds.m_dotSymp = m_curSymp = oldCurSymp; - m_ftaskp = NULL; + } + m_ds.m_dotSymp = m_curSymp = oldCurSymp; + m_ftaskp = NULL; } virtual void visit(AstRefDType* nodep) { - // Resolve its reference - if (nodep->user3SetOnce()) return; - if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) { - if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); - if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + // Resolve its reference + if (nodep->user3SetOnce()) return; + if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) { + if (!VN_IS(m_ds.m_dotp->lhsp(), PackageRef)) { + m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + } + if (!VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()) { + m_ds.m_dotp->lhsp()->v3fatalSrc("Bad package link"); + } nodep->packagep(VN_CAST(m_ds.m_dotp->lhsp(), PackageRef)->packagep()); - m_ds.m_dotPos = DP_SCOPE; - m_ds.m_dotp = NULL; - } else { - checkNoDot(nodep); - } - if (!nodep->defp()) { - VSymEnt* foundp; - if (nodep->packagep()) { - foundp = m_statep->getNodeSym(nodep->packagep())->findIdFlat(nodep->name()); - } else { - foundp = m_curSymp->findIdFallback(nodep->name()); - } + m_ds.m_dotPos = DP_SCOPE; + m_ds.m_dotp = NULL; + } else { + checkNoDot(nodep); + } + if (!nodep->defp()) { + VSymEnt* foundp; + if (nodep->packagep()) { + foundp = m_statep->getNodeSym(nodep->packagep())->findIdFlat(nodep->name()); + } else { + foundp = m_curSymp->findIdFallback(nodep->name()); + } if (AstTypedef* defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : NULL) { - nodep->refDTypep(defp->subDTypep()); - nodep->packagep(foundp->packagep()); - } - else if (AstParamTypeDType* defp = foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : NULL) { - nodep->refDTypep(defp); - nodep->packagep(foundp->packagep()); - } - else { - nodep->v3error("Can't find typedef: "<prettyName()); - } - } + nodep->refDTypep(defp->subDTypep()); + nodep->packagep(foundp->packagep()); + } + else if (AstParamTypeDType* defp = foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) + : NULL) { + nodep->refDTypep(defp); + nodep->packagep(foundp->packagep()); + } + else { + nodep->v3error("Can't find typedef: "<prettyName()); + } + } iterateChildren(nodep); } virtual void visit(AstDpiExport* nodep) { - // AstDpiExport: Make sure the function referenced exists, then dump it + // AstDpiExport: Make sure the function referenced exists, then dump it iterateChildren(nodep); - checkNoDot(nodep); - VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); + checkNoDot(nodep); + VSymEnt* foundp = m_curSymp->findIdFallback(nodep->name()); AstNodeFTask* taskp = foundp ? VN_CAST(foundp->nodep(), NodeFTask) : NULL; - if (!taskp) { nodep->v3error("Can't find definition of exported task/function: "<prettyName()); } - else if (taskp->dpiExport()) { - nodep->v3error("Function was already DPI Exported, duplicate not allowed: "<prettyName()); - } else { - taskp->dpiExport(true); - if (nodep->cname()!="") taskp->cname(nodep->cname()); - } - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + if (!taskp) { nodep->v3error("Can't find definition of exported task/function: " + <prettyName()); } + else if (taskp->dpiExport()) { + nodep->v3error("Function was already DPI Exported, duplicate not allowed: " + <prettyName()); + } else { + taskp->dpiExport(true); + if (nodep->cname()!="") taskp->cname(nodep->cname()); + } + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstPackageImport* nodep) { - // No longer needed - checkNoDot(nodep); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // No longer needed + checkNoDot(nodep); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstPackageExport* nodep) { - // No longer needed - checkNoDot(nodep); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // No longer needed + checkNoDot(nodep); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstPackageExportStarStar* nodep) { - // No longer needed - checkNoDot(nodep); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + // No longer needed + checkNoDot(nodep); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstCellRef* nodep) { - UINFO(5," AstCellRef: "<dumpTreeFile(v3Global.debugFilename("prelinkdot-find.tree")); } if (step == LDS_PRIMARY || step == LDS_PARAMED) { - // Initial link stage, resolve parameters - LinkDotParamVisitor visitors(rootp,&state); + // Initial link stage, resolve parameters + LinkDotParamVisitor visitors(rootp,&state); if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree")); } } else if (step == LDS_ARRAYED) {} else if (step == LDS_SCOPED) { - // Well after the initial link when we're ready to operate on the flat design, - // process AstScope's. This needs to be separate pass after whole hierarchy graph created. - LinkDotScopeVisitor visitors(rootp,&state); + // Well after the initial link when we're ready to operate on the flat design, + // process AstScope's. This needs to be separate pass after whole hierarchy graph created. + LinkDotScopeVisitor visitors(rootp,&state); if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree")); } diff --git a/src/V3LinkDot.h b/src/V3LinkDot.h index b6c29d5cf..0a8a38d49 100644 --- a/src/V3LinkDot.h +++ b/src/V3LinkDot.h @@ -58,4 +58,4 @@ public: } }; -#endif // Guard +#endif // Guard diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 5c1d86f0f..a63067b78 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -20,9 +20,9 @@ // V3LinkJump's Transformations: // // Each module: -// Look for BEGINs -// BEGIN(VAR...) -> VAR ... {renamed} -// FOR -> WHILEs +// Look for BEGINs +// BEGIN(VAR...) -> VAR ... {renamed} +// FOR -> WHILEs // //************************************************************************* @@ -45,194 +45,205 @@ private: typedef std::vector BeginStack; // STATE - AstNodeModule* m_modp; // Current module - AstNodeFTask* m_ftaskp; // Current function/task - AstWhile* m_loopp; // Current loop - bool m_loopInc; // In loop increment - int m_repeatNum; // Repeat counter - BeginStack m_beginStack; // All begin blocks above current node + AstNodeModule* m_modp; // Current module + AstNodeFTask* m_ftaskp; // Current function/task + AstWhile* m_loopp; // Current loop + bool m_loopInc; // In loop increment + int m_repeatNum; // Repeat counter + BeginStack m_beginStack; // All begin blocks above current node // METHODS VL_DEBUG_FUNC; // Declare debug() AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) { - // Put label under given node, and if WHILE optionally at end of iteration - UINFO(4,"Create label for "<stmtsp(); else if (VN_IS(nodep, NodeFTask)) underp = VN_CAST(nodep, NodeFTask)->stmtsp(); else if (VN_IS(nodep, While)) { - if (endOfIter) { - // Note we jump to end of bodysp; a FOR loop has its increment under incsp() which we don't skip + if (endOfIter) { + // Note we jump to end of bodysp; a FOR loop has its + // increment under incsp() which we don't skip underp = VN_CAST(nodep, While)->bodysp(); - } else { - underp = nodep; under_and_next=false; // IE we skip the entire while - } - } - else { - nodep->v3fatalSrc("Unknown jump point for break/disable/continue"); - return NULL; - } - // Skip over variables as we'll just move them in a momement - // Also this would otherwise prevent us from using a label twice - // see t_func_return test. + } else { + underp = nodep; under_and_next = false; // IE we skip the entire while + } + } + else { + nodep->v3fatalSrc("Unknown jump point for break/disable/continue"); + return NULL; + } + // Skip over variables as we'll just move them in a momement + // Also this would otherwise prevent us from using a label twice + // see t_func_return test. while (underp && VN_IS(underp, Var)) underp = underp->nextp(); - if (underp) UINFO(5," Underpoint is "<v3fatalSrc("Break/disable/continue not under expected statement"); - return NULL; + if (!underp) { + nodep->v3fatalSrc("Break/disable/continue not under expected statement"); + return NULL; } else if (VN_IS(underp, JumpLabel)) { return VN_CAST(underp, JumpLabel); - } else { // Move underp stuff to be under a new label - AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL); + } else { // Move underp stuff to be under a new label + AstJumpLabel* labelp = new AstJumpLabel(nodep->fileline(), NULL); - AstNRelinker repHandle; - if (under_and_next) underp->unlinkFrBackWithNext(&repHandle); - else underp->unlinkFrBack(&repHandle); - repHandle.relink(labelp); + AstNRelinker repHandle; + if (under_and_next) underp->unlinkFrBackWithNext(&repHandle); + else underp->unlinkFrBack(&repHandle); + repHandle.relink(labelp); - labelp->addStmtsp(underp); - // Keep any AstVars under the function not under the new JumpLabel - for (AstNode* nextp, *varp=underp; varp; varp = nextp) { - nextp = varp->nextp(); + labelp->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)) { - labelp->addPrev(varp->unlinkFrBack()); - } - } - return labelp; - } + labelp->addPrev(varp->unlinkFrBack()); + } + } + return labelp; + } } // VISITORS virtual void visit(AstNodeModule* nodep) { - if (nodep->dead()) return; - m_modp = nodep; - m_repeatNum = 0; + if (nodep->dead()) return; + m_modp = nodep; + m_repeatNum = 0; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstNodeFTask* nodep) { - m_ftaskp = nodep; + m_ftaskp = nodep; iterateChildren(nodep); - m_ftaskp = NULL; + m_ftaskp = NULL; } virtual void visit(AstBegin* nodep) { - UINFO(8," "< loop=count,WHILE(loop>0) { body, loop-- } - // Note var can be signed or unsigned based on original number. - AstNode* countp = nodep->countp()->unlinkFrBackWithNext(); - string name = string("__Vrepeat")+cvtToStr(m_repeatNum++); - // Spec says value is integral, if negative is ignored - AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name, - nodep->findSigned32DType()); - varp->usedLoopIdx(true); - m_modp->addStmtp(varp); - AstNode* initsp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true), - countp); - AstNode* decp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true), - new AstSub(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false), + // So later optimizations don't need to deal with them, + // REPEAT(count,body) -> loop=count,WHILE(loop>0) { body, loop-- } + // Note var can be signed or unsigned based on original number. + AstNode* countp = nodep->countp()->unlinkFrBackWithNext(); + string name = string("__Vrepeat")+cvtToStr(m_repeatNum++); + // Spec says value is integral, if negative is ignored + AstVar* varp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, name, + nodep->findSigned32DType()); + varp->usedLoopIdx(true); + m_modp->addStmtp(varp); + AstNode* initsp = new AstAssign(nodep->fileline(), + new AstVarRef(nodep->fileline(), varp, true), + countp); + AstNode* decp = new AstAssign(nodep->fileline(), + new AstVarRef(nodep->fileline(), varp, true), + new AstSub(nodep->fileline(), + new AstVarRef(nodep->fileline(), varp, false), new AstConst(nodep->fileline(), 1))); - V3Number zero (nodep, 32, 0); zero.isSigned(true); + V3Number zero (nodep, 32, 0); zero.isSigned(true); AstNode* zerosp = new AstConst(nodep->fileline(), zero); - AstNode* condp = new AstGtS(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, false), - zerosp); - AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext(); - AstNode* newp = new AstWhile(nodep->fileline(), - condp, - bodysp, - decp); - initsp = initsp->addNext(newp); - newp = initsp; - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); + AstNode* condp = new AstGtS(nodep->fileline(), + new AstVarRef(nodep->fileline(), varp, false), + zerosp); + AstNode* bodysp = nodep->bodysp(); if (bodysp) bodysp->unlinkFrBackWithNext(); + AstNode* newp = new AstWhile(nodep->fileline(), + condp, + bodysp, + decp); + initsp = initsp->addNext(newp); + newp = initsp; + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstWhile* nodep) { - // Don't need to track AstRepeat/AstFor as they have already been converted - AstWhile* lastLoopp = m_loopp; - bool lastInc = m_loopInc; - m_loopp = nodep; - m_loopInc = false; + // Don't need to track AstRepeat/AstFor as they have already been converted + AstWhile* lastLoopp = m_loopp; + bool lastInc = m_loopInc; + m_loopp = nodep; + m_loopInc = false; iterateAndNextNull(nodep->precondsp()); iterateAndNextNull(nodep->condp()); iterateAndNextNull(nodep->bodysp()); - m_loopInc = true; + m_loopInc = true; iterateAndNextNull(nodep->incsp()); - m_loopInc = lastInc; - m_loopp = lastLoopp; + m_loopInc = lastInc; + m_loopp = lastLoopp; } virtual void visit(AstReturn* nodep) { iterateChildren(nodep); AstFunc* funcp = VN_CAST(m_ftaskp, Func); - if (!m_ftaskp) { nodep->v3error("Return isn't underneath a task or function"); } - else if (funcp && !nodep->lhsp()) { nodep->v3error("Return underneath a function should have return value"); } - else if (!funcp && nodep->lhsp()) { nodep->v3error("Return underneath a task shouldn't have return value"); } - else { - if (funcp && nodep->lhsp()) { - // Set output variable to return value - nodep->addPrev(new AstAssign(nodep->fileline(), - new AstVarRef(nodep->fileline(), VN_CAST(funcp->fvarp(), Var), true), - nodep->lhsp()->unlinkFrBackWithNext())); - } - // Jump to the end of the function call - AstJumpLabel* labelp = findAddLabel(m_ftaskp, false); - nodep->addPrev(new AstJumpGo(nodep->fileline(), labelp)); - } - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + if (!m_ftaskp) { + nodep->v3error("Return isn't underneath a task or function"); + } else if (funcp && !nodep->lhsp()) { + nodep->v3error("Return underneath a function should have return value"); + } else if (!funcp && nodep->lhsp()) { + nodep->v3error("Return underneath a task shouldn't have return value"); + } else { + if (funcp && nodep->lhsp()) { + // Set output variable to return value + nodep->addPrev(new AstAssign(nodep->fileline(), + new AstVarRef(nodep->fileline(), + VN_CAST(funcp->fvarp(), Var), true), + nodep->lhsp()->unlinkFrBackWithNext())); + } + // Jump to the end of the function call + AstJumpLabel* labelp = findAddLabel(m_ftaskp, false); + nodep->addPrev(new AstJumpGo(nodep->fileline(), labelp)); + } + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstBreak* nodep) { iterateChildren(nodep); - if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); } - else { - // Jump to the end of the loop - AstJumpLabel* labelp = findAddLabel(m_loopp, false); - nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp)); - } - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + if (!m_loopp) { nodep->v3error("break isn't underneath a loop"); } + else { + // Jump to the end of the loop + AstJumpLabel* labelp = findAddLabel(m_loopp, false); + nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp)); + } + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstContinue* nodep) { iterateChildren(nodep); - if (!m_loopp) { nodep->v3error("continue isn't underneath a loop"); } - else { - // Jump to the end of this iteration - // If a "for" loop then need to still do the post-loop increment - AstJumpLabel* labelp = findAddLabel(m_loopp, true); - nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp)); - } - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + if (!m_loopp) { nodep->v3error("continue isn't underneath a loop"); } + else { + // Jump to the end of this iteration + // If a "for" loop then need to still do the post-loop increment + AstJumpLabel* labelp = findAddLabel(m_loopp, true); + nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp)); + } + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstDisable* nodep) { - UINFO(8," DISABLE "<name() == nodep->name()) { - beginp = *it; - break; - } - } - //if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout," labeli: "); } - if (!beginp) { nodep->v3error("disable isn't underneath a begin with name: "<prettyName()); } - else { - // Jump to the end of the named begin - AstJumpLabel* labelp = findAddLabel(beginp, false); - nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp)); - } - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - //if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout," labelo: "); } + AstBegin* beginp = NULL; + for (BeginStack::reverse_iterator it = m_beginStack.rbegin(); + it != m_beginStack.rend(); ++it) { + UINFO(9," UNDERBLK "<<*it<name() == nodep->name()) { + beginp = *it; + break; + } + } + //if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout, " labeli: "); } + if (!beginp) { nodep->v3error("disable isn't underneath a begin with name: " + <prettyName()); } + else { + // Jump to the end of the named begin + AstJumpLabel* labelp = findAddLabel(beginp, false); + nodep->addNextHere(new AstJumpGo(nodep->fileline(), labelp)); + } + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + //if (debug()>=9) { UINFO(0,"\n"); beginp->dumpTree(cout, " labelo: "); } } virtual void visit(AstVarRef* nodep) { - if (m_loopInc && nodep->varp()) nodep->varp()->usedLoopIdx(true); + if (m_loopInc && nodep->varp()) nodep->varp()->usedLoopIdx(true); } virtual void visit(AstConst* nodep) {} @@ -242,11 +253,11 @@ private: public: // CONSTUCTORS explicit LinkJumpVisitor(AstNetlist* nodep) { - m_modp = NULL; - m_ftaskp = NULL; - m_loopp = NULL; - m_loopInc = false; - m_repeatNum = 0; + m_modp = NULL; + m_ftaskp = NULL; + m_loopp = NULL; + m_loopInc = false; + m_repeatNum = 0; iterate(nodep); } virtual ~LinkJumpVisitor() {} diff --git a/src/V3LinkJump.h b/src/V3LinkJump.h index bfcef3af6..3f202e9b3 100644 --- a/src/V3LinkJump.h +++ b/src/V3LinkJump.h @@ -34,4 +34,4 @@ public: static void linkJump(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 88d666e8f..34b652696 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -18,8 +18,8 @@ // //************************************************************************* // LinkLValue TRANSFORMATIONS: -// Top-down traversal -// Set lvalue() attributes on appropriate VARREFs. +// Top-down traversal +// Set lvalue() attributes on appropriate VARREFs. //************************************************************************* #include "config_build.h" @@ -42,8 +42,8 @@ private: // NODE STATE // STATE - bool m_setRefLvalue; // Set VarRefs to lvalues for pin assignments - AstNodeFTask* m_ftaskp; // Function or task we're inside + bool m_setRefLvalue; // Set VarRefs to lvalues for pin assignments + AstNodeFTask* m_ftaskp; // Function or task we're inside // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -51,14 +51,14 @@ private: // VISITs // Result handing virtual void visit(AstNodeVarRef* nodep) { - // VarRef: LValue its reference - if (m_setRefLvalue) { - nodep->lvalue(true); - } - if (nodep->varp()) { + // VarRef: LValue its reference + if (m_setRefLvalue) { + nodep->lvalue(true); + } + if (nodep->varp()) { if (nodep->lvalue() && !m_ftaskp && nodep->varp()->isReadOnly()) { - nodep->v3warn(ASSIGNIN,"Assigning to input/const variable: " + nodep->v3warn(ASSIGNIN, "Assigning to input/const variable: " <prettyName()); } } @@ -72,64 +72,64 @@ private: // Now that we do, and it's from a output, we know it's a lvalue m_setRefLvalue = true; iterateChildren(nodep); - m_setRefLvalue = false; - } else { + m_setRefLvalue = false; + } else { iterateChildren(nodep); - } + } } virtual void visit(AstNodeAssign* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->lhsp()); - m_setRefLvalue = false; + m_setRefLvalue = false; iterateAndNextNull(nodep->rhsp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstFOpen* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->filep()); - m_setRefLvalue = false; + m_setRefLvalue = false; iterateAndNextNull(nodep->filenamep()); iterateAndNextNull(nodep->modep()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstFClose* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->filep()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstFFlush* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->filep()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstFGetC* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->filep()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstFGetS* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->filep()); iterateAndNextNull(nodep->strgp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstFRead* nodep) { bool last_setRefLvalue = m_setRefLvalue; @@ -141,111 +141,111 @@ private: m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstFScanF* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->filep()); iterateAndNextNull(nodep->exprsp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstSScanF* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->exprsp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstSysIgnore* nodep) { - // Can't know if lvalue or not; presume so as stricter - bool last_setRefLvalue = m_setRefLvalue; + // Can't know if lvalue or not; presume so as stricter + bool last_setRefLvalue = m_setRefLvalue; iterateChildren(nodep); - m_setRefLvalue = last_setRefLvalue; + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstReadMem* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->memp()); - m_setRefLvalue = false; + m_setRefLvalue = false; iterateAndNextNull(nodep->filenamep()); iterateAndNextNull(nodep->lsbp()); iterateAndNextNull(nodep->msbp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstValuePlusArgs* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = false; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = false; iterateAndNextNull(nodep->searchp()); - m_setRefLvalue = true; + m_setRefLvalue = true; iterateAndNextNull(nodep->outp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstSFormat* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { - m_setRefLvalue = true; + bool last_setRefLvalue = m_setRefLvalue; + { + m_setRefLvalue = true; iterateAndNextNull(nodep->lhsp()); - m_setRefLvalue = false; + m_setRefLvalue = false; iterateAndNextNull(nodep->fmtp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } // Nodes that change LValue state virtual void visit(AstSel* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { + bool last_setRefLvalue = m_setRefLvalue; + { iterateAndNextNull(nodep->lhsp()); - // Only set lvalues on the from - m_setRefLvalue = false; + // Only set lvalues on the from + m_setRefLvalue = false; iterateAndNextNull(nodep->rhsp()); iterateAndNextNull(nodep->thsp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstNodeSel* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { // Only set lvalues on the from + bool last_setRefLvalue = m_setRefLvalue; + { // Only set lvalues on the from iterateAndNextNull(nodep->lhsp()); - m_setRefLvalue = false; + m_setRefLvalue = false; iterateAndNextNull(nodep->rhsp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstCellArrayRef* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { // selp is not an lvalue - m_setRefLvalue = false; + bool last_setRefLvalue = m_setRefLvalue; + { // selp is not an lvalue + m_setRefLvalue = false; iterateAndNextNull(nodep->selp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstNodePreSel* nodep) { - bool last_setRefLvalue = m_setRefLvalue; - { // Only set lvalues on the from + bool last_setRefLvalue = m_setRefLvalue; + { // Only set lvalues on the from iterateAndNextNull(nodep->lhsp()); - m_setRefLvalue = false; + m_setRefLvalue = false; iterateAndNextNull(nodep->rhsp()); iterateAndNextNull(nodep->thsp()); - } - m_setRefLvalue = last_setRefLvalue; + } + m_setRefLvalue = last_setRefLvalue; } virtual void visit(AstNodeFTask* nodep) { - m_ftaskp = nodep; + m_ftaskp = nodep; iterateChildren(nodep); - m_ftaskp = NULL; + m_ftaskp = NULL; } virtual void visit(AstNodeFTaskRef* nodep) { - AstNode* pinp = nodep->pinsp(); - AstNodeFTask* taskp = nodep->taskp(); - // We'll deal with mismatching pins later - if (!taskp) return; - for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp=stmtp->nextp()) { + AstNode* pinp = nodep->pinsp(); + AstNodeFTask* taskp = nodep->taskp(); + // We'll deal with mismatching pins later + if (!taskp) return; + for (AstNode* stmtp = taskp->stmtsp(); stmtp && pinp; stmtp=stmtp->nextp()) { if (const AstVar* portp = VN_CAST(stmtp, Var)) { if (portp->isIO()) { if (portp->isWritable()) { @@ -255,23 +255,23 @@ private: } else { iterate(pinp); } - // Advance pin - pinp = pinp->nextp(); - } - } - } + // Advance pin + pinp = pinp->nextp(); + } + } + } } virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS LinkLValueVisitor(AstNode* nodep, bool start) { - m_setRefLvalue = start; - m_ftaskp = NULL; + m_setRefLvalue = start; + m_ftaskp = NULL; iterate(nodep); } virtual ~LinkLValueVisitor() {} diff --git a/src/V3LinkLValue.h b/src/V3LinkLValue.h index b7cf2a8ee..7ac891415 100644 --- a/src/V3LinkLValue.h +++ b/src/V3LinkLValue.h @@ -35,4 +35,4 @@ public: static void linkLValueSet(AstNode* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index a77af7eea..12ec62e8d 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -18,9 +18,9 @@ // //************************************************************************* // LINKTOP TRANSFORMATIONS: -// Utility functions -// Sort cells by depth -// Create new MODULE TOP with connections to below signals +// Utility functions +// Sort cells by depth +// Create new MODULE TOP with connections to below signals //************************************************************************* #include "config_build.h" @@ -40,7 +40,7 @@ struct CmpLevel { inline bool operator() (const AstNodeModule* lhsp, const AstNodeModule* rhsp) const { - return lhsp->level() < rhsp->level(); + return lhsp->level() < rhsp->level(); } }; @@ -55,28 +55,30 @@ void V3LinkLevel::modSortByLevel() { ModVec vec; AstNodeModule* topp = NULL; - for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { - if (nodep->level()<=2) { - if (topp) { - nodep->v3warn(E_MULTITOP, "Unsupported: Multiple top level modules: " - <prettyName()<<" and "<prettyName()); - nodep->v3warn(E_MULTITOP, "Fix, or use --top-module option to select which you want."); - } - topp = nodep; - } - vec.push_back(nodep); + for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); + nodep; nodep=VN_CAST(nodep->nextp(), NodeModule)) { + if (nodep->level()<=2) { + if (topp) { + nodep->v3warn(E_MULTITOP, "Unsupported: Multiple top level modules: " + <prettyName()<<" and "<prettyName()); + nodep->v3warn(E_MULTITOP, "Fix, or use --top-module option to select which you want."); + } + topp = nodep; + } + vec.push_back(nodep); } - stable_sort(vec.begin(), vec.end(), CmpLevel()); // Sort the vector + stable_sort(vec.begin(), vec.end(), CmpLevel()); // Sort the vector UINFO(9,"modSortByLevel() sorted\n"); // Comment required for gcc4.6.3 / bug666 for (ModVec::iterator it = vec.begin(); it != vec.end(); ++it) { - AstNodeModule* nodep = *it; - nodep->clearIter(); // Because we didn't iterate to find the node pointers, may have a stale m_iterp() needing cleanup - nodep->unlinkFrBack(); + AstNodeModule* nodep = *it; + nodep->clearIter(); // Because we didn't iterate to find the node + // pointers, may have a stale m_iterp() needing cleanup + nodep->unlinkFrBack(); } if (v3Global.rootp()->modulesp()) v3Global.rootp()->v3fatalSrc("Unlink didn't work"); for (ModVec::iterator it = vec.begin(); it != vec.end(); ++it) { - AstNodeModule* nodep = *it; - v3Global.rootp()->addModulep(nodep); + AstNodeModule* nodep = *it; + v3Global.rootp()->addModulep(nodep); } UINFO(9,"modSortByLevel() done\n"); // Comment required for gcc4.6.3 / bug666 V3Global::dumpCheckGlobalTree("cells", false, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); @@ -113,20 +115,21 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { // Add instance AstCell* cellp = new AstCell(newmodp->fileline(), - ((v3Global.opt.l2Name()!="") ? v3Global.opt.l2Name() : oldmodp->name()), - oldmodp->name(), - NULL, NULL, NULL); + (!v3Global.opt.l2Name().empty() + ? v3Global.opt.l2Name() : oldmodp->name()), + oldmodp->name(), + NULL, NULL, NULL); cellp->modp(oldmodp); newmodp->addStmtp(cellp); // Add pins for (AstNode* subnodep=oldmodp->stmtsp(); subnodep; subnodep = subnodep->nextp()) { - if (AstVar* oldvarp=VN_CAST(subnodep, Var)) { - UINFO(8,"VARWRAP "<isIO()) { - AstVar* varp = oldvarp->cloneTree(false); - newmodp->addStmtp(varp); - varp->sigPublic(true); // User needs to be able to get to it... + if (AstVar* oldvarp = VN_CAST(subnodep, Var)) { + UINFO(8,"VARWRAP "<isIO()) { + AstVar* varp = oldvarp->cloneTree(false); + newmodp->addStmtp(varp); + varp->sigPublic(true); // User needs to be able to get to it... if (oldvarp->isIO()) { oldvarp->primaryIO(false); varp->primaryIO(true); @@ -135,36 +138,36 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { varp->v3error("Unsupported: ref/const ref as primary input/output: " <prettyName()); } - if (varp->isIO() && v3Global.opt.systemC()) { - varp->sc(true); - // User can see trace one level down from the wrapper - // Avoids packing & unpacking SC signals a second time - varp->trace(false); - } + if (varp->isIO() && v3Global.opt.systemC()) { + varp->sc(true); + // User can see trace one level down from the wrapper + // Avoids packing & unpacking SC signals a second time + varp->trace(false); + } - AstPin* pinp = new AstPin(oldvarp->fileline(),0,oldvarp->name(), + AstPin* pinp = new AstPin(oldvarp->fileline(), 0, oldvarp->name(), new AstVarRef(varp->fileline(), varp, oldvarp->isWritable())); // Skip length and width comp; we know it's a direct assignment - pinp->modVarp(oldvarp); - cellp->addPinsp(pinp); - } - } + pinp->modVarp(oldvarp); + cellp->addPinsp(pinp); + } + } } // Instantiate all packages under the top wrapper // This way all later SCOPE based optimizations can ignore packages for (AstNodeModule* modp = rootp->modulesp(); modp; modp=VN_CAST(modp->nextp(), NodeModule)) { if (VN_IS(modp, Package) - && modp != oldmodp) { // Don't duplicate if didn't find a top module - AstCell* cellp = new AstCell(modp->fileline(), - // Could add __03a__03a="::" to prevent conflict - // with module names/"v" - modp->name(), - modp->name(), - NULL, NULL, NULL); - cellp->modp(modp); - newmodp->addStmtp(cellp); - } + && modp != oldmodp) { // Don't duplicate if didn't find a top module + AstCell* cellp = new AstCell(modp->fileline(), + // Could add __03a__03a="::" to prevent conflict + // with module names/"v" + modp->name(), + modp->name(), + NULL, NULL, NULL); + cellp->modp(modp); + newmodp->addStmtp(cellp); + } } } diff --git a/src/V3LinkLevel.h b/src/V3LinkLevel.h index 1ac4ce4a8..c85a8c5af 100644 --- a/src/V3LinkLevel.h +++ b/src/V3LinkLevel.h @@ -38,4 +38,4 @@ public: static void wrapTop(AstNetlist* rootp); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 217cde670..53eecbd63 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -18,8 +18,8 @@ // //************************************************************************* // LinkParse TRANSFORMATIONS: -// Top-down traversal -// Move some attributes around +// Top-down traversal +// Move some attributes around //************************************************************************* #include "config_build.h" @@ -42,43 +42,43 @@ class LinkParseVisitor : public AstNVisitor { private: // NODE STATE // Cleared on netlist - // AstNode::user1() -> bool. True if processed - // AstNode::user2() -> bool. True if fileline recomputed - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + // AstNode::user1() -> bool. True if processed + // AstNode::user2() -> bool. True if fileline recomputed + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // TYPES typedef std::map,AstTypedef*> ImplTypedefMap; typedef std::set FileLineSet; // STATE - AstVar* m_varp; // Variable we're under - ImplTypedefMap m_implTypedef; // Created typedefs for each - FileLineSet m_filelines; // Filelines that have been seen - bool m_inAlways; // Inside an always - bool m_inGenerate; // Inside a generate - bool m_needStart; // Need start marker on lower AstParse - AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module - AstNodeModule* m_modp; // Current module - AstNodeFTask* m_ftaskp; // Current task - AstNodeDType* m_dtypep; // Current data type + AstVar* m_varp; // Variable we're under + ImplTypedefMap m_implTypedef; // Created typedefs for each + FileLineSet m_filelines; // Filelines that have been seen + bool m_inAlways; // Inside an always + bool m_inGenerate; // Inside a generate + bool m_needStart; // Need start marker on lower AstParse + AstNodeModule* m_valueModp; // If set, move AstVar->valuep() initial values to this module + AstNodeModule* m_modp; // Current module + AstNodeFTask* m_ftaskp; // Current task + AstNodeDType* m_dtypep; // Current data type // METHODS VL_DEBUG_FUNC; // Declare debug() void cleanFileline(AstNode* nodep) { - if (!nodep->user2SetOnce()) { // Process once - // We make all filelines unique per AstNode. This allows us to - // later turn off messages on a fileline when an issue is found - // so that messages on replicated blocks occur only once, - // without suppressing other token's messages as a side effect. - // We could have verilog.l create a new one on every token, - // but that's a lot more structures than only doing AST nodes. - if (m_filelines.find(nodep->fileline()) != m_filelines.end()) { - nodep->fileline(new FileLine(nodep->fileline())); - } - m_filelines.insert(nodep->fileline()); - } + if (!nodep->user2SetOnce()) { // Process once + // We make all filelines unique per AstNode. This allows us to + // later turn off messages on a fileline when an issue is found + // so that messages on replicated blocks occur only once, + // without suppressing other token's messages as a side effect. + // We could have verilog.l create a new one on every token, + // but that's a lot more structures than only doing AST nodes. + if (m_filelines.find(nodep->fileline()) != m_filelines.end()) { + nodep->fileline(new FileLine(nodep->fileline())); + } + m_filelines.insert(nodep->fileline()); + } } string nameFromTypedef(AstNode* nodep) { @@ -106,22 +106,22 @@ private: // VISITs virtual void visit(AstNodeFTask* nodep) { - if (!nodep->user1SetOnce()) { // Process only once. - cleanFileline(nodep); - m_ftaskp = nodep; + if (!nodep->user1SetOnce()) { // Process only once. + cleanFileline(nodep); + m_ftaskp = nodep; iterateChildren(nodep); - m_ftaskp = NULL; - } + m_ftaskp = NULL; + } } virtual void visit(AstNodeFTaskRef* nodep) { - if (!nodep->user1SetOnce()) { // Process only once. - cleanFileline(nodep); - UINFO(5," "<user1SetOnce()) { // Process only once. + cleanFileline(nodep); + UINFO(5," "<name() == "") { nodep->name(nameFromTypedef(nodep)); // Might still remain "" - } + } visitIterateNodeDType(nodep); } virtual void visit(AstEnumItem* nodep) { - // Expand ranges - cleanFileline(nodep); + // Expand ranges + cleanFileline(nodep); iterateChildren(nodep); - if (nodep->rangep()) { + if (nodep->rangep()) { if (!VN_IS(nodep->rangep()->msbp(), Const) - || !VN_IS(nodep->rangep()->lsbp(), Const)) nodep->v3error("Enum ranges must be integral, per spec"); - int msb = nodep->rangep()->msbConst(); - int lsb = nodep->rangep()->lsbConst(); - int increment = (msb > lsb) ? -1 : 1; - int offset_from_init = 0; - AstNode* addp = NULL; - for (int i=msb; i!=(lsb+increment); i+=increment, offset_from_init++) { - string name = nodep->name() + cvtToStr(i); - AstNode* valuep = NULL; - if (nodep->valuep()) valuep = new AstAdd(nodep->fileline(), nodep->valuep()->cloneTree(true), - new AstConst(nodep->fileline(), AstConst::Unsized32(), offset_from_init)); - AstNode* newp = new AstEnumItem(nodep->fileline(), name, NULL, valuep); - if (addp) addp = addp->addNextNull(newp); - else addp = newp; - } - nodep->replaceWith(addp); - nodep->deleteTree(); - } + || !VN_IS(nodep->rangep()->lsbp(), Const)) { + nodep->v3error("Enum ranges must be integral, per spec"); + } + int msb = nodep->rangep()->msbConst(); + int lsb = nodep->rangep()->lsbConst(); + int increment = (msb > lsb) ? -1 : 1; + int offset_from_init = 0; + AstNode* addp = NULL; + for (int i=msb; i!=(lsb+increment); i+=increment, offset_from_init++) { + string name = nodep->name() + cvtToStr(i); + AstNode* valuep = NULL; + if (nodep->valuep()) valuep = new AstAdd + (nodep->fileline(), + nodep->valuep()->cloneTree(true), + new AstConst(nodep->fileline(), + AstConst::Unsized32(), + offset_from_init)); + AstNode* newp = new AstEnumItem(nodep->fileline(), name, NULL, valuep); + if (addp) addp = addp->addNextNull(newp); + else addp = newp; + } + nodep->replaceWith(addp); + nodep->deleteTree(); + } } virtual void visit(AstVar* nodep) { - cleanFileline(nodep); + cleanFileline(nodep); if (VN_IS(nodep->subDTypep(), ParseTypeDType)) { - // It's a parameter type. Use a different node type for this. + // It's a parameter type. Use a different node type for this. AstNodeDType* dtypep = VN_CAST(nodep->valuep(), NodeDType); - if (!dtypep) { - nodep->v3error("Parameter type's initial value isn't a type: "<prettyName()); - nodep->unlinkFrBack(); - } else { - dtypep->unlinkFrBack(); - AstNode* newp = new AstParamTypeDType(nodep->fileline(), nodep->varType(), nodep->name(), - VFlagChildDType(), dtypep); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - } - return; - } + if (!dtypep) { + nodep->v3error("Parameter type's initial value isn't a type: " + <prettyName()); + nodep->unlinkFrBack(); + } else { + dtypep->unlinkFrBack(); + AstNode* newp = new AstParamTypeDType(nodep->fileline(), + nodep->varType(), nodep->name(), + VFlagChildDType(), dtypep); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + } + return; + } - // We used modTrace before leveling, and we may now - // want to turn it off now that we know the levelizations - if (v3Global.opt.traceDepth() - && m_modp - && (m_modp->level()-1) > v3Global.opt.traceDepth()) { - m_modp->modTrace(false); - nodep->trace(false); - } - m_varp = nodep; + // We used modTrace before leveling, and we may now + // want to turn it off now that we know the levelizations + if (v3Global.opt.traceDepth() + && m_modp + && (m_modp->level()-1) > v3Global.opt.traceDepth()) { + m_modp->modTrace(false); + nodep->trace(false); + } + m_varp = nodep; iterateChildren(nodep); - m_varp = NULL; - // temporaries under an always aren't expected to be blocking - if (m_inAlways) nodep->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); - if (nodep->valuep()) { - // A variable with an = value can be three things: - FileLine* fl = nodep->valuep()->fileline(); - // 1. Parameters and function inputs: It's a default to use if not overridden + m_varp = NULL; + // temporaries under an always aren't expected to be blocking + if (m_inAlways) nodep->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); + if (nodep->valuep()) { + // A variable with an = value can be three things: + FileLine* fl = nodep->valuep()->fileline(); + // 1. Parameters and function inputs: It's a default to use if not overridden if (nodep->isParam() || (m_ftaskp && nodep->isNonOutput())) { } else if (!m_ftaskp && nodep->isNonOutput()) { - nodep->v3error("Unsupported: Default value on module input: "<prettyName()); - nodep->valuep()->unlinkFrBack()->deleteTree(); + nodep->v3error("Unsupported: Default value on module input: "<prettyName()); + nodep->valuep()->unlinkFrBack()->deleteTree(); } // 2. Under modules, it's an initial value to be loaded at time 0 via an AstInitial else if (m_valueModp) { // Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it - FileLine* newfl = new FileLine (fl); + FileLine* newfl = new FileLine(fl); newfl->warnOff(V3ErrorCode::PROCASSWIRE, true); nodep->addNextHere (new AstInitial @@ -217,164 +225,167 @@ private: nodep->addNextHere (new AstAssign(fl, new AstVarRef(fl, nodep->name(), true), nodep->valuep()->unlinkFrBack())); - } - } - if (nodep->isIfaceRef() && !nodep->isIfaceParent()) { - // Only AstIfaceRefDType's at this point correspond to ports; - // haven't made additional ones for interconnect yet, so assert is simple - // What breaks later is we don't have a Scope/Cell representing the interface to attach to - if (m_modp->level()<=2) nodep->v3error("Unsupported: Interfaced port on top level module"); - } + } + } + if (nodep->isIfaceRef() && !nodep->isIfaceParent()) { + // Only AstIfaceRefDType's at this point correspond to ports; + // haven't made additional ones for interconnect yet, so assert is simple + // What breaks later is we don't have a Scope/Cell representing + // the interface to attach to + if (m_modp->level()<=2) nodep->v3error("Unsupported: Interfaced port on top level module"); + } } virtual void visit(AstAttrOf* nodep) { - cleanFileline(nodep); + cleanFileline(nodep); iterateChildren(nodep); - if (nodep->attrType() == AstAttrType::DT_PUBLIC) { + if (nodep->attrType() == AstAttrType::DT_PUBLIC) { AstTypedef* typep = VN_CAST(nodep->backp(), Typedef); - if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef"); - typep->attrPublic(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_CLOCK) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->attrScClocked(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_CLOCK_ENABLE) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->attrClockEn(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigUserRWPublic(true); m_varp->sigModPublic(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigUserRWPublic(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigUserRdPublic(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigUserRWPublic(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->attrIsolateAssign(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_SFORMAT) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->attrSFormat(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_SC_BV) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->attrScBv(true); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_CLOCKER) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->attrClocker(AstVarAttrClocker::CLOCKER_YES); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } - else if (nodep->attrType() == AstAttrType::VAR_NO_CLOCKER) { - if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->attrClocker(AstVarAttrClocker::CLOCKER_NO); - nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); - } + if (!typep) nodep->v3fatalSrc("Attribute not attached to typedef"); + typep->attrPublic(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_CLOCK) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrScClocked(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_CLOCK_ENABLE) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrClockEn(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->sigUserRWPublic(true); m_varp->sigModPublic(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->sigUserRWPublic(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->sigUserRdPublic(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->sigUserRWPublic(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrIsolateAssign(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_SFORMAT) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrSFormat(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_SC_BV) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrScBv(true); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_CLOCKER) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrClocker(AstVarAttrClocker::CLOCKER_YES); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } + else if (nodep->attrType() == AstAttrType::VAR_NO_CLOCKER) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrClocker(AstVarAttrClocker::CLOCKER_NO); + nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + } } virtual void visit(AstAlwaysPublic* nodep) { - // AlwaysPublic was attached under a var, but it's a statement that should be - // at the same level as the var - cleanFileline(nodep); + // AlwaysPublic was attached under a var, but it's a statement that should be + // at the same level as the var + cleanFileline(nodep); iterateChildren(nodep); - if (m_varp) { - nodep->unlinkFrBack(); - m_varp->addNext(nodep); - // lvalue is true, because we know we have a verilator public_flat_rw - // but someday we may be more general - bool lvalue = m_varp->isSigUserRWPublic(); - nodep->addStmtp(new AstVarRef(nodep->fileline(), m_varp, lvalue)); - } + if (m_varp) { + nodep->unlinkFrBack(); + m_varp->addNext(nodep); + // lvalue is true, because we know we have a verilator public_flat_rw + // but someday we may be more general + bool lvalue = m_varp->isSigUserRWPublic(); + nodep->addStmtp(new AstVarRef(nodep->fileline(), m_varp, lvalue)); + } } virtual void visit(AstDefImplicitDType* nodep) { - cleanFileline(nodep); - UINFO(8," DEFIMPLICIT "<containerp(), nodep->name())); - if (it != m_implTypedef.end()) { - defp = it->second; - } else { - // Definition must be inserted right after the variable (etc) that needed it - // AstVar, AstTypedef, AstNodeFTask are common containers - AstNode* backp = nodep->backp(); - for (; backp; backp=backp->backp()) { + cleanFileline(nodep); + UINFO(8," DEFIMPLICIT "<containerp(), nodep->name())); + if (it != m_implTypedef.end()) { + defp = it->second; + } else { + // Definition must be inserted right after the variable (etc) that needed it + // AstVar, AstTypedef, AstNodeFTask are common containers + AstNode* backp = nodep->backp(); + for (; backp; backp=backp->backp()) { if (VN_IS(backp, Var)) break; else if (VN_IS(backp, Typedef)) break; else if (VN_IS(backp, NodeFTask)) break; - } - if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type"); - AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack(); + } + if (!backp) nodep->v3fatalSrc("Implicit enum/struct type created under unexpected node type"); + AstNodeDType* dtypep = nodep->childDTypep(); dtypep->unlinkFrBack(); if (VN_IS(backp, Typedef)) { // A typedef doesn't need us to make yet another level of typedefing - // For typedefs just remove the AstRefDType level of abstraction - nodep->replaceWith(dtypep); - nodep->deleteTree(); VL_DANGLING(nodep); - return; - } else { - defp = new AstTypedef(nodep->fileline(), nodep->name(), NULL, VFlagChildDType(), dtypep); - m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp)); - backp->addNextHere(defp); - } - } - nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name())); - nodep->deleteTree(); VL_DANGLING(nodep); + // For typedefs just remove the AstRefDType level of abstraction + nodep->replaceWith(dtypep); + nodep->deleteTree(); VL_DANGLING(nodep); + return; + } else { + defp = new AstTypedef(nodep->fileline(), nodep->name(), NULL, + VFlagChildDType(), dtypep); + m_implTypedef.insert(make_pair(make_pair(nodep->containerp(), defp->name()), defp)); + backp->addNextHere(defp); + } + } + nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name())); + nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstTypedefFwd* nodep) { - // We only needed the forward declaration in order to parse correctly. - // We won't even check it was ever really defined, as it might have been in a header - // file referring to a module we never needed - nodep->unlinkFrBack()->deleteTree(); + // We only needed the forward declaration in order to parse correctly. + // We won't even check it was ever really defined, as it might have been in a header + // file referring to a module we never needed + nodep->unlinkFrBack()->deleteTree(); } virtual void visit(AstForeach* nodep) { - // FOREACH(array,loopvars,body) - // -> BEGIN(declare vars, loopa=lowest; WHILE(loopa<=highest, ... body)) - //nodep->dumpTree(cout, "-foreach-old:"); - AstNode* newp = nodep->bodysp()->unlinkFrBackWithNext(); - AstNode* arrayp = nodep->arrayp(); - int dimension = 1; - // Must do innermost (last) variable first - AstNode* firstVarsp = nodep->varsp()->unlinkFrBackWithNext(); - AstNode* lastVarsp = firstVarsp; - while (lastVarsp->nextp()) { lastVarsp = lastVarsp->nextp(); dimension++; } - for (AstNode* varsp = lastVarsp; varsp; varsp=varsp->backp()) { - UINFO(9,"foreachVar "<fileline(); - AstNode* varp = new AstVar(fl, AstVarType::BLOCKTEMP, - varsp->name(), nodep->findSigned32DType()); + // FOREACH(array,loopvars,body) + // -> BEGIN(declare vars, loopa=lowest; WHILE(loopa<=highest, ... body)) + //nodep->dumpTree(cout, "-foreach-old:"); + AstNode* newp = nodep->bodysp()->unlinkFrBackWithNext(); + AstNode* arrayp = nodep->arrayp(); + int dimension = 1; + // Must do innermost (last) variable first + AstNode* firstVarsp = nodep->varsp()->unlinkFrBackWithNext(); + AstNode* lastVarsp = firstVarsp; + while (lastVarsp->nextp()) { lastVarsp = lastVarsp->nextp(); dimension++; } + for (AstNode* varsp = lastVarsp; varsp; varsp=varsp->backp()) { + UINFO(9,"foreachVar "<fileline(); + AstNode* varp = new AstVar(fl, AstVarType::BLOCKTEMP, + varsp->name(), nodep->findSigned32DType()); // These will be the left and right dimensions of the array: - AstNode* leftp = new AstAttrOf(fl, AstAttrType::DIM_LEFT, - new AstVarRef(fl, arrayp->name(), false), - new AstConst(fl, dimension)); - AstNode* rightp = new AstAttrOf(fl, AstAttrType::DIM_RIGHT, - new AstVarRef(fl, arrayp->name(), false), - new AstConst(fl, dimension)); - AstNode* stmtsp = varp; + AstNode* leftp = new AstAttrOf(fl, AstAttrType::DIM_LEFT, + new AstVarRef(fl, arrayp->name(), false), + new AstConst(fl, dimension)); + AstNode* rightp = new AstAttrOf(fl, AstAttrType::DIM_RIGHT, + new AstVarRef(fl, arrayp->name(), false), + new AstConst(fl, dimension)); + AstNode* stmtsp = varp; // Assign left-dimension into the loop var: stmtsp->addNext(new AstAssign (fl, new AstVarRef(fl, varp->name(), true), leftp)); @@ -395,69 +406,69 @@ private: new AstCond(fl, countupp, new AstConst(fl, 1), new AstConst(fl, -1)))); - stmtsp->addNext(new AstWhile(fl, comparep, newp, incp)); - newp = new AstBegin(nodep->fileline(),"",stmtsp); - dimension--; - } - //newp->dumpTree(cout, "-foreach-new:"); - firstVarsp->deleteTree(); VL_DANGLING(firstVarsp); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + stmtsp->addNext(new AstWhile(fl, comparep, newp, incp)); + newp = new AstBegin(nodep->fileline(), "", stmtsp); + dimension--; + } + //newp->dumpTree(cout, "-foreach-new:"); + firstVarsp->deleteTree(); VL_DANGLING(firstVarsp); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstNodeModule* nodep) { - // Module: Create sim table for entire module and iterate - cleanFileline(nodep); - // - m_modp = nodep; - m_valueModp = nodep; + // Module: Create sim table for entire module and iterate + cleanFileline(nodep); + // + m_modp = nodep; + m_valueModp = nodep; iterateChildren(nodep); - m_modp = NULL; - m_valueModp = NULL; + m_modp = NULL; + m_valueModp = NULL; } void visitIterateNoValueMod(AstNode* nodep) { - // Iterate a node which shouldn't have any local variables moved to an Initial - cleanFileline(nodep); - // - AstNodeModule* upperValueModp = m_valueModp; - m_valueModp = NULL; + // Iterate a node which shouldn't have any local variables moved to an Initial + cleanFileline(nodep); + // + AstNodeModule* upperValueModp = m_valueModp; + m_valueModp = NULL; iterateChildren(nodep); - m_valueModp = upperValueModp; + m_valueModp = upperValueModp; } virtual void visit(AstInitial* nodep) { - visitIterateNoValueMod(nodep); + visitIterateNoValueMod(nodep); } virtual void visit(AstFinal* nodep) { - visitIterateNoValueMod(nodep); + visitIterateNoValueMod(nodep); } virtual void visit(AstAlways* nodep) { - m_inAlways = true; - visitIterateNoValueMod(nodep); - m_inAlways = false; + m_inAlways = true; + visitIterateNoValueMod(nodep); + m_inAlways = false; } virtual void visit(AstPslCover* nodep) { - visitIterateNoValueMod(nodep); + visitIterateNoValueMod(nodep); } virtual void visit(AstPslRestrict* nodep) { visitIterateNoValueMod(nodep); } virtual void visit(AstNode* nodep) { - // Default: Just iterate - cleanFileline(nodep); + // Default: Just iterate + cleanFileline(nodep); iterateChildren(nodep); } public: // CONSTUCTORS explicit LinkParseVisitor(AstNetlist* rootp) { - m_varp = NULL; - m_modp = NULL; - m_ftaskp = NULL; - m_dtypep = NULL; - m_inAlways = false; - m_inGenerate = false; - m_needStart = false; - m_valueModp = NULL; + m_varp = NULL; + m_modp = NULL; + m_ftaskp = NULL; + m_dtypep = NULL; + m_inAlways = false; + m_inGenerate = false; + m_needStart = false; + m_valueModp = NULL; iterate(rootp); } virtual ~LinkParseVisitor() {} diff --git a/src/V3LinkParse.h b/src/V3LinkParse.h index 349aca899..6aa0f4d92 100644 --- a/src/V3LinkParse.h +++ b/src/V3LinkParse.h @@ -34,4 +34,4 @@ public: static void linkParse(AstNetlist* rootp); }; -#endif // Guard +#endif // Guard diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 623ac89d4..a075f0455 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -18,14 +18,14 @@ // //************************************************************************* // LinkResolve TRANSFORMATIONS: -// Top-down traversal -// Extracts: -// Add SUB so that we subtract off the "base 0-start" of the array -// SelBit: Convert to ArraySel -// Add SUB so that we subtract off the "base 0-start" of the array -// File operations -// Convert normal var to FILE* type -// SenItems: Convert pos/negedge of non-simple signals to temporaries +// Top-down traversal +// Extracts: +// Add SUB so that we subtract off the "base 0-start" of the array +// SelBit: Convert to ArraySel +// Add SUB so that we subtract off the "base 0-start" of the array +// File operations +// Convert normal var to FILE* type +// SenItems: Convert pos/negedge of non-simple signals to temporaries //************************************************************************* #include "config_build.h" @@ -48,15 +48,15 @@ class LinkResolveVisitor : public AstNVisitor { private: // NODE STATE // Entire netlist: - // AstCaseItem::user2() // bool Moved default caseitems - AstUser2InUse m_inuser2; + // AstCaseItem::user2() // bool Moved default caseitems + AstUser2InUse m_inuser2; // STATE // Below state needs to be preserved between each module call. - AstNodeModule* m_modp; // Current module - AstNodeFTask* m_ftaskp; // Function or task we're inside - AstVAssert* m_assertp; // Current assertion - int m_senitemCvtNum; // Temporary signal counter + AstNodeModule* m_modp; // Current module + AstNodeFTask* m_ftaskp; // Function or task we're inside + AstVAssert* m_assertp; // Current assertion + int m_senitemCvtNum; // Temporary signal counter // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -66,281 +66,285 @@ private: // TODO: ExpectDecriptor can move to data type resolution, and the rest // TODO: could move to V3LinkParse to get them out of the way of elaboration virtual void visit(AstNodeModule* nodep) { - // Module: Create sim table for entire module and iterate - UINFO(8,"MODULE "<dead()) return; - m_modp = nodep; - m_senitemCvtNum = 0; + // Module: Create sim table for entire module and iterate + UINFO(8,"MODULE "<dead()) return; + m_modp = nodep; + m_senitemCvtNum = 0; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstInitial* nodep) { iterateChildren(nodep); - // Initial assignments under function/tasks can just be simple assignments without the initial - if (m_ftaskp) { - nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); VL_DANGLING(nodep); - } + // Initial assignments under function/tasks can just be simple + // assignments without the initial + if (m_ftaskp) { + nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); VL_DANGLING(nodep); + } } virtual void visit(AstVAssert* nodep) { - if (m_assertp) nodep->v3error("Assert not allowed under another assert"); - m_assertp = nodep; + if (m_assertp) nodep->v3error("Assert not allowed under another assert"); + m_assertp = nodep; iterateChildren(nodep); - m_assertp = NULL; + m_assertp = NULL; } virtual void visit(AstVar* nodep) { iterateChildren(nodep); - if (m_ftaskp) nodep->funcLocal(true); - if (nodep->isSigModPublic()) { - nodep->sigModPublic(false); // We're done with this attribute - m_modp->modPublic(true); // Avoid flattening if signals are exposed - } + if (m_ftaskp) nodep->funcLocal(true); + if (nodep->isSigModPublic()) { + nodep->sigModPublic(false); // We're done with this attribute + m_modp->modPublic(true); // Avoid flattening if signals are exposed + } } virtual void visit(AstNodeVarRef* nodep) { - // VarRef: Resolve its reference - if (nodep->varp()) { - nodep->varp()->usedParam(true); - } + // VarRef: Resolve its reference + if (nodep->varp()) { + nodep->varp()->usedParam(true); + } iterateChildren(nodep); } virtual void visit(AstNodeFTask* nodep) { - // NodeTask: Remember its name for later resolution - // Remember the existing symbol table scope - m_ftaskp = nodep; + // NodeTask: Remember its name for later resolution + // Remember the existing symbol table scope + m_ftaskp = nodep; iterateChildren(nodep); - m_ftaskp = NULL; - if (nodep->dpiExport()) { - nodep->scopeNamep(new AstScopeName(nodep->fileline())); - } + m_ftaskp = NULL; + if (nodep->dpiExport()) { + nodep->scopeNamep(new AstScopeName(nodep->fileline())); + } } virtual void visit(AstNodeFTaskRef* nodep) { iterateChildren(nodep); - if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) { - nodep->scopeNamep(new AstScopeName(nodep->fileline())); - } + if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) { + nodep->scopeNamep(new AstScopeName(nodep->fileline())); + } } virtual void visit(AstSenItem* nodep) { - // Remove bit selects, and bark if it's not a simple variable + // Remove bit selects, and bark if it's not a simple variable iterateChildren(nodep); - if (nodep->isClocked()) { - // If it's not a simple variable wrap in a temporary - // This is a bit unfortunate as we haven't done width resolution - // and any width errors will look a bit odd, but it works. - AstNode* sensp = nodep->sensp(); - if (sensp + if (nodep->isClocked()) { + // If it's not a simple variable wrap in a temporary + // This is a bit unfortunate as we haven't done width resolution + // and any width errors will look a bit odd, but it works. + AstNode* sensp = nodep->sensp(); + if (sensp && !VN_IS(sensp, NodeVarRef) && !VN_IS(sensp, Const)) { - // Make a new temp wire - string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum); - AstVar* newvarp = new AstVar(sensp->fileline(), AstVarType::MODULETEMP, newvarname, + // Make a new temp wire + string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum); + AstVar* newvarp = new AstVar(sensp->fileline(), + AstVarType::MODULETEMP, newvarname, VFlagLogicPacked(), 1); - // We can't just add under the module, because we may be inside a generate, begin, etc. - // We know a SenItem should be under a SenTree/Always etc, we we'll just hunt upwards - AstNode* addwherep = nodep; // Add to this element's next + // We can't just add under the module, because we may be + // inside a generate, begin, etc. + // We know a SenItem should be under a SenTree/Always etc, + // we we'll just hunt upwards + AstNode* addwherep = nodep; // Add to this element's next while (VN_IS(addwherep, SenItem) || VN_IS(addwherep, SenTree)) { - addwherep = addwherep->backp(); - } + addwherep = addwherep->backp(); + } if (!VN_IS(addwherep, Always)) { // Assertion perhaps? - sensp->v3error("Unsupported: Non-single-bit pos/negedge clock statement under some complicated block"); - addwherep = m_modp; - } - addwherep->addNext(newvarp); + sensp->v3error("Unsupported: Non-single-bit pos/negedge clock statement under some complicated block"); + addwherep = m_modp; + } + addwherep->addNext(newvarp); sensp->replaceWith(new AstVarRef(sensp->fileline(), newvarp, false)); - AstAssignW* assignp = new AstAssignW - (sensp->fileline(), - new AstVarRef(sensp->fileline(), newvarp, true), - sensp); - addwherep->addNext(assignp); - } - } else { // Old V1995 sensitivity list; we'll probably mostly ignore - bool did=1; - while (did) { - did=0; + AstAssignW* assignp = new AstAssignW + (sensp->fileline(), + new AstVarRef(sensp->fileline(), newvarp, true), + sensp); + addwherep->addNext(assignp); + } + } else { // Old V1995 sensitivity list; we'll probably mostly ignore + bool did = 1; + while (did) { + did = 0; if (AstNodeSel* selp = VN_CAST(nodep->sensp(), NodeSel)) { - AstNode* fromp = selp->fromp()->unlinkFrBack(); - selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); - did=1; - } - // NodeSel doesn't include AstSel.... + AstNode* fromp = selp->fromp()->unlinkFrBack(); + selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); + did = 1; + } + // NodeSel doesn't include AstSel.... if (AstSel* selp = VN_CAST(nodep->sensp(), Sel)) { - AstNode* fromp = selp->fromp()->unlinkFrBack(); - selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); - did=1; - } + AstNode* fromp = selp->fromp()->unlinkFrBack(); + selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); + did = 1; + } if (AstNodePreSel* selp = VN_CAST(nodep->sensp(), NodePreSel)) { - AstNode* fromp = selp->lhsp()->unlinkFrBack(); - selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); - did=1; - } - } - } + AstNode* fromp = selp->lhsp()->unlinkFrBack(); + selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp); + did = 1; + } + } + } if (!VN_IS(nodep->sensp(), NodeVarRef) && !VN_IS(nodep->sensp(), EnumItemRef) // V3Const will cleanup - && !nodep->isIllegal()) { - if (debug()) nodep->dumpTree(cout,"-tree: "); - nodep->v3error("Unsupported: Complex statement in sensitivity list"); - } + && !nodep->isIllegal()) { + if (debug()) nodep->dumpTree(cout, "-tree: "); + nodep->v3error("Unsupported: Complex statement in sensitivity list"); + } } virtual void visit(AstSenGate* nodep) { - nodep->v3fatalSrc("SenGates shouldn't be in tree yet"); + nodep->v3fatalSrc("SenGates shouldn't be in tree yet"); } virtual void visit(AstNodePreSel* nodep) { - if (!nodep->attrp()) { + if (!nodep->attrp()) { iterateChildren(nodep); - // Constification may change the fromp() to a constant, which will lose the - // variable we're extracting from (to determine MSB/LSB/endianness/etc.) - // So we replicate it in another node - // Note that V3Param knows not to replace AstVarRef's under AstAttrOf's - AstNode* basefromp = AstArraySel::baseFromp(nodep); + // Constification may change the fromp() to a constant, which will lose the + // variable we're extracting from (to determine MSB/LSB/endianness/etc.) + // So we replicate it in another node + // Note that V3Param knows not to replace AstVarRef's under AstAttrOf's + AstNode* basefromp = AstArraySel::baseFromp(nodep); if (AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) { // Maybe varxref - so need to clone - nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, - varrefp->cloneTree(false))); + nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, + varrefp->cloneTree(false))); } else if (AstUnlinkedRef* uvxrp = VN_CAST(basefromp, UnlinkedRef)) { // Maybe unlinked - so need to clone - nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, - uvxrp->cloneTree(false))); + nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE, + uvxrp->cloneTree(false))); } else if (AstMemberSel* fromp = VN_CAST(basefromp, MemberSel)) { - nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE, - fromp->cloneTree(false))); + nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE, + fromp->cloneTree(false))); } else if (AstEnumItemRef* fromp = VN_CAST(basefromp, EnumItemRef)) { - nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::ENUM_BASE, - fromp->cloneTree(false))); + nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::ENUM_BASE, + fromp->cloneTree(false))); } else if (VN_IS(basefromp, Replicate)) { // From {...}[...] syntax in IEEE 2017 if (basefromp) { UINFO(1," Related node: "<v3error("Unsupported: Select of concatenation"); - nodep = NULL; - } else { - if (basefromp) { UINFO(1," Related node: "<v3fatalSrc("Illegal bit select; no signal/member being extracted from"); - } - } + nodep = NULL; + } else { + if (basefromp) { UINFO(1," Related node: "<v3fatalSrc("Illegal bit select; no signal/member being extracted from"); + } + } } virtual void visit(AstCaseItem* nodep) { - // Move default caseItems to the bottom of the list - // That saves us from having to search each case list twice, for non-defaults and defaults + // Move default caseItems to the bottom of the list + // That saves us from having to search each case list twice, for non-defaults and defaults iterateChildren(nodep); - if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) { - nodep->user2(true); - AstNode* nextp = nodep->nextp(); - nodep->unlinkFrBack(); - nextp->addNext(nodep); - } + if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) { + nodep->user2(true); + AstNode* nextp = nodep->nextp(); + nodep->unlinkFrBack(); + nextp->addNext(nodep); + } } virtual void visit(AstPragma* nodep) { - if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) { - if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module"); - m_modp->modPublic(true); - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } - else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) { - if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task"); - m_ftaskp->taskPublic(true); - m_modp->modPublic(true); // Need to get to the task... - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } - else if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { - if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize better without - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } - } - else { + if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) { + if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module"); + m_modp->modPublic(true); + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } + else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) { + if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task"); + m_ftaskp->taskPublic(true); + m_modp->modPublic(true); // Need to get to the task... + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } + else if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) { + if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize better without + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } + } + else { iterateChildren(nodep); - } + } } string expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) { - // Check display arguments, return new format string - string newFormat; - bool inPct = false; + // Check display arguments, return new format string + string newFormat; + bool inPct = false; string fmt; - for (string::const_iterator it = format.begin(); it != format.end(); ++it) { - char ch = *it; - if (!inPct && ch=='%') { - inPct = true; - fmt = ch; - } else if (inPct && (isdigit(ch) || ch=='.')) { - fmt += ch; - } else if (inPct) { - inPct = false; - fmt += ch; - switch (tolower(ch)) { - case '%': // %% - just output a % - break; - case 'm': // %m - auto insert "name" - if (isScan) { nodep->v3error("Unsupported: %m in $fscanf"); fmt = ""; } - break; - case 'l': // %l - auto insert "library" - if (isScan) { nodep->v3error("Unsupported: %l in $fscanf"); fmt = ""; } - if (m_modp) fmt = VString::quotePercent(m_modp->prettyName()); - break; - default: // Most operators, just move to next argument - if (!V3Number::displayedFmtLegal(ch)) { - nodep->v3error("Unknown $display-like format code: %"<v3error("Missing arguments for $display-like format"); - } else { - argp = argp->nextp(); - } - } - break; - } // switch - newFormat += fmt; - } else { - newFormat += ch; - } - } + for (string::const_iterator it = format.begin(); it != format.end(); ++it) { + char ch = *it; + if (!inPct && ch=='%') { + inPct = true; + fmt = ch; + } else if (inPct && (isdigit(ch) || ch=='.')) { + fmt += ch; + } else if (inPct) { + inPct = false; + fmt += ch; + switch (tolower(ch)) { + case '%': // %% - just output a % + break; + case 'm': // %m - auto insert "name" + if (isScan) { nodep->v3error("Unsupported: %m in $fscanf"); fmt = ""; } + break; + case 'l': // %l - auto insert "library" + if (isScan) { nodep->v3error("Unsupported: %l in $fscanf"); fmt = ""; } + if (m_modp) fmt = VString::quotePercent(m_modp->prettyName()); + break; + default: // Most operators, just move to next argument + if (!V3Number::displayedFmtLegal(ch)) { + nodep->v3error("Unknown $display-like format code: %"<v3error("Missing arguments for $display-like format"); + } else { + argp = argp->nextp(); + } + } + break; + } // switch + newFormat += fmt; + } else { + newFormat += ch; + } + } - if (argp && !isScan) { - int skipCount = 0; // number of args consume by any additional format strings - while (argp) { - if (skipCount) { - argp = argp->nextp(); - skipCount--; - continue; - } + if (argp && !isScan) { + int skipCount = 0; // number of args consume by any additional format strings + while (argp) { + if (skipCount) { + argp = argp->nextp(); + skipCount--; + continue; + } AstConst *constp = VN_CAST(argp, Const); - bool isFromString = (constp) ? constp->num().isFromString() : false; - if (isFromString) { - int numchars = argp->dtypep()->width()/8; - string str(numchars, ' '); - // now scan for % operators - bool inpercent = false; - for (int i = 0; i < numchars; i++) { - int ii = numchars - i - 1; - char c = constp->num().dataByte(ii); - str[i] = c; - if (!inpercent && c == '%') { - inpercent = true; - } else if (inpercent) { - inpercent = 0; - switch (c) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '.': - inpercent = true; - break; - case '%': - break; - default: - if (V3Number::displayedFmtLegal(c)) { - skipCount++; - } - } - } - } - newFormat.append(str); - AstNode *nextp = argp->nextp(); - argp->unlinkFrBack(); pushDeletep(argp); VL_DANGLING(argp); - argp = nextp; + bool isFromString = (constp) ? constp->num().isFromString() : false; + if (isFromString) { + int numchars = argp->dtypep()->width()/8; + string str(numchars, ' '); + // now scan for % operators + bool inpercent = false; + for (int i = 0; i < numchars; i++) { + int ii = numchars - i - 1; + char c = constp->num().dataByte(ii); + str[i] = c; + if (!inpercent && c == '%') { + inpercent = true; + } else if (inpercent) { + inpercent = 0; + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '.': + inpercent = true; + break; + case '%': + break; + default: + if (V3Number::displayedFmtLegal(c)) { + skipCount++; + } + } + } + } + newFormat.append(str); + AstNode *nextp = argp->nextp(); + argp->unlinkFrBack(); pushDeletep(argp); VL_DANGLING(argp); + argp = nextp; } else { newFormat.append("%?"); // V3Width to figure it out argp = argp->nextp(); @@ -351,8 +355,8 @@ private: } void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) { - if (!filep) nodep->v3error("Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable"); - if (filep && filep->varp()) filep->varp()->attrFileDescr(true); + if (!filep) nodep->v3error("Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable"); + if (filep && filep->varp()) filep->varp()->attrFileDescr(true); } virtual void visit(AstFOpen* nodep) { @@ -373,46 +377,46 @@ private: } virtual void visit(AstFScanF* nodep) { iterateChildren(nodep); - expectFormat(nodep, nodep->text(), nodep->exprsp(), true); + expectFormat(nodep, nodep->text(), nodep->exprsp(), true); } virtual void visit(AstSScanF* nodep) { iterateChildren(nodep); - expectFormat(nodep, nodep->text(), nodep->exprsp(), true); + expectFormat(nodep, nodep->text(), nodep->exprsp(), true); } virtual void visit(AstSFormatF* nodep) { iterateChildren(nodep); - // Cleanup old-school displays without format arguments - if (!nodep->hasFormat()) { - if (nodep->text()!="") nodep->v3fatalSrc("Non-format $sformatf should have \"\" format"); + // Cleanup old-school displays without format arguments + if (!nodep->hasFormat()) { + if (nodep->text()!="") nodep->v3fatalSrc("Non-format $sformatf should have \"\" format"); if (VN_IS(nodep->exprsp(), Const) && VN_CAST(nodep->exprsp(), Const)->num().isFromString()) { AstConst* fmtp = VN_CAST(nodep->exprsp()->unlinkFrBack(), Const); - nodep->text(fmtp->num().toString()); - pushDeletep(fmtp); VL_DANGLING(fmtp); - } - nodep->hasFormat(true); - } - string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false); - nodep->text(newFormat); + nodep->text(fmtp->num().toString()); + pushDeletep(fmtp); VL_DANGLING(fmtp); + } + nodep->hasFormat(true); + } + string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false); + nodep->text(newFormat); if ((VN_IS(nodep->backp(), Display) && VN_CAST(nodep->backp(), Display)->displayType().needScopeTracking()) - || nodep->formatScopeTracking()) { - nodep->scopeNamep(new AstScopeName(nodep->fileline())); - } + || nodep->formatScopeTracking()) { + nodep->scopeNamep(new AstScopeName(nodep->fileline())); + } } virtual void visit(AstDisplay* nodep) { iterateChildren(nodep); } virtual void visit(AstUdpTable* nodep) { - UINFO(5,"UDPTABLE "<stmtsp(); stmtp; stmtp=stmtp->nextp()) { + UINFO(5,"UDPTABLE "<stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* varp = VN_CAST(stmtp, Var)) { if (varp->isReadOnly()) { } else if (varp->isWritable()) { @@ -424,45 +428,46 @@ private: m_modp->addStmtp(new AstAssignW( varp->fileline(), new AstVarRef(varp->fileline(), varp, true), - new AstConst(varp->fileline(), AstConst::LogicFalse()))); - } else { - varp->v3error("Only inputs and outputs are allowed in udp modules"); - } - } - } - nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); - } + new AstConst(varp->fileline(), + AstConst::LogicFalse()))); + } else { + varp->v3error("Only inputs and outputs are allowed in udp modules"); + } + } + } + nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); + } } virtual void visit(AstScCtor* nodep) { - // Constructor info means the module must remain public - m_modp->modPublic(true); + // Constructor info means the module must remain public + m_modp->modPublic(true); iterateChildren(nodep); } virtual void visit(AstScDtor* nodep) { - // Destructor info means the module must remain public - m_modp->modPublic(true); + // Destructor info means the module must remain public + m_modp->modPublic(true); iterateChildren(nodep); } virtual void visit(AstScInt* nodep) { - // Special class info means the module must remain public - m_modp->modPublic(true); + // Special class info means the module must remain public + m_modp->modPublic(true); iterateChildren(nodep); } virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS explicit LinkResolveVisitor(AstNetlist* rootp) { - m_ftaskp = NULL; - m_modp = NULL; - m_assertp = NULL; - m_senitemCvtNum = 0; - // + m_ftaskp = NULL; + m_modp = NULL; + m_assertp = NULL; + m_senitemCvtNum = 0; + // iterate(rootp); } virtual ~LinkResolveVisitor() {} @@ -470,44 +475,44 @@ public: //###################################################################### // LinkBotupVisitor -// Recurses cells backwards, so we can pick up those things that propagate -// from child cells up to the top module. +// Recurses cells backwards, so we can pick up those things that propagate +// from child cells up to the top module. class LinkBotupVisitor : public AstNVisitor { private: // STATE - AstNodeModule* m_modp; // Current module + AstNodeModule* m_modp; // Current module // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITs virtual void visit(AstNetlist* nodep) { - // Iterate modules backwards, in bottom-up order. + // Iterate modules backwards, in bottom-up order. iterateChildrenBackwards(nodep); } virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; + m_modp = nodep; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstCell* nodep) { - // Parent module inherits child's publicity - if (nodep->modp()->modPublic()) m_modp->modPublic(true); - //** No iteration for speed + // Parent module inherits child's publicity + if (nodep->modp()->modPublic()) m_modp->modPublic(true); + //** No iteration for speed } virtual void visit(AstNodeMath* nodep) { - // Speedup + // Speedup } virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS explicit LinkBotupVisitor(AstNetlist* rootp) { - m_modp = NULL; - // + m_modp = NULL; + // iterate(rootp); } virtual ~LinkBotupVisitor() {} diff --git a/src/V3LinkResolve.h b/src/V3LinkResolve.h index 4f69ffeb5..b46f4d74d 100644 --- a/src/V3LinkResolve.h +++ b/src/V3LinkResolve.h @@ -34,4 +34,4 @@ public: static void linkResolve(AstNetlist* rootp); }; -#endif // Guard +#endif // Guard diff --git a/src/V3List.h b/src/V3List.h index a176ad75a..5f610a4e5 100644 --- a/src/V3List.h +++ b/src/V3List.h @@ -35,19 +35,19 @@ template class V3List { // List container for linked list of elements of type *T (T is a pointer type) private: - // STATE - T m_headp; // First element - T m_tailp; // Last element + // MEMBERS + T m_headp; // First element + T m_tailp; // Last element friend class V3ListEnt; public: V3List() - : m_headp(NULL), m_tailp(NULL) {} + : m_headp(NULL), m_tailp(NULL) {} ~V3List() {} // METHODS T begin() const { return m_headp; } T end() const { return NULL; } bool empty() const { return m_headp==NULL; } - void reset() { m_headp=NULL; m_tailp=NULL; } // clear() without walking the list + void reset() { m_headp = NULL; m_tailp = NULL; } // clear() without walking the list }; //============================================================================ @@ -56,61 +56,61 @@ template class V3ListEnt { // List entry for linked list of elements of type *T (T is a pointer type) private: - // STATE - T m_nextp; // Pointer to next element, NULL=end - T m_prevp; // Pointer to previous element, NULL=beginning + // MEMBERS + T m_nextp; // Pointer to next element, NULL=end + T m_prevp; // Pointer to previous element, NULL=beginning friend class V3List; static V3ListEnt* baseToListEnt(void* newbasep, size_t offset) { - // "this" must be a element inside of *basep - // Use that to determine a structure offset, then apply to the new base - // to get our new pointer information - return (V3ListEnt*) ( ((vluint8_t*)newbasep) + offset); + // "this" must be a element inside of *basep + // Use that to determine a structure offset, then apply to the new base + // to get our new pointer information + return (V3ListEnt*) ( ((vluint8_t*)newbasep) + offset); } public: V3ListEnt() - : m_nextp(NULL), m_prevp(NULL) {} + : m_nextp(NULL), m_prevp(NULL) {} ~V3ListEnt() { #ifdef VL_DEBUG - // Load bogus pointers so we can catch deletion bugs - m_nextp = (T)1; - m_prevp = (T)1; + // Load bogus pointers so we can catch deletion bugs + m_nextp = (T)1; + m_prevp = (T)1; #endif } T nextp() const { return m_nextp; } // METHODS void pushBack(V3List& listr, T newp) { - // "this" must be a element inside of *newp - // cppcheck-suppress thisSubtraction - size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(newp); - m_nextp = NULL; - if (!listr.m_headp) listr.m_headp = newp; - m_prevp = listr.m_tailp; - if (m_prevp) baseToListEnt(m_prevp,offset)->m_nextp = newp; - listr.m_tailp = newp; + // "this" must be a element inside of *newp + // cppcheck-suppress thisSubtraction + size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(newp); + m_nextp = NULL; + if (!listr.m_headp) listr.m_headp = newp; + m_prevp = listr.m_tailp; + if (m_prevp) baseToListEnt(m_prevp, offset)->m_nextp = newp; + listr.m_tailp = newp; } void pushFront(V3List& listr, T newp) { - // "this" must be a element inside of *newp - // cppcheck-suppress thisSubtraction - size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(newp); - m_nextp = listr.m_headp; - if (m_nextp) baseToListEnt(m_nextp,offset)->m_prevp = newp; - listr.m_headp = newp; - m_prevp = NULL; - if (!listr.m_tailp) listr.m_tailp = newp; + // "this" must be a element inside of *newp + // cppcheck-suppress thisSubtraction + size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(newp); + m_nextp = listr.m_headp; + if (m_nextp) baseToListEnt(m_nextp, offset)->m_prevp = newp; + listr.m_headp = newp; + m_prevp = NULL; + if (!listr.m_tailp) listr.m_tailp = newp; } // Unlink from side void unlink(V3List& listr, T oldp) { - // "this" must be a element inside of *oldp - // cppcheck-suppress thisSubtraction - size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(oldp); - if (m_nextp) baseToListEnt(m_nextp,offset)->m_prevp = m_prevp; - else listr.m_tailp = m_prevp; - if (m_prevp) baseToListEnt(m_prevp,offset)->m_nextp = m_nextp; - else listr.m_headp = m_nextp; - m_prevp = m_nextp = NULL; + // "this" must be a element inside of *oldp + // cppcheck-suppress thisSubtraction + size_t offset = (size_t)(vluint8_t*)(this) - (size_t)(vluint8_t*)(oldp); + if (m_nextp) baseToListEnt(m_nextp, offset)->m_prevp = m_prevp; + else listr.m_tailp = m_prevp; + if (m_prevp) baseToListEnt(m_prevp, offset)->m_nextp = m_nextp; + else listr.m_headp = m_nextp; + m_prevp = m_nextp = NULL; } }; //============================================================================ -#endif // Guard +#endif // Guard diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index 750330c6b..aad36a43e 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -18,11 +18,11 @@ // //************************************************************************* // LOCALIZE TRANSFORMATIONS: -// All modules: -// VAR(BLOCKTEMP... -// if only referenced in a CFUNC, make it local to that CFUNC -// VAR(others -// if non-public, set before used, and in single CFUNC, make it local +// All modules: +// VAR(BLOCKTEMP... +// if only referenced in a CFUNC, make it local to that CFUNC +// VAR(others +// if non-public, set before used, and in single CFUNC, make it local // //************************************************************************* @@ -44,27 +44,27 @@ class LocalizeBaseVisitor : public AstNVisitor { protected: // NODE STATE // Cleared on entire tree - // AstVar::user1p() -> CFunc which references the variable - // AstVar::user2() -> VarFlags. Flag state - // AstVar::user4() -> AstVarRef*. First place signal set; must be first assignment + // AstVar::user1p() -> CFunc which references the variable + // AstVar::user2() -> VarFlags. Flag state + // AstVar::user4() -> AstVarRef*. First place signal set; must be first assignment // METHODS VL_DEBUG_FUNC; // Declare debug() // TYPES union VarFlags { - // Per-variable flags - // Used in user()'s so initializes to all zeros - struct { - int m_notOpt:1; // NOT optimizable - int m_notStd:1; // NOT optimizable if a non-blocktemp signal - int m_stdFuncAsn:1; // Found simple assignment - int m_done:1; // Removed - }; + // Per-variable flags + // Used in user()'s so initializes to all zeros + struct { + int m_notOpt:1; // NOT optimizable + int m_notStd:1; // NOT optimizable if a non-blocktemp signal + int m_stdFuncAsn:1; // Found simple assignment + int m_done:1; // Removed + }; // cppcheck-suppress unusedStructMember - uint32_t m_flags; + uint32_t m_flags; explicit VarFlags(AstNode* nodep) { m_flags = nodep->user2(); } - void setNodeFlags(AstNode* nodep) { nodep->user2(m_flags); } + void setNodeFlags(AstNode* nodep) { nodep->user2(m_flags); } }; }; @@ -78,11 +78,11 @@ private: // METHODS virtual void visit(AstVarRef* nodep) { - VarFlags flags (nodep->varp()); - if (flags.m_done) { - nodep->hiername(""); // Remove this-> - nodep->hierThis(true); - } + VarFlags flags (nodep->varp()); + if (flags.m_done) { + nodep->hiername(""); // Remove this-> + nodep->hierThis(true); + } } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -102,127 +102,127 @@ class LocalizeVisitor : public LocalizeBaseVisitor { private: // NODE STATE/TYPES // See above - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser4InUse m_inuser4; + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser4InUse m_inuser4; // STATE - V3Double0 m_statLocVars; // Statistic tracking - AstCFunc* m_cfuncp; // Current active function - std::vector m_varps; // List of variables to consider for deletion + V3Double0 m_statLocVars; // Statistic tracking + AstCFunc* m_cfuncp; // Current active function + std::vector m_varps; // List of variables to consider for deletion // METHODS void clearOptimizable(AstVar* nodep, const char* reason) { - UINFO(4," NoOpt "<::iterator it = m_varps.begin(); it != m_varps.end(); ++it) { - AstVar* nodep = *it; - if (nodep->valuep()) clearOptimizable(nodep,"HasInitValue"); - if (!VarFlags(nodep).m_stdFuncAsn) clearStdOptimizable(nodep,"NoStdAssign"); - VarFlags flags (nodep); - if ((nodep->isMovableToBlock() // Blocktemp - || !flags.m_notStd) // Or used only in block - && !flags.m_notOpt // Optimizable - && nodep->user1p()) { // Single cfunc - // We don't need to test for tracing; it would be in the tracefunc if it was needed - UINFO(4," ModVar->BlkVar "<valuep()) clearOptimizable(nodep, "HasInitValue"); + if (!VarFlags(nodep).m_stdFuncAsn) clearStdOptimizable(nodep, "NoStdAssign"); + VarFlags flags (nodep); + if ((nodep->isMovableToBlock() // Blocktemp + || !flags.m_notStd) // Or used only in block + && !flags.m_notOpt // Optimizable + && nodep->user1p()) { // Single cfunc + // We don't need to test for tracing; it would be in the tracefunc if it was needed + UINFO(4," ModVar->BlkVar "<user1p(), CFunc); - nodep->unlinkFrBack(); - newfuncp->addInitsp(nodep); - // Done - flags.m_done = true; - flags.setNodeFlags(nodep); - } else { - clearOptimizable(nodep, "NotDone"); - } - } - m_varps.clear(); + nodep->unlinkFrBack(); + newfuncp->addInitsp(nodep); + // Done + flags.m_done = true; + flags.setNodeFlags(nodep); + } else { + clearOptimizable(nodep, "NotDone"); + } + } + m_varps.clear(); } // VISITORS virtual void visit(AstNetlist* nodep) { iterateChildren(nodep); - moveVars(); + moveVars(); } virtual void visit(AstCFunc* nodep) { - UINFO(4," CFUNC "<argsp()); - searchFuncStmts(nodep->initsp()); - searchFuncStmts(nodep->stmtsp()); - searchFuncStmts(nodep->finalsp()); + UINFO(4," CFUNC "<argsp()); + searchFuncStmts(nodep->initsp()); + searchFuncStmts(nodep->stmtsp()); + searchFuncStmts(nodep->finalsp()); iterateChildren(nodep); - m_cfuncp = NULL; + m_cfuncp = NULL; } void searchFuncStmts(AstNode* nodep) { - // Search for basic assignments to allow moving non-blocktemps - // For now we only find simple assignments not under any other statement. - // This could be more complicated; allow always-set under both branches of a IF. - // If so, check for ArrayRef's and such, as they aren't acceptable. - for (; nodep; nodep=nodep->nextp()) { + // Search for basic assignments to allow moving non-blocktemps + // For now we only find simple assignments not under any other statement. + // This could be more complicated; allow always-set under both branches of a IF. + // If so, check for ArrayRef's and such, as they aren't acceptable. + for (; nodep; nodep=nodep->nextp()) { if (VN_IS(nodep, NodeAssign)) { if (AstVarRef* varrefp = VN_CAST(VN_CAST(nodep, NodeAssign)->lhsp(), VarRef)) { - if (!varrefp->lvalue()) varrefp->v3fatalSrc("LHS assignment not lvalue"); - if (!varrefp->varp()->user4p()) { - UINFO(4," FuncAsn "<varp()->user4p(varrefp); - VarFlags flags (varrefp->varp()); - flags.m_stdFuncAsn = true; - flags.setNodeFlags(varrefp->varp()); - } - } - } - } + if (!varrefp->lvalue()) varrefp->v3fatalSrc("LHS assignment not lvalue"); + if (!varrefp->varp()->user4p()) { + UINFO(4," FuncAsn "<varp()->user4p(varrefp); + VarFlags flags (varrefp->varp()); + flags.m_stdFuncAsn = true; + flags.setNodeFlags(varrefp->varp()); + } + } + } + } } virtual void visit(AstVar* nodep) { - if (!nodep->isSigPublic() - && !nodep->isPrimaryIO() - && !m_cfuncp) { // Not already inside a function - UINFO(4," BLKVAR "<isSigPublic() + && !nodep->isPrimaryIO() + && !m_cfuncp) { // Not already inside a function + UINFO(4," BLKVAR "<varp()).m_notOpt) { - if (!m_cfuncp) { // Not in function, can't optimize - clearOptimizable(nodep->varp(), "BVnofunc"); - } - else { - // If we're scoping down to it, it isn't really in the same block - if (!nodep->hierThis()) clearOptimizable(nodep->varp(),"HierRef"); - // Allow a variable to appear in only a single function - AstNode* oldfunc = nodep->varp()->user1p(); - if (!oldfunc) { - UINFO(4," BVnewref "<varp()->user1p(m_cfuncp); // Remember where it was used - } else if (m_cfuncp == oldfunc) { - // Same usage - } else { - // Used in multiple functions - clearOptimizable(nodep->varp(),"BVmultiF"); - } - // First varref in function must be assignment found earlier + if (!VarFlags(nodep->varp()).m_notOpt) { + if (!m_cfuncp) { // Not in function, can't optimize + clearOptimizable(nodep->varp(), "BVnofunc"); + } + else { + // If we're scoping down to it, it isn't really in the same block + if (!nodep->hierThis()) clearOptimizable(nodep->varp(), "HierRef"); + // Allow a variable to appear in only a single function + AstNode* oldfunc = nodep->varp()->user1p(); + if (!oldfunc) { + UINFO(4," BVnewref "<varp()->user1p(m_cfuncp); // Remember where it was used + } else if (m_cfuncp == oldfunc) { + // Same usage + } else { + // Used in multiple functions + clearOptimizable(nodep->varp(), "BVmultiF"); + } + // First varref in function must be assignment found earlier AstVarRef* firstasn = static_cast(nodep->varp()->user4p()); - if (firstasn && nodep!=firstasn) { - clearStdOptimizable(nodep->varp(),"notFirstAsn"); - nodep->varp()->user4p(NULL); - } - } - } - // No iterate; Don't want varrefs under it + if (firstasn && nodep!=firstasn) { + clearStdOptimizable(nodep->varp(), "notFirstAsn"); + nodep->varp()->user4p(NULL); + } + } + } + // No iterate; Don't want varrefs under it } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -230,11 +230,11 @@ private: public: // CONSTRUCTORS explicit LocalizeVisitor(AstNetlist* nodep) { - m_cfuncp = NULL; + m_cfuncp = NULL; iterate(nodep); } virtual ~LocalizeVisitor() { - V3Stats::addStat("Optimizations, Vars localized", m_statLocVars); + V3Stats::addStat("Optimizations, Vars localized", m_statLocVars); } }; diff --git a/src/V3Localize.h b/src/V3Localize.h index 6b7c1cca7..3732298e7 100644 --- a/src/V3Localize.h +++ b/src/V3Localize.h @@ -34,4 +34,4 @@ public: static void localizeAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Name.cpp b/src/V3Name.cpp index d0cb81c2a..9dfdacb24 100644 --- a/src/V3Name.cpp +++ b/src/V3Name.cpp @@ -19,8 +19,8 @@ //************************************************************************* // V3Name's Transformations: // -// Cell/Var's -// Prepend __PVT__ to variable names +// Cell/Var's +// Prepend __PVT__ to variable names //************************************************************************* #include "config_build.h" @@ -41,89 +41,90 @@ class NameVisitor : public AstNVisitor { private: // NODE STATE // Cleared on Netlist - // AstCell::user1() -> bool. Set true if already processed - // AstScope::user1() -> bool. Set true if already processed - // AstVar::user1() -> bool. Set true if already processed - AstUser1InUse m_inuser1; + // AstCell::user1() -> bool. Set true if already processed + // AstScope::user1() -> bool. Set true if already processed + // AstVar::user1() -> bool. Set true if already processed + AstUser1InUse m_inuser1; // STATE - AstNodeModule* m_modp; - V3LanguageWords m_words; // Reserved word detector + AstNodeModule* m_modp; + V3LanguageWords m_words; // Reserved word detector // METHODS VL_DEBUG_FUNC; // Declare debug() void rename(AstNode* nodep, bool addPvt) { - if (!nodep->user1()) { // Not already done - if (addPvt) { + if (!nodep->user1()) { // Not already done + if (addPvt) { string newname = string("__PVT__")+nodep->name(); - nodep->name(newname); - } else { - string rsvd = m_words.isKeyword(nodep->name()); - if (rsvd != "") { - nodep->v3warn(SYMRSVDWORD,"Symbol matches "+rsvd+": '"<prettyName()<<"'"); + nodep->name(newname); + } else { + string rsvd = m_words.isKeyword(nodep->name()); + if (rsvd != "") { + nodep->v3warn(SYMRSVDWORD, "Symbol matches "+rsvd + +": '"<prettyName()<<"'"); string newname = string("__SYM__")+nodep->name(); - nodep->name(newname); - } - } - nodep->user1(1); - } + nodep->name(newname); + } + } + nodep->user1(1); + } } // VISITORS virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; + m_modp = nodep; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } // Add __PVT__ to names of local signals virtual void visit(AstVar* nodep) { - // Don't iterate... Don't need temps for RANGES under the Var. - rename(nodep, (!m_modp->isTop() - && !nodep->isSigPublic() - && !nodep->isFuncLocal() // Isn't exposed, and would mess up dpi import wrappers - && !nodep->isTemp())); // Don't bother to rename internal signals + // Don't iterate... Don't need temps for RANGES under the Var. + rename(nodep, (!m_modp->isTop() + && !nodep->isSigPublic() + && !nodep->isFuncLocal() // Isn't exposed, and would mess up dpi import wrappers + && !nodep->isTemp())); // Don't bother to rename internal signals } virtual void visit(AstCFunc* nodep) { - if (!nodep->user1()) { + if (!nodep->user1()) { iterateChildren(nodep); - rename(nodep, false); - } + rename(nodep, false); + } } virtual void visit(AstVarRef* nodep) { - if (nodep->varp()) { + if (nodep->varp()) { iterate(nodep->varp()); - nodep->name(nodep->varp()->name()); - } + nodep->name(nodep->varp()->name()); + } } virtual void visit(AstCell* nodep) { - if (!nodep->user1()) { - rename(nodep, !nodep->modp()->modPublic()); + if (!nodep->user1()) { + rename(nodep, !nodep->modp()->modPublic()); iterateChildren(nodep); - } + } } virtual void visit(AstMemberDType* nodep) { - if (!nodep->user1()) { - rename(nodep, false); + if (!nodep->user1()) { + rename(nodep, false); iterateChildren(nodep); - } + } } virtual void visit(AstMemberSel* nodep) { - if (!nodep->user1()) { - rename(nodep, false); + if (!nodep->user1()) { + rename(nodep, false); iterateChildren(nodep); - } + } } virtual void visit(AstScope* nodep) { - if (!nodep->user1SetOnce()) { + if (!nodep->user1SetOnce()) { if (nodep->aboveScopep()) iterate(nodep->aboveScopep()); if (nodep->aboveCellp()) iterate(nodep->aboveCellp()); - // Always recompute name (as many level above scope may have changed) - // Same formula as V3Scope - nodep->name(nodep->isTop() ? "TOP" - : (nodep->aboveScopep()->name()+"."+nodep->aboveCellp()->name())); + // Always recompute name (as many level above scope may have changed) + // Same formula as V3Scope + nodep->name(nodep->isTop() ? "TOP" + : (nodep->aboveScopep()->name()+"."+nodep->aboveCellp()->name())); iterateChildren(nodep); - } + } } //-------------------- diff --git a/src/V3Name.h b/src/V3Name.h index 23db1a604..aa920fe2c 100644 --- a/src/V3Name.h +++ b/src/V3Name.h @@ -34,4 +34,4 @@ public: static void nameAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 1cfac108c..d395ee8cb 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -52,12 +52,12 @@ V3Number::V3Number(VerilogStringLiteral, AstNode* nodep, const string& str) { init(nodep, str.length()*8); m_fromString = true; for (unsigned pos=0; posopAdd(product, addend); if (product.bitsValue(width(), 4)) { // Overflowed v3error("Too many digits for "<=0; bit--) { - if (bitIs0(bit)) str+='0'; - else if (bitIs1(bit)) str+='1'; - else if (bitIsZ(bit)) str+='z'; - else str+='x'; - } - return str; + int bit = width()-1; + if (fmtsize == "0") while (bit && bitIs0(bit)) bit--; + for (; bit>=0; bit--) { + if (bitIs0(bit)) str+='0'; + else if (bitIs1(bit)) str+='1'; + else if (bitIsZ(bit)) str+='z'; + else str+='x'; + } + return str; } case 'o': { - int bit = width()-1; - if (fmtsize == "0") while (bit && bitIs0(bit)) bit--; - while ((bit%3)!=2) bit++; - for (; bit>0; bit -= 3) { - int v = bitsValue(bit-2, 3); + int bit = width()-1; + if (fmtsize == "0") while (bit && bitIs0(bit)) bit--; + while ((bit%3)!=2) bit++; + for (; bit>0; bit -= 3) { + int v = bitsValue(bit-2, 3); str += static_cast('0' + v); - } - return str; + } + return str; } case 'h': case 'x': { - int bit = width()-1; - if (fmtsize == "0") while (bit && bitIs0(bit)) bit--; - while ((bit%4)!=3) bit++; - for (; bit>0; bit -= 4) { - int v = bitsValue(bit-3, 4); + int bit = width()-1; + if (fmtsize == "0") while (bit && bitIs0(bit)) bit--; + while ((bit%4)!=3) bit++; + for (; bit>0; bit -= 4) { + int v = bitsValue(bit-3, 4); if (v>=10) str += static_cast('a' + v - 10); else str += static_cast('0' + v); - } - return str; + } + return str; } case 'c': { if (width()>8) fl->v3warn(WIDTH, "$display-like format of %c format of > 8 bit value"); unsigned int v = bitsValue(0, 8); char strc[2]; strc[0] = v&0xff; strc[1] = '\0'; - str = strc; - return str; + str = strc; + return str; } case 's': { - // Spec says always drop leading zeros, this isn't quite right, we space pad. - int bit=this->width()-1; - bool start=true; - while ((bit%8)!=7) bit++; - for (; bit>=0; bit -= 8) { - int v = bitsValue(bit-7, 8); - if (!start || v) { + // Spec says always drop leading zeros, this isn't quite right, we space pad. + int bit = this->width()-1; + bool start = true; + while ((bit%8)!=7) bit++; + for (; bit>=0; bit -= 8) { + int v = bitsValue(bit-7, 8); + if (!start || v) { str += static_cast((v==0) ? ' ' : v); start = false; // Drop leading 0s - } else { - if (fmtsize != "0") str += ' '; - } - } - return str; + } else { + if (fmtsize != "0") str += ' '; + } + } + return str; } - case '~': // Signed decimal - case 't': // Time - case 'd': { // Unsigned decimal - bool issigned = (code == '~'); - if (fmtsize == "") { - double mantissabits = this->width() - (issigned?1:0); - double maxval = pow(2.0, mantissabits); - double dchars = log10(maxval)+1.0; - if (issigned) dchars++; // space for sign - fmtsize = cvtToStr(int(dchars)); - } + case '~': // Signed decimal + case 't': // Time + case 'd': { // Unsigned decimal + bool issigned = (code == '~'); + if (fmtsize == "") { + double mantissabits = this->width() - (issigned?1:0); + double maxval = pow(2.0, mantissabits); + double dchars = log10(maxval)+1.0; + if (issigned) dchars++; // space for sign + fmtsize = cvtToStr(int(dchars)); + } if (issigned) { if (width() > 64) { str = toDecimalS(); @@ -547,35 +548,35 @@ string V3Number::displayed(FileLine*fl, const string& vformat) const { str = cvtToStr(toUQuad()); } } - int intfmtsize = atoi(fmtsize.c_str()); - bool zeropad = fmtsize.length()>0 && fmtsize[0]=='0'; + int intfmtsize = atoi(fmtsize.c_str()); + bool zeropad = fmtsize.length()>0 && fmtsize[0]=='0'; while (static_cast(str.length()) < intfmtsize) { if (zeropad) str.insert(0, "0"); else str.insert(0, " "); - } - return str; + } + return str; } case 'e': case 'f': case 'g': case '^': { // Realtime char tmp[MAX_SPRINTF_DOUBLE_SIZE]; - sprintf(tmp, vformat.c_str(), toDouble()); - return tmp; + sprintf(tmp, vformat.c_str(), toDouble()); + return tmp; } // 'l' // Library - converted to text by V3LinkResolve // 'p' // Packed - converted to another code by V3Width case 'u': { // Packed 2-state - for (int i=0; i((m_value[i] >> 0) & 0xff); str += static_cast((m_value[i] >> 8) & 0xff); str += static_cast((m_value[i] >> 16) & 0xff); str += static_cast((m_value[i] >> 24) & 0xff); - } - return str; + } + return str; } case 'z': { // Packed 4-state - for (int i=0; i((m_value[i] >> 0) & 0xff); str += static_cast((m_value[i] >> 8) & 0xff); str += static_cast((m_value[i] >> 16) & 0xff); @@ -584,21 +585,21 @@ string V3Number::displayed(FileLine*fl, const string& vformat) const { str += static_cast((m_valueX[i] >> 8) & 0xff); str += static_cast((m_valueX[i] >> 16) & 0xff); str += static_cast((m_valueX[i] >> 24) & 0xff); - } - return str; + } + return str; } case 'v': { // Strength - int bit = width()-1; - for (; bit>=0; bit--) { - if (bitIs0(bit)) str+="St0 "; // Yes, always a space even for bit 0 - else if (bitIs1(bit)) str+="St1 "; - else if (bitIsZ(bit)) str+="StZ "; - else str+="StX"; - } - return str; + int bit = width()-1; + for (; bit>=0; bit--) { + if (bitIs0(bit)) str += "St0 "; // Yes, always a space even for bit 0 + else if (bitIs1(bit)) str += "St1 "; + else if (bitIsZ(bit)) str += "StZ "; + else str += "StX"; + } + return str; } case '@': { // Packed string - return toString(); + return toString(); } default: fl->v3fatalSrc("Unknown $display-like format code for number: %"<= 0; --from_bit) { // Any digits >= 5 need an add 3 (via tmp) - for (int nibble_bit = 0; nibble_bit < maxdecwidth; nibble_bit+=4) { + for (int nibble_bit = 0; nibble_bit < maxdecwidth; nibble_bit += 4) { if (bcd.bitsValue(nibble_bit, 4) >= 5) { tmp2.setAllBits0(); tmp2.setBit(nibble_bit, 1); // Add 3, decompsed as two bits @@ -648,7 +649,7 @@ string V3Number::toDecimalU() const { string output; int lsb = (maxdecwidth-1) & ~3; - for (; lsb>0; lsb-=4) { // Skip leading zeros + for (; lsb>0; lsb-=4) { // Skip leading zeros if (bcd.bitsValue(lsb, 4)) break; } for (; lsb>=0; lsb-=4) { @@ -661,10 +662,10 @@ string V3Number::toDecimalU() const { // ACCESSORS - as numbers uint32_t V3Number::toUInt() const { - UASSERT(!isFourState(),"toUInt with 4-state "<<*this); + UASSERT(!isFourState(), "toUInt with 4-state "<<*this); // We allow wide numbers that represent values <= 32 bits for (int i=1; i(extended); } else { - // Where we use this (widths, etc) and care about signedness, - // we can reasonably assume the MSB isn't set on unsigned numbers. + // Where we use this (widths, etc) and care about signedness, + // we can reasonably assume the MSB isn't set on unsigned numbers. return static_cast(toUInt()); } } vluint64_t V3Number::toUQuad() const { - UASSERT(!isFourState(),"toUQuad with 4-state "<<*this); + UASSERT(!isFourState(), "toUQuad with 4-state "<<*this); // We allow wide numbers that represent values <= 64 bits for (int i=2; i(toUInt())); return ((static_cast(m_value[1])<width()-1; - bool start=true; + int bit = this->width()-1; + bool start = true; while ((bit%8)!=7) bit++; string str; for (; bit>=0; bit -= 8) { - int v = bitsValue(bit-7, 8); - if (!start || v) { + int v = bitsValue(bit-7, 8); + if (!start || v) { str += static_cast((v==0) ? ' ' : v); start = false; // Drop leading 0s - } + } } return str; } @@ -735,86 +736,86 @@ uint32_t V3Number::toHash() const { } uint32_t V3Number::dataWord(int word) const { - UASSERT(!isFourState(),"dataWord with 4-state "<<*this); + UASSERT(!isFourState(), "dataWord with 4-state "<<*this); return m_value[word]; } bool V3Number::isEqZero() const { for (int i=0; iwidth(),rhs.width()); bit++) { - if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; } - if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; } - if (this->bitIsXZ(bit)) { return 0; } - if (rhs.bitIsXZ(bit)) { return 0; } + for (int bit=0; bit < std::max(this->width(), rhs.width()); bit++) { + if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; } + if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; } + if (this->bitIsXZ(bit)) { return 0; } + if (rhs.bitIsXZ(bit)) { return 0; } } return 0; } bool V3Number::isLtXZ(const V3Number& rhs) const { // Include X/Z in comparisons for sort ordering - for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) { - if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; } - if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; } - if (this->bitIsXZ(bit)) { return 1; } - if (rhs.bitIsXZ(bit)) { return 0; } + for (int bit=0; bit < std::max(this->width(), rhs.width()); bit++) { + if (this->bitIs1(bit) && rhs.bitIs0(bit)) { return 1; } + if (rhs.bitIs1(bit) && this->bitIs0(bit)) { return 0; } + if (this->bitIsXZ(bit)) { return 1; } + if (rhs.bitIsXZ(bit)) { return 0; } } return 0; } int V3Number::widthMin() const { - for(int bit=width()-1; bit>0; bit--) { - if (!bitIs0(bit)) return bit+1; + for (int bit=width()-1; bit>0; bit--) { + if (!bitIs0(bit)) return bit+1; } - return 1; // one bit even if number is == 0 + return 1; // one bit even if number is == 0 } uint32_t V3Number::countOnes() const { - int n=0; - for(int bit=0; bitwidth(); bit++) { - if (bitIs1(bit)) n++; + int n = 0; + for (int bit=0; bitwidth(); bit++) { + if (bitIs1(bit)) n++; } return n; } uint32_t V3Number::mostSetBitP1() const { for (int bit=this->width()-1; bit>=0; bit--) { - if (bitIs1(bit)) return bit+1; + if (bitIs1(bit)) return bit+1; } return 0; } @@ -823,40 +824,40 @@ uint32_t V3Number::mostSetBitP1() const { V3Number& V3Number::opBitsNonX(const V3Number& lhs) { // 0/1->1, X/Z->0 // op i, L(lhs) bit return setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIs0(bit) || lhs.bitIs1(bit)) { setBit(bit,1); } + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIs0(bit) || lhs.bitIs1(bit)) { setBit(bit, 1); } } return *this; } V3Number& V3Number::opBitsOne(const V3Number& lhs) { // 1->1, 0/X/Z->0 // op i, L(lhs) bit return setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIs1(bit)) { setBit(bit,1); } + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit)) { setBit(bit, 1); } } return *this; } V3Number& V3Number::opBitsXZ(const V3Number& lhs) { // 0/1->1, X/Z->0 // op i, L(lhs) bit return setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIsXZ(bit)) { setBit(bit,1); } + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIsXZ(bit)) { setBit(bit, 1); } } return *this; } V3Number& V3Number::opBitsZ(const V3Number& lhs) { // 0/1->1, X/Z->0 // op i, L(lhs) bit return setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIsZ(bit)) { setBit(bit,1); } + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIsZ(bit)) { setBit(bit, 1); } } return *this; } V3Number& V3Number::opBitsNonZ(const V3Number& lhs) { // 0/1->1, X/Z->0 // op i, L(lhs) bit return setZero(); - for(int bit=0; bitwidth(); bit++) { - if (!lhs.bitIsZ(bit)) { setBit(bit,1); } + for (int bit=0; bitwidth(); bit++) { + if (!lhs.bitIsZ(bit)) { setBit(bit, 1); } } return *this; } @@ -867,10 +868,10 @@ V3Number& V3Number::opBitsNonZ(const V3Number& lhs) { // 0/1->1, X/Z->0 V3Number& V3Number::opRedOr(const V3Number& lhs) { // op i, 1 bit return char outc = 0; - for(int bit=0; bit=0; bit--) { - if (lhs.bitIs1(bit)) { - setLong(bit+adjust); - return *this; - } + if (lhs.bitIs1(bit)) { + setLong(bit+adjust); + return *this; + } } setZero(); return *this; @@ -943,10 +944,10 @@ V3Number& V3Number::opCLog2(const V3Number& lhs) { V3Number& V3Number::opLogNot(const V3Number& lhs) { // op i, 1 bit return char outc = 1; - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIs0(bit)) { setBit(bit,1); } - else if (lhs.bitIsXZ(bit)) { setBit(bit,'x'); } + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIs0(bit)) { setBit(bit, 1); } + else if (lhs.bitIsXZ(bit)) { setBit(bit,'x'); } } return *this; } @@ -965,10 +966,10 @@ V3Number& V3Number::opNot(const V3Number& lhs) { V3Number& V3Number::opAnd(const V3Number& lhs, const V3Number& rhs) { // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit,1); } - else if (lhs.bitIs0(bit) || rhs.bitIs0(bit)) ; // 0 - else { setBit(bit,'x'); } + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit, 1); } + else if (lhs.bitIs0(bit) || rhs.bitIs0(bit)) ; // 0 + else { setBit(bit,'x'); } } return *this; } @@ -976,28 +977,28 @@ V3Number& V3Number::opAnd(const V3Number& lhs, const V3Number& rhs) { V3Number& V3Number::opOr(const V3Number& lhs, const V3Number& rhs) { // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIs1(bit) || rhs.bitIs1(bit)) { setBit(bit,1); } - else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) ; // 0 - else { setBit(bit,'x'); } + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) || rhs.bitIs1(bit)) { setBit(bit, 1); } + else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) ; // 0 + else { setBit(bit,'x'); } } return *this; } V3Number& V3Number::opChangeXor(const V3Number& lhs, const V3Number& rhs) { // 32 bit result - opEq(lhs,rhs); + opEq(lhs, rhs); return *this; } V3Number& V3Number::opXor(const V3Number& lhs, const V3Number& rhs) { // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { setBit(bit,1); } - else if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { setBit(bit,1); } - else if (lhs.bitIsXZ(bit) && rhs.bitIsXZ(bit)) { setBit(bit,'x'); } - // else zero + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { setBit(bit, 1); } + else if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) { setBit(bit, 1); } + else if (lhs.bitIsXZ(bit) && rhs.bitIsXZ(bit)) { setBit(bit,'x'); } + // else zero } return *this; } @@ -1005,11 +1006,11 @@ V3Number& V3Number::opXor(const V3Number& lhs, const V3Number& rhs) { V3Number& V3Number::opXnor(const V3Number& lhs, const V3Number& rhs) { // i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend. setZero(); - for(int bit=0; bitwidth(); bit++) { - if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit,1); } - else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) { setBit(bit,1); } - else if (lhs.bitIsXZ(bit) && rhs.bitIsXZ(bit)) { setBit(bit,'x'); } - // else zero + for (int bit=0; bitwidth(); bit++) { + if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) { setBit(bit, 1); } + else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) { setBit(bit, 1); } + else if (lhs.bitIsXZ(bit) && rhs.bitIsXZ(bit)) { setBit(bit,'x'); } + // else zero } return *this; } @@ -1018,16 +1019,16 @@ V3Number& V3Number::opConcat(const V3Number& lhs, const V3Number& rhs) { setZero(); // See also error in V3Width if (!lhs.sized() || !rhs.sized()) { - v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in concatenations."); + v3warn(WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations."); } int obit = 0; - for(int bit=0; bit 8192) { - v3warn(WIDTHCONCAT,"More than a 8k bit replication is probably wrong: "<(lhs.width())); for (int istart=0; istartwidth() != rhs.width()) return false; - for (int bit=0; bit < std::max(this->width(),rhs.width()); bit++) { - if (this->bitIs(bit) != rhs.bitIs(bit)) { return false; } + for (int bit=0; bit < std::max(this->width(), rhs.width()); bit++) { + if (this->bitIs(bit) != rhs.bitIs(bit)) { return false; } } return true; } @@ -1166,8 +1167,8 @@ V3Number& V3Number::opCaseEq(const V3Number& lhs, const V3Number& rhs) { V3Number& V3Number::opCaseNeq(const V3Number& lhs, const V3Number& rhs) { // i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend. char outc = 0; - for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) { - if (lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; } + for (int bit=0; bit < std::max(lhs.width(), rhs.width()); bit++) { + if (lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; } } last: return setSingleBits(outc); @@ -1175,10 +1176,10 @@ last: V3Number& V3Number::opWildEq(const V3Number& lhs, const V3Number& rhs) { char outc = 1; - for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) { - if (!rhs.bitIsXZ(bit) - && lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=0; goto last; } - if (lhs.bitIsXZ(bit)) outc='x'; + for (int bit=0; bit < std::max(lhs.width(), rhs.width()); bit++) { + if (!rhs.bitIsXZ(bit) + && lhs.bitIs(bit) != rhs.bitIs(bit)) { outc = 0; goto last; } + if (lhs.bitIsXZ(bit)) outc='x'; } last: return setSingleBits(outc); @@ -1186,10 +1187,10 @@ last: V3Number& V3Number::opWildNeq(const V3Number& lhs, const V3Number& rhs) { char outc = 0; - for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) { - if (!rhs.bitIsXZ(bit) - && lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; } - if (lhs.bitIsXZ(bit)) outc='x'; + for (int bit=0; bit < std::max(lhs.width(), rhs.width()); bit++) { + if (!rhs.bitIsXZ(bit) + && lhs.bitIs(bit) != rhs.bitIs(bit)) { outc=1; goto last; } + if (lhs.bitIsXZ(bit)) outc='x'; } last: return setSingleBits(outc); @@ -1198,11 +1199,11 @@ last: V3Number& V3Number::opGt(const V3Number& lhs, const V3Number& rhs) { // i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend. char outc = 0; - for (int bit=0; bit < std::max(lhs.width(),rhs.width()); bit++) { - if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=1; } - if (rhs.bitIs1(bit) && lhs.bitIs0(bit)) { outc=0; } - if (lhs.bitIsXZ(bit)) { outc='x'; } - if (rhs.bitIsXZ(bit)) { outc='x'; } + for (int bit=0; bit < std::max(lhs.width(), rhs.width()); bit++) { + if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) { outc=1; } + if (rhs.bitIs1(bit) && lhs.bitIs0(bit)) { outc = 0; } + if (lhs.bitIsXZ(bit)) { outc='x'; } + if (rhs.bitIsXZ(bit)) { outc='x'; } } return setSingleBits(outc); } @@ -1211,49 +1212,49 @@ V3Number& V3Number::opGtS(const V3Number& lhs, const V3Number& rhs) { // i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend. char outc = 0; { - int mbit = std::max(lhs.width()-1,rhs.width()-1); - if (lhs.bitIsXZ(mbit)) { outc='x'; } - else if (rhs.bitIsXZ(mbit)) { outc='x'; } - else if (lhs.bitIs0(mbit) && rhs.bitIs1Extend(mbit)) { outc=1; } // + > - - else if (lhs.bitIs1Extend(mbit) && rhs.bitIs0(mbit)) { outc=0; } // - !> + - else { - // both positive or negative, normal > - for (int bit=0; bit < std::max(lhs.width()-1,rhs.width()-1); bit++) { - if (lhs.bitIs1Extend(bit) && rhs.bitIs0(bit)) { outc=1; } - if (rhs.bitIs1Extend(bit) && lhs.bitIs0(bit)) { outc=0; } - if (lhs.bitIsXZ(bit)) { outc='x'; } - if (rhs.bitIsXZ(bit)) { outc='x'; } - } - } + int mbit = std::max(lhs.width()-1, rhs.width()-1); + if (lhs.bitIsXZ(mbit)) { outc='x'; } + else if (rhs.bitIsXZ(mbit)) { outc='x'; } + else if (lhs.bitIs0(mbit) && rhs.bitIs1Extend(mbit)) { outc = 1; } // + > - + else if (lhs.bitIs1Extend(mbit) && rhs.bitIs0(mbit)) { outc = 0; } // - !> + + else { + // both positive or negative, normal > + for (int bit=0; bit < std::max(lhs.width()-1, rhs.width()-1); bit++) { + if (lhs.bitIs1Extend(bit) && rhs.bitIs0(bit)) { outc = 1; } + if (rhs.bitIs1Extend(bit) && lhs.bitIs0(bit)) { outc = 0; } + if (lhs.bitIsXZ(bit)) { outc = 'x'; } + if (rhs.bitIsXZ(bit)) { outc = 'x'; } + } + } } return setSingleBits(outc); } V3Number& V3Number::opGte(const V3Number& lhs, const V3Number& rhs) { // i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend. - V3Number& eq = opEq(lhs,rhs); - if (eq.isNeqZero()) return eq; // Return true - return opGt(lhs,rhs); + V3Number& eq = opEq(lhs, rhs); + if (eq.isNeqZero()) return eq; // Return true + return opGt(lhs, rhs); } V3Number& V3Number::opGteS(const V3Number& lhs, const V3Number& rhs) { // i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend. - V3Number& eq = opEq(lhs,rhs); - if (eq.isNeqZero()) return eq; // Return true - return opGtS(lhs,rhs); + V3Number& eq = opEq(lhs, rhs); + if (eq.isNeqZero()) return eq; // Return true + return opGtS(lhs, rhs); } V3Number& V3Number::opLt(const V3Number& lhs, const V3Number& rhs) { - return opGt(rhs,lhs); + return opGt(rhs, lhs); } V3Number& V3Number::opLte(const V3Number& lhs, const V3Number& rhs) { - return opGte(rhs,lhs); + return opGte(rhs, lhs); } V3Number& V3Number::opLtS(const V3Number& lhs, const V3Number& rhs) { - return opGtS(rhs,lhs); + return opGtS(rhs, lhs); } V3Number& V3Number::opLteS(const V3Number& lhs, const V3Number& rhs) { - return opGteS(rhs,lhs); + return opGteS(rhs, lhs); } V3Number& V3Number::opRotR(const V3Number& lhs, const V3Number& rhs) { @@ -1262,7 +1263,7 @@ V3Number& V3Number::opRotR(const V3Number& lhs, const V3Number& rhs) { setZero(); uint32_t rhsval = rhs.toUInt(); for (int bit=0; bitwidth(); bit++) { - setBit(bit,lhs.bitIs((bit + rhsval) % this->width())); + setBit(bit, lhs.bitIs((bit + rhsval) % this->width())); } return *this; } @@ -1274,8 +1275,8 @@ V3Number& V3Number::opRotL(const V3Number& lhs, const V3Number& rhs) { uint32_t rhsval = rhs.toUInt(); for (int bit=0; bitwidth(); bit++) { if (bit >= static_cast(rhsval)) { - setBit(bit,lhs.bitIs((bit - rhsval) % this->width())); - } + setBit(bit, lhs.bitIs((bit - rhsval) % this->width())); + } } return *this; } @@ -1285,13 +1286,13 @@ V3Number& V3Number::opShiftR(const V3Number& lhs, const V3Number& rhs) { if (rhs.isFourState()) return setAllBitsX(); setZero(); for (int bit=32; bit(lhs.width())) { - for (int bit=0; bitwidth(); bit++) { - setBit(bit,lhs.bitIs(bit + rhsval)); - } + for (int bit=0; bitwidth(); bit++) { + setBit(bit, lhs.bitIs(bit + rhsval)); + } } return *this; } @@ -1303,21 +1304,21 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t if (rhs.isFourState()) return setAllBitsX(); setZero(); for (int bit=32; bitwidth(); bit++) { - setBit(bit,lhs.bitIs(lbits-1)); // 0/1/X/Z - } - if (rhs.bitIs1(lbits-1)) setAllBits1(); // -1 else 0 - return *this; // shift of over 2^32 must be -1/0 + for (int bit=0; bitwidth(); bit++) { + setBit(bit, lhs.bitIs(lbits-1)); // 0/1/X/Z + } + if (rhs.bitIs1(lbits-1)) setAllBits1(); // -1 else 0 + return *this; // shift of over 2^32 must be -1/0 } uint32_t rhsval = rhs.toUInt(); if (rhsval < static_cast(lhs.width())) { - for (int bit=0; bitwidth(); bit++) { - setBit(bit,lhs.bitIsExtend(bit + rhsval, lbits)); - } + for (int bit=0; bitwidth(); bit++) { + setBit(bit, lhs.bitIsExtend(bit + rhsval, lbits)); + } } else { - for (int bit=0; bitwidth(); bit++) { - setBit(bit,lhs.bitIs(lbits-1)); // 0/1/X/Z - } + for (int bit=0; bitwidth(); bit++) { + setBit(bit, lhs.bitIs(lbits-1)); // 0/1/X/Z + } } return *this; } @@ -1327,13 +1328,13 @@ V3Number& V3Number::opShiftL(const V3Number& lhs, const V3Number& rhs) { if (rhs.isFourState()) return setAllBitsX(); setZero(); for (int bit=32; bitwidth(); bit++) { if (bit >= static_cast(rhsval)) { setBit(bit, lhs.bitIs(bit - rhsval)); - } + } } return *this; } @@ -1345,9 +1346,9 @@ V3Number& V3Number::opAbsS(const V3Number& lhs) { // op i, L(lhs) bit return if (lhs.isFourState()) return setAllBitsX(); if (lhs.isNegative()) { - return opNegate(lhs); + return opNegate(lhs); } else { - return opAssign(lhs); + return opAssign(lhs); } } V3Number& V3Number::opNegate(const V3Number& lhs) { @@ -1356,7 +1357,7 @@ V3Number& V3Number::opNegate(const V3Number& lhs) { V3Number notlhs (&lhs, width()); notlhs.opNot(lhs); V3Number one (&lhs, width(), 1); - opAdd(notlhs,one); + opAdd(notlhs, one); return *this; } V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) { @@ -1366,11 +1367,11 @@ V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) { // Addem int carry=0; for (int bit=0; bitwidth(); bit++) { - int sum = ((lhs.bitIs1(bit)?1:0) + (rhs.bitIs1(bit)?1:0) + carry); - if (sum & 1) { - setBit(bit,1); - } - carry = (sum >= 2); + int sum = ((lhs.bitIs1(bit)?1:0) + (rhs.bitIs1(bit)?1:0) + carry); + if (sum & 1) { + setBit(bit, 1); + } + carry = (sum >= 2); } return *this; } @@ -1386,21 +1387,21 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) { if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); setZero(); if (width() <= 64) { - setQuad(lhs.toUQuad() * rhs.toUQuad()); - opCleanThis(); // Mult produces extra bits in result + setQuad(lhs.toUQuad() * rhs.toUQuad()); + opCleanThis(); // Mult produces extra bits in result } else { - for (int lword=0; lword(lhs.m_value[lword]) * static_cast(rhs.m_value[rword]); - for (int qword=lword+rword; qwordwords(); qword++) { - mul += (vluint64_t)(m_value[qword]); - m_value[qword] = (mul & VL_ULL(0xffffffff)); - mul = (mul >> VL_ULL(32)) & VL_ULL(0xffffffff); - } - } - } - opCleanThis(); // Mult produces extra bits in result + for (int qword=lword+rword; qwordwords(); qword++) { + mul += (vluint64_t)(m_value[qword]); + m_value[qword] = (mul & VL_ULL(0xffffffff)); + mul = (mul >> VL_ULL(32)) & VL_ULL(0xffffffff); + } + } + } + opCleanThis(); // Mult produces extra bits in result } return *this; } @@ -1409,12 +1410,12 @@ V3Number& V3Number::opMulS(const V3Number& lhs, const V3Number& rhs) { if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opNegate(lhs); V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opNegate(rhs); - V3Number qNoSign = opMul(lhsNoSign,rhsNoSign); + V3Number qNoSign = opMul(lhsNoSign, rhsNoSign); if ((lhs.isNegative() && !rhs.isNegative()) - || (!lhs.isNegative() && rhs.isNegative())) { - opNegate(qNoSign); + || (!lhs.isNegative() && rhs.isNegative())) { + opNegate(qNoSign); } else { - opAssign(qNoSign); + opAssign(qNoSign); } return *this; } @@ -1424,11 +1425,11 @@ V3Number& V3Number::opDiv(const V3Number& lhs, const V3Number& rhs) { if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX(); if (rhs.isEqZero()) return setAllBitsXRemoved(); if (lhs.width()<=64) { - setQuad(lhs.toUQuad() / rhs.toUQuad()); - return *this; + setQuad(lhs.toUQuad() / rhs.toUQuad()); + return *this; } else { - // Wide division - return opModDivGuts(lhs,rhs,false); + // Wide division + return opModDivGuts(lhs, rhs, false); } } V3Number& V3Number::opDivS(const V3Number& lhs, const V3Number& rhs) { @@ -1438,13 +1439,13 @@ V3Number& V3Number::opDivS(const V3Number& lhs, const V3Number& rhs) { if (rhs.isEqZero()) return setAllBitsXRemoved(); V3Number lhsNoSign = lhs; if (lhs.isNegative()) lhsNoSign.opNegate(lhs); V3Number rhsNoSign = rhs; if (rhs.isNegative()) rhsNoSign.opNegate(rhs); - V3Number qNoSign = opDiv(lhsNoSign,rhsNoSign); + V3Number qNoSign = opDiv(lhsNoSign, rhsNoSign); //UINFO(9, " >divs-mid "<= 0; j--) { + vluint64_t k = 0; + for (int j = uw-1; j >= 0; j--) { vluint64_t unw64 = ((k<(lhs.m_value[j])); m_value[j] = unw64 / static_cast(rhs.m_value[0]); k = unw64 - (static_cast(m_value[j]) * static_cast(rhs.m_value[0])); - } - UINFO(9, " opmoddiv-1w "<> 32 won't mask the value for (int i = vw-1; i>0; i--) { - vn[i] = (rhs.m_value[i] << s) | (shift_mask & (rhs.m_value[i-1] >> (32-s))); + vn[i] = (rhs.m_value[i] << s) | (shift_mask & (rhs.m_value[i-1] >> (32-s))); } vn[0] = rhs.m_value[0] << s; @@ -1527,88 +1529,88 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool if (s) un[uw] = lhs.m_value[uw-1] >> (32-s); else un[uw] = 0; for (int i=uw-1; i>0; i--) { - un[i] = (lhs.m_value[i] << s) | (shift_mask & (lhs.m_value[i-1] >> (32-s))); + un[i] = (lhs.m_value[i] << s) | (shift_mask & (lhs.m_value[i-1] >> (32-s))); } un[0] = lhs.m_value[0] << s; - //printf(" un="); for(int i=5; i>=0; i--) printf(" %08x",un[i]); printf("\n"); - //printf(" vn="); for(int i=5; i>=0; i--) printf(" %08x",vn[i]); printf("\n"); - //printf(" mv="); for(int i=5; i>=0; i--) printf(" %08x",m_value[i]); printf("\n"); + //printf(" un="); for (int i=5; i>=0; i--) printf(" %08x",un[i]); printf("\n"); + //printf(" vn="); for (int i=5; i>=0; i--) printf(" %08x",vn[i]); printf("\n"); + //printf(" mv="); for (int i=5; i>=0; i--) printf(" %08x",m_value[i]); printf("\n"); // Main loop for (int j = uw - vw; j >= 0; j--) { - // Estimate + // Estimate vluint64_t unw64 = (static_cast(un[j+vw])<(un[j+vw-1])); vluint64_t qhat = unw64 / static_cast(vn[vw-1]); vluint64_t rhat = unw64 - qhat*static_cast(vn[vw-1]); again: - if (qhat >= VL_ULL(0x100000000) - || ((qhat*vn[vw-2]) > ((rhat<= VL_ULL(0x100000000) + || ((qhat*vn[vw-2]) > ((rhat<> VL_ULL(32)) - (t >> VL_ULL(32)); - } - t = un[j+vw] - k; - un[j+vw] = t; - this->m_value[j] = qhat; // Save quotient digit + vlsint64_t t = 0; // Must be signed + vluint64_t k = 0; + for (int i=0; i> VL_ULL(32)) - (t >> VL_ULL(32)); + } + t = un[j+vw] - k; + un[j+vw] = t; + this->m_value[j] = qhat; // Save quotient digit - if (t < 0) { - // Over subtracted; correct by adding back - this->m_value[j]--; - k = 0; - for (int i=0; im_value[j]--; + k = 0; + for (int i=0; i(un[i+j]) + static_cast(vn[i]) + k; - un[i+j] = t; - k = t >> VL_ULL(32); - } - un[j+vw] = un[j+vw] + k; - } + un[i+j] = t; + k = t >> VL_ULL(32); + } + un[j+vw] = un[j+vw] + k; + } } - //printf(" un="); for(int i=5; i>=0; i--) printf(" %08x",un[i]); printf("\n"); - //printf(" vn="); for(int i=5; i>=0; i--) printf(" %08x",vn[i]); printf("\n"); - //printf(" mv="); for(int i=5; i>=0; i--) printf(" %08x",m_value[i]); printf("\n"); + //printf(" un="); for (int i=5; i>=0; i--) printf(" %08x",un[i]); printf("\n"); + //printf(" vn="); for (int i=5; i>=0; i--) printf(" %08x",vn[i]); printf("\n"); + //printf(" mv="); for (int i=5; i>=0; i--) printf(" %08x",m_value[i]); printf("\n"); - if (is_modulus) { // modulus - // Need to reverse normalization on copy to output - for (int i=0; i> s) | (shift_mask & (un[i+1] << (32-s))); - } - for (int i=vw; iwidth(); bit++) { - if (ibit>=0 && ibitwidth(); bit++) { + if (ibit>=0 && ibit(msbval)) { setBit(bit, lhs.bitIs(ibit)); - } else { + } else { setBit(bit, 'x'); - } + } ++ibit; } //UINFO(0,"RANGE "<width(); bit++) { - if (if0s.bitIs1(bit) && if1s.bitIs1(bit)) { setBit(bit,1); } - else if (if0s.bitIs0(bit) && if1s.bitIs0(bit)) { setBit(bit,0); } - else setBit(bit,'x'); - } + else { // select is "X/Z" + setZero(); + for (int bit=0; bitwidth(); bit++) { + if (if0s.bitIs1(bit) && if1s.bitIs1(bit)) { setBit(bit, 1); } + else if (if0s.bitIs0(bit) && if1s.bitIs0(bit)) { setBit(bit, 0); } + else setBit(bit,'x'); + } } return *this; } @@ -1841,7 +1844,7 @@ V3Number& V3Number::opReplN(const V3Number& lhs, const V3Number& rhs) { V3Number& V3Number::opReplN(const V3Number& lhs, uint32_t rhsval) { string out; out.reserve(lhs.toString().length() * rhsval); for (unsigned times=0; times m_value; // The Value, with bit 0 being in bit 0 of this vector (unless X/Z) std::vector m_valueX; // Each bit is true if it's X or Z, 10=z, 11=x - string m_stringVal; // If isString, the value of the string + string m_stringVal; // If isString, the value of the string // METHODS V3Number& setSingleBits(char value); - V3Number& setString(const string& str) { m_isString=true; m_stringVal=str; return *this; } + V3Number& setString(const string& str) { m_isString = true; m_stringVal = str; return *this; } void opCleanThis(bool warnOnTruncation = false); public: void nodep(AstNode* nodep) { setNames(nodep); } @@ -58,71 +58,76 @@ public: V3Number& setLongS(vlsint32_t value); V3Number& setDouble(double value); void setBit(int bit, char value) { // Note must be pre-zeroed! - if (bit>=m_width) return; - uint32_t mask = (1UL<<(bit&31)); - if (value=='0' || value==0) { - m_value [bit/32] &= ~mask; - m_valueX[bit/32] &= ~mask; - } else if (value=='1'|| value==1) { - m_value [bit/32] |= mask; - m_valueX[bit/32] &= ~mask; - } else if (value=='z'|| value==2) { - m_value [bit/32] &= ~mask; - m_valueX[bit/32] |= mask; - } else { // X - m_value [bit/32] |= mask; - m_valueX[bit/32] |= mask; - } + if (bit>=m_width) return; + uint32_t mask = (1UL<<(bit&31)); + if (value=='0' || value==0) { + m_value [bit/32] &= ~mask; + m_valueX[bit/32] &= ~mask; + } else if (value=='1'|| value==1) { + m_value [bit/32] |= mask; + m_valueX[bit/32] &= ~mask; + } else if (value=='z'|| value==2) { + m_value [bit/32] &= ~mask; + m_valueX[bit/32] |= mask; + } else { // X + m_value [bit/32] |= mask; + m_valueX[bit/32] |= mask; + } } private: char bitIs(int bit) const { - if (bit>=m_width || bit<0) { - // We never sign extend - return '0'; - } - return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) - | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); } + if (bit>=m_width || bit<0) { + // We never sign extend + return '0'; + } + return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) + | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); } char bitIsExtend(int bit, int lbits) const { - // lbits usually = width, but for C optimizations width=32_bits, lbits = 32_or_less - if (bit<0) return '0'; - UASSERT(lbits<=m_width, "Extend of wrong size"); - if (bit>=lbits) { - bit = lbits ? lbits-1 : 0; - // We do sign extend - return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) - | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); - } - return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) - | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); } + // lbits usually = width, but for C optimizations width=32_bits, lbits = 32_or_less + if (bit<0) return '0'; + UASSERT(lbits<=m_width, "Extend of wrong size"); + if (bit>=lbits) { + bit = lbits ? lbits-1 : 0; + // We do sign extend + return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) + | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); + } + return ( "01zx"[(((m_value[bit/32] & (1UL<<(bit&31)))?1:0) + | ((m_valueX[bit/32] & (1UL<<(bit&31)))?2:0))] ); } bool bitIs0(int bit) const { - if (bit<0) return false; - if (bit>=m_width) return !bitIsXZ(m_width-1); - return ( (m_value[bit/32] & (1UL<<(bit&31)))==0 && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } + if (bit<0) return false; + if (bit>=m_width) return !bitIsXZ(m_width-1); + return ( (m_value[bit/32] & (1UL<<(bit&31)))==0 + && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } bool bitIs1(int bit) const { - if (bit<0) return false; - if (bit>=m_width) return false; - return ( (m_value[bit/32] & (1UL<<(bit&31))) && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } + if (bit<0) return false; + if (bit>=m_width) return false; + return ( (m_value[bit/32] & (1UL<<(bit&31))) + && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } bool bitIs1Extend(int bit) const { - if (bit<0) return false; - if (bit>=m_width) return bitIs1Extend(m_width-1); - return ( (m_value[bit/32] & (1UL<<(bit&31))) && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } + if (bit<0) return false; + if (bit>=m_width) return bitIs1Extend(m_width-1); + return ( (m_value[bit/32] & (1UL<<(bit&31))) + && !(m_valueX[bit/32] & (1UL<<(bit&31))) ); } bool bitIsX(int bit) const { - if (bit<0) return false; - if (bit>=m_width) return bitIsZ(m_width-1); - return ( (m_value[bit/32] & (1UL<<(bit&31))) && (m_valueX[bit/32] & (1UL<<(bit&31))) ); } + if (bit<0) return false; + if (bit>=m_width) return bitIsZ(m_width-1); + return ( (m_value[bit/32] & (1UL<<(bit&31))) + && (m_valueX[bit/32] & (1UL<<(bit&31))) ); } bool bitIsXZ(int bit) const { - if (bit<0) return false; - if (bit>=m_width) return bitIsXZ(m_width-1); - return ( (m_valueX[bit/32] & (1UL<<(bit&31)))); + if (bit<0) return false; + if (bit>=m_width) return bitIsXZ(m_width-1); + return ( (m_valueX[bit/32] & (1UL<<(bit&31)))); } bool bitIsZ(int bit) const { - if (bit<0) return false; - if (bit>=m_width) return bitIsZ(m_width-1); - return ( (~m_value[bit/32] & (1UL<<(bit&31))) && (m_valueX[bit/32] & (1UL<<(bit&31))) ); } + if (bit<0) return false; + if (bit>=m_width) return bitIsZ(m_width-1); + return ( (~m_value[bit/32] & (1UL<<(bit&31))) + && (m_valueX[bit/32] & (1UL<<(bit&31))) ); } uint32_t bitsValue(int lsb, int nbits) const { - uint32_t v=0; - for (int bitn=0; bitnfileline(); } @@ -160,13 +165,13 @@ private: void V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl); void init(AstNode* nodep, int swidth) { setNames(nodep); - m_signed = false; - m_double = false; - m_isString = false; - m_autoExtend = false; - m_fromString = false; - width(swidth); - for (int i=0; i> (8*(byte&3))) & 0xff; } uint32_t countOnes() const; - uint32_t mostSetBitP1() const; // Highest bit set plus one, IE for 16 return 5, for 0 return 0. + uint32_t mostSetBitP1() const; // Highest bit set plus one, IE for 16 return 5, for 0 return 0. // Operators bool operator<(const V3Number& rhs) const { return isLtXZ(rhs); } @@ -247,115 +261,115 @@ public: // MATH // "this" is the output, as we need the output width before some computations - V3Number& isTrue (const V3Number& lhs); - V3Number& opBitsNonX(const V3Number& lhs); // 0/1->1, X/Z->0 - V3Number& opBitsOne (const V3Number& lhs); // 1->1, 0/X/Z->0 - V3Number& opBitsXZ (const V3Number& lhs); // 0/1->0, X/Z->1 - V3Number& opBitsZ (const V3Number& lhs); // Z->1, 0/1/X->0 - V3Number& opBitsNonZ(const V3Number& lhs); // Z->0, 0/1/X->1 + V3Number& isTrue (const V3Number& lhs); + V3Number& opBitsNonX(const V3Number& lhs); // 0/1->1, X/Z->0 + V3Number& opBitsOne (const V3Number& lhs); // 1->1, 0/X/Z->0 + V3Number& opBitsXZ (const V3Number& lhs); // 0/1->0, X/Z->1 + V3Number& opBitsZ (const V3Number& lhs); // Z->1, 0/1/X->0 + V3Number& opBitsNonZ(const V3Number& lhs); // Z->0, 0/1/X->1 // V3Number& opAssign (const V3Number& lhs); V3Number& opExtendS (const V3Number& lhs, uint32_t lbits); // Sign extension V3Number& opExtendXZ(const V3Number& lhs, uint32_t lbits); // X/Z extension - V3Number& opRedOr (const V3Number& lhs); - V3Number& opRedAnd (const V3Number& lhs); - V3Number& opRedXor (const V3Number& lhs); - V3Number& opRedXnor (const V3Number& lhs); + V3Number& opRedOr (const V3Number& lhs); + V3Number& opRedAnd (const V3Number& lhs); + V3Number& opRedXor (const V3Number& lhs); + V3Number& opRedXnor (const V3Number& lhs); V3Number& opCountOnes(const V3Number& lhs); V3Number& opIsUnknown(const V3Number& lhs); - V3Number& opOneHot (const V3Number& lhs); - V3Number& opOneHot0 (const V3Number& lhs); - V3Number& opCLog2 (const V3Number& lhs); - V3Number& opClean (const V3Number& lhs, uint32_t bits); - V3Number& opConcat (const V3Number& lhs, const V3Number& rhs); - V3Number& opLenN (const V3Number& lhs); - V3Number& opRepl (const V3Number& lhs, const V3Number& rhs); + V3Number& opOneHot (const V3Number& lhs); + V3Number& opOneHot0 (const V3Number& lhs); + V3Number& opCLog2 (const V3Number& lhs); + V3Number& opClean (const V3Number& lhs, uint32_t bits); + V3Number& opConcat (const V3Number& lhs, const V3Number& rhs); + V3Number& opLenN (const V3Number& lhs); + V3Number& opRepl (const V3Number& lhs, const V3Number& rhs); V3Number& opRepl (const V3Number& lhs, uint32_t rhsval); - V3Number& opStreamL (const V3Number& lhs, const V3Number& rhs); + V3Number& opStreamL (const V3Number& lhs, const V3Number& rhs); V3Number& opSel (const V3Number& lhs, const V3Number& msb, const V3Number& lsb); V3Number& opSel (const V3Number& lhs, uint32_t msbval, uint32_t lsbval); - V3Number& opSelInto (const V3Number& lhs, const V3Number& lsb, int width); + V3Number& opSelInto (const V3Number& lhs, const V3Number& lsb, int width); V3Number& opSelInto (const V3Number& lhs, int lsbval, int width); V3Number& opCond (const V3Number& lhs, const V3Number& if1s, const V3Number& if0s); - V3Number& opCaseEq (const V3Number& lhs, const V3Number& rhs); - V3Number& opCaseNeq (const V3Number& lhs, const V3Number& rhs); - V3Number& opWildEq (const V3Number& lhs, const V3Number& rhs); - V3Number& opWildNeq (const V3Number& lhs, const V3Number& rhs); + V3Number& opCaseEq (const V3Number& lhs, const V3Number& rhs); + V3Number& opCaseNeq (const V3Number& lhs, const V3Number& rhs); + V3Number& opWildEq (const V3Number& lhs, const V3Number& rhs); + V3Number& opWildNeq (const V3Number& lhs, const V3Number& rhs); V3Number& opBufIf1 (const V3Number& ens, const V3Number& if1s); // "standard" math - V3Number& opNot (const V3Number& lhs); - V3Number& opLogNot (const V3Number& lhs); - V3Number& opLogAnd (const V3Number& lhs, const V3Number& rhs); - V3Number& opLogOr (const V3Number& lhs, const V3Number& rhs); - V3Number& opLogIf (const V3Number& lhs, const V3Number& rhs); - V3Number& opLogIff (const V3Number& lhs, const V3Number& rhs); + V3Number& opNot (const V3Number& lhs); + V3Number& opLogNot (const V3Number& lhs); + V3Number& opLogAnd (const V3Number& lhs, const V3Number& rhs); + V3Number& opLogOr (const V3Number& lhs, const V3Number& rhs); + V3Number& opLogIf (const V3Number& lhs, const V3Number& rhs); + V3Number& opLogIff (const V3Number& lhs, const V3Number& rhs); V3Number& opAbsS (const V3Number& lhs); - V3Number& opNegate (const V3Number& lhs); - V3Number& opAdd (const V3Number& lhs, const V3Number& rhs); - V3Number& opSub (const V3Number& lhs, const V3Number& rhs); - V3Number& opMul (const V3Number& lhs, const V3Number& rhs); - V3Number& opMulS (const V3Number& lhs, const V3Number& rhs); // Signed - V3Number& opDiv (const V3Number& lhs, const V3Number& rhs); - V3Number& opDivS (const V3Number& lhs, const V3Number& rhs); // Signed - V3Number& opModDiv (const V3Number& lhs, const V3Number& rhs); - V3Number& opModDivS (const V3Number& lhs, const V3Number& rhs); // Signed - V3Number& opPow (const V3Number& lhs, const V3Number& rhs, bool lsign=false, bool rsign=false); - V3Number& opPowSU (const V3Number& lhs, const V3Number& rhs); // Signed lhs, unsigned rhs - V3Number& opPowSS (const V3Number& lhs, const V3Number& rhs); // Signed lhs, signed rhs - V3Number& opPowUS (const V3Number& lhs, const V3Number& rhs); // Unsigned lhs, signed rhs - V3Number& opAnd (const V3Number& lhs, const V3Number& rhs); + V3Number& opNegate (const V3Number& lhs); + V3Number& opAdd (const V3Number& lhs, const V3Number& rhs); + V3Number& opSub (const V3Number& lhs, const V3Number& rhs); + V3Number& opMul (const V3Number& lhs, const V3Number& rhs); + V3Number& opMulS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opDiv (const V3Number& lhs, const V3Number& rhs); + V3Number& opDivS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opModDiv (const V3Number& lhs, const V3Number& rhs); + V3Number& opModDivS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opPow (const V3Number& lhs, const V3Number& rhs, bool lsign=false, bool rsign=false); + V3Number& opPowSU (const V3Number& lhs, const V3Number& rhs); // Signed lhs, unsigned rhs + V3Number& opPowSS (const V3Number& lhs, const V3Number& rhs); // Signed lhs, signed rhs + V3Number& opPowUS (const V3Number& lhs, const V3Number& rhs); // Unsigned lhs, signed rhs + V3Number& opAnd (const V3Number& lhs, const V3Number& rhs); V3Number& opChangeXor(const V3Number& lhs, const V3Number& rhs); - V3Number& opXor (const V3Number& lhs, const V3Number& rhs); - V3Number& opXnor (const V3Number& lhs, const V3Number& rhs); - V3Number& opOr (const V3Number& lhs, const V3Number& rhs); - V3Number& opRotR (const V3Number& lhs, const V3Number& rhs); - V3Number& opRotL (const V3Number& lhs, const V3Number& rhs); - V3Number& opShiftR (const V3Number& lhs, const V3Number& rhs); - V3Number& opShiftRS (const V3Number& lhs, const V3Number& rhs, uint32_t lbits); // Arithmetic w/carry - V3Number& opShiftL (const V3Number& lhs, const V3Number& rhs); + V3Number& opXor (const V3Number& lhs, const V3Number& rhs); + V3Number& opXnor (const V3Number& lhs, const V3Number& rhs); + V3Number& opOr (const V3Number& lhs, const V3Number& rhs); + V3Number& opRotR (const V3Number& lhs, const V3Number& rhs); + V3Number& opRotL (const V3Number& lhs, const V3Number& rhs); + V3Number& opShiftR (const V3Number& lhs, const V3Number& rhs); + V3Number& opShiftRS (const V3Number& lhs, const V3Number& rhs, uint32_t lbits); // Arithmetic w/carry + V3Number& opShiftL (const V3Number& lhs, const V3Number& rhs); // Comparisons - V3Number& opEq (const V3Number& lhs, const V3Number& rhs); - V3Number& opNeq (const V3Number& lhs, const V3Number& rhs); - V3Number& opGt (const V3Number& lhs, const V3Number& rhs); - V3Number& opGtS (const V3Number& lhs, const V3Number& rhs); // Signed - V3Number& opGte (const V3Number& lhs, const V3Number& rhs); - V3Number& opGteS (const V3Number& lhs, const V3Number& rhs); // Signed - V3Number& opLt (const V3Number& lhs, const V3Number& rhs); - V3Number& opLtS (const V3Number& lhs, const V3Number& rhs); // Signed - V3Number& opLte (const V3Number& lhs, const V3Number& rhs); - V3Number& opLteS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opEq (const V3Number& lhs, const V3Number& rhs); + V3Number& opNeq (const V3Number& lhs, const V3Number& rhs); + V3Number& opGt (const V3Number& lhs, const V3Number& rhs); + V3Number& opGtS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opGte (const V3Number& lhs, const V3Number& rhs); + V3Number& opGteS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opLt (const V3Number& lhs, const V3Number& rhs); + V3Number& opLtS (const V3Number& lhs, const V3Number& rhs); // Signed + V3Number& opLte (const V3Number& lhs, const V3Number& rhs); + V3Number& opLteS (const V3Number& lhs, const V3Number& rhs); // Signed // "D" - double (aka real) math - V3Number& opIToRD (const V3Number& lhs); - V3Number& opRToIS (const V3Number& lhs); - V3Number& opRToIRoundS (const V3Number& lhs); - V3Number& opRealToBits (const V3Number& lhs); + V3Number& opIToRD (const V3Number& lhs); + V3Number& opRToIS (const V3Number& lhs); + V3Number& opRToIRoundS(const V3Number& lhs); + V3Number& opRealToBits(const V3Number& lhs); V3Number& opBitsToRealD(const V3Number& lhs); - V3Number& opNegateD (const V3Number& lhs); - V3Number& opAddD (const V3Number& lhs, const V3Number& rhs); - V3Number& opSubD (const V3Number& lhs, const V3Number& rhs); - V3Number& opMulD (const V3Number& lhs, const V3Number& rhs); - V3Number& opDivD (const V3Number& lhs, const V3Number& rhs); - V3Number& opPowD (const V3Number& lhs, const V3Number& rhs); + V3Number& opNegateD (const V3Number& lhs); + V3Number& opAddD (const V3Number& lhs, const V3Number& rhs); + V3Number& opSubD (const V3Number& lhs, const V3Number& rhs); + V3Number& opMulD (const V3Number& lhs, const V3Number& rhs); + V3Number& opDivD (const V3Number& lhs, const V3Number& rhs); + V3Number& opPowD (const V3Number& lhs, const V3Number& rhs); // Comparisons - V3Number& opEqD (const V3Number& lhs, const V3Number& rhs); - V3Number& opNeqD (const V3Number& lhs, const V3Number& rhs); - V3Number& opGtD (const V3Number& lhs, const V3Number& rhs); - V3Number& opGteD (const V3Number& lhs, const V3Number& rhs); - V3Number& opLtD (const V3Number& lhs, const V3Number& rhs); - V3Number& opLteD (const V3Number& lhs, const V3Number& rhs); + V3Number& opEqD (const V3Number& lhs, const V3Number& rhs); + V3Number& opNeqD (const V3Number& lhs, const V3Number& rhs); + V3Number& opGtD (const V3Number& lhs, const V3Number& rhs); + V3Number& opGteD (const V3Number& lhs, const V3Number& rhs); + V3Number& opLtD (const V3Number& lhs, const V3Number& rhs); + V3Number& opLteD (const V3Number& lhs, const V3Number& rhs); // "N" - string operations - V3Number& opConcatN (const V3Number& lhs, const V3Number& rhs); - V3Number& opReplN (const V3Number& lhs, const V3Number& rhs); + V3Number& opConcatN (const V3Number& lhs, const V3Number& rhs); + V3Number& opReplN (const V3Number& lhs, const V3Number& rhs); V3Number& opReplN (const V3Number& lhs, uint32_t rhsval); - V3Number& opEqN (const V3Number& lhs, const V3Number& rhs); - V3Number& opNeqN (const V3Number& lhs, const V3Number& rhs); - V3Number& opGtN (const V3Number& lhs, const V3Number& rhs); - V3Number& opGteN (const V3Number& lhs, const V3Number& rhs); - V3Number& opLtN (const V3Number& lhs, const V3Number& rhs); - V3Number& opLteN (const V3Number& lhs, const V3Number& rhs); + V3Number& opEqN (const V3Number& lhs, const V3Number& rhs); + V3Number& opNeqN (const V3Number& lhs, const V3Number& rhs); + V3Number& opGtN (const V3Number& lhs, const V3Number& rhs); + V3Number& opGteN (const V3Number& lhs, const V3Number& rhs); + V3Number& opLtN (const V3Number& lhs, const V3Number& rhs); + V3Number& opLteN (const V3Number& lhs, const V3Number& rhs); }; inline std::ostream& operator<<(std::ostream& os, const V3Number& rhs) { return os<") gotnum.opGt (lhnum,rhnum); - else if (op==">>") gotnum.opShiftR (lhnum,rhnum); - else if (op=="<<") gotnum.opShiftL (lhnum,rhnum); - else if (op=="==") gotnum.opEq (lhnum,rhnum); - else if (op=="===") gotnum.opCaseEq (lhnum,rhnum); - else if (op=="==?") gotnum.opWildEq (lhnum,rhnum); - else if (op=="!=") gotnum.opNeq (lhnum,rhnum); - else if (op=="!==") gotnum.opCaseNeq (lhnum,rhnum); - else if (op=="!=?") gotnum.opWildNeq (lhnum,rhnum); - else if (op=="<=") gotnum.opLte (lhnum,rhnum); - else if (op==">=") gotnum.opGte (lhnum,rhnum); - else if (op=="&&") gotnum.opLogAnd (lhnum,rhnum); - else if (op=="||") gotnum.opLogOr (lhnum,rhnum); + if (op=="redOr") gotnum.opRedOr (lhnum); + else if (op=="redAnd") gotnum.opRedAnd (lhnum); + else if (op=="redXor") gotnum.opRedXor (lhnum); + else if (op=="redXnor") gotnum.opRedXnor (lhnum); + else if (op=="concat") gotnum.opConcat (lhnum, rhnum); + else if (op=="repl") gotnum.opRepl (lhnum, rhnum); + else if (op=="~") gotnum.opNot (lhnum); + else if (op=="!") gotnum.opLogNot (lhnum); + else if (op=="negate") gotnum.opNegate (lhnum); + else if (op=="+") gotnum.opAdd (lhnum, rhnum); + else if (op=="-") gotnum.opSub (lhnum, rhnum); + else if (op=="*") gotnum.opMul (lhnum, rhnum); + else if (op=="/") gotnum.opDiv (lhnum, rhnum); + else if (op=="%") gotnum.opModDiv (lhnum, rhnum); + else if (op=="&") gotnum.opAnd (lhnum, rhnum); + else if (op=="|") gotnum.opOr (lhnum, rhnum); + else if (op=="<") gotnum.opLt (lhnum, rhnum); + else if (op==">") gotnum.opGt (lhnum, rhnum); + else if (op==">>") gotnum.opShiftR (lhnum, rhnum); + else if (op=="<<") gotnum.opShiftL (lhnum, rhnum); + else if (op=="==") gotnum.opEq (lhnum, rhnum); + else if (op=="===") gotnum.opCaseEq (lhnum, rhnum); + else if (op=="==?") gotnum.opWildEq (lhnum, rhnum); + else if (op=="!=") gotnum.opNeq (lhnum, rhnum); + else if (op=="!==") gotnum.opCaseNeq (lhnum, rhnum); + else if (op=="!=?") gotnum.opWildNeq (lhnum, rhnum); + else if (op=="<=") gotnum.opLte (lhnum, rhnum); + else if (op==">=") gotnum.opGte (lhnum, rhnum); + else if (op=="&&") gotnum.opLogAnd (lhnum, rhnum); + else if (op=="||") gotnum.opLogOr (lhnum, rhnum); else v3fatalSrc("Bad opcode: "< m_langExts; // Language extension map std::list m_libExtVs; // Library extensions (ordered) std::set m_libExtVSet; // Library extensions (for removing duplicates) - DirMap m_dirMap; // Directory listing + DirMap m_dirMap; // Directory listing // ACCESSOR METHODS void addIncDirUser(const string& incdir) { - if (m_incDirUserSet.find(incdir) == m_incDirUserSet.end()) { - m_incDirUserSet.insert(incdir); - m_incDirUsers.push_back(incdir); - m_incDirFallbacks.remove(incdir); // User has priority over Fallback - m_incDirFallbackSet.erase(incdir); // User has priority over Fallback - } + if (m_incDirUserSet.find(incdir) == m_incDirUserSet.end()) { + m_incDirUserSet.insert(incdir); + m_incDirUsers.push_back(incdir); + m_incDirFallbacks.remove(incdir); // User has priority over Fallback + m_incDirFallbackSet.erase(incdir); // User has priority over Fallback + } } void addIncDirFallback(const string& incdir) { - if (m_incDirUserSet.find(incdir) == m_incDirUserSet.end()) { // User has priority over Fallback - if (m_incDirFallbackSet.find(incdir) == m_incDirFallbackSet.end()) { - m_incDirFallbackSet.insert(incdir); - m_incDirFallbacks.push_back(incdir); - } - } + if (m_incDirUserSet.find(incdir) == m_incDirUserSet.end()) { // User has priority over Fallback + if (m_incDirFallbackSet.find(incdir) == m_incDirFallbackSet.end()) { + m_incDirFallbackSet.insert(incdir); + m_incDirFallbacks.push_back(incdir); + } + } } void addLangExt(const string& langext, const V3LangCode& lc) { - // New language extension replaces any pre-existing one. - (void)m_langExts.erase(langext); - m_langExts[langext] = lc; + // New language extension replaces any pre-existing one. + (void)m_langExts.erase(langext); + m_langExts[langext] = lc; } void addLibExtV(const string& libext) { - if (m_libExtVSet.find(libext) == m_libExtVSet.end()) { - m_libExtVSet.insert(libext); - m_libExtVs.push_back(libext); - } + if (m_libExtVSet.find(libext) == m_libExtVSet.end()) { + m_libExtVSet.insert(libext); + m_libExtVs.push_back(libext); + } } V3OptionsImp() {} ~V3OptionsImp() {} @@ -114,20 +114,20 @@ void V3Options::addDefine(const string& defline, bool allowPlus) { // + is not quotable, as other simulators do not allow that string left = defline; while (left != "") { - string def = left; - string::size_type pos; + string def = left; + string::size_type pos; if (allowPlus && ((pos = left.find('+')) != string::npos)) { - left = left.substr(pos+1); - def.erase(pos); - } else { - left = ""; - } - string value; + left = left.substr(pos+1); + def.erase(pos); + } else { + left = ""; + } + string value; if ((pos = def.find('=')) != string::npos) { - value = def.substr(pos+1); - def.erase(pos); - } - V3PreShell::defineCmdLine(def,value); + value = def.substr(pos+1); + def.erase(pos); + } + V3PreShell::defineCmdLine(def, value); } } void V3Options::addParameter(const string& paramline, bool allowPlus) { @@ -179,7 +179,7 @@ void V3Options::checkParameters() { void V3Options::addCppFile(const string& filename) { if (m_cppFiles.find(filename) == m_cppFiles.end()) { - m_cppFiles.insert(filename); + m_cppFiles.insert(filename); } } void V3Options::addCFlags(const string& filename) { @@ -190,7 +190,7 @@ void V3Options::addLdLibs(const string& filename) { } void V3Options::addFuture(const string& flag) { if (m_futures.find(flag) == m_futures.end()) { - m_futures.insert(flag); + m_futures.insert(flag); } } bool V3Options::isFuture(const string& flag) const { @@ -201,7 +201,7 @@ bool V3Options::isLibraryFile(const string& filename) const { } void V3Options::addLibraryFile(const string& filename) { if (m_libraryFiles.find(filename) == m_libraryFiles.end()) { - m_libraryFiles.insert(filename); + m_libraryFiles.insert(filename); } } bool V3Options::isClocker(const string& signame) const { @@ -209,7 +209,7 @@ bool V3Options::isClocker(const string& signame) const { } void V3Options::addClocker(const string& signame) { if (m_clockers.find(signame) == m_clockers.end()) { - m_clockers.insert(signame); + m_clockers.insert(signame); } } bool V3Options::isNoClocker(const string& signame) const { @@ -217,7 +217,7 @@ bool V3Options::isNoClocker(const string& signame) const { } void V3Options::addNoClocker(const string& signame) { if (m_noClockers.find(signame) == m_noClockers.end()) { - m_noClockers.insert(signame); + m_noClockers.insert(signame); } } void V3Options::addVFile(const string& filename) { @@ -235,9 +235,10 @@ void V3Options::addArg(const string& arg) { string V3Options::allArgsString() { string out; - for (std::list::iterator it=m_impp->m_allArgs.begin(); it!=m_impp->m_allArgs.end(); ++it) { - if (out != "") out += " "; - out += *it; + for (std::list::iterator it=m_impp->m_allArgs.begin(); + it != m_impp->m_allArgs.end(); ++it) { + if (out != "") out += " "; + out += *it; } return out; } @@ -249,9 +250,9 @@ V3LangCode::V3LangCode(const char* textp) { // Return code for given string, or ERROR, which is a bad code for (int codei=V3LangCode::L_ERROR; codei0) ::close(fd); + if (fd>0) ::close(fd); } } @@ -298,41 +299,42 @@ string V3Options::fileExists(const string& filename) { V3OptionsImp::DirMap::iterator diriter = m_impp->m_dirMap.find(dir); if (diriter == m_impp->m_dirMap.end()) { - // Read the listing + // Read the listing m_impp->m_dirMap.insert(std::make_pair(dir, std::set() )); - diriter = m_impp->m_dirMap.find(dir); + diriter = m_impp->m_dirMap.find(dir); std::set* setp = &(diriter->second); - if (DIR* dirp = opendir(dir.c_str())) { - while (struct dirent* direntp = readdir(dirp)) { + if (DIR* dirp = opendir(dir.c_str())) { + while (struct dirent* direntp = readdir(dirp)) { - setp->insert(direntp->d_name); - } - closedir(dirp); - } + setp->insert(direntp->d_name); + } + closedir(dirp); + } } // Find it std::set* filesetp = &(diriter->second); std::set::iterator fileiter = filesetp->find(basename); if (fileiter == filesetp->end()) { - return ""; // Not found + return ""; // Not found } // Check if it is a directory, ignore if so string filenameOut = V3Os::filenameFromDirBase(dir, basename); - if (!fileStatNormal(filenameOut)) return ""; // Directory + if (!fileStatNormal(filenameOut)) return ""; // Directory return filenameOut; } string V3Options::filePathCheckOneDir(const string& modname, const string& dirname) { - for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) { - string fn = V3Os::filenameFromDirBase(dirname, modname+*extIter); - string exists = fileExists(fn); - if (exists!="") { - // Strip ./, it just looks ugly - if (exists.substr(0,2)=="./") exists.erase(0,2); - return exists; - } + for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); + extIter != m_impp->m_libExtVs.end(); ++extIter) { + string fn = V3Os::filenameFromDirBase(dirname, modname+*extIter); + string exists = fileExists(fn); + if (exists!="") { + // Strip ./, it just looks ugly + if (exists.substr(0, 2)=="./") exists.erase(0, 2); + return exists; + } } return ""; } @@ -343,25 +345,25 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la // using the incdir and libext's. // Return "" if not found. for (std::list::iterator dirIter=m_impp->m_incDirUsers.begin(); - dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) { - string exists = filePathCheckOneDir(modname, *dirIter); - if (exists!="") return exists; + dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) { + string exists = filePathCheckOneDir(modname, *dirIter); + if (exists!="") return exists; } for (std::list::iterator dirIter=m_impp->m_incDirFallbacks.begin(); - dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) { - string exists = filePathCheckOneDir(modname, *dirIter); - if (exists!="") return exists; + dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) { + string exists = filePathCheckOneDir(modname, *dirIter); + if (exists!="") return exists; } if (m_relativeIncludes) { - string exists = filePathCheckOneDir(modname, lastpath); - if (exists!="") return V3Os::filenameRealPath(exists); + string exists = filePathCheckOneDir(modname, lastpath); + if (exists!="") return V3Os::filenameRealPath(exists); } // Warn and return not found if (errmsg != "") { - fl->v3error(errmsg+modname); - filePathLookedMsg(fl, modname); + fl->v3error(errmsg+modname); + filePathLookedMsg(fl, modname); } return ""; } @@ -369,25 +371,27 @@ string V3Options::filePath(FileLine* fl, const string& modname, const string& la void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) { static bool shown_notfound_msg = false; if (!shown_notfound_msg) { - shown_notfound_msg = true; - if (m_impp->m_incDirUsers.empty()) { - fl->v3error("This may be because there's no search path specified with -I."<v3error("Looked in:"<m_incDirUsers.empty()) { + fl->v3error("This may be because there's no search path specified with -I."<v3error("Looked in:"<::iterator dirIter=m_impp->m_incDirUsers.begin(); - dirIter!=m_impp->m_incDirUsers.end(); ++dirIter) { - for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) { - string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter); - fl->v3error(" "<m_incDirUsers.end(); ++dirIter) { + for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); + extIter != m_impp->m_libExtVs.end(); ++extIter) { + string fn = V3Os::filenameFromDirBase(*dirIter, modname+*extIter); + fl->v3error(" "<::iterator dirIter=m_impp->m_incDirFallbacks.begin(); - dirIter!=m_impp->m_incDirFallbacks.end(); ++dirIter) { - for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); extIter!=m_impp->m_libExtVs.end(); ++extIter) { - string fn = V3Os::filenameFromDirBase(*dirIter,modname+*extIter); - fl->v3error(" "<m_incDirFallbacks.end(); ++dirIter) { + for (std::list::iterator extIter=m_impp->m_libExtVs.begin(); + extIter != m_impp->m_libExtVs.end(); ++extIter) { + string fn = V3Os::filenameFromDirBase(*dirIter, modname+*extIter); + fl->v3error(" "<::iterator it = m_impp->m_langExts.find(ext); - if (it != m_impp->m_langExts.end()) { - return it->second; - } + if (it != m_impp->m_langExts.end()) { + return it->second; + } } return m_defaultLanguage; } @@ -420,28 +424,28 @@ string V3Options::getenvBuiltins(const string& var) { else if (var == "SYSTEMC_LIBDIR") return getenvSYSTEMC_LIBDIR(); else if (var == "VERILATOR_ROOT") return getenvVERILATOR_ROOT(); else { - return V3Os::getenvStr(var,""); + return V3Os::getenvStr(var, ""); } } string V3Options::getenvPERL() { - return V3Os::getenvStr("PERL","perl"); + return V3Os::getenvStr("PERL", "perl"); } string V3Options::getenvSYSTEMC() { - string var = V3Os::getenvStr("SYSTEMC",""); + string var = V3Os::getenvStr("SYSTEMC", ""); if (var == "" && string(DEFENV_SYSTEMC) != "") { - var = DEFENV_SYSTEMC; - V3Os::setenvStr("SYSTEMC", var, "Hardcoded at build time"); + var = DEFENV_SYSTEMC; + V3Os::setenvStr("SYSTEMC", var, "Hardcoded at build time"); } return var; } string V3Options::getenvSYSTEMC_ARCH() { - string var = V3Os::getenvStr("SYSTEMC_ARCH",""); + string var = V3Os::getenvStr("SYSTEMC_ARCH", ""); if (var == "" && string(DEFENV_SYSTEMC_ARCH) != "") { - var = DEFENV_SYSTEMC_ARCH; - V3Os::setenvStr("SYSTEMC_ARCH", var, "Hardcoded at build time"); + var = DEFENV_SYSTEMC_ARCH; + V3Os::setenvStr("SYSTEMC_ARCH", var, "Hardcoded at build time"); } if (var == "") { #if defined (__MINGW32__) @@ -453,67 +457,67 @@ string V3Options::getenvSYSTEMC_ARCH() { var = "win32"; #else // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) - struct utsname uts; - uname(&uts); - string sysname = VString::downcase(uts.sysname); // aka 'uname -s' - if (VString::wildmatch(sysname.c_str(), "*solaris*")) { var = "gccsparcOS5"; } - else if (VString::wildmatch(sysname.c_str(), "*cygwin*")) { var ="cygwin"; } - else { var = "linux"; } + struct utsname uts; + uname(&uts); + string sysname = VString::downcase(uts.sysname); // aka 'uname -s' + if (VString::wildmatch(sysname.c_str(), "*solaris*")) { var = "gccsparcOS5"; } + else if (VString::wildmatch(sysname.c_str(), "*cygwin*")) { var = "cygwin"; } + else { var = "linux"; } #endif - V3Os::setenvStr("SYSTEMC_ARCH", var,"From sysname '"+sysname+"'"); + V3Os::setenvStr("SYSTEMC_ARCH", var, "From sysname '"+sysname+"'"); } return var; } string V3Options::getenvSYSTEMC_INCLUDE() { - string var = V3Os::getenvStr("SYSTEMC_INCLUDE",""); + string var = V3Os::getenvStr("SYSTEMC_INCLUDE", ""); if (var == "" && string(DEFENV_SYSTEMC_INCLUDE) != "") { - var = DEFENV_SYSTEMC_INCLUDE; - V3Os::setenvStr("SYSTEMC_INCLUDE", var, "Hardcoded at build time"); + var = DEFENV_SYSTEMC_INCLUDE; + V3Os::setenvStr("SYSTEMC_INCLUDE", var, "Hardcoded at build time"); } if (var == "") { - string sc = getenvSYSTEMC(); - if (sc != "") var = sc+"/include"; + string sc = getenvSYSTEMC(); + if (sc != "") var = sc+"/include"; } // Only correct or check it if we really need the value if (v3Global.opt.usingSystemCLibs()) { - if (var == "") { - v3fatal("Need $SYSTEMC_INCLUDE in environment or when Verilator configured\n" - "Probably System-C isn't installed, see http://www.systemc.org\n"); - } + if (var == "") { + v3fatal("Need $SYSTEMC_INCLUDE in environment or when Verilator configured\n" + "Probably System-C isn't installed, see http://www.systemc.org\n"); + } } return var; } string V3Options::getenvSYSTEMC_LIBDIR() { - string var = V3Os::getenvStr("SYSTEMC_LIBDIR",""); + string var = V3Os::getenvStr("SYSTEMC_LIBDIR", ""); if (var == "" && string(DEFENV_SYSTEMC_LIBDIR) != "") { - var = DEFENV_SYSTEMC_LIBDIR; - V3Os::setenvStr("SYSTEMC_LIBDIR", var, "Hardcoded at build time"); + var = DEFENV_SYSTEMC_LIBDIR; + V3Os::setenvStr("SYSTEMC_LIBDIR", var, "Hardcoded at build time"); } if (var == "") { - string sc = getenvSYSTEMC(); - string arch = getenvSYSTEMC_ARCH(); - if (sc != "" && arch != "") var = sc+"/lib-"+arch; + string sc = getenvSYSTEMC(); + string arch = getenvSYSTEMC_ARCH(); + if (sc != "" && arch != "") var = sc+"/lib-"+arch; } // Only correct or check it if we really need the value if (v3Global.opt.usingSystemCLibs()) { - if (var == "") { - v3fatal("Need $SYSTEMC_LIBDIR in environment or when Verilator configured\n" - "Probably System-C isn't installed, see http://www.systemc.org\n"); - } + if (var == "") { + v3fatal("Need $SYSTEMC_LIBDIR in environment or when Verilator configured\n" + "Probably System-C isn't installed, see http://www.systemc.org\n"); + } } return var; } string V3Options::getenvVERILATOR_ROOT() { - string var = V3Os::getenvStr("VERILATOR_ROOT",""); + string var = V3Os::getenvStr("VERILATOR_ROOT", ""); if (var == "" && string(DEFENV_VERILATOR_ROOT) != "") { - var = DEFENV_VERILATOR_ROOT; - V3Os::setenvStr("VERILATOR_ROOT", var, "Hardcoded at build time"); + var = DEFENV_VERILATOR_ROOT; + V3Os::setenvStr("VERILATOR_ROOT", var, "Hardcoded at build time"); } if (var == "") { - v3fatal("$VERILATOR_ROOT needs to be in environment\n"); + v3fatal("$VERILATOR_ROOT needs to be in environment\n"); } return var; } @@ -529,7 +533,7 @@ string V3Options::version() { void V3Options::throwSigsegv() { #if !(defined(VL_CPPCHECK) || defined(__clang_analyzer__)) - char* zp=NULL; *zp=0; // Intentional core dump, ignore warnings here + char* zp=NULL; *zp=0; // Intentional core dump, ignore warnings here #endif } @@ -540,8 +544,8 @@ string V3Options::argString(int argc, char** argv) { // Return list of arguments as simple string string opts; for (int i=0; i=1) m_prefix = string("V")+V3Os::filenameNonExt(*(vFilesList.begin())); + if (prefix()=="" && vFilesList.size()>=1) { + m_prefix = string("V")+V3Os::filenameNonExt(*(vFilesList.begin())); + } if (modPrefix()=="") m_modPrefix = prefix(); // Find files in makedir @@ -578,9 +584,9 @@ bool V3Options::onoff(const char* sw, const char* arg, bool& flag) { // if sw=="-noarg", then return true (found it), and flag=false // else return false if (arg[0]!='-') v3fatalSrc("OnOff switches must have leading dash"); - if (0==strcmp(sw,arg)) { flag=true; return true; } - else if (0==strncmp(sw,"-no",3) && (0==strcmp(sw+3,arg+1))) { flag=false; return true; } - else if (0==strncmp(sw,"-no-",4) && (0==strcmp(sw+4,arg+1))) { flag=false; return true; } + if (0==strcmp(sw, arg)) { flag = true; return true; } + else if (0==strncmp(sw, "-no", 3) && (0==strcmp(sw+3, arg+1))) { flag = false; return true; } + else if (0==strncmp(sw, "-no-", 4) && (0==strcmp(sw+4, arg+1))) { flag = false; return true; } return false; } @@ -594,281 +600,281 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char // Note argc and argv DO NOT INCLUDE the filename in [0]!!! // May be called recursively when there are -f files. for (int i=0; iv3fatal("Invalid Option: "<v3fatal("Unknown language specified: "<v3fatal("Unknown language specified: "<m_outputSplitCFuncs)) { - m_outputSplitCTrace = m_outputSplitCFuncs; - } - } - else if ( !strcmp (sw, "-output-split-ctrace") ) { // Undocumented optimization tweak - shift; - m_outputSplitCTrace = atoi(argv[i]); - } + } + else if (!strcmp(sw, "-o") && (i+1)m_outputSplitCFuncs)) { + m_outputSplitCTrace = m_outputSplitCFuncs; + } + } + else if (!strcmp(sw, "-output-split-ctrace")) { // Undocumented optimization tweak + shift; + m_outputSplitCTrace = atoi(argv[i]); + } else if (!strcmp(sw, "-trace-fst")) { m_trace = true; m_traceFormat = TraceFormat::FST; @@ -879,231 +885,234 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char m_traceFormat = TraceFormat::FST_THREAD; addLdLibs("-lz"); } - else if ( !strcmp (sw, "-trace-depth") && (i+1)v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown warning specified: "<v3fatal("Unknown setting for --compiler: "<v3fatal("Unknown setting for --compiler: "< 65) fl->v3fatal("--pins-bv maximum is 65: "<v3fatal("--threads must be >= 0: "< 65) fl->v3fatal("--pins-bv maximum is 65: "<v3fatal("--threads must be >= 0: "<v3fatal("Unknown setting for --threads-dpi: "<v3fatal("--threads-max-mtasks must be >= 1: "<v3fatal("Unknown setting for --x-assign: "<v3fatal("Unknown setting for --x-initial: "<v3fatal("Unknown setting for --x-assign: "<v3fatal("Unknown setting for --x-initial: "<v3fatal("Invalid Option: "< ifp (V3File::new_ifstream(filename)); if (ifp->fail()) { - fl->v3error("Cannot open -f command file: "+filename); - return; + fl->v3error("Cannot open -f command file: "+filename); + return; } string whole_file; @@ -1126,24 +1135,24 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) { string line = V3Os::getline(*ifp); // Strip simple comments string oline; - // cppcheck-suppress StlMissingComparison - for (string::const_iterator pos = line.begin(); pos != line.end(); ++pos) { - if (inCmt) { - if (*pos=='*' && *(pos+1)=='/') { - inCmt = false; - ++pos; - } - } else if (*pos=='/' && *(pos+1)=='/') { - break; // Ignore to EOL - } else if (*pos=='/' && *(pos+1)=='*') { - inCmt = true; - // cppcheck-suppress StlMissingComparison - ++pos; - } else { - oline += *pos; - } - } - whole_file += oline + " "; + // cppcheck-suppress StlMissingComparison + for (string::const_iterator pos = line.begin(); pos != line.end(); ++pos) { + if (inCmt) { + if (*pos=='*' && *(pos+1)=='/') { + inCmt = false; + ++pos; + } + } else if (*pos=='/' && *(pos+1)=='/') { + break; // Ignore to EOL + } else if (*pos=='/' && *(pos+1)=='*') { + inCmt = true; + // cppcheck-suppress StlMissingComparison + ++pos; + } else { + oline += *pos; + } + } + whole_file += oline + " "; } whole_file += "\n"; // So string match below is simplified if (inCmt) fl->v3error("Unterminated /* comment inside -f file."); @@ -1154,22 +1163,22 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) { // Note we don't respect quotes. It seems most simulators dont. // Woez those that expect it; we'll at least complain. if (whole_file.find('\"') != string::npos) { - fl->v3error("Double quotes in -f files cause unspecified behavior."); + fl->v3error("Double quotes in -f files cause unspecified behavior."); } // Strip off arguments and parse into words std::vector args; string::size_type startpos = 0; while (startpos < whole_file.length()) { - while (isspace(whole_file[startpos])) ++startpos; - string::size_type endpos = startpos; - while (endpos < whole_file.length() && !isspace(whole_file[endpos])) ++endpos; - if (startpos != endpos) { - string arg (whole_file, startpos, endpos-startpos); - args.reserve(args.size()+1); - args.push_back(arg); - } - startpos = endpos; + while (isspace(whole_file[startpos])) ++startpos; + string::size_type endpos = startpos; + while (endpos < whole_file.length() && !isspace(whole_file[endpos])) ++endpos; + if (startpos != endpos) { + string arg (whole_file, startpos, endpos-startpos); + args.reserve(args.size()+1); + args.push_back(arg); + } + startpos = endpos; } // Path @@ -1188,7 +1197,7 @@ void V3Options::parseOptsFile(FileLine* fl, const string& filename, bool rel) { string V3Options::parseFileArg(const string& optdir, const string& relfilename) { string filename = V3Os::filenameSubstitute(relfilename); if (optdir != "." && V3Os::filenameIsRel(filename)) { - filename = optdir + "/" + filename; + filename = optdir + "/" + filename; } return filename; } @@ -1201,10 +1210,10 @@ bool V3Options::parseLangExt(const char* swp, //!< argument text const V3LangCode& lc) { //!< language code int len = strlen(langswp); if (!strncmp(swp, langswp, len)) { - addLangExt(swp + len, lc); - return true; + addLangExt(swp + len, lc); + return true; } else { - return false; + return false; } } @@ -1336,7 +1345,7 @@ V3Options::V3Options() { m_defaultLanguage = V3LangCode::mostRecent(); - VName::maxLength(128); // Linux filename limits 256; leave half for prefix + VName::maxLength(128); // Linux filename limits 256; leave half for prefix optimize(1); // Default +libext+ @@ -1344,16 +1353,16 @@ V3Options::V3Options() { addLibExtV(".v"); addLibExtV(".sv"); // Default -I - addIncDirFallback("."); // Looks better than {long_cwd_path}/... + addIncDirFallback("."); // Looks better than {long_cwd_path}/... } V3Options::~V3Options() { - delete m_impp; m_impp=NULL; + delete m_impp; m_impp = NULL; } void V3Options::setDebugMode(int level) { V3Error::debugDefault(level); - if (!m_dumpTree) m_dumpTree = 3; // Don't override if already set. + if (!m_dumpTree) m_dumpTree = 3; // Don't override if already set. m_stats = true; m_debugCheck = true; cout << "Starting "<second = level; + iter->second = level; } else { - m_debugSrcs.insert(make_pair(srcfile,level)); + m_debugSrcs.insert(make_pair(srcfile, level)); } } @@ -1374,18 +1383,18 @@ int V3Options::debugSrcLevel(const string& srcfile_path, int default_level) { string srcfile = V3Os::filenameNonDirExt(srcfile_path); DebugSrcMap::iterator iter = m_debugSrcs.find(srcfile); if (iter!=m_debugSrcs.end()) { - return iter->second; + return iter->second; } else { - return default_level; + return default_level; } } void V3Options::setDumpTreeLevel(const string& srcfile, int level) { DebugSrcMap::iterator iter = m_dumpTrees.find(srcfile); if (iter!=m_dumpTrees.end()) { - iter->second = level; + iter->second = level; } else { - m_dumpTrees.insert(make_pair(srcfile,level)); + m_dumpTrees.insert(make_pair(srcfile, level)); } } @@ -1395,9 +1404,9 @@ int V3Options::dumpTreeLevel(const string& srcfile_path) { string srcfile = V3Os::filenameNonDirExt(srcfile_path); DebugSrcMap::iterator iter = m_dumpTrees.find(srcfile); if (iter!=m_dumpTrees.end()) { - return iter->second; + return iter->second; } else { - return m_dumpTree; + return m_dumpTree; } } @@ -1425,6 +1434,6 @@ void V3Options::optimize(int level) { m_oAssemble = flag; // And set specific optimization levels if (level >= 3) { - m_inlineMult = -1; // Maximum inlining + m_inlineMult = -1; // Maximum inlining } } diff --git a/src/V3Options.h b/src/V3Options.h index 6ce5a6ca3..6f28ce538 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -84,138 +84,138 @@ class V3Options { typedef std::map DebugSrcMap; // MEMBERS (general options) - V3OptionsImp* m_impp; // Slow hidden options + V3OptionsImp* m_impp; // Slow hidden options - V3StringSet m_cppFiles; // argument: C++ files to link against - V3StringList m_cFlags; // argument: user CFLAGS - V3StringList m_ldLibs; // argument: user LDFLAGS - V3StringSet m_futures; // argument: -Wfuture- list - V3StringSet m_libraryFiles; // argument: Verilog -v files - V3StringSet m_clockers; // argument: Verilog -clk signals - V3StringSet m_noClockers; // argument: Verilog -noclk signals - V3StringList m_vFiles; // argument: Verilog files to read - V3StringList m_forceIncs; // argument: -FI - DebugSrcMap m_debugSrcs; // argument: --debugi-= - DebugSrcMap m_dumpTrees; // argument: --dump-treei-= + V3StringSet m_cppFiles; // argument: C++ files to link against + V3StringList m_cFlags; // argument: user CFLAGS + V3StringList m_ldLibs; // argument: user LDFLAGS + V3StringSet m_futures; // argument: -Wfuture- list + V3StringSet m_libraryFiles; // argument: Verilog -v files + V3StringSet m_clockers; // argument: Verilog -clk signals + V3StringSet m_noClockers; // argument: Verilog -noclk signals + V3StringList m_vFiles; // argument: Verilog files to read + V3StringList m_forceIncs; // argument: -FI + DebugSrcMap m_debugSrcs; // argument: --debugi-= + DebugSrcMap m_dumpTrees; // argument: --dump-treei-= std::map m_parameters; // Parameters - bool m_preprocOnly; // main switch: -E - bool m_makeDepend; // main switch: -MMD - bool m_makePhony; // main switch: -MP - bool m_preprocNoLine;// main switch: -P - bool m_assert; // main switch: --assert - bool m_autoflush; // main switch: --autoflush - bool m_bboxSys; // main switch: --bbox-sys - bool m_bboxUnsup; // main switch: --bbox-unsup - bool m_cdc; // main switch: --cdc - bool m_coverageLine; // main switch: --coverage-block - bool m_coverageToggle;// main switch: --coverage-toggle - bool m_coverageUnderscore;// main switch: --coverage-underscore - bool m_coverageUser; // main switch: --coverage-func - bool m_debugCheck; // main switch: --debug-check + bool m_preprocOnly; // main switch: -E + bool m_makeDepend; // main switch: -MMD + bool m_makePhony; // main switch: -MP + bool m_preprocNoLine;// main switch: -P + bool m_assert; // main switch: --assert + bool m_autoflush; // main switch: --autoflush + bool m_bboxSys; // main switch: --bbox-sys + bool m_bboxUnsup; // main switch: --bbox-unsup + bool m_cdc; // main switch: --cdc + bool m_coverageLine; // main switch: --coverage-block + bool m_coverageToggle;// main switch: --coverage-toggle + bool m_coverageUnderscore;// main switch: --coverage-underscore + bool m_coverageUser; // main switch: --coverage-func + bool m_debugCheck; // main switch: --debug-check bool m_debugCollision; // main switch: --debug-collision bool m_debugLeak; // main switch: --debug-leak bool m_debugNondeterminism; // main switch: --debug-nondeterminism bool m_debugPartition; // main switch: --debug-partition bool m_debugSelfTest; // main switch: --debug-self-test - bool m_decoration; // main switch: --decoration + bool m_decoration; // main switch: --decoration bool m_dumpDefines; // main switch: --dump-defines - bool m_exe; // main switch: --exe - bool m_ignc; // main switch: --ignc - bool m_inhibitSim; // main switch: --inhibit-sim - bool m_lintOnly; // main switch: --lint-only - bool m_orderClockDly;// main switch: --order-clock-delay - bool m_outFormatOk; // main switch: --cc, --sc or --sp was specified - bool m_pinsScUint; // main switch: --pins-sc-uint - bool m_pinsScBigUint;// main switch: --pins-sc-biguint - bool m_pinsUint8; // main switch: --pins-uint8 + bool m_exe; // main switch: --exe + bool m_ignc; // main switch: --ignc + bool m_inhibitSim; // main switch: --inhibit-sim + bool m_lintOnly; // main switch: --lint-only + bool m_orderClockDly;// main switch: --order-clock-delay + bool m_outFormatOk; // main switch: --cc, --sc or --sp was specified + bool m_pinsScUint; // main switch: --pins-sc-uint + bool m_pinsScBigUint;// main switch: --pins-sc-biguint + bool m_pinsUint8; // main switch: --pins-uint8 bool m_ppComments; // main switch: --pp-comments bool m_profCFuncs; // main switch: --prof-cfuncs bool m_profThreads; // main switch: --prof-threads - bool m_public; // main switch: --public - bool m_relativeCFuncs; // main switch: --relative-cfuncs - bool m_relativeIncludes; // main switch: --relative-includes - bool m_reportUnoptflat; // main switch: --report-unoptflat - bool m_savable; // main switch: --savable - bool m_systemC; // main switch: --sc: System C instead of simple C++ - bool m_skipIdentical;// main switch: --skip-identical - bool m_stats; // main switch: --stats - bool m_statsVars; // main switch: --stats-vars + bool m_public; // main switch: --public + bool m_relativeCFuncs; // main switch: --relative-cfuncs + bool m_relativeIncludes; // main switch: --relative-includes + bool m_reportUnoptflat; // main switch: --report-unoptflat + bool m_savable; // main switch: --savable + bool m_systemC; // main switch: --sc: System C instead of simple C++ + bool m_skipIdentical;// main switch: --skip-identical + bool m_stats; // main switch: --stats + bool m_statsVars; // main switch: --stats-vars bool m_threadsCoarsen; // main switch: --threads-coarsen bool m_threadsDpiPure; // main switch: --threads-dpi all/pure bool m_threadsDpiUnpure; // main switch: --threads-dpi all - bool m_trace; // main switch: --trace - bool m_traceDups; // main switch: --trace-dups - bool m_traceParams; // main switch: --trace-params - bool m_traceStructs; // main switch: --trace-structs - bool m_traceUnderscore;// main switch: --trace-underscore - bool m_underlineZero;// main switch: --underline-zero; undocumented old Verilator 2 - bool m_vpi; // main switch: --vpi - bool m_xInitialEdge; // main switch: --x-initial-edge - bool m_xmlOnly; // main switch: --xml-netlist + bool m_trace; // main switch: --trace + bool m_traceDups; // main switch: --trace-dups + bool m_traceParams; // main switch: --trace-params + bool m_traceStructs; // main switch: --trace-structs + bool m_traceUnderscore;// main switch: --trace-underscore + bool m_underlineZero;// main switch: --underline-zero; undocumented old Verilator 2 + bool m_vpi; // main switch: --vpi + bool m_xInitialEdge; // main switch: --x-initial-edge + bool m_xmlOnly; // main switch: --xml-netlist - int m_convergeLimit;// main switch: --converge-limit - int m_dumpTree; // main switch: --dump-tree + int m_convergeLimit;// main switch: --converge-limit + int m_dumpTree; // main switch: --dump-tree int m_gateStmts; // main switch: --gate-stmts - int m_ifDepth; // main switch: --if-depth - int m_inlineMult; // main switch: --inline-mult - int m_moduleRecursion;// main switch: --module-recursion-depth - int m_outputSplit; // main switch: --output-split - int m_outputSplitCFuncs;// main switch: --output-split-cfuncs - int m_outputSplitCTrace;// main switch: --output-split-ctrace - int m_pinsBv; // main switch: --pins-bv - int m_threads; // main switch: --threads (0 == --no-threads) + int m_ifDepth; // main switch: --if-depth + int m_inlineMult; // main switch: --inline-mult + int m_moduleRecursion;// main switch: --module-recursion-depth + int m_outputSplit; // main switch: --output-split + int m_outputSplitCFuncs;// main switch: --output-split-cfuncs + int m_outputSplitCTrace;// main switch: --output-split-ctrace + int m_pinsBv; // main switch: --pins-bv + int m_threads; // main switch: --threads (0 == --no-threads) int m_threadsMaxMTasks; // main switch: --threads-max-mtasks int m_traceDepth; // main switch: --trace-depth TraceFormat m_traceFormat; // main switch: --trace or --trace-fst int m_traceMaxArray;// main switch: --trace-max-array - int m_traceMaxWidth;// main switch: --trace-max-width - int m_unrollCount; // main switch: --unroll-count - int m_unrollStmts; // main switch: --unroll-stmts + int m_traceMaxWidth;// main switch: --trace-max-width + int m_unrollCount; // main switch: --unroll-count + int m_unrollStmts; // main switch: --unroll-stmts int m_compLimitBlocks; // compiler selection; number of nested blocks int m_compLimitMembers; // compiler selection; number of members in struct before make anon array int m_compLimitParens; // compiler selection; number of nested parens - string m_bin; // main switch: --bin {binary} - string m_exeName; // main switch: -o {name} - string m_flags; // main switch: -f {name} - string m_l2Name; // main switch: --l2name; "" for top-module's name - string m_makeDir; // main switch: -Mdir - string m_modPrefix; // main switch: --mod-prefix - string m_pipeFilter; // main switch: --pipe-filter - string m_prefix; // main switch: --prefix - string m_topModule; // main switch: --top-module - string m_unusedRegexp; // main switch: --unused-regexp - string m_xAssign; // main switch: --x-assign - string m_xInitial; // main switch: --x-initial + string m_bin; // main switch: --bin {binary} + string m_exeName; // main switch: -o {name} + string m_flags; // main switch: -f {name} + string m_l2Name; // main switch: --l2name; "" for top-module's name + string m_makeDir; // main switch: -Mdir + string m_modPrefix; // main switch: --mod-prefix + string m_pipeFilter; // main switch: --pipe-filter + string m_prefix; // main switch: --prefix + string m_topModule; // main switch: --top-module + string m_unusedRegexp; // main switch: --unused-regexp + string m_xAssign; // main switch: --x-assign + string m_xInitial; // main switch: --x-initial // Language is now held in FileLine, on a per-node basis. However we still // have a concept of the default language at a global level. - V3LangCode m_defaultLanguage; // main switch: --language + V3LangCode m_defaultLanguage; // main switch: --language // MEMBERS (optimizations) - // // main switch: -Op: --public - bool m_oAcycSimp; // main switch: -Oy: acyclic pre-optimizations - bool m_oCase; // main switch: -Oe: case tree conversion - bool m_oCombine; // main switch: -Ob: common icode packing - bool m_oConst; // main switch: -Oc: constant folding - bool m_oDedupe; // main switch: -Od: logic deduplication - bool m_oAssemble; // main switch: -Om: assign assemble - bool m_oExpand; // main switch: -Ox: expansion of C macros - bool m_oFlopGater; // main switch: -Of: flop gater detection - bool m_oGate; // main switch: -Og: gate wire elimination - bool m_oLife; // main switch: -Ol: variable lifetime - bool m_oLifePost; // main switch: -Ot: delayed assignment elimination - bool m_oLocalize; // main switch: -Oz: convert temps to local variables - bool m_oInline; // main switch: -Oi: module inlining + // // main switch: -Op: --public + bool m_oAcycSimp; // main switch: -Oy: acyclic pre-optimizations + bool m_oCase; // main switch: -Oe: case tree conversion + bool m_oCombine; // main switch: -Ob: common icode packing + bool m_oConst; // main switch: -Oc: constant folding + bool m_oDedupe; // main switch: -Od: logic deduplication + bool m_oAssemble; // main switch: -Om: assign assemble + bool m_oExpand; // main switch: -Ox: expansion of C macros + bool m_oFlopGater; // main switch: -Of: flop gater detection + bool m_oGate; // main switch: -Og: gate wire elimination + bool m_oLife; // main switch: -Ol: variable lifetime + bool m_oLifePost; // main switch: -Ot: delayed assignment elimination + bool m_oLocalize; // main switch: -Oz: convert temps to local variables + bool m_oInline; // main switch: -Oi: module inlining bool m_oReloop; // main switch: -Ov: reform loops - bool m_oReorder; // main switch: -Or: reorder assignments in blocks - bool m_oSplit; // main switch: -Os: always assignment splitting - bool m_oSubst; // main switch: -Ou: substitute expression temp values - bool m_oSubstConst; // main switch: -Ok: final constant substitution - bool m_oTable; // main switch: -Oa: lookup table creation + bool m_oReorder; // main switch: -Or: reorder assignments in blocks + bool m_oSplit; // main switch: -Os: always assignment splitting + bool m_oSubst; // main switch: -Ou: substitute expression temp values + bool m_oSubstConst; // main switch: -Ok: final constant substitution + bool m_oTable; // main switch: -Oa: lookup table creation private: // METHODS @@ -317,29 +317,29 @@ class V3Options { bool xInitialEdge() const { return m_xInitialEdge; } bool xmlOnly() const { return m_xmlOnly; } - int convergeLimit() const { return m_convergeLimit; } - int dumpTree() const { return m_dumpTree; } - int gateStmts() const { return m_gateStmts; } - int ifDepth() const { return m_ifDepth; } - int inlineMult() const { return m_inlineMult; } - int moduleRecursionDepth() const { return m_moduleRecursion; } - int outputSplit() const { return m_outputSplit; } - int outputSplitCFuncs() const { return m_outputSplitCFuncs; } - int outputSplitCTrace() const { return m_outputSplitCTrace; } - int pinsBv() const { return m_pinsBv; } + int convergeLimit() const { return m_convergeLimit; } + int dumpTree() const { return m_dumpTree; } + int gateStmts() const { return m_gateStmts; } + int ifDepth() const { return m_ifDepth; } + int inlineMult() const { return m_inlineMult; } + int moduleRecursionDepth() const { return m_moduleRecursion; } + int outputSplit() const { return m_outputSplit; } + int outputSplitCFuncs() const { return m_outputSplitCFuncs; } + int outputSplitCTrace() const { return m_outputSplitCTrace; } + int pinsBv() const { return m_pinsBv; } int threads() const { return m_threads; } int threadsMaxMTasks() const { return m_threadsMaxMTasks; } bool mtasks() const { return (m_threads > 1); } int traceDepth() const { return m_traceDepth; } TraceFormat traceFormat() const { return m_traceFormat; } - int traceMaxArray() const { return m_traceMaxArray; } - int traceMaxWidth() const { return m_traceMaxWidth; } - int unrollCount() const { return m_unrollCount; } - int unrollStmts() const { return m_unrollStmts; } + int traceMaxArray() const { return m_traceMaxArray; } + int traceMaxWidth() const { return m_traceMaxWidth; } + int unrollCount() const { return m_unrollCount; } + int unrollStmts() const { return m_unrollStmts; } - int compLimitBlocks() const { return m_compLimitBlocks; } - int compLimitMembers() const { return m_compLimitMembers; } - int compLimitParens() const { return m_compLimitParens; } + int compLimitBlocks() const { return m_compLimitBlocks; } + int compLimitMembers() const { return m_compLimitMembers; } + int compLimitParens() const { return m_compLimitParens; } string exeName() const { return m_exeName!="" ? m_exeName : prefix(); } string l2Name() const { return m_l2Name; } @@ -396,8 +396,8 @@ class V3Options { // METHODS (from main) static string version(); - static string argString(int argc, char** argv); ///< Return list of arguments as simple string - string allArgsString(); ///< Return all passed arguments as simple string + static string argString(int argc, char** argv); ///< Return list of arguments as simple string + string allArgsString(); ///< Return all passed arguments as simple string void bin(const string& flag) { m_bin = flag; } void parseOpts(FileLine* fl, int argc, char** argv); void parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv); @@ -417,7 +417,8 @@ class V3Options { // METHODS (file utilities using these options) string fileExists(const string& filename); - string filePath(FileLine* fl, const string& modname, const string& lastpath, const string& errmsg); + string filePath(FileLine* fl, const string& modname, + const string& lastpath, const string& errmsg); void filePathLookedMsg(FileLine* fl, const string& modname); V3LangCode fileLanguage(const string &filename); static bool fileStatDir(const string& filename); @@ -430,4 +431,4 @@ class V3Options { //###################################################################### -#endif // guard +#endif // guard diff --git a/src/V3Order.cpp b/src/V3Order.cpp index c0ceedf15..e0d7f1b0c 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -22,57 +22,57 @@ // Compute near optimal scheduling of always/wire statements // Make a graph of the entire netlist // -// Add master "*INPUTS*" vertex. -// For inputs on top level -// Add vertex for each input var. -// Add edge INPUTS->var_vertex +// Add master "*INPUTS*" vertex. +// For inputs on top level +// Add vertex for each input var. +// Add edge INPUTS->var_vertex // -// For seq logic -// Add logic_sensitive_vertex for this list of SenItems -// Add edge for each sensitive_var->logic_sensitive_vertex -// For AssignPre's -// Add vertex for this logic -// Add edge logic_sensitive_vertex->logic_vertex -// Add edge logic_consumed_var_PREVAR->logic_vertex -// Add edge logic_vertex->logic_generated_var (same as if comb) -// Add edge logic_vertex->generated_var_PREORDER -// Cutable dependency to attempt to order dlyed -// assignments to avoid saving state, thus we prefer -// a <= b ... As the opposite order would -// b <= c ... require the old value of b. -// For Logic -// Add vertex for this logic -// Add edge logic_sensitive_vertex->logic_vertex -// Add edge logic_generated_var_PREORDER->logic_vertex -// This ensures the AssignPre gets scheduled before this logic -// Add edge logic_vertex->consumed_var_PREVAR -// Add edge logic_vertex->consumed_var_POSTVAR -// Add edge logic_vertex->logic_generated_var (same as if comb) -// For AssignPost's -// Add vertex for this logic -// Add edge logic_sensitive_vertex->logic_vertex -// Add edge logic_consumed_var->logic_vertex (same as if comb) -// Add edge logic_vertex->logic_generated_var (same as if comb) +// For seq logic +// Add logic_sensitive_vertex for this list of SenItems +// Add edge for each sensitive_var->logic_sensitive_vertex +// For AssignPre's +// Add vertex for this logic +// Add edge logic_sensitive_vertex->logic_vertex +// Add edge logic_consumed_var_PREVAR->logic_vertex +// Add edge logic_vertex->logic_generated_var (same as if comb) +// Add edge logic_vertex->generated_var_PREORDER +// Cutable dependency to attempt to order dlyed +// assignments to avoid saving state, thus we prefer +// a <= b ... As the opposite order would +// b <= c ... require the old value of b. +// For Logic +// Add vertex for this logic +// Add edge logic_sensitive_vertex->logic_vertex +// Add edge logic_generated_var_PREORDER->logic_vertex +// This ensures the AssignPre gets scheduled before this logic +// Add edge logic_vertex->consumed_var_PREVAR +// Add edge logic_vertex->consumed_var_POSTVAR +// Add edge logic_vertex->logic_generated_var (same as if comb) +// For AssignPost's +// Add vertex for this logic +// Add edge logic_sensitive_vertex->logic_vertex +// Add edge logic_consumed_var->logic_vertex (same as if comb) +// Add edge logic_vertex->logic_generated_var (same as if comb) // -// For comb logic -// For comb logic -// Add vertex for this logic -// Add edge logic_consumed_var->logic_vertex -// Add edge logic_vertex->logic_generated_var -// Mark it cutable, as circular logic may require -// the generated signal to become a primary input again. +// For comb logic +// For comb logic +// Add vertex for this logic +// Add edge logic_consumed_var->logic_vertex +// Add edge logic_vertex->logic_generated_var +// Mark it cutable, as circular logic may require +// the generated signal to become a primary input again. // // // // Rank the graph starting at INPUTS (see V3Graph) // // Visit the graph's logic vertices in ranked order -// For all logic vertices with all inputs already ordered -// Make ordered block for this module -// For all ^^ in same domain -// Move logic to ordered activation -// When we have no more choices, we move to the next module -// and make a new block. Add that new activation block to the list of calls to make. +// For all logic vertices with all inputs already ordered +// Make ordered block for this module +// For all ^^ in same domain +// Move logic to ordered activation +// When we have no more choices, we move to the next module +// and make a new block. Add that new activation block to the list of calls to make. // //************************************************************************* @@ -117,10 +117,12 @@ static bool domainsExclusive(const AstSenTree* fromp, const AstSenTree* top); void OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) { if (debug()) cout<<"-Info-Loop: "<(vertexp)) { - std::cerr<nodep()->fileline()<<" "<nodep()->typeName()<nodep()->fileline()<<" "<nodep()->typeName()<(vertexp)) { - std::cerr<varScp()->fileline()<<" "<varScp()->prettyName()<varScp()->fileline()<<" "<varScp()->prettyName()< m_readyDomScopeE;// List of next ready dom scope - V3List m_readyVertices; // Ready vertices with same domain & scope + V3ListEnt m_readyDomScopeE;// List of next ready dom scope + V3List m_readyVertices; // Ready vertices with same domain & scope private: - bool m_onReadyList; // True if DomScope is already on list of ready dom/scopes - const AstSenTree* m_domainp; // Domain all vertices belong to - const AstScope* m_scopep; // Scope all vertices belong to + bool m_onReadyList; // True if DomScope is already on list of ready dom/scopes + const AstSenTree* m_domainp; // Domain all vertices belong to + const AstScope* m_scopep; // Scope all vertices belong to typedef std::pair DomScopeKey; typedef std::map DomScopeMap; - static DomScopeMap s_dsMap; // Structure registered for each dom/scope pairing + static DomScopeMap s_dsMap; // Structure registered for each dom/scope pairing public: OrderMoveDomScope(const AstSenTree* domainp, const AstScope* scopep) - : m_onReadyList(false), m_domainp(domainp), m_scopep(scopep) {} + : m_onReadyList(false), m_domainp(domainp), m_scopep(scopep) {} OrderMoveDomScope* readyDomScopeNextp() const { return m_readyDomScopeE.nextp(); } const AstSenTree* domainp() const { return m_domainp; } const AstScope* scopep() const { return m_scopep; } - void ready(OrderVisitor* ovp); // Check the domScope is on ready list, add if not - void movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp); // Mark one vertex as finished, remove from ready list if done + void ready(OrderVisitor* ovp); // Check the domScope is on ready list, add if not + void movedVertex(OrderVisitor* ovp, OrderMoveVertex* vertexp); // Mark one vertex as finished, remove from ready list if done // STATIC MEMBERS (for lookup) static void clear() { - for (DomScopeMap::iterator it=s_dsMap.begin(); it!=s_dsMap.end(); ++it) { - delete it->second; - } - s_dsMap.clear(); + for (DomScopeMap::iterator it=s_dsMap.begin(); it!=s_dsMap.end(); ++it) { + delete it->second; + } + s_dsMap.clear(); } V3List& readyVertices() { return m_readyVertices; } static OrderMoveDomScope* findCreate(const AstSenTree* domainp, const AstScope* scopep) { - const DomScopeKey key = make_pair(domainp,scopep); - DomScopeMap::iterator iter = s_dsMap.find(key); - if (iter != s_dsMap.end()) { - return iter->second; - } else { - OrderMoveDomScope* domScopep = new OrderMoveDomScope(domainp, scopep); - s_dsMap.insert(make_pair(key, domScopep)); - return domScopep; - } + const DomScopeKey key = make_pair(domainp, scopep); + DomScopeMap::iterator iter = s_dsMap.find(key); + if (iter != s_dsMap.end()) { + return iter->second; + } else { + OrderMoveDomScope* domScopep = new OrderMoveDomScope(domainp, scopep); + s_dsMap.insert(make_pair(key, domScopep)); + return domScopep; + } } string name() const { - return (string("MDS:") + return (string("MDS:") +" d="+cvtToHex(domainp()) +" s="+cvtToHex(scopep())); } }; -OrderMoveDomScope::DomScopeMap OrderMoveDomScope::s_dsMap; +OrderMoveDomScope::DomScopeMap OrderMoveDomScope::s_dsMap; inline std::ostream& operator<< (std::ostream& lhs, const OrderMoveDomScope& rhs) { lhs<=WV_MAX) varscp->v3fatalSrc("Bad case"); - OrderVarVertex* vertexp = m_vertexp[type]; - if (!vertexp) { - UINFO(6,"New vertex "<v3fatalSrc("Bad case"); - } - m_vertexp[type] = vertexp; - } else { - if (createdp) *createdp=false; - } - return vertexp; + AstVarScope* varscp, WhichVertex type, bool* createdp=NULL) { + if (type>=WV_MAX) varscp->v3fatalSrc("Bad case"); + OrderVarVertex* vertexp = m_vertexp[type]; + if (!vertexp) { + UINFO(6,"New vertex "<v3fatalSrc("Bad case"); + } + m_vertexp[type] = vertexp; + } else { + if (createdp) *createdp = false; + } + return vertexp; } public: // CONSTRUCTORS OrderUser() { - for (int i=0; ivarScp()->varp()->width() - > vsv2p->varScp()->varp()->width(); + return vsv1p->varScp()->varp()->width() + > vsv2p->varScp()->varp()->width(); } }; //! Comparator for fanout of vertex struct OrderVarFanoutCmp { bool operator() (OrderVarStdVertex* vsv1p, OrderVarStdVertex* vsv2p) { - return vsv1p->fanout() > vsv2p->fanout(); + return vsv1p->fanout() > vsv2p->fanout(); } }; @@ -260,103 +262,106 @@ struct OrderVarFanoutCmp { // class OrderClkMarkVisitor : public AstNVisitor { private: - bool m_hasClk; // flag indicating whether there is clock signal on rhs - bool m_inClocked; // Currently inside a sequential block - bool m_newClkMarked; // Flag for deciding whether a new run is needed - bool m_inAss; // Currently inside of a assignment - int m_childClkWidth; // If in hasClk, width of clock signal in child - int m_rightClkWidth; // Clk width on the RHS + bool m_hasClk; // flag indicating whether there is clock signal on rhs + bool m_inClocked; // Currently inside a sequential block + bool m_newClkMarked; // Flag for deciding whether a new run is needed + bool m_inAss; // Currently inside of a assignment + int m_childClkWidth; // If in hasClk, width of clock signal in child + int m_rightClkWidth; // Clk width on the RHS // METHODS VL_DEBUG_FUNC; // Declare debug() virtual void visit(AstNodeAssign* nodep) { - m_hasClk = false; + m_hasClk = false; if (AstVarRef* varrefp = VN_CAST(nodep->rhsp(), VarRef)) { - this->visit(varrefp); - m_rightClkWidth = varrefp->width(); - if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { + this->visit(varrefp); + m_rightClkWidth = varrefp->width(); + if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { if (m_inClocked) { varrefp->v3warn(CLKDATA, "Clock used as data (on rhs of assignment) in sequential block " <prettyName()<rhsp()); - m_rightClkWidth = m_childClkWidth; - m_inAss = false; - } + m_rightClkWidth = m_childClkWidth; + m_inAss = false; + } - // do the marking - if (m_hasClk) { - if (nodep->lhsp()->width() > m_rightClkWidth) { - nodep->v3warn(CLKDATA, "Clock is assigned to part of data signal "<< nodep->lhsp()<lhsp()->width() <lhsp()->width() > m_rightClkWidth) { + nodep->v3warn(CLKDATA, "Clock is assigned to part of data signal " + <lhsp()<lhsp()->width() <lhsp(), VarRef); - if (lhsp && (lhsp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_UNKNOWN)) { - lhsp->varp()->attrClocker(AstVarAttrClocker::CLOCKER_YES); // mark as clocker - m_newClkMarked = true; // enable a further run since new clocker is marked - UINFO(5, "node is newly marked as clocker by assignment "<varp()->attrClocker() == AstVarAttrClocker::CLOCKER_UNKNOWN)) { + lhsp->varp()->attrClocker(AstVarAttrClocker::CLOCKER_YES); // mark as clocker + m_newClkMarked = true; // enable a further run since new clocker is marked + UINFO(5, "node is newly marked as clocker by assignment "<varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { - if (m_inClocked) { - nodep->v3warn(CLKDATA, "Clock used as data (on rhs of assignment) in sequential block "<prettyName()); - } else { - m_hasClk = true; - m_childClkWidth = nodep->width(); // Pass up - UINFO(5, "node is already marked as clocker "<varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { + if (m_inClocked) { + nodep->v3warn(CLKDATA, + "Clock used as data (on rhs of assignment) in sequential block " + <prettyName()); + } else { + m_hasClk = true; + m_childClkWidth = nodep->width(); // Pass up + UINFO(5, "node is already marked as clocker "<lhsp()); - int lw = m_childClkWidth; + int lw = m_childClkWidth; iterateAndNextNull(nodep->rhsp()); - int rw = m_childClkWidth; - m_childClkWidth = lw + rw; // Pass up - } + int rw = m_childClkWidth; + m_childClkWidth = lw + rw; // Pass up + } } virtual void visit(AstNodeSel* nodep) { - if (m_inAss) { + if (m_inAss) { iterateChildren(nodep); - // Pass up result width - if (m_childClkWidth > nodep->width()) m_childClkWidth = nodep->width(); - } + // Pass up result width + if (m_childClkWidth > nodep->width()) m_childClkWidth = nodep->width(); + } } virtual void visit(AstSel* nodep) { - if (m_inAss) { + if (m_inAss) { iterateChildren(nodep); - if (m_childClkWidth > nodep->width()) m_childClkWidth = nodep->width(); - } + if (m_childClkWidth > nodep->width()) m_childClkWidth = nodep->width(); + } } virtual void visit(AstReplicate* nodep) { - if (m_inAss) { + if (m_inAss) { iterateChildren(nodep); if (VN_IS(nodep->rhsp(), Const)) { m_childClkWidth = m_childClkWidth * VN_CAST(nodep->rhsp(), Const)->toUInt(); - } else { - m_childClkWidth = nodep->width(); // can not check in this case. - } - } + } else { + m_childClkWidth = nodep->width(); // can not check in this case. + } + } } virtual void visit(AstActive* nodep) { - m_inClocked = nodep->hasClocked(); + m_inClocked = nodep->hasClocked(); iterateChildren(nodep); - m_inClocked = false; + m_inClocked = false; } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -365,15 +370,15 @@ private: public: // CONSTUCTORS explicit OrderClkMarkVisitor(AstNode* nodep) { - m_hasClk = false; - m_inClocked = false; - m_inAss = false; - m_childClkWidth = 0; - m_rightClkWidth = 0; - do { - m_newClkMarked = false; + m_hasClk = false; + m_inClocked = false; + m_inAss = false; + m_childClkWidth = 0; + m_rightClkWidth = 0; + do { + m_newClkMarked = false; iterate(nodep); - } while (m_newClkMarked); + } while (m_newClkMarked); } virtual ~OrderClkMarkVisitor() {} }; @@ -383,15 +388,15 @@ public: class OrderClkAssVisitor : public AstNVisitor { private: - bool m_clkAss; // There is signals marked as clocker in the assignment + bool m_clkAss; // There is signals marked as clocker in the assignment // METHODS VL_DEBUG_FUNC; // Declare debug() virtual void visit(AstNodeAssign* nodep) { if (const AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef)) { - if (varrefp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { - m_clkAss = true; - UINFO(6, "node was marked as clocker "<varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { + m_clkAss = true; + UINFO(6, "node was marked as clocker "<rhsp()); } @@ -407,7 +412,7 @@ private: public: // CONSTUCTORS explicit OrderClkAssVisitor(AstNode* nodep) { - m_clkAss = false; + m_clkAss = false; iterate(nodep); } virtual ~OrderClkAssVisitor() {} @@ -668,54 +673,54 @@ private: // NODE STATE // Forming graph: // Entire Netlist: - // AstVarScope::user1p -> OrderUser* for usage var + // AstVarScope::user1p -> OrderUser* for usage var // {statement}Node::user1p-> AstModule* statement is under // USER4 Cleared on each Logic stmt - // AstVarScope::user4() -> VarUsage(gen/con/both). Where already encountered signal + // AstVarScope::user4() -> VarUsage(gen/con/both). Where already encountered signal // Ordering (user3/4/5 cleared between forming and ordering) - // AstScope::user1p() -> AstNodeModule*. Module this scope is under + // AstScope::user1p() -> AstNodeModule*. Module this scope is under // AstNodeModule::user3() -> Number of routines created // Each call to V3Const::constify - // AstNode::user4() Used by V3Const::constify, called below - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; - //AstUser4InUse m_inuser4; // Used only when building tree, so below + // AstNode::user4() Used by V3Const::constify, called below + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; + //AstUser4InUse m_inuser4; // Used only when building tree, so below // STATE - OrderGraph m_graph; // Scoreboard of var usages/dependencies - SenTreeFinder m_finder; // Find global sentree's and add them - AstSenTree* m_comboDomainp; // Combo activation tree - AstSenTree* m_deleteDomainp;// Delete this from tree - AstSenTree* m_settleDomainp;// Initial activation tree - OrderInputsVertex* m_inputsVxp; // Top level vertex all inputs point from - OrderSettleVertex* m_settleVxp; // Top level vertex all settlement vertexes point from - OrderLogicVertex* m_logicVxp; // Current statement being tracked, NULL=ignored - AstTopScope* m_topScopep; // Current top scope being processed - AstScope* m_scopetopp; // Scope under TOPSCOPE - AstNodeModule* m_modp; // Current module - AstScope* m_scopep; // Current scope being processed - AstActive* m_activep; // Current activation block - bool m_inSenTree; // Underneath AstSenItem; any varrefs are clocks - bool m_inClocked; // Underneath clocked block - bool m_inClkAss; // Underneath AstAssign - bool m_inPre; // Underneath AstAssignPre - bool m_inPost; // Underneath AstAssignPost - OrderLogicVertex* m_activeSenVxp; // Sensitivity vertex + OrderGraph m_graph; // Scoreboard of var usages/dependencies + SenTreeFinder m_finder; // Find global sentree's and add them + AstSenTree* m_comboDomainp; // Combo activation tree + AstSenTree* m_deleteDomainp;// Delete this from tree + AstSenTree* m_settleDomainp;// Initial activation tree + OrderInputsVertex* m_inputsVxp; // Top level vertex all inputs point from + OrderSettleVertex* m_settleVxp; // Top level vertex all settlement vertexes point from + OrderLogicVertex* m_logicVxp; // Current statement being tracked, NULL=ignored + AstTopScope* m_topScopep; // Current top scope being processed + AstScope* m_scopetopp; // Scope under TOPSCOPE + AstNodeModule* m_modp; // Current module + AstScope* m_scopep; // Current scope being processed + AstActive* m_activep; // Current activation block + bool m_inSenTree; // Underneath AstSenItem; any varrefs are clocks + bool m_inClocked; // Underneath clocked block + bool m_inClkAss; // Underneath AstAssign + bool m_inPre; // Underneath AstAssignPre + bool m_inPost; // Underneath AstAssignPost + OrderLogicVertex* m_activeSenVxp; // Sensitivity vertex std::deque m_orderUserps; // All created OrderUser's for later deletion. // STATE... for inside process - AstCFunc* m_pomNewFuncp; // Current function being created - int m_pomNewStmts; // Statements in function being created - V3Graph m_pomGraph; // Graph of logic elements to move - V3List m_pomWaiting; // List of nodes needing inputs to become ready + AstCFunc* m_pomNewFuncp; // Current function being created + int m_pomNewStmts; // Statements in function being created + V3Graph m_pomGraph; // Graph of logic elements to move + V3List m_pomWaiting; // List of nodes needing inputs to become ready protected: friend class OrderMoveDomScope; - V3List m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId + V3List m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId std::vector m_unoptflatVars; // Vector of variables in UNOPTFLAT loop private: // STATS - V3Double0 m_statCut[OrderVEdgeType::_ENUM_END]; // Count of each edge type cut + V3Double0 m_statCut[OrderVEdgeType::_ENUM_END]; // Count of each edge type cut // TYPES enum VarUsage { VU_NONE=0, VU_CON=1, VU_GEN=2 }; @@ -724,35 +729,35 @@ private: VL_DEBUG_FUNC; // Declare debug() void iterateNewStmt(AstNode* nodep) { - if (m_scopep) { - UINFO(4," STMT "<sensesp()) nodep->v3fatalSrc("NULL"); - // If inside combo logic, ignore the domain, we'll assign one based on interconnect - AstSenTree* startDomainp = m_activep->sensesp(); - if (startDomainp->hasCombo()) startDomainp=NULL; - m_logicVxp = new OrderLogicVertex(&m_graph, m_scopep, startDomainp, nodep); - if (m_activeSenVxp) { - // If in a clocked activation, add a link from the sensitivity to this block - // Add edge logic_sensitive_vertex->logic_vertex - new OrderEdge(&m_graph, m_activeSenVxp, m_logicVxp, WEIGHT_NORMAL); - } - nodep->user1p(m_modp); + if (m_scopep) { + UINFO(4," STMT "<sensesp()) nodep->v3fatalSrc("NULL"); + // If inside combo logic, ignore the domain, we'll assign one based on interconnect + AstSenTree* startDomainp = m_activep->sensesp(); + if (startDomainp->hasCombo()) startDomainp=NULL; + m_logicVxp = new OrderLogicVertex(&m_graph, m_scopep, startDomainp, nodep); + if (m_activeSenVxp) { + // If in a clocked activation, add a link from the sensitivity to this block + // Add edge logic_sensitive_vertex->logic_vertex + new OrderEdge(&m_graph, m_activeSenVxp, m_logicVxp, WEIGHT_NORMAL); + } + nodep->user1p(m_modp); iterateChildren(nodep); - m_logicVxp = NULL; - } + m_logicVxp = NULL; + } } OrderVarVertex* newVarUserVertex(AstVarScope* varscp, WhichVertex type, bool* createdp=NULL) { - if (!varscp->user1p()) { - OrderUser* newup = new OrderUser(); - m_orderUserps.push_back(newup); - varscp->user1p(newup); - } + if (!varscp->user1p()) { + OrderUser* newup = new OrderUser(); + m_orderUserps.push_back(newup); + varscp->user1p(newup); + } OrderUser* up = reinterpret_cast(varscp->user1p()); - OrderVarVertex* varVxp = up->newVarUserVertex(&m_graph, m_scopep, varscp, type, createdp); - return varVxp; + OrderVarVertex* varVxp = up->newVarUserVertex(&m_graph, m_scopep, varscp, type, createdp); + return varVxp; } void process(); @@ -789,75 +794,80 @@ private: typedef enum {LOGIC_INITIAL, LOGIC_SETTLE} InitialLogicE; void processMTasksInitial(InitialLogicE logic_type); - string cfuncName(AstNodeModule* modp, AstSenTree* domainp, AstScope* scopep, AstNode* forWhatp) { - modp->user3Inc(); - int funcnum = modp->user3(); - string name = (domainp->hasCombo() ? "_combo" - : (domainp->hasInitial() ? "_initial" - : (domainp->hasSettle() ? "_settle" - : (domainp->isMulti() ? "_multiclk" : "_sequent")))); - name = name+"__"+scopep->nameDotless()+"__"+cvtToStr(funcnum); + string cfuncName(AstNodeModule* modp, AstSenTree* domainp, + AstScope* scopep, AstNode* forWhatp) { + modp->user3Inc(); + int funcnum = modp->user3(); + string name = (domainp->hasCombo() ? "_combo" + : (domainp->hasInitial() ? "_initial" + : (domainp->hasSettle() ? "_settle" + : (domainp->isMulti() ? "_multiclk" : "_sequent")))); + name = name+"__"+scopep->nameDotless()+"__"+cvtToStr(funcnum); if (v3Global.opt.profCFuncs()) { - name += "__PROF__"+forWhatp->fileline()->profileFuncname(); - } - return name; + name += "__PROF__"+forWhatp->fileline()->profileFuncname(); + } + return name; } void nodeMarkCircular(OrderVarVertex* vertexp, OrderEdge* edgep) { - AstVarScope* nodep = vertexp->varScp(); - OrderLogicVertex* fromLVtxp = NULL; - OrderLogicVertex* toLVtxp = NULL; - if (edgep) { - fromLVtxp = dynamic_cast(edgep->fromp()); - toLVtxp = dynamic_cast(edgep->top()); - } - // + AstVarScope* nodep = vertexp->varScp(); + OrderLogicVertex* fromLVtxp = NULL; + OrderLogicVertex* toLVtxp = NULL; + if (edgep) { + fromLVtxp = dynamic_cast(edgep->fromp()); + toLVtxp = dynamic_cast(edgep->top()); + } + // if ((fromLVtxp && VN_IS(fromLVtxp->nodep(), Initial)) || (toLVtxp && VN_IS(toLVtxp->nodep(), Initial))) { - // IEEE does not specify ordering between initial blocks, so we can do whatever we want - // We especially do not want to evaluate multiple times, so do not mark the edge circular - } - else { - nodep->circular(true); - ++m_statCut[vertexp->type()]; - if (edgep) ++m_statCut[edgep->type()]; - // - if (vertexp->isClock()) { - // Seems obvious; no warning yet - //nodep->v3warn(GENCLK,"Signal unoptimizable: Generated clock: "<prettyName()); - } else if (nodep->varp()->isSigPublic()) { - nodep->v3warn(UNOPT,"Signal unoptimizable: Feedback to public clock or circular logic: "<prettyName()); - if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPT)) { - nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPT, true); // Complain just once - // Give the user an example. - bool tempWeight = (edgep && edgep->weight()==0); - if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop - m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb - if (tempWeight) edgep->weight(0); - } - } else { - // We don't use UNOPT, as there are lots of V2 places where it was needed, that aren't any more - // First v3warn not inside warnIsOff so we can see the suppressions with --debug - nodep->v3warn(UNOPTFLAT,"Signal unoptimizable: Feedback to clock or circular logic: "<prettyName()); - if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPTFLAT)) { - nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPTFLAT, true); // Complain just once - // Give the user an example. - bool tempWeight = (edgep && edgep->weight()==0); - if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop - m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb - if (tempWeight) edgep->weight(0); - if (v3Global.opt.reportUnoptflat()) { - // Report candidate variables for splitting - reportLoopVars(vertexp); - // Do a subgraph for the UNOPTFLAT loop - OrderGraph loopGraph; - m_graph.subtreeLoops(&OrderEdge::followComboConnected, - vertexp, &loopGraph); - loopGraph.dumpDotFilePrefixedAlways("unoptflat"); - } - } - } - } + // IEEE does not specify ordering between initial blocks, so we + // can do whatever we want. We especially do not want to + // evaluate multiple times, so do not mark the edge circular + } + else { + nodep->circular(true); + ++m_statCut[vertexp->type()]; + if (edgep) ++m_statCut[edgep->type()]; + // + if (vertexp->isClock()) { + // Seems obvious; no warning yet + //nodep->v3warn(GENCLK,"Signal unoptimizable: Generated clock: "<prettyName()); + } else if (nodep->varp()->isSigPublic()) { + nodep->v3warn(UNOPT, "Signal unoptimizable: Feedback to public clock or circular logic: " + <prettyName()); + if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPT)) { + nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPT, true); // Complain just once + // Give the user an example. + bool tempWeight = (edgep && edgep->weight()==0); + if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop + m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb + if (tempWeight) edgep->weight(0); + } + } else { + // We don't use UNOPT, as there are lots of V2 places where + // it was needed, that aren't any more + // First v3warn not inside warnIsOff so we can see the suppressions with --debug + nodep->v3warn(UNOPTFLAT, "Signal unoptimizable: Feedback to clock or circular logic: " + <prettyName()); + if (!nodep->fileline()->warnIsOff(V3ErrorCode::UNOPTFLAT)) { + nodep->fileline()->modifyWarnOff(V3ErrorCode::UNOPTFLAT, true); // Complain just once + // Give the user an example. + bool tempWeight = (edgep && edgep->weight()==0); + if (tempWeight) edgep->weight(1); // Else the below loop detect can't see the loop + m_graph.reportLoops(&OrderEdge::followComboConnected, vertexp); // calls OrderGraph::loopsVertexCb + if (tempWeight) edgep->weight(0); + if (v3Global.opt.reportUnoptflat()) { + // Report candidate variables for splitting + reportLoopVars(vertexp); + // Do a subgraph for the UNOPTFLAT loop + OrderGraph loopGraph; + m_graph.subtreeLoops(&OrderEdge::followComboConnected, + vertexp, &loopGraph); + loopGraph.dumpDotFilePrefixedAlways("unoptflat"); + } + } + } + } } //! Find all variables in an UNOPTFLAT loop @@ -872,148 +882,149 @@ private: //! @todo We could be cleverer in the future and consider just //! the width that is generated/consumed. void reportLoopVars(OrderVarVertex* vertexp) { - m_graph.userClearVertices(); - AstNode::user3ClearTree(); - m_unoptflatVars.clear(); + m_graph.userClearVertices(); + AstNode::user3ClearTree(); + m_unoptflatVars.clear(); reportLoopVarsIterate(vertexp, vertexp->color()); - AstNode::user3ClearTree(); - m_graph.userClearVertices(); - // May be very large vector, so only report the "most important" - // elements. Up to 10 of the widest + AstNode::user3ClearTree(); + m_graph.userClearVertices(); + // May be very large vector, so only report the "most important" + // elements. Up to 10 of the widest std::cerr<varScp()->varp(); + int lim = m_unoptflatVars.size() < 10 ? m_unoptflatVars.size() : 10; + for (int i = 0; i < lim; i++) { + OrderVarStdVertex* vsvertexp = m_unoptflatVars[i]; + AstVar* varp = vsvertexp->varScp()->varp(); std::cerr<fileline()<<" "<prettyName()<width()<<", fanout " <fanout()<varScp()->varp(); + lim = m_unoptflatVars.size() < 10 ? m_unoptflatVars.size() : 10; + for (int i = 0; i < lim; i++) { + OrderVarStdVertex* vsvertexp = m_unoptflatVars[i]; + AstVar* varp = vsvertexp->varScp()->varp(); std::cerr<fileline()<<" "<prettyName() <<", width "<width() <<", fanout "<fanout()<user()) return; // Already done - vertexp->user(1); - if (OrderVarStdVertex* vsvertexp = dynamic_cast(vertexp)) { - // Only reporting on standard variable vertices - AstVar* varp = vsvertexp->varScp()->varp(); - if (!varp->user3()) { - string name = varp->prettyName(); - if ((varp->width() != 1) - && (name.find("__Vdly") == string::npos) - && (name.find("__Vcell") == string::npos)) { - // Variable to report on and not yet done - m_unoptflatVars.push_back(vsvertexp); - } - varp->user3Inc(); - } - } - // Iterate through all the to and from vertices of the same color - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; - edgep = edgep->outNextp()) { - if (edgep->top()->color() == color) { - reportLoopVarsIterate(edgep->top(), color); - } - } - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; - edgep = edgep->inNextp()) { - if (edgep->fromp()->color() == color) { - reportLoopVarsIterate(edgep->fromp(), color); - } - } + if (vertexp->user()) return; // Already done + vertexp->user(1); + if (OrderVarStdVertex* vsvertexp = dynamic_cast(vertexp)) { + // Only reporting on standard variable vertices + AstVar* varp = vsvertexp->varScp()->varp(); + if (!varp->user3()) { + string name = varp->prettyName(); + if ((varp->width() != 1) + && (name.find("__Vdly") == string::npos) + && (name.find("__Vcell") == string::npos)) { + // Variable to report on and not yet done + m_unoptflatVars.push_back(vsvertexp); + } + varp->user3Inc(); + } + } + // Iterate through all the to and from vertices of the same color + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; + edgep = edgep->outNextp()) { + if (edgep->top()->color() == color) { + reportLoopVarsIterate(edgep->top(), color); + } + } + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; + edgep = edgep->inNextp()) { + if (edgep->fromp()->color() == color) { + reportLoopVarsIterate(edgep->fromp(), color); + } + } } // VISITORS virtual void visit(AstNetlist* nodep) { - { - AstUser4InUse m_inuser4; // Used only when building tree, so below + { + AstUser4InUse m_inuser4; // Used only when building tree, so below iterateChildren(nodep); - } - // We're finished, complete the topscopes - if (m_topScopep) { process(); m_topScopep=NULL; } + } + // We're finished, complete the topscopes + if (m_topScopep) { process(); m_topScopep=NULL; } } virtual void visit(AstTopScope* nodep) { - // Process the last thing we're finishing - if (m_topScopep) nodep->v3fatalSrc("Only one topscope should ever be created"); - UINFO(2," Loading tree...\n"); - //VV***** We reset userp() - AstNode::user1ClearTree(); - AstNode::user3ClearTree(); - m_graph.clear(); - m_activep = NULL; - m_topScopep = nodep; - m_scopetopp = nodep->scopep(); - // Find sentree's - m_finder.main(m_topScopep); - // ProcessDomainsIterate will use these when it needs to move - // something to a combodomain. This saves a ton of find() operations. + // Process the last thing we're finishing + if (m_topScopep) nodep->v3fatalSrc("Only one topscope should ever be created"); + UINFO(2," Loading tree...\n"); + //VV***** We reset userp() + AstNode::user1ClearTree(); + AstNode::user3ClearTree(); + m_graph.clear(); + m_activep = NULL; + m_topScopep = nodep; + m_scopetopp = nodep->scopep(); + // Find sentree's + m_finder.main(m_topScopep); + // ProcessDomainsIterate will use these when it needs to move + // something to a combodomain. This saves a ton of find() operations. AstSenTree* combp = new AstSenTree(nodep->fileline(), // Gets cloned() so ok if goes out of scope new AstSenItem(nodep->fileline(), AstSenItem::Combo())); - m_comboDomainp = m_finder.getSenTree(nodep->fileline(), combp); - pushDeletep(combp); // Cleanup when done + m_comboDomainp = m_finder.getSenTree(nodep->fileline(), combp); + pushDeletep(combp); // Cleanup when done AstSenTree* settlep = new AstSenTree(nodep->fileline(), // Gets cloned() so ok if goes out of scope - new AstSenItem(nodep->fileline(), AstSenItem::Settle())); - m_settleDomainp = m_finder.getSenTree(nodep->fileline(), settlep); - pushDeletep(settlep); // Cleanup when done - // Fake AstSenTree we set domainp to indicate needs deletion + new AstSenItem(nodep->fileline(), + AstSenItem::Settle())); + m_settleDomainp = m_finder.getSenTree(nodep->fileline(), settlep); + pushDeletep(settlep); // Cleanup when done + // Fake AstSenTree we set domainp to indicate needs deletion m_deleteDomainp = new AstSenTree(nodep->fileline(), new AstSenItem(nodep->fileline(), AstSenItem::Settle())); - pushDeletep(m_deleteDomainp); // Cleanup when done - UINFO(5," DeleteDomain = "<user1p(m_modp); - // Iterate + UINFO(4," SCOPE "<user1p(m_modp); + // Iterate iterateChildren(nodep); - m_scopep = NULL; + m_scopep = NULL; } virtual void visit(AstActive* nodep) { - // Create required activation blocks and add to module - UINFO(4," ACTIVE "<hasClocked(); - // Grab the sensitivity list - if (nodep->sensesStorep()) nodep->v3fatalSrc("Senses should have been activeTop'ed to be global!"); + // Create required activation blocks and add to module + UINFO(4," ACTIVE "<hasClocked(); + // Grab the sensitivity list + if (nodep->sensesStorep()) nodep->v3fatalSrc("Senses should have been activeTop'ed to be global!"); iterate(nodep->sensesp()); - // Collect statements under it + // Collect statements under it iterateChildren(nodep); - m_activep = NULL; + m_activep = NULL; m_activeSenVxp = NULL; m_inClocked = false; } @@ -1021,199 +1032,202 @@ private: // Create links to all input signals if (m_modp->isTop() && nodep->varp()->isNonOutput()) { OrderVarVertex* varVxp = newVarUserVertex(nodep, WV_STD); - new OrderEdge(&m_graph, m_inputsVxp, varVxp, WEIGHT_INPUT); - } + new OrderEdge(&m_graph, m_inputsVxp, varVxp, WEIGHT_INPUT); + } } virtual void visit(AstNodeVarRef* nodep) { - if (m_scopep) { - AstVarScope* varscp = nodep->varScopep(); - if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); - if (m_inSenTree) { - // Add CLOCK dependency... This is a root of the tree we'll trace - if (nodep->lvalue()) nodep->v3fatalSrc("How can a sensitivity be setting a var?"); - OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); - varVxp->isClock(true); - new OrderEdge(&m_graph, varVxp, m_activeSenVxp, WEIGHT_MEDIUM); - } else { - if (!m_logicVxp) nodep->v3fatalSrc("Var ref not under a logic block"); - // What new directions is this used - // We don't want to add extra edges if the logic block has many usages of same var - bool gen = false; - bool con = false; - if (nodep->lvalue()) { - gen = !(varscp->user4() & VU_GEN); - } else { - con = !(varscp->user4() & VU_CON); - if ((varscp->user4() & VU_GEN) && !m_inClocked) { - // Dangerous assumption: - // If a variable is used in the same activation which defines it first, - // consider it something like: - // foo = 1 - // foo = foo + 1 - // and still optimize. This is the rule verilog-mode assumes for /*AS*/ - // Note this will break though: - // if (sometimes) foo = 1 - // foo = foo + 1 - con = false; - } - if (varscp->varp()->attrClockEn() && !m_inPre && !m_inPost && !m_inClocked) { - // clock_enable attribute: user's worring about it for us - con = false; - } + if (m_scopep) { + AstVarScope* varscp = nodep->varScopep(); + if (!varscp) nodep->v3fatalSrc("Var didn't get varscoped in V3Scope.cpp"); + if (m_inSenTree) { + // Add CLOCK dependency... This is a root of the tree we'll trace + if (nodep->lvalue()) nodep->v3fatalSrc("How can a sensitivity be setting a var?"); + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + varVxp->isClock(true); + new OrderEdge(&m_graph, varVxp, m_activeSenVxp, WEIGHT_MEDIUM); + } else { + if (!m_logicVxp) nodep->v3fatalSrc("Var ref not under a logic block"); + // What new directions is this used + // We don't want to add extra edges if the logic block has many usages of same var + bool gen = false; + bool con = false; + if (nodep->lvalue()) { + gen = !(varscp->user4() & VU_GEN); + } else { + con = !(varscp->user4() & VU_CON); + if ((varscp->user4() & VU_GEN) && !m_inClocked) { + // Dangerous assumption: + // If a variable is used in the same activation which defines it first, + // consider it something like: + // foo = 1 + // foo = foo + 1 + // and still optimize. This is the rule verilog-mode assumes for /*AS*/ + // Note this will break though: + // if (sometimes) foo = 1 + // foo = foo + 1 + con = false; + } + if (varscp->varp()->attrClockEn() && !m_inPre && !m_inPost && !m_inClocked) { + // clock_enable attribute: user's worring about it for us + con = false; + } if (m_inClkAss && (varscp->varp()->attrClocker() != AstVarAttrClocker::CLOCKER_YES)) { - con = false; - UINFO(4, "nodep used as clock_enable "<nodep()<user4(varscp->user4() | VU_GEN); - if (con) varscp->user4(varscp->user4() | VU_CON); - // Add edges - if (!m_inClocked - || m_inPost - ) { - // Combo logic - { // not settle and (combo or inPost) - if (gen) { - // Add edge logic_vertex->logic_generated_var - OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); - if (m_inPost) { - new OrderPostCutEdge(&m_graph, m_logicVxp, varVxp); - // Mark the vertex. Used to control marking - // internal clocks circular, which must only - // happen if they are generated by delayed - // assignment. - UINFO(5, " Found delayed assignment (post) " - << varVxp << endl); - varVxp->isDelayed(true); - } else { - // If the lhs is a clocker, avoid marking that as circular by - // putting a hard edge instead of normal cuttable + con = false; + UINFO(4, "nodep used as clock_enable "<nodep()<user4(varscp->user4() | VU_GEN); + if (con) varscp->user4(varscp->user4() | VU_CON); + // Add edges + if (!m_inClocked + || m_inPost + ) { + // Combo logic + { // not settle and (combo or inPost) + if (gen) { + // Add edge logic_vertex->logic_generated_var + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + if (m_inPost) { + new OrderPostCutEdge(&m_graph, m_logicVxp, varVxp); + // Mark the vertex. Used to control marking + // internal clocks circular, which must only + // happen if they are generated by delayed + // assignment. + UINFO(5, " Found delayed assignment (post) " + << varVxp << endl); + varVxp->isDelayed(true); + } else { + // If the lhs is a clocker, avoid marking that as circular by + // putting a hard edge instead of normal cuttable if (varscp->varp()->attrClocker() == AstVarAttrClocker::CLOCKER_YES) { - new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); + new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); } else { - new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp); + new OrderComboCutEdge(&m_graph, m_logicVxp, varVxp); } - } - // For m_inPost: - // Add edge consumed_var_POST->logic_vertex - // This prevents a consumer of the "early" value to be scheduled - // after we've changed to the next-cycle value - // ALWAYS do it: - // There maybe a wire a=b; between the two blocks - OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST); - new OrderEdge(&m_graph, postVxp, m_logicVxp, WEIGHT_POST); - } - if (con) { - // Add edge logic_consumed_var->logic_vertex - OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); - new OrderEdge(&m_graph, varVxp, m_logicVxp, WEIGHT_MEDIUM); - } - } - } else if (m_inPre) { - // AstAssignPre logic - if (gen) { - // Add edge logic_vertex->generated_var_PREORDER - OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD); - new OrderEdge(&m_graph, m_logicVxp, ordVxp, WEIGHT_NORMAL); - // Add edge logic_vertex->logic_generated_var (same as if comb) - OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); - new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); - } - if (con) { - // Add edge logic_consumed_var_PREVAR->logic_vertex - // This one is cutable (vs the producer) as there's only one of these, but many producers - OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE); - new OrderPreCutEdge(&m_graph, preVxp, m_logicVxp); - } - } else { - // Seq logic - if (gen) { - // Add edge logic_generated_var_PREORDER->logic_vertex - OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD); - new OrderEdge(&m_graph, ordVxp, m_logicVxp, WEIGHT_NORMAL); - // Add edge logic_vertex->logic_generated_var (same as if comb) - OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); - new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); - } - if (con) { - // Add edge logic_vertex->consumed_var_PREVAR - // Generation of 'pre' because we want to indicate it should be before AstAssignPre - OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE); - new OrderEdge(&m_graph, m_logicVxp, preVxp, WEIGHT_NORMAL); - // Add edge logic_vertex->consumed_var_POST - OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST); - new OrderEdge(&m_graph, m_logicVxp, postVxp, WEIGHT_POST); - } - } - } - } + } + // For m_inPost: + // Add edge consumed_var_POST->logic_vertex + // This prevents a consumer of the "early" value to be scheduled + // after we've changed to the next-cycle value + // ALWAYS do it: + // There maybe a wire a=b; between the two blocks + OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST); + new OrderEdge(&m_graph, postVxp, m_logicVxp, WEIGHT_POST); + } + if (con) { + // Add edge logic_consumed_var->logic_vertex + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + new OrderEdge(&m_graph, varVxp, m_logicVxp, WEIGHT_MEDIUM); + } + } + } else if (m_inPre) { + // AstAssignPre logic + if (gen) { + // Add edge logic_vertex->generated_var_PREORDER + OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD); + new OrderEdge(&m_graph, m_logicVxp, ordVxp, WEIGHT_NORMAL); + // Add edge logic_vertex->logic_generated_var (same as if comb) + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); + } + if (con) { + // Add edge logic_consumed_var_PREVAR->logic_vertex + // This one is cutable (vs the producer) as there's + // only one of these, but many producers + OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE); + new OrderPreCutEdge(&m_graph, preVxp, m_logicVxp); + } + } else { + // Seq logic + if (gen) { + // Add edge logic_generated_var_PREORDER->logic_vertex + OrderVarVertex* ordVxp = newVarUserVertex(varscp, WV_PORD); + new OrderEdge(&m_graph, ordVxp, m_logicVxp, WEIGHT_NORMAL); + // Add edge logic_vertex->logic_generated_var (same as if comb) + OrderVarVertex* varVxp = newVarUserVertex(varscp, WV_STD); + new OrderEdge(&m_graph, m_logicVxp, varVxp, WEIGHT_NORMAL); + } + if (con) { + // Add edge logic_vertex->consumed_var_PREVAR + // Generation of 'pre' because we want to indicate + // it should be before AstAssignPre + OrderVarVertex* preVxp = newVarUserVertex(varscp, WV_PRE); + new OrderEdge(&m_graph, m_logicVxp, preVxp, WEIGHT_NORMAL); + // Add edge logic_vertex->consumed_var_POST + OrderVarVertex* postVxp = newVarUserVertex(varscp, WV_POST); + new OrderEdge(&m_graph, m_logicVxp, postVxp, WEIGHT_POST); + } + } + } + } } virtual void visit(AstSenTree* nodep) { - // Having a node derived from the sentree isn't required for - // correctness, it merely makes the graph better connected - // and improves graph algorithmic performance - if (m_scopep) { // Else TOPSCOPE's SENTREE list - m_inSenTree = true; - if (nodep->hasClocked()) { - if (!m_activeSenVxp) { - m_activeSenVxp = new OrderLogicVertex(&m_graph, m_scopep, nodep, m_activep); - } + // Having a node derived from the sentree isn't required for + // correctness, it merely makes the graph better connected + // and improves graph algorithmic performance + if (m_scopep) { // Else TOPSCOPE's SENTREE list + m_inSenTree = true; + if (nodep->hasClocked()) { + if (!m_activeSenVxp) { + m_activeSenVxp = new OrderLogicVertex(&m_graph, m_scopep, nodep, m_activep); + } iterateChildren(nodep); - } - m_inSenTree = false; - } + } + m_inSenTree = false; + } } virtual void visit(AstAlways* nodep) { - iterateNewStmt(nodep); + iterateNewStmt(nodep); } virtual void visit(AstAlwaysPost* nodep) { - m_inPost = true; - iterateNewStmt(nodep); - m_inPost = false; + m_inPost = true; + iterateNewStmt(nodep); + m_inPost = false; } virtual void visit(AstAlwaysPublic* nodep) { - iterateNewStmt(nodep); + iterateNewStmt(nodep); } virtual void visit(AstAssignAlias* nodep) { - iterateNewStmt(nodep); + iterateNewStmt(nodep); } virtual void visit(AstAssignW* nodep) { - OrderClkAssVisitor visitor(nodep); - m_inClkAss = visitor.isClkAss(); - iterateNewStmt(nodep); - m_inClkAss = false; + OrderClkAssVisitor visitor(nodep); + m_inClkAss = visitor.isClkAss(); + iterateNewStmt(nodep); + m_inClkAss = false; } virtual void visit(AstAssignPre* nodep) { - OrderClkAssVisitor visitor(nodep); - m_inClkAss = visitor.isClkAss(); - m_inPre = true; - iterateNewStmt(nodep); - m_inPre = false; - m_inClkAss = false; + OrderClkAssVisitor visitor(nodep); + m_inClkAss = visitor.isClkAss(); + m_inPre = true; + iterateNewStmt(nodep); + m_inPre = false; + m_inClkAss = false; } virtual void visit(AstAssignPost* nodep) { - OrderClkAssVisitor visitor(nodep); - m_inClkAss = visitor.isClkAss(); - m_inPost = true; - iterateNewStmt(nodep); - m_inPost = false; - m_inClkAss = false; + OrderClkAssVisitor visitor(nodep); + m_inClkAss = visitor.isClkAss(); + m_inPost = true; + iterateNewStmt(nodep); + m_inPost = false; + m_inClkAss = false; } virtual void visit(AstCoverToggle* nodep) { - iterateNewStmt(nodep); + iterateNewStmt(nodep); } virtual void visit(AstInitial* nodep) { - // We use initials to setup parameters and static consts's which may be referenced - // in user initial blocks. So use ordering to sort them all out. - iterateNewStmt(nodep); + // We use initials to setup parameters and static consts's which may be referenced + // in user initial blocks. So use ordering to sort them all out. + iterateNewStmt(nodep); } virtual void visit(AstCFunc*) { - // Ignore for now - // We should detect what variables are set in the function, and make - // settlement code for them, then set a global flag, so we call "settle" - // on the next evaluation loop. + // Ignore for now + // We should detect what variables are set in the function, and make + // settlement code for them, then set a global flag, so we call "settle" + // on the next evaluation loop. } //-------------------- // Default @@ -1223,39 +1237,40 @@ private: public: // CONSTUCTORS OrderVisitor() { - m_topScopep = NULL; - m_scopetopp = NULL; - m_modp = NULL; - m_scopep = NULL; - m_activep = NULL; - m_inSenTree = false; - m_inClocked = false; - m_inClkAss = false; - m_inPre = m_inPost = false; - m_comboDomainp = NULL; - m_deleteDomainp = NULL; - m_settleDomainp = NULL; - m_settleVxp = NULL; - m_inputsVxp = NULL; - m_activeSenVxp = NULL; - m_logicVxp = NULL; - m_pomNewFuncp = NULL; - m_pomNewStmts = 0; - if (debug()) m_graph.debug(5); // 3 is default if global debug; we want acyc debugging + m_topScopep = NULL; + m_scopetopp = NULL; + m_modp = NULL; + m_scopep = NULL; + m_activep = NULL; + m_inSenTree = false; + m_inClocked = false; + m_inClkAss = false; + m_inPre = m_inPost = false; + m_comboDomainp = NULL; + m_deleteDomainp = NULL; + m_settleDomainp = NULL; + m_settleVxp = NULL; + m_inputsVxp = NULL; + m_activeSenVxp = NULL; + m_logicVxp = NULL; + m_pomNewFuncp = NULL; + m_pomNewStmts = 0; + if (debug()) m_graph.debug(5); // 3 is default if global debug; we want acyc debugging } virtual ~OrderVisitor() { - // Stats - for (int type=0; type::iterator it=m_orderUserps.begin(); it!=m_orderUserps.end(); ++it) { - delete *it; - } - m_graph.debug(V3Error::debugDefault()); + // Stats + for (int type=0; type::iterator it=m_orderUserps.begin(); + it != m_orderUserps.end(); ++it) { + delete *it; + } + m_graph.debug(V3Error::debugDefault()); } void main(AstNode* nodep) { iterate(nodep); @@ -1335,12 +1350,12 @@ inline void OrderMoveDomScope::movedVertex(OrderVisitor* ovp, OrderMoveVertex* v void OrderVisitor::processInputs() { m_graph.userClearVertices(); // Vertex::user() // 1 if input recursed, 2 if marked as input, 3 if out-edges recursed // Start at input vertex, process from input-to-output order - VertexVec todoVec; // List of newly-input marked vectors we need to process + VertexVec todoVec; // List of newly-input marked vectors we need to process todoVec.push_front(m_inputsVxp); m_inputsVxp->isFromInput(true); // By definition while (!todoVec.empty()) { - OrderEitherVertex* vertexp = todoVec.back(); todoVec.pop_back(); - processInputsOutIterate(vertexp, todoVec); + OrderEitherVertex* vertexp = todoVec.back(); todoVec.pop_back(); + processInputsOutIterate(vertexp, todoVec); } } @@ -1348,10 +1363,10 @@ void OrderVisitor::processInputsInIterate(OrderEitherVertex* vertexp, VertexVec& // Propagate PrimaryIn through simple assignments if (vertexp->user()) return; // Already processed if (0 && debug()>=9) { - UINFO(9," InIIter "<(vertexp)) { - vvertexp->nodep()->dumpTree(cout,"- TT: "); - } + UINFO(9," InIIter "<(vertexp)) { + vvertexp->nodep()->dumpTree(cout, "- TT: "); + } } vertexp->user(1); // Processing // First handle all inputs to this vertex, in most cases they'll be already processed earlier @@ -1359,26 +1374,26 @@ void OrderVisitor::processInputsInIterate(OrderEitherVertex* vertexp, VertexVec& int inonly = 1; // 0=no, 1=maybe, 2=yes until a no for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { OrderEitherVertex* frVertexp = static_cast(edgep->fromp()); - processInputsInIterate(frVertexp, todoVec); - if (frVertexp->isFromInput()) { - if (inonly==1) inonly = 2; - } else if (dynamic_cast(frVertexp)) { - // Ignore post assignments, just for ordering - } else { - //UINFO(9," InItStopDueTo "<isFromInput()) { + if (inonly==1) inonly = 2; + } else if (dynamic_cast(frVertexp)) { + // Ignore post assignments, just for ordering + } else { + //UINFO(9," InItStopDueTo "<user()<2) { // Set it. Note may have already been set earlier, too - UINFO(9," Input reassignment: "<isFromInput(true); - vertexp->user(2); // 2 means on list - // Can't work on out-edges of a node we know is an input immediately, - // as it might visit other nodes before their input state is resolved. - // So push to list and work on it later when all in-edges known resolved - todoVec.push_back(vertexp); + UINFO(9," Input reassignment: "<isFromInput(true); + vertexp->user(2); // 2 means on list + // Can't work on out-edges of a node we know is an input immediately, + // as it might visit other nodes before their input state is resolved. + // So push to list and work on it later when all in-edges known resolved + todoVec.push_back(vertexp); } //UINFO(9," InIdone "<user(3); // out-edges processed { - // Propagate PrimaryIn through simple assignments, followint target of vertex - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + // Propagate PrimaryIn through simple assignments, followint target of vertex + for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { OrderEitherVertex* toVertexp = static_cast(edgep->top()); - if (OrderVarStdVertex* vvertexp = dynamic_cast(toVertexp)) { - processInputsInIterate(vvertexp, todoVec); - } - if (OrderLogicVertex* vvertexp = dynamic_cast(toVertexp)) { + if (OrderVarStdVertex* vvertexp = dynamic_cast(toVertexp)) { + processInputsInIterate(vvertexp, todoVec); + } + if (OrderLogicVertex* vvertexp = dynamic_cast(toVertexp)) { if (VN_IS(vvertexp->nodep(), NodeAssign)) { - processInputsInIterate(vvertexp, todoVec); - } - } - } + processInputsInIterate(vvertexp, todoVec); + } + } + } } } @@ -1417,46 +1432,46 @@ void OrderVisitor::processCircular() { // Take broken edges and add circular flags // The change detect code will use this to force changedets for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (OrderVarStdVertex* vvertexp = dynamic_cast(itp)) { - if (vvertexp->isClock() && !vvertexp->isFromInput()) { - // If a clock is generated internally, we need to do another - // loop through the entire evaluation. This fixes races; see - // t_clk_dpulse test. - // - // This all seems to hinge on how the clock is generated. If - // it is generated by delayed assignment, we need the loop. If - // it is combinatorial, we do not (and indeed it will break - // other tests such as t_gated_clk_1. - if (!v3Global.opt.orderClockDly()) { - UINFO(5,"Circular Clock, no-order-clock-delay "<isDelayed()) { - UINFO(5,"Circular Clock, delayed "<outBeginp(); edgep; edgep=edgep->outNextp()) { - if (edgep->weight()==0) { // was cut - OrderEdge* oedgep = dynamic_cast(edgep); - if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type"); - UINFO(6," CutCircularO: "<name()<inBeginp(); edgep; edgep = edgep->inNextp()) { - if (edgep->weight()==0) { // was cut - OrderEdge* oedgep = dynamic_cast(edgep); - if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type"); - UINFO(6," CutCircularI: "<name()<(itp)) { + if (vvertexp->isClock() && !vvertexp->isFromInput()) { + // If a clock is generated internally, we need to do another + // loop through the entire evaluation. This fixes races; see + // t_clk_dpulse test. + // + // This all seems to hinge on how the clock is generated. If + // it is generated by delayed assignment, we need the loop. If + // it is combinatorial, we do not (and indeed it will break + // other tests such as t_gated_clk_1. + if (!v3Global.opt.orderClockDly()) { + UINFO(5,"Circular Clock, no-order-clock-delay "<isDelayed()) { + UINFO(5,"Circular Clock, delayed "<outBeginp(); edgep; edgep=edgep->outNextp()) { + if (edgep->weight()==0) { // was cut + OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type"); + UINFO(6," CutCircularO: "<name()<inBeginp(); edgep; edgep = edgep->inNextp()) { + if (edgep->weight()==0) { // was cut + OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) vvertexp->varScp()->v3fatalSrc("Cuttable edge not of proper type"); + UINFO(6," CutCircularI: "<name()<(itp)) { if (vvertexp->varScp()->varp()->isNonOutput()) { //UINFO(0," scsen "<outBeginp(); edgep; edgep=edgep->outNextp()) { - if (OrderEitherVertex* toVertexp = dynamic_cast(edgep->top())) { - if (edgep->weight() && toVertexp->domainp()) { - //UINFO(0," "<domainp()<domainp()->hasCombo()) { - vvertexp->varScp()->varp()->scSensitive(true); - } - } - } - } - } - } + for (V3GraphEdge* edgep = vvertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + if (OrderEitherVertex* toVertexp + = dynamic_cast(edgep->top())) { + if (edgep->weight() && toVertexp->domainp()) { + //UINFO(0," "<domainp()<domainp()->hasCombo()) { + vvertexp->varScp()->varp()->scSensitive(true); + } + } + } + } + } + } } } void OrderVisitor::processDomains() { for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - OrderEitherVertex* vertexp = dynamic_cast(itp); + OrderEitherVertex* vertexp = dynamic_cast(itp); UASSERT(vertexp, "Null or vertex not derived from EitherVertex"); - processDomainsIterate(vertexp); + processDomainsIterate(vertexp); } } @@ -1497,7 +1513,7 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) { // Combo logic may be pushed into a seq domain if all its inputs are the same domain, // else, if all inputs are from flops, it's end-of-sequential code // else, it's full combo code - if (vertexp->domainp()) return; // Already processed, or sequential logic + if (vertexp->domainp()) return; // Already processed, or sequential logic UINFO(5," pdi: "<(vertexp); AstSenTree* domainp = NULL; @@ -1506,77 +1522,77 @@ void OrderVisitor::processDomainsIterate(OrderEitherVertex* vertexp) { domainp = m_comboDomainp; } if (vvertexp && vvertexp->varScp()->isCircular()) { - domainp = m_comboDomainp; + domainp = m_comboDomainp; } if (!domainp) { - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { OrderEitherVertex* fromVertexp = static_cast(edgep->fromp()); - if (edgep->weight() - && fromVertexp->domainMatters() - ) { + if (edgep->weight() + && fromVertexp->domainMatters() + ) { UINFO(9," from d="<domainp()) <<" "<hasSettle() // or, we can ignore being in the settle domain - || domainp->hasInitial()) { - domainp = fromVertexp->domainp(); - } - else if (domainp->hasCombo()) { - // Once in combo, keep in combo; already as severe as we can get - } - else if (fromVertexp->domainp()->hasCombo()) { - // Any combo input means this vertex must remain combo - domainp = m_comboDomainp; - } - else if (fromVertexp->domainp()->hasSettle() - || fromVertexp->domainp()->hasInitial()) { - // Ignore that we have a constant (initial) input - } - else if (domainp != fromVertexp->domainp()) { - // Make a domain that merges the two domains - bool ddebug = debug()>=9; - if (ddebug) { - cout<addSensesp(newtree2p); - newtree2p=NULL; // Below edit may replace it - V3Const::constifyExpensiveEdit(newtreep); // Remove duplicates - newtreep->multi(true); // Comment that it was made from 2 clock domains - domainp = m_finder.getSenTree(domainp->fileline(), newtreep); - if (ddebug) { - UINFO(0," dnew ="<dumpTree(cout); - UINFO(0," find ="<dumpTree(cout); - cout<deleteTree(); VL_DANGLING(newtreep); - } - } - } // next input edgep - // Default the domain - // This is a node which has only constant inputs, or is otherwise indeterminate. - // It should have already been copied into the settle domain. Presumably it has - // inputs which we never trigger, or nothing it's sensitive to, so we can rip it out. - if (!domainp && vertexp->scopep()) { - domainp = m_deleteDomainp; - } + if (!domainp // First input to this vertex + || domainp->hasSettle() // or, we can ignore being in the settle domain + || domainp->hasInitial()) { + domainp = fromVertexp->domainp(); + } + else if (domainp->hasCombo()) { + // Once in combo, keep in combo; already as severe as we can get + } + else if (fromVertexp->domainp()->hasCombo()) { + // Any combo input means this vertex must remain combo + domainp = m_comboDomainp; + } + else if (fromVertexp->domainp()->hasSettle() + || fromVertexp->domainp()->hasInitial()) { + // Ignore that we have a constant (initial) input + } + else if (domainp != fromVertexp->domainp()) { + // Make a domain that merges the two domains + bool ddebug = debug()>=9; + if (ddebug) { + cout<addSensesp(newtree2p); + newtree2p = NULL; // Below edit may replace it + V3Const::constifyExpensiveEdit(newtreep); // Remove duplicates + newtreep->multi(true); // Comment that it was made from 2 clock domains + domainp = m_finder.getSenTree(domainp->fileline(), newtreep); + if (ddebug) { + UINFO(0," dnew ="<dumpTree(cout); + UINFO(0," find ="<dumpTree(cout); + cout<deleteTree(); VL_DANGLING(newtreep); + } + } + } // next input edgep + // Default the domain + // This is a node which has only constant inputs, or is otherwise indeterminate. + // It should have already been copied into the settle domain. Presumably it has + // inputs which we never trigger, or nothing it's sensitive to, so we can rip it out. + if (!domainp && vertexp->scopep()) { + domainp = m_deleteDomainp; + } } // vertexp->domainp(domainp); if (vertexp->domainp()) { UINFO(5," done d="<domainp()) - <<(vertexp->domainp()->hasCombo()?" [COMB]":"") - <<(vertexp->domainp()->isMulti()?" [MULT]":"") - <<" "<domainp()->hasCombo()?" [COMB]":"") + <<(vertexp->domainp()->isMulti()?" [MULT]":"") + <<" "< report; for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (OrderVarVertex* vvertexp = dynamic_cast(itp)) { - string name (vvertexp->varScp()->prettyName()); - if (dynamic_cast(itp)) name += " {PRE}"; - else if (dynamic_cast(itp)) name += " {POST}"; - else if (dynamic_cast(itp)) name += " {PORD}"; - else if (dynamic_cast(itp)) name += " {STL}"; + if (OrderVarVertex* vvertexp = dynamic_cast(itp)) { + string name (vvertexp->varScp()->prettyName()); + if (dynamic_cast(itp)) name += " {PRE}"; + else if (dynamic_cast(itp)) name += " {POST}"; + else if (dynamic_cast(itp)) name += " {PORD}"; + else if (dynamic_cast(itp)) name += " {STL}"; std::ostringstream os; os.setf(std::ios::left); os<<" "<varScp())<<" "<domainp(); - if (sentreep) V3EmitV::verilogForTree(sentreep, os); - report.push_back(os.str()); - } + AstSenTree* sentreep = vvertexp->domainp(); + if (sentreep) V3EmitV::verilogForTree(sentreep, os); + report.push_back(os.str()); + } } *logp<<"Signals and their clock domains:"<::iterator it=report.begin(); it!=report.end(); ++it) { - *logp<<(*it)<ready graph - // (This may add nodes the for loop directly above needs to detext) + // Make ordered activation block for this module + // Add that new activation to the list of calls to make. + // Move logic to ordered active + // Any children that have all inputs now ready move from waiting->ready graph + // (This may add nodes the for loop directly above needs to detext) processMovePrepReady(); // New domain... another loop UINFO(5," MoveIterate\n"); while (!m_pomReadyDomScope.empty()) { - // Start with top node on ready list's domain & scope - OrderMoveDomScope* domScopep = m_pomReadyDomScope.begin(); - OrderMoveVertex* topVertexp = domScopep->readyVertices().begin(); // lintok-begin-on-ref - UASSERT(topVertexp, "domScope on ready list without any nodes ready under it"); - // Work on all scopes ready inside this domain - while (domScopep) { - UINFO(6," MoveDomain l="<domainp()<readyVertices().begin()) { // lintok-begin-on-ref - processMoveOne(vertexp, domScopep, 1); - } - // Done with scope/domain pair, pick new scope under same domain, or NULL if none left - OrderMoveDomScope* domScopeNextp = NULL; - for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin(); - huntp; huntp = huntp->readyDomScopeNextp()) { - if (huntp->domainp() == domScopep->domainp()) { - domScopeNextp = huntp; - break; - } - } - domScopep = domScopeNextp; - } + // Start with top node on ready list's domain & scope + OrderMoveDomScope* domScopep = m_pomReadyDomScope.begin(); + OrderMoveVertex* topVertexp = domScopep->readyVertices().begin(); // lintok-begin-on-ref + UASSERT(topVertexp, "domScope on ready list without any nodes ready under it"); + // Work on all scopes ready inside this domain + while (domScopep) { + UINFO(6," MoveDomain l="<domainp()<readyVertices().begin()) { // lintok-begin-on-ref + processMoveOne(vertexp, domScopep, 1); + } + // Done with scope/domain pair, pick new scope under same domain, or NULL if none left + OrderMoveDomScope* domScopeNextp = NULL; + for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin(); + huntp; huntp = huntp->readyDomScopeNextp()) { + if (huntp->domainp() == domScopep->domainp()) { + domScopeNextp = huntp; + break; + } + } + domScopep = domScopeNextp; + } } - UASSERT (m_pomWaiting.empty(), "Didn't converge; nodes waiting, none ready, perhaps some input activations lost."); + UASSERT(m_pomWaiting.empty(), "Didn't converge; nodes waiting, none ready, perhaps some input activations lost."); // Cleanup memory processMoveClear(); } @@ -1688,11 +1704,11 @@ void OrderVisitor::processMovePrepReady() { // Make list of ready nodes UINFO(5," MovePrepReady\n"); for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp; ) { - OrderMoveVertex* nextp = vertexp->pomWaitingNextp(); - if (vertexp->isWait() && vertexp->inEmpty()) { - processMoveReadyOne(vertexp); - } - vertexp = nextp; + OrderMoveVertex* nextp = vertexp->pomWaitingNextp(); + if (vertexp->isWait() && vertexp->inEmpty()) { + processMoveReadyOne(vertexp); + } + vertexp = nextp; } } @@ -1724,21 +1740,23 @@ void OrderVisitor::processMoveDoneOne(OrderMoveVertex* vertexp) { // Don't need to add it to another list, as we're done with it // Mark our outputs as one closer to ready for (V3GraphEdge* edgep = vertexp->outBeginp(), *nextp; edgep; edgep=nextp) { - nextp = edgep->outNextp(); + nextp = edgep->outNextp(); OrderMoveVertex* toVertexp = static_cast(edgep->top()); - UINFO(9," Clear to "<<(toVertexp->inEmpty()?"[EMP] ":" ") - <unlinkDelete(); VL_DANGLING(edgep); - if (toVertexp->inEmpty()) { - // If destination node now has all inputs resolved; recurse to move that vertex - // This is thus depth first (before width) which keeps the resulting executable's d-cache happy. - processMoveReadyOne(toVertexp); - } + UINFO(9," Clear to "<<(toVertexp->inEmpty()?"[EMP] ":" ") + <unlinkDelete(); VL_DANGLING(edgep); + if (toVertexp->inEmpty()) { + // If destination node now has all inputs resolved; recurse to move that vertex + // This is thus depth first (before width) which keeps the + // resulting executable's d-cache happy. + processMoveReadyOne(toVertexp); + } } } -void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, OrderMoveDomScope* domScopep, int level) { +void OrderVisitor::processMoveOne(OrderMoveVertex* vertexp, + OrderMoveDomScope* domScopep, int level) { if (vertexp->domScopep() != domScopep) { vertexp->v3fatalSrc("Domain mismatch; list misbuilt?"); } @@ -1759,51 +1777,52 @@ AstActive* OrderVisitor::processMoveOneLogic(const OrderLogicVertex* lvertexp, AstScope* scopep = lvertexp->scopep(); AstSenTree* domainp = lvertexp->domainp(); AstNode* nodep = lvertexp->nodep(); - AstNodeModule* modp = VN_CAST(scopep->user1p(), NodeModule); UASSERT(modp,"NULL"); // Stashed by visitor func + AstNodeModule* modp = VN_CAST(scopep->user1p(), NodeModule); // Stashed by visitor func + UASSERT(modp, "NULL"); if (VN_IS(nodep, UntilStable)) { - nodep->v3fatalSrc("Not implemented"); + nodep->v3fatalSrc("Not implemented"); } else if (VN_IS(nodep, SenTree)) { - // Just ignore sensitivities, we'll deal with them when we move statements that need them + // Just ignore sensitivities, we'll deal with them when we move statements that need them } else { // Normal logic - // Make or borrow a CFunc to contain the new statements + // Make or borrow a CFunc to contain the new statements if (v3Global.opt.profCFuncs() - || (v3Global.opt.outputSplitCFuncs() + || (v3Global.opt.outputSplitCFuncs() && v3Global.opt.outputSplitCFuncs() < newStmtsr)) { - // Put every statement into a unique function to ease profiling or reduce function size + // Put every statement into a unique function to ease profiling or reduce function size newFuncpr = NULL; - } + } if (!newFuncpr && domainp != m_deleteDomainp) { - string name = cfuncName(modp, domainp, scopep, nodep); + string name = cfuncName(modp, domainp, scopep, nodep); newFuncpr = new AstCFunc(nodep->fileline(), name, scopep); newFuncpr->argTypes(EmitCBaseVisitor::symClassVar()); newFuncpr->symProlog(true); newStmtsr = 0; if (domainp->hasInitial() || domainp->hasSettle()) newFuncpr->slow(true); scopep->addActivep(newFuncpr); - // Where will we be adding the call? + // Where will we be adding the call? activep = new AstActive(nodep->fileline(), name, domainp); - // Add a top call to it + // Add a top call to it AstCCall* callp = new AstCCall(nodep->fileline(), newFuncpr); - callp->argTypes("vlSymsp"); + callp->argTypes("vlSymsp"); activep->addStmtsp(callp); UINFO(6," New "<unlinkFrBack(); - if (domainp == m_deleteDomainp) { - UINFO(4," Ordering deleting pre-settled "<unlinkFrBack(); + if (domainp == m_deleteDomainp) { + UINFO(4," Ordering deleting pre-settled "<addStmtsp(nodep); - if (v3Global.opt.outputSplitCFuncs()) { - // Add in the number of nodes we're adding - EmitCBaseCounterVisitor visitor(nodep); + if (v3Global.opt.outputSplitCFuncs()) { + // Add in the number of nodes we're adding + EmitCBaseCounterVisitor visitor(nodep); newStmtsr += visitor.count(); - } - } + } + } } return activep; } @@ -1943,7 +1962,8 @@ void OrderVisitor::processMTasks() { } last_domainp = logicp->domainp(); - AstActive* newActivep = processMoveOneLogic(logicp, leafCFuncp/*ref*/, leafStmts/*ref*/); + AstActive* newActivep = processMoveOneLogic(logicp, leafCFuncp/*ref*/, + leafStmts/*ref*/); if (newActivep) bodyp->addStmtsp(newActivep); } @@ -2032,10 +2052,10 @@ void OrderVisitor::process() { // Dump data m_graph.dumpDotFilePrefixed("orderg_done"); if (0 && debug()) { - string dfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_INT_order"; + string dfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_INT_order"; const vl_unique_ptr logp (V3File::new_ofstream(dfilename)); if (logp->fail()) v3fatal("Can't write "<(_e)) {} operator en() const { return m_e; } }; - inline bool operator== (OrderVEdgeType lhs, OrderVEdgeType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (OrderVEdgeType lhs, OrderVEdgeType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (OrderVEdgeType::en lhs, OrderVEdgeType rhs) { return (lhs == rhs.m_e); } + inline bool operator==(OrderVEdgeType lhs, OrderVEdgeType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator==(OrderVEdgeType lhs, OrderVEdgeType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator==(OrderVEdgeType::en lhs, OrderVEdgeType rhs) { return (lhs == rhs.m_e); } //###################################################################### // Graph types @@ -132,42 +132,42 @@ public: // Vertex types class OrderEitherVertex : public V3GraphVertex { - AstScope* m_scopep; // Scope the vertex is in - AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate) - bool m_isFromInput; // From input, or derived therefrom (conservatively false) + AstScope* m_scopep; // Scope the vertex is in + AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate) + bool m_isFromInput; // From input, or derived therefrom (conservatively false) protected: OrderEitherVertex(V3Graph* graphp, const OrderEitherVertex& old) - : V3GraphVertex(graphp, old), m_scopep(old.m_scopep), m_domainp(old.m_domainp) - , m_isFromInput(old.m_isFromInput) {} + : V3GraphVertex(graphp, old), m_scopep(old.m_scopep), m_domainp(old.m_domainp) + , m_isFromInput(old.m_isFromInput) {} public: OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp) - : V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp) - , m_isFromInput(false) {} + : V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp) + , m_isFromInput(false) {} virtual ~OrderEitherVertex() {} virtual OrderEitherVertex* clone(V3Graph* graphp) const = 0; // Methods virtual OrderVEdgeType type() const = 0; - virtual bool domainMatters() = 0; // Must be in same domain when cross edge to this vertex + virtual bool domainMatters() = 0; // Must be in same domain when cross edge to this vertex virtual string dotName() const { return cvtToHex(m_scopep)+"_"; } // ACCESSORS void domainp(AstSenTree* domainp) { m_domainp = domainp; } AstScope* scopep() const { return m_scopep; } AstSenTree* domainp() const { return m_domainp; } - void isFromInput(bool flag) { m_isFromInput=flag; } + void isFromInput(bool flag) { m_isFromInput = flag; } bool isFromInput() const { return m_isFromInput; } }; class OrderInputsVertex : public OrderEitherVertex { OrderInputsVertex(V3Graph* graphp, const OrderInputsVertex& old) - : OrderEitherVertex(graphp, old) {} + : OrderEitherVertex(graphp, old) {} public: OrderInputsVertex(V3Graph* graphp, AstSenTree* domainp) - : OrderEitherVertex(graphp, NULL, domainp) { - isFromInput(true); // By definition + : OrderEitherVertex(graphp, NULL, domainp) { + isFromInput(true); // By definition } virtual ~OrderInputsVertex() {} virtual OrderInputsVertex* clone(V3Graph* graphp) const { - return new OrderInputsVertex(graphp, *this); } + return new OrderInputsVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_INPUTS; } virtual string name() const { return "*INPUTS*"; } virtual string dotColor() const { return "green"; } @@ -177,13 +177,13 @@ public: class OrderSettleVertex : public OrderEitherVertex { OrderSettleVertex(V3Graph* graphp, const OrderSettleVertex& old) - : OrderEitherVertex(graphp, old) {} + : OrderEitherVertex(graphp, old) {} public: OrderSettleVertex(V3Graph* graphp, AstSenTree* domainp) - : OrderEitherVertex(graphp, NULL, domainp) {} + : OrderEitherVertex(graphp, NULL, domainp) {} virtual ~OrderSettleVertex() {} virtual OrderSettleVertex* clone(V3Graph* graphp) const { - return new OrderSettleVertex(graphp, *this); } + return new OrderSettleVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_SETTLE; } virtual string name() const { return "*SETTLE*"; } virtual string dotColor() const { return "green"; } @@ -192,7 +192,7 @@ public: }; class OrderLogicVertex : public OrderEitherVertex { - AstNode* m_nodep; + AstNode* m_nodep; protected: OrderLogicVertex(V3Graph* graphp, const OrderLogicVertex& old) : OrderEitherVertex(graphp, old), m_nodep(old.m_nodep) {} @@ -201,27 +201,28 @@ public: : OrderEitherVertex(graphp, scopep, domainp), m_nodep(nodep) {} virtual ~OrderLogicVertex() {} virtual OrderLogicVertex* clone(V3Graph* graphp) const { - return new OrderLogicVertex(graphp, *this); } + return new OrderLogicVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_LOGIC; } virtual bool domainMatters() { return true; } // ACCESSORS - virtual string name() const { return (cvtToHex(m_nodep)+"\\n "+cvtToStr(nodep()->typeName())); } + virtual string name() const { + return (cvtToHex(m_nodep)+"\\n "+cvtToStr(nodep()->typeName())); } AstNode* nodep() const { return m_nodep; } virtual string dotColor() const { return "yellow"; } }; class OrderVarVertex : public OrderEitherVertex { AstVarScope* m_varScp; - bool m_isClock; // Used as clock - bool m_isDelayed; // Set in a delayed assignment + bool m_isClock; // Used as clock + bool m_isDelayed; // Set in a delayed assignment protected: OrderVarVertex(V3Graph* graphp, const OrderVarVertex& old) - : OrderEitherVertex(graphp, old) + : OrderEitherVertex(graphp, old) , m_varScp(old.m_varScp), m_isClock(old.m_isClock) - , m_isDelayed(old.m_isDelayed) {} + , m_isDelayed(old.m_isDelayed) {} public: OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp) + : OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp) , m_isClock(false), m_isDelayed(false) {} virtual ~OrderVarVertex() {} virtual OrderVarVertex* clone(V3Graph* graphp) const = 0; @@ -229,21 +230,21 @@ public: virtual FileLine* fileline() const { return varScp()->fileline(); } // ACCESSORS AstVarScope* varScp() const { return m_varScp; } - void isClock(bool flag) { m_isClock=flag; } + void isClock(bool flag) { m_isClock = flag; } bool isClock() const { return m_isClock; } - void isDelayed(bool flag) { m_isDelayed=flag; } + void isDelayed(bool flag) { m_isDelayed = flag; } bool isDelayed() const { return m_isDelayed; } }; class OrderVarStdVertex : public OrderVarVertex { OrderVarStdVertex(V3Graph* graphp, const OrderVarStdVertex& old) - : OrderVarVertex(graphp, old) {} + : OrderVarVertex(graphp, old) {} public: OrderVarStdVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : OrderVarVertex(graphp, scopep, varScp) {} + : OrderVarVertex(graphp, scopep, varScp) {} virtual ~OrderVarStdVertex() {} virtual OrderVarStdVertex* clone(V3Graph* graphp) const { - return new OrderVarStdVertex(graphp, *this); } + return new OrderVarStdVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARSTD; } virtual string name() const { return (cvtToHex(varScp())+"\\n "+varScp()->name());} virtual string dotColor() const { return "skyblue"; } @@ -251,13 +252,13 @@ public: }; class OrderVarPreVertex : public OrderVarVertex { OrderVarPreVertex(V3Graph* graphp, const OrderVarPreVertex& old) - : OrderVarVertex(graphp, old) {} + : OrderVarVertex(graphp, old) {} public: OrderVarPreVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : OrderVarVertex(graphp, scopep,varScp) {} + : OrderVarVertex(graphp, scopep, varScp) {} virtual ~OrderVarPreVertex() {} virtual OrderVarPreVertex* clone(V3Graph* graphp) const { - return new OrderVarPreVertex(graphp, *this); } + return new OrderVarPreVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPRE; } virtual string name() const { return (cvtToHex(varScp())+" PRE\\n "+varScp()->name());} virtual string dotColor() const { return "lightblue"; } @@ -265,12 +266,12 @@ public: }; class OrderVarPostVertex : public OrderVarVertex { OrderVarPostVertex(V3Graph* graphp, const OrderVarPostVertex& old) - : OrderVarVertex(graphp, old) {} + : OrderVarVertex(graphp, old) {} public: OrderVarPostVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : OrderVarVertex(graphp, scopep,varScp) {} + : OrderVarVertex(graphp, scopep, varScp) {} virtual OrderVarPostVertex* clone(V3Graph* graphp) const { - return new OrderVarPostVertex(graphp, *this); } + return new OrderVarPostVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPOST; } virtual ~OrderVarPostVertex() {} virtual string name() const { return (cvtToHex(varScp())+" POST\\n "+varScp()->name());} @@ -279,13 +280,13 @@ public: }; class OrderVarPordVertex : public OrderVarVertex { OrderVarPordVertex(V3Graph* graphp, const OrderVarPordVertex& old) - : OrderVarVertex(graphp, old) {} + : OrderVarVertex(graphp, old) {} public: OrderVarPordVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : OrderVarVertex(graphp, scopep,varScp) {} + : OrderVarVertex(graphp, scopep, varScp) {} virtual ~OrderVarPordVertex() {} virtual OrderVarPordVertex* clone(V3Graph* graphp) const { - return new OrderVarPordVertex(graphp, *this); } + return new OrderVarPordVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARPORD; } virtual string name() const { return (cvtToHex(varScp())+" PORD\\n "+varScp()->name());} virtual string dotColor() const { return "NavyBlue"; } @@ -293,13 +294,13 @@ public: }; class OrderVarSettleVertex : public OrderVarVertex { OrderVarSettleVertex(V3Graph* graphp, const OrderVarSettleVertex& old) - : OrderVarVertex(graphp, old) {} + : OrderVarVertex(graphp, old) {} public: OrderVarSettleVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : OrderVarVertex(graphp, scopep,varScp) {} + : OrderVarVertex(graphp, scopep, varScp) {} virtual ~OrderVarSettleVertex() {} virtual OrderVarSettleVertex* clone(V3Graph* graphp) const { - return new OrderVarSettleVertex(graphp, *this); } + return new OrderVarSettleVertex(graphp, *this); } virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_VARSETTLE; } virtual string name() const { return (cvtToHex(varScp())+" STL\\n "+varScp()->name());} virtual string dotColor() const { return "PowderBlue"; } @@ -312,17 +313,17 @@ public: class OrderMoveVertex : public V3GraphVertex { typedef enum {POM_WAIT, POM_READY, POM_MOVED} OrderMState; - OrderLogicVertex* m_logicp; - OrderMState m_state; // Movement state - OrderMoveDomScope* m_domScopep; // Domain/scope list information + OrderLogicVertex* m_logicp; + OrderMState m_state; // Movement state + OrderMoveDomScope* m_domScopep; // Domain/scope list information protected: friend class OrderVisitor; friend class OrderMoveVertexMaker; // These only contain the "next" item, // for the head of the list, see the same var name under OrderVisitor - V3ListEnt m_pomWaitingE; // List of nodes needing inputs to become ready - V3ListEnt m_readyVerticesE;// List of ready under domain/scope + V3ListEnt m_pomWaitingE; // List of nodes needing inputs to become ready + V3ListEnt m_readyVerticesE; // List of ready under domain/scope public: // CONSTRUCTORS OrderMoveVertex(V3Graph* graphp, OrderLogicVertex* logicp) @@ -356,15 +357,15 @@ public: bool isWait() const { return m_state==POM_WAIT; } void setReady() { UASSERT(m_state==POM_WAIT, "Wait->Ready on node not in proper state"); - m_state = POM_READY; + m_state = POM_READY; } void setMoved() { UASSERT(m_state==POM_READY, "Ready->Moved on node not in proper state"); - m_state = POM_MOVED; + m_state = POM_MOVED; } OrderMoveDomScope* domScopep() const { return m_domScopep; } OrderMoveVertex* pomWaitingNextp() const { return m_pomWaitingE.nextp(); } - void domScopep(OrderMoveDomScope* ds) { m_domScopep=ds; } + void domScopep(OrderMoveDomScope* ds) { m_domScopep = ds; } }; // Similar to OrderMoveVertex, but modified for threaded code generation. @@ -424,29 +425,30 @@ public: class OrderEdge : public V3GraphEdge { protected: OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - const OrderEdge& old) - : V3GraphEdge(graphp, fromp, top, old) {} + const OrderEdge& old) + : V3GraphEdge(graphp, fromp, top, old) {} public: OrderEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - int weight, bool cutable=false) - : V3GraphEdge(graphp, fromp, top, weight, cutable) {} + int weight, bool cutable=false) + : V3GraphEdge(graphp, fromp, top, weight, cutable) {} virtual ~OrderEdge() {} virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_STD; } virtual OrderEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { - return new OrderEdge(graphp, fromp, top, *this); + return new OrderEdge(graphp, fromp, top, *this); } - // When ordering combo blocks with stronglyConnected, follow edges not involving pre/pos variables + // When ordering combo blocks with stronglyConnected, follow edges not + // involving pre/pos variables virtual bool followComboConnected() const { return true; } virtual bool followSequentConnected() const { return true; } static bool followComboConnected(const V3GraphEdge* edgep) { - const OrderEdge* oedgep = dynamic_cast(edgep); - if (!oedgep) v3fatalSrc("Following edge of non-OrderEdge type"); - return (oedgep->followComboConnected()); + const OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-OrderEdge type"); + return (oedgep->followComboConnected()); } static bool followSequentConnected(const V3GraphEdge* edgep) { - const OrderEdge* oedgep = dynamic_cast(edgep); - if (!oedgep) v3fatalSrc("Following edge of non-OrderEdge type"); - return (oedgep->followSequentConnected()); + const OrderEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-OrderEdge type"); + return (oedgep->followSequentConnected()); } }; @@ -455,15 +457,16 @@ class OrderComboCutEdge : public OrderEdge { // Breakable if the output var is also a input, // in which case we'll need a change detect loop around this var. OrderComboCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - const OrderComboCutEdge& old) - : OrderEdge(graphp, fromp, top, old) {} + const OrderComboCutEdge& old) + : OrderEdge(graphp, fromp, top, old) {} public: OrderComboCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {} + : OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {} virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_COMBOCUT; } virtual ~OrderComboCutEdge() {} - virtual OrderComboCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { - return new OrderComboCutEdge(graphp, fromp, top, *this); + virtual OrderComboCutEdge* clone(V3Graph* graphp, + V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderComboCutEdge(graphp, fromp, top, *this); } virtual string dotColor() const { return "yellowGreen"; } virtual bool followComboConnected() const { return true; } @@ -475,15 +478,16 @@ class OrderPostCutEdge : public OrderEdge { // Breakable if the output var feeds back to input combo logic or another clock pin // in which case we'll need a change detect loop around this var. OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - const OrderPostCutEdge& old) - : OrderEdge(graphp, fromp, top, old) {} + const OrderPostCutEdge& old) + : OrderEdge(graphp, fromp, top, old) {} public: OrderPostCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {} + : OrderEdge(graphp, fromp, top, WEIGHT_COMBO, CUTABLE) {} virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_POSTCUT; } virtual ~OrderPostCutEdge() {} - virtual OrderPostCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { - return new OrderPostCutEdge(graphp, fromp, top, *this); + virtual OrderPostCutEdge* clone(V3Graph* graphp, + V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderPostCutEdge(graphp, fromp, top, *this); } virtual string dotColor() const { return "PaleGreen"; } virtual bool followComboConnected() const { return false; } @@ -495,14 +499,15 @@ class OrderPreCutEdge : public OrderEdge { // Always breakable, just results in performance loss // in which case we can't optimize away the pre/post delayed assignments OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - const OrderPreCutEdge& old) - : OrderEdge(graphp, fromp, top, old) {} + const OrderPreCutEdge& old) + : OrderEdge(graphp, fromp, top, old) {} public: OrderPreCutEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : OrderEdge(graphp, fromp, top, WEIGHT_PRE, CUTABLE) {} + : OrderEdge(graphp, fromp, top, WEIGHT_PRE, CUTABLE) {} virtual OrderVEdgeType type() const { return OrderVEdgeType::EDGE_PRECUT; } - virtual OrderPreCutEdge* clone(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) const { - return new OrderPreCutEdge(graphp, fromp, top, *this); + virtual OrderPreCutEdge* clone(V3Graph* graphp, + V3GraphVertex* fromp, V3GraphVertex* top) const { + return new OrderPreCutEdge(graphp, fromp, top, *this); } virtual ~OrderPreCutEdge() {} virtual string dotColor() const { return "khaki"; } diff --git a/src/V3Os.cpp b/src/V3Os.cpp index 511ace89c..97350aad4 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -46,20 +46,20 @@ string V3Os::getenvStr(const string& envvar, const string& defaultValue) { if (const char* envvalue = getenv(envvar.c_str())) { - return envvalue; + return envvalue; } else { - return defaultValue; + return defaultValue; } } void V3Os::setenvStr(const string& envvar, const string& value, const string& why) { if (why != "") { - UINFO(1,"export "<d_name, regexp.c_str())) { - string fullname = dir + "/" + string(direntp->d_name); + while (struct dirent* direntp = readdir(dirp)) { + if (VString::wildmatch(direntp->d_name, regexp.c_str())) { + string fullname = dir + "/" + string(direntp->d_name); unlink(fullname.c_str()); - } - } - closedir(dirp); + } + } + closedir(dirp); } } @@ -236,16 +237,16 @@ uint64_t V3Os::memUsageBytes() { #else // Highly unportable. Sorry const char* const statmFilename = "/proc/self/statm"; - FILE* fp = fopen(statmFilename,"r"); + FILE* fp = fopen(statmFilename, "r"); if (!fp) { - return 0; + return 0; } vluint64_t size, resident, share, text, lib, data, dt; // All in pages if (7 != fscanf(fp, "%" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u %" VL_PRI64 "u", - &size, &resident, &share, &text, &lib, &data, &dt)) { - fclose(fp); - return 0; + &size, &resident, &share, &text, &lib, &data, &dt)) { + fclose(fp); + return 0; } fclose(fp); return (text + data) * getpagesize(); diff --git a/src/V3Os.h b/src/V3Os.h index eca8c815f..fbe5df9d2 100644 --- a/src/V3Os.h +++ b/src/V3Os.h @@ -58,7 +58,7 @@ public: // METHODS (performance) static uint64_t timeUsecs(); ///< Return wall time since epoch in microseconds, or 0 if not implemented - static uint64_t memUsageBytes(); ///< Return memory usage in bytes, or 0 if not implemented + static uint64_t memUsageBytes(); ///< Return memory usage in bytes, or 0 if not implemented }; -#endif // Guard +#endif // Guard diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 69564e00c..61633dc1a 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -19,29 +19,29 @@ //************************************************************************* // PARAM TRANSFORMATIONS: // Top down traversal: -// For each cell: -// If parameterized, -// Determine all parameter widths, constant values. -// (Interfaces also matter, as if an interface is parameterized -// this effectively changes the width behavior of all that -// reference the iface.) -// Clone module cell calls, renaming with __{par1}_{par2}_... -// Substitute constants for cell's module's parameters. -// Relink pins and cell and ifacerefdtype to point to new module. +// For each cell: +// If parameterized, +// Determine all parameter widths, constant values. +// (Interfaces also matter, as if an interface is parameterized +// this effectively changes the width behavior of all that +// reference the iface.) +// Clone module cell calls, renaming with __{par1}_{par2}_... +// Substitute constants for cell's module's parameters. +// Relink pins and cell and ifacerefdtype to point to new module. // -// For interface Parent's we have the AstIfaceRefDType::cellp() -// pointing to this module. If that parent cell's interface -// module gets parameterized, AstIfaceRefDType::cloneRelink +// For interface Parent's we have the AstIfaceRefDType::cellp() +// pointing to this module. If that parent cell's interface +// module gets parameterized, AstIfaceRefDType::cloneRelink // will update AstIfaceRefDType::cellp(), and V3LinkDot will -// see the new interface. +// see the new interface. // -// However if a submodule's AstIfaceRefDType::ifacep() points -// to the old (unparameterized) interface and needs correction. -// To detect this we must walk all pins looking for interfaces -// that the parent has changed and propagate down. +// However if a submodule's AstIfaceRefDType::ifacep() points +// to the old (unparameterized) interface and needs correction. +// To detect this we must walk all pins looking for interfaces +// that the parent has changed and propagate down. // -// Then process all modules called by that cell. -// (Cells never referenced after parameters expanded must be ignored.) +// Then process all modules called by that cell. +// (Cells never referenced after parameters expanded must be ignored.) // // After we complete parameters, the varp's will be wrong (point to old module) // and must be relinked. @@ -71,13 +71,14 @@ class ParamVisitor : public AstNVisitor { private: // NODE STATE - // AstNodeModule::user5() // bool True if processed - // AstGenFor::user5() // bool True if processed - // AstVar::user5() // bool True if constant propagated - // AstVar::user4() // int Global parameter number (for naming new module) - // // (0=not processed, 1=iterated, but no number, 65+ parameter numbered) - AstUser4InUse m_inuser4; - AstUser5InUse m_inuser5; + // AstNodeModule::user5() // bool True if processed + // AstGenFor::user5() // bool True if processed + // AstVar::user5() // bool True if constant propagated + // AstVar::user4() // int Global parameter number (for naming new module) + // // (0=not processed, 1=iterated, but no number, + // // 65+ parameter numbered) + AstUser4InUse m_inuser4; + AstUser5InUse m_inuser5; // User1/2/3 used by constant function simulations // TYPES @@ -86,16 +87,16 @@ private: // STATE typedef std::map CloneMap; struct ModInfo { - AstNodeModule* m_modp; // Module with specified name - CloneMap m_cloneMap; // Map of old-varp -> new cloned varp - explicit ModInfo(AstNodeModule* modp) { m_modp=modp; } + AstNodeModule* m_modp; // Module with specified name + CloneMap m_cloneMap; // Map of old-varp -> new cloned varp + explicit ModInfo(AstNodeModule* modp) { m_modp = modp; } }; typedef std::map ModNameMap; - ModNameMap m_modNameMap; // Hash of created module flavors by name + ModNameMap m_modNameMap; // Hash of created module flavors by name typedef std::map LongMap; - LongMap m_longMap; // Hash of very long names to unique identity number - int m_longId; + LongMap m_longMap; // Hash of very long names to unique identity number + int m_longId; typedef std::pair ValueMapValue; typedef std::map ValueMap; @@ -103,14 +104,14 @@ private: int m_nextValue; // Next value to use in m_valueMap typedef std::multimap LevelModMap; - LevelModMap m_todoModps; // Modules left to process + LevelModMap m_todoModps; // Modules left to process typedef std::deque CellList; - CellList m_cellps; // Cells left to process (in this module) + CellList m_cellps; // Cells left to process (in this module) AstNodeModule* m_modp; // Current module being processed - string m_unlinkedTxt; // Text for AstUnlinkedRef + string m_unlinkedTxt; // Text for AstUnlinkedRef UnrollStateful m_unroller; // Loop unroller @@ -119,34 +120,34 @@ private: void makeSmallNames(AstNodeModule* modp) { std::vector usedLetter; usedLetter.resize(256); - // Pass 1, assign first letter to each gparam's name - for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) { + // Pass 1, assign first letter to each gparam's name + for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* varp = VN_CAST(stmtp, Var)) { - if (varp->isGParam()||varp->isIfaceRef()) { - char ch = varp->name()[0]; - ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z'; - varp->user4(usedLetter[static_cast(ch)]*256 + ch); - usedLetter[static_cast(ch)]++; - } + if (varp->isGParam()||varp->isIfaceRef()) { + char ch = varp->name()[0]; + ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z'; + varp->user4(usedLetter[static_cast(ch)]*256 + ch); + usedLetter[static_cast(ch)]++; + } } else if (AstParamTypeDType* typep = VN_CAST(stmtp, ParamTypeDType)) { - char ch = 'T'; - typep->user4(usedLetter[static_cast(ch)]*256 + ch); - usedLetter[static_cast(ch)]++; - } - } + char ch = 'T'; + typep->user4(usedLetter[static_cast(ch)]*256 + ch); + usedLetter[static_cast(ch)]++; + } + } } string paramSmallName(AstNodeModule* modp, AstNode* varp) { - if (varp->user4()<=1) { - makeSmallNames(modp); - } - int index = varp->user4()/256; - char ch = varp->user4()&255; - string st = cvtToStr(ch); - while (index) { - st += cvtToStr(char((index%25)+'A')); - index /= 26; - } - return st; + if (varp->user4()<=1) { + makeSmallNames(modp); + } + int index = varp->user4()/256; + char ch = varp->user4()&255; + string st = cvtToStr(ch); + while (index) { + st += cvtToStr(char((index%25)+'A')); + index /= 26; + } + return st; } string paramValueNumber(AstNode* nodep) { string key = nodep->name(); @@ -155,7 +156,7 @@ private: key = ifrtp->cellp()->modp()->name(); } else if (ifrtp->ifacep()) { key = ifrtp->ifacep()->name(); - } else { + } else { nodep->v3fatalSrc("Can't parameterize interface without module name"); } } else if (AstBasicDType* bdtp = VN_CAST(nodep, BasicDType)) { @@ -179,103 +180,103 @@ private: return string("z")+cvtToStr(num); } void collectPins(CloneMap* clonemapp, AstNodeModule* modp) { - // Grab all I/O so we can remap our pins later - for (AstNode* stmtp=modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + // Grab all I/O so we can remap our pins later + for (AstNode* stmtp=modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { if (AstVar* varp = VN_CAST(stmtp, Var)) { - if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) { - // Cloning saved a pointer to the new node for us, so just follow that link. - AstVar* oldvarp = varp->clonep(); - //UINFO(8,"Clone list 0x"< 0x"<<(uint32_t)varp<insert(make_pair(oldvarp, varp)); - } - } + if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) { + // Cloning saved a pointer to the new node for us, so just follow that link. + AstVar* oldvarp = varp->clonep(); + //UINFO(8,"Clone list 0x"< 0x"<<(uint32_t)varp<insert(make_pair(oldvarp, varp)); + } + } else if (AstParamTypeDType* ptp = VN_CAST(stmtp, ParamTypeDType)) { - if (ptp->isGParam()) { - AstParamTypeDType* oldptp = ptp->clonep(); - clonemapp->insert(make_pair(oldptp, ptp)); - } - } - } + if (ptp->isGParam()) { + AstParamTypeDType* oldptp = ptp->clonep(); + clonemapp->insert(make_pair(oldptp, ptp)); + } + } + } } void relinkPins(CloneMap* clonemapp, AstPin* startpinp) { for (AstPin* pinp = startpinp; pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - if (pinp->modVarp()) { - // Find it in the clone structure - //UINFO(8,"Clone find 0x"<modVarp()<find(pinp->modVarp()); + if (pinp->modVarp()) { + // Find it in the clone structure + //UINFO(8,"Clone find 0x"<modVarp()<find(pinp->modVarp()); if (cloneiter == clonemapp->end()) { pinp->v3fatalSrc("Couldn't find pin in clone list"); } pinp->modVarp(VN_CAST(cloneiter->second, Var)); - } - else if (pinp->modPTypep()) { - CloneMap::iterator cloneiter = clonemapp->find(pinp->modPTypep()); + } + else if (pinp->modPTypep()) { + CloneMap::iterator cloneiter = clonemapp->find(pinp->modPTypep()); if (cloneiter == clonemapp->end()) { pinp->v3fatalSrc("Couldn't find pin in clone list"); } pinp->modPTypep(VN_CAST(cloneiter->second, ParamTypeDType)); - } - else { - pinp->v3fatalSrc("Not linked?"); - } - } + } + else { + pinp->v3fatalSrc("Not linked?"); + } + } } void visitCell(AstCell* nodep); void visitModules() { - // Loop on all modules left to process - // Hitting a cell adds to the appropriate level of this level-sorted list, - // so since cells originally exist top->bottom we process in top->bottom order too. - while (!m_todoModps.empty()) { - LevelModMap::iterator it = m_todoModps.begin(); - AstNodeModule* nodep = it->second; - m_todoModps.erase(it); - if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again - m_modp = nodep; - UINFO(4," MOD "<bottom we process in top->bottom order too. + while (!m_todoModps.empty()) { + LevelModMap::iterator it = m_todoModps.begin(); + AstNodeModule* nodep = it->second; + m_todoModps.erase(it); + if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again + m_modp = nodep; + UINFO(4," MOD "<modp(), Iface)) || (nonIf==1 && !VN_IS(nodep->modp(), Iface))) { - visitCell(nodep); - } - } - } - m_cellps.clear(); - m_modp = NULL; - } - } + visitCell(nodep); + } + } + } + m_cellps.clear(); + m_modp = NULL; + } + } } // VISITORS virtual void visit(AstNetlist* nodep) { - // Modules must be done in top-down-order + // Modules must be done in top-down-order iterateChildren(nodep); } virtual void visit(AstNodeModule* nodep) { - if (nodep->dead()) { - UINFO(4," MOD-dead. "<recursiveClone()) { - UINFO(4," MOD-recursive-dead. "<dead(true); // So Dead checks won't count references to it - } else if (nodep->level() <= 2 // Haven't added top yet, so level 2 is the top + if (nodep->dead()) { + UINFO(4," MOD-dead. "<recursiveClone()) { + UINFO(4," MOD-recursive-dead. "<dead(true); // So Dead checks won't count references to it + } else if (nodep->level() <= 2 // Haven't added top yet, so level 2 is the top || VN_IS(nodep, Package)) { // Likewise haven't done wrapTopPackages yet - // Add request to END of modules left to process - m_todoModps.insert(make_pair(nodep->level(), nodep)); - visitModules(); - } else if (nodep->user5()) { - UINFO(4," MOD-done "<level(), nodep)); + visitModules(); + } else if (nodep->user5()) { + UINFO(4," MOD-done "<valuep(), Const)) { // Complex init, like an array // Make a new INITIAL to set the value. // This allows the normal array/struct handling code to properly - // initialize the parameter. + // initialize the parameter. nodep->addNext( new AstInitial(nodep->fileline(), new AstAssign( @@ -309,138 +310,142 @@ private: if (nodep->varp()) iterate(nodep->varp()); } bool ifaceParamReplace(AstVarXRef* nodep, AstNode* candp) { - for (; candp; candp = candp->nextp()) { - if (nodep->name() == candp->name()) { + for (; candp; candp = candp->nextp()) { + if (nodep->name() == candp->name()) { if (AstVar* varp = VN_CAST(candp, Var)) { - UINFO(9,"Found interface parameter: "<varp(varp); - return true; + UINFO(9,"Found interface parameter: "<varp(varp); + return true; } else if (AstPin* pinp = VN_CAST(candp, Pin)) { - UINFO(9,"Found interface parameter: "<exprp()) pinp->v3fatalSrc("Interface parameter pin missing expression"); - nodep->replaceWith(pinp->exprp()->cloneTree(false)); VL_DANGLING(nodep); - return true; - } - } - } - return false; + UINFO(9,"Found interface parameter: "<exprp()) pinp->v3fatalSrc("Interface parameter pin missing expression"); + nodep->replaceWith(pinp->exprp()->cloneTree(false)); VL_DANGLING(nodep); + return true; + } + } + } + return false; } virtual void visit(AstVarXRef* nodep) { - // Check to see if the scope is just an interface because interfaces are special - string dotted = nodep->dotted(); - if (!dotted.empty() && nodep->varp() && nodep->varp()->isParam()) { - AstNode* backp = nodep; - while ((backp = backp->backp())) { + // Check to see if the scope is just an interface because interfaces are special + string dotted = nodep->dotted(); + if (!dotted.empty() && nodep->varp() && nodep->varp()->isParam()) { + AstNode* backp = nodep; + while ((backp = backp->backp())) { if (VN_IS(backp, NodeModule)) { - UINFO(9,"Hit module boundary, done looking for interface"<isIfaceRef() && VN_CAST(backp, Var)->childDTypep() && VN_CAST(VN_CAST(backp, Var)->childDTypep(), IfaceRefDType)) { - AstIfaceRefDType* ifacerefp = VN_CAST(VN_CAST(backp, Var)->childDTypep(), IfaceRefDType); - // Interfaces passed in on the port map have ifaces - if (AstIface* ifacep = ifacerefp->ifacep()) { - if (dotted == backp->name()) { - UINFO(9,"Iface matching scope: "<stmtsp())) { - return; - } - } - } - // Interfaces declared in this module have cells - else if (AstCell* cellp = ifacerefp->cellp()) { - if (dotted == cellp->name()) { - UINFO(9,"Iface matching scope: "<paramsp())) { - return; - } - } - } - } - } - } - nodep->varp(NULL); // Needs relink, as may remove pointed-to var + AstIfaceRefDType* ifacerefp + = VN_CAST(VN_CAST(backp, Var)->childDTypep(), IfaceRefDType); + // Interfaces passed in on the port map have ifaces + if (AstIface* ifacep = ifacerefp->ifacep()) { + if (dotted == backp->name()) { + UINFO(9,"Iface matching scope: "<stmtsp())) { + return; + } + } + } + // Interfaces declared in this module have cells + else if (AstCell* cellp = ifacerefp->cellp()) { + if (dotted == cellp->name()) { + UINFO(9,"Iface matching scope: "<paramsp())) { + return; + } + } + } + } + } + } + nodep->varp(NULL); // Needs relink, as may remove pointed-to var } virtual void visit(AstUnlinkedRef* nodep) { AstVarXRef* varxrefp = VN_CAST(nodep->op1p(), VarXRef); AstNodeFTaskRef* taskrefp = VN_CAST(nodep->op1p(), NodeFTaskRef); - if (varxrefp) { - m_unlinkedTxt = varxrefp->dotted(); - } else if (taskrefp) { - m_unlinkedTxt = taskrefp->dotted(); - } else { - nodep->v3fatalSrc("Unexpected AstUnlinkedRef node"); - return; - } + if (varxrefp) { + m_unlinkedTxt = varxrefp->dotted(); + } else if (taskrefp) { + m_unlinkedTxt = taskrefp->dotted(); + } else { + nodep->v3fatalSrc("Unexpected AstUnlinkedRef node"); + return; + } iterate(nodep->cellrefp()); - if (varxrefp) { - varxrefp->dotted(m_unlinkedTxt); - } else { - taskrefp->dotted(m_unlinkedTxt); - } - nodep->replaceWith(nodep->op1p()->unlinkFrBack()); - pushDeletep(nodep); VL_DANGLING(nodep); + if (varxrefp) { + varxrefp->dotted(m_unlinkedTxt); + } else { + taskrefp->dotted(m_unlinkedTxt); + } + nodep->replaceWith(nodep->op1p()->unlinkFrBack()); + pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstCellArrayRef* nodep) { - V3Const::constifyParamsEdit(nodep->selp()); + V3Const::constifyParamsEdit(nodep->selp()); if (const AstConst* constp = VN_CAST(nodep->selp(), Const)) { - string index = AstNode::encodeNumber(constp->toSInt()); - string replacestr = nodep->name() + "__BRA__??__KET__"; - size_t pos = m_unlinkedTxt.find(replacestr); - if (pos == string::npos) { - nodep->v3error("Could not find array index in unlinked text: '" << m_unlinkedTxt << "' for node: " << nodep); - return; - } - m_unlinkedTxt.replace(pos, replacestr.length(), nodep->name() + "__BRA__"+index+"__KET__"); - } else { - nodep->v3error("Could not expand constant selection inside dotted reference: "<selp()->prettyName()); - return; - } + string index = AstNode::encodeNumber(constp->toSInt()); + string replacestr = nodep->name() + "__BRA__??__KET__"; + size_t pos = m_unlinkedTxt.find(replacestr); + if (pos == string::npos) { + nodep->v3error("Could not find array index in unlinked text: '" + <name()+"__BRA__"+index+"__KET__"); + } else { + nodep->v3error("Could not expand constant selection inside dotted reference: " + <selp()->prettyName()); + return; + } } // Generate Statements virtual void visit(AstGenerate* nodep) { - if (debug()>=9) nodep->dumpTree(cout,"-genin: "); + if (debug()>=9) nodep->dumpTree(cout, "-genin: "); iterateChildren(nodep); - // After expanding the generate, all statements under it can be moved - // up, and the generate block deleted as it's not relevant - if (AstNode* stmtsp = nodep->stmtsp()) { - stmtsp->unlinkFrBackWithNext(); - nodep->replaceWith(stmtsp); - if (debug()>=9) stmtsp->dumpTree(cout,"-genout: "); - } else { - nodep->unlinkFrBack(); - } - nodep->deleteTree(); VL_DANGLING(nodep); + // After expanding the generate, all statements under it can be moved + // up, and the generate block deleted as it's not relevant + if (AstNode* stmtsp = nodep->stmtsp()) { + stmtsp->unlinkFrBackWithNext(); + nodep->replaceWith(stmtsp); + if (debug()>=9) stmtsp->dumpTree(cout, "-genout: "); + } else { + nodep->unlinkFrBack(); + } + nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstGenIf* nodep) { - UINFO(9," GENIF "<condp()); - // We suppress errors when widthing params since short-circuiting in - // the conditional evaluation may mean these error can never occur. We - // then make sure that short-circuiting is used by constifyParamsEdit. - V3Width::widthGenerateParamsEdit(nodep); // Param typed widthing will - // NOT recurse the body. - V3Const::constifyGenerateParamsEdit(nodep->condp()); // condp may change + // We suppress errors when widthing params since short-circuiting in + // the conditional evaluation may mean these error can never occur. We + // then make sure that short-circuiting is used by constifyParamsEdit. + V3Width::widthGenerateParamsEdit(nodep); // Param typed widthing will + // NOT recurse the body. + V3Const::constifyGenerateParamsEdit(nodep->condp()); // condp may change if (const AstConst* constp = VN_CAST(nodep->condp(), Const)) { - AstNode* keepp = (constp->isZero() - ? nodep->elsesp() - : nodep->ifsp()); - if (keepp) { - keepp->unlinkFrBackWithNext(); - nodep->replaceWith(keepp); - } else { - nodep->unlinkFrBack(); - } - nodep->deleteTree(); VL_DANGLING(nodep); - // Normal edit rules will now recurse the replacement - } else { - nodep->condp()->v3error("Generate If condition must evaluate to constant"); - } + AstNode* keepp = (constp->isZero() + ? nodep->elsesp() + : nodep->ifsp()); + if (keepp) { + keepp->unlinkFrBackWithNext(); + nodep->replaceWith(keepp); + } else { + nodep->unlinkFrBack(); + } + nodep->deleteTree(); VL_DANGLING(nodep); + // Normal edit rules will now recurse the replacement + } else { + nodep->condp()->v3error("Generate If condition must evaluate to constant"); + } } //! Parameter subsitution for generated for loops. @@ -448,85 +453,88 @@ private: //! expression, since this is currently restricted to simple comparisons. If we ever do //! move to more generic constant expressions, such code will be needed here. virtual void visit(AstBegin* nodep) { - if (nodep->genforp()) { + if (nodep->genforp()) { AstGenFor* forp = VN_CAST(nodep->genforp(), GenFor); - if (!forp) nodep->v3fatalSrc("Non-GENFOR under generate-for BEGIN"); - // We should have a GENFOR under here. We will be replacing the begin, - // so process here rather than at the generate to avoid iteration problems - UINFO(9," BEGIN "<name(); - // Leave the original Begin, as need a container for the (possible) GENVAR + if (!forp) nodep->v3fatalSrc("Non-GENFOR under generate-for BEGIN"); + // We should have a GENFOR under here. We will be replacing the begin, + // so process here rather than at the generate to avoid iteration problems + UINFO(9," BEGIN "<name(); + // Leave the original Begin, as need a container for the (possible) GENVAR // Note V3Unroll will replace some AstVarRef's to the loop variable with constants // Don't remove any deleted nodes in m_unroller until whole process finishes, // (are held in m_unroller), as some AstXRefs may still point to old nodes. m_unroller.unrollGen(forp, beginName); VL_DANGLING(forp); // Blocks were constructed under the special begin, move them up - // Note forp is null, so grab statements again - if (AstNode* stmtsp = nodep->genforp()) { - stmtsp->unlinkFrBackWithNext(); - nodep->addNextHere(stmtsp); - // Note this clears nodep->genforp(), so begin is no longer special - } - } else { + // Note forp is null, so grab statements again + if (AstNode* stmtsp = nodep->genforp()) { + stmtsp->unlinkFrBackWithNext(); + nodep->addNextHere(stmtsp); + // Note this clears nodep->genforp(), so begin is no longer special + } + } else { iterateChildren(nodep); - } + } } virtual void visit(AstGenFor* nodep) { - nodep->v3fatalSrc("GENFOR should have been wrapped in BEGIN"); + nodep->v3fatalSrc("GENFOR should have been wrapped in BEGIN"); } virtual void visit(AstGenCase* nodep) { - UINFO(9," GENCASE "<exprp()); - V3Case::caseLint(nodep); - V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the body, - // don't trigger errors yet. - V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change + V3Case::caseLint(nodep); + V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the body, + // don't trigger errors yet. + V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change AstConst* exprp = VN_CAST(nodep->exprp(), Const); - // Constify - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - for (AstNode* ep = itemp->condsp(); ep; ) { - AstNode* nextp = ep->nextp(); //May edit list + // Constify + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) { + for (AstNode* ep = itemp->condsp(); ep; ) { + AstNode* nextp = ep->nextp(); // May edit list iterateAndNextNull(ep); - V3Const::constifyParamsEdit(ep); VL_DANGLING(ep); // ep may change - ep = nextp; - } - } - // Item match - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - if (!itemp->isDefault()) { - for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) { + V3Const::constifyParamsEdit(ep); VL_DANGLING(ep); // ep may change + ep = nextp; + } + } + // Item match + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) { + if (!itemp->isDefault()) { + for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) { if (const AstConst* ccondp = VN_CAST(ep, Const)) { V3Number match (nodep, 1); match.opEq(ccondp->num(), exprp->num()); if (!keepp && match.isNeqZero()) { - keepp = itemp->bodysp(); - } - } else { - itemp->v3error("Generate Case item does not evaluate to constant"); - } - } - } - } - // Else default match - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - if (itemp->isDefault()) { - if (!keepp) keepp=itemp->bodysp(); - } - } - // Replace - if (keepp) { - keepp->unlinkFrBackWithNext(); - nodep->replaceWith(keepp); - } - else nodep->unlinkFrBack(); - nodep->deleteTree(); VL_DANGLING(nodep); + keepp = itemp->bodysp(); + } + } else { + itemp->v3error("Generate Case item does not evaluate to constant"); + } + } + } + } + // Else default match + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) { + if (itemp->isDefault()) { + if (!keepp) keepp = itemp->bodysp(); + } + } + // Replace + if (keepp) { + keepp->unlinkFrBackWithNext(); + nodep->replaceWith(keepp); + } + else nodep->unlinkFrBack(); + nodep->deleteTree(); VL_DANGLING(nodep); } // Default: Just iterate @@ -556,89 +564,95 @@ void ParamVisitor::visitCell(AstCell* nodep) { // We always run this, even if no parameters, as need to look for interfaces, // and remove any recursive references { - UINFO(4,"De-parameterize: "<=10) nodep->dumpTree(cout,"-cell:\t"); - // Evaluate all module constants - V3Const::constifyParamsEdit(nodep); - AstNodeModule* srcModp = nodep->modp(); + UINFO(4,"De-parameterize: "<=10) nodep->dumpTree(cout, "-cell:\t"); + // Evaluate all module constants + V3Const::constifyParamsEdit(nodep); + AstNodeModule* srcModp = nodep->modp(); - // Make sure constification worked - // Must be a separate loop, as constant conversion may have changed some pointers. - //if (debug()) nodep->dumpTree(cout,"-cel2:\t"); - string longname = srcModp->name(); - bool any_overrides = false; - if (nodep->recursive()) any_overrides = true; // Must always clone __Vrcm (recursive modules) - longname += "_"; - if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout,"-cellparams:\t"); + // Make sure constification worked + // Must be a separate loop, as constant conversion may have changed some pointers. + //if (debug()) nodep->dumpTree(cout, "-cel2:\t"); + string longname = srcModp->name(); + bool any_overrides = false; + if (nodep->recursive()) any_overrides = true; // Must always clone __Vrcm (recursive modules) + longname += "_"; + if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout, "-cellparams:\t"); for (AstPin* pinp = nodep->paramsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - if (!pinp->exprp()) continue; // No-connect - if (AstVar* modvarp = pinp->modVarp()) { - if (!modvarp->isGParam()) { - pinp->v3error("Attempted parameter setting of non-parameter: Param "<prettyName()<<" of "<prettyName()); + if (!pinp->exprp()) continue; // No-connect + if (AstVar* modvarp = pinp->modVarp()) { + if (!modvarp->isGParam()) { + pinp->v3error("Attempted parameter setting of non-parameter: Param " + <prettyName()<<" of "<prettyName()); } else if (VN_IS(pinp->exprp(), InitArray) && VN_IS(modvarp->subDTypep(), UnpackArrayDType)) { - // Array assigned to array - AstNode* exprp = pinp->exprp(); - longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp); - any_overrides = true; - } else { + // Array assigned to array + AstNode* exprp = pinp->exprp(); + longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp); + any_overrides = true; + } else { AstConst* exprp = VN_CAST(pinp->exprp(), Const); AstConst* origp = VN_CAST(modvarp->valuep(), Const); if (!exprp) { - //if (debug()) pinp->dumpTree(cout,"error:"); + //if (debug()) pinp->dumpTree(cout, "error:"); pinp->v3error("Can't convert defparam value to constant: Param " <name()<<" of "<prettyName()); pinp->exprp()->replaceWith( new AstConst(pinp->fileline(), V3Number(pinp, modvarp->width(), 0))); } else if (origp && exprp->sameTree(origp)) { - // Setting parameter to its default value. Just ignore it. - // This prevents making additional modules, and makes coverage more - // obvious as it won't show up under a unique module page name. - } else if (exprp->num().isDouble() - || exprp->num().isString() - || exprp->num().isFourState()) { - longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp); - any_overrides = true; - } else { - longname += "_" + paramSmallName(srcModp, modvarp) + exprp->num().ascii(false); - any_overrides = true; - } - } - } else if (AstParamTypeDType* modvarp = pinp->modPTypep()) { + // Setting parameter to its default value. Just ignore it. + // This prevents making additional modules, and makes coverage more + // obvious as it won't show up under a unique module page name. + } else if (exprp->num().isDouble() + || exprp->num().isString() + || exprp->num().isFourState()) { + longname += ("_" + paramSmallName(srcModp, modvarp) + + paramValueNumber(exprp)); + any_overrides = true; + } else { + longname += ("_" + paramSmallName(srcModp, modvarp) + + exprp->num().ascii(false)); + any_overrides = true; + } + } + } else if (AstParamTypeDType* modvarp = pinp->modPTypep()) { AstNodeDType* exprp = VN_CAST(pinp->exprp(), NodeDType); - AstNodeDType* origp = modvarp->subDTypep(); - if (!exprp) { - pinp->v3error("Parameter type pin value isn't a type: Param "<prettyName()<<" of "<prettyName()); - } else if (!origp) { - pinp->v3error("Parameter type variable isn't a type: Param "<prettyName()); - } else { - UINFO(9,"Parameter type assignment expr="<sameTree(origp)) { - // Setting parameter to its default value. Just ignore it. - // This prevents making additional modules, and makes coverage more - // obvious as it won't show up under a unique module page name. - } else { - longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp); - any_overrides = true; - } - } - } else { - pinp->v3error("Parameter not found in sub-module: Param "<prettyName()<<" of "<prettyName()); - } - } - IfaceRefRefs ifaceRefRefs; + AstNodeDType* origp = modvarp->subDTypep(); + if (!exprp) { + pinp->v3error("Parameter type pin value isn't a type: Param " + <prettyName()<<" of "<prettyName()); + } else if (!origp) { + pinp->v3error("Parameter type variable isn't a type: Param " + <prettyName()); + } else { + UINFO(9,"Parameter type assignment expr="<sameTree(origp)) { + // Setting parameter to its default value. Just ignore it. + // This prevents making additional modules, and makes coverage more + // obvious as it won't show up under a unique module page name. + } else { + longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp); + any_overrides = true; + } + } + } else { + pinp->v3error("Parameter not found in sub-module: Param " + <prettyName()<<" of "<prettyName()); + } + } + IfaceRefRefs ifaceRefRefs; for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - AstVar* modvarp = pinp->modVarp(); - if (modvarp->isIfaceRef()) { + AstVar* modvarp = pinp->modVarp(); + if (modvarp->isIfaceRef()) { AstIfaceRefDType* portIrefp = VN_CAST(modvarp->subDTypep(), IfaceRefDType); if (!portIrefp && VN_IS(modvarp->subDTypep(), UnpackArrayDType)) { portIrefp = VN_CAST(VN_CAST(modvarp->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType); - } + } - AstIfaceRefDType* pinIrefp = NULL; - AstNode* exprp = pinp->exprp(); - if (exprp + AstIfaceRefDType* pinIrefp = NULL; + AstNode* exprp = pinp->exprp(); + if (exprp && VN_IS(exprp, VarRef) && VN_CAST(exprp, VarRef)->varp() && VN_CAST(exprp, VarRef)->varp()->subDTypep() @@ -663,19 +677,22 @@ void ParamVisitor::visitCell(AstCell* nodep) { pinIrefp = VN_CAST(VN_CAST(VN_CAST(exprp, VarRef)->varp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType); } - UINFO(9," portIfaceRef "<v3error("Interface port '"<prettyName()<<"' is not an interface " << modvarp); - } else if (!pinIrefp) { - pinp->v3error("Interface port '"<prettyName()<<"' is not connected to interface/modport pin expression"); - } else { - UINFO(9," pinIfaceRef "<ifaceViaCellp() != pinIrefp->ifaceViaCellp()) { - UINFO(9," IfaceRefDType needs reconnect "<modVarp()) + paramValueNumber(pinIrefp); - any_overrides = true; - ifaceRefRefs.push_back(make_pair(portIrefp,pinIrefp)); + if (!portIrefp) { + pinp->v3error("Interface port '"<prettyName() + <<"' is not an interface " << modvarp); + } else if (!pinIrefp) { + pinp->v3error("Interface port '"<prettyName() + <<"' is not connected to interface/modport pin expression"); + } else { + UINFO(9," pinIfaceRef "<ifaceViaCellp() != pinIrefp->ifaceViaCellp()) { + UINFO(9," IfaceRefDType needs reconnect "<modVarp()) + + paramValueNumber(pinIrefp)); + any_overrides = true; + ifaceRefRefs.push_back(make_pair(portIrefp, pinIrefp)); if (portIrefp->ifacep() != pinIrefp->ifacep() // Might be different only due to param cloning, so check names too && portIrefp->ifaceName() != pinIrefp->ifaceName()) { @@ -684,125 +701,131 @@ void ParamVisitor::visitCell(AstCell* nodep) { <<"' interface but pin connects '" <ifaceName())<<"' interface"); } - } - } - } - } + } + } + } + } - if (!any_overrides) { - UINFO(8,"Cell parameters all match original values, skipping expansion.\n"); - } else { - // If the name is very long, we don't want to overwhelm the filename limit - // We don't do this always, as it aids debugability to have intuitive naming. - // TODO can use new V3Name hash replacement instead of this - string newname = longname; - if (longname.length()>30) { - LongMap::iterator iter = m_longMap.find(longname); - if (iter != m_longMap.end()) { - newname = iter->second; - } else { - newname = srcModp->name(); - newname += "__pi"+cvtToStr(++m_longId); // We use all upper case above, so lower here can't conflict - m_longMap.insert(make_pair(longname, newname)); - } - } - UINFO(4,"Name: "<name()<<"->"<"<30) { + LongMap::iterator iter = m_longMap.find(longname); + if (iter != m_longMap.end()) { + newname = iter->second; + } else { + newname = srcModp->name(); + newname += "__pi"+cvtToStr(++m_longId); // We use all upper case above, so lower here can't conflict + m_longMap.insert(make_pair(longname, newname)); + } + } + UINFO(4,"Name: "<name()<<"->"<"<second.m_modp; - if (!cellmodp) { - // Deep clone of new module - // Note all module internal variables will be re-linked to the new modules by clone - // However links outside the module (like on the upper cells) will not. - cellmodp = srcModp->cloneTree(false); - cellmodp->name(newname); - cellmodp->user5(false); // We need to re-recurse this module once changed - cellmodp->recursive(false); - cellmodp->recursiveClone(false); - nodep->recursive(false); - // Recursion may need level cleanups - if (cellmodp->level() <= m_modp->level()) cellmodp->level(m_modp->level()+1); - if ((cellmodp->level() - srcModp->level()) >= (v3Global.opt.moduleRecursionDepth() - 2)) { - nodep->v3error("Exceeded maximum --module-recursion-depth of "<second.m_modp; + if (!cellmodp) { + // Deep clone of new module + // Note all module internal variables will be re-linked to the new modules by clone + // However links outside the module (like on the upper cells) will not. + cellmodp = srcModp->cloneTree(false); + cellmodp->name(newname); + cellmodp->user5(false); // We need to re-recurse this module once changed + cellmodp->recursive(false); + cellmodp->recursiveClone(false); + nodep->recursive(false); + // Recursion may need level cleanups + if (cellmodp->level() <= m_modp->level()) cellmodp->level(m_modp->level()+1); + if ((cellmodp->level() - srcModp->level()) + >= (v3Global.opt.moduleRecursionDepth() - 2)) { + nodep->v3error("Exceeded maximum --module-recursion-depth of " + <nextp(), NodeModule) && VN_CAST(insertp->nextp(), NodeModule)->level() < cellmodp->level()) { insertp = VN_CAST(insertp->nextp(), NodeModule); - } - insertp->addNextHere(cellmodp); + } + insertp->addNextHere(cellmodp); - m_modNameMap.insert(make_pair(cellmodp->name(), ModInfo(cellmodp))); - iter = m_modNameMap.find(newname); - CloneMap* clonemapp = &(iter->second.m_cloneMap); - UINFO(4," De-parameterize to new: "<name(), ModInfo(cellmodp))); + iter = m_modNameMap.find(newname); + CloneMap* clonemapp = &(iter->second.m_cloneMap); + UINFO(4," De-parameterize to new: "<paramsp()); + // Grab all I/O so we can remap our pins later + // Note we allow multiple users of a parameterized model, + // thus we need to stash this info. + collectPins(clonemapp, cellmodp); + // Relink parameter vars to the new module + relinkPins(clonemapp, nodep->paramsp()); - // Fix any interface references - for (IfaceRefRefs::iterator it=ifaceRefRefs.begin(); it!=ifaceRefRefs.end(); ++it) { - AstIfaceRefDType* portIrefp = it->first; - AstIfaceRefDType* pinIrefp = it->second; - AstIfaceRefDType* cloneIrefp = portIrefp->clonep(); - UINFO(8," IfaceOld "<v3fatalSrc("parameter clone didn't hit AstIfaceRefDType"); - UINFO(8," IfaceClo "<ifacep(pinIrefp->ifaceViaCellp()); - UINFO(8," IfaceNew "<first; + AstIfaceRefDType* pinIrefp = it->second; + AstIfaceRefDType* cloneIrefp = portIrefp->clonep(); + UINFO(8," IfaceOld "<v3fatalSrc("parameter clone didn't hit AstIfaceRefDType"); + UINFO(8," IfaceClo "<ifacep(pinIrefp->ifaceViaCellp()); + UINFO(8," IfaceNew "<paramsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) { - if (pinp->exprp()) { - if (AstVar* modvarp = pinp->modVarp()) { - AstNode* newp = pinp->exprp(); // Const or InitArray - // Remove any existing parameter - if (modvarp->valuep()) modvarp->valuep()->unlinkFrBack()->deleteTree(); - // Set this parameter to value requested by cell - modvarp->valuep(newp->cloneTree(false)); - } - else if (AstParamTypeDType* modptp = pinp->modPTypep()) { + if (pinp->exprp()) { + if (AstVar* modvarp = pinp->modVarp()) { + AstNode* newp = pinp->exprp(); // Const or InitArray + // Remove any existing parameter + if (modvarp->valuep()) modvarp->valuep()->unlinkFrBack()->deleteTree(); + // Set this parameter to value requested by cell + modvarp->valuep(newp->cloneTree(false)); + } + else if (AstParamTypeDType* modptp = pinp->modPTypep()) { AstNodeDType* dtypep = VN_CAST(pinp->exprp(), NodeDType); - if (!dtypep) pinp->v3fatalSrc("unlinked param dtype"); - if (modptp->childDTypep()) pushDeletep(modptp->childDTypep()->unlinkFrBack()); - // Set this parameter to value requested by cell - modptp->childDTypep(dtypep->cloneTree(false)); - // Later V3LinkDot will convert the ParamDType to a Typedef - // Not done here as may be localparams, etc, that also need conversion - } - } - } + if (!dtypep) pinp->v3fatalSrc("unlinked param dtype"); + if (modptp->childDTypep()) { + pushDeletep(modptp->childDTypep()->unlinkFrBack()); + } + // Set this parameter to value requested by cell + modptp->childDTypep(dtypep->cloneTree(false)); + // Later V3LinkDot will convert the ParamDType to a Typedef + // Not done here as may be localparams, etc, that also need conversion + } + } + } - } else { - UINFO(4," De-parameterize to old: "<modp(cellmodp); - nodep->modName(newname); + // Have child use this module instead. + nodep->modp(cellmodp); + nodep->modName(newname); - // We need to relink the pins to the new module - CloneMap* clonemapp = &(iter->second.m_cloneMap); - relinkPins(clonemapp, nodep->pinsp()); - UINFO(8," Done with "<second.m_cloneMap); + relinkPins(clonemapp, nodep->pinsp()); + UINFO(8," Done with "<recursive(false); + nodep->recursive(false); - // Delete the parameters from the cell; they're not relevant any longer. - if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); - UINFO(8," Done with "<=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree")); + // Delete the parameters from the cell; they're not relevant any longer. + if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree(); + UINFO(8," Done with "<=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree")); } // Now remember to process the child module at the end of the module diff --git a/src/V3Param.h b/src/V3Param.h index bd370fff6..fb8bfd6ba 100644 --- a/src/V3Param.h +++ b/src/V3Param.h @@ -34,4 +34,4 @@ public: static void param(AstNetlist* rootp); }; -#endif // Guard +#endif // Guard diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index 4bf75d399..eff0e2835 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -18,12 +18,12 @@ // //************************************************************************* // Overview of files involved in parsing -// V3Parse.h External consumer interface to V3ParseImp -// V3ParseImp Internals to parser, common to across flex & bison -// V3ParseGrammar Wrapper that includes V3ParseBison -// V3ParseBison Bison output -// V3ParseLex Wrapper that includes lex output -// V3Lexer.yy.cpp Flex output +// V3Parse.h External consumer interface to V3ParseImp +// V3ParseImp Internals to parser, common to across flex & bison +// V3ParseGrammar Wrapper that includes V3ParseBison +// V3ParseBison Bison output +// V3ParseLex Wrapper that includes lex output +// V3Lexer.yy.cpp Flex output //************************************************************************* #include "config_build.h" @@ -44,20 +44,20 @@ //====================================================================== // Globals -V3ParseImp* V3ParseImp::s_parsep = NULL; +V3ParseImp* V3ParseImp::s_parsep = NULL; -int V3ParseSym::s_anonNum = 0; +int V3ParseSym::s_anonNum = 0; //###################################################################### // Read class functions V3ParseImp::~V3ParseImp() { for (std::deque::iterator it = m_stringps.begin(); it != m_stringps.end(); ++it) { - delete (*it); + delete (*it); } m_stringps.clear(); for (std::deque::iterator it = m_numberps.begin(); it != m_numberps.end(); ++it) { - delete (*it); + delete (*it); } m_numberps.clear(); lexDestroy(); @@ -68,22 +68,22 @@ V3ParseImp::~V3ParseImp() { size_t V3ParseImp::ppInputToLex(char* buf, size_t max_size) { size_t got = 0; - while (got < max_size // Haven't got enough - && !m_ppBuffers.empty()) { // And something buffered - string front = m_ppBuffers.front(); m_ppBuffers.pop_front(); - size_t len = front.length(); - if (len > (max_size-got)) { // Front string too big - string remainder = front.substr(max_size-got); - front = front.substr(0, max_size-got); - m_ppBuffers.push_front(remainder); // Put back remainder for next time - len = (max_size-got); - } - strncpy(buf+got, front.c_str(), len); - got += len; + while (got < max_size // Haven't got enough + && !m_ppBuffers.empty()) { // And something buffered + string front = m_ppBuffers.front(); m_ppBuffers.pop_front(); + size_t len = front.length(); + if (len > (max_size-got)) { // Front string too big + string remainder = front.substr(max_size-got); + front = front.substr(0, max_size-got); + m_ppBuffers.push_front(remainder); // Put back remainder for next time + len = (max_size-got); + } + strncpy(buf+got, front.c_str(), len); + got += len; } if (debug()>=9) { - string out = string(buf,got); - cout<<" inputToLex got="<begin(); its != it->end(); ++its) { - if (!isspace(*its) && *its!='\n') { blank=false; break; } + if (!isspace(*its) && *its!='\n') { blank = false; break; } } if (blank) continue; } @@ -108,7 +108,7 @@ void V3ParseImp::preprocDumps(std::ostream& os) { } void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool inLibrary, - const string& errmsg) { // "" for no error, make fake node + const string& errmsg) { // "" for no error, make fake node string modname = V3Os::filenameNonExt(modfilename); UINFO(2,__FUNCTION__<<": "<addModulep(nodep); - return; + if (errmsg != "") return; // Threw error already + // Create fake node for later error reporting + AstNodeModule* nodep = new AstNotFoundModule(fileline, modname); + v3Global.rootp()->addModulep(nodep); + return; } if (v3Global.opt.preprocOnly() || v3Global.opt.keepTempFiles()) { - // Create output file with all the preprocessor output we buffered up - string vppfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_"+modname+".vpp"; + // Create output file with all the preprocessor output we buffered up + string vppfilename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"_"+modname+".vpp"; std::ofstream* ofp = NULL; std::ostream* osp; - if (v3Global.opt.preprocOnly()) { - osp = &cout; - } else { - osp = ofp = V3File::new_ofstream(vppfilename); - } - if (osp->fail()) { - fileline->v3error("Cannot write preprocessor output: "+vppfilename); - return; - } else { + if (v3Global.opt.preprocOnly()) { + osp = &cout; + } else { + osp = ofp = V3File::new_ofstream(vppfilename); + } + if (osp->fail()) { + fileline->v3error("Cannot write preprocessor output: "+vppfilename); + return; + } else { preprocDumps(*osp); - if (ofp) { - ofp->close(); - delete ofp; VL_DANGLING(ofp); - } - } + if (ofp) { + ofp->close(); + delete ofp; VL_DANGLING(ofp); + } + } } // Parse it if (!v3Global.opt.preprocOnly()) { lexFile(modfilename); } else { - m_ppBuffers.clear(); + m_ppBuffers.clear(); } } @@ -159,8 +159,8 @@ void V3ParseImp::lexFile(const string& modname) { // Prepare for lexing UINFO(3,"Lexing "<warnResetDefault(); // Reenable warnings on each file - lexDestroy(); // Restart from clean slate. + fileline()->warnResetDefault(); // Reenable warnings on each file + lexDestroy(); // Restart from clean slate. lexNew(); // Lex it @@ -177,7 +177,7 @@ V3Parse::~V3Parse() { delete m_impp; m_impp = NULL; } void V3Parse::parseFile(FileLine* fileline, const string& modname, bool inLibrary, - const string& errmsg) { + const string& errmsg) { m_impp->parseFile(fileline, modname, inLibrary, errmsg); } void V3Parse::ppPushText(V3ParseImp* impp, const string& text) { diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 648801749..ef19ef6a9 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -49,46 +49,46 @@ typedef enum { iprop_NONE, iprop_CONTEXT, iprop_PURE } V3ImportProperty; // We can't use bison's %union as we want to pass the fileline with all tokens struct V3ParseBisonYYSType { - FileLine* fl; - AstNode* scp; // Symbol table scope for future lookups - int token; // Read token, aka tok + FileLine* fl; + AstNode* scp; // Symbol table scope for future lookups + int token; // Read token, aka tok union { - V3Number* nump; - string* strp; - int cint; - double cdouble; - bool cbool; - V3UniqState uniqstate; - VSignedState signstate; - V3ImportProperty iprop; - V3ErrorCode::en errcodeen; + V3Number* nump; + string* strp; + int cint; + double cdouble; + bool cbool; + V3UniqState uniqstate; + VSignedState signstate; + V3ImportProperty iprop; + V3ErrorCode::en errcodeen; - AstNode* nodep; + AstNode* nodep; - AstBasicDType* bdtypep; - AstBegin* beginp; - AstCase* casep; - AstCaseItem* caseitemp; - AstCell* cellp; - AstConst* constp; - AstMemberDType* memberp; - AstNodeModule* modulep; - AstNodeClassDType* classp; - AstNodeDType* dtypep; - AstNodeFTask* ftaskp; - AstNodeFTaskRef* ftaskrefp; + AstBasicDType* bdtypep; + AstBegin* beginp; + AstCase* casep; + AstCaseItem* caseitemp; + AstCell* cellp; + AstConst* constp; + AstMemberDType* memberp; + AstNodeModule* modulep; + AstNodeClassDType* classp; + AstNodeDType* dtypep; + AstNodeFTask* ftaskp; + AstNodeFTaskRef* ftaskrefp; AstNodeRange* rangep; - AstNodeSenItem* senitemp; - AstNodeVarRef* varnodep; - AstPackage* packagep; - AstPackageRef* packagerefp; - AstParseRef* parserefp; - AstPatMember* patmemberp; - AstPattern* patternp; - AstPin* pinp; - AstSenTree* sentreep; - AstVar* varp; - AstVarRef* varrefp; + AstNodeSenItem* senitemp; + AstNodeVarRef* varnodep; + AstPackage* packagep; + AstPackageRef* packagerefp; + AstParseRef* parserefp; + AstPatMember* patmemberp; + AstPattern* patternp; + AstPin* pinp; + AstSenTree* sentreep; + AstVar* varp; + AstVarRef* varrefp; }; }; @@ -98,43 +98,43 @@ struct V3ParseBisonYYSType { class V3ParseImp { // MEMBERS - AstNetlist* m_rootp; // Root of the design - V3InFilter* m_filterp; // Reading filter - V3ParseSym* m_symp; // Symbol table + AstNetlist* m_rootp; // Root of the design + V3InFilter* m_filterp; // Reading filter + V3ParseSym* m_symp; // Symbol table - V3Lexer* m_lexerp; // Current FlexLexer - static V3ParseImp* s_parsep; // Current THIS, bison() isn't class based - FileLine* m_fileline; // Filename/linenumber currently active + V3Lexer* m_lexerp; // Current FlexLexer + static V3ParseImp* s_parsep; // Current THIS, bison() isn't class based + FileLine* m_fileline; // Filename/linenumber currently active - bool m_inCellDefine; // Inside a `celldefine - bool m_inLibrary; // Currently reading a library vs. regular file - int m_inBeginKwd; // Inside a `begin_keywords - int m_lastVerilogState; // Last LEX state in `begin_keywords + bool m_inCellDefine; // Inside a `celldefine + bool m_inLibrary; // Currently reading a library vs. regular file + int m_inBeginKwd; // Inside a `begin_keywords + int m_lastVerilogState; // Last LEX state in `begin_keywords - int m_prevLexToken; // previous parsed token (for lexer) - bool m_ahead; // aheadval is valid - V3ParseBisonYYSType m_aheadVal; // ahead token value - V3ParseBisonYYSType m_curBisonVal; // current token for error reporting - V3ParseBisonYYSType m_prevBisonVal; // previous token for error reporting + int m_prevLexToken; // previous parsed token (for lexer) + bool m_ahead; // aheadval is valid + V3ParseBisonYYSType m_aheadVal; // ahead token value + V3ParseBisonYYSType m_curBisonVal; // current token for error reporting + V3ParseBisonYYSType m_prevBisonVal; // previous token for error reporting - std::deque m_stringps; // Created strings for later cleanup + std::deque m_stringps; // Created strings for later cleanup std::deque m_numberps; // Created numbers for later cleanup - std::deque m_lintState; // Current lint state for save/restore - std::deque m_ppBuffers; // Preprocessor->lex buffer of characters to process + std::deque m_lintState; // Current lint state for save/restore + std::deque m_ppBuffers; // Preprocessor->lex buffer of characters to process string m_tag; // Contents (if any) of current verilator tag AstNode* m_tagNodep; // Points to the node to set to m_tag or NULL to not set. public: // Note these are an exception to using the filename as the debug type static int debugBison() { - static int level = -1; - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel("bison"); - return level; + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel("bison"); + return level; } static int debugFlex() { - static int level = -1; - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel("flex"); - return level; + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel("flex"); + return level; } static int debug() { return debugBison() ? debugFlex() : 0; } @@ -153,8 +153,9 @@ public: AstNode* tagNodep() const { return m_tagNodep;} static double parseDouble(const char* text, size_t length, bool* successp = NULL); - void pushBeginKeywords(int state) { m_inBeginKwd++; m_lastVerilogState=state; } - bool popBeginKeywords() { if (m_inBeginKwd) { m_inBeginKwd--; return true; } else return false; } + void pushBeginKeywords(int state) { m_inBeginKwd++; m_lastVerilogState = state; } + bool popBeginKeywords() { + if (m_inBeginKwd) { m_inBeginKwd--; return true; } else return false; } int lastVerilogState() const { return m_lastVerilogState; } static const char* tokenName(int tok); @@ -166,21 +167,21 @@ public: // TODO: Many of these functions are the old interface; they'd be better as non-static // and called as READP->newString(...) etc. string* newString(const string& text) { - // Allocate a string, remembering it so we can reclaim storage at lex end + // Allocate a string, remembering it so we can reclaim storage at lex end string* strp = new string(text); - m_stringps.push_back(strp); - return strp; + m_stringps.push_back(strp); + return strp; } string* newString(const char* text) { - // Allocate a string, remembering it so we can reclaim storage at lex end + // Allocate a string, remembering it so we can reclaim storage at lex end string* strp = new string(text); - m_stringps.push_back(strp); - return strp; + m_stringps.push_back(strp); + return strp; } string* newString(const char* text, size_t length) { string* strp = new string(text, length); - m_stringps.push_back(strp); - return strp; + m_stringps.push_back(strp); + return strp; } V3Number* newNumber(FileLine* fl, const char* text) { V3Number* nump = new V3Number(V3Number::FileLined(), fl, text); @@ -202,10 +203,10 @@ public: // Interactions with lexer void lexNew(); void lexDestroy(); - void statePop(); // Parser -> lexer communication - static int stateVerilogRecent(); // Parser -> lexer communication - int prevLexToken() { return m_prevLexToken; } // Parser -> lexer communication - size_t flexPpInputToLex(char* buf, size_t max_size) { return ppInputToLex(buf,max_size); } + void statePop(); // Parser -> lexer communication + static int stateVerilogRecent(); // Parser -> lexer communication + int prevLexToken() { return m_prevLexToken; } // Parser -> lexer communication + size_t flexPpInputToLex(char* buf, size_t max_size) { return ppInputToLex(buf, max_size); } const V3ParseBisonYYSType curBisonVal() const { return m_curBisonVal; } const V3ParseBisonYYSType prevBisonVal() const { return m_prevBisonVal; } @@ -215,18 +216,18 @@ public: public: // CONSTRUCTORS V3ParseImp(AstNetlist* rootp, V3InFilter* filterp, V3ParseSym* parserSymp) - : m_rootp(rootp), m_filterp(filterp), m_symp(parserSymp) { - m_fileline = NULL; - m_lexerp = NULL; - m_inCellDefine = false; - m_inLibrary = false; - m_inBeginKwd = 0; - m_lastVerilogState = stateVerilogRecent(); - m_prevLexToken = 0; - m_ahead = false; - m_curBisonVal.token = 0; - m_prevBisonVal.token = 0; - // m_aheadVal not used as m_ahead = false, and not all compilers support initing it + : m_rootp(rootp), m_filterp(filterp), m_symp(parserSymp) { + m_fileline = NULL; + m_lexerp = NULL; + m_inCellDefine = false; + m_inLibrary = false; + m_inBeginKwd = 0; + m_lastVerilogState = stateVerilogRecent(); + m_prevLexToken = 0; + m_ahead = false; + m_curBisonVal.token = 0; + m_prevBisonVal.token = 0; + // m_aheadVal not used as m_ahead = false, and not all compilers support initing it m_tagNodep = NULL; } ~V3ParseImp(); @@ -238,13 +239,13 @@ public: int lexToBison(); // Pass token to bison void parseFile(FileLine* fileline, const string& modfilename, bool inLibrary, - const string& errmsg); + const string& errmsg); private: void lexFile(const string& modname); int yylexReadTok(); - void lexToken(); // Internal; called from lexToBison + void lexToken(); // Internal; called from lexToBison void preprocDumps(std::ostream& os); }; -#endif // Guard +#endif // Guard diff --git a/src/V3ParseSym.h b/src/V3ParseSym.h index a647660e6..4ba9e17b0 100644 --- a/src/V3ParseSym.h +++ b/src/V3ParseSym.h @@ -40,28 +40,28 @@ class V3ParseSym { private: // MEMBERS - static int s_anonNum; // Number of next anonymous object (parser use only) - VSymGraph m_syms; // Graph of symbol tree - VSymEnt* m_symTableNextId; // Symbol table for next lexer lookup (parser use only) - VSymEnt* m_symCurrentp; // Active symbol table for additions/lookups - SymStack m_sympStack; // Stack of upper nodes with pending symbol tables + static int s_anonNum; // Number of next anonymous object (parser use only) + VSymGraph m_syms; // Graph of symbol tree + VSymEnt* m_symTableNextId; // Symbol table for next lexer lookup (parser use only) + VSymEnt* m_symCurrentp; // Active symbol table for additions/lookups + SymStack m_sympStack; // Stack of upper nodes with pending symbol tables public: // CONSTRUCTORS explicit V3ParseSym(AstNetlist* rootp) - : m_syms(rootp) { - s_anonNum = 0; // Number of next anonymous object - pushScope(findNewTable(rootp)); - m_symTableNextId = NULL; - m_symCurrentp = symCurrentp(); + : m_syms(rootp) { + s_anonNum = 0; // Number of next anonymous object + pushScope(findNewTable(rootp)); + m_symTableNextId = NULL; + m_symCurrentp = symCurrentp(); } ~V3ParseSym() {} private: // METHODS static VSymEnt* getTable(AstNode* nodep) { - if (!nodep->user4p()) nodep->v3fatalSrc("Current symtable not found"); - return nodep->user4u().toSymEnt(); + if (!nodep->user4p()) nodep->v3fatalSrc("Current symtable not found"); + return nodep->user4u().toSymEnt(); } public: @@ -70,96 +70,96 @@ public: VSymEnt* symRootp() const { return m_syms.rootp(); } VSymEnt* findNewTable(AstNode* nodep) { - if (!nodep->user4p()) { - VSymEnt* symsp = new VSymEnt(&m_syms, nodep); - nodep->user4p(symsp); - } - return getTable(nodep); + if (!nodep->user4p()) { + VSymEnt* symsp = new VSymEnt(&m_syms, nodep); + nodep->user4p(symsp); + } + return getTable(nodep); } void nextId(AstNode* entp) { - if (entp) { - UINFO(9,"symTableNextId under "<type().ascii()<type().ascii()<name()); + reinsert(nodep, parentp, nodep->name()); } void reinsert(AstNode* nodep, VSymEnt* parentp, string name) { - if (!parentp) parentp = symCurrentp(); - if (name == "") { // New name with space in name so can't collide with users - name = string(" anon") + nodep->type().ascii() + cvtToStr(++s_anonNum); - } - parentp->reinsert(name, findNewTable(nodep)); + if (!parentp) parentp = symCurrentp(); + if (name == "") { // New name with space in name so can't collide with users + name = string(" anon") + nodep->type().ascii() + cvtToStr(++s_anonNum); + } + parentp->reinsert(name, findNewTable(nodep)); } void pushNew(AstNode* nodep) { pushNewUnder(nodep, NULL); } void pushNewUnder(AstNode* nodep, VSymEnt* parentp) { - if (!parentp) parentp = symCurrentp(); - VSymEnt* symp = findNewTable(nodep); // Will set user4p, which is how we connect table to node - symp->fallbackp(parentp); - reinsert(nodep, parentp); - pushScope(symp); + if (!parentp) parentp = symCurrentp(); + VSymEnt* symp = findNewTable(nodep); // Will set user4p, which is how we connect table to node + symp->fallbackp(parentp); + reinsert(nodep, parentp); + pushScope(symp); } void pushScope(VSymEnt* symp) { - m_sympStack.push_back(symp); - m_symCurrentp = symp; + m_sympStack.push_back(symp); + m_symCurrentp = symp; } void popScope(AstNode* nodep) { - if (symCurrentp()->nodep() != nodep) { - if (debug()) { showUpward(); dump(cout,"-mism: "); } - nodep->v3fatalSrc("Symbols suggest ending "<nodep()->prettyTypeName() - <<" but parser thinks ending "<prettyTypeName()); - return; - } - m_sympStack.pop_back(); - if (m_sympStack.empty()) { nodep->v3fatalSrc("symbol stack underflow"); return; } - m_symCurrentp = m_sympStack.back(); + if (symCurrentp()->nodep() != nodep) { + if (debug()) { showUpward(); dump(cout, "-mism: "); } + nodep->v3fatalSrc("Symbols suggest ending "<nodep()->prettyTypeName() + <<" but parser thinks ending "<prettyTypeName()); + return; + } + m_sympStack.pop_back(); + if (m_sympStack.empty()) { nodep->v3fatalSrc("symbol stack underflow"); return; } + m_symCurrentp = m_sympStack.back(); } void showUpward() { - UINFO(1,"ParseSym Stack:\n"); - for (SymStack::reverse_iterator it=m_sympStack.rbegin(); it!=m_sympStack.rend(); ++it) { - VSymEnt* symp = *it; - UINFO(1,"\t"<nodep()<nodep()<nodep()<nodep()<findIdFallback(name); - if (foundp) return foundp->nodep(); - else return NULL; + // Lookup the given string as an identifier, return type of the id, scanning upward + VSymEnt* foundp = symCurrentp()->findIdFallback(name); + if (foundp) return foundp->nodep(); + else return NULL; } void importItem(AstNode* packagep, const string& id_or_star) { - // Import from package::id_or_star to this - VSymEnt* symp = getTable(packagep); - if (!symp) { // Internal problem, because we earlier found pkg to label it an ID__aPACKAGE - packagep->v3fatalSrc("Import package not found"); - return; - } - // Walk old sym table and reinsert into current table - // We let V3LinkDot report the error instead of us - symCurrentp()->importFromPackage(&m_syms, symp, id_or_star); + // Import from package::id_or_star to this + VSymEnt* symp = getTable(packagep); + if (!symp) { // Internal problem, because we earlier found pkg to label it an ID__aPACKAGE + packagep->v3fatalSrc("Import package not found"); + return; + } + // Walk old sym table and reinsert into current table + // We let V3LinkDot report the error instead of us + symCurrentp()->importFromPackage(&m_syms, symp, id_or_star); } void exportItem(AstNode* packagep, const string& id_or_star) { - // Export from this the remote package::id_or_star - VSymEnt* symp = getTable(packagep); - if (!symp) { // Internal problem, because we earlier found pkg to label it an ID__aPACKAGE - packagep->v3fatalSrc("Export package not found"); - return; - } - symCurrentp()->exportFromPackage(&m_syms, symp, id_or_star); + // Export from this the remote package::id_or_star + VSymEnt* symp = getTable(packagep); + if (!symp) { // Internal problem, because we earlier found pkg to label it an ID__aPACKAGE + packagep->v3fatalSrc("Export package not found"); + return; + } + symCurrentp()->exportFromPackage(&m_syms, symp, id_or_star); } void exportStarStar(AstNode* packagep) { - // Export *::* from remote packages - symCurrentp()->exportStarStar(&m_syms); + // Export *::* from remote packages + symCurrentp()->exportStarStar(&m_syms); } }; -#endif // Guard +#endif // Guard diff --git a/src/V3PreLex.h b/src/V3PreLex.h index 61d4c5fe1..28475db48 100644 --- a/src/V3PreLex.h +++ b/src/V3PreLex.h @@ -22,7 +22,7 @@ // It is not intended for user applications. //************************************************************************* -#ifndef _VPREPROCLEX_H_ // Guard +#ifndef _VPREPROCLEX_H_ // Guard #define _VPREPROCLEX_H_ 1 #include "V3Error.h" @@ -38,36 +38,36 @@ class V3PreProcImp; // Token codes // If changing, see V3PreProc.cpp's V3PreProcImp::tokenName() -#define VP_EOF 0 +#define VP_EOF 0 -#define VP_INCLUDE 256 -#define VP_IFDEF 257 -#define VP_IFNDEF 258 -#define VP_ENDIF 259 -#define VP_UNDEF 260 -#define VP_DEFINE 261 -#define VP_ELSE 262 -#define VP_ELSIF 263 -#define VP_LINE 264 -#define VP_UNDEFINEALL 265 +#define VP_INCLUDE 256 +#define VP_IFDEF 257 +#define VP_IFNDEF 258 +#define VP_ENDIF 259 +#define VP_UNDEF 260 +#define VP_DEFINE 261 +#define VP_ELSE 262 +#define VP_ELSIF 263 +#define VP_LINE 264 +#define VP_UNDEFINEALL 265 -#define VP_SYMBOL 300 -#define VP_STRING 301 -#define VP_DEFVALUE 302 -#define VP_COMMENT 303 -#define VP_TEXT 304 -#define VP_WHITE 305 -#define VP_DEFREF 306 -#define VP_DEFARG 307 -#define VP_ERROR 308 -#define VP_DEFFORM 309 -#define VP_STRIFY 310 -#define VP_BACKQUOTE 311 -#define VP_SYMBOL_JOIN 312 -#define VP_DEFREF_JOIN 313 -#define VP_JOIN 314 +#define VP_SYMBOL 300 +#define VP_STRING 301 +#define VP_DEFVALUE 302 +#define VP_COMMENT 303 +#define VP_TEXT 304 +#define VP_WHITE 305 +#define VP_DEFREF 306 +#define VP_DEFARG 307 +#define VP_ERROR 308 +#define VP_DEFFORM 309 +#define VP_STRIFY 310 +#define VP_BACKQUOTE 311 +#define VP_SYMBOL_JOIN 312 +#define VP_DEFREF_JOIN 313 +#define VP_JOIN 314 -#define VP_PSL 350 +#define VP_PSL 350 //====================================================================== // Externs created by flex @@ -126,21 +126,21 @@ void yy_delete_buffer(YY_BUFFER_STATE b); class VPreStream { public: - FileLine* m_curFilelinep; // Current processing point (see also m_tokFilelinep) - V3PreLex* m_lexp; // Lexer, for resource tracking + FileLine* m_curFilelinep; // Current processing point (see also m_tokFilelinep) + V3PreLex* m_lexp; // Lexer, for resource tracking std::deque m_buffers; // Buffer of characters to process - int m_ignNewlines; // Ignore multiline newlines - bool m_eof; // "EOF" buffer - bool m_file; // Buffer is start of new file - int m_termState; // Termination fsm + int m_ignNewlines; // Ignore multiline newlines + bool m_eof; // "EOF" buffer + bool m_file; // Buffer is start of new file + int m_termState; // Termination fsm VPreStream(FileLine* fl, V3PreLex* lexp) - : m_curFilelinep(fl), m_lexp(lexp), - m_ignNewlines(0), - m_eof(false), m_file(false), m_termState(0) { - lexStreamDepthAdd(1); + : m_curFilelinep(fl), m_lexp(lexp), + m_ignNewlines(0), + m_eof(false), m_file(false), m_termState(0) { + lexStreamDepthAdd(1); } ~VPreStream() { - lexStreamDepthAdd(-1); + lexStreamDepthAdd(-1); } private: void lexStreamDepthAdd(int delta); @@ -150,55 +150,55 @@ private: // Class entry for each per-lexer state class V3PreLex { - public: // Used only by V3PreLex.cpp and V3PreProc.cpp - V3PreProcImp* m_preimpp; // Preprocessor lexor belongs to + public: // Used only by V3PreLex.cpp and V3PreProc.cpp + V3PreProcImp* m_preimpp; // Preprocessor lexor belongs to std::stack m_streampStack; // Stack of processing files - int m_streamDepth; // Depth of stream processing - YY_BUFFER_STATE m_bufferState; // Flex state - FileLine* m_tokFilelinep; // Starting position of current token + int m_streamDepth; // Depth of stream processing + YY_BUFFER_STATE m_bufferState; // Flex state + FileLine* m_tokFilelinep; // Starting position of current token // State to lexer - static V3PreLex* s_currentLexp; ///< Current lexing point - int m_keepComments; ///< Emit comments in output text - int m_keepWhitespace; ///< Emit all whitespace in output text - bool m_pedantic; ///< Obey standard; don't Substitute `error + static V3PreLex* s_currentLexp; ///< Current lexing point + int m_keepComments; ///< Emit comments in output text + int m_keepWhitespace; ///< Emit all whitespace in output text + bool m_pedantic; ///< Obey standard; don't Substitute `error // State from lexer - int m_formalLevel; // Parenthesis counting inside def formals - int m_parenLevel; // Parenthesis counting inside def args - bool m_defCmtSlash; // /*...*/ comment in define had \ ending - bool m_defQuote; // Definition value inside quote - string m_defValue; // Definition value being built. - int m_enterExit; // For VL_LINE, the enter/exit level + int m_formalLevel; // Parenthesis counting inside def formals + int m_parenLevel; // Parenthesis counting inside def args + bool m_defCmtSlash; // /*...*/ comment in define had \ ending + bool m_defQuote; // Definition value inside quote + string m_defValue; // Definition value being built. + int m_enterExit; // For VL_LINE, the enter/exit level // CONSTRUCTORS V3PreLex(V3PreProcImp* preimpp, FileLine* filelinep) { - m_preimpp = preimpp; - m_streamDepth = 0; - m_keepComments = 0; - m_keepWhitespace = 1; - m_pedantic = false; - m_formalLevel = 0; - m_parenLevel = 0; - m_defQuote = false; - m_defCmtSlash = false; - m_tokFilelinep = filelinep; - m_enterExit = 0; - initFirstBuffer(filelinep); + m_preimpp = preimpp; + m_streamDepth = 0; + m_keepComments = 0; + m_keepWhitespace = 1; + m_pedantic = false; + m_formalLevel = 0; + m_parenLevel = 0; + m_defQuote = false; + m_defCmtSlash = false; + m_tokFilelinep = filelinep; + m_enterExit = 0; + initFirstBuffer(filelinep); } ~V3PreLex() { - while (!m_streampStack.empty()) { delete m_streampStack.top(); m_streampStack.pop(); } - yy_delete_buffer(m_bufferState); m_bufferState=NULL; + while (!m_streampStack.empty()) { delete m_streampStack.top(); m_streampStack.pop(); } + yy_delete_buffer(m_bufferState); m_bufferState = NULL; } // Called by V3PreLex.l from lexer VPreStream* curStreamp() { return m_streampStack.top(); } // Can't be empty, "EOF" is on top FileLine* curFilelinep() { return curStreamp()->m_curFilelinep; } void curFilelinep(FileLine* fl) { curStreamp()->m_curFilelinep = fl; } - void appendDefValue(const char* textp, size_t len) { m_defValue.append(textp,len); } + void appendDefValue(const char* textp, size_t len) { m_defValue.append(textp, len); } void lineDirective(const char* textp); void linenoInc() { if (curStreamp()->m_ignNewlines) curStreamp()->m_ignNewlines--; - else curFilelinep()->linenoInc(); } + else curFilelinep()->linenoInc(); } // Called by V3PreProc.cpp to inform lexer void pushStateDefArg(int level); void pushStateDefForm(); @@ -211,7 +211,7 @@ class V3PreLex { /// Called by V3PreProc.cpp to get data from lexer YY_BUFFER_STATE currentBuffer(); int lex(); - int currentStartState() const; + int currentStartState() const; void dumpSummary(); void dumpStack(); void unused(); @@ -232,4 +232,4 @@ private: inline void VPreStream::lexStreamDepthAdd(int delta) { m_lexp->streamDepthAdd(delta); } -#endif // Guard +#endif // Guard diff --git a/src/V3PreLex.l b/src/V3PreLex.l index bd1b292d8..064782a30 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -116,8 +116,9 @@ bom [\357\273\277] [\\]{wsn}+{crnl} { yyless(1); LEXP->curFilelinep()->v3warn(BSSPACE, "Backslash followed by whitespace, perhaps the whitespace is accidental?"); } [\\]. { yymore(); } {quote} { yy_pop_state(); - if (LEXP->m_parenLevel || LEXP->m_defQuote) { LEXP->m_defQuote=false; appendDefValue(yytext,yyleng); yyleng=0; } - else return (VP_STRING); } + if (LEXP->m_parenLevel || LEXP->m_defQuote) { + LEXP->m_defQuote=false; appendDefValue(yytext, yyleng); yyleng=0; + } else return (VP_STRING); } /* Stringification */ {tickquote} { yy_push_state(STRIFY); return VP_STRIFY; } @@ -159,7 +160,8 @@ bom [\357\273\277] /* Reading definition formals (declaration of a define) */ [(] { appendDefValue(yytext,yyleng); yyleng=0; ++LEXP->m_formalLevel; } -[)] { appendDefValue(yytext,yyleng); yyleng=0; if ((--LEXP->m_formalLevel)==0) { yy_pop_state(); return VP_DEFFORM; } } +[)] { appendDefValue(yytext,yyleng); yyleng=0; + if ((--LEXP->m_formalLevel)==0) { yy_pop_state(); return VP_DEFFORM; } } "/*" { yy_push_state(CMTMODE); yymore(); } "//"[^\n\r]* { return (VP_COMMENT);} {drop} { } @@ -356,7 +358,7 @@ size_t V3PreLex::inputToLex(char* buf, size_t max_size) { if (again) goto again; } } - if (debug()>=10) { cout<<"- pp::inputToLex got="<=10) { cout<<"- pp::inputToLex got="< m_args; // List of define arguments public: @@ -80,7 +80,7 @@ public: void parenLevel(int value) { m_parenLevel = value; } std::vector& args() { return m_args; } V3DefineRef(const string& name, const string& params) - : m_name(name), m_params(params), m_parenLevel(0) {} + : m_name(name), m_params(params), m_parenLevel(0) {} ~V3DefineRef() {} }; @@ -89,13 +89,13 @@ public: class VPreIfEntry { // One for each pending ifdef/ifndef - bool m_on; // Current parse for this ifdef level is "on" - bool m_everOn; // Some if term in elsif tree has been on + bool m_on; // Current parse for this ifdef level is "on" + bool m_everOn; // Some if term in elsif tree has been on public: bool on() const { return m_on; } bool everOn() const { return m_everOn; } VPreIfEntry(bool on, bool everOn) - : m_on(on), m_everOn(everOn || on) {} // Note everOn includes new state + : m_on(on), m_everOn(everOn || on) {} // Note everOn includes new state ~VPreIfEntry() {} }; @@ -111,63 +111,63 @@ public: // debug() -> see V3PreShellImp::debug; use --debugi-V3PreShell // Defines list - DefinesMap m_defines; ///< Map of defines + DefinesMap m_defines; ///< Map of defines // STATE - V3PreProc* m_preprocp; ///< Object we're holding data for - V3PreLex* m_lexp; ///< Current lexer state (NULL = closed) + V3PreProc* m_preprocp; ///< Object we're holding data for + V3PreLex* m_lexp; ///< Current lexer state (NULL = closed) std::stack m_includeStack; ///< Stack of includers above current m_lexp enum ProcState { ps_TOP, - ps_DEFNAME_UNDEF, ps_DEFNAME_DEFINE, - ps_DEFNAME_IFDEF, ps_DEFNAME_IFNDEF, ps_DEFNAME_ELSIF, - ps_DEFFORM, ps_DEFVALUE, ps_DEFPAREN, ps_DEFARG, - ps_INCNAME, ps_ERRORNAME, ps_JOIN, ps_STRIFY }; + ps_DEFNAME_UNDEF, ps_DEFNAME_DEFINE, + ps_DEFNAME_IFDEF, ps_DEFNAME_IFNDEF, ps_DEFNAME_ELSIF, + ps_DEFFORM, ps_DEFVALUE, ps_DEFPAREN, ps_DEFARG, + ps_INCNAME, ps_ERRORNAME, ps_JOIN, ps_STRIFY }; static const char* procStateName(ProcState s) { - static const char* states[] - = {"ps_TOP", - "ps_DEFNAME_UNDEF", "ps_DEFNAME_DEFINE", - "ps_DEFNAME_IFDEF", "ps_DEFNAME_IFNDEF", "ps_DEFNAME_ELSIF", - "ps_DEFFORM", "ps_DEFVALUE", "ps_DEFPAREN", "ps_DEFARG", - "ps_INCNAME", "ps_ERRORNAME", "ps_JOIN", "ps_STRIFY"}; - return states[s]; + static const char* states[] + = {"ps_TOP", + "ps_DEFNAME_UNDEF", "ps_DEFNAME_DEFINE", + "ps_DEFNAME_IFDEF", "ps_DEFNAME_IFNDEF", "ps_DEFNAME_ELSIF", + "ps_DEFFORM", "ps_DEFVALUE", "ps_DEFPAREN", "ps_DEFARG", + "ps_INCNAME", "ps_ERRORNAME", "ps_JOIN", "ps_STRIFY"}; + return states[s]; }; std::stack m_states; ///< Current state of parser - int m_off; ///< If non-zero, ifdef level is turned off, don't dump text - string m_lastSym; ///< Last symbol name found. - string m_formals; ///< Last formals found + int m_off; ///< If non-zero, ifdef level is turned off, don't dump text + string m_lastSym; ///< Last symbol name found. + string m_formals; ///< Last formals found // For getRawToken/ `line insertion - string m_lineCmt; ///< Line comment(s) to be returned - bool m_lineCmtNl; ///< Newline needed before inserting lineCmt - int m_lineAdd; ///< Empty lines to return to maintain line count - bool m_rawAtBol; ///< Last rawToken left us at beginning of line + string m_lineCmt; ///< Line comment(s) to be returned + bool m_lineCmtNl; ///< Newline needed before inserting lineCmt + int m_lineAdd; ///< Empty lines to return to maintain line count + bool m_rawAtBol; ///< Last rawToken left us at beginning of line // For getFinalToken - bool m_finAhead; ///< Have read a token ahead - int m_finToken; ///< Last token read - string m_finBuf; ///< Last yytext read - bool m_finAtBol; ///< Last getFinalToken left us at beginning of line - FileLine* m_finFilelinep; ///< Location of last returned token (internal only) + bool m_finAhead; ///< Have read a token ahead + int m_finToken; ///< Last token read + string m_finBuf; ///< Last yytext read + bool m_finAtBol; ///< Last getFinalToken left us at beginning of line + FileLine* m_finFilelinep; ///< Location of last returned token (internal only) // For stringification - string m_strify; ///< Text to be stringified + string m_strify; ///< Text to be stringified // For defines std::stack m_defRefs; ///< Pending definine substitution std::stack m_ifdefStack; ///< Stack of true/false emitting evaluations - unsigned m_defDepth; ///< How many `defines deep - bool m_defPutJoin; ///< Insert `` after substitution + unsigned m_defDepth; ///< How many `defines deep + bool m_defPutJoin; ///< Insert `` after substitution // For `` join std::stack m_joinStack; ///< Text on lhs of join // For getline() - string m_lineChars; ///< Characters left for next line + string m_lineChars; ///< Characters left for next line void v3errorEnd(std::ostringstream& str) { - fileline()->v3errorEnd(str); + fileline()->v3errorEnd(str); } static const char* tokenName(int tok); @@ -192,9 +192,9 @@ private: void unputDefrefString(const string& strg); void parsingOn() { - m_off--; - if (m_off<0) fatalSrc("Underflow of parsing cmds"); - // addLineComment no longer needed; getFinalToken will correct. + m_off--; + if (m_off<0) fatalSrc("Underflow of parsing cmds"); + // addLineComment no longer needed; getFinalToken will correct. } void parsingOff() { m_off++; } @@ -211,17 +211,17 @@ private: || state()==ps_DEFNAME_ELSIF; } void statePush(ProcState state) { - m_states.push(state); + m_states.push(state); } void statePop() { - m_states.pop(); - if (m_states.empty()) { - error("InternalError: Pop of parser state with nothing on stack"); - m_states.push(ps_TOP); - } + m_states.pop(); + if (m_states.empty()) { + error("InternalError: Pop of parser state with nothing on stack"); + m_states.push(ps_TOP); + } } void stateChange(ProcState state) { - statePop(); statePush(state); + statePop(); statePush(state); } public: @@ -241,40 +241,40 @@ public: virtual void undefineall(); virtual void define(FileLine* fl, const string& name, const string& value, const string& params, bool cmdline); - virtual string removeDefines(const string& text); // Remove defines in a text string + virtual string removeDefines(const string& text); // Remove defines in a text string // CONSTRUCTORS V3PreProcImp() { - m_debug = 0; - m_states.push(ps_TOP); - m_off = 0; - m_lineChars = ""; - m_lastSym = ""; - m_lineAdd = 0; - m_lineCmtNl = false; - m_rawAtBol = true; - m_finAhead = false; - m_finAtBol = true; - m_defDepth = 0; - m_defPutJoin = false; - m_finToken = 0; - m_finFilelinep = NULL; - m_lexp = NULL; - m_preprocp = NULL; + m_debug = 0; + m_states.push(ps_TOP); + m_off = 0; + m_lineChars = ""; + m_lastSym = ""; + m_lineAdd = 0; + m_lineCmtNl = false; + m_rawAtBol = true; + m_finAhead = false; + m_finAtBol = true; + m_defDepth = 0; + m_defPutJoin = false; + m_finToken = 0; + m_finFilelinep = NULL; + m_lexp = NULL; + m_preprocp = NULL; } void configure(FileLine* filelinep) { - // configure() separate from constructor to avoid calling abstract functions - m_preprocp = this; // Silly, but to make code more similar to Verilog-Perl - m_finFilelinep = filelinep->create(1); - // Create lexer + // configure() separate from constructor to avoid calling abstract functions + m_preprocp = this; // Silly, but to make code more similar to Verilog-Perl + m_finFilelinep = filelinep->create(1); + // Create lexer m_lexp = new V3PreLex(this, filelinep); - m_lexp->m_keepComments = m_preprocp->keepComments(); - m_lexp->m_keepWhitespace = m_preprocp->keepWhitespace(); - m_lexp->m_pedantic = m_preprocp->pedantic(); - m_lexp->debug(debug()>=5 ? debug() : 0); // See also V3PreProc::debug() method + m_lexp->m_keepComments = m_preprocp->keepComments(); + m_lexp->m_keepWhitespace = m_preprocp->keepWhitespace(); + m_lexp->m_pedantic = m_preprocp->pedantic(); + m_lexp->debug(debug()>=5 ? debug() : 0); // See also V3PreProc::debug() method } ~V3PreProcImp() { - if (m_lexp) { delete m_lexp; m_lexp = NULL; } + if (m_lexp) { delete m_lexp; m_lexp = NULL; } } }; @@ -295,8 +295,8 @@ void V3PreProcImp::undef(const string& name) { } void V3PreProcImp::undefineall() { for (DefinesMap::iterator nextit, it = m_defines.begin(); it != m_defines.end(); it=nextit) { - nextit = it; ++nextit; - if (!it->second.cmdline()) m_defines.erase(it); + nextit = it; ++nextit; + if (!it->second.cmdline()) m_defines.erase(it); } } bool V3PreProcImp::defExists(const string& name) { @@ -306,16 +306,16 @@ bool V3PreProcImp::defExists(const string& name) { string V3PreProcImp::defValue(const string& name) { DefinesMap::iterator iter = m_defines.find(name); if (iter == m_defines.end()) { - fileline()->v3error("Define or directive not defined: `"+name); - return ""; + fileline()->v3error("Define or directive not defined: `"+name); + return ""; } return iter->second.value(); } string V3PreProcImp::defParams(const string& name) { DefinesMap::iterator iter = m_defines.find(name); if (iter == m_defines.end()) { - fileline()->v3error("Define or directive not defined: `"+name); - return ""; + fileline()->v3error("Define or directive not defined: `"+name); + return ""; } return iter->second.params(); } @@ -325,16 +325,18 @@ FileLine* V3PreProcImp::defFileline(const string& name) { return iter->second.fileline(); } void V3PreProcImp::define(FileLine* fl, const string& name, const string& value, - const string& params, bool cmdline) { + const string& params, bool cmdline) { UINFO(4,"DEFINE '"<v3warn(REDEFMACRO,"Redefining existing define: "<v3warn(REDEFMACRO,"Previous definition is here, with value: " - <v3warn(REDEFMACRO, "Redefining existing define: "<v3warn(REDEFMACRO, "Previous definition is here, with value: " + <v3error("Extra underscore in meta-comment; use /*verilator {...}*/ not /*verilator_{...}*/"); + if (*cp == '_') fileline()->v3error("Extra underscore in meta-comment;" + " use /*verilator {...}*/ not /*verilator_{...}*/"); vlcomment = true; - } else if (0==(strncmp(cp,"synopsys",strlen("synopsys")))) { + } else if (0==(strncmp(cp, "synopsys", strlen("synopsys")))) { cp += strlen("synopsys"); - synth = true; - if (*cp == '_') fileline()->v3error("Extra underscore in meta-comment; use /*synopsys {...}*/ not /*synopsys_{...}*/"); - } else if (0==(strncmp(cp,"cadence",strlen("cadence")))) { + synth = true; + if (*cp == '_') fileline()->v3error("Extra underscore in meta-comment;" + " use /*synopsys {...}*/ not /*synopsys_{...}*/"); + } else if (0==(strncmp(cp, "cadence", strlen("cadence")))) { cp += strlen("cadence"); - synth = true; - } else if (0==(strncmp(cp,"pragma",strlen("pragma")))) { + synth = true; + } else if (0==(strncmp(cp, "pragma", strlen("pragma")))) { cp += strlen("pragma"); - synth = true; - } else if (0==(strncmp(cp,"ambit synthesis",strlen("ambit synthesis")))) { + synth = true; + } else if (0==(strncmp(cp, "ambit synthesis", strlen("ambit synthesis")))) { cp += strlen("ambit synthesis"); - synth = true; + synth = true; } else { - return; + return; } if (!vlcomment && !synth) return; // Short-circuit @@ -434,22 +438,22 @@ void V3PreProcImp::comment(const string& text) { // cmd now is comment without extra spaces and "verilator" prefix if (synth) { - if (v3Global.opt.assertOn()) { - // one_hot, one_cold, (full_case, parallel_case) - if (commentTokenMatch(cmd/*ref*/, "full_case")) { + if (v3Global.opt.assertOn()) { + // one_hot, one_cold, (full_case, parallel_case) + if (commentTokenMatch(cmd/*ref*/, "full_case")) { if (!printed) insertUnreadback("/*verilator full_case*/"); - } - if (commentTokenMatch(cmd/*ref*/, "parallel_case")) { + } + if (commentTokenMatch(cmd/*ref*/, "parallel_case")) { if (!printed) insertUnreadback("/*verilator parallel_case*/"); - } - //if (commentTokenMatch(cmd/*ref*/, "one_hot")) { + } + //if (commentTokenMatch(cmd/*ref*/, "one_hot")) { // insertUnreadback ("/*verilator one_hot*/ "+cmd+";"); - //} - //if (commentTokenMatch(cmd/*ref*/, "one_cold")) { + //} + //if (commentTokenMatch(cmd/*ref*/, "one_cold")) { // insertUnreadback ("/*verilator one_cold*/ "+cmd+";"); - //} - // else ignore the comment we don't recognize - } // else no assertions + //} + // else ignore the comment we don't recognize + } // else no assertions } else if (vlcomment) { string::size_type pos; if ((pos = cmd.find("public_flat_rw")) != string::npos) { @@ -476,32 +480,32 @@ FileLine* V3PreProc::fileline() { const char* V3PreProcImp::tokenName(int tok) { switch (tok) { - case VP_BACKQUOTE : return("BACKQUOTE"); - case VP_COMMENT : return("COMMENT"); - case VP_DEFARG : return("DEFARG"); - case VP_DEFFORM : return("DEFFORM"); - case VP_DEFINE : return("DEFINE"); - case VP_DEFREF : return("DEFREF"); - case VP_DEFREF_JOIN : return("DEFREF_JOIN"); - case VP_DEFVALUE : return("DEFVALUE"); - case VP_ELSE : return("ELSE"); - case VP_ELSIF : return("ELSIF"); - case VP_ENDIF : return("ENDIF"); - case VP_EOF : return("EOF"); - case VP_ERROR : return("ERROR"); - case VP_IFDEF : return("IFDEF"); - case VP_IFNDEF : return("IFNDEF"); - case VP_JOIN : return("JOIN"); - case VP_INCLUDE : return("INCLUDE"); - case VP_LINE : return("LINE"); - case VP_STRIFY : return("STRIFY"); - case VP_STRING : return("STRING"); - case VP_SYMBOL : return("SYMBOL"); - case VP_SYMBOL_JOIN : return("SYMBOL_JOIN"); - case VP_TEXT : return("TEXT"); - case VP_UNDEF : return("UNDEF"); - case VP_UNDEFINEALL : return("UNDEFINEALL"); - case VP_WHITE : return("WHITE"); + case VP_BACKQUOTE : return("BACKQUOTE"); + case VP_COMMENT : return("COMMENT"); + case VP_DEFARG : return("DEFARG"); + case VP_DEFFORM : return("DEFFORM"); + case VP_DEFINE : return("DEFINE"); + case VP_DEFREF : return("DEFREF"); + case VP_DEFREF_JOIN : return("DEFREF_JOIN"); + case VP_DEFVALUE : return("DEFVALUE"); + case VP_ELSE : return("ELSE"); + case VP_ELSIF : return("ELSIF"); + case VP_ENDIF : return("ENDIF"); + case VP_EOF : return("EOF"); + case VP_ERROR : return("ERROR"); + case VP_IFDEF : return("IFDEF"); + case VP_IFNDEF : return("IFNDEF"); + case VP_JOIN : return("JOIN"); + case VP_INCLUDE : return("INCLUDE"); + case VP_LINE : return("LINE"); + case VP_STRIFY : return("STRIFY"); + case VP_STRING : return("STRING"); + case VP_SYMBOL : return("SYMBOL"); + case VP_SYMBOL_JOIN : return("SYMBOL_JOIN"); + case VP_TEXT : return("TEXT"); + case VP_UNDEF : return("UNDEF"); + case VP_UNDEFINEALL : return("UNDEFINEALL"); + case VP_WHITE : return("WHITE"); default: return("?"); } } @@ -513,8 +517,8 @@ void V3PreProcImp::unputString(const string& strg) { // so instead we scan from a temporary buffer, then on EOF return. // This is also faster than the old scheme, amazingly. if (m_lexp->m_bufferState!=m_lexp->currentBuffer()) { - fatalSrc("bufferStack missing current buffer; will return incorrectly"); - // Hard to debug lost text as won't know till much later + fatalSrc("bufferStack missing current buffer; will return incorrectly"); + // Hard to debug lost text as won't know till much later } m_lexp->scanBytes(strg); } @@ -522,7 +526,7 @@ void V3PreProcImp::unputString(const string& strg) { void V3PreProcImp::unputDefrefString(const string& strg) { int multiline = 0; for (size_t i=0; i leadspace - && isspace(out[leadspace])) leadspace++; - if (leadspace) out.erase(0,leadspace); + && isspace(out[leadspace])) leadspace++; + if (leadspace) out.erase(0, leadspace); // Remove trailing whitespace if (trailing) { - string::size_type trailspace = 0; - while (out.length() > trailspace - && isspace(out[out.length()-1-trailspace])) trailspace++; - // Don't remove \{space_or_newline} - if (trailspace && out.length() > trailspace && out[out.length()-1-trailspace]=='\\') - trailspace--; - if (trailspace) out.erase(out.length()-trailspace,trailspace); + string::size_type trailspace = 0; + while (out.length() > trailspace + && isspace(out[out.length()-1-trailspace])) trailspace++; + // Don't remove \{space_or_newline} + if (trailspace && out.length() > trailspace && out[out.length()-1-trailspace]=='\\') + trailspace--; + if (trailspace) out.erase(out.length()-trailspace, trailspace); } return out; } @@ -561,7 +565,7 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { // parsed result. UINFO(4,"defineSubstIn `"<name()<<" "<params()<args().size(); i++) { - UINFO(4,"defineArg["<args()[i]<<"'"<name()); @@ -569,163 +573,167 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { std::map argValueByName; { // Parse argument list into map - unsigned numArgs=0; - string argName; - int paren = 1; // (), {} and [] can use same counter, as must be matched pair per spec - string token; - bool quote = false; - bool haveDefault = false; - // Note there's a leading ( and trailing ), so parens==1 is the base parsing level - string params = refp->params(); // Must keep str in scope to get pointer - const char* cp=params.c_str(); - if (*cp == '(') cp++; - for (; *cp; cp++) { - //UINFO(4," Parse Paren="<args().size() > numArgs) { - // A call `def( a ) must be equivelent to `def(a ), so trimWhitespace - // At one point we didn't trim trailing whitespace, but this confuses `" - string arg = trimWhitespace(refp->args()[numArgs], true); - if (arg != "") valueDef = arg; - } else if (!haveDefault) { - error("Define missing argument '"+argName+"' for: "+refp->name()+"\n"); - return " `"+refp->name()+" "; - } - numArgs++; - } - argValueByName[argName] = valueDef; - // Prepare for next - argName = ""; - token = ""; - haveDefault = false; - continue; - } - else if (*cp=='=') { - haveDefault = true; - argName = token; - token = ""; - continue; - } - } - if (cp[0]=='\\' && cp[1]) { - token += cp[0]; // \{any} Put out literal next character - token += cp[1]; - cp++; - continue; - } - if (!quote) { - if (*cp=='(' || *cp=='{' || *cp=='[') paren++; - else if (*cp==')' || *cp=='}' || *cp==']') paren--; - } - if (*cp=='"') quote=!quote; - if (*cp) token += *cp; - } - if (refp->args().size() > numArgs - // `define X() is ok to call with nothing - && !(refp->args().size()==1 && numArgs==0 && trimWhitespace(refp->args()[0],false)=="")) { - error("Define passed too many arguments: "+refp->name()+"\n"); - return " `"+refp->name()+" "; - } + unsigned numArgs = 0; + string argName; + int paren = 1; // (), {} and [] can use same counter, as must be matched pair per spec + string token; + bool quote = false; + bool haveDefault = false; + // Note there's a leading ( and trailing ), so parens==1 is the base parsing level + string params = refp->params(); // Must keep str in scope to get pointer + const char* cp = params.c_str(); + if (*cp == '(') cp++; + for (; *cp; cp++) { + //UINFO(4," Parse Paren="<args().size() > numArgs) { + // A call `def( a ) must be equivelent to `def(a ), so trimWhitespace + // At one point we didn't trim trailing + // whitespace, but this confuses `" + string arg = trimWhitespace(refp->args()[numArgs], true); + if (arg != "") valueDef = arg; + } else if (!haveDefault) { + error("Define missing argument '"+argName+"' for: "+refp->name()+"\n"); + return " `"+refp->name()+" "; + } + numArgs++; + } + argValueByName[argName] = valueDef; + // Prepare for next + argName = ""; + token = ""; + haveDefault = false; + continue; + } + else if (*cp=='=') { + haveDefault = true; + argName = token; + token = ""; + continue; + } + } + if (cp[0]=='\\' && cp[1]) { + token += cp[0]; // \{any} Put out literal next character + token += cp[1]; + cp++; + continue; + } + if (!quote) { + if (*cp=='(' || *cp=='{' || *cp=='[') paren++; + else if (*cp==')' || *cp=='}' || *cp==']') paren--; + } + if (*cp=='"') quote=!quote; + if (*cp) token += *cp; + } + if (refp->args().size() > numArgs + // `define X() is ok to call with nothing + && !(refp->args().size()==1 && numArgs==0 + && trimWhitespace(refp->args()[0], false)=="")) { + error("Define passed too many arguments: "+refp->name()+"\n"); + return " `"+refp->name()+" "; + } } string out; { // Parse substitution define using arguments - string argName; - bool quote = false; - bool backslashesc = false; // In \.....{space} block - // Note we go through the loop once more at the NULL end-of-string - for (const char* cp=value.c_str(); (*cp) || argName!=""; cp=(*cp?cp+1:cp)) { - //UINFO(4, "CH "<<*cp<<" an "<::iterator iter = argValueByName.find(argName); - if (iter != argValueByName.end()) { - // Substitute - string subst = iter->second; - if (subst == "") { - // Normally `` is removed later, but with no token after, we're otherwise - // stuck, so remove proceeding `` - if (out.size()>=2 && out.substr(out.size()-2) == "``") { - out = out.substr(0, out.size()-2); - } - } else { - out += subst; - } - } else { - out += argName; - } - argName = ""; - } - if (!quote) { - // Check for `` only after we've detected end-of-argname - if (cp[0]=='`' && cp[1]=='`') { - if (backslashesc) { - // Don't put out the ``, we're forming an escape which will not expand further later - } else { - out += "``"; // `` must get removed later, as `FOO```BAR must pre-expand FOO and BAR - // See also removal in empty substitutes above - } - cp++; - continue; - } - else if (cp[0]=='`' && cp[1]=='"') { - out += "`\""; // `" means to put out a " without enabling quote mode (sort of) - // however we must expand any macro calls inside it first. - // So keep it `", so we don't enter quote mode. - cp++; - continue; - } - else if (cp[0]=='`' && cp[1]=='\\' && cp[2]=='`' && cp[3]=='"') { - out += "`\\`\""; // `\`" means to put out a backslash quote - // Leave it literal until we parse the VP_STRIFY string - cp+=3; - continue; - } - else if (cp[0]=='`' && cp[1]=='\\') { - out += '\\'; // `\ means to put out a backslash - cp++; - continue; - } - else if (cp[0]=='\\' && cp[1]=='\n') { - // We kept the \\n when we lexed because we don't want whitespace - // trimming to mis-drop the final \\n - // At replacement time we need the standard newline. - out += "\n"; // \\n newline - cp++; - continue; - } - } - if (cp[0]=='\\' && cp[1]=='\"') { - out += cp[0]; // \{any} Put out literal next character - out += cp[1]; - cp++; - continue; - } - else if (cp[0]=='\\') { - // Normally \{any} would put out literal next character - // Instead we allow "`define A(nm) \nm" to expand, per proposed mantis1537 - out += cp[0]; - continue; - } - if (*cp=='"') quote=!quote; - if (*cp) out += *cp; - } + if (iter != argValueByName.end()) { + // Substitute + string subst = iter->second; + if (subst == "") { + // Normally `` is removed later, but with no token after, we're otherwise + // stuck, so remove proceeding `` + if (out.size()>=2 && out.substr(out.size()-2) == "``") { + out = out.substr(0, out.size()-2); + } + } else { + out += subst; + } + } else { + out += argName; + } + argName = ""; + } + if (!quote) { + // Check for `` only after we've detected end-of-argname + if (cp[0]=='`' && cp[1]=='`') { + if (backslashesc) { + // Don't put out the ``, we're forming an escape + // which will not expand further later + } else { + out += "``"; // `` must get removed later, as `FOO```BAR must pre-expand FOO and BAR + // See also removal in empty substitutes above + } + cp++; + continue; + } + else if (cp[0]=='`' && cp[1]=='"') { + out += "`\""; // `" means to put out a " without enabling quote mode (sort of) + // however we must expand any macro calls inside it first. + // So keep it `", so we don't enter quote mode. + cp++; + continue; + } + else if (cp[0]=='`' && cp[1]=='\\' && cp[2]=='`' && cp[3]=='"') { + out += "`\\`\""; // `\`" means to put out a backslash quote + // Leave it literal until we parse the VP_STRIFY string + cp += 3; + continue; + } + else if (cp[0]=='`' && cp[1]=='\\') { + out += '\\'; // `\ means to put out a backslash + cp++; + continue; + } + else if (cp[0]=='\\' && cp[1]=='\n') { + // We kept the \\n when we lexed because we don't want whitespace + // trimming to mis-drop the final \\n + // At replacement time we need the standard newline. + out += "\n"; // \\n newline + cp++; + continue; + } + } + if (cp[0]=='\\' && cp[1]=='\"') { + out += cp[0]; // \{any} Put out literal next character + out += cp[1]; + cp++; + continue; + } + else if (cp[0]=='\\') { + // Normally \{any} would put out literal next character + // Instead we allow "`define A(nm) \nm" to expand, per proposed mantis1537 + out += cp[0]; + continue; + } + if (*cp=='"') quote=!quote; + if (*cp) out += *cp; + } } UINFO(4,"defineSubstOut '"<readWholefile(filename, wholefile/*ref*/); if (!ok) { - error("File not found: "+filename+"\n"); - return; + error("File not found: "+filename+"\n"); + return; } if (!m_preprocp->isEof()) { // IE not the first file. - // We allow the same include file twice, because occasionally it pops - // up, with guards preventing a real recursion. - if (m_lexp->m_streampStack.size()>V3PreProc::INCLUDE_DEPTH_MAX) { - error("Recursive inclusion of file: "+filename); - return; - } - // There's already a file active. Push it to work on the new one. - addLineComment(0); + // We allow the same include file twice, because occasionally it pops + // up, with guards preventing a real recursion. + if (m_lexp->m_streampStack.size()>V3PreProc::INCLUDE_DEPTH_MAX) { + error("Recursive inclusion of file: "+filename); + return; + } + // There's already a file active. Push it to work on the new one. + addLineComment(0); } // Create new stream structure m_lexp->scanNewFile(m_preprocp->fileline()->create(filename, 1)); - addLineComment(1); // Enter + addLineComment(1); // Enter // Filter all DOS CR's en-mass. This avoids bugs with lexing CRs in the wrong places. - // This will also strip them from strings, but strings aren't supposed to be multi-line without a "\" + // This will also strip them from strings, but strings aren't supposed + // to be multi-line without a "\" for (StrList::iterator it=wholefile.begin(); it!=wholefile.end(); ++it) { - // We don't end-loop at \0 as we allow and strip mid-string '\0's (for now). - bool strip = false; - const char* sp = it->data(); - const char* ep = sp + it->length(); - // Only process if needed, as saves extra string allocations - for (const char* cp=sp; cplength()); - for (const char* cp=sp; cpdata(); + const char* ep = sp + it->length(); + // Only process if needed, as saves extra string allocations + for (const char* cp=sp; cplength()); + for (const char* cp=sp; cpscanBytesBack(*it); - // Reclaim memory; the push saved the string contents for us - *it = ""; + // Push the data to an internal buffer. + m_lexp->scanBytesBack(*it); + // Reclaim memory; the push saved the string contents for us + *it = ""; } } @@ -797,7 +806,7 @@ void V3PreProcImp::insertUnreadbackAtBol(const string& text) { // We don't always add a leading newline, as it may result in extra unreadback(newlines). if (m_lineCmt == "") { m_lineCmtNl = true; } else if (m_lineCmt[m_lineCmt.length()-1]!='\n') { - insertUnreadback("\n"); + insertUnreadback("\n"); } insertUnreadback(text); } @@ -822,53 +831,53 @@ int V3PreProcImp::getRawToken() { // Get a token from the file, whatever it may be. while (1) { next_tok: - if (m_lineAdd) { - m_lineAdd--; - m_rawAtBol = true; - yyourtext("\n",1); - if (debug()>=5) debugToken(VP_WHITE, "LNA"); - return (VP_WHITE); - } - if (m_lineCmt!="") { - // We have some `line directive or other processed data to return to the user. - static string rtncmt; // Keep the c string till next call - rtncmt = m_lineCmt; - if (m_lineCmtNl) { + if (m_lineAdd) { + m_lineAdd--; + m_rawAtBol = true; + yyourtext("\n", 1); + if (debug()>=5) debugToken(VP_WHITE, "LNA"); + return (VP_WHITE); + } + if (m_lineCmt!="") { + // We have some `line directive or other processed data to return to the user. + static string rtncmt; // Keep the c string till next call + rtncmt = m_lineCmt; + if (m_lineCmtNl) { if (!m_rawAtBol) rtncmt.insert(0, "\n"); - m_lineCmtNl = false; - } - yyourtext(rtncmt.c_str(), rtncmt.length()); - m_lineCmt = ""; - if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n'); - if (state()==ps_DEFVALUE) { - V3PreLex::s_currentLexp->appendDefValue(yyourtext(),yyourleng()); - goto next_tok; - } else { - if (debug()>=5) debugToken(VP_TEXT, "LCM"); - return (VP_TEXT); - } - } - if (isEof()) return (VP_EOF); + m_lineCmtNl = false; + } + yyourtext(rtncmt.c_str(), rtncmt.length()); + m_lineCmt = ""; + if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n'); + if (state()==ps_DEFVALUE) { + V3PreLex::s_currentLexp->appendDefValue(yyourtext(), yyourleng()); + goto next_tok; + } else { + if (debug()>=5) debugToken(VP_TEXT, "LCM"); + return (VP_TEXT); + } + } + if (isEof()) return (VP_EOF); - // Snarf next token from the file - int tok = m_lexp->lex(); + // Snarf next token from the file + int tok = m_lexp->lex(); - if (debug()>=5) debugToken(tok, "RAW"); + if (debug()>=5) debugToken(tok, "RAW"); - // A EOF on an include, so we can print `line and detect mis-matched "s - if (tok==VP_EOF) { - goto next_tok; // find the EOF, after adding needed lines - } + // A EOF on an include, so we can print `line and detect mis-matched "s + if (tok==VP_EOF) { + goto next_tok; // find the EOF, after adding needed lines + } - if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n'); - return tok; + if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n'); + return tok; } } void V3PreProcImp::debugToken(int tok, const char* cmtp) { if (debug()>=5) { string buf = string(yyourtext(), yyourleng()); - string::size_type pos; + string::size_type pos; while ((pos = buf.find('\n')) != string::npos) { buf.replace(pos, 1, "\\n"); } while ((pos = buf.find('\r')) != string::npos) { buf.replace(pos, 1, "\\r"); } fprintf(stderr, "%d: %s %s %s(%d) dr%d: <%d>%-10s: %s\n", @@ -886,529 +895,537 @@ int V3PreProcImp::getStateToken() { // Return the next state-determined token while (1) { next_tok: - if (isEof()) return VP_EOF; - int tok = getRawToken(); + if (isEof()) return VP_EOF; + int tok = getRawToken(); - // Most states emit white space and comments between tokens. (Unless collecting a string) - if (tok==VP_WHITE && state() !=ps_STRIFY) return (tok); - if (tok==VP_BACKQUOTE && state() !=ps_STRIFY) { tok = VP_TEXT; } - if (tok==VP_COMMENT) { - if (!m_off) { - if (m_lexp->m_keepComments == KEEPCMT_SUB) { - string rtn; rtn.assign(yyourtext(),yyourleng()); - comment(rtn); + // Most states emit white space and comments between tokens. (Unless collecting a string) + if (tok==VP_WHITE && state() !=ps_STRIFY) return (tok); + if (tok==VP_BACKQUOTE && state() !=ps_STRIFY) { tok = VP_TEXT; } + if (tok==VP_COMMENT) { + if (!m_off) { + if (m_lexp->m_keepComments == KEEPCMT_SUB) { + string rtn; rtn.assign(yyourtext(), yyourleng()); + comment(rtn); // Need to ensure "foo/**/bar" becomes two tokens insertUnreadback(" "); - } else if (m_lexp->m_keepComments) { - return (tok); - } else { + } else if (m_lexp->m_keepComments) { + return (tok); + } else { // Need to ensure "foo/**/bar" becomes two tokens insertUnreadback(" "); - } - } - // We're off or processed the comment specially. If there are newlines - // in it, we also return the newlines as TEXT so that the linenumber - // count is maintained for downstream tools + } + } + // We're off or processed the comment specially. If there are newlines + // in it, we also return the newlines as TEXT so that the linenumber + // count is maintained for downstream tools for (size_t len=0; len(yyourleng()); len++) { if (yyourtext()[len]=='\n') m_lineAdd++; } - goto next_tok; - } - if (tok==VP_LINE) { - addLineComment(m_lexp->m_enterExit); - goto next_tok; - } + goto next_tok; + } + if (tok==VP_LINE) { + addLineComment(m_lexp->m_enterExit); + goto next_tok; + } - if (tok==VP_DEFREF_JOIN) { - // Here's something fun and unspecified as yet: - // The existance of non-existance of a base define changes `` expansion - // `define QA_b zzz - // `define Q1 `QA``_b - // 1Q1 -> zzz - // `define QA a - // `Q1 -> a_b - // Note parenthesis make this unambiguous - // `define Q1 `QA()``_b // -> a_b - // This may be a side effect of how `UNDEFINED remains as `UNDEFINED, - // but it screws up our method here. So hardcode it. - string name (yyourtext()+1,yyourleng()-1); - if (defExists(name)) { // JOIN(DEFREF) - // Put back the `` and process the defref - UINFO(5,"```: define "< string doesn't include the ``, so can just grab next and continue - string out (yyourtext(),yyourleng()); - UINFO(5,"`` LHS:"< zzz + // `define QA a + // `Q1 -> a_b + // Note parenthesis make this unambiguous + // `define Q1 `QA()``_b // -> a_b + // This may be a side effect of how `UNDEFINED remains as `UNDEFINED, + // but it screws up our method here. So hardcode it. + string name (yyourtext()+1, yyourleng()-1); + if (defExists(name)) { // JOIN(DEFREF) + // Put back the `` and process the defref + UINFO(5,"```: define "< string doesn't include the ``, so can just grab next and continue + string out (yyourtext(), yyourleng()); + UINFO(5,"`` LHS:"<pushStateDefForm(); - goto next_tok; - } - else fatalSrc("Bad case\n"); - goto next_tok; - } - else if (tok==VP_TEXT) { - // IE, something like comment between define and symbol - if (!m_off) return tok; - else goto next_tok; - } - else if (tok==VP_DEFREF) { - // IE, `ifdef `MACRO(x): Substitue and come back here when state pops. - break; - } - else { + // Deal with some special parser states + switch (state()) { + case ps_TOP: { + break; + } + case ps_DEFNAME_UNDEF: // FALLTHRU + case ps_DEFNAME_DEFINE: // FALLTHRU + case ps_DEFNAME_IFDEF: // FALLTHRU + case ps_DEFNAME_IFNDEF: // FALLTHRU + case ps_DEFNAME_ELSIF: { + if (tok==VP_SYMBOL) { + m_lastSym.assign(yyourtext(), yyourleng()); + if (state()==ps_DEFNAME_IFDEF + || state()==ps_DEFNAME_IFNDEF) { + bool enable = defExists(m_lastSym); + UINFO(4,"Ifdef "<pushStateDefForm(); + goto next_tok; + } + else fatalSrc("Bad case\n"); + goto next_tok; + } + else if (tok==VP_TEXT) { + // IE, something like comment between define and symbol + if (!m_off) return tok; + else goto next_tok; + } + else if (tok==VP_DEFREF) { + // IE, `ifdef `MACRO(x): Substitue and come back here when state pops. + break; + } + else { error(string("Expecting define name. Found: ")+tokenName(tok)+"\n"); - goto next_tok; - } - } - case ps_DEFFORM: { - if (tok==VP_DEFFORM) { - m_formals = m_lexp->m_defValue; - if (debug()>=5) cout<<"DefFormals='"<pushStateDefValue(); - goto next_tok; - } else if (tok==VP_TEXT) { - // IE, something like comment in formals - if (!m_off) return tok; - else goto next_tok; - } else { + goto next_tok; + } + } + case ps_DEFFORM: { + if (tok==VP_DEFFORM) { + m_formals = m_lexp->m_defValue; + if (debug()>=5) cout<<"DefFormals='"<pushStateDefValue(); + goto next_tok; + } else if (tok==VP_TEXT) { + // IE, something like comment in formals + if (!m_off) return tok; + else goto next_tok; + } else { error(string("Expecting define formal arguments. Found: ")+tokenName(tok)+"\n"); - goto next_tok; - } - } - case ps_DEFVALUE: { - static string newlines; - newlines = "\n"; // Always start with trailing return - if (tok == VP_DEFVALUE) { - if (debug()>=5) cout<<"DefValue='"<m_defValue) - <<"' formals='"<m_defValue; - // Remove returns - // Not removing returns in values has two problems, - // 1. we need to correct line numbers with `line after each substitution - // 2. Substituting in " .... " with embedded returns requires \ escape. - // This is very difficult in the presence of `", so we keep the \ before the newline. - for (size_t i=0; i=5) cout<<"DefValue='"<m_defValue) + <<"' formals='"<m_defValue; + // Remove returns + // Not removing returns in values has two problems, + // 1. we need to correct line numbers with `line after each substitution + // 2. Substituting in " .... " with embedded returns requires \ escape. + // This is very difficult in the presence of `", so we + // keep the \ before the newline. + for (size_t i=0; iname()+"\n"); - statePop(); - goto next_tok; - } - } - case ps_DEFARG: { - if (m_defRefs.empty()) fatalSrc("Shouldn't be in DEFARG w/o active defref"); - V3DefineRef* refp = &(m_defRefs.top()); - refp->nextarg(refp->nextarg()+m_lexp->m_defValue); m_lexp->m_defValue=""; - UINFO(4,"defarg++ "<nextarg()<args().push_back(refp->nextarg()); - stateChange(ps_DEFARG); - m_lexp->pushStateDefArg(1); - refp->nextarg(""); - goto next_tok; - } else if (tok==VP_DEFARG && yyourleng()==1 && yyourtext()[0]==')') { - // Substitute in and prepare for next action - // Similar code in non-parenthesized define (Search for END_OF_DEFARG) - refp->args().push_back(refp->nextarg()); - string out; - if (!m_off) { - out = defineSubst(refp); - //NOP: out = m_preprocp->defSubstitute(out); - } - m_defRefs.pop(); VL_DANGLING(refp); - if (m_defRefs.empty()) { - statePop(); - if (state() == ps_JOIN) { // Handle {left}```FOO(ARG) where `FOO(ARG) might be empty - if (m_joinStack.empty()) fatalSrc("`` join stack empty, but in a ``"); - string lhs = m_joinStack.top(); m_joinStack.pop(); + statePop(); + goto next_tok; + } + } + case ps_DEFARG: { + if (m_defRefs.empty()) fatalSrc("Shouldn't be in DEFARG w/o active defref"); + V3DefineRef* refp = &(m_defRefs.top()); + refp->nextarg(refp->nextarg()+m_lexp->m_defValue); m_lexp->m_defValue = ""; + UINFO(4,"defarg++ "<nextarg()<args().push_back(refp->nextarg()); + stateChange(ps_DEFARG); + m_lexp->pushStateDefArg(1); + refp->nextarg(""); + goto next_tok; + } else if (tok==VP_DEFARG && yyourleng()==1 && yyourtext()[0]==')') { + // Substitute in and prepare for next action + // Similar code in non-parenthesized define (Search for END_OF_DEFARG) + refp->args().push_back(refp->nextarg()); + string out; + if (!m_off) { + out = defineSubst(refp); + //NOP: out = m_preprocp->defSubstitute(out); + } + m_defRefs.pop(); VL_DANGLING(refp); + if (m_defRefs.empty()) { + statePop(); + if (state() == ps_JOIN) { // Handle {left}```FOO(ARG) where `FOO(ARG) might be empty + if (m_joinStack.empty()) fatalSrc("`` join stack empty, but in a ``"); + string lhs = m_joinStack.top(); m_joinStack.pop(); out.insert(0, lhs); - UINFO(5,"``-end-defarg Out:"<m_parenLevel = 0; - } - else { // Finished a defref inside a upper defref - // Can't subst now, or - // `define a(ign) x,y - // foo(`a(ign),`b) would break because a contains comma - refp = &(m_defRefs.top()); // We popped, so new top - refp->nextarg(refp->nextarg()+m_lexp->m_defValue+out); m_lexp->m_defValue=""; - m_lexp->m_parenLevel = refp->parenLevel(); - statePop(); // Will go to ps_DEFARG, as we're under another define - } - goto next_tok; - } else if (tok==VP_DEFREF) { - // Expand it, then state will come back here - // Value of building argument is data before the lower defref - // we'll append it when we push the argument. - break; - } else if (tok==VP_SYMBOL || tok==VP_STRING || tok==VP_TEXT || tok==VP_WHITE) { - string rtn; rtn.assign(yyourtext(),yyourleng()); - refp->nextarg(refp->nextarg()+rtn); - goto next_tok; + UINFO(5,"``-end-defarg Out:"<m_parenLevel = 0; + } + else { // Finished a defref inside a upper defref + // Can't subst now, or + // `define a(ign) x,y + // foo(`a(ign),`b) would break because a contains comma + refp = &(m_defRefs.top()); // We popped, so new top + refp->nextarg(refp->nextarg()+m_lexp->m_defValue+out); m_lexp->m_defValue = ""; + m_lexp->m_parenLevel = refp->parenLevel(); + statePop(); // Will go to ps_DEFARG, as we're under another define + } + goto next_tok; + } else if (tok==VP_DEFREF) { + // Expand it, then state will come back here + // Value of building argument is data before the lower defref + // we'll append it when we push the argument. + break; + } else if (tok==VP_SYMBOL || tok==VP_STRING || tok==VP_TEXT || tok==VP_WHITE) { + string rtn; rtn.assign(yyourtext(), yyourleng()); + refp->nextarg(refp->nextarg()+rtn); + goto next_tok; } else if (tok==VP_STRIFY) { // We must expand stringinfication, when done will return to this state statePush(ps_STRIFY); goto next_tok; - } else { - error(string("Expecting ) or , to end argument list for define reference. Found: ")+tokenName(tok)); - statePop(); - goto next_tok; - } - } - case ps_INCNAME: { - if (tok==VP_STRING) { - statePop(); - m_lastSym.assign(yyourtext(),yyourleng()); - UINFO(4,"Include "< - stateChange(ps_INCNAME); // Still - m_lexp->pushStateIncFilename(); - goto next_tok; - } - else if (tok==VP_DEFREF - || tok==VP_STRIFY) { - // Expand it, then state will come back here - break; - } - else { - statePop(); + } else { + error(string("Expecting ) or , to end argument list for define reference. Found: ") + +tokenName(tok)); + statePop(); + goto next_tok; + } + } + case ps_INCNAME: { + if (tok==VP_STRING) { + statePop(); + m_lastSym.assign(yyourtext(), yyourleng()); + UINFO(4,"Include "< + stateChange(ps_INCNAME); // Still + m_lexp->pushStateIncFilename(); + goto next_tok; + } + else if (tok==VP_DEFREF + || tok==VP_STRIFY) { + // Expand it, then state will come back here + break; + } + else { + statePop(); error(string("Expecting include filename. Found: ")+tokenName(tok)+"\n"); - goto next_tok; - } - } - case ps_ERRORNAME: { - if (tok==VP_STRING) { - if (!m_off) { - m_lastSym.assign(yyourtext(),yyourleng()); - error(m_lastSym); - } - statePop(); - goto next_tok; - } - else { + goto next_tok; + } + } + case ps_ERRORNAME: { + if (tok==VP_STRING) { + if (!m_off) { + m_lastSym.assign(yyourtext(), yyourleng()); + error(m_lastSym); + } + statePop(); + goto next_tok; + } + else { error(string("Expecting `error string. Found: ")+tokenName(tok)+"\n"); - statePop(); - goto next_tok; - } - } - case ps_JOIN: { - if (tok==VP_SYMBOL || tok==VP_TEXT) { - if (m_joinStack.empty()) fatalSrc("`` join stack empty, but in a ``"); - string lhs = m_joinStack.top(); m_joinStack.pop(); - UINFO(5,"`` LHS:"< V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { - error("Recursive `define substitution: `"+name); - goto next_tok; - } - // Substitute - if (!defExists(name)) { // Not found, return original string as-is - m_defDepth = 0; - UINFO(4,"Defref `"< not_defined"<defSubstitute(out); - if (m_defRefs.empty()) { - // Just output the substitution - if (state() == ps_JOIN) { // Handle {left}```FOO where `FOO might be empty - if (m_joinStack.empty()) fatalSrc("`` join stack empty, but in a ``"); - string lhs = m_joinStack.top(); m_joinStack.pop(); + case VP_DEFREF: { + // m_off not right here, but inside substitution, to make this work: + // `ifdef NEVER `DEFUN(`endif) + string name (yyourtext()+1, yyourleng()-1); + UINFO(4,"DefRef "< V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { + error("Recursive `define substitution: `"+name); + goto next_tok; + } + // Substitute + if (!defExists(name)) { // Not found, return original string as-is + m_defDepth = 0; + UINFO(4,"Defref `"< not_defined"<defSubstitute(out); + if (m_defRefs.empty()) { + // Just output the substitution + if (state() == ps_JOIN) { // Handle {left}```FOO where `FOO might be empty + if (m_joinStack.empty()) fatalSrc("`` join stack empty, but in a ``"); + string lhs = m_joinStack.top(); m_joinStack.pop(); out.insert(0, lhs); - UINFO(5,"``-end-defref Out:"<nextarg(refp->nextarg()+m_lexp->m_defValue+out); m_lexp->m_defValue=""; - } - goto next_tok; - } - else { // Found, with parameters - UINFO(4,"Defref `"< parameterized"<m_parenLevel); - m_defRefs.push(V3DefineRef(name, params)); - statePush(ps_DEFPAREN); - m_lexp->pushStateDefArg(0); - goto next_tok; - } - } - fatalSrc("Bad case\n"); // FALLTHRU - goto next_tok; // but not really, above fatal catches it, but if add goto, static analysis complains - } - case VP_ERROR: { - statePush(ps_ERRORNAME); - goto next_tok; - } - case VP_EOF: - if (!m_ifdefStack.empty()) { - error("`ifdef not terminated at EOF\n"); - } - return tok; - case VP_UNDEFINEALL: - if (!m_off) { - UINFO(4,"Undefineall "<nextarg(refp->nextarg()+m_lexp->m_defValue+out); + m_lexp->m_defValue = ""; + } + goto next_tok; + } + else { // Found, with parameters + UINFO(4,"Defref `"< parameterized"<m_parenLevel); + m_defRefs.push(V3DefineRef(name, params)); + statePush(ps_DEFPAREN); + m_lexp->pushStateDefArg(0); + goto next_tok; + } + } + fatalSrc("Bad case\n"); // FALLTHRU + goto next_tok; // above fatal means unreachable, but fixes static analysis warning + } + case VP_ERROR: { + statePush(ps_ERRORNAME); + goto next_tok; + } + case VP_EOF: + if (!m_ifdefStack.empty()) { + error("`ifdef not terminated at EOF\n"); + } + return tok; + case VP_UNDEFINEALL: + if (!m_off) { + UINFO(4,"Undefineall "<=5) { - string bufcln = V3PreLex::cleanDbgStrg(buf); - fprintf(stderr,"%d: FIN: %-10s: %s\n", + string bufcln = V3PreLex::cleanDbgStrg(buf); + fprintf(stderr, "%d: FIN: %-10s: %s\n", m_lexp->m_tokFilelinep->lineno(), tokenName(tok), bufcln.c_str()); } // Track `line const char* bufp = buf.c_str(); while (*bufp == '\n') bufp++; - if ((tok == VP_TEXT || tok == VP_LINE) && 0==strncmp(bufp,"`line ",6)) { - int enter; - m_finFilelinep->lineDirective(bufp, enter/*ref*/); + if ((tok == VP_TEXT || tok == VP_LINE) && 0==strncmp(bufp, "`line ", 6)) { + int enter; + m_finFilelinep->lineDirective(bufp, enter/*ref*/); } else { - if (m_finAtBol && !(tok==VP_TEXT && buf=="\n") - && m_preprocp->lineDirectives()) { - if (int outBehind = m_lexp->m_tokFilelinep->lineno() - m_finFilelinep->lineno()) { + if (m_finAtBol && !(tok==VP_TEXT && buf=="\n") + && m_preprocp->lineDirectives()) { + if (int outBehind = m_lexp->m_tokFilelinep->lineno() - m_finFilelinep->lineno()) { if (debug()>=5) { fprintf(stderr, "%d: FIN: readjust, fin at %d request at %d\n", m_lexp->m_tokFilelinep->lineno(), m_finFilelinep->lineno(), m_lexp->m_tokFilelinep->lineno()); } - m_finFilelinep = m_finFilelinep->create(m_lexp->m_tokFilelinep->filename(),m_lexp->m_tokFilelinep->lineno()); + m_finFilelinep = m_finFilelinep->create(m_lexp->m_tokFilelinep->filename(), + m_lexp->m_tokFilelinep->lineno()); if (outBehind > 0 && (outBehind <= static_cast(V3PreProc::NEWLINES_VS_TICKLINE))) { - // Output stream is behind, send newlines to get back in sync - // (Most likely because we're completing a disabled `endif) - if (m_preprocp->keepWhitespace()) { - buf = string(outBehind,'\n'); - return VP_TEXT; - } - } else { - // Need to backup, use `line - buf = m_finFilelinep->lineDirectiveStrg(0); - return VP_LINE; - } - } - } - // Track newlines in prep for next token - for (string::iterator cp=buf.begin(); cp!=buf.end(); ++cp) { - if (*cp == '\n') { - m_finAtBol = true; - m_finFilelinep->linenoIncInPlace(); // Increment in place to avoid new/delete calls. It's private data. - } else { - m_finAtBol = false; - } - } + // Output stream is behind, send newlines to get back in sync + // (Most likely because we're completing a disabled `endif) + if (m_preprocp->keepWhitespace()) { + buf = string(outBehind,'\n'); + return VP_TEXT; + } + } else { + // Need to backup, use `line + buf = m_finFilelinep->lineDirectiveStrg(0); + return VP_LINE; + } + } + } + // Track newlines in prep for next token + for (string::iterator cp=buf.begin(); cp!=buf.end(); ++cp) { + if (*cp == '\n') { + m_finAtBol = true; + // Increment in place to avoid new/delete calls. It's private data. + m_finFilelinep->linenoIncInPlace(); + } else { + m_finAtBol = false; + } + } } m_finAhead = false; // Consumed the token return tok; @@ -1478,33 +1497,33 @@ string V3PreProcImp::getline() { if (isEof()) return ""; const char* rtnp; bool gotEof = false; - while (NULL==(rtnp=strchr(m_lineChars.c_str(),'\n')) && !gotEof) { - string buf; - int tok = getFinalToken(buf/*ref*/); - if (debug()>=5) { - string bufcln = V3PreLex::cleanDbgStrg(buf); - fprintf(stderr,"%d: GETFETC: %-10s: %s\n", + while (NULL == (rtnp = strchr(m_lineChars.c_str(), '\n')) && !gotEof) { + string buf; + int tok = getFinalToken(buf/*ref*/); + if (debug()>=5) { + string bufcln = V3PreLex::cleanDbgStrg(buf); + fprintf(stderr, "%d: GETFETC: %-10s: %s\n", m_lexp->m_tokFilelinep->lineno(), tokenName(tok), bufcln.c_str()); - } - if (tok==VP_EOF) { - // Add a final newline, if the user forgot the final \n. - if (m_lineChars != "" && m_lineChars[m_lineChars.length()-1] != '\n') { - m_lineChars.append("\n"); - } - gotEof = true; - } - else { - m_lineChars.append(buf); - } + } + if (tok==VP_EOF) { + // Add a final newline, if the user forgot the final \n. + if (m_lineChars != "" && m_lineChars[m_lineChars.length()-1] != '\n') { + m_lineChars.append("\n"); + } + gotEof = true; + } + else { + m_lineChars.append(buf); + } } // Make new string with data up to the newline. int len = rtnp-m_lineChars.c_str()+1; string theLine(m_lineChars, 0, len); - m_lineChars = m_lineChars.erase(0,len); // Remove returned characters + m_lineChars = m_lineChars.erase(0, len); // Remove returned characters if (debug()>=4) { - string lncln = V3PreLex::cleanDbgStrg(theLine); - fprintf(stderr,"%d: GETLINE: %s\n", + string lncln = V3PreLex::cleanDbgStrg(theLine); + fprintf(stderr, "%d: GETLINE: %s\n", m_lexp->m_tokFilelinep->lineno(), lncln.c_str()); } return theLine; diff --git a/src/V3PreProc.h b/src/V3PreProc.h index d61d4a845..defc3e8ae 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -42,52 +42,54 @@ class V3PreProc { protected: // STATE - int m_debug; // Debugging + int m_debug; // Debugging public: // CONSTANTS enum MiscConsts { - DEFINE_RECURSION_LEVEL_MAX = 1000, // How many `def substitutions before an error - INCLUDE_DEPTH_MAX = 500, // How many `includes deep before an error - STREAM_DEPTH_LEVEL_MAX = 2000, // How many streams deep (sometimes `def deep) before an error - // // Set more than DEFINE_RECURSION_LEVEL_MAX or INCLUDE_DEPTH_MAX - NEWLINES_VS_TICKLINE = 20 // Use `line in place of this many newlines + DEFINE_RECURSION_LEVEL_MAX = 1000, // How many `def substitutions before an error + INCLUDE_DEPTH_MAX = 500, // How many `includes deep before an error + STREAM_DEPTH_LEVEL_MAX = 2000, // How many streams deep (sometimes `def deep) before an error + // // Set more than DEFINE_RECURSION_LEVEL_MAX + // // or INCLUDE_DEPTH_MAX + NEWLINES_VS_TICKLINE = 20 // Use `line in place of this many newlines }; // ACCESSORS // Insert given file into this point in input stream - virtual void openFile(FileLine* fileline, V3InFilter* filterp, const string& filename)=0; - virtual string getline()=0; // Return next line/lines. (Null if done.) - virtual bool isEof() const =0; // Return true on EOF. + virtual void openFile(FileLine* fileline, V3InFilter* filterp, const string& filename) = 0; + virtual string getline() = 0; // Return next line/lines. (Null if done.) + virtual bool isEof() const = 0; // Return true on EOF. virtual void insertUnreadback(const string& text) = 0; int debug() const { return m_debug; } void debug(int level) { m_debug = level; } - FileLine* fileline(); ///< File/Line number for last getline call + FileLine* fileline(); ///< File/Line number for last getline call // CONTROL METHODS // These options control how the parsing proceeds - static int keepComments() { return 2; } // Return comments, 0=no, 1=yes, 2=callback + static int keepComments() { return 2; } // Return comments, 0=no, 1=yes, 2=callback static bool keepWhitespace() { return false; } - static bool lineDirectives() { // Insert `line directives - return !(v3Global.opt.preprocOnly() && v3Global.opt.preprocNoLine()); + static bool lineDirectives() { // Insert `line directives + return !(v3Global.opt.preprocOnly() && v3Global.opt.preprocNoLine()); } - static bool pedantic() { return false; } // Obey standard; Don't substitute `error + static bool pedantic() { return false; } // Obey standard; Don't substitute `error // CALLBACK METHODS // This probably will want to be overridden for given child users of this class. - virtual void comment(const string& cmt)=0; // Comment detected (if keepComments==2) - virtual void include(const string& filename)=0; // Request a include file be processed + virtual void comment(const string& cmt) = 0; // Comment detected (if keepComments==2) + virtual void include(const string& filename) = 0; // Request a include file be processed - virtual void undef(const string& name)=0; // Remove a definition + virtual void undef(const string& name) = 0; // Remove a definition virtual void define(FileLine* fileline, const string& name, - const string& value, const string& params="", bool cmdline=false)=0; // `define without any parameters + const string& value, const string& params="", + bool cmdline=false) = 0; // `define without any parameters virtual void defineCmdLine(FileLine* fileline, const string& name, - const string& value) { // `define without any parameters - define(fileline, name, value, "", true); + const string& value) { // `define without any parameters + define(fileline, name, value, "", true); } - virtual string removeDefines(const string& text)=0; // Remove defines in a text string + virtual string removeDefines(const string& text) = 0; // Remove defines in a text string // UTILITIES void error(const string& msg) { fileline()->v3error(msg); } ///< Report an error @@ -97,7 +99,7 @@ public: protected: // CONSTUCTORS V3PreProc() { - m_debug=0; + m_debug=0; }; void configure(FileLine* fl); public: @@ -105,4 +107,4 @@ public: virtual ~V3PreProc() {} }; -#endif // Guard +#endif // Guard diff --git a/src/V3PreShell.cpp b/src/V3PreShell.cpp index ae7145c97..c2b42f71e 100644 --- a/src/V3PreShell.cpp +++ b/src/V3PreShell.cpp @@ -41,116 +41,117 @@ protected: friend class V3PreShell; static V3PreShellImp s_preImp; - static V3PreProc* s_preprocp; - static V3InFilter* s_filterp; + static V3PreProc* s_preprocp; + static V3InFilter* s_filterp; //--------------------------------------- // METHODS static int debug(bool reset=false) { - static int level = -1; - if (VL_UNLIKELY(level < 0) || reset) { - level = v3Global.opt.debugSrcLevel(__FILE__); - if (s_preprocp) s_preprocp->debug(debug()); - } - return level; + static int level = -1; + if (VL_UNLIKELY(level < 0) || reset) { + level = v3Global.opt.debugSrcLevel(__FILE__); + if (s_preprocp) s_preprocp->debug(debug()); + } + return level; } void boot(char** env) { - // Create the implementation pointer - if (env) {} - if (!s_preprocp) { - FileLine* cmdfl = new FileLine("COMMAND_LINE",0); - s_preprocp = V3PreProc::createPreProc(cmdfl); - s_preprocp->debug(debug()); - // Default defines - FileLine* prefl = new FileLine("INTERNAL_VERILATOR_DEFINE",0); - s_preprocp->defineCmdLine(prefl,"VERILATOR", "1"); // LEAK_OK - s_preprocp->defineCmdLine(prefl,"verilator", "1"); // LEAK_OK - s_preprocp->defineCmdLine(prefl,"verilator3", "1"); // LEAK_OK - s_preprocp->defineCmdLine(prefl,"systemc_clock", "/*verilator systemc_clock*/"); // LEAK_OK - s_preprocp->defineCmdLine(prefl,"coverage_block_off", "/*verilator coverage_block_off*/"); // LEAK_OK - if (prefl->language().systemVerilog()) { - // Synthesis compatibility - s_preprocp->defineCmdLine(prefl,"SYSTEMVERILOG", "1"); // LEAK_OK - // IEEE predefined - s_preprocp->defineCmdLine(prefl,"SV_COV_START", "0"); - s_preprocp->defineCmdLine(prefl,"SV_COV_STOP", "1"); - s_preprocp->defineCmdLine(prefl,"SV_COV_RESET", "2"); - s_preprocp->defineCmdLine(prefl,"SV_COV_CHECK", "3"); - s_preprocp->defineCmdLine(prefl,"SV_COV_MODULE", "10"); - s_preprocp->defineCmdLine(prefl,"SV_COV_HIER", "11"); - s_preprocp->defineCmdLine(prefl,"SV_COV_ASSERTION", "20"); - s_preprocp->defineCmdLine(prefl,"SV_COV_FSM_STATE", "21"); - s_preprocp->defineCmdLine(prefl,"SV_COV_STATEMENT", "22"); - s_preprocp->defineCmdLine(prefl,"SV_COV_TOGGLE", "23"); - s_preprocp->defineCmdLine(prefl,"SV_COV_OVERFLOW", "-2"); - s_preprocp->defineCmdLine(prefl,"SV_COV_ERROR", "-1"); - s_preprocp->defineCmdLine(prefl,"SV_COV_NOCOV", "0"); - s_preprocp->defineCmdLine(prefl,"SV_COV_OK", "1"); - s_preprocp->defineCmdLine(prefl,"SV_COV_PARTIAL", "2"); - } - } + // Create the implementation pointer + if (env) {} + if (!s_preprocp) { + FileLine* cmdfl = new FileLine("COMMAND_LINE", 0); + s_preprocp = V3PreProc::createPreProc(cmdfl); + s_preprocp->debug(debug()); + // Default defines + FileLine* prefl = new FileLine("INTERNAL_VERILATOR_DEFINE", 0); + s_preprocp->defineCmdLine(prefl, "VERILATOR", "1"); // LEAK_OK + s_preprocp->defineCmdLine(prefl, "verilator", "1"); // LEAK_OK + s_preprocp->defineCmdLine(prefl, "verilator3", "1"); // LEAK_OK + s_preprocp->defineCmdLine(prefl, "systemc_clock", "/*verilator systemc_clock*/"); // LEAK_OK + s_preprocp->defineCmdLine(prefl, "coverage_block_off", "/*verilator coverage_block_off*/"); // LEAK_OK + if (prefl->language().systemVerilog()) { + // Synthesis compatibility + s_preprocp->defineCmdLine(prefl, "SYSTEMVERILOG", "1"); // LEAK_OK + // IEEE predefined + s_preprocp->defineCmdLine(prefl, "SV_COV_START", "0"); + s_preprocp->defineCmdLine(prefl, "SV_COV_STOP", "1"); + s_preprocp->defineCmdLine(prefl, "SV_COV_RESET", "2"); + s_preprocp->defineCmdLine(prefl, "SV_COV_CHECK", "3"); + s_preprocp->defineCmdLine(prefl, "SV_COV_MODULE", "10"); + s_preprocp->defineCmdLine(prefl, "SV_COV_HIER", "11"); + s_preprocp->defineCmdLine(prefl, "SV_COV_ASSERTION", "20"); + s_preprocp->defineCmdLine(prefl, "SV_COV_FSM_STATE", "21"); + s_preprocp->defineCmdLine(prefl, "SV_COV_STATEMENT", "22"); + s_preprocp->defineCmdLine(prefl, "SV_COV_TOGGLE", "23"); + s_preprocp->defineCmdLine(prefl, "SV_COV_OVERFLOW", "-2"); + s_preprocp->defineCmdLine(prefl, "SV_COV_ERROR", "-1"); + s_preprocp->defineCmdLine(prefl, "SV_COV_NOCOV", "0"); + s_preprocp->defineCmdLine(prefl, "SV_COV_OK", "1"); + s_preprocp->defineCmdLine(prefl, "SV_COV_PARTIAL", "2"); + } + } } bool preproc(FileLine* fl, const string& modname, V3InFilter* filterp, V3ParseImp* parsep, const string& errmsg) { // "" for no error - debug(true); // Recheck if debug on - first check was before command line passed + debug(true); // Recheck if debug on - first check was before command line passed - // Preprocess the given module, putting output in vppFilename - UINFONL(1," Preprocessing "<ext+ options since it was first ecountered. - FileLine* modfileline = new FileLine(modfilename, 0); - modfileline->language(v3Global.opt.fileLanguage(modfilename)); - V3Parse::ppPushText(parsep, (string("`begin_keywords \"") - +modfileline->language().ascii()+"\"\n")); - } + // Set language standard up front + if (!v3Global.opt.preprocOnly()) { + // Leting lex parse this saves us from having to specially en/decode + // from the V3LangCode to the various Lex BEGIN states. The language + // of this source file is updated here, in case there have been any + // intervening +ext+ options since it was first ecountered. + FileLine* modfileline = new FileLine(modfilename, 0); + modfileline->language(v3Global.opt.fileLanguage(modfilename)); + V3Parse::ppPushText(parsep, (string("`begin_keywords \"") + +modfileline->language().ascii()+"\"\n")); + } - while (!s_preprocp->isEof()) { - string line = s_preprocp->getline(); - V3Parse::ppPushText(parsep, line); - } - return true; + while (!s_preprocp->isEof()) { + string line = s_preprocp->getline(); + V3Parse::ppPushText(parsep, line); + } + return true; } void preprocInclude(FileLine* fl, const string& modname) { - if (modname[0]=='/' || modname[0]=='\\') { - fl->v3warn(INCABSPATH,"Suggest `include with absolute path be made relative, and use +include: "<filename()), - "Cannot find include file: "); + if (modname[0]=='/' || modname[0]=='\\') { + fl->v3warn(INCABSPATH, "Suggest `include with absolute path be made relative, and use +include: " + <filename()), + "Cannot find include file: "); } private: string preprocOpen(FileLine* fl, V3InFilter* filterp, const string& modname, const string& lastpath, const string& errmsg) { // Error message or "" to suppress - // Returns filename if successful - // Try a pure name in case user has a bogus `filename they don't expect + // Returns filename if successful + // Try a pure name in case user has a bogus `filename they don't expect string filename = v3Global.opt.filePath(fl, modname, lastpath, errmsg); - if (filename=="") { - // Allow user to put `defined names on the command line instead of filenames, - // then convert them properly. + if (filename=="") { + // Allow user to put `defined names on the command line instead of filenames, + // then convert them properly. string ppmodname = s_preprocp->removeDefines(modname); filename = v3Global.opt.filePath(fl, ppmodname, lastpath, errmsg); - } - if (filename=="") return ""; // Not found + } + if (filename=="") return ""; // Not found - UINFO(2," Reading "<openFile(fl, filterp, filename); - return filename; + UINFO(2," Reading "<openFile(fl, filterp, filename); + return filename; } public: @@ -170,14 +171,14 @@ void V3PreShell::boot(char** env) { V3PreShellImp::s_preImp.boot(env); } bool V3PreShell::preproc(FileLine* fl, const string& modname, V3InFilter* filterp, - V3ParseImp* parsep, const string& errmsg) { + V3ParseImp* parsep, const string& errmsg) { return V3PreShellImp::s_preImp.preproc(fl, modname, filterp, parsep, errmsg); } void V3PreShell::preprocInclude(FileLine* fl, const string& modname) { V3PreShellImp::s_preImp.preprocInclude(fl, modname); } void V3PreShell::defineCmdLine(const string& name, const string& value) { - FileLine* prefl = new FileLine("COMMAND_LINE_DEFINE",0); + FileLine* prefl = new FileLine("COMMAND_LINE_DEFINE", 0); V3PreShellImp::s_preprocp->defineCmdLine(prefl, name, value); } void V3PreShell::undef(const string& name) { diff --git a/src/V3PreShell.h b/src/V3PreShell.h index 5d929c85a..b4368571a 100644 --- a/src/V3PreShell.h +++ b/src/V3PreShell.h @@ -39,10 +39,10 @@ public: static bool preproc(FileLine* fl, const string& modname, V3InFilter* filterp, V3ParseImp* parsep, const string& errmsg); static void preprocInclude(FileLine* fl, const string& modname); - static string dependFiles() { return ""; } // Perl only + static string dependFiles() { return ""; } // Perl only static void defineCmdLine(const string& name, const string& value); static void undef(const string& name); static void dumpDefines(std::ostream& os); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index 754ea434f..bbc884e22 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -20,11 +20,11 @@ // V3Premit's Transformations: // // Each module: -// For each wide OP, make a a temporary variable with the wide value -// For each deep expression, assign expression to temporary. +// For each wide OP, make a a temporary variable with the wide value +// For each deep expression, assign expression to temporary. // // Each display (independant transformation; here as Premit is a good point) -// If autoflush, insert a flush +// If autoflush, insert a flush // //************************************************************************* @@ -45,11 +45,11 @@ class PremitAssignVisitor : public AstNVisitor { private: // NODE STATE - // AstVar::user4() // bool; occurs on LHS of current assignment - AstUser4InUse m_inuser4; + // AstVar::user4() // bool; occurs on LHS of current assignment + AstUser4InUse m_inuser4; // STATE - bool m_noopt; // Disable optimization of variables in this block + bool m_noopt; // Disable optimization of variables in this block // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -57,21 +57,21 @@ private: // VISITORS virtual void visit(AstNodeAssign* nodep) { //AstNode::user4ClearTree(); // Implied by AstUser4InUse - // LHS first as fewer varrefs + // LHS first as fewer varrefs iterateAndNextNull(nodep->lhsp()); - // Now find vars marked as lhs + // Now find vars marked as lhs iterateAndNextNull(nodep->rhsp()); } virtual void visit(AstVarRef* nodep) { - // it's LHS var is used so need a deep temporary - if (nodep->lvalue()) { - nodep->varp()->user4(true); - } else { - if (nodep->varp()->user4()) { - if (!m_noopt) UINFO(4, "Block has LHS+RHS var: "<lvalue()) { + nodep->varp()->user4(true); + } else { + if (nodep->varp()->user4()) { + if (!m_noopt) UINFO(4, "Block has LHS+RHS var: "< bool. True if iterated already - // AstShiftL::user2() -> bool. True if converted to conditional - // AstShiftR::user2() -> bool. True if converted to conditional - // *::user4() -> See PremitAssignVisitor - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + // AstNodeMath::user() -> bool. True if iterated already + // AstShiftL::user2() -> bool. True if converted to conditional + // AstShiftR::user2() -> bool. True if converted to conditional + // *::user4() -> See PremitAssignVisitor + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // STATE - AstNodeModule* m_modp; // Current module - AstCFunc* m_funcp; // Current block - AstNode* m_stmtp; // Current statement - AstWhile* m_inWhilep; // Inside while loop, special statement additions - AstTraceInc* m_inTracep; // Inside while loop, special statement additions - bool m_assignLhs; // Inside assignment lhs, don't breakup extracts + AstNodeModule* m_modp; // Current module + AstCFunc* m_funcp; // Current block + AstNode* m_stmtp; // Current statement + AstWhile* m_inWhilep; // Inside while loop, special statement additions + AstTraceInc* m_inTracep; // Inside while loop, special statement additions + bool m_assignLhs; // Inside assignment lhs, don't breakup extracts // METHODS VL_DEBUG_FUNC; // Declare debug() bool assignNoTemp(AstNodeAssign* nodep) { return (VN_IS(nodep->lhsp(), VarRef) - && !AstVar::scVarRecurse(nodep->lhsp()) + && !AstVar::scVarRecurse(nodep->lhsp()) && VN_IS(nodep->rhsp(), Const)); } void checkNode(AstNode* nodep) { - // Consider adding a temp for this expression. - // We need to avoid adding temps to the following: - // ASSIGN(x, *here*) - // ASSIGN(CONST*here*, VARREF(!sc)) - // ARRAYSEL(*here*, ...) (No wides can be in any argument but first, so we don't check which arg is wide) - // ASSIGN(x, SEL*HERE*(ARRAYSEL()...) (m_assignLhs==true handles this.) - //UINFO(9, " Check: "<user1()) { // Not already done - if (nodep->isWide()) { - if (m_assignLhs) { - } else if (nodep->firstAbovep() + // Consider adding a temp for this expression. + // We need to avoid adding temps to the following: + // ASSIGN(x, *here*) + // ASSIGN(CONST*here*, VARREF(!sc)) + // ARRAYSEL(*here*, ...) (No wides can be in any argument but first, + // so we don't check which arg is wide) + // ASSIGN(x, SEL*HERE*(ARRAYSEL()...) (m_assignLhs==true handles this.) + //UINFO(9, " Check: "<user1()) { // Not already done + if (nodep->isWide()) { + if (m_assignLhs) { + } else if (nodep->firstAbovep() && VN_IS(nodep->firstAbovep(), NodeAssign) && assignNoTemp(VN_CAST(nodep->firstAbovep(), NodeAssign))) { - // Not much point if it's just a direct assignment to a constant + // Not much point if it's just a direct assignment to a constant } else if (VN_IS(nodep->backp(), Sel) && VN_CAST(nodep->backp(), Sel)->widthp() == nodep) { - // AstSel::width must remain a constant - } else if (nodep->firstAbovep() + // AstSel::width must remain a constant + } else if (nodep->firstAbovep() && VN_IS(nodep->firstAbovep(), ArraySel)) { - // ArraySel's are pointer refs, ignore - } else { - UINFO(4,"Cre Temp: "<varNumGetInc())); AstVar* varp = new AstVar(nodep->fileline(), AstVarType::STMTTEMP, newvarname, nodep->dtypep()); - m_funcp->addInitsp(varp); - return varp; + m_funcp->addInitsp(varp); + return varp; } void insertBeforeStmt(AstNode* newp) { - // Insert newp before m_stmtp - if (m_inWhilep) { - // Statements that are needed for the 'condition' in a while actually have to - // be put before & after the loop, since we can't do any statements in a while's (cond). - m_inWhilep->addPrecondsp(newp); - } else if (m_inTracep) { - m_inTracep->addPrecondsp(newp); - } else if (m_stmtp) { - AstNRelinker linker; - m_stmtp->unlinkFrBack(&linker); - newp->addNext(m_stmtp); - linker.relink(newp); - } else { - newp->v3fatalSrc("No statement insertion point."); - } + // Insert newp before m_stmtp + if (m_inWhilep) { + // Statements that are needed for the 'condition' in a while + // actually have to be put before & after the loop, since we + // can't do any statements in a while's (cond). + m_inWhilep->addPrecondsp(newp); + } else if (m_inTracep) { + m_inTracep->addPrecondsp(newp); + } else if (m_stmtp) { + AstNRelinker linker; + m_stmtp->unlinkFrBack(&linker); + newp->addNext(m_stmtp); + linker.relink(newp); + } else { + newp->v3fatalSrc("No statement insertion point."); + } } void createDeepTemp(AstNode* nodep, bool noSubst) { - if (debug()>8) nodep->dumpTree(cout,"deepin:"); + if (debug()>8) nodep->dumpTree(cout, "deepin:"); - AstNRelinker linker; - nodep->unlinkFrBack(&linker); + AstNRelinker linker; + nodep->unlinkFrBack(&linker); - AstVar* varp = getBlockTemp(nodep); - if (noSubst) varp->noSubst(true); // Do not remove varrefs to this in V3Const - // Replace node tree with reference to var + AstVar* varp = getBlockTemp(nodep); + if (noSubst) varp->noSubst(true); // Do not remove varrefs to this in V3Const + // Replace node tree with reference to var AstVarRef* newp = new AstVarRef(nodep->fileline(), varp, false); - linker.relink(newp); - // Put assignment before the referencing statement + linker.relink(newp); + // Put assignment before the referencing statement AstAssign* assp = new AstAssign(nodep->fileline(), new AstVarRef(nodep->fileline(), varp, true), nodep); - insertBeforeStmt(assp); - if (debug()>8) assp->dumpTree(cout,"deepou:"); - nodep->user1(true); // Don't add another assignment + insertBeforeStmt(assp); + if (debug()>8) assp->dumpTree(cout, "deepou:"); + nodep->user1(true); // Don't add another assignment } // VISITORS virtual void visit(AstNodeModule* nodep) { - UINFO(4," MOD "<precondsp()); - startStatement(nodep); - m_inWhilep = nodep; + startStatement(nodep); + m_inWhilep = nodep; iterateAndNextNull(nodep->condp()); - m_inWhilep = NULL; - startStatement(nodep); + m_inWhilep = NULL; + startStatement(nodep); iterateAndNextNull(nodep->bodysp()); iterateAndNextNull(nodep->incsp()); - m_stmtp = NULL; + m_stmtp = NULL; } virtual void visit(AstNodeAssign* nodep) { - startStatement(nodep); - { - bool noopt = PremitAssignVisitor(nodep).noOpt(); - if (noopt && !nodep->user1()) { - // Need to do this even if not wide, as e.g. a select may be on a wide operator - UINFO(4,"Deep temp for LHS/RHS\n"); - createDeepTemp(nodep->rhsp(), false); - } - } + startStatement(nodep); + { + bool noopt = PremitAssignVisitor(nodep).noOpt(); + if (noopt && !nodep->user1()) { + // Need to do this even if not wide, as e.g. a select may be on a wide operator + UINFO(4,"Deep temp for LHS/RHS\n"); + createDeepTemp(nodep->rhsp(), false); + } + } iterateAndNextNull(nodep->rhsp()); - m_assignLhs = true; + m_assignLhs = true; iterateAndNextNull(nodep->lhsp()); - m_assignLhs = false; - m_stmtp = NULL; + m_assignLhs = false; + m_stmtp = NULL; } virtual void visit(AstNodeStmt* nodep) { if (!nodep->isStatement()) { @@ -248,70 +250,70 @@ private: UINFO(4," STMT "< 32/64 bits in C++ will wrap-around and generate non-0s - if (!nodep->user2SetOnce()) { - UINFO(4," ShiftFix "< 32/64 bits in C++ will wrap-around and generate non-0s + if (!nodep->user2SetOnce()) { + UINFO(4," ShiftFix "<rhsp(), Const); - if (shiftp && shiftp->num().mostSetBitP1() > 32) { - shiftp->v3error("Unsupported: Shifting of by over 32-bit number isn't supported." - <<" (This isn't a shift of 32 bits, but a shift of 2^32, or 4 billion!)\n"); - } - if (nodep->widthMin()<=64 // Else we'll use large operators which work right - // C operator's width must be < maximum shift which is based on Verilog width - && nodep->width() < (1LL<rhsp()->widthMin())) { - AstNRelinker replaceHandle; - nodep->unlinkFrBack(&replaceHandle); - AstNode* constzerop; - int m1value = nodep->widthMin()-1; // Constant of width-1; not changing dtype width - if (nodep->signedFlavor()) { - // Then over shifting gives the sign bit, not all zeros - // Note *NOT* clean output -- just like normal shift! - // Create equivalent of VL_SIGNONES_(node_width) + if (shiftp && shiftp->num().mostSetBitP1() > 32) { + shiftp->v3error("Unsupported: Shifting of by over 32-bit number isn't supported." + <<" (This isn't a shift of 32 bits, but a shift of 2^32, or 4 billion!)\n"); + } + if (nodep->widthMin() <= 64 // Else we'll use large operators which work right + // C operator's width must be < maximum shift which is based on Verilog width + && nodep->width() < (1LL<rhsp()->widthMin())) { + AstNRelinker replaceHandle; + nodep->unlinkFrBack(&replaceHandle); + AstNode* constzerop; + int m1value = nodep->widthMin()-1; // Constant of width-1; not changing dtype width + if (nodep->signedFlavor()) { + // Then over shifting gives the sign bit, not all zeros + // Note *NOT* clean output -- just like normal shift! + // Create equivalent of VL_SIGNONES_(node_width) constzerop = new AstNegate(nodep->fileline(), new AstShiftR(nodep->fileline(), nodep->lhsp()->cloneTree(false), new AstConst(nodep->fileline(), m1value), nodep->width())); - } else { + } else { V3Number zeronum (nodep, nodep->width(), 0); constzerop = new AstConst(nodep->fileline(), zeronum); } constzerop->dtypeFrom(nodep); // unsigned V3Number widthnum (nodep, nodep->rhsp()->widthMin(), m1value); - AstNode* constwidthp = new AstConst(nodep->fileline(), widthnum); + AstNode* constwidthp = new AstConst(nodep->fileline(), widthnum); constwidthp->dtypeFrom(nodep->rhsp()); // unsigned - AstCond* newp = + AstCond* newp = new AstCond(nodep->fileline(), new AstGte(nodep->fileline(), constwidthp, nodep->rhsp()->cloneTree(false)), nodep, constzerop); - replaceHandle.relink(newp); - } - } + replaceHandle.relink(newp); + } + } iterateChildren(nodep); checkNode(nodep); } virtual void visit(AstShiftL* nodep) { - visitShift(nodep); + visitShift(nodep); } virtual void visit(AstShiftR* nodep) { - visitShift(nodep); + visitShift(nodep); } virtual void visit(AstShiftRS* nodep) { - visitShift(nodep); + visitShift(nodep); } // Operators virtual void visit(AstNodeTermop* nodep) { @@ -328,74 +330,74 @@ private: } virtual void visit(AstSel* nodep) { iterateAndNextNull(nodep->fromp()); - { // Only the 'from' is part of the assignment LHS - bool prevAssign = m_assignLhs; - m_assignLhs = false; + { // Only the 'from' is part of the assignment LHS + bool prevAssign = m_assignLhs; + m_assignLhs = false; iterateAndNextNull(nodep->lsbp()); iterateAndNextNull(nodep->widthp()); - m_assignLhs = prevAssign; - } - checkNode(nodep); + m_assignLhs = prevAssign; + } + checkNode(nodep); } virtual void visit(AstArraySel* nodep) { iterateAndNextNull(nodep->fromp()); - { // Only the 'from' is part of the assignment LHS - bool prevAssign = m_assignLhs; - m_assignLhs = false; + { // Only the 'from' is part of the assignment LHS + bool prevAssign = m_assignLhs; + m_assignLhs = false; iterateAndNextNull(nodep->bitp()); - m_assignLhs = prevAssign; - } - checkNode(nodep); + m_assignLhs = prevAssign; + } + checkNode(nodep); } virtual void visit(AstConst* nodep) { iterateChildren(nodep); checkNode(nodep); } virtual void visit(AstNodeCond* nodep) { iterateChildren(nodep); - if (nodep->expr1p()->isWide() + if (nodep->expr1p()->isWide() && !VN_IS(nodep->condp(), Const) && !VN_IS(nodep->condp(), VarRef)) { - // We're going to need the expression several times in the expanded code, - // so might as well make it a common expression - createDeepTemp(nodep->condp(), false); - } - checkNode(nodep); + // We're going to need the expression several times in the expanded code, + // so might as well make it a common expression + createDeepTemp(nodep->condp(), false); + } + checkNode(nodep); } // Autoflush virtual void visit(AstDisplay* nodep) { - startStatement(nodep); + startStatement(nodep); iterateChildren(nodep); - m_stmtp = NULL; - if (v3Global.opt.autoflush()) { - AstNode* searchp = nodep->nextp(); + m_stmtp = NULL; + if (v3Global.opt.autoflush()) { + AstNode* searchp = nodep->nextp(); while (searchp && VN_IS(searchp, Comment)) searchp = searchp->nextp(); - if (searchp + if (searchp && VN_IS(searchp, Display) && nodep->filep()->sameGateTree(VN_CAST(searchp, Display)->filep())) { - // There's another display next; we can just wait to flush - } else { - UINFO(4,"Autoflush "<addNextHere(new AstFFlush(nodep->fileline(), + // There's another display next; we can just wait to flush + } else { + UINFO(4,"Autoflush "<addNextHere(new AstFFlush(nodep->fileline(), AstNode::cloneTreeNull(nodep->filep(), true))); - } - } + } + } } virtual void visit(AstSFormatF* nodep) { iterateChildren(nodep); - // Any strings sent to a display must be var of string data type, - // to avoid passing a pointer to a temporary. - for (AstNode* expp=nodep->exprsp(); expp; expp = expp->nextp()) { - if (expp->dtypep()->basicp()->isString() + // Any strings sent to a display must be var of string data type, + // to avoid passing a pointer to a temporary. + for (AstNode* expp=nodep->exprsp(); expp; expp = expp->nextp()) { + if (expp->dtypep()->basicp()->isString() && !VN_IS(expp, VarRef)) { - createDeepTemp(expp, true); - } - } + createDeepTemp(expp, true); + } + } } //-------------------- // Default: Just iterate - virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars + virtual void visit(AstVar* nodep) {} // Don't hit varrefs under vars virtual void visit(AstNode* nodep) { iterateChildren(nodep); } @@ -403,11 +405,11 @@ private: public: // CONSTUCTORS explicit PremitVisitor(AstNetlist* nodep) { - m_modp = NULL; - m_funcp = NULL; - m_stmtp = NULL; - m_inWhilep = NULL; - m_inTracep = NULL; + m_modp = NULL; + m_funcp = NULL; + m_stmtp = NULL; + m_inWhilep = NULL; + m_inTracep = NULL; m_assignLhs = false; iterate(nodep); } diff --git a/src/V3Premit.h b/src/V3Premit.h index 72df48347..dca05770e 100644 --- a/src/V3Premit.h +++ b/src/V3Premit.h @@ -34,4 +34,4 @@ public: static void premitAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index d11ae7fa4..d10084f53 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -19,9 +19,9 @@ //************************************************************************* // V3Scope's Transformations: // -// For every CELL that references this module, create a -// SCOPE -// {all blocked statements} +// For every CELL that references this module, create a +// SCOPE +// {all blocked statements} // //************************************************************************* @@ -45,10 +45,10 @@ class ScopeVisitor : public AstNVisitor { private: // NODE STATE - // AstVar::user1p -> AstVarScope replacement for this variable - // AstTask::user2p -> AstTask*. Replacement task - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + // AstVar::user1p -> AstVarScope replacement for this variable + // AstTask::user2p -> AstTask*. Replacement task + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // TYPES typedef vl_unordered_map PackageScopeMap; @@ -57,235 +57,237 @@ private: typedef std::set > VarRefScopeSet; // STATE, inside processing a single module - AstNodeModule* m_modp; // Current module - AstScope* m_scopep; // Current scope we are building + AstNodeModule* m_modp; // Current module + AstScope* m_scopep; // Current scope we are building // STATE, for passing down one level of hierarchy (may need save/restore) - AstCell* m_aboveCellp; // Cell that instantiates this module - AstScope* m_aboveScopep; // Scope that instantiates this scope + AstCell* m_aboveCellp; // Cell that instantiates this module + AstScope* m_aboveScopep; // Scope that instantiates this scope - PackageScopeMap m_packageScopes; // Scopes for each package - VarScopeMap m_varScopes; // Varscopes created for each scope and var - VarRefScopeSet m_varRefScopes; // Varrefs-in-scopes needing fixup when donw + PackageScopeMap m_packageScopes; // Scopes for each package + VarScopeMap m_varScopes; // Varscopes created for each scope and var + VarRefScopeSet m_varRefScopes; // Varrefs-in-scopes needing fixup when donw // METHODS VL_DEBUG_FUNC; // Declare debug() void cleanupVarRefs() { - for (VarRefScopeSet::iterator it = m_varRefScopes.begin(); - it!=m_varRefScopes.end(); ++it) { - AstVarRef* nodep = it->first; - AstScope* scopep = it->second; - if (nodep->packagep()) { - PackageScopeMap::iterator it2 = m_packageScopes.find(nodep->packagep()); - if (it2==m_packageScopes.end()) nodep->v3fatalSrc("Can't locate package scope"); - scopep = it2->second; - } - VarScopeMap::iterator it3 = m_varScopes.find(make_pair(nodep->varp(), scopep)); - if (it3==m_varScopes.end()) nodep->v3fatalSrc("Can't locate varref scope"); - AstVarScope* varscp = it3->second; - nodep->varScopep(varscp); - } + for (VarRefScopeSet::iterator it = m_varRefScopes.begin(); + it!=m_varRefScopes.end(); ++it) { + AstVarRef* nodep = it->first; + AstScope* scopep = it->second; + if (nodep->packagep()) { + PackageScopeMap::iterator it2 = m_packageScopes.find(nodep->packagep()); + if (it2==m_packageScopes.end()) nodep->v3fatalSrc("Can't locate package scope"); + scopep = it2->second; + } + VarScopeMap::iterator it3 = m_varScopes.find(make_pair(nodep->varp(), scopep)); + if (it3==m_varScopes.end()) nodep->v3fatalSrc("Can't locate varref scope"); + AstVarScope* varscp = it3->second; + nodep->varScopep(varscp); + } } // VISITORS virtual void visit(AstNetlist* nodep) { - AstNodeModule* modp = nodep->topModulep(); - if (!modp) { nodep->v3error("No root module specified"); return; } - // Operate starting at the top of the hierarchy - m_aboveCellp = NULL; - m_aboveScopep = NULL; + AstNodeModule* modp = nodep->topModulep(); + if (!modp) { nodep->v3error("No root module specified"); return; } + // Operate starting at the top of the hierarchy + m_aboveCellp = NULL; + m_aboveScopep = NULL; iterate(modp); - cleanupVarRefs(); + cleanupVarRefs(); } virtual void visit(AstNodeModule* nodep) { - // Create required blocks and add to module - string scopename; - if (!m_aboveScopep) scopename = "TOP"; - else scopename = m_aboveScopep->name()+"."+m_aboveCellp->name(); + // Create required blocks and add to module + string scopename; + if (!m_aboveScopep) scopename = "TOP"; + else scopename = m_aboveScopep->name()+"."+m_aboveCellp->name(); - UINFO(4," MOD AT "<(m_aboveCellp) : static_cast(nodep)) ->fileline(), - nodep, scopename, m_aboveScopep, m_aboveCellp); - if (VN_IS(nodep, Package)) m_packageScopes.insert(make_pair(VN_CAST(nodep, Package), m_scopep)); + nodep, scopename, m_aboveScopep, m_aboveCellp); + if (VN_IS(nodep, Package)) { + m_packageScopes.insert(make_pair(VN_CAST(nodep, Package), m_scopep)); + } - // Now for each child cell, iterate the module this cell points to - for (AstNode* cellnextp = nodep->stmtsp(); cellnextp; cellnextp=cellnextp->nextp()) { + // Now for each child cell, iterate the module this cell points to + for (AstNode* cellnextp = nodep->stmtsp(); cellnextp; cellnextp=cellnextp->nextp()) { if (AstCell* cellp = VN_CAST(cellnextp, Cell)) { - AstScope* oldScopep = m_scopep; - AstCell* oldAbCellp = m_aboveCellp; - AstScope* oldAbScopep = m_aboveScopep; - { - m_aboveCellp = cellp; - m_aboveScopep = m_scopep; - AstNodeModule* modp = cellp->modp(); - if (!modp) cellp->v3fatalSrc("Unlinked mod"); + AstScope* oldScopep = m_scopep; + AstCell* oldAbCellp = m_aboveCellp; + AstScope* oldAbScopep = m_aboveScopep; + { + m_aboveCellp = cellp; + m_aboveScopep = m_scopep; + AstNodeModule* modp = cellp->modp(); + if (!modp) cellp->v3fatalSrc("Unlinked mod"); iterate(modp); // Recursive call to visit(AstNodeModule) - } - // Done, restore vars - m_scopep = oldScopep; - m_aboveCellp = oldAbCellp; - m_aboveScopep = oldAbScopep; - } - } + } + // Done, restore vars + m_scopep = oldScopep; + m_aboveCellp = oldAbCellp; + m_aboveScopep = oldAbScopep; + } + } - // Create scope for the current usage of this module - UINFO(4," back AT "<isTop()) { - AstTopScope* topscp = new AstTopScope(nodep->fileline(), m_scopep); - m_modp->addStmtp(topscp); - } else { - m_modp->addStmtp(m_scopep); - } + m_modp = nodep; + if (m_modp->isTop()) { + AstTopScope* topscp = new AstTopScope(nodep->fileline(), m_scopep); + m_modp->addStmtp(topscp); + } else { + m_modp->addStmtp(m_scopep); + } - // Copy blocks into this scope - // If this is the first usage of the block ever, we can simply move the reference + // Copy blocks into this scope + // If this is the first usage of the block ever, we can simply move the reference iterateChildren(nodep); - // ***Note m_scopep is passed back to the caller of the routine (above) + // ***Note m_scopep is passed back to the caller of the routine (above) } virtual void visit(AstActive* nodep) { - nodep->v3fatalSrc("Actives now made after scoping"); + nodep->v3fatalSrc("Actives now made after scoping"); } virtual void visit(AstInitial* nodep) { - // Add to list of blocks under this scope - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstFinal* nodep) { - // Add to list of blocks under this scope - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstAssignAlias* nodep) { - // Add to list of blocks under this scope - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstAssignVarScope* nodep) { - // Copy under the scope but don't recurse - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Copy under the scope but don't recurse + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstAssignW* nodep) { - // Add to list of blocks under this scope - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstAlways* nodep) { - // Add to list of blocks under this scope - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstAlwaysPublic* nodep) { - // Add to list of blocks under this scope - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstCoverToggle* nodep) { - // Add to list of blocks under this scope - UINFO(4," Move "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); iterateChildren(clonep); // We iterate under the *clone* } virtual void visit(AstCFunc* nodep) { - // Add to list of blocks under this scope - UINFO(4," CFUNC "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); - clonep->scopep(m_scopep); - // We iterate under the *clone* + // Add to list of blocks under this scope + UINFO(4," CFUNC "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); + clonep->scopep(m_scopep); + // We iterate under the *clone* iterateChildren(clonep); } virtual void visit(AstNodeFTask* nodep) { - // Add to list of blocks under this scope - UINFO(4," FTASK "<cloneTree(false); - nodep->user2p(clonep); - m_scopep->addActivep(clonep); - // We iterate under the *clone* + // Add to list of blocks under this scope + UINFO(4," FTASK "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); + // We iterate under the *clone* iterateChildren(clonep); } virtual void visit(AstVar* nodep) { - // Make new scope variable - // This is called cross-module by AstVar, so we cannot trust any m_ variables - if (!nodep->user1p()) { - AstVarScope* varscp = new AstVarScope(nodep->fileline(), m_scopep, nodep); - UINFO(6," New scope "<isTrace()) varscp->trace(false); - nodep->user1p(varscp); - if (v3Global.opt.isClocker(varscp->prettyName())) { - nodep->attrClocker(AstVarAttrClocker::CLOCKER_YES); - } - if (v3Global.opt.isNoClocker(varscp->prettyName())) { - nodep->attrClocker(AstVarAttrClocker::CLOCKER_NO); - } - if (!m_scopep) nodep->v3fatalSrc("No scope for var"); - m_varScopes.insert(make_pair(make_pair(nodep, m_scopep), varscp)); - m_scopep->addVarp(varscp); - } + // Make new scope variable + // This is called cross-module by AstVar, so we cannot trust any m_ variables + if (!nodep->user1p()) { + AstVarScope* varscp = new AstVarScope(nodep->fileline(), m_scopep, nodep); + UINFO(6," New scope "<isTrace()) varscp->trace(false); + nodep->user1p(varscp); + if (v3Global.opt.isClocker(varscp->prettyName())) { + nodep->attrClocker(AstVarAttrClocker::CLOCKER_YES); + } + if (v3Global.opt.isNoClocker(varscp->prettyName())) { + nodep->attrClocker(AstVarAttrClocker::CLOCKER_NO); + } + if (!m_scopep) nodep->v3fatalSrc("No scope for var"); + m_varScopes.insert(make_pair(make_pair(nodep, m_scopep), varscp)); + m_scopep->addVarp(varscp); + } } virtual void visit(AstVarRef* nodep) { - // VarRef needs to point to VarScope - // Make sure variable has made user1p. - if (!nodep->varp()) nodep->v3fatalSrc("Unlinked"); - if (nodep->varp()->isIfaceRef()) { - nodep->varScopep(NULL); - } else { - // We may have not made the variable yet, and we can't make it now as - // the var's referenced package etc might not be created yet. - // So push to a list and post-correct - m_varRefScopes.insert(make_pair(nodep, m_scopep)); - } + // VarRef needs to point to VarScope + // Make sure variable has made user1p. + if (!nodep->varp()) nodep->v3fatalSrc("Unlinked"); + if (nodep->varp()->isIfaceRef()) { + nodep->varScopep(NULL); + } else { + // We may have not made the variable yet, and we can't make it now as + // the var's referenced package etc might not be created yet. + // So push to a list and post-correct + m_varRefScopes.insert(make_pair(nodep, m_scopep)); + } } virtual void visit(AstScopeName* nodep) { - // If there's a %m in the display text, we add a special node that will contain the name() + // If there's a %m in the display text, we add a special node that will contain the name() string prefix = string("__DOT__")+m_scopep->name(); - // TOP and above will be the user's name(). - // Note 'TOP.' is stripped by scopePrettyName - // To keep correct visual order, must add before other Text's - AstNode* afterp = nodep->scopeAttrp(); - if (afterp) afterp->unlinkFrBackWithNext(); - nodep->scopeAttrp(new AstText(nodep->fileline(), prefix)); - if (afterp) nodep->scopeAttrp(afterp); - afterp = nodep->scopeEntrp(); - if (afterp) afterp->unlinkFrBackWithNext(); - nodep->scopeEntrp(new AstText(nodep->fileline(), prefix)); - if (afterp) nodep->scopeEntrp(afterp); + // TOP and above will be the user's name(). + // Note 'TOP.' is stripped by scopePrettyName + // To keep correct visual order, must add before other Text's + AstNode* afterp = nodep->scopeAttrp(); + if (afterp) afterp->unlinkFrBackWithNext(); + nodep->scopeAttrp(new AstText(nodep->fileline(), prefix)); + if (afterp) nodep->scopeAttrp(afterp); + afterp = nodep->scopeEntrp(); + if (afterp) afterp->unlinkFrBackWithNext(); + nodep->scopeEntrp(new AstText(nodep->fileline(), prefix)); + if (afterp) nodep->scopeEntrp(afterp); iterateChildren(nodep); } virtual void visit(AstScope* nodep) { - // Scope that was made by this module for different cell; - // Want to ignore blocks under it, so just do nothing + // Scope that was made by this module for different cell; + // Want to ignore blocks under it, so just do nothing } //-------------------- // Default @@ -295,11 +297,11 @@ private: public: // CONSTUCTORS explicit ScopeVisitor(AstNetlist* nodep) { - m_aboveCellp = NULL; - m_aboveScopep = NULL; - m_modp = NULL; - m_scopep = NULL; - // + m_aboveCellp = NULL; + m_aboveScopep = NULL; + m_modp = NULL; + m_scopep = NULL; + // iterate(nodep); } virtual ~ScopeVisitor() {} @@ -311,85 +313,85 @@ public: class ScopeCleanupVisitor : public AstNVisitor { private: // STATE - AstScope* m_scopep; // Current scope we are building + AstScope* m_scopep; // Current scope we are building // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstScope* nodep) { - // Want to ignore blocks under it - m_scopep = nodep; + // Want to ignore blocks under it + m_scopep = nodep; iterateChildren(nodep); - m_scopep = NULL; + m_scopep = NULL; } virtual void movedDeleteOrIterate(AstNode* nodep) { - if (m_scopep) { - // The new block; repair varrefs + if (m_scopep) { + // The new block; repair varrefs iterateChildren(nodep); - } else { - // A block that was just moved under a scope, Kill it. - // Certain nodes can be referenced later in this pass, notably - // an FTaskRef needs to access the FTask to find the cloned task - pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); - } + } else { + // A block that was just moved under a scope, Kill it. + // Certain nodes can be referenced later in this pass, notably + // an FTaskRef needs to access the FTask to find the cloned task + pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); + } } virtual void visit(AstInitial* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstFinal* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstAssignAlias* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstAssignVarScope* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstAssignW* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstAlways* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstAlwaysPublic* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstCoverToggle* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstNodeFTask* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstCFunc* nodep) { - movedDeleteOrIterate(nodep); + movedDeleteOrIterate(nodep); } virtual void visit(AstVarXRef* nodep) { - // The crossrefs are dealt with in V3LinkDot - nodep->varp(NULL); + // The crossrefs are dealt with in V3LinkDot + nodep->varp(NULL); } virtual void visit(AstNodeFTaskRef* nodep) { - // The crossrefs are dealt with in V3LinkDot - UINFO(9," Old pkg-taskref "<packagep()) { - // Point to the clone - if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked"); + // The crossrefs are dealt with in V3LinkDot + UINFO(9," Old pkg-taskref "<packagep()) { + // Point to the clone + if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked"); AstNodeFTask* newp = VN_CAST(nodep->taskp()->user2p(), NodeFTask); - if (!newp) nodep->v3fatalSrc("No clone for package function"); - nodep->taskp(newp); - UINFO(9," New pkg-taskref "<taskp(NULL); - UINFO(9," New pkg-taskref "<v3fatalSrc("No clone for package function"); + nodep->taskp(newp); + UINFO(9," New pkg-taskref "<taskp(NULL); + UINFO(9," New pkg-taskref "<ftaskp(NULL); + // The crossrefs are dealt with in V3LinkDot + nodep->ftaskp(NULL); iterateChildren(nodep); } @@ -401,7 +403,7 @@ private: public: // CONSTUCTORS explicit ScopeCleanupVisitor(AstNetlist* nodep) { - m_scopep = NULL; + m_scopep = NULL; iterate(nodep); } virtual ~ScopeCleanupVisitor() {} diff --git a/src/V3Scope.h b/src/V3Scope.h index 437af9731..1d20a4a91 100644 --- a/src/V3Scope.h +++ b/src/V3Scope.h @@ -34,4 +34,4 @@ public: static void scopeAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3SenTree.h b/src/V3SenTree.h index 257d35bf5..e22268670 100644 --- a/src/V3SenTree.h +++ b/src/V3SenTree.h @@ -21,12 +21,12 @@ // // Note this can be called multiple times. // Create a IBLOCK(initial), SBLOCK(combo) -// ALWAYS: Remove any-edges from sense list -// If no POS/NEG in senselist, Fold into SBLOCK(combo) -// Else fold into SBLOCK(sequent). -// OPTIMIZE: When support async clocks, fold into that block if possible -// INITIAL: Move into IBLOCK -// WIRE: Move into SBLOCK(combo) +// ALWAYS: Remove any-edges from sense list +// If no POS/NEG in senselist, Fold into SBLOCK(combo) +// Else fold into SBLOCK(sequent). +// OPTIMIZE: When support async clocks, fold into that block if possible +// INITIAL: Move into IBLOCK +// WIRE: Move into SBLOCK(combo) // //************************************************************************* @@ -60,7 +60,7 @@ private: size_t operator() (const AstSenTree* kp) const { return V3Hashed::uncachedHash(kp).fullValue(); } - // Copying required for OSX's libc++ + // Copying required for OSX's libc++ }; class EqSenTree { @@ -69,7 +69,7 @@ private: bool operator() (const AstSenTree* ap, const AstSenTree* bp) const { return ap->sameTree(bp); } - // Copying required for OSX's libc++ + // Copying required for OSX's libc++ }; // MEMBERS @@ -99,29 +99,29 @@ private: class SenTreeFinder : public AstNVisitor { private: // STATE - AstTopScope* m_topscopep; // Top scope to add statement to + AstTopScope* m_topscopep; // Top scope to add statement to SenTreeSet m_trees; // Set of sensitive blocks, for folding // VISITORS VL_DEBUG_FUNC; // Declare debug() virtual void visit(AstNodeModule* nodep) { - // Only do the top - if (nodep->isTop()) { + // Only do the top + if (nodep->isTop()) { iterateChildren(nodep); - } + } } virtual void visit(AstTopScope* nodep) { - m_topscopep = nodep; + m_topscopep = nodep; iterateChildren(nodep); - // Don't clear topscopep, the namer persists beyond this visit + // Don't clear topscopep, the namer persists beyond this visit } virtual void visit(AstScope* nodep) { - // But no SenTrees under TopScope's scope + // But no SenTrees under TopScope's scope } // Memorize existing block names virtual void visit(AstActive* nodep) { - // Don't grab SenTrees under Actives, only those that are global (under Scope directly) + // Don't grab SenTrees under Actives, only those that are global (under Scope directly) iterateChildren(nodep); } virtual void visit(AstSenTree* nodep) { m_trees.add(nodep); } @@ -133,27 +133,27 @@ private: // METHODS public: void clear() { - m_topscopep = NULL; + m_topscopep = NULL; m_trees.clear(); } AstSenTree* getSenTree(FileLine* fl, AstSenTree* sensesp) { - // Return a global sentree that matches given sense list. + // Return a global sentree that matches given sense list. AstSenTree* treep = m_trees.find(sensesp); - // Not found, form a new one - if (!treep) { - UASSERT(m_topscopep,"Never called main()"); - treep = sensesp->cloneTree(false); - m_topscopep->addStmtsp(treep); - UINFO(8," New SENTREE "<cloneTree(false); + m_topscopep->addStmtsp(treep); + UINFO(8," New SENTREE "<::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { -// simvis.newNumber(invscp, #); -// } -// // Simulate -// simvis.main(nodep); -// // Read outputs -// for (deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { -// V3Number* outnump = simvis.fetchOutNumberNull(outvscp); +// SimulateVisitor simvis (false, false); +// simvis.clear(); +// // Set all inputs to the constant +// for (deque::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { +// simvis.newNumber(invscp, #); +// } +// // Simulate +// simvis.main(nodep); +// // Read outputs +// for (deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { +// V3Number* outnump = simvis.fetchOutNumberNull(outvscp); // //************************************************************************* @@ -56,12 +56,12 @@ class SimStackNode { public: // MEMBERS - AstFuncRef* m_funcp; - V3TaskConnects* m_tconnects; + AstFuncRef* m_funcp; + V3TaskConnects* m_tconnects; // CONSTRUCTORS SimStackNode(AstFuncRef* funcp, V3TaskConnects* tconnects): - m_funcp(funcp), - m_tconnects(tconnects) {} + m_funcp(funcp), + m_tconnects(tconnects) {} ~SimStackNode() {} }; @@ -76,40 +76,42 @@ class SimulateVisitor : public AstNVisitor { private: // NODE STATE // Cleared on each always/assignw - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; // Checking: - // AstVar(Scope)::user1() -> VarUsage. Set true to indicate tracking as lvalue/rvalue + // AstVar(Scope)::user1() -> VarUsage. Set true to indicate tracking as lvalue/rvalue // Simulating: - // AstVar(Scope)::user3() -> V3Number*. Input value of variable or node (and output for non-delayed assignments) - // AstVar(Scope)::user2() -> V3Number*. Output value of variable (delayed assignments) + // AstVar(Scope)::user3() -> V3Number*. Input value of variable or node + // (and output for non-delayed assignments) + // AstVar(Scope)::user2() -> V3Number*. Output value of variable (delayed assignments) enum VarUsage { VU_NONE=0, VU_LV=1, VU_RV=2, VU_LVDLY=4 }; // STATE // Major mode - bool m_checkOnly; ///< Checking only (no simulation) mode - bool m_scoped; ///< Running with AstVarScopes instead of AstVars - bool m_params; ///< Doing parameter propagation + bool m_checkOnly; ///< Checking only (no simulation) mode + bool m_scoped; ///< Running with AstVarScopes instead of AstVars + bool m_params; ///< Doing parameter propagation // Checking: - string m_whyNotOptimizable; ///< String explaining why not optimizable or NULL to optimize - AstNode* m_whyNotNodep; ///< First node not optimizable - bool m_anyAssignDly; ///< True if found a delayed assignment - bool m_anyAssignComb; ///< True if found a non-delayed assignment - bool m_inDlyAssign; ///< Under delayed assignment - int m_instrCount; ///< Number of nodes - int m_dataCount; ///< Bytes of data - AstJumpGo* m_jumpp; ///< Jump label we're branching from + string m_whyNotOptimizable; ///< String explaining why not optimizable or NULL to optimize + AstNode* m_whyNotNodep; ///< First node not optimizable + bool m_anyAssignDly; ///< True if found a delayed assignment + bool m_anyAssignComb; ///< True if found a non-delayed assignment + bool m_inDlyAssign; ///< Under delayed assignment + int m_instrCount; ///< Number of nodes + int m_dataCount; ///< Bytes of data + AstJumpGo* m_jumpp; ///< Jump label we're branching from // Simulating: - std::deque m_numFreeps; ///< List of all numbers free and not in use - std::deque m_numAllps; ///< List of all numbers free and in use - std::deque m_callStack; ///< Call stack for verbose error messages + std::deque m_numFreeps; ///< List of all numbers free and not in use + std::deque m_numAllps; ///< List of all numbers free and in use + std::deque m_callStack; ///< Call stack for verbose error messages // Cleanup - // V3Numbers that represents strings are a bit special and the API for V3Number does not allow changing them. - std::deque m_stringNumbersp; // List of allocated string numbers + // V3Numbers that represents strings are a bit special and the API for + // V3Number does not allow changing them. + std::deque m_stringNumbersp; // List of allocated string numbers // Note level 8&9 include debugging each simulation value @@ -118,49 +120,50 @@ private: // Potentially very slow, intended for debugging string prettyNumber(V3Number* nump, AstNodeDType* dtypep) { if (AstRefDType* refdtypep = VN_CAST(dtypep, RefDType)) { - dtypep = refdtypep->skipRefp(); - } + dtypep = refdtypep->skipRefp(); + } if (AstStructDType* stp = VN_CAST(dtypep, StructDType)) { - if (stp->packed()) { + if (stp->packed()) { std::ostringstream out; - out<<"'{"; - for (AstMemberDType* itemp = stp->membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { - int width = itemp->width(); - int lsb = itemp->lsb(); + out<<"'{"; + for (AstMemberDType* itemp = stp->membersp(); + itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { + int width = itemp->width(); + int lsb = itemp->lsb(); int msb = lsb + width - 1; V3Number fieldNum = V3Number(nump, width); fieldNum.opSel(*nump, msb, lsb); - out<name()<<": "; - if (AstNodeDType * childTypep = itemp->subDTypep()) { - out<nextp()) out<<", "; - } - out<<"}"; - return out.str(); - } + out<name()<<": "; + if (AstNodeDType * childTypep = itemp->subDTypep()) { + out<nextp()) out<<", "; + } + out<<"}"; + return out.str(); + } } else if (AstPackArrayDType * arrayp = VN_CAST(dtypep, PackArrayDType)) { - if (AstNodeDType * childTypep = arrayp->subDTypep()) { + if (AstNodeDType * childTypep = arrayp->subDTypep()) { std::ostringstream out; - out<<"["; - int arrayElements = arrayp->elementsConst(); - for (int element = 0; element < arrayElements; ++element) { - int width = childTypep->width(); - int lsb = width * element; + out<<"["; + int arrayElements = arrayp->elementsConst(); + for (int element = 0; element < arrayElements; ++element) { + int width = childTypep->width(); + int lsb = width * element; int msb = lsb + width - 1; V3Number fieldNum = V3Number(nump, width); fieldNum.opSel(*nump, msb, lsb); - int arrayElem = arrayp->lsb() + element; - out<ascii(); + int arrayElem = arrayp->lsb() + element; + out<ascii(); } // Checking METHODS @@ -169,30 +172,34 @@ public: virtual void varRefCb(AstVarRef* nodep) {} void clearOptimizable(AstNode* nodep/*null ok*/, const string& why) { - // Something bad found. optimizable() will return false, - // and fetchNumber should not be called or it may assert. - if (!m_whyNotNodep) { - m_whyNotNodep = nodep; - if (debug()>=5) { - UINFO(0,"Clear optimizable: "<=5) { + UINFO(0,"Clear optimizable: "<::iterator it=m_callStack.begin(); it !=m_callStack.end(); ++it) { - AstFuncRef* funcp = (*it)->m_funcp; - stack<<"\nCalled from:\n"<fileline()<<" "<prettyName()<<"() with parameters:"; - V3TaskConnects* tconnects = (*it)->m_tconnects; - for (V3TaskConnects::iterator conIt = tconnects->begin(); conIt != tconnects->end(); ++conIt) { - AstVar* portp = conIt->first; - AstNode* pinp = conIt->second->exprp(); - AstNodeDType* dtypep = pinp->dtypep(); - stack<<"\n "<prettyName()<<" = "<::iterator it=m_callStack.begin(); + it != m_callStack.end(); ++it) { + AstFuncRef* funcp = (*it)->m_funcp; + stack<<"\nCalled from:\n"<fileline()<<" " + <prettyName()<<"() with parameters:"; + V3TaskConnects* tconnects = (*it)->m_tconnects; + for (V3TaskConnects::iterator conIt = tconnects->begin(); + conIt != tconnects->end(); ++conIt) { + AstVar* portp = conIt->first; + AstNode* pinp = conIt->second->exprp(); + AstNodeDType* dtypep = pinp->dtypep(); + stack<<"\n "<prettyName( + )<<" = "<width()<width(nodep->width()); + // Save time - kept a list of allocated but unused V3Numbers + // It would be more efficient to do this by size, but the extra accounting + // slows things down more than we gain. + V3Number* nump; + if (!m_numFreeps.empty()) { + //UINFO(7,"Num Reuse "<width()<width(nodep->width()); nump->nodep(nodep); nump->setLong(value); } else { //UINFO(7,"Num New "<width()<width(), value); - m_numAllps.push_back(nump); - } + m_numAllps.push_back(nump); + } nump->isDouble(nodep->isDouble()); nump->isString(nodep->isString()); return nump; } public: V3Number* newNumber(AstNode* nodep, uint32_t value=0) { - // Set a constant value for this node - if (!nodep->user3p()) { - V3Number* nump = allocNumber(nodep, value); - setNumber(nodep, nump); - return nump; - } else { - return (fetchNumber(nodep)); - } + // Set a constant value for this node + if (!nodep->user3p()) { + V3Number* nump = allocNumber(nodep, value); + setNumber(nodep, nump); + return nump; + } else { + return (fetchNumber(nodep)); + } } V3Number* newOutNumber(AstNode* nodep, uint32_t value=0) { - // Set a constant value for this node - if (!nodep->user2p()) { - V3Number* nump = allocNumber(nodep, value); - setOutNumber(nodep, nump); - return nump; - } else { - return (fetchOutNumber(nodep)); - } + // Set a constant value for this node + if (!nodep->user2p()) { + V3Number* nump = allocNumber(nodep, value); + setOutNumber(nodep, nump); + return nump; + } else { + return (fetchOutNumber(nodep)); + } } void newNumber(AstNode* nodep, const V3Number& numr) { newNumber(nodep)->opAssign(numr); @@ -252,307 +259,314 @@ public: newOutNumber(nodep)->opAssign(numr); } V3Number* fetchNumberNull(AstNode* nodep) { - return ((V3Number*)nodep->user3p()); + return ((V3Number*)nodep->user3p()); } V3Number* fetchOutNumberNull(AstNode* nodep) { - return ((V3Number*)nodep->user2p()); + return ((V3Number*)nodep->user2p()); } V3Number* fetchNumber(AstNode* nodep) { - V3Number* nump = fetchNumberNull(nodep); - if (!nump) nodep->v3fatalSrc("No value found for node."); - //UINFO(9," fetch num "<<*nump<<" on "<v3fatalSrc("No value found for node."); + //UINFO(9," fetch num "<<*nump<<" on "<v3fatalSrc("No value found for node."); - return nump; + V3Number* nump = fetchOutNumberNull(nodep); + if (!nump) nodep->v3fatalSrc("No value found for node."); + return nump; } private: inline void setNumber(AstNode* nodep, const V3Number* nump) { - UINFO(9," set num "<<*nump<<" on "<user3p((void*)nump); + UINFO(9," set num "<<*nump<<" on "<user3p((void*)nump); } inline void setOutNumber(AstNode* nodep, const V3Number* nump) { - UINFO(9," set num "<<*nump<<" on "<user2p((void*)nump); + UINFO(9," set num "<<*nump<<" on "<user2p((void*)nump); } void checkNodeInfo(AstNode* nodep) { - if (m_checkOnly) { - m_instrCount += nodep->instrCount(); - m_dataCount += nodep->width(); - } - if (!nodep->isPredictOptimizable()) { - //UINFO(9," !predictopt "<instrCount(); + m_dataCount += nodep->width(); + } + if (!nodep->isPredictOptimizable()) { + //UINFO(9," !predictopt "<prettyTypeName()<prettyTypeName()<varScopep(); - else vscp = nodep->varp(); - if (!vscp) nodep->v3fatalSrc("Not linked"); - return vscp; + AstNode* vscp; + if (m_scoped) vscp = nodep->varScopep(); + else vscp = nodep->varp(); + if (!vscp) nodep->v3fatalSrc("Not linked"); + return vscp; } int unrollCount() { - return m_params ? v3Global.opt.unrollCount()*16 - : v3Global.opt.unrollCount(); + return m_params ? v3Global.opt.unrollCount()*16 + : v3Global.opt.unrollCount(); } bool jumpingOver(AstNode* nodep) { - // True to jump over this node - all visitors must call this up front - return (m_jumpp && m_jumpp->labelp()!=nodep); + // True to jump over this node - all visitors must call this up front + return (m_jumpp && m_jumpp->labelp()!=nodep); } void assignOutNumber(AstNodeAssign* nodep, AstNode* vscp, const V3Number* nump) { if (VN_IS(nodep, AssignDly)) { - // Don't do setNumber, as value isn't yet visible to following statements + // Don't do setNumber, as value isn't yet visible to following statements newOutNumber(vscp, *nump); - } else { + } else { newNumber(vscp, *nump); newOutNumber(vscp, *nump); - } + } } // VISITORS virtual void visit(AstAlways* nodep) { - if (jumpingOver(nodep)) return; - checkNodeInfo(nodep); + if (jumpingOver(nodep)) return; + checkNodeInfo(nodep); iterateChildren(nodep); } virtual void visit(AstSenTree* nodep) { - // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. + // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. } virtual void visit(AstVarRef* nodep) { - if (jumpingOver(nodep)) return; - if (!optimizable()) return; // Accelerate + if (jumpingOver(nodep)) return; + if (!optimizable()) return; // Accelerate if (!nodep->varp()) nodep->v3fatalSrc("Unlinked"); iterateChildren(nodep->varp()); - AstNode* vscp = varOrScope(nodep); + AstNode* vscp = varOrScope(nodep); - // We can't have non-delayed assignments with same value on LHS and RHS - // as we don't figure out variable ordering. - // Delayed is OK though, as we'll decode the next state separately. + // We can't have non-delayed assignments with same value on LHS and RHS + // as we don't figure out variable ordering. + // Delayed is OK though, as we'll decode the next state separately. if (!VN_IS(nodep->varp()->dtypeSkipRefp(), BasicDType) && !VN_IS(nodep->varp()->dtypeSkipRefp(), PackArrayDType) && !VN_IS(nodep->varp()->dtypeSkipRefp(), StructDType)) - clearOptimizable(nodep,"Array references/not basic"); - if (nodep->lvalue()) { - if (m_inDlyAssign) { - if (!(vscp->user1() & VU_LVDLY)) { - vscp->user1( vscp->user1() | VU_LVDLY); - if (m_checkOnly) varRefCb (nodep); - } + clearOptimizable(nodep, "Array references/not basic"); + if (nodep->lvalue()) { + if (m_inDlyAssign) { + if (!(vscp->user1() & VU_LVDLY)) { + vscp->user1( vscp->user1() | VU_LVDLY); + if (m_checkOnly) varRefCb(nodep); + } } else { // nondly asn - if (!(vscp->user1() & VU_LV)) { - if (!m_params && (vscp->user1() & VU_RV)) clearOptimizable(nodep,"Var read & write"); - vscp->user1( vscp->user1() | VU_LV); - if (m_checkOnly) varRefCb (nodep); - } - } - } else { - if (!(vscp->user1() & VU_RV)) { - if (!m_params && (vscp->user1() & VU_LV)) clearOptimizable(nodep,"Var write & read"); - vscp->user1( vscp->user1() | VU_RV); - bool isConst = nodep->varp()->isParam(); - V3Number* nump = isConst ? fetchNumberNull(nodep->varp()->valuep()) : NULL; + if (!(vscp->user1() & VU_LV)) { + if (!m_params && (vscp->user1() & VU_RV)) { + clearOptimizable(nodep, "Var read & write"); + } + vscp->user1( vscp->user1() | VU_LV); + if (m_checkOnly) varRefCb(nodep); + } + } + } else { + if (!(vscp->user1() & VU_RV)) { + if (!m_params && (vscp->user1() & VU_LV)) { + clearOptimizable(nodep, "Var write & read"); + } + vscp->user1( vscp->user1() | VU_RV); + bool isConst = nodep->varp()->isParam(); + V3Number* nump = isConst ? fetchNumberNull(nodep->varp()->valuep()) : NULL; if (isConst && nump) { // Propagate PARAM constants for constant function analysis - if (!m_checkOnly && optimizable()) { + if (!m_checkOnly && optimizable()) { newNumber(vscp, *nump); - } - } else { - if (m_checkOnly) varRefCb (nodep); - } - } - } + } + } else { + if (m_checkOnly) varRefCb(nodep); + } + } + } if (!m_checkOnly && optimizable()) { // simulating - if (nodep->lvalue()) { - nodep->v3fatalSrc("LHS varref should be handled in AstAssign visitor."); - } else { - // Return simulation value - copy by reference instead of value for speed - V3Number* nump = fetchNumberNull(vscp); - if (!nump) { - if (m_params) { - clearOptimizable(nodep,"Language violation: reference to non-function-local variable"); - } else { - nodep->v3fatalSrc("Variable value should have been set before any visitor called."); - } - nump = allocNumber(nodep, 0); // Any value; just so recover from error - } - setNumber(nodep, nump); - } - } + if (nodep->lvalue()) { + nodep->v3fatalSrc("LHS varref should be handled in AstAssign visitor."); + } else { + // Return simulation value - copy by reference instead of value for speed + V3Number* nump = fetchNumberNull(vscp); + if (!nump) { + if (m_params) { + clearOptimizable(nodep, "Language violation: reference to non-function-local variable"); + } else { + nodep->v3fatalSrc("Variable value should have been set before any visitor called."); + } + nump = allocNumber(nodep, 0); // Any value; just so recover from error + } + setNumber(nodep, nump); + } + } } virtual void visit(AstVarXRef* nodep) { - if (jumpingOver(nodep)) return; + if (jumpingOver(nodep)) return; if (m_scoped) { badNodeType(nodep); return; } - else { clearOptimizable(nodep,"Language violation: Dotted hierarchical references not allowed in constant functions"); } + else { clearOptimizable(nodep, "Language violation: Dotted hierarchical references not allowed in constant functions"); } } virtual void visit(AstNodeFTask* nodep) { - if (jumpingOver(nodep)) return; - if (!m_params) { badNodeType(nodep); return; } - if (nodep->dpiImport()) { clearOptimizable(nodep,"DPI import functions aren't simulatable"); } - checkNodeInfo(nodep); + if (jumpingOver(nodep)) return; + if (!m_params) { badNodeType(nodep); return; } + if (nodep->dpiImport()) { + clearOptimizable(nodep, "DPI import functions aren't simulatable"); + } + checkNodeInfo(nodep); iterateChildren(nodep); } virtual void visit(AstNodeIf* nodep) { - if (jumpingOver(nodep)) return; - UINFO(5," IF "<condp()); - if (optimizable()) { - if (fetchNumber(nodep->condp())->isNeqZero()) { + if (optimizable()) { + if (fetchNumber(nodep->condp())->isNeqZero()) { iterateAndNextNull(nodep->ifsp()); - } else { + } else { iterateAndNextNull(nodep->elsesp()); - } - } - } + } + } + } } virtual void visit(AstConst* nodep) { - checkNodeInfo(nodep); - if (!m_checkOnly && optimizable()) { - setNumber(nodep, &(nodep->num())); - } + checkNodeInfo(nodep); + if (!m_checkOnly && optimizable()) { + setNumber(nodep, &(nodep->num())); + } } virtual void visit(AstEnumItemRef* nodep) { - checkNodeInfo(nodep); - if (!nodep->itemp()) nodep->v3fatalSrc("Not linked"); - if (!m_checkOnly && optimizable()) { + checkNodeInfo(nodep); + if (!nodep->itemp()) nodep->v3fatalSrc("Not linked"); + if (!m_checkOnly && optimizable()) { AstNode* valuep = nodep->itemp()->valuep(); - if (valuep) { + if (valuep) { iterateAndNextNull(valuep); - if (optimizable()) { + if (optimizable()) { newNumber(nodep, *fetchNumber(valuep)); - } + } } else { clearOptimizable(nodep, "No value found for enum item"); } } } virtual void visit(AstNodeUniop* nodep) { - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); iterateChildren(nodep); - if (!m_checkOnly && optimizable()) { + if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp())); - } + } } virtual void visit(AstNodeBiop* nodep) { - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); iterateChildren(nodep); - if (!m_checkOnly && optimizable()) { + if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()), *fetchNumber(nodep->rhsp())); - } + } } virtual void visit(AstNodeTriop* nodep) { - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); iterateChildren(nodep); - if (!m_checkOnly && optimizable()) { - nodep->numberOperate(*newNumber(nodep), - *fetchNumber(nodep->lhsp()), - *fetchNumber(nodep->rhsp()), - *fetchNumber(nodep->thsp())); - } + if (!m_checkOnly && optimizable()) { + nodep->numberOperate(*newNumber(nodep), + *fetchNumber(nodep->lhsp()), + *fetchNumber(nodep->rhsp()), + *fetchNumber(nodep->thsp())); + } } virtual void visit(AstLogAnd* nodep) { - // Need to short circuit - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); - if (m_checkOnly) { + // Need to short circuit + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { iterateChildren(nodep); - } else { + } else { iterate(nodep->lhsp()); - if (optimizable()) { - if (fetchNumber(nodep->lhsp())->isNeqZero()) { + if (optimizable()) { + if (fetchNumber(nodep->lhsp())->isNeqZero()) { iterate(nodep->rhsp()); newNumber(nodep, *fetchNumber(nodep->rhsp())); - } else { + } else { newNumber(nodep, *fetchNumber(nodep->lhsp())); // a zero - } - } - } + } + } + } } virtual void visit(AstLogOr* nodep) { - // Need to short circuit - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); - if (m_checkOnly) { + // Need to short circuit + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { iterateChildren(nodep); - } else { + } else { iterate(nodep->lhsp()); - if (optimizable()) { - if (fetchNumber(nodep->lhsp())->isNeqZero()) { + if (optimizable()) { + if (fetchNumber(nodep->lhsp())->isNeqZero()) { newNumber(nodep, *fetchNumber(nodep->lhsp())); // a one - } else { + } else { iterate(nodep->rhsp()); newNumber(nodep, *fetchNumber(nodep->rhsp())); - } - } - } + } + } + } } virtual void visit(AstLogIf* nodep) { - // Need to short circuit, same as (!A || B) - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); - if (m_checkOnly) { + // Need to short circuit, same as (!A || B) + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { iterateChildren(nodep); - } else { + } else { iterate(nodep->lhsp()); - if (optimizable()) { + if (optimizable()) { if (fetchNumber(nodep->lhsp())->isEqZero()) { newNumber(nodep, V3Number(nodep, 1, 1)); // a one } else { iterate(nodep->rhsp()); newNumber(nodep, *fetchNumber(nodep->rhsp())); - } - } - } + } + } + } } virtual void visit(AstNodeCond* nodep) { - // We could use above visit(AstNodeTriop), but need to do short circuiting. - // It's also slower even O(n^2) to evaluate both sides when we really only need to evaluate one side. - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); - if (m_checkOnly) { + // We could use above visit(AstNodeTriop), but need to do short circuiting. + // It's also slower even O(n^2) to evaluate both sides when we + // really only need to evaluate one side. + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { iterateChildren(nodep); - } else { + } else { iterate(nodep->condp()); - if (optimizable()) { - if (fetchNumber(nodep->condp())->isNeqZero()) { + if (optimizable()) { + if (fetchNumber(nodep->condp())->isNeqZero()) { iterate(nodep->expr1p()); newNumber(nodep, *fetchNumber(nodep->expr1p())); - } else { + } else { iterate(nodep->expr2p()); newNumber(nodep, *fetchNumber(nodep->expr2p())); - } - } - } + } + } + } } void handleAssignSel(AstNodeAssign* nodep, AstSel* selp) { - AstVarRef* varrefp = NULL; + AstVarRef* varrefp = NULL; V3Number lsb = V3Number(nodep); iterateAndNextNull(nodep->rhsp()); // Value to assign handleAssignSelRecurse(nodep, selp, varrefp/*ref*/, lsb/*ref*/, 0); @@ -568,345 +582,350 @@ private: outnum = *vscpnump; } else { // Assignment to unassigned variable, all bits are X or 0 outnum = V3Number(nodep, varrefp->varp()->widthMin()); - if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) { - outnum.setAllBits0(); - } else { - outnum.setAllBitsX(); - } - } - outnum.opSelInto(*fetchNumber(nodep->rhsp()), - lsb, - selp->widthConst()); - assignOutNumber(nodep, vscp, &outnum); - } + if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) { + outnum.setAllBits0(); + } else { + outnum.setAllBitsX(); + } + } + outnum.opSelInto(*fetchNumber(nodep->rhsp()), + lsb, + selp->widthConst()); + assignOutNumber(nodep, vscp, &outnum); + } } void handleAssignSelRecurse(AstNodeAssign* nodep, AstSel* selp, AstVarRef*& outVarrefpRef, V3Number& lsbRef, int depth) { - // Recurse down to find final variable being set (outVarrefp), with value to write on nodep->rhsp() - checkNodeInfo(selp); + // Recurse down to find final variable being set (outVarrefp), with + // value to write on nodep->rhsp() + checkNodeInfo(selp); iterateAndNextNull(selp->lsbp()); // Bit index if (AstVarRef* varrefp = VN_CAST(selp->fromp(), VarRef)) { - outVarrefpRef = varrefp; - lsbRef = *fetchNumber(selp->lsbp()); - return; // And presumably still optimizable() + outVarrefpRef = varrefp; + lsbRef = *fetchNumber(selp->lsbp()); + return; // And presumably still optimizable() } else if (AstSel* subselp = VN_CAST(selp->lhsp(), Sel)) { V3Number sublsb = V3Number(nodep); handleAssignSelRecurse(nodep, subselp, outVarrefpRef, sublsb/*ref*/, depth+1); if (optimizable()) { - lsbRef = sublsb; - lsbRef.opAdd(sublsb, *fetchNumber(selp->lsbp())); - } - } else { - clearOptimizable(nodep, "Select LHS isn't simple variable"); - } + lsbRef = sublsb; + lsbRef.opAdd(sublsb, *fetchNumber(selp->lsbp())); + } + } else { + clearOptimizable(nodep, "Select LHS isn't simple variable"); + } } virtual void visit(AstNodeAssign* nodep) { - if (jumpingOver(nodep)) return; - if (!optimizable()) return; // Accelerate + if (jumpingOver(nodep)) return; + if (!optimizable()) return; // Accelerate if (VN_IS(nodep, AssignDly)) { - if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non-dly assigns"); - m_anyAssignDly = true; - m_inDlyAssign = true; - } else { - if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non-dly assigns"); - m_anyAssignComb = true; - } + if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non-dly assigns"); + m_anyAssignDly = true; + m_inDlyAssign = true; + } else { + if (m_anyAssignDly) clearOptimizable(nodep, "Mix of dly/non-dly assigns"); + m_anyAssignComb = true; + } if (AstSel* selp = VN_CAST(nodep->lhsp(), Sel)) { - if (!m_params) { clearOptimizable(nodep, "LHS has select"); return; } - handleAssignSel(nodep, selp); - } + if (!m_params) { clearOptimizable(nodep, "LHS has select"); return; } + handleAssignSel(nodep, selp); + } else if (!VN_IS(nodep->lhsp(), VarRef)) { - clearOptimizable(nodep, "LHS isn't simple variable"); - } - else if (m_checkOnly) { + clearOptimizable(nodep, "LHS isn't simple variable"); + } + else if (m_checkOnly) { iterateChildren(nodep); - } - else if (optimizable()) { + } + else if (optimizable()) { iterateAndNextNull(nodep->rhsp()); - if (optimizable()) { + if (optimizable()) { AstNode* vscp = varOrScope(VN_CAST(nodep->lhsp(), VarRef)); - assignOutNumber(nodep, vscp, fetchNumber(nodep->rhsp())); - } - } - m_inDlyAssign = false; + assignOutNumber(nodep, vscp, fetchNumber(nodep->rhsp())); + } + } + m_inDlyAssign = false; } virtual void visit(AstBegin* nodep) { - checkNodeInfo(nodep); + checkNodeInfo(nodep); iterateChildren(nodep); } virtual void visit(AstNodeCase* nodep) { - if (jumpingOver(nodep)) return; - UINFO(5," CASE "<exprp()); - bool hit = false; - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - if (!itemp->isDefault()) { - for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) { - if (hit) break; + bool hit = false; + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { + if (!itemp->isDefault()) { + for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) { + if (hit) break; iterateAndNextNull(ep); if (optimizable()) { V3Number match (nodep, 1); match.opEq(*fetchNumber(nodep->exprp()), *fetchNumber(ep)); - if (match.isNeqZero()) { + if (match.isNeqZero()) { iterateAndNextNull(itemp->bodysp()); - hit = true; - } - } - } - } - } - // Else default match - for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - if (hit) break; - if (!hit && itemp->isDefault()) { + hit = true; + } + } + } + } + } + // Else default match + for (AstCaseItem* itemp = nodep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), CaseItem)) { + if (hit) break; + if (!hit && itemp->isDefault()) { iterateAndNextNull(itemp->bodysp()); - hit = true; - } - } - } + hit = true; + } + } + } } virtual void visit(AstCaseItem* nodep) { - // Real handling is in AstNodeCase - if (jumpingOver(nodep)) return; - checkNodeInfo(nodep); + // Real handling is in AstNodeCase + if (jumpingOver(nodep)) return; + checkNodeInfo(nodep); iterateChildren(nodep); } virtual void visit(AstComment*) {} virtual void visit(AstJumpGo* nodep) { - if (jumpingOver(nodep)) return; - checkNodeInfo(nodep); - if (!m_checkOnly) { - UINFO(5," JUMP GO "<labelp() == nodep) { - UINFO(5," JUMP DONE "<labelp() == nodep) { + UINFO(5," JUMP DONE "<initsp()); - while (1) { - UINFO(5," FOR-ITER "<condp()); - if (!optimizable()) break; - if (!fetchNumber(nodep->condp())->isNeqZero()) { - break; - } + if (!optimizable()) break; + if (!fetchNumber(nodep->condp())->isNeqZero()) { + break; + } iterateAndNextNull(nodep->bodysp()); iterateAndNextNull(nodep->incsp()); - if (loops++ > unrollCount()*16) { + if (loops++ > unrollCount()*16) { clearOptimizable(nodep, "Loop unrolling took too long; probably this is an" "infinite loop, or set --unroll-count above " + cvtToStr(unrollCount())); - break; - } - } - } + break; + } + } + } } virtual void visit(AstWhile* nodep) { - // Doing lots of Whiles is slow, so only for parameters - if (jumpingOver(nodep)) return; - UINFO(5," WHILE "<precondsp()); - if (jumpingOver(nodep)) break; + if (jumpingOver(nodep)) break; iterateAndNextNull(nodep->condp()); - if (jumpingOver(nodep)) break; - if (!optimizable()) break; - if (!fetchNumber(nodep->condp())->isNeqZero()) { - break; - } + if (jumpingOver(nodep)) break; + if (!optimizable()) break; + if (!fetchNumber(nodep->condp())->isNeqZero()) { + break; + } iterateAndNextNull(nodep->bodysp()); - if (jumpingOver(nodep)) break; + if (jumpingOver(nodep)) break; iterateAndNextNull(nodep->incsp()); - if (jumpingOver(nodep)) break; + if (jumpingOver(nodep)) break; - // Prep for next loop - if (loops++ > unrollCount()*16) { + // Prep for next loop + if (loops++ > unrollCount()*16) { clearOptimizable(nodep, "Loop unrolling took too long; probably this is an infinite" " loop, or set --unroll-count above "+cvtToStr(unrollCount())); - break; - } - } - } + break; + } + } + } } virtual void visit(AstFuncRef* nodep) { - if (jumpingOver(nodep)) return; - if (!optimizable()) return; // Accelerate - UINFO(5," FUNCREF "<taskp(), NodeFTask); if (!funcp) nodep->v3fatalSrc("Not linked"); + if (jumpingOver(nodep)) return; + if (!optimizable()) return; // Accelerate + UINFO(5," FUNCREF "<taskp(), NodeFTask); + if (!funcp) nodep->v3fatalSrc("Not linked"); if (m_params) { V3Width::widthParamsEdit(funcp); } VL_DANGLING(funcp); // Make sure we've sized the function funcp = VN_CAST(nodep->taskp(), NodeFTask); if (!funcp) nodep->v3fatalSrc("Not linked"); - // Apply function call values to function - V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); - // Must do this in two steps, eval all params, then apply them - // Otherwise chained functions may have the wrong results - for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { - AstVar* portp = it->first; - AstNode* pinp = it->second->exprp(); - if (pinp) { // Else too few arguments in function call - ignore it + // Apply function call values to function + V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); + // Must do this in two steps, eval all params, then apply them + // Otherwise chained functions may have the wrong results + for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { + AstVar* portp = it->first; + AstNode* pinp = it->second->exprp(); + if (pinp) { // Else too few arguments in function call - ignore it if (portp->isWritable()) { clearOptimizable(portp, "Language violation: Outputs/refs not allowed in constant functions"); return; } // Evaluate pin value iterate(pinp); - } - } - for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { - AstVar* portp = it->first; - AstNode* pinp = it->second->exprp(); - if (pinp) { // Else too few arguments in function call - ignore it - // Apply value to the function - if (!m_checkOnly && optimizable()) { + } + } + for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { + AstVar* portp = it->first; + AstNode* pinp = it->second->exprp(); + if (pinp) { // Else too few arguments in function call - ignore it + // Apply value to the function + if (!m_checkOnly && optimizable()) { newNumber(portp, *fetchNumber(pinp)); - } - } - } + } + } + } SimStackNode stackNode(nodep, &tconnects); - m_callStack.push_front(&stackNode); - // Evaluate the function + m_callStack.push_front(&stackNode); + // Evaluate the function iterate(funcp); - m_callStack.pop_front(); - if (!m_checkOnly && optimizable()) { - // Grab return value from output variable (if it's a function) - if (!funcp->fvarp()) nodep->v3fatalSrc("Function reference points at non-function"); + m_callStack.pop_front(); + if (!m_checkOnly && optimizable()) { + // Grab return value from output variable (if it's a function) + if (!funcp->fvarp()) nodep->v3fatalSrc("Function reference points at non-function"); newNumber(nodep, *fetchNumber(funcp->fvarp())); - } + } } virtual void visit(AstVar* nodep) { - if (jumpingOver(nodep)) return; - if (!m_params) { badNodeType(nodep); return; } + if (jumpingOver(nodep)) return; + if (!m_params) { badNodeType(nodep); return; } } virtual void visit(AstScopeName *nodep) { - if (jumpingOver(nodep)) return; - if (!m_params) { badNodeType(nodep); return; } - // Ignore + if (jumpingOver(nodep)) return; + if (!m_params) { badNodeType(nodep); return; } + // Ignore } virtual void visit(AstSFormatF *nodep) { - if (jumpingOver(nodep)) return; - if (!optimizable()) return; // Accelerate + if (jumpingOver(nodep)) return; + if (!optimizable()) return; // Accelerate iterateChildren(nodep); - if (m_params) { - AstNode* nextArgp = nodep->exprsp(); + if (m_params) { + AstNode* nextArgp = nodep->exprsp(); string result; - string format = nodep->text(); - string::const_iterator pos = format.begin(); - bool inPct = false; - for (; pos != format.end(); ++pos) { - if (!inPct && pos[0] == '%') { - inPct = true; + string format = nodep->text(); + string::const_iterator pos = format.begin(); + bool inPct = false; + for (; pos != format.end(); ++pos) { + if (!inPct && pos[0] == '%') { + inPct = true; } else if (!inPct) { // Normal text - result += *pos; + result += *pos; } else { // Format character - inPct = false; + inPct = false; - if (V3Number::displayedFmtLegal(tolower(pos[0]))) { - AstNode* argp = nextArgp; - nextArgp = nextArgp->nextp(); - V3Number* nump = fetchNumberNull(argp); - if (!nump) { - clearOptimizable(nodep, "Argument for $display like statement is not constant"); - break; - } + if (V3Number::displayedFmtLegal(tolower(pos[0]))) { + AstNode* argp = nextArgp; + nextArgp = nextArgp->nextp(); + V3Number* nump = fetchNumberNull(argp); + if (!nump) { + clearOptimizable(nodep, "Argument for $display like statement is not constant"); + break; + } string format = string("%") + pos[0]; result += nump->displayed(nodep, format); } else { - switch (tolower(pos[0])) { - case '%': - result += "%"; - break; - case 'm': - // This happens prior to AstScope so we don't know the scope name. Leave the %m in place. - result += "%m"; - break; - default: - clearOptimizable(nodep, "Unknown $display-like format code."); - break; - } - } - } - } + switch (tolower(pos[0])) { + case '%': + result += "%"; + break; + case 'm': + // This happens prior to AstScope so we don't + // know the scope name. Leave the %m in place. + result += "%m"; + break; + default: + clearOptimizable(nodep, "Unknown $display-like format code."); + break; + } + } + } + } V3Number* resultNump = new V3Number(V3Number::String(), nodep, result); setNumber(nodep, resultNump); m_stringNumbersp.push_back(resultNump); - } + } } virtual void visit(AstDisplay *nodep) { - if (jumpingOver(nodep)) return; - if (!optimizable()) return; // Accelerate + if (jumpingOver(nodep)) return; + if (!optimizable()) return; // Accelerate iterateChildren(nodep); - if (m_params) { - V3Number* textp = fetchNumber(nodep->fmtp()); - switch (nodep->displayType()) { - case AstDisplayType::DT_DISPLAY: // FALLTHRU - case AstDisplayType::DT_INFO: - v3warn(USERINFO, textp->toString()); - break; - case AstDisplayType::DT_ERROR: - v3warn(USERERROR, textp->toString()); - break; - case AstDisplayType::DT_WARNING: - v3warn(USERWARN, textp->toString()); - break; - case AstDisplayType::DT_FATAL: - v3warn(USERFATAL, textp->toString()); - break; - case AstDisplayType::DT_WRITE: // FALLTHRU - default: - clearOptimizable(nodep, "Unexpected display type"); - } - } + if (m_params) { + V3Number* textp = fetchNumber(nodep->fmtp()); + switch (nodep->displayType()) { + case AstDisplayType::DT_DISPLAY: // FALLTHRU + case AstDisplayType::DT_INFO: + v3warn(USERINFO, textp->toString()); + break; + case AstDisplayType::DT_ERROR: + v3warn(USERERROR, textp->toString()); + break; + case AstDisplayType::DT_WARNING: + v3warn(USERWARN, textp->toString()); + break; + case AstDisplayType::DT_FATAL: + v3warn(USERFATAL, textp->toString()); + break; + case AstDisplayType::DT_WRITE: // FALLTHRU + default: + clearOptimizable(nodep, "Unexpected display type"); + } + } } // default @@ -914,74 +933,76 @@ private: // AstCoverInc, AstArraySel, AstFinish, // AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt virtual void visit(AstNode* nodep) { - if (jumpingOver(nodep)) return; - badNodeType(nodep); + if (jumpingOver(nodep)) return; + badNodeType(nodep); } private: // MEMBERS - called by constructor void setMode(bool scoped, bool checkOnly, bool params) { - m_checkOnly = checkOnly; - m_scoped = scoped; - m_params = params; + m_checkOnly = checkOnly; + m_scoped = scoped; + m_params = params; } void mainGuts(AstNode* nodep) { iterate(nodep); - if (m_jumpp) { - m_jumpp->v3fatalSrc("JumpGo branched to label that wasn't found"); - m_jumpp = NULL; - } + if (m_jumpp) { + m_jumpp->v3fatalSrc("JumpGo branched to label that wasn't found"); + m_jumpp = NULL; + } } public: // CONSTRUCTORS SimulateVisitor() { // Note AstUser#InUse ensures only one invocation exists at once - setMode(false,false,false); + setMode(false, false, false); clear(); // We reuse this structure in the main loop, so put initializers inside clear() } void clear() { - m_whyNotOptimizable = ""; - m_whyNotNodep = NULL; - m_anyAssignComb = false; - m_anyAssignDly = false; - m_inDlyAssign = false; - m_instrCount = 0; - m_dataCount = 0; - m_jumpp = NULL; + m_whyNotOptimizable = ""; + m_whyNotNodep = NULL; + m_anyAssignComb = false; + m_anyAssignDly = false; + m_inDlyAssign = false; + m_instrCount = 0; + m_dataCount = 0; + m_jumpp = NULL; AstNode::user1ClearTree(); AstNode::user2ClearTree(); AstNode::user3ClearTree(); - // Move all allocated numbers to the free pool - m_numFreeps = m_numAllps; + // Move all allocated numbers to the free pool + m_numFreeps = m_numAllps; } void mainTableCheck(AstNode* nodep) { - setMode(true/*scoped*/,true/*checking*/, false/*params*/); - mainGuts(nodep); + setMode(true/*scoped*/, true/*checking*/, false/*params*/); + mainGuts(nodep); } void mainTableEmulate(AstNode* nodep) { - setMode(true/*scoped*/,false/*checking*/, false/*params*/); - mainGuts(nodep); + setMode(true/*scoped*/, false/*checking*/, false/*params*/); + mainGuts(nodep); } void mainCheckTree(AstNode* nodep) { - setMode(false/*scoped*/,true/*checking*/, false/*params*/); - mainGuts(nodep); + setMode(false/*scoped*/, true/*checking*/, false/*params*/); + mainGuts(nodep); } void mainParamEmulate(AstNode* nodep) { - setMode(false/*scoped*/,false/*checking*/, true/*params*/); - mainGuts(nodep); + setMode(false/*scoped*/, false/*checking*/, true/*params*/); + mainGuts(nodep); } virtual ~SimulateVisitor() { - for (std::deque::iterator it = m_numAllps.begin(); it != m_numAllps.end(); ++it) { - delete (*it); - } - for (std::deque::iterator it = m_stringNumbersp.begin(); it != m_stringNumbersp.end(); ++it) { - delete (*it); - } - m_stringNumbersp.clear(); - m_numFreeps.clear(); - m_numAllps.clear(); + for (std::deque::iterator it = m_numAllps.begin(); + it != m_numAllps.end(); ++it) { + delete (*it); + } + for (std::deque::iterator it = m_stringNumbersp.begin(); + it != m_stringNumbersp.end(); ++it) { + delete (*it); + } + m_stringNumbersp.clear(); + m_numFreeps.clear(); + m_numAllps.clear(); } }; diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 8aa132ea6..5a1be7966 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -18,22 +18,22 @@ // //************************************************************************* // Slice TRANSFORMATIONS: -// Top-down traversal (SliceVisitor): -// NODEASSIGN -// ARRAYSEL -// Compare the dimensions to the Var to check for implicit slices. -// Using ->length() calculate the number of clones needed. -// VARREF -// Check the dimensions of the Var for an implicit slice. -// Replace with ArraySel nodes if needed. -// SEL, EXTEND -// We might be assigning a 1-D packed array to a 2-D packed array, -// this is unsupported. -// SliceCloneVisitor (called if this node is a slice): -// NODEASSIGN -// Clone and iterate the clone: -// ARRAYSEL -// Modify bitp() for the new value and set ->length(1) +// Top-down traversal (SliceVisitor): +// NODEASSIGN +// ARRAYSEL +// Compare the dimensions to the Var to check for implicit slices. +// Using ->length() calculate the number of clones needed. +// VARREF +// Check the dimensions of the Var for an implicit slice. +// Replace with ArraySel nodes if needed. +// SEL, EXTEND +// We might be assigning a 1-D packed array to a 2-D packed array, +// this is unsupported. +// SliceCloneVisitor (called if this node is a slice): +// NODEASSIGN +// Clone and iterate the clone: +// ARRAYSEL +// Modify bitp() for the new value and set ->length(1) // // TODO: This code was written before SLICESEL was a type it might be // simplified to look primarily for SLICESELs. @@ -53,125 +53,129 @@ class SliceVisitor : public AstNVisitor { // NODE STATE // Cleared on netlist - // AstNodeAssign::user1() -> bool. True if find is complete + // AstNodeAssign::user1() -> bool. True if find is complete // AstNodeUniop::user1() -> bool. True if find is complete - // AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to - AstUser1InUse m_inuser1; + // AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to + AstUser1InUse m_inuser1; // STATE - AstNode* m_assignp; // Assignment we are under - bool m_assignError; // True if the current assign already has an error + AstNode* m_assignp; // Assignment we are under + bool m_assignError; // True if the current assign already has an error // METHODS VL_DEBUG_FUNC; // Declare debug() AstNode* cloneAndSel(AstNode* nodep, int elements, int offset) { - // Insert an ArraySel, except for a few special cases + // Insert an ArraySel, except for a few special cases AstUnpackArrayDType* arrayp = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); - if (!arrayp) { // V3Width should have complained, but... + if (!arrayp) { // V3Width should have complained, but... if (!m_assignError) { nodep->v3error(nodep->prettyTypeName() <<" is not an unpacked array, but is in an unpacked array context"); } - m_assignError = true; - return nodep->cloneTree(false); // Likely will cause downstream errors - } - if (arrayp->rangep()->elementsConst() != elements) { - if (!m_assignError) nodep->v3error("Slices of arrays in assignments have different unpacked dimensions, " - <rangep()->elementsConst()); - m_assignError = true; - elements = 1; offset = 0; - } - AstNode* newp; + m_assignError = true; + return nodep->cloneTree(false); // Likely will cause downstream errors + } + if (arrayp->rangep()->elementsConst() != elements) { + if (!m_assignError) nodep->v3error("Slices of arrays in assignments have different unpacked dimensions, " + <rangep()->elementsConst()); + m_assignError = true; + elements = 1; offset = 0; + } + AstNode* newp; if (AstInitArray* initp = VN_CAST(nodep, InitArray)) { - UINFO(9," cloneInitArray("<initsp(); - int leOffset = !arrayp->rangep()->littleEndian() ? arrayp->rangep()->elementsConst()-1-offset : offset; - for (int pos = 0; itemp && pos < leOffset; ++pos) { - itemp = itemp->nextp(); - } - if (!itemp) { - nodep->v3error("Array initialization has too few elements, need element "<initsp(); - } - newp = itemp->cloneTree(false); - } + UINFO(9," cloneInitArray("<initsp(); + int leOffset = !arrayp->rangep()->littleEndian() + ? arrayp->rangep()->elementsConst()-1-offset : offset; + for (int pos = 0; itemp && pos < leOffset; ++pos) { + itemp = itemp->nextp(); + } + if (!itemp) { + nodep->v3error("Array initialization has too few elements, need element "<initsp(); + } + newp = itemp->cloneTree(false); + } else if (AstNodeCond* snodep = VN_CAST(nodep, NodeCond)) { - UINFO(9," cloneCond("<cloneType(snodep->condp()->cloneTree(false), - cloneAndSel(snodep->expr1p(), elements, offset), - cloneAndSel(snodep->expr2p(), elements, offset)); - } + UINFO(9," cloneCond("<cloneType(snodep->condp()->cloneTree(false), + cloneAndSel(snodep->expr1p(), elements, offset), + cloneAndSel(snodep->expr2p(), elements, offset)); + } else if (AstSliceSel* snodep = VN_CAST(nodep, SliceSel)) { UINFO(9," cloneSliceSel("<declRange().lo() - + (!snodep->declRange().littleEndian() ? snodep->declRange().elements()-1-offset : offset)); + + (!snodep->declRange().littleEndian() + ? snodep->declRange().elements()-1-offset : offset)); newp = new AstArraySel(nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset); } else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)) { - UINFO(9," cloneSel("<rangep()->littleEndian() ? arrayp->rangep()->elementsConst()-1-offset : offset; - newp = new AstArraySel(nodep->fileline(), nodep->cloneTree(false), leOffset); - } - else { - if (!m_assignError) nodep->v3error(nodep->prettyTypeName()<<" unexpected in assignment to unpacked array"); - m_assignError = true; - newp = nodep->cloneTree(false); // Likely will cause downstream errors - } - return newp; + UINFO(9," cloneSel("<rangep()->littleEndian() + ? arrayp->rangep()->elementsConst()-1-offset : offset; + newp = new AstArraySel(nodep->fileline(), nodep->cloneTree(false), leOffset); + } + else { + if (!m_assignError) nodep->v3error(nodep->prettyTypeName()<<" unexpected in assignment to unpacked array"); + m_assignError = true; + newp = nodep->cloneTree(false); // Likely will cause downstream errors + } + return newp; } virtual void visit(AstNodeAssign* nodep) { - // Called recursively on newly created assignments - if (!nodep->user1() + // Called recursively on newly created assignments + if (!nodep->user1() && !VN_IS(nodep, AssignAlias)) { - nodep->user1(true); - m_assignError = false; - if (debug()>=9) { cout<dumpTree(cout," Deslice-In: "); } - AstNodeDType* dtp = nodep->lhsp()->dtypep()->skipRefp(); + nodep->user1(true); + m_assignError = false; + if (debug()>=9) { cout<dumpTree(cout, " Deslice-In: "); } + AstNodeDType* dtp = nodep->lhsp()->dtypep()->skipRefp(); if (AstUnpackArrayDType* arrayp = VN_CAST(dtp, UnpackArrayDType)) { - // Left and right could have different msb/lsbs/endianness, but #elements is common - // and all variables are realigned to start at zero - // Assign of a little endian'ed slice to a big endian one must reverse the elements - AstNode* newlistp = NULL; - int elements = arrayp->rangep()->elementsConst(); - for (int offset = 0; offset < elements; ++offset) { - AstNode* newp = nodep->cloneType // AstNodeAssign - (cloneAndSel(nodep->lhsp(), elements, offset), - cloneAndSel(nodep->rhsp(), elements, offset)); - if (debug()>=9) { newp->dumpTree(cout,"-new "); } - newlistp = AstNode::addNextNull(newlistp, newp); - } - if (debug()>=9) { cout<dumpTree(cout," Deslice-Dn: "); } - nodep->replaceWith(newlistp); nodep->deleteTree(); VL_DANGLING(nodep); - // Normal edit iterator will now iterate on all of the expansion assignments - // This will potentially call this function again to resolve next level of slicing - return; - } - m_assignp = nodep; + // Left and right could have different msb/lsbs/endianness, but #elements is common + // and all variables are realigned to start at zero + // Assign of a little endian'ed slice to a big endian one must reverse the elements + AstNode* newlistp = NULL; + int elements = arrayp->rangep()->elementsConst(); + for (int offset = 0; offset < elements; ++offset) { + AstNode* newp = nodep->cloneType // AstNodeAssign + (cloneAndSel(nodep->lhsp(), elements, offset), + cloneAndSel(nodep->rhsp(), elements, offset)); + if (debug()>=9) { newp->dumpTree(cout, "-new "); } + newlistp = AstNode::addNextNull(newlistp, newp); + } + if (debug()>=9) { cout<dumpTree(cout, " Deslice-Dn: "); } + nodep->replaceWith(newlistp); nodep->deleteTree(); VL_DANGLING(nodep); + // Normal edit iterator will now iterate on all of the expansion assignments + // This will potentially call this function again to resolve next level of slicing + return; + } + m_assignp = nodep; iterateChildren(nodep); - m_assignp = NULL; - } + m_assignp = NULL; + } } virtual void visit(AstInitArray* nodep) { - if (m_assignp) { - nodep->v3fatalSrc("Array initialization should have been removed earlier"); - } + if (m_assignp) { + nodep->v3fatalSrc("Array initialization should have been removed earlier"); + } } void expandBiOp(AstNodeBiop* nodep) { - if (!nodep->user1()) { - nodep->user1(true); - // If it's an unpacked array, blow it up into comparing each element - AstNodeDType* fromDtp = nodep->lhsp()->dtypep()->skipRefp(); - UINFO(9, " Bi-Eq/Neq expansion "<user1()) { + nodep->user1(true); + // If it's an unpacked array, blow it up into comparing each element + AstNodeDType* fromDtp = nodep->lhsp()->dtypep()->skipRefp(); + UINFO(9, " Bi-Eq/Neq expansion "<rangep()->elementsConst(); ++index) { - // EQ(a,b) -> LOGAND(EQ(ARRAYSEL(a,0), ARRAYSEL(b,0)), ...[1]) + AstNodeBiop* logp = NULL; + for (int index = 0; index < adtypep->rangep()->elementsConst(); ++index) { + // EQ(a,b) -> LOGAND(EQ(ARRAYSEL(a,0), ARRAYSEL(b,0)), ...[1]) AstNodeBiop* clonep = VN_CAST(nodep->cloneType (new AstArraySel(nodep->fileline(), @@ -181,54 +185,54 @@ class SliceVisitor : public AstNVisitor { nodep->rhsp()->cloneTree(false), index)), NodeBiop); - if (!logp) logp = clonep; - else { - switch (nodep->type()) { - case AstType::atEq: // FALLTHRU - case AstType::atEqCase: - logp = new AstLogAnd(nodep->fileline(), logp, clonep); - break; - case AstType::atNeq: // FALLTHRU - case AstType::atNeqCase: - logp = new AstLogOr(nodep->fileline(), logp, clonep); - break; - default: - nodep->v3fatalSrc("Unknown node type processing array slice"); - break; - } - } - } - if (!logp) nodep->v3fatalSrc("Unpacked array with empty indices range"); - nodep->replaceWith(logp); - pushDeletep(nodep); VL_DANGLING(nodep); - nodep = logp; - } + if (!logp) logp = clonep; + else { + switch (nodep->type()) { + case AstType::atEq: // FALLTHRU + case AstType::atEqCase: + logp = new AstLogAnd(nodep->fileline(), logp, clonep); + break; + case AstType::atNeq: // FALLTHRU + case AstType::atNeqCase: + logp = new AstLogOr(nodep->fileline(), logp, clonep); + break; + default: + nodep->v3fatalSrc("Unknown node type processing array slice"); + break; + } + } + } + if (!logp) nodep->v3fatalSrc("Unpacked array with empty indices range"); + nodep->replaceWith(logp); + pushDeletep(nodep); VL_DANGLING(nodep); + nodep = logp; + } iterateChildren(nodep); - } + } } virtual void visit(AstEq* nodep) { - expandBiOp(nodep); + expandBiOp(nodep); } virtual void visit(AstNeq* nodep) { - expandBiOp(nodep); + expandBiOp(nodep); } virtual void visit(AstEqCase* nodep) { - expandBiOp(nodep); + expandBiOp(nodep); } virtual void visit(AstNeqCase* nodep) { - expandBiOp(nodep); + expandBiOp(nodep); } virtual void visit(AstNode* nodep) { - // Default: Just iterate + // Default: Just iterate iterateChildren(nodep); } public: // CONSTUCTORS explicit SliceVisitor(AstNetlist* nodep) { - m_assignp = NULL; - m_assignError = false; + m_assignp = NULL; + m_assignError = false; iterate(nodep); } virtual ~SliceVisitor() {} diff --git a/src/V3Slice.h b/src/V3Slice.h index e4e72a9af..f988d7026 100644 --- a/src/V3Slice.h +++ b/src/V3Slice.h @@ -34,4 +34,4 @@ public: static void sliceAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Split.cpp b/src/V3Split.cpp index 2eca7d652..9ffe49b77 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -30,12 +30,12 @@ // // The scoreboard tracks data deps as follows: // -// ALWAYS -// ASSIGN ({var} <= {cons}) -// Record as generating var_DLY (independent of use of var), consumers -// ASSIGN ({var} = {cons} -// Record generator and consumer -// Any var that is only consumed can be ignored. +// ALWAYS +// ASSIGN ({var} <= {cons}) +// Record as generating var_DLY (independent of use of var), consumers +// ASSIGN ({var} = {cons} +// Record generator and consumer +// Any var that is only consumed can be ignored. // Then we split into separate ALWAYS blocks. // // The scoreboard includes innards of if/else nodes also. Splitting is no @@ -61,20 +61,20 @@ // handling enormous blocks with long lists of inputs and outputs. // // Furthermore, the optional reorder routine can optimize this: -// NODEASSIGN/NODEIF/WHILE -// S1: ASSIGN {v1} <= 0. // Duplicate of below -// S2: ASSIGN {v1} <= {v0} -// S3: IF (..., -// X1: ASSIGN {v2} <= {v1} -// X2: ASSIGN {v3} <= {v2} -// We'd like to swap S2 and S3, and X1 and X2. +// NODEASSIGN/NODEIF/WHILE +// S1: ASSIGN {v1} <= 0. // Duplicate of below +// S2: ASSIGN {v1} <= {v0} +// S3: IF (..., +// X1: ASSIGN {v2} <= {v1} +// X2: ASSIGN {v3} <= {v2} +// We'd like to swap S2 and S3, and X1 and X2. // // Create a graph in split assignment order. -// v3 -breakable-> v3Dly --> X2 --> v2 -brk-> v2Dly -> X1 -> v1 -// Likewise on each "upper" statement vertex -// v3Dly & v2Dly -> S3 -> v1 & v2 -// v1 -brk-> v1Dly -> S2 -> v0 -// v1Dly -> S1 -> {empty} +// v3 -breakable-> v3Dly --> X2 --> v2 -brk-> v2Dly -> X1 -> v1 +// Likewise on each "upper" statement vertex +// v3Dly & v2Dly -> S3 -> v1 & v2 +// v1 -brk-> v1Dly -> S2 -> v0 +// v1Dly -> S1 -> {empty} // Multiple assignments to the same variable must remain in order // // Also vars must not be "public" and we also scoreboard nodep->isPure() @@ -101,20 +101,20 @@ // Support classes class SplitNodeVertex : public V3GraphVertex { - AstNode* m_nodep; + AstNode* m_nodep; protected: SplitNodeVertex(V3Graph* graphp, AstNode* nodep) - : V3GraphVertex(graphp), m_nodep(nodep) {} + : V3GraphVertex(graphp), m_nodep(nodep) {} virtual ~SplitNodeVertex() {} // ACCESSORS // Do not make accessor for nodep(), It may change due to // reordering a lower block, but we don't repair it virtual string name() const { - if (m_nodep->name() == "") { + if (m_nodep->name() == "") { return cvtToHex(m_nodep); - } else { - return m_nodep->name(); - } + } else { + return m_nodep->name(); + } } virtual FileLine* fileline() const { return nodep()->fileline(); } public: @@ -133,7 +133,7 @@ public: class SplitLogicVertex : public SplitNodeVertex { public: SplitLogicVertex(V3Graph* graphp, AstNode* nodep) - : SplitNodeVertex(graphp,nodep) {} + : SplitNodeVertex(graphp, nodep) {} virtual ~SplitLogicVertex() {} virtual string dotColor() const { return "yellow"; } }; @@ -141,7 +141,7 @@ public: class SplitVarStdVertex : public SplitNodeVertex { public: SplitVarStdVertex(V3Graph* graphp, AstNode* nodep) - : SplitNodeVertex(graphp,nodep) {} + : SplitNodeVertex(graphp, nodep) {} virtual ~SplitVarStdVertex() {} virtual string dotColor() const { return "skyblue"; } }; @@ -149,7 +149,7 @@ public: class SplitVarPostVertex : public SplitNodeVertex { public: SplitVarPostVertex(V3Graph* graphp, AstNode* nodep) - : SplitNodeVertex(graphp,nodep) {} + : SplitNodeVertex(graphp, nodep) {} virtual ~SplitVarPostVertex() {} virtual string name() const { return string("POST ")+SplitNodeVertex::name(); } virtual string dotColor() const { return "CadetBlue"; } @@ -159,14 +159,14 @@ public: // Edge types class SplitEdge : public V3GraphEdge { - uint32_t m_ignoreInStep; // Step number that if set to, causes this edge to be ignored - static uint32_t s_stepNum; // Global step number + uint32_t m_ignoreInStep; // Step number that if set to, causes this edge to be ignored + static uint32_t s_stepNum; // Global step number protected: - enum { WEIGHT_NORMAL=10 }; + enum { WEIGHT_NORMAL = 10 }; SplitEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - int weight, bool cutable=CUTABLE) - : V3GraphEdge(graphp, fromp, top, weight, cutable) - ,m_ignoreInStep(0) {} + int weight, bool cutable=CUTABLE) + : V3GraphEdge(graphp, fromp, top, weight, cutable) + , m_ignoreInStep(0) {} virtual ~SplitEdge() {} public: // Iterator for graph functions @@ -175,26 +175,26 @@ public: void setIgnoreThisStep() { m_ignoreInStep = s_stepNum; } virtual bool followScoreboard() const = 0; static bool followScoreboard(const V3GraphEdge* edgep) { - const SplitEdge* oedgep = dynamic_cast(edgep); - if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); - if (oedgep->ignoreThisStep()) return false; - return oedgep->followScoreboard(); + const SplitEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); + if (oedgep->ignoreThisStep()) return false; + return oedgep->followScoreboard(); } static bool followCyclic(const V3GraphEdge* edgep) { - const SplitEdge* oedgep = dynamic_cast(edgep); - if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); + const SplitEdge* oedgep = dynamic_cast(edgep); + if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type"); return (!oedgep->ignoreThisStep()); } virtual string dotStyle() const { return ignoreThisStep() ? "dotted" : V3GraphEdge::dotStyle(); } }; -uint32_t SplitEdge::s_stepNum = 0; +uint32_t SplitEdge::s_stepNum = 0; class SplitPostEdge : public SplitEdge { public: SplitPostEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitPostEdge() {} virtual bool followScoreboard() const { return false; } virtual string dotColor() const { return "khaki"; } @@ -203,7 +203,7 @@ public: class SplitLVEdge : public SplitEdge { public: SplitLVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitLVEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "yellowGreen"; } @@ -212,7 +212,7 @@ public: class SplitRVEdge : public SplitEdge { public: SplitRVEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitRVEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "green"; } @@ -221,7 +221,7 @@ public: struct SplitScorebdEdge : public SplitEdge { public: SplitScorebdEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL) {} virtual ~SplitScorebdEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "blue"; } @@ -232,7 +232,7 @@ struct SplitStrictEdge : public SplitEdge { // The only non-cutable edge type public: SplitStrictEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top) - : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL, NOT_CUTABLE) {} + : SplitEdge(graphp, fromp, top, WEIGHT_NORMAL, NOT_CUTABLE) {} virtual ~SplitStrictEdge() {} virtual bool followScoreboard() const { return true; } virtual string dotColor() const { return "blue"; } @@ -244,26 +244,26 @@ public: class SplitReorderBaseVisitor : public AstNVisitor { private: // NODE STATE - // AstVarScope::user1p -> Var SplitNodeVertex* for usage var, 0=not set yet - // AstVarScope::user2p -> Var SplitNodeVertex* for delayed assignment var, 0=not set yet - // Ast*::user3p -> Statement SplitLogicVertex* (temporary only) - // Ast*::user4 -> Current ordering number (reorderBlock usage) - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; - AstUser4InUse m_inuser4; + // AstVarScope::user1p -> Var SplitNodeVertex* for usage var, 0=not set yet + // AstVarScope::user2p -> Var SplitNodeVertex* for delayed assignment var, 0=not set yet + // Ast*::user3p -> Statement SplitLogicVertex* (temporary only) + // Ast*::user4 -> Current ordering number (reorderBlock usage) + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; + AstUser4InUse m_inuser4; protected: // TYPES typedef std::vector VStack; // STATE - string m_noReorderWhy; // Reason we can't reorder - VStack m_stmtStackps; // Current statements being tracked - SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering - V3Graph m_graph; // Scoreboard of var usages/dependencies - bool m_inDly; // Inside ASSIGNDLY - V3Double0 m_statSplits; // Statistic tracking + string m_noReorderWhy; // Reason we can't reorder + VStack m_stmtStackps; // Current statements being tracked + SplitPliVertex* m_pliVertexp; // Element specifying PLI ordering + V3Graph m_graph; // Scoreboard of var usages/dependencies + bool m_inDly; // Inside ASSIGNDLY + V3Double0 m_statSplits; // Statistic tracking // CONSTUCTORS public: @@ -279,53 +279,53 @@ protected: VL_DEBUG_FUNC; // Declare debug() void scoreboardClear() { - //VV***** We reset user1p() and user2p on each block!!! - m_inDly = false; - m_graph.clear(); - m_stmtStackps.clear(); - m_pliVertexp = NULL; - m_noReorderWhy = ""; - AstNode::user1ClearTree(); - AstNode::user2ClearTree(); - AstNode::user3ClearTree(); - AstNode::user4ClearTree(); + //VV***** We reset user1p() and user2p on each block!!! + m_inDly = false; + m_graph.clear(); + m_stmtStackps.clear(); + m_pliVertexp = NULL; + m_noReorderWhy = ""; + AstNode::user1ClearTree(); + AstNode::user2ClearTree(); + AstNode::user3ClearTree(); + AstNode::user4ClearTree(); } private: void scoreboardPli(AstNode* nodep) { - // Order all PLI statements with other PLI statements - // This ensures $display's and such remain in proper order - // We don't prevent splitting out other non-pli statements, however. - if (!m_pliVertexp) { + // Order all PLI statements with other PLI statements + // This ensures $display's and such remain in proper order + // We don't prevent splitting out other non-pli statements, however. + if (!m_pliVertexp) { m_pliVertexp = new SplitPliVertex(&m_graph, nodep); // m_graph.clear() will delete it - } - for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { - // Both ways... - new SplitScorebdEdge(&m_graph, *it, m_pliVertexp); - new SplitScorebdEdge(&m_graph, m_pliVertexp, *it); - } + } + for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { + // Both ways... + new SplitScorebdEdge(&m_graph, *it, m_pliVertexp); + new SplitScorebdEdge(&m_graph, m_pliVertexp, *it); + } } void scoreboardPushStmt(AstNode* nodep) { - //UINFO(9," push "<user3p()) nodep->v3fatalSrc("user3p should not be used; cleared in processBlock"); - nodep->user3p(vertexp); + //UINFO(9," push "<user3p()) nodep->v3fatalSrc("user3p should not be used; cleared in processBlock"); + nodep->user3p(vertexp); } void scoreboardPopStmt() { - //UINFO(9," pop"<nextp()) { - scoreboardPushStmt(nextp); + // Iterate across current block, making the scoreboard + for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { + scoreboardPushStmt(nextp); iterate(nextp); - scoreboardPopStmt(); - } + scoreboardPopStmt(); + } } void pruneDepsOnInputs() { @@ -404,14 +404,17 @@ protected: SplitVarPostVertex* vpostp = reinterpret_cast(vscp->user2p()); // Add edges - for (VStack::iterator it = m_stmtStackps.begin(); it != m_stmtStackps.end(); ++it) { + for (VStack::iterator it = m_stmtStackps.begin(); + it != m_stmtStackps.end(); ++it) { new SplitLVEdge(&m_graph, vpostp, *it); } } else { // Nondelayed assignment if (nodep->lvalue()) { - // Non-delay; need to maintain existing ordering with all consumers of the signal + // Non-delay; need to maintain existing ordering + // with all consumers of the signal UINFO(4," VARREFLV: "<=9) { + if (debug()>=9) { m_graph.dumpDotFilePrefixed("reorderg_nodup", false); - //m_graph.dump(); cout<nextp()) { + // Mark all the logic for this step + // Vertex::m_user begin: true indicates logic for this step + m_graph.userClearVertices(); + for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = reinterpret_cast(nextp->user3p()); - vvertexp->user(true); - } + vvertexp->user(true); + } - // If a var vertex has only inputs, it's a input-only node, - // and can be ignored for coloring **this block only** + // If a var vertex has only inputs, it's a input-only node, + // and can be ignored for coloring **this block only** SplitEdge::incrementStep(); pruneDepsOnInputs(); // For reordering this single block only, mark all logic // vertexes not involved with this step as unimportant - for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) { + for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); + vertexp; vertexp=vertexp->verticesNextp()) { if (SplitLogicVertex* vvertexp = dynamic_cast(vertexp)) { if (!vvertexp->user()) { for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { SplitEdge* oedgep = dynamic_cast(edgep); oedgep->setIgnoreThisStep(); } - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { + for (V3GraphEdge* edgep = vertexp->outBeginp(); + edgep; edgep=edgep->outNextp()) { SplitEdge* oedgep = dynamic_cast(edgep); oedgep->setIgnoreThisStep(); } @@ -504,56 +509,56 @@ protected: } } - // Weak coloring to determine what needs to remain in order - // This follows all step-relevant edges excluding PostEdges, which are done later - m_graph.weaklyConnected(&SplitEdge::followScoreboard); + // Weak coloring to determine what needs to remain in order + // This follows all step-relevant edges excluding PostEdges, which are done later + m_graph.weaklyConnected(&SplitEdge::followScoreboard); - // Add hard orderings between all nodes of same color, in the order they appeared + // Add hard orderings between all nodes of same color, in the order they appeared vl_unordered_map lastOfColor; - for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { + for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = reinterpret_cast(nextp->user3p()); uint32_t color = vvertexp->color(); - if (!color) nextp->v3fatalSrc("No node color assigned"); - if (lastOfColor[color]) { - new SplitStrictEdge(&m_graph, lastOfColor[color], vvertexp); - } - lastOfColor[color] = vvertexp; - } + if (!color) nextp->v3fatalSrc("No node color assigned"); + if (lastOfColor[color]) { + new SplitStrictEdge(&m_graph, lastOfColor[color], vvertexp); + } + lastOfColor[color] = vvertexp; + } - // And a real ordering to get the statements into something reasonable - // We don't care if there's cutable violations here... - // Non-cutable violations should be impossible; as those edges are program-order + // And a real ordering to get the statements into something reasonable + // We don't care if there's cutable violations here... + // Non-cutable violations should be impossible; as those edges are program-order if (debug()>=9) m_graph.dumpDotFilePrefixed(string("splitg_preo"), false); - m_graph.acyclic(&SplitEdge::followCyclic); - m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive + m_graph.acyclic(&SplitEdge::followCyclic); + m_graph.rank(&SplitEdge::followCyclic); // Or order(), but that's more expensive if (debug()>=9) m_graph.dumpDotFilePrefixed(string("splitg_opt"), false); } void reorderBlock(AstNode* nodep) { - // Reorder statements in the completed graph + // Reorder statements in the completed graph - // Map the rank numbers into nodes they associate with + // Map the rank numbers into nodes they associate with typedef std::multimap RankNodeMap; RankNodeMap rankMap; - int currOrder = 0; // Existing sequence number of assignment - for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { + int currOrder = 0; // Existing sequence number of assignment + for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = reinterpret_cast(nextp->user3p()); - rankMap.insert(make_pair(vvertexp->rank(), nextp)); - nextp->user4(++currOrder); // Record current ordering - } + rankMap.insert(make_pair(vvertexp->rank(), nextp)); + nextp->user4(++currOrder); // Record current ordering + } - // Is the current ordering OK? - bool leaveAlone=true; - int newOrder = 0; // New sequence number of assignment + // Is the current ordering OK? + bool leaveAlone = true; + int newOrder = 0; // New sequence number of assignment for (RankNodeMap::const_iterator it = rankMap.begin(); it != rankMap.end(); ++it) { AstNode* nextp = it->second; - if (++newOrder != nextp->user4()) leaveAlone=false; + if (++newOrder != nextp->user4()) leaveAlone = false; } - if (leaveAlone) { - UINFO(6," No changes\n"); - } else { - AstNRelinker replaceHandle; // Where to add the list + if (leaveAlone) { + UINFO(6," No changes\n"); + } else { + AstNRelinker replaceHandle; // Where to add the list AstNode* newListp = NULL; for (RankNodeMap::const_iterator it = rankMap.begin(); it != rankMap.end(); ++it) { AstNode* nextp = it->second; @@ -564,52 +569,54 @@ protected: else newListp = nextp; } replaceHandle.relink(newListp); - } // leaveAlone + } } void processBlock(AstNode* nodep) { - if (!nodep) return; // Empty lists are ignorable - // Pass the first node in a list of block items, we'll process them - // Check there's >= 2 sub statements, else nothing to analyze - // Save recursion state - AstNode* firstp = nodep; // We may reorder, and nodep is no longer first. - void* oldBlockUser3 = nodep->user3p(); // May be overloaded in below loop, save it - nodep->user3p(NULL); - if (!nodep->firstAbovep()) nodep->v3fatalSrc("Node passed is in next list; should have processed all list at once"); - // Process it - if (!nodep->nextp()) { - // Just one, so can't reorder. Just look for more blocks/statements. + if (!nodep) return; // Empty lists are ignorable + // Pass the first node in a list of block items, we'll process them + // Check there's >= 2 sub statements, else nothing to analyze + // Save recursion state + AstNode* firstp = nodep; // We may reorder, and nodep is no longer first. + void* oldBlockUser3 = nodep->user3p(); // May be overloaded in below loop, save it + nodep->user3p(NULL); + if (!nodep->firstAbovep()) { + nodep->v3fatalSrc("Node passed is in next list; should have processed all list at once"); + } + // Process it + if (!nodep->nextp()) { + // Just one, so can't reorder. Just look for more blocks/statements. iterate(nodep); - } else { - UINFO(9," processBlock "<backp()->nextp()==firstp) firstp = firstp->backp(); - for (AstNode* nextp=firstp; nextp; nextp=nextp->nextp()) { + for (AstNode* nextp=firstp; nextp; nextp=nextp->nextp()) { SplitLogicVertex* vvertexp = reinterpret_cast(nextp->user3p()); - vvertexp->unlinkDelete(&m_graph); - } - } - } - // Again, nodep may no longer be first. - firstp->user3p(oldBlockUser3); + vvertexp->unlinkDelete(&m_graph); + } + } + } + // Again, nodep may no longer be first. + firstp->user3p(oldBlockUser3); } virtual void visit(AstAlways* nodep) { - UINFO(4," ALW "<=9) nodep->dumpTree(cout," alwIn:: "); - scoreboardClear(); - processBlock(nodep->bodysp()); - if (debug()>=9) nodep->dumpTree(cout," alwOut: "); + UINFO(4," ALW "<=9) nodep->dumpTree(cout, " alwIn:: "); + scoreboardClear(); + processBlock(nodep->bodysp()); + if (debug()>=9) nodep->dumpTree(cout, " alwOut: "); } virtual void visit(AstNodeIf* nodep) { diff --git a/src/V3Split.h b/src/V3Split.h index 509a24bac..a4f041ce5 100644 --- a/src/V3Split.h +++ b/src/V3Split.h @@ -35,4 +35,4 @@ public: static void splitAlwaysAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3SplitAs.cpp b/src/V3SplitAs.cpp index e6bf22917..5551d28d8 100644 --- a/src/V3SplitAs.cpp +++ b/src/V3SplitAs.cpp @@ -19,9 +19,9 @@ //************************************************************************* // V3SplitAs's Transformations: // -// Search each ALWAYS for a VARREF lvalue with a /*isolate_assignments*/ attribute -// If found, color statements with both, assignment to that varref, or other assignments. -// Replicate the Always, and remove mis-colored duplicate code. +// Search each ALWAYS for a VARREF lvalue with a /*isolate_assignments*/ attribute +// If found, color statements with both, assignment to that varref, or other assignments. +// Replicate the Always, and remove mis-colored duplicate code. // //************************************************************************* @@ -52,14 +52,14 @@ public: class SplitAsFindVisitor : public SplitAsBaseVisitor { private: // STATE - AstVarScope* m_splitVscp; // Variable we want to split + AstVarScope* m_splitVscp; // Variable we want to split // METHODS virtual void visit(AstVarRef* nodep) { - if (nodep->lvalue() && !m_splitVscp - && nodep->varp()->attrIsolateAssign()) { - m_splitVscp = nodep->varScopep(); - } + if (nodep->lvalue() && !m_splitVscp + && nodep->varp()->attrIsolateAssign()) { + m_splitVscp = nodep->varScopep(); + } } virtual void visit(AstNode* nodep) { iterateChildren(nodep); @@ -67,7 +67,7 @@ private: public: // CONSTUCTORS explicit SplitAsFindVisitor(AstAlways* nodep) { - m_splitVscp = NULL; + m_splitVscp = NULL; iterate(nodep); } virtual ~SplitAsFindVisitor() {} @@ -81,45 +81,45 @@ public: class SplitAsCleanVisitor : public SplitAsBaseVisitor { private: // STATE - AstVarScope* m_splitVscp; // Variable we want to split - bool m_modeMatch; // Remove matching Vscp, else non-matching - bool m_keepStmt; // Current Statement must be preserved - bool m_matches; // Statement below has matching lvalue reference + AstVarScope* m_splitVscp; // Variable we want to split + bool m_modeMatch; // Remove matching Vscp, else non-matching + bool m_keepStmt; // Current Statement must be preserved + bool m_matches; // Statement below has matching lvalue reference // METHODS virtual void visit(AstVarRef* nodep) { - if (nodep->lvalue()) { - if (nodep->varScopep()==m_splitVscp) { - UINFO(6," CL VAR "<lvalue()) { + if (nodep->varScopep()==m_splitVscp) { + UINFO(6," CL VAR "<isStatement()) { iterateChildren(nodep); return; } - UINFO(6," CL STMT "<unlinkFrBack(); pushDeletep(nodep); - } - } - // If something below matches, the upper statement remains too. - m_keepStmt = oldKeep || m_keepStmt; - UINFO(9," upKeep="<unlinkFrBack(); pushDeletep(nodep); + } + } + // If something below matches, the upper statement remains too. + m_keepStmt = oldKeep || m_keepStmt; + UINFO(9," upKeep="< bool. True if already processed - AstUser1InUse m_inuser1; + // AstAlways::user() -> bool. True if already processed + AstUser1InUse m_inuser1; // STATE - V3Double0 m_statSplits; // Statistic tracking - AstVarScope* m_splitVscp; // Variable we want to split + V3Double0 m_statSplits; // Statistic tracking + AstVarScope* m_splitVscp; // Variable we want to split // METHODS void splitAlways(AstAlways* nodep) { - UINFO(3,"Split "<=9) nodep->dumpTree(cout,"-in : "); - // Duplicate it and link in - AstAlways* newp = nodep->cloneTree(false); - newp->user1(true); // So we don't clone it again - nodep->addNextHere(newp); - { // Delete stuff we don't want in old - SplitAsCleanVisitor visitor (nodep, m_splitVscp, false); - if (debug()>=9) nodep->dumpTree(cout,"-out0: "); - } - { // Delete stuff we don't want in new - SplitAsCleanVisitor visitor (newp, m_splitVscp, true); - if (debug()>=9) newp->dumpTree(cout,"-out1: "); - } + UINFO(3,"Split "<=9) nodep->dumpTree(cout, "-in : "); + // Duplicate it and link in + AstAlways* newp = nodep->cloneTree(false); + newp->user1(true); // So we don't clone it again + nodep->addNextHere(newp); + { // Delete stuff we don't want in old + SplitAsCleanVisitor visitor (nodep, m_splitVscp, false); + if (debug()>=9) nodep->dumpTree(cout, "-out0: "); + } + { // Delete stuff we don't want in new + SplitAsCleanVisitor visitor (newp, m_splitVscp, true); + if (debug()>=9) newp->dumpTree(cout, "-out1: "); + } } virtual void visit(AstAlways* nodep) { - // Are there any lvalue references below this? - // There could be more than one. So, we process the first one found first. - AstVarScope* lastSplitVscp = NULL; - while (!nodep->user1()) { - // Find any splittable variables - SplitAsFindVisitor visitor (nodep); - m_splitVscp = visitor.splitVscp(); - if (m_splitVscp && m_splitVscp == lastSplitVscp) { - // We did this last time! Something's stuck! - nodep->v3fatalSrc("Infinite loop in isolate_assignments removal for: "<prettyName()) - m_splitVscp = NULL; - } - lastSplitVscp = m_splitVscp; - // Now isolate the always - if (m_splitVscp) { - splitAlways(nodep); - ++m_statSplits; - } else { - nodep->user1(true); - } - } + // Are there any lvalue references below this? + // There could be more than one. So, we process the first one found first. + AstVarScope* lastSplitVscp = NULL; + while (!nodep->user1()) { + // Find any splittable variables + SplitAsFindVisitor visitor (nodep); + m_splitVscp = visitor.splitVscp(); + if (m_splitVscp && m_splitVscp == lastSplitVscp) { + // We did this last time! Something's stuck! + nodep->v3fatalSrc("Infinite loop in isolate_assignments removal for: " + <prettyName()) + m_splitVscp = NULL; + } + lastSplitVscp = m_splitVscp; + // Now isolate the always + if (m_splitVscp) { + splitAlways(nodep); + ++m_statSplits; + } else { + nodep->user1(true); + } + } } // Speedup; no always under math @@ -202,12 +203,12 @@ private: public: // CONSTUCTORS explicit SplitAsVisitor(AstNetlist* nodep) { - m_splitVscp = NULL; - AstNode::user1ClearTree(); // user1p() used on entire tree + m_splitVscp = NULL; + AstNode::user1ClearTree(); // user1p() used on entire tree iterate(nodep); } virtual ~SplitAsVisitor() { - V3Stats::addStat("Optimizations, isolate_assignments blocks", m_statSplits); + V3Stats::addStat("Optimizations, isolate_assignments blocks", m_statSplits); } }; diff --git a/src/V3SplitAs.h b/src/V3SplitAs.h index de4b9f1bd..ec8e2a4b3 100644 --- a/src/V3SplitAs.h +++ b/src/V3SplitAs.h @@ -34,4 +34,4 @@ public: static void splitAsAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index 8e458cceb..e8d8f08cb 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -43,145 +43,145 @@ private: typedef std::map NameMap; // Number of times a name appears // STATE - string m_stage; // Name of the stage we are scanning + string m_stage; // Name of the stage we are scanning /// m_fast = true: Counting only critical branch of fastpath /// m_fast = false: Counting every node, ignoring structure of program - bool m_fast; + bool m_fast; - AstCFunc* m_cfuncp; // Current CFUNC - V3Double0 m_statInstrLong; // Instruction count - bool m_counting; // Currently counting - double m_instrs; // Current instr count (for determining branch direction) - bool m_tracingCall; // Iterating into a CCall to a CFunc + AstCFunc* m_cfuncp; // Current CFUNC + V3Double0 m_statInstrLong; // Instruction count + bool m_counting; // Currently counting + double m_instrs; // Current instr count (for determining branch direction) + bool m_tracingCall; // Iterating into a CCall to a CFunc std::vector m_statTypeCount; // Nodes of given type - V3Double0 m_statAbove[AstType::_ENUM_END][AstType::_ENUM_END]; // Nodes of given type - V3Double0 m_statPred[AstBranchPred::_ENUM_END]; // Nodes of given type - V3Double0 m_statInstr; // Instruction count - V3Double0 m_statInstrFast; // Instruction count, non-slow() eval functions only + V3Double0 m_statAbove[AstType::_ENUM_END][AstType::_ENUM_END]; // Nodes of given type + V3Double0 m_statPred[AstBranchPred::_ENUM_END]; // Nodes of given type + V3Double0 m_statInstr; // Instruction count + V3Double0 m_statInstrFast; // Instruction count, non-slow() eval functions only std::vector m_statVarWidths; // Variables of given width std::vector m_statVarWidthNames; // Var names of given width - V3Double0 m_statVarArray; // Statistic tracking - V3Double0 m_statVarBytes; // Statistic tracking - V3Double0 m_statVarClock; // Statistic tracking - V3Double0 m_statVarScpBytes; // Statistic tracking + V3Double0 m_statVarArray; // Statistic tracking + V3Double0 m_statVarBytes; // Statistic tracking + V3Double0 m_statVarClock; // Statistic tracking + V3Double0 m_statVarScpBytes; // Statistic tracking // METHODS VL_DEBUG_FUNC; // Declare debug() void allNodes(AstNode* nodep) { - m_instrs += nodep->instrCount(); - if (m_counting) { - ++m_statTypeCount[nodep->type()]; - if (nodep->firstAbovep()) { // Grab only those above, not those "back" - ++m_statAbove[nodep->firstAbovep()->type()][nodep->type()]; - } - m_statInstr += nodep->instrCount(); - if (m_cfuncp && !m_cfuncp->slow()) m_statInstrFast += nodep->instrCount(); - } + m_instrs += nodep->instrCount(); + if (m_counting) { + ++m_statTypeCount[nodep->type()]; + if (nodep->firstAbovep()) { // Grab only those above, not those "back" + ++m_statAbove[nodep->firstAbovep()->type()][nodep->type()]; + } + m_statInstr += nodep->instrCount(); + if (m_cfuncp && !m_cfuncp->slow()) m_statInstrFast += nodep->instrCount(); + } } // VISITORS virtual void visit(AstNodeModule* nodep) { - allNodes(nodep); - if (!m_fast) { + allNodes(nodep); + if (!m_fast) { // Count all CFuncs below this module iterateChildrenConst(nodep); - } + } // Else we recursively trace fast CFuncs from the top _eval // func, see visit(AstNetlist*) } virtual void visit(AstVar* nodep) { - allNodes(nodep); + allNodes(nodep); iterateChildrenConst(nodep); - if (m_counting && nodep->dtypep()) { - if (nodep->isUsedClock()) ++m_statVarClock; + if (m_counting && nodep->dtypep()) { + if (nodep->isUsedClock()) ++m_statVarClock; if (VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType)) ++m_statVarArray; - else m_statVarBytes += nodep->dtypeSkipRefp()->widthTotalBytes(); - if (int(m_statVarWidths.size()) <= nodep->width()) { - m_statVarWidths.resize(nodep->width()+5); - if (v3Global.opt.statsVars()) m_statVarWidthNames.resize(nodep->width()+5); - } - ++ m_statVarWidths.at(nodep->width()); - string pn = nodep->prettyName(); - if (v3Global.opt.statsVars()) { - NameMap& nameMapr = m_statVarWidthNames.at(nodep->width()); - if (nameMapr.find(pn) != nameMapr.end()) { - nameMapr[pn]++; - } else { - nameMapr[pn]=1; - } - } - } + else m_statVarBytes += nodep->dtypeSkipRefp()->widthTotalBytes(); + if (int(m_statVarWidths.size()) <= nodep->width()) { + m_statVarWidths.resize(nodep->width()+5); + if (v3Global.opt.statsVars()) m_statVarWidthNames.resize(nodep->width()+5); + } + ++ m_statVarWidths.at(nodep->width()); + string pn = nodep->prettyName(); + if (v3Global.opt.statsVars()) { + NameMap& nameMapr = m_statVarWidthNames.at(nodep->width()); + if (nameMapr.find(pn) != nameMapr.end()) { + nameMapr[pn]++; + } else { + nameMapr[pn] = 1; + } + } + } } virtual void visit(AstVarScope* nodep) { - allNodes(nodep); + allNodes(nodep); iterateChildrenConst(nodep); - if (m_counting) { + if (m_counting) { if (VN_IS(nodep->varp()->dtypeSkipRefp(), BasicDType)) { - m_statVarScpBytes += nodep->varp()->dtypeSkipRefp()->widthTotalBytes(); - } - } + m_statVarScpBytes += nodep->varp()->dtypeSkipRefp()->widthTotalBytes(); + } + } } virtual void visit(AstNodeIf* nodep) { - UINFO(4," IF i="<condp()); - // Track prediction - if (m_counting) { - ++m_statPred[nodep->branchPred()]; - } - if (!m_fast) { - // Count everything + // Track prediction + if (m_counting) { + ++m_statPred[nodep->branchPred()]; + } + if (!m_fast) { + // Count everything iterateChildrenConst(nodep); - } else { - // See which path we want to take - // Need to do even if !m_counting because maybe determining upstream if/else - double ifInstrs = 0.0; - double elseInstrs = 0.0; - if (nodep->branchPred() != AstBranchPred::BP_UNLIKELY) { // Check if - double prevInstr = m_instrs; - bool prevCounting = m_counting; - { - m_counting = false; - m_instrs = 0.0; + } else { + // See which path we want to take + // Need to do even if !m_counting because maybe determining upstream if/else + double ifInstrs = 0.0; + double elseInstrs = 0.0; + if (nodep->branchPred() != AstBranchPred::BP_UNLIKELY) { // Check if + double prevInstr = m_instrs; + bool prevCounting = m_counting; + { + m_counting = false; + m_instrs = 0.0; iterateAndNextConstNull(nodep->ifsp()); - ifInstrs = m_instrs; - } - m_instrs = prevInstr; - m_counting = prevCounting; - } - if (nodep->branchPred() != AstBranchPred::BP_LIKELY) { // Check else - double prevInstr = m_instrs; - bool prevCounting = m_counting; - { - m_counting = false; - m_instrs = 0.0; + ifInstrs = m_instrs; + } + m_instrs = prevInstr; + m_counting = prevCounting; + } + if (nodep->branchPred() != AstBranchPred::BP_LIKELY) { // Check else + double prevInstr = m_instrs; + bool prevCounting = m_counting; + { + m_counting = false; + m_instrs = 0.0; iterateAndNextConstNull(nodep->elsesp()); - elseInstrs = m_instrs; - } - m_instrs = prevInstr; - m_counting = prevCounting; - } - // Now collect the stats - if (m_counting) { - if (ifInstrs >= elseInstrs) { + elseInstrs = m_instrs; + } + m_instrs = prevInstr; + m_counting = prevCounting; + } + // Now collect the stats + if (m_counting) { + if (ifInstrs >= elseInstrs) { iterateAndNextConstNull(nodep->ifsp()); - } else { + } else { iterateAndNextConstNull(nodep->elsesp()); - } - } - } + } + } + } } // While's we assume evaluate once. //virtual void visit(AstWhile* nodep) { virtual void visit(AstCCall* nodep) { - allNodes(nodep); + allNodes(nodep); iterateChildrenConst(nodep); if (m_fast && !nodep->funcp()->entryPoint()) { - // Enter the function and trace it + // Enter the function and trace it m_tracingCall = true; iterate(nodep->funcp()); } @@ -191,13 +191,13 @@ private: if (!m_tracingCall && !nodep->entryPoint()) return; m_tracingCall = false; } - m_cfuncp = nodep; - allNodes(nodep); + m_cfuncp = nodep; + allNodes(nodep); iterateChildrenConst(nodep); - m_cfuncp = NULL; + m_cfuncp = NULL; } virtual void visit(AstNode* nodep) { - allNodes(nodep); + allNodes(nodep); iterateChildrenConst(nodep); } virtual void visit(AstNetlist* nodep) { @@ -213,65 +213,69 @@ private: public: // CONSTRUCTORS StatsVisitor(AstNetlist* nodep, const string& stage, bool fast) - : m_stage(stage), m_fast(fast) { - UINFO(9,"Starting stats, fast="<first; - V3Stats::addStat(m_stage, os.str(), it->second); - } - } else { + // Done. Publish statistics + V3Stats::addStat(m_stage, "Instruction count, TOTAL", m_statInstr); + V3Stats::addStat(m_stage, "Instruction count, fast critical", m_statInstrFast); + // Vars + V3Stats::addStat(m_stage, "Vars, unpacked arrayed", m_statVarArray); + V3Stats::addStat(m_stage, "Vars, clock attribute", m_statVarClock); + V3Stats::addStat(m_stage, "Var space, non-arrays, bytes", m_statVarBytes); + if (m_statVarScpBytes!=0.0) { + V3Stats::addStat(m_stage, "Var space, scoped, bytes", m_statVarScpBytes); + } + for (unsigned i=0; ifirst; + V3Stats::addStat(m_stage, os.str(), it->second); + } + } else { std::ostringstream os; os<<"Vars, width "<count(); - otherp->m_printit = false; + m_count += otherp->count(); + otherp->m_printit = false; } // CONSTRUCTORS - V3Statistic(const string& stage, const string& name, double count, bool sumit=false, bool perf=false) - : m_name(name), m_count(count), m_stage(stage), m_sumit(sumit), m_perf(perf) - , m_printit(true) {} + V3Statistic(const string& stage, const string& name, + double count, bool sumit=false, bool perf=false) + : m_name(name), m_count(count), m_stage(stage), m_sumit(sumit), m_perf(perf) + , m_printit(true) {} virtual ~V3Statistic() {} }; @@ -86,13 +87,13 @@ class V3Stats { public: static void addStat(const V3Statistic&); static void addStat(const string& stage, const string& name, double count) { - addStat(V3Statistic(stage,name,count)); } + addStat(V3Statistic(stage, name, count)); } static void addStat(const string& name, double count) { - addStat(V3Statistic("*",name,count)); } + addStat(V3Statistic("*", name, count)); } static void addStatSum(const string& name, double count) { - addStat(V3Statistic("*",name,count,true)); } + addStat(V3Statistic("*", name, count, true)); } static void addStatPerf(const string& name, double count) { - addStat(V3Statistic("*",name,count,true,true)); } + addStat(V3Statistic("*", name, count, true, true)); } /// Called each stage static void statsStage(const string& name); /// Called by the top level to collect statistics @@ -103,4 +104,4 @@ public: }; -#endif // Guard +#endif // Guard diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 70e62445b..127fabe35 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -40,167 +40,167 @@ class StatsReport { typedef std::vector StatColl; // STATE - std::ofstream& os; ///< Output stream - static StatColl s_allStats; ///< All statistics + std::ofstream& os; ///< Output stream + static StatColl s_allStats; ///< All statistics void header() { - os<<"Verilator Statistics Report\n"; - os< ByName; - ByName byName; - // * is always first - for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { - V3Statistic* repp = &(*it); - byName.insert(make_pair(repp->name(), repp)); - } + ByName byName; + // * is always first + for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { + V3Statistic* repp = &(*it); + byName.insert(make_pair(repp->name(), repp)); + } - // Process duplicates - V3Statistic* lastp = NULL; - for (ByName::iterator it = byName.begin(); it!=byName.end(); ++it) { - V3Statistic* repp = it->second; - if (lastp && lastp->sumit() && lastp->printit() - && lastp->name() == repp->name() && lastp->stage() == repp->stage()) { - repp->combineWith(lastp); - } - lastp = repp; - } + // Process duplicates + V3Statistic* lastp = NULL; + for (ByName::iterator it = byName.begin(); it!=byName.end(); ++it) { + V3Statistic* repp = it->second; + if (lastp && lastp->sumit() && lastp->printit() + && lastp->name() == repp->name() && lastp->stage() == repp->stage()) { + repp->combineWith(lastp); + } + lastp = repp; + } } void stars() { - // Find all stages - size_t maxWidth = 0; + // Find all stages + size_t maxWidth = 0; typedef std::multimap ByName; - ByName byName; - // * is always first - for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { - const V3Statistic* repp = &(*it); - if (repp->stage() == "*" && repp->printit()) { - if (maxWidth < repp->name().length()) maxWidth = repp->name().length(); - byName.insert(make_pair(repp->name(), repp)); - } - } + ByName byName; + // * is always first + for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { + const V3Statistic* repp = &(*it); + if (repp->stage() == "*" && repp->printit()) { + if (maxWidth < repp->name().length()) maxWidth = repp->name().length(); + byName.insert(make_pair(repp->name(), repp)); + } + } - // Print organized by stage - os<<"Global Statistics:\n"; - os<second; - if (repp->perf()) continue; + // Print organized by stage + os<<"Global Statistics:\n"; + os<second; + if (repp->perf()) continue; os<<" "<name(); - repp->dump(os); - os<dump(os); + os<second; - if (!repp->perf()) continue; + // Print organized by stage + os<<"Performance Statistics:\n"; + os<second; + if (!repp->perf()) continue; os<<" "<name(); - repp->dump(os); - os<dump(os); + os< Stages; - Stages stages; - vl_unordered_map stageInt; + Stages stages; + vl_unordered_map stageInt; typedef std::multimap ByName; - ByName byName; - // * is always first - for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { - const V3Statistic* repp = &(*it); - if (repp->stage() != "*" && repp->printit()) { - if (maxWidth < repp->name().length()) maxWidth = repp->name().length(); - if (stageInt.find(repp->stage()) == stageInt.end()) { - stageInt.insert(make_pair(repp->stage(), stage++)); - stages.push_back(repp->stage()); - } - byName.insert(make_pair(repp->name(), repp)); - } - } + ByName byName; + // * is always first + for (StatColl::iterator it = s_allStats.begin(); it!=s_allStats.end(); ++it) { + const V3Statistic* repp = &(*it); + if (repp->stage() != "*" && repp->printit()) { + if (maxWidth < repp->name().length()) maxWidth = repp->name().length(); + if (stageInt.find(repp->stage()) == stageInt.end()) { + stageInt.insert(make_pair(repp->stage(), stage++)); + stages.push_back(repp->stage()); + } + byName.insert(make_pair(repp->name(), repp)); + } + } - // Header + // Header os<<" Stat "<second; - if (lastName != repp->name()) { - lastName = repp->name(); - { - string commaName = lastName; - string::size_type pos; + // Print organized by stage + string lastName = "__NONE__"; + string lastCommaName = "__NONE__"; + unsigned col = 0; + for (ByName::iterator it = byName.begin(); it!=byName.end(); ++it) { + const V3Statistic* repp = it->second; + if (lastName != repp->name()) { + lastName = repp->name(); + { + string commaName = lastName; + string::size_type pos; if ((pos = commaName.find(',')) != string::npos) { - commaName.erase(pos); - } - if (lastCommaName != commaName) { - lastCommaName = commaName; - os<name(); - } - while (colstage()) { + } + while (colstage()) { os<dump(os); - col++; - } - os<dump(os); + col++; + } + os<(datap); } else { - // If there are large inserts it would be more efficient to avoid this copy - // by copying bytes in the loop below from either m_remainder or the data - // as appropriate. + // If there are large inserts it would be more efficient to avoid this copy + // by copying bytes in the loop below from either m_remainder or the data + // as appropriate. tempData = m_remainder + string(static_cast(datap), length); chunkLen = tempData.length(); chunkp = reinterpret_cast(tempData.data()); } // See wikipedia SHA-1 algorithm summary - uint32_t w[80]; // Round buffer, [0..15] are input data, rest used by rounds - int posBegin = 0; // Position in buffer for start of this block - int posEnd = 0; // Position in buffer for end of this block + uint32_t w[80]; // Round buffer, [0..15] are input data, rest used by rounds + int posBegin = 0; // Position in buffer for start of this block + int posEnd = 0; // Position in buffer for end of this block // Process complete 64-byte blocks while (posBegin <= chunkLen - 64) { - posEnd = posBegin + 64; - // 64 byte round input data, being careful to swap on big, keep on little - for (int roundByte = 0; posBegin < posEnd; posBegin += 4) { + posEnd = posBegin + 64; + // 64 byte round input data, being careful to swap on big, keep on little + for (int roundByte = 0; posBegin < posEnd; posBegin += 4) { w[roundByte++] = (static_cast(chunkp[posBegin + 3]) | (static_cast(chunkp[posBegin + 2]) << 8) | (static_cast(chunkp[posBegin + 1]) << 16) | (static_cast(chunkp[posBegin]) << 24)); - } - sha1Block(m_inthash, w); + } + sha1Block(m_inthash, w); } m_remainder = string(reinterpret_cast(chunkp+posBegin), chunkLen-posEnd); @@ -187,27 +187,27 @@ void VHashSha1::insert(const void* datap, size_t length) { void VHashSha1::finalize() { if (!m_final) { - // Make sure no 64 byte blocks left - insert(""); - m_final = true; + // Make sure no 64 byte blocks left + insert(""); + m_final = true; - // Process final possibly non-complete 64-byte block - uint32_t w[80]; // Round buffer, [0..15] are input data, rest used by rounds - for (int i=0; i<16; ++i) w[i] = 0; - size_t blockPos = 0; - for (; blockPos < m_remainder.length(); ++blockPos) { + // Process final possibly non-complete 64-byte block + uint32_t w[80]; // Round buffer, [0..15] are input data, rest used by rounds + for (int i=0; i<16; ++i) w[i] = 0; + size_t blockPos = 0; + for (; blockPos < m_remainder.length(); ++blockPos) { w[blockPos >> 2] |= ((static_cast(m_remainder[blockPos])) << ((3 - (blockPos & 3)) << 3)); - } - w[blockPos >> 2] |= 0x80 << ((3 - (blockPos & 3)) << 3); - if (m_remainder.length() >= 56) { - sha1Block(m_inthash, w); - for (int i=0; i<16; ++i) w[i] = 0; - } - w[15] = m_totLength << 3; - sha1Block(m_inthash, w); + } + w[blockPos >> 2] |= 0x80 << ((3 - (blockPos & 3)) << 3); + if (m_remainder.length() >= 56) { + sha1Block(m_inthash, w); + for (int i=0; i<16; ++i) w[i] = 0; + } + w[15] = m_totLength << 3; + sha1Block(m_inthash, w); - m_remainder.clear(); + m_remainder.clear(); } } @@ -235,8 +235,8 @@ string VHashSha1::digestHex() { const string& binhash = digestBinary(); string out; out.reserve(40); for (size_t byte=0; byte<20; ++byte) { - out += digits[ (binhash[byte]>>4) & 0xf ]; - out += digits[ (binhash[byte]>>0) & 0xf ]; + out += digits[ (binhash[byte]>>4) & 0xf ]; + out += digits[ (binhash[byte]>>0) & 0xf ]; } return out; } @@ -246,33 +246,34 @@ string VHashSha1::digestSymbol() { // has + and / for last two digits, but need C symbol, and we also // avoid conflicts with use of _, so use "AB" at the end. // Thus this function is non-reversable. - static const char digits[64+1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"; + static const char digits[64+1] + = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789AB"; const string& binhash = digestBinary(); string out; out.reserve(28); int pos = 0; for (; pos < (160/8) - 2; pos += 3) { - out += digits[((binhash[pos] >> 2) & 0x3f)]; - out += digits[((binhash[pos] & 0x3) << 4) + out += digits[((binhash[pos] >> 2) & 0x3f)]; + out += digits[((binhash[pos] & 0x3) << 4) | (static_cast(binhash[pos + 1] & 0xf0) >> 4)]; - out += digits[((binhash[pos + 1] & 0xf) << 2) + out += digits[((binhash[pos + 1] & 0xf) << 2) | (static_cast(binhash[pos + 2] & 0xc0) >> 6)]; - out += digits[((binhash[pos + 2] & 0x3f))]; + out += digits[((binhash[pos + 2] & 0x3f))]; } - if (0) { // Not needed for 160 bit hash - out += digits[((binhash[pos] >> 2) & 0x3f)]; - out += digits[((binhash[pos] & 0x3) << 4)]; + if (0) { // Not needed for 160 bit hash + out += digits[((binhash[pos] >> 2) & 0x3f)]; + out += digits[((binhash[pos] & 0x3) << 4)]; } else { - out += digits[((binhash[pos] >> 2) & 0x3f)]; - out += digits[((binhash[pos] & 0x3) << 4) + out += digits[((binhash[pos] >> 2) & 0x3f)]; + out += digits[((binhash[pos] & 0x3) << 4) | (static_cast(binhash[pos + 1] & 0xf0) >> 4)]; - out += digits[((binhash[pos + 1] & 0xf) << 2)]; + out += digits[((binhash[pos + 1] & 0xf) << 2)]; } return out; } void VHashSha1::selfTestOne(const string& data, const string& data2, - const string& exp, const string& exp64) { + const string& exp, const string& exp64) { VHashSha1 digest (data); if (data2!="") digest.insert(data2); if (digest.digestHex() != exp) { @@ -289,23 +290,23 @@ void VHashSha1::selfTestOne(const string& data, const string& data2, void VHashSha1::selfTest() { selfTestOne("", "", - "da39a3ee5e6b4b0d3255bfef95601890afd80709", - "2jmj7l5rSw0yVbBvlWAYkKBYBwk"); + "da39a3ee5e6b4b0d3255bfef95601890afd80709", + "2jmj7l5rSw0yVbBvlWAYkKBYBwk"); selfTestOne("a", "", - "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", - "hvfkNBqlpBzhXR3cuerq6jd2Z7g"); + "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", + "hvfkNBqlpBzhXR3cuerq6jd2Z7g"); selfTestOne("The quick brown fox jumps over the lazy dog", "", - "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - "L9ThxnotKPzthJ7hu3bnORuT6xI"); - selfTestOne("The quick brown fox jumps over the lazy"," dog", - "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", - "L9ThxnotKPzthJ7hu3bnORuT6xI"); + "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + "L9ThxnotKPzthJ7hu3bnORuT6xI"); + selfTestOne("The quick brown fox jumps over the lazy", " dog", + "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", + "L9ThxnotKPzthJ7hu3bnORuT6xI"); selfTestOne("Test using larger than block-size key and larger than one block-size data", "", - "9026e8faed6ef4ec5ae3ff049020d7f0af7abbbf", - "kCboAu1u9Oxa4B8EkCDX8K96u78"); + "9026e8faed6ef4ec5ae3ff049020d7f0af7abbbf", + "kCboAu1u9Oxa4B8EkCDX8K96u78"); selfTestOne("Test using", " larger than block-size key and larger than one block-size data", - "9026e8faed6ef4ec5ae3ff049020d7f0af7abbbf", - "kCboAu1u9Oxa4B8EkCDX8K96u78"); + "9026e8faed6ef4ec5ae3ff049020d7f0af7abbbf", + "kCboAu1u9Oxa4B8EkCDX8K96u78"); } //###################################################################### @@ -313,18 +314,18 @@ void VHashSha1::selfTest() { string VName::hashedName() { if (m_name=="") return ""; - if (m_hashed!="") return m_hashed; // Memoized + if (m_hashed!="") return m_hashed; // Memoized if (s_maxLength==0 || m_name.length() < s_maxLength) { - m_hashed = m_name; - return m_hashed; + m_hashed = m_name; + return m_hashed; } else { - VHashSha1 hash(m_name); - string suffix = "__Vhsh"+hash.digestSymbol(); - if (s_minLength < s_maxLength) { - m_hashed = m_name.substr(0,s_minLength) + suffix; - } else { - m_hashed = suffix; - } - return m_hashed; + VHashSha1 hash(m_name); + string suffix = "__Vhsh"+hash.digestSymbol(); + if (s_minLength < s_maxLength) { + m_hashed = m_name.substr(0, s_minLength) + suffix; + } else { + m_hashed = suffix; + } + return m_hashed; } } diff --git a/src/V3String.h b/src/V3String.h index d49d6a950..a2573c45a 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -53,10 +53,10 @@ class VHashSha1 { // Or improve to store 0-63 bytes of data between calls to input(). // MEMBERS - uint32_t m_inthash[5]; // Intermediate hash, in host order - string m_remainder; // Unhashed data - bool m_final; // Finalized - size_t m_totLength; // Total all-chunk length as needed by output digest + uint32_t m_inthash[5]; // Intermediate hash, in host order + string m_remainder; // Unhashed data + bool m_final; // Finalized + size_t m_totLength; // Total all-chunk length as needed by output digest public: // CONSTRUCTORS VHashSha1() { init(); } @@ -64,11 +64,11 @@ public: ~VHashSha1() {} // METHODS - string digestBinary(); // Return digest as 20 character binary - string digestHex(); // Return digest formatted as a hex string - string digestSymbol(); // Return digest formatted as C symbol/base64ish - uint64_t digestUInt64(); // Return 64-bits of digest - static void selfTest(); // Test this class + string digestBinary(); // Return digest as 20 character binary + string digestHex(); // Return digest formatted as a hex string + string digestSymbol(); // Return digest formatted as C symbol/base64ish + uint64_t digestUInt64(); // Return 64-bits of digest + static void selfTest(); // Test this class // Inerting hash data void insert(const void* datap, size_t length); // Process data into the digest @@ -77,14 +77,14 @@ public: private: void init() { - m_inthash[0] = 0x67452301; m_inthash[1] = 0xefcdab89; m_inthash[2] = 0x98badcfe; - m_inthash[3] = 0x10325476; m_inthash[4] = 0xc3d2e1f0; - m_final = false; - m_totLength = 0; + m_inthash[0] = 0x67452301; m_inthash[1] = 0xefcdab89; m_inthash[2] = 0x98badcfe; + m_inthash[3] = 0x10325476; m_inthash[4] = 0xc3d2e1f0; + m_final = false; + m_totLength = 0; } static void selfTestOne(const string& data, const string& data2, - const string& exp, const string& exp64); - void finalize(); // Process remaining data + const string& exp, const string& exp64); + void finalize(); // Process remaining data }; //###################################################################### @@ -95,8 +95,8 @@ class VName { string m_name; string m_hashed; - static size_t s_maxLength; // Length at which to start hashing - static size_t s_minLength; // Length to preserve if over maxLength + static size_t s_maxLength; // Length at which to start hashing + static size_t s_minLength; // Length to preserve if over maxLength public: // CONSTRUCTORS explicit VName(const string& name) : m_name(name) {} @@ -106,10 +106,11 @@ public: string name() const { return m_name; } string hashedName(); // CONFIG STATIC METHORS - static void maxLength(size_t flag) { s_maxLength=flag; } // Length at which to start hashing, 0=disable + // Length at which to start hashing, 0=disable + static void maxLength(size_t flag) { s_maxLength = flag; } static size_t maxLength() { return s_maxLength; } }; //###################################################################### -#endif // guard +#endif // guard diff --git a/src/V3Subst.cpp b/src/V3Subst.cpp index 34e8b12d6..4b8d485fb 100644 --- a/src/V3Subst.cpp +++ b/src/V3Subst.cpp @@ -20,9 +20,9 @@ // V3Subst's Transformations: // // Each module: -// Search all ASSIGN(WORDSEL(...)) and build what it's assigned to -// Later usages of that word may then be replaced as long as -// the RHS hasn't changed value. +// Search all ASSIGN(WORDSEL(...)) and build what it's assigned to +// Later usages of that word may then be replaced as long as +// the RHS hasn't changed value. // //************************************************************************* @@ -53,17 +53,17 @@ public: class SubstVarWord { protected: // MEMBERS - AstNodeAssign* m_assignp; // Last assignment to each word of this var - int m_step; // Step number of last assignment - bool m_use; // True if each word was consumed - bool m_complex; // True if each word is complex + AstNodeAssign* m_assignp; // Last assignment to each word of this var + int m_step; // Step number of last assignment + bool m_use; // True if each word was consumed + bool m_complex; // True if each word is complex friend class SubstVarEntry; // METHODS void clear() { - m_assignp = NULL; - m_step = 0; - m_use = false; - m_complex = false; + m_assignp = NULL; + m_step = 0; + m_use = false; + m_complex = false; } }; @@ -72,108 +72,112 @@ protected: class SubstVarEntry { // MEMBERS - AstVar* m_varp; // Variable this tracks - bool m_wordAssign; // True if any word assignments - bool m_wordUse; // True if any individual word usage - SubstVarWord m_whole; // Data for whole vector used at once + AstVar* m_varp; // Variable this tracks + bool m_wordAssign; // True if any word assignments + bool m_wordUse; // True if any individual word usage + SubstVarWord m_whole; // Data for whole vector used at once std::vector m_words; // Data for every word, if multi word variable int debug() { return SubstBaseVisitor::debug(); } public: // CONSTRUCTORS explicit SubstVarEntry(AstVar* varp) { // Construction for when a var is used - m_varp = varp; - m_wordAssign = false; - m_wordUse = false; - m_words.resize(varp->widthWords()); - m_whole.clear(); - for (int i=0; iwidthWords(); i++) { - m_words[i].clear(); - } + m_varp = varp; + m_wordAssign = false; + m_wordUse = false; + m_words.resize(varp->widthWords()); + m_whole.clear(); + for (int i=0; iwidthWords(); i++) { + m_words[i].clear(); + } } ~SubstVarEntry() {} private: // METHODS bool wordNumOk(int word) const { - return word < m_varp->widthWords(); + return word < m_varp->widthWords(); } AstNodeAssign* getWordAssignp(int word) const { - if (!wordNumOk(word)) return NULL; - else return m_words[word].m_assignp; + if (!wordNumOk(word)) return NULL; + else return m_words[word].m_assignp; } public: void assignWhole(int step, AstNodeAssign* assp) { - if (m_whole.m_assignp) m_whole.m_complex = true; - m_whole.m_assignp = assp; - m_whole.m_step = step; + if (m_whole.m_assignp) m_whole.m_complex = true; + m_whole.m_assignp = assp; + m_whole.m_step = step; } void assignWord(int step, int word, AstNodeAssign* assp) { - if (!wordNumOk(word) || getWordAssignp(word) || m_words[word].m_complex) m_whole.m_complex = true; - m_wordAssign = true; - if (wordNumOk(word)) { - m_words[word].m_assignp = assp; - m_words[word].m_step = step; - } + if (!wordNumOk(word) || getWordAssignp(word) + || m_words[word].m_complex) m_whole.m_complex = true; + m_wordAssign = true; + if (wordNumOk(word)) { + m_words[word].m_assignp = assp; + m_words[word].m_step = step; + } } void assignWordComplex(int word) { - if (!wordNumOk(word) || getWordAssignp(word) || m_words[word].m_complex) m_whole.m_complex = true; - m_words[word].m_complex = true; + if (!wordNumOk(word) || getWordAssignp(word) + || m_words[word].m_complex) m_whole.m_complex = true; + m_words[word].m_complex = true; } void assignComplex() { - m_whole.m_complex = true; + m_whole.m_complex = true; } void consumeWhole() { //==consumeComplex as we don't know the difference - m_whole.m_use = true; + m_whole.m_use = true; } void consumeWord(int word) { - m_words[word].m_use = true; - m_wordUse = true; + m_words[word].m_use = true; + m_wordUse = true; } // ACCESSORS AstNode* substWhole(AstNode* errp) { - if (!m_varp->isWide() - && !m_whole.m_complex && m_whole.m_assignp && !m_wordAssign) { - AstNodeAssign* assp = m_whole.m_assignp; - if (!assp) errp->v3fatalSrc("Reading whole that was never assigned"); - return (assp->rhsp()); - } else { - return NULL; - } + if (!m_varp->isWide() + && !m_whole.m_complex && m_whole.m_assignp && !m_wordAssign) { + AstNodeAssign* assp = m_whole.m_assignp; + if (!assp) errp->v3fatalSrc("Reading whole that was never assigned"); + return (assp->rhsp()); + } else { + return NULL; + } } AstNode* substWord(AstNode* errp, int word) { // Return what to substitute given word number for - if (!m_whole.m_complex && !m_whole.m_assignp && !m_words[word].m_complex) { - AstNodeAssign* assp = getWordAssignp(word); - if (!assp) errp->v3fatalSrc("Reading a word that was never assigned, or bad word #"); - return (assp->rhsp()); - } else { - return NULL; - } + if (!m_whole.m_complex && !m_whole.m_assignp && !m_words[word].m_complex) { + AstNodeAssign* assp = getWordAssignp(word); + if (!assp) errp->v3fatalSrc("Reading a word that was never assigned, or bad word #"); + return (assp->rhsp()); + } else { + return NULL; + } } int getWholeStep() const { - return m_whole.m_step; + return m_whole.m_step; } int getWordStep(int word) const { - if (!wordNumOk(word)) return 0; else return m_words[word].m_step; + if (!wordNumOk(word)) return 0; else return m_words[word].m_step; } void deleteAssign(AstNodeAssign* nodep) { - UINFO(5, "Delete "<unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); + UINFO(5, "Delete "<unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } void deleteUnusedAssign() { - // If there are unused assignments in this var, kill them - if (!m_whole.m_use && !m_wordUse && m_whole.m_assignp) { - deleteAssign(m_whole.m_assignp); m_whole.m_assignp=NULL; - } - for (unsigned i=0; ivarp()->user2()) { - if (m_ok) UINFO(9," RHS variable changed since subst recorded: "<varp()->user2()) { + if (m_ok) UINFO(9," RHS variable changed since subst recorded: "< SubstVar* for usage var, 0=not set yet - // AstVar::user2 -> int step number for last assignment, 0=not set yet - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + // AstVar::user1p -> SubstVar* for usage var, 0=not set yet + // AstVar::user2 -> int step number for last assignment, 0=not set yet + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // STATE std::vector m_entryps; // Nodes to delete when we are finished - int m_ops; // Number of operators on assign rhs - int m_assignStep; // Assignment number to determine var lifetime - V3Double0 m_statSubsts; // Statistic tracking + int m_ops; // Number of operators on assign rhs + int m_assignStep; // Assignment number to determine var lifetime + V3Double0 m_statSubsts; // Statistic tracking - enum { SUBST_MAX_OPS_SUBST = 30, // Maximum number of ops to substitute in - SUBST_MAX_OPS_NA = 9999 }; // Not allowed to substitute + enum { SUBST_MAX_OPS_SUBST = 30, // Maximum number of ops to substitute in + SUBST_MAX_OPS_NA = 9999 }; // Not allowed to substitute // METHODS SubstVarEntry* getEntryp(AstVarRef* nodep) { - if (!nodep->varp()->user1p()) { + if (!nodep->varp()->user1p()) { SubstVarEntry* entryp = new SubstVarEntry(nodep->varp()); - m_entryps.push_back(entryp); - nodep->varp()->user1p(entryp); - return entryp; - } else { + m_entryps.push_back(entryp); + nodep->varp()->user1p(entryp); + return entryp; + } else { SubstVarEntry* entryp = reinterpret_cast(nodep->varp()->user1p()); - return entryp; - } + return entryp; + } } inline bool isSubstVar(AstVar* nodep) { - return nodep->isStatementTemp() && !nodep->noSubst(); + return nodep->isStatementTemp() && !nodep->noSubst(); } // VISITORS virtual void visit(AstNodeAssign* nodep) { - m_ops = 0; - m_assignStep++; + m_ops = 0; + m_assignStep++; iterateAndNextNull(nodep->rhsp()); - bool hit=false; + bool hit=false; if (AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef)) { - if (isSubstVar(varrefp->varp())) { - SubstVarEntry* entryp = getEntryp(varrefp); - hit = true; - if (m_ops > SUBST_MAX_OPS_SUBST) { - UINFO(8," ASSIGNtooDeep "<varp())) { + SubstVarEntry* entryp = getEntryp(varrefp); + hit = true; + if (m_ops > SUBST_MAX_OPS_SUBST) { + UINFO(8," ASSIGNtooDeep "<assignComplex(); - } else { - UINFO(8," ASSIGNwhole "<assignWhole(m_assignStep, nodep); - } - } - } + } else { + UINFO(8," ASSIGNwhole "<assignWhole(m_assignStep, nodep); + } + } + } else if (AstWordSel* wordp = VN_CAST(nodep->lhsp(), WordSel)) { if (AstVarRef* varrefp = VN_CAST(wordp->lhsp(), VarRef)) { if (VN_IS(wordp->rhsp(), Const) - && isSubstVar(varrefp->varp())) { + && isSubstVar(varrefp->varp())) { int word = VN_CAST(wordp->rhsp(), Const)->toUInt(); - SubstVarEntry* entryp = getEntryp(varrefp); - hit = true; - if (m_ops > SUBST_MAX_OPS_SUBST) { - UINFO(8," ASSIGNtooDeep "< SUBST_MAX_OPS_SUBST) { + UINFO(8," ASSIGNtooDeep "<assignWordComplex(word); - } else { - UINFO(8," ASSIGNword"<assignWord(m_assignStep, word, nodep); - } - } - } - } - if (!hit) { + } else { + UINFO(8," ASSIGNword"<assignWord(m_assignStep, word, nodep); + } + } + } + } + if (!hit) { iterate(nodep->lhsp()); - } + } } void replaceSubstEtc(AstNode* nodep, AstNode* substp) { - if (debug()>5) nodep->dumpTree(cout," substw_old: "); - AstNode* newp = substp->cloneTree(true); - if (!nodep->isQuad() && newp->isQuad()) { + if (debug()>5) nodep->dumpTree(cout, " substw_old: "); + AstNode* newp = substp->cloneTree(true); + if (!nodep->isQuad() && newp->isQuad()) { newp = new AstCCast(newp->fileline(), newp, nodep); - } - if (debug()>5) newp->dumpTree(cout," w_new: "); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - ++m_statSubsts; + } + if (debug()>5) newp->dumpTree(cout, " w_new: "); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + ++m_statSubsts; } virtual void visit(AstWordSel* nodep) { iterate(nodep->rhsp()); AstVarRef* varrefp = VN_CAST(nodep->lhsp(), VarRef); AstConst* constp = VN_CAST(nodep->rhsp(), Const); - if (varrefp && isSubstVar(varrefp->varp()) - && !varrefp->lvalue() - && constp) { - // Nicely formed lvalues handled in NodeAssign - // Other lvalues handled as unknown mess in AstVarRef - int word = constp->toUInt(); - UINFO(8," USEword"<substWord (nodep, word)) { - // Check that the RHS hasn't changed value since we recorded it. - SubstUseVisitor visitor (substp, entryp->getWordStep(word)); - if (visitor.ok()) { - replaceSubstEtc(nodep, substp); VL_DANGLING(nodep); - } else { - entryp->consumeWord(word); - } - } else { - entryp->consumeWord(word); - } - } else { + if (varrefp && isSubstVar(varrefp->varp()) + && !varrefp->lvalue() + && constp) { + // Nicely formed lvalues handled in NodeAssign + // Other lvalues handled as unknown mess in AstVarRef + int word = constp->toUInt(); + UINFO(8," USEword"<substWord(nodep, word)) { + // Check that the RHS hasn't changed value since we recorded it. + SubstUseVisitor visitor (substp, entryp->getWordStep(word)); + if (visitor.ok()) { + replaceSubstEtc(nodep, substp); VL_DANGLING(nodep); + } else { + entryp->consumeWord(word); + } + } else { + entryp->consumeWord(word); + } + } else { iterate(nodep->lhsp()); - } + } } virtual void visit(AstVarRef* nodep) { - // Any variable - if (nodep->lvalue()) { - m_assignStep++; - nodep->varp()->user2(m_assignStep); - UINFO(9, " ASSIGNstep u2="<varp()->user2()<<" "<varp())) { + // Any variable + if (nodep->lvalue()) { + m_assignStep++; + nodep->varp()->user2(m_assignStep); + UINFO(9, " ASSIGNstep u2="<varp()->user2()<<" "<varp())) { SubstVarEntry* entryp = getEntryp(nodep); - if (nodep->lvalue()) { - UINFO(8," ASSIGNcpx "<lvalue()) { + UINFO(8," ASSIGNcpx "<assignComplex(); - } else if (AstNode* substp = entryp->substWhole(nodep)) { - // Check that the RHS hasn't changed value since we recorded it. - SubstUseVisitor visitor (substp, entryp->getWholeStep()); - if (visitor.ok()) { - UINFO(8," USEwhole "<consumeWhole(); - } - } else { // Consumed w/o substitute - UINFO(8," USEwtf "<consumeWhole(); - } - } + } else if (AstNode* substp = entryp->substWhole(nodep)) { + // Check that the RHS hasn't changed value since we recorded it. + SubstUseVisitor visitor (substp, entryp->getWholeStep()); + if (visitor.ok()) { + UINFO(8," USEwhole "<consumeWhole(); + } + } else { // Consumed w/o substitute + UINFO(8," USEwtf "<consumeWhole(); + } + } } virtual void visit(AstVar* nodep) {} virtual void visit(AstConst* nodep) {} virtual void visit(AstNode* nodep) { - m_ops++; - if (!nodep->isSubstOptimizable()) { - m_ops = SUBST_MAX_OPS_NA; - } + m_ops++; + if (!nodep->isSubstOptimizable()) { + m_ops = SUBST_MAX_OPS_NA; + } iterateChildren(nodep); } public: // CONSTUCTORS explicit SubstVisitor(AstNode* nodep) { - AstNode::user1ClearTree(); // user1p() used on entire tree - AstNode::user2ClearTree(); // user2p() used on entire tree - m_ops = 0; - m_assignStep = 0; + AstNode::user1ClearTree(); // user1p() used on entire tree + AstNode::user2ClearTree(); // user2p() used on entire tree + m_ops = 0; + m_assignStep = 0; iterate(nodep); } virtual ~SubstVisitor() { - V3Stats::addStat("Optimizations, Substituted temps", m_statSubsts); - for (std::vector::iterator it = m_entryps.begin(); it != m_entryps.end(); ++it) { - (*it)->deleteUnusedAssign(); - delete (*it); - } + V3Stats::addStat("Optimizations, Substituted temps", m_statSubsts); + for (std::vector::iterator it = m_entryps.begin(); + it != m_entryps.end(); ++it) { + (*it)->deleteUnusedAssign(); + delete (*it); + } } }; diff --git a/src/V3Subst.h b/src/V3Subst.h index 9cb068ce2..98c495bfc 100644 --- a/src/V3Subst.h +++ b/src/V3Subst.h @@ -34,4 +34,4 @@ public: static void substituteAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3SymTable.h b/src/V3SymTable.h index 6056bceb9..9ffbbd9dd 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -47,19 +47,19 @@ class VSymEnt { private: // MEMBERS typedef std::multimap IdNameMap; - IdNameMap m_idNameMap; // Hash of variables by name - AstNode* m_nodep; // Node that entry belongs to - VSymEnt* m_fallbackp; // Table "above" this one in name scope, for fallback resolution - VSymEnt* m_parentp; // Table that created this table, dot notation needed to resolve into it - AstPackage* m_packagep; // Package node is in (for V3LinkDot, unused here) - string m_symPrefix; // String to prefix symbols with (for V3LinkDot, unused here) - bool m_exported; // Allow importing - bool m_imported; // Was imported + IdNameMap m_idNameMap; // Hash of variables by name + AstNode* m_nodep; // Node that entry belongs to + VSymEnt* m_fallbackp; // Table "above" this one in name scope, for fallback resolution + VSymEnt* m_parentp; // Table that created this table, dot notation needed to resolve into it + AstPackage* m_packagep; // Package node is in (for V3LinkDot, unused here) + string m_symPrefix; // String to prefix symbols with (for V3LinkDot, unused here) + bool m_exported; // Allow importing + bool m_imported; // Was imported #ifdef VL_DEBUG static int debug() { - static int level = -1; - if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel("V3LinkDot.cpp"); - return level; + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel("V3LinkDot.cpp"); + return level; } #else static inline int debug() { return 0; } // NOT runtime, too hot of a function @@ -67,42 +67,44 @@ private: public: void dumpIterate(std::ostream& os, VSymConstMap& doneSymsr, const string& indent, int numLevels, const string& searchName) const { - os<= 1) { - it->second->dumpIterate(os, doneSymsr, indent+"| ", numLevels-1, it->first); - } - } - } + os<<" n="<= 1) { + it->second->dumpIterate(os, doneSymsr, indent+"| ", numLevels-1, it->first); + } + } + } } void dump(std::ostream& os, const string& indent="", int numLevels=1) const { - VSymConstMap doneSyms; - dumpIterate(os, doneSyms, indent, numLevels, "TOP"); + VSymConstMap doneSyms; + dumpIterate(os, doneSyms, indent, numLevels, "TOP"); } // METHODS VSymEnt(VSymGraph* graphp, const VSymEnt* symp); // Below VSymEnt(VSymGraph* graphp, AstNode* nodep); // Below ~VSymEnt() { - // Change links so we coredump if used + // Change links so we coredump if used #ifdef VL_DEBUG - m_nodep = (AstNode*)1; - m_fallbackp = (VSymEnt*)1; - m_parentp = (VSymEnt*)1; - m_packagep = (AstPackage*)1; + m_nodep = (AstNode*)1; + m_fallbackp = (VSymEnt*)1; + m_parentp = (VSymEnt*)1; + m_packagep = (AstPackage*)1; #endif } #if defined(VL_DEBUG) && !defined(VL_LEAK_CHECKS) - void operator delete(void* objp, size_t size) {} // For testing, leak so above destructor 1 assignments work + // For testing, leak so above destructor 1 assignments work + void operator delete(void* objp, size_t size) {} #endif void fallbackp(VSymEnt* entp) { m_fallbackp = entp; } void parentp(VSymEnt* entp) { m_parentp = entp; } @@ -119,121 +121,124 @@ public: void insert(const string& name, VSymEnt* entp) { UINFO(9, " SymInsert se"<nodep()<=9 || V3Error::debugDefault()) dump(cout,"- err-dump: ", 1); - entp->nodep()->v3fatalSrc("Inserting two symbols with same name: "<=9 || V3Error::debugDefault()) dump(cout,"- err-dump: ", 1); + entp->nodep()->v3fatalSrc("Inserting two symbols with same name: "<nodep()<second = entp; // Replace - } else { - insert(name,entp); - } + it->second = entp; // Replace + } else { + insert(name, entp); + } } VSymEnt* findIdFlat(const string& name) const { - // Find identifier without looking upward through symbol hierarchy - // First, scan this begin/end block or module for the name - IdNameMap::const_iterator it = m_idNameMap.find(name); + // Find identifier without looking upward through symbol hierarchy + // First, scan this begin/end block or module for the name + IdNameMap::const_iterator it = m_idNameMap.find(name); UINFO(9, " SymFind se"< "<<(it == m_idNameMap.end() ? "NONE" + <<"' -> "<<(it == m_idNameMap.end() ? "NONE" : "se"+cvtToHex(it->second)+" n="+cvtToHex(it->second->nodep()))<second); - return NULL; + if (it != m_idNameMap.end()) return (it->second); + return NULL; } VSymEnt* findIdFallback(const string& name) const { - // Find identifier looking upward through symbol hierarchy - // First, scan this begin/end block or module for the name - if (VSymEnt* entp = findIdFlat(name)) return entp; - // Then scan the upper begin/end block or module for the name - if (m_fallbackp) return m_fallbackp->findIdFallback(name); - return NULL; + // Find identifier looking upward through symbol hierarchy + // First, scan this begin/end block or module for the name + if (VSymEnt* entp = findIdFlat(name)) return entp; + // Then scan the upper begin/end block or module for the name + if (m_fallbackp) return m_fallbackp->findIdFallback(name); + return NULL; } private: void importOneSymbol(VSymGraph* graphp, const string& name, const VSymEnt* srcp) { - if (srcp->exported() - && !findIdFlat(name)) { // Don't insert over existing entry - VSymEnt* symp = new VSymEnt(graphp, srcp); - symp->exported(false); // Can't reimport an import without an export - symp->imported(true); - reinsert(name, symp); - } + if (srcp->exported() + && !findIdFlat(name)) { // Don't insert over existing entry + VSymEnt* symp = new VSymEnt(graphp, srcp); + symp->exported(false); // Can't reimport an import without an export + symp->imported(true); + reinsert(name, symp); + } } void exportOneSymbol(VSymGraph* graphp, const string& name, const VSymEnt* srcp) { - if (srcp->exported()) { - if (VSymEnt* symp = findIdFlat(name)) { // Should already exist in current table - if (!symp->exported()) symp->exported(true); - } - } + if (srcp->exported()) { + if (VSymEnt* symp = findIdFlat(name)) { // Should already exist in current table + if (!symp->exported()) symp->exported(true); + } + } } public: void importFromPackage(VSymGraph* graphp, const VSymEnt* srcp, const string& id_or_star) { - // Import tokens from source symbol table into this symbol table - if (id_or_star != "*") { - IdNameMap::const_iterator it = srcp->m_idNameMap.find(id_or_star); - if (it != srcp->m_idNameMap.end()) { - importOneSymbol(graphp, it->first, it->second); - } - } else { - for (IdNameMap::const_iterator it=srcp->m_idNameMap.begin(); it!=srcp->m_idNameMap.end(); ++it) { - importOneSymbol(graphp, it->first, it->second); - } - } + // Import tokens from source symbol table into this symbol table + if (id_or_star != "*") { + IdNameMap::const_iterator it = srcp->m_idNameMap.find(id_or_star); + if (it != srcp->m_idNameMap.end()) { + importOneSymbol(graphp, it->first, it->second); + } + } else { + for (IdNameMap::const_iterator it = srcp->m_idNameMap.begin(); + it != srcp->m_idNameMap.end(); ++it) { + importOneSymbol(graphp, it->first, it->second); + } + } } void exportFromPackage(VSymGraph* graphp, const VSymEnt* srcp, const string& id_or_star) { - // Export tokens from source symbol table into this symbol table - if (id_or_star != "*") { - IdNameMap::const_iterator it = srcp->m_idNameMap.find(id_or_star); - if (it != srcp->m_idNameMap.end()) { - exportOneSymbol(graphp, it->first, it->second); - } - } else { - for (IdNameMap::const_iterator it=srcp->m_idNameMap.begin(); it!=srcp->m_idNameMap.end(); ++it) { - exportOneSymbol(graphp, it->first, it->second); - } - } + // Export tokens from source symbol table into this symbol table + if (id_or_star != "*") { + IdNameMap::const_iterator it = srcp->m_idNameMap.find(id_or_star); + if (it != srcp->m_idNameMap.end()) { + exportOneSymbol(graphp, it->first, it->second); + } + } else { + for (IdNameMap::const_iterator it = srcp->m_idNameMap.begin(); + it != srcp->m_idNameMap.end(); ++it) { + exportOneSymbol(graphp, it->first, it->second); + } + } } void exportStarStar(VSymGraph* graphp) { - // Export *:*: Export all tokens from imported packages - for (IdNameMap::const_iterator it=m_idNameMap.begin(); it!=m_idNameMap.end(); ++it) { - VSymEnt* symp = it->second; - if (!symp->exported()) symp->exported(true); - } + // Export *:*: Export all tokens from imported packages + for (IdNameMap::const_iterator it=m_idNameMap.begin(); it!=m_idNameMap.end(); ++it) { + VSymEnt* symp = it->second; + if (!symp->exported()) symp->exported(true); + } } void importFromIface(VSymGraph* graphp, const VSymEnt* srcp, bool onlyUnmodportable = false) { - // Import interface tokens from source symbol table into this symbol table, recursively + // Import interface tokens from source symbol table into this symbol table, recursively UINFO(9, " importIf se"<m_idNameMap.begin(); it!=srcp->m_idNameMap.end(); ++it) { - const string& name = it->first; - VSymEnt* subSrcp = it->second; + for (IdNameMap::const_iterator it = srcp->m_idNameMap.begin(); + it != srcp->m_idNameMap.end(); ++it) { + const string& name = it->first; + VSymEnt* subSrcp = it->second; const AstVar* varp = VN_CAST(subSrcp->nodep(), Var); - if (!onlyUnmodportable || (varp && varp->varType() == AstVarType::GPARAM)) { - VSymEnt* subSymp = new VSymEnt(graphp, subSrcp); - reinsert(name, subSymp); - // And recurse to create children - subSymp->importFromIface(graphp, subSrcp); - } - } + if (!onlyUnmodportable || (varp && varp->varType() == AstVarType::GPARAM)) { + VSymEnt* subSymp = new VSymEnt(graphp, subSrcp); + reinsert(name, subSymp); + // And recurse to create children + subSymp->importFromIface(graphp, subSrcp); + } + } } void cellErrorScopes(AstNode* lookp, string prettyName="") { - if (prettyName=="") prettyName = lookp->prettyName(); - string scopes; - for (IdNameMap::iterator it = m_idNameMap.begin(); it!=m_idNameMap.end(); ++it) { - AstNode* nodep = it->second->nodep(); + if (prettyName=="") prettyName = lookp->prettyName(); + string scopes; + for (IdNameMap::iterator it = m_idNameMap.begin(); it!=m_idNameMap.end(); ++it) { + AstNode* nodep = it->second->nodep(); if (VN_IS(nodep, Cell) || (VN_IS(nodep, Module) && VN_CAST(nodep, Module)->isTop())) { - if (scopes != "") scopes += ", "; - scopes += AstNode::prettyName(it->first); - } - } - if (scopes=="") scopes=""; + if (scopes != "") scopes += ", "; + scopes += AstNode::prettyName(it->first); + } + } + if (scopes=="") scopes=""; std::cerr< SymStack; // MEMBERS - VSymEnt* m_symRootp; // Root symbol table - SymStack m_symsp; // All symbol tables, to cleanup + VSymEnt* m_symRootp; // Root symbol table + SymStack m_symsp; // All symbol tables, to cleanup // CONSTRUCTORS VL_UNCOPYABLE(VSymGraph); public: explicit VSymGraph(AstNetlist* nodep) { - m_symRootp = new VSymEnt(this, nodep); + m_symRootp = new VSymEnt(this, nodep); } ~VSymGraph() { - for (SymStack::iterator it = m_symsp.begin(); it != m_symsp.end(); ++it) { - delete (*it); - } + for (SymStack::iterator it = m_symsp.begin(); it != m_symsp.end(); ++it) { + delete (*it); + } } public: @@ -269,25 +274,25 @@ public: VSymEnt* rootp() const { return m_symRootp; } // Debug void dump(std::ostream& os, const string& indent="") { - VSymConstMap doneSyms; - os<<"SymEnt Dump:\n"; - m_symRootp->dumpIterate(os, doneSyms, indent, 9999, "$root"); - bool first = true; - for (SymStack::iterator it = m_symsp.begin(); it != m_symsp.end(); ++it) { - if (doneSyms.find(*it) == doneSyms.end()) { - if (first) { first=false; os<<"%%Warning: SymEnt Orphans:\n"; } - (*it)->dumpIterate(os, doneSyms, indent, 9999, "Orphan"); - } - } + VSymConstMap doneSyms; + os<<"SymEnt Dump:\n"; + m_symRootp->dumpIterate(os, doneSyms, indent, 9999, "$root"); + bool first = true; + for (SymStack::iterator it = m_symsp.begin(); it != m_symsp.end(); ++it) { + if (doneSyms.find(*it) == doneSyms.end()) { + if (first) { first=false; os<<"%%Warning: SymEnt Orphans:\n"; } + (*it)->dumpIterate(os, doneSyms, indent, 9999, "Orphan"); + } + } } void dumpFilePrefixed(const string& nameComment) { - if (v3Global.opt.dumpTree()) { - string filename = v3Global.debugFilename(nameComment)+".txt"; - UINFO(2,"Dumping "< logp (V3File::new_ofstream(filename)); if (logp->fail()) v3fatal("Can't write "<pushNewEnt(this); } -#endif // guard +#endif // guard diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 61d4d09b7..3ed436ded 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -18,10 +18,10 @@ // //************************************************************************* // TABLE TRANSFORMATIONS: -// Look at all large always and assignments. -// Count # of input bits and # of output bits, and # of statements -// If high # of statements relative to inpbits*outbits, -// replace with lookup table +// Look at all large always and assignments. +// Count # of input bits and # of output bits, and # of statements +// If high # of statements relative to inpbits*outbits, +// replace with lookup table // //************************************************************************* @@ -42,10 +42,10 @@ // Table class functions // CONFIG -static const double TABLE_MAX_BYTES = 1*1024*1024; // 1MB is max table size (better be lots of instructs to be worth it!) -static const double TABLE_TOTAL_BYTES = 64*1024*1024; // 64MB is close to max memory of some systems (256MB or so), so don't get out of control -static const double TABLE_SPACE_TIME_MULT = 8; // Worth 8 bytes of data to replace a instruction -static const int TABLE_MIN_NODE_COUNT = 32; // If < 32 instructions, not worth the effort +static const double TABLE_MAX_BYTES = 1*1024*1024; // 1MB is max table size (better be lots of instructs to be worth it!) +static const double TABLE_TOTAL_BYTES = 64*1024*1024; // 64MB is close to max memory of some systems (256MB or so), so don't get out of control +static const double TABLE_SPACE_TIME_MULT = 8; // Worth 8 bytes of data to replace a instruction +static const int TABLE_MIN_NODE_COUNT = 32; // If < 32 instructions, not worth the effort //###################################################################### @@ -53,14 +53,14 @@ class TableVisitor; class TableSimulateVisitor : public SimulateVisitor { // MEMBERS - TableVisitor* m_cbthis; ///< Class for callback + TableVisitor* m_cbthis; ///< Class for callback public: - virtual void varRefCb(AstVarRef* nodep); ///< Call other-this function on all new var references + virtual void varRefCb(AstVarRef* nodep); ///< Call other-this function on all new var references // CONSTRUCTORS explicit TableSimulateVisitor(TableVisitor* cbthis) { - m_cbthis = cbthis; + m_cbthis = cbthis; } virtual ~TableSimulateVisitor() {} }; @@ -74,22 +74,22 @@ private: // Cleared on each always/assignw // STATE - double m_totalBytes; // Total bytes in tables created - V3Double0 m_statTablesCre; // Statistic tracking + double m_totalBytes; // Total bytes in tables created + V3Double0 m_statTablesCre; // Statistic tracking // State cleared on each module - AstNodeModule* m_modp; // Current MODULE - int m_modTables; // Number of tables created in this module + AstNodeModule* m_modp; // Current MODULE + int m_modTables; // Number of tables created in this module std::deque m_modTableVscs; // All tables created // State cleared on each scope - AstScope* m_scopep; // Current SCOPE + AstScope* m_scopep; // Current SCOPE // State cleared on each always/assignw - bool m_assignDly; // Consists of delayed assignments instead of normal assignments - int m_inWidth; // Input table width - int m_outWidth; // Output table width - std::deque m_inVarps; // Input variable list + bool m_assignDly; // Consists of delayed assignments instead of normal assignments + int m_inWidth; // Input table width + int m_outWidth; // Output table width + std::deque m_inVarps; // Input variable list std::deque m_outVarps; // Output variable list std::deque m_outNotSet; // True if output variable is not set at some point @@ -100,140 +100,143 @@ private: VL_DEBUG_FUNC; // Declare debug() bool treeTest(AstAlways* nodep) { - // Process alw/assign tree - m_inWidth = 0; - m_outWidth = 0; - m_inVarps.clear(); - m_outVarps.clear(); - m_outNotSet.clear(); + // Process alw/assign tree + m_inWidth = 0; + m_outWidth = 0; + m_inVarps.clear(); + m_outVarps.clear(); + m_outNotSet.clear(); - // Collect stats - TableSimulateVisitor chkvis (this); - chkvis.mainTableCheck(nodep); - m_assignDly = chkvis.isAssignDly(); - // Also sets m_inWidth - // Also sets m_outWidth - // Also sets m_inVarps - // Also sets m_outVarps + // Collect stats + TableSimulateVisitor chkvis (this); + chkvis.mainTableCheck(nodep); + m_assignDly = chkvis.isAssignDly(); + // Also sets m_inWidth + // Also sets m_outWidth + // Also sets m_inVarps + // Also sets m_outVarps - // Calc data storage in bytes - size_t chgWidth = m_outVarps.size(); // Width of one change-it-vector - if (chgWidth<8) chgWidth = 8; + // Calc data storage in bytes + size_t chgWidth = m_outVarps.size(); // Width of one change-it-vector + if (chgWidth<8) chgWidth = 8; double space = (pow(static_cast(2.0), static_cast(m_inWidth)) * static_cast(m_outWidth + chgWidth)); - // Instruction count bytes (ok, it's space also not time :) - double bytesPerInst = 4; - double time = (chkvis.instrCount()*bytesPerInst + chkvis.dataCount()) + 1; // +1 so won't div by zero - if (chkvis.instrCount() < TABLE_MIN_NODE_COUNT) { - chkvis.clearOptimizable(nodep,"Table has too few nodes involved"); - } - if (space > TABLE_MAX_BYTES) { - chkvis.clearOptimizable(nodep,"Table takes too much space"); - } - if (space > time * TABLE_SPACE_TIME_MULT) { - chkvis.clearOptimizable(nodep,"Table has bad tradeoff"); - } - if (m_totalBytes > TABLE_TOTAL_BYTES) { - chkvis.clearOptimizable(nodep,"Table out of memory"); - } - if (!m_outWidth || !m_inWidth) { - chkvis.clearOptimizable(nodep,"Table has no outputs"); - } - UINFO(4, " Test: Opt="<<(chkvis.optimizable()?"OK":"NO") - <<", Instrs="< TABLE_MAX_BYTES) { + chkvis.clearOptimizable(nodep, "Table takes too much space"); + } + if (space > time * TABLE_SPACE_TIME_MULT) { + chkvis.clearOptimizable(nodep, "Table has bad tradeoff"); + } + if (m_totalBytes > TABLE_TOTAL_BYTES) { + chkvis.clearOptimizable(nodep, "Table out of memory"); + } + if (!m_outWidth || !m_inWidth) { + chkvis.clearOptimizable(nodep, "Table has no outputs"); + } + UINFO(4, " Test: Opt="<<(chkvis.optimizable()?"OK":"NO") + <<", Instrs="<varScopep(); - if (nodep->lvalue()) { - m_outWidth += nodep->varp()->dtypeSkipRefp()->widthTotalBytes(); - m_outVarps.push_back(vscp); - } else { - // We'll make the table with a separate natural alignment for each - // output var, so always have char, 16 or 32 bit widths, so use widthTotalBytes - m_inWidth += nodep->varp()->width(); // Space for var - m_inVarps.push_back(vscp); - } + // Called by TableSimulateVisitor on each unique varref enountered + UINFO(9," SimVARREF "<varScopep(); + if (nodep->lvalue()) { + m_outWidth += nodep->varp()->dtypeSkipRefp()->widthTotalBytes(); + m_outVarps.push_back(vscp); + } else { + // We'll make the table with a separate natural alignment for each + // output var, so always have char, 16 or 32 bit widths, so use widthTotalBytes + m_inWidth += nodep->varp()->width(); // Space for var + m_inVarps.push_back(vscp); + } } private: void createTable(AstAlways* nodep) { - // We've determined this table of nodes is optimizable, do it. - ++m_modTables; - ++m_statTablesCre; + // We've determined this table of nodes is optimizable, do it. + ++m_modTables; + ++m_statTablesCre; - // Index into our table + // Index into our table AstVar* indexVarp = new AstVar(nodep->fileline(), AstVarType::BLOCKTEMP, "__Vtableidx" + cvtToStr(m_modTables), VFlagBitPacked(), m_inWidth); - m_modp->addStmtp(indexVarp); + m_modp->addStmtp(indexVarp); AstVarScope* indexVscp = new AstVarScope(indexVarp->fileline(), m_scopep, indexVarp); - m_scopep->addVarp(indexVscp); + m_scopep->addVarp(indexVscp); - // Change it variable - FileLine* fl = nodep->fileline(); - AstNodeArrayDType* dtypep + // Change it variable + FileLine* fl = nodep->fileline(); + AstNodeArrayDType* dtypep = new AstUnpackArrayDType(fl, nodep->findBitDType(m_outVarps.size(), m_outVarps.size(), AstNumeric::UNSIGNED), new AstRange(fl, VL_MASK_I(m_inWidth), 0)); - v3Global.rootp()->typeTablep()->addTypesp(dtypep); - AstVar* chgVarp + v3Global.rootp()->typeTablep()->addTypesp(dtypep); + AstVar* chgVarp = new AstVar(fl, AstVarType::MODULETEMP, "__Vtablechg" + cvtToStr(m_modTables), dtypep); - chgVarp->isConst(true); + chgVarp->isConst(true); chgVarp->valuep(new AstInitArray(nodep->fileline(), dtypep, NULL)); - m_modp->addStmtp(chgVarp); + m_modp->addStmtp(chgVarp); AstVarScope* chgVscp = new AstVarScope(chgVarp->fileline(), m_scopep, chgVarp); - m_scopep->addVarp(chgVscp); + m_scopep->addVarp(chgVscp); - createTableVars(nodep); - AstNode* stmtsp = createLookupInput(nodep, indexVscp); - createTableValues(nodep, chgVscp); + createTableVars(nodep); + AstNode* stmtsp = createLookupInput(nodep, indexVscp); + createTableValues(nodep, chgVscp); - // Collapse duplicate tables - chgVscp = findDuplicateTable(chgVscp); - for (std::deque::iterator it = m_tableVarps.begin(); it!=m_tableVarps.end(); ++it) { - *it = findDuplicateTable(*it); - } + // Collapse duplicate tables + chgVscp = findDuplicateTable(chgVscp); + for (std::deque::iterator it = m_tableVarps.begin(); + it != m_tableVarps.end(); ++it) { + *it = findDuplicateTable(*it); + } - createOutputAssigns(nodep, stmtsp, indexVscp, chgVscp); + createOutputAssigns(nodep, stmtsp, indexVscp, chgVscp); - // Link it in. + // Link it in. if (AstAlways* nodeap = VN_CAST(nodep, Always)) { - // Keep sensitivity list, but delete all else - nodeap->bodysp()->unlinkFrBackWithNext()->deleteTree(); - nodeap->addStmtp(stmtsp); - if (debug()>=6) nodeap->dumpTree(cout," table_new: "); - } else { - nodep->v3fatalSrc("Creating table under unknown node type"); - } + // Keep sensitivity list, but delete all else + nodeap->bodysp()->unlinkFrBackWithNext()->deleteTree(); + nodeap->addStmtp(stmtsp); + if (debug()>=6) nodeap->dumpTree(cout, " table_new: "); + } else { + nodep->v3fatalSrc("Creating table under unknown node type"); + } - // Cleanup internal structures - m_tableVarps.clear(); + // Cleanup internal structures + m_tableVarps.clear(); } void createTableVars(AstNode* nodep) { // Create table for each output typedef std::map NameCounts; NameCounts namecounts; - for (std::deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { - AstVarScope* outvscp = *it; - AstVar* outvarp = outvscp->varp(); - FileLine* fl = nodep->fileline(); - AstNodeArrayDType* dtypep + for (std::deque::iterator it = m_outVarps.begin(); + it != m_outVarps.end(); ++it) { + AstVarScope* outvscp = *it; + AstVar* outvarp = outvscp->varp(); + FileLine* fl = nodep->fileline(); + AstNodeArrayDType* dtypep = new AstUnpackArrayDType(fl, outvarp->dtypep(), new AstRange(fl, VL_MASK_I(m_inWidth), 0)); v3Global.rootp()->typeTablep()->addTypesp(dtypep); @@ -247,166 +250,175 @@ private: namecounts[name] = 0; } AstVar* tablevarp = new AstVar(fl, AstVarType::MODULETEMP, name, dtypep); - tablevarp->isConst(true); - tablevarp->isStatic(true); + tablevarp->isConst(true); + tablevarp->isStatic(true); tablevarp->valuep(new AstInitArray(nodep->fileline(), dtypep, NULL)); - m_modp->addStmtp(tablevarp); - AstVarScope* tablevscp = new AstVarScope(tablevarp->fileline(), m_scopep, tablevarp); - m_scopep->addVarp(tablevscp); - m_tableVarps.push_back(tablevscp); - } + m_modp->addStmtp(tablevarp); + AstVarScope* tablevscp = new AstVarScope(tablevarp->fileline(), m_scopep, tablevarp); + m_scopep->addVarp(tablevscp); + m_tableVarps.push_back(tablevscp); + } } AstNode* createLookupInput(AstNode* nodep, AstVarScope* indexVscp) { - // Concat inputs into a single temp variable (inside always) - // First var in inVars becomes the LSB of the concat - AstNode* concatp = NULL; + // Concat inputs into a single temp variable (inside always) + // First var in inVars becomes the LSB of the concat + AstNode* concatp = NULL; for (std::deque::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { - AstVarScope* invscp = *it; + AstVarScope* invscp = *it; AstVarRef* refp = new AstVarRef(nodep->fileline(), invscp, false); - if (concatp) { + if (concatp) { concatp = new AstConcat(nodep->fileline(), refp, concatp); - } else concatp = refp; - } + } else concatp = refp; + } - AstNode* stmtsp = new AstAssign - (nodep->fileline(), + AstNode* stmtsp = new AstAssign + (nodep->fileline(), new AstVarRef(nodep->fileline(), indexVscp, true), - concatp); - return stmtsp; + concatp); + return stmtsp; } void createTableValues(AstAlways* nodep, AstVarScope* chgVscp) { - // Create table - // There may be a simulation path by which the output doesn't change value. - // We could bail on these cases, or we can have a "change it" boolean. - // We've choosen the later route, since recirc is common in large FSMs. - for (std::deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { - m_outNotSet.push_back(false); - } - uint32_t inValueNextInitArray=0; - TableSimulateVisitor simvis (this); - for (uint32_t inValue=0; inValue <= VL_MASK_I(m_inWidth); inValue++) { - // Make a new simulation structure so we can set new input values + // Create table + // There may be a simulation path by which the output doesn't change value. + // We could bail on these cases, or we can have a "change it" boolean. + // We've choosen the later route, since recirc is common in large FSMs. + for (std::deque::iterator it = m_outVarps.begin(); + it != m_outVarps.end(); ++it) { + m_outNotSet.push_back(false); + } + uint32_t inValueNextInitArray = 0; + TableSimulateVisitor simvis (this); + for (uint32_t inValue=0; inValue <= VL_MASK_I(m_inWidth); inValue++) { + // Make a new simulation structure so we can set new input values UINFO(8," Simulating "<::iterator it = m_inVarps.begin(); it!=m_inVarps.end(); ++it) { - AstVarScope* invscp = *it; - // LSB is first variable, so extract it that way + // Set all inputs to the constant + uint32_t shift = 0; + for (std::deque::iterator it = m_inVarps.begin(); + it != m_inVarps.end(); ++it) { + AstVarScope* invscp = *it; + // LSB is first variable, so extract it that way simvis.newNumber(invscp, V3Number(invscp, invscp->width(), VL_MASK_I(invscp->width()) & (inValue>>shift))); - shift += invscp->width(); - // We're just using32 bit arithmetic, because there's no way the input table can be 2^32 bytes! - if (shift>31) nodep->v3fatalSrc("shift overflow"); - UINFO(8," Input "<name()<<" = "<<*(simvis.fetchNumber(invscp))<width(); + // We're just using32 bit arithmetic, because there's no + // way the input table can be 2^32 bytes! + if (shift>31) nodep->v3fatalSrc("shift overflow"); + UINFO(8," Input "<name()<<" = "<<*(simvis.fetchNumber(invscp))<v3fatalSrc("Optimizable cleared, even though earlier test run said not: " <::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { - AstVarScope* outvscp = *it; - V3Number* outnump = simvis.fetchOutNumberNull(outvscp); - AstNode* setp; - if (!outnump) { - UINFO(8," Output "<name()<<" never set\n"); - m_outNotSet[outnum] = true; - // Value in table is arbitrary, but we need something + for (std::deque::iterator it = m_outVarps.begin(); + it != m_outVarps.end(); ++it) { + AstVarScope* outvscp = *it; + V3Number* outnump = simvis.fetchOutNumberNull(outvscp); + AstNode* setp; + if (!outnump) { + UINFO(8," Output "<name()<<" never set\n"); + m_outNotSet[outnum] = true; + // Value in table is arbitrary, but we need something setp = new AstConst(outvscp->fileline(), V3Number(outvscp, outvscp->width(), 0)); } else { UINFO(8," Output "<name()<<" = "<<*outnump<fileline(), *outnump); - } - // Note InitArray requires us to have the values in inValue order + } + // Note InitArray requires us to have the values in inValue order VN_CAST(m_tableVarps[outnum]->varp()->valuep(), InitArray)->addValuep(setp); - outnum++; - } + outnum++; + } - { // Set changed table - if (inValue != inValueNextInitArray++) - nodep->v3fatalSrc("InitArray requires us to have the values in inValue order"); + { // Set changed table + if (inValue != inValueNextInitArray++) + nodep->v3fatalSrc("InitArray requires us to have the values in inValue order"); AstNode* setp = new AstConst(nodep->fileline(), outputChgMask); VN_CAST(chgVscp->varp()->valuep(), InitArray)->addValuep(setp); - } - } // each value + } + } // each value } AstVarScope* findDuplicateTable(AstVarScope* vsc1p) { - // See if another table we've created is identical, if so use it for both. - // (A more 'modern' way would be to instead use V3Hashed::findDuplicate) - AstVar* var1p = vsc1p->varp(); - for (std::deque::iterator it = m_modTableVscs.begin(); it!=m_modTableVscs.end(); ++it) { - AstVarScope* vsc2p= *it; - AstVar* var2p = vsc2p->varp(); - if (var1p->width() == var2p->width() - && (var1p->dtypep()->arrayUnpackedElements() - == var2p->dtypep()->arrayUnpackedElements())) { + // See if another table we've created is identical, if so use it for both. + // (A more 'modern' way would be to instead use V3Hashed::findDuplicate) + AstVar* var1p = vsc1p->varp(); + for (std::deque::iterator it = m_modTableVscs.begin(); + it != m_modTableVscs.end(); ++it) { + AstVarScope* vsc2p= *it; + AstVar* var2p = vsc2p->varp(); + if (var1p->width() == var2p->width() + && (var1p->dtypep()->arrayUnpackedElements() + == var2p->dtypep()->arrayUnpackedElements())) { const AstNode* init1p = VN_CAST(var1p->valuep(), InitArray); const AstNode* init2p = VN_CAST(var2p->valuep(), InitArray); - if (init1p->sameGateTree(init2p)) { - UINFO(8," Duplicate table var "<unlinkFrBack()->deleteTree(); + return vsc2p; + } + } + } + m_modTableVscs.push_back(vsc1p); + return vsc1p; } - void createOutputAssigns(AstNode* nodep, AstNode* stmtsp, AstVarScope* indexVscp, AstVarScope* chgVscp) { - // We walk through the changemask table, and if all ones know - // the output is set on all branches and therefore eliminate the - // if. If all uses of the changemask disappear, dead code - // elimination will remove it for us. - // Set each output from array ref into our table - int outnum = 0; - for (std::deque::iterator it = m_outVarps.begin(); it!=m_outVarps.end(); ++it) { - AstVarScope* outvscp = *it; - AstNode* alhsp = new AstVarRef(nodep->fileline(), outvscp, true); - AstNode* arhsp = new AstArraySel(nodep->fileline(), - new AstVarRef(nodep->fileline(), m_tableVarps[outnum], false), - new AstVarRef(nodep->fileline(), indexVscp, false)); + void createOutputAssigns(AstNode* nodep, AstNode* stmtsp, + AstVarScope* indexVscp, AstVarScope* chgVscp) { + // We walk through the changemask table, and if all ones know + // the output is set on all branches and therefore eliminate the + // if. If all uses of the changemask disappear, dead code + // elimination will remove it for us. + // Set each output from array ref into our table + int outnum = 0; + for (std::deque::iterator it = m_outVarps.begin(); + it != m_outVarps.end(); ++it) { + AstVarScope* outvscp = *it; + AstNode* alhsp = new AstVarRef(nodep->fileline(), outvscp, true); + AstNode* arhsp + = new AstArraySel(nodep->fileline(), + new AstVarRef(nodep->fileline(), m_tableVarps[outnum], false), + new AstVarRef(nodep->fileline(), indexVscp, false)); AstNode* outasnp = (m_assignDly ? static_cast(new AstAssignDly(nodep->fileline(), alhsp, arhsp)) : static_cast(new AstAssign(nodep->fileline(), alhsp, arhsp))); - AstNode* outsetp = outasnp; + AstNode* outsetp = outasnp; - // Is the value set in only some branches of the table? + // Is the value set in only some branches of the table? if (m_outNotSet[outnum]) { V3Number outputChgMask (nodep, m_outVarps.size(), 0); outputChgMask.setBit(outnum, 1); - outsetp = new AstIf(nodep->fileline(), - new AstAnd(nodep->fileline(), - new AstArraySel(nodep->fileline(), - new AstVarRef(nodep->fileline(), chgVscp, false), - new AstVarRef(nodep->fileline(), indexVscp, false)), - new AstConst(nodep->fileline(), outputChgMask)), - outsetp, NULL); - } + outsetp = new AstIf( + nodep->fileline(), + new AstAnd(nodep->fileline(), + new AstArraySel(nodep->fileline(), + new AstVarRef(nodep->fileline(), chgVscp, false), + new AstVarRef(nodep->fileline(), indexVscp, false)), + new AstConst(nodep->fileline(), outputChgMask)), + outsetp, NULL); + } - stmtsp->addNext(outsetp); - outnum++; - } + stmtsp->addNext(outsetp); + outnum++; + } } @@ -415,30 +427,30 @@ private: iterateChildren(nodep); } virtual void visit(AstNodeModule* nodep) { - m_modTables = 0; - m_modTableVscs.clear(); - m_modp = nodep; + m_modTables = 0; + m_modTableVscs.clear(); + m_modp = nodep; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstScope* nodep) { - UINFO(4," SCOPE "<fileline(); } AstCFunc* cFuncp() const { return m_cFuncp; } - void cFuncp(AstCFunc* nodep) { m_cFuncp=nodep; } + void cFuncp(AstCFunc* nodep) { m_cFuncp = nodep; } }; class TaskCodeVertex : public TaskBaseVertex { // Top vertex for all calls not under another task public: explicit TaskCodeVertex(V3Graph* graphp) - : TaskBaseVertex(graphp) {} + : TaskBaseVertex(graphp) {} virtual ~TaskCodeVertex() {} virtual string name() const { return "*CODE*"; } virtual string dotColor() const { return "green"; } @@ -90,7 +90,7 @@ public: class TaskEdge : public V3GraphEdge { public: TaskEdge(V3Graph* graphp, TaskBaseVertex* fromp, TaskBaseVertex* top) - : V3GraphEdge(graphp, fromp, top, 1, false) {} + : V3GraphEdge(graphp, fromp, top, 1, false) {} virtual ~TaskEdge() {} virtual string dotLabel() const { return "w"+cvtToStr(weight()); } }; @@ -101,132 +101,135 @@ class TaskStateVisitor : public AstNVisitor { private: // NODE STATE // Output: - // AstNodeFTask::user3p // AstScope* this FTask is under - // AstNodeFTask::user4p // GraphFTaskVertex* this FTask is under - // AstVar::user4p // GraphFTaskVertex* this variable is declared in + // AstNodeFTask::user3p // AstScope* this FTask is under + // AstNodeFTask::user4p // GraphFTaskVertex* this FTask is under + // AstVar::user4p // GraphFTaskVertex* this variable is declared in - AstUser3InUse m_inuser3; - AstUser4InUse m_inuser4; + AstUser3InUse m_inuser3; + AstUser4InUse m_inuser4; // TYPES typedef std::map,AstVarScope*> VarToScopeMap; // MEMBERS - VarToScopeMap m_varToScopeMap; // Map for Var -> VarScope mappings - AstAssignW* m_assignwp; // Current assignment - V3Graph m_callGraph; // Task call graph - TaskBaseVertex* m_curVxp; // Current vertex we're adding to + VarToScopeMap m_varToScopeMap; // Map for Var -> VarScope mappings + AstAssignW* m_assignwp; // Current assignment + V3Graph m_callGraph; // Task call graph + TaskBaseVertex* m_curVxp; // Current vertex we're adding to public: // METHODS AstScope* getScope(AstNodeFTask* nodep) { AstScope* scopep = VN_CAST(nodep->user3p(), Scope); - if (!scopep) nodep->v3fatalSrc("No scope for function"); - return scopep; + if (!scopep) nodep->v3fatalSrc("No scope for function"); + return scopep; } AstVarScope* findVarScope(AstScope* scopep, AstVar* nodep) { - VarToScopeMap::iterator iter = m_varToScopeMap.find(make_pair(scopep,nodep)); - if (iter == m_varToScopeMap.end()) nodep->v3fatalSrc("No scope for var"); - return iter->second; + VarToScopeMap::iterator iter = m_varToScopeMap.find(make_pair(scopep, nodep)); + if (iter == m_varToScopeMap.end()) nodep->v3fatalSrc("No scope for var"); + return iter->second; } bool ftaskNoInline(AstNodeFTask* nodep) { - return (getFTaskVertex(nodep)->noInline()); + return (getFTaskVertex(nodep)->noInline()); } AstCFunc* ftaskCFuncp(AstNodeFTask* nodep) { - return (getFTaskVertex(nodep)->cFuncp()); + return (getFTaskVertex(nodep)->cFuncp()); } void ftaskCFuncp(AstNodeFTask* nodep, AstCFunc* cfuncp) { - getFTaskVertex(nodep)->cFuncp(cfuncp); + getFTaskVertex(nodep)->cFuncp(cfuncp); } void checkPurity(AstNodeFTask* nodep) { - checkPurity(nodep, getFTaskVertex(nodep)); + checkPurity(nodep, getFTaskVertex(nodep)); } void checkPurity(AstNodeFTask* nodep, TaskBaseVertex* vxp) { - if (!vxp->pure()) { - nodep->v3warn(IMPURE,"Unsupported: External variable referenced by non-inlined function/task: "<prettyName()<impureNode()->warnMore()<<"... Location of the external reference: "<impureNode()->prettyName()); - } - // And, we need to check all tasks this task calls - for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep=edgep->outNextp()) { - checkPurity(nodep, static_cast(edgep->top())); - } + if (!vxp->pure()) { + nodep->v3warn(IMPURE, "Unsupported: External variable referenced by non-inlined function/task: " + <prettyName()<impureNode()->warnMore()<<"... Location of the external reference: " + <impureNode()->prettyName()); + } + // And, we need to check all tasks this task calls + for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep=edgep->outNextp()) { + checkPurity(nodep, static_cast(edgep->top())); + } } private: TaskFTaskVertex* getFTaskVertex(AstNodeFTask* nodep) { - if (!nodep->user4p()) { - nodep->user4p(new TaskFTaskVertex(&m_callGraph, nodep)); - } - return static_cast(nodep->user4u().toGraphVertex()); + if (!nodep->user4p()) { + nodep->user4p(new TaskFTaskVertex(&m_callGraph, nodep)); + } + return static_cast(nodep->user4u().toGraphVertex()); } // VISITORS virtual void visit(AstScope* nodep) { - // Each FTask is unique per-scope, so AstNodeFTaskRefs do not need - // pointers to what scope the FTask is to be invoked under. - // However, to create variables, we need to track the scopes involved. - // Find all var->varscope mappings, for later cleanup - for (AstNode* stmtp = nodep->varsp(); stmtp; stmtp=stmtp->nextp()) { + // Each FTask is unique per-scope, so AstNodeFTaskRefs do not need + // pointers to what scope the FTask is to be invoked under. + // However, to create variables, we need to track the scopes involved. + // Find all var->varscope mappings, for later cleanup + for (AstNode* stmtp = nodep->varsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVarScope* vscp = VN_CAST(stmtp, VarScope)) { - if (vscp->varp()->isFuncLocal()) { - UINFO(9," funcvsc "<varp()->isFuncLocal()) { + UINFO(9," funcvsc "<varp()), vscp)); - } - } - } - // Likewise, all FTask->scope mappings - for (AstNode* stmtp = nodep->blocksp(); stmtp; stmtp=stmtp->nextp()) { + } + } + } + // Likewise, all FTask->scope mappings + for (AstNode* stmtp = nodep->blocksp(); stmtp; stmtp=stmtp->nextp()) { if (AstNodeFTask* taskp = VN_CAST(stmtp, NodeFTask)) { - taskp->user3p(nodep); - } - } + taskp->user3p(nodep); + } + } iterateChildren(nodep); } virtual void visit(AstAssignW* nodep) { - m_assignwp = nodep; + m_assignwp = nodep; iterateChildren(nodep); VL_DANGLING(nodep); // May delete nodep. - m_assignwp = NULL; + m_assignwp = NULL; } virtual void visit(AstNodeFTaskRef* nodep) { - if (m_assignwp) { - // Wire assigns must become always statements to deal with insertion - // of multiple statements. Perhaps someday make all wassigns into always's? - UINFO(5," IM_WireRep "<convertToAlways(); pushDeletep(m_assignwp); m_assignwp=NULL; - } - // We make multiple edges if a task is called multiple times from another task. - if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked task"); + if (m_assignwp) { + // Wire assigns must become always statements to deal with insertion + // of multiple statements. Perhaps someday make all wassigns into always's? + UINFO(5," IM_WireRep "<convertToAlways(); + pushDeletep(m_assignwp); m_assignwp = NULL; + } + // We make multiple edges if a task is called multiple times from another task. + if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked task"); new TaskEdge(&m_callGraph, m_curVxp, getFTaskVertex(nodep->taskp())); } virtual void visit(AstNodeFTask* nodep) { - UINFO(9," TASK "<dpiImport()) m_curVxp->noInline(true); + UINFO(9," TASK "<dpiImport()) m_curVxp->noInline(true); iterateChildren(nodep); - m_curVxp = lastVxp; + m_curVxp = lastVxp; } virtual void visit(AstPragma* nodep) { - if (nodep->pragType() == AstPragmaType::NO_INLINE_TASK) { - // Just mark for the next steps, and we're done with it. - m_curVxp->noInline(true); - nodep->unlinkFrBack()->deleteTree(); - } - else { + if (nodep->pragType() == AstPragmaType::NO_INLINE_TASK) { + // Just mark for the next steps, and we're done with it. + m_curVxp->noInline(true); + nodep->unlinkFrBack()->deleteTree(); + } + else { iterateChildren(nodep); - } + } } virtual void visit(AstVar* nodep) { iterateChildren(nodep); - nodep->user4p(m_curVxp); // Remember what task it's under + nodep->user4p(m_curVxp); // Remember what task it's under } virtual void visit(AstVarRef* nodep) { iterateChildren(nodep); - if (nodep->varp()->user4u().toGraphVertex() != m_curVxp) { - if (m_curVxp->pure() - && !nodep->varp()->isXTemp()) { - m_curVxp->impure(nodep); - } - } + if (nodep->varp()->user4u().toGraphVertex() != m_curVxp) { + if (m_curVxp->pure() + && !nodep->varp()->isXTemp()) { + m_curVxp->impure(nodep); + } + } } //-------------------- // Default: Just iterate @@ -236,15 +239,15 @@ private: public: // CONSTUCTORS explicit TaskStateVisitor(AstNetlist* nodep) { - m_assignwp = NULL; - m_curVxp = new TaskCodeVertex(&m_callGraph); - AstNode::user3ClearTree(); - AstNode::user4ClearTree(); - // + m_assignwp = NULL; + m_curVxp = new TaskCodeVertex(&m_callGraph); + AstNode::user3ClearTree(); + AstNode::user4ClearTree(); + // iterate(nodep); - // - m_callGraph.removeRedundantEdgesSum(&TaskEdge::followAlwaysTrue); - m_callGraph.dumpDotFilePrefixed("task_call"); + // + m_callGraph.removeRedundantEdgesSum(&TaskEdge::followAlwaysTrue); + m_callGraph.dumpDotFilePrefixed("task_call"); } virtual ~TaskStateVisitor() {} }; @@ -256,19 +259,19 @@ class TaskRelinkVisitor : public AstNVisitor { private: // NODE STATE // Input: - // AstVar::user2p // AstVarScope* to replace varref with + // AstVar::user2p // AstVarScope* to replace varref with // VISITORS virtual void visit(AstVarRef* nodep) { - // Similar code in V3Inline - if (nodep->varp()->user2p()) { // It's being converted to a alias. + // Similar code in V3Inline + if (nodep->varp()->user2p()) { // It's being converted to an alias. UINFO(9, " relinkVar "<varp()->user2p())<<" "<varp()->user2p(), VarScope); - if (!newvscp) nodep->v3fatalSrc("not linked"); - nodep->varScopep(newvscp); - nodep->varp(nodep->varScopep()->varp()); - nodep->name(nodep->varp()->name()); - } + if (!newvscp) nodep->v3fatalSrc("not linked"); + nodep->varScopep(newvscp); + nodep->varp(nodep->varScopep()->varp()); + nodep->name(nodep->varp()->name()); + } iterateChildren(nodep); } @@ -291,31 +294,31 @@ class TaskVisitor : public AstNVisitor { private: // NODE STATE // Each module: - // AstNodeFTask::user // True if its been expanded + // AstNodeFTask::user // True if its been expanded // Each funccall // to TaskRelinkVisitor: - // AstVar::user2p // AstVarScope* to replace varref with + // AstVar::user2p // AstVarScope* to replace varref with - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // TYPES enum InsertMode { - IM_BEFORE, // Pointing at statement ref is in, insert before this - IM_AFTER, // Pointing at last inserted stmt, insert after - IM_WHILE_PRECOND // Pointing to for loop, add to body end + IM_BEFORE, // Pointing at statement ref is in, insert before this + IM_AFTER, // Pointing at last inserted stmt, insert after + IM_WHILE_PRECOND // Pointing to for loop, add to body end }; typedef std::map > DpiNames; // STATE - TaskStateVisitor* m_statep; // Common state between visitors - AstNodeModule* m_modp; // Current module - AstTopScope* m_topScopep; // Current top scope - AstScope* m_scopep; // Current scope - InsertMode m_insMode; // How to insert - AstNode* m_insStmtp; // Where to insert statement - int m_modNCalls; // Incrementing func # for making symbols - DpiNames m_dpiNames; // Map of all created DPI functions + TaskStateVisitor* m_statep; // Common state between visitors + AstNodeModule* m_modp; // Current module + AstTopScope* m_topScopep; // Current top scope + AstScope* m_scopep; // Current scope + InsertMode m_insMode; // How to insert + AstNode* m_insStmtp; // Where to insert statement + int m_modNCalls; // Incrementing func # for making symbols + DpiNames m_dpiNames; // Map of all created DPI functions // METHODS VL_DEBUG_FUNC; // Declare debug() @@ -323,11 +326,11 @@ private: AstVarScope* createFuncVar(AstCFunc* funcp, const string& name, AstVar* examplep) { AstVar* newvarp = new AstVar(funcp->fileline(), AstVarType::BLOCKTEMP, name, examplep); - newvarp->funcLocal(true); - funcp->addInitsp(newvarp); - AstVarScope* newvscp = new AstVarScope(funcp->fileline(), m_scopep, newvarp); - m_scopep->addVarp(newvscp); - return newvscp; + newvarp->funcLocal(true); + funcp->addInitsp(newvarp); + AstVarScope* newvscp = new AstVarScope(funcp->fileline(), m_scopep, newvarp); + m_scopep->addVarp(newvscp); + return newvscp; } AstVarScope* createInputVar(AstCFunc* funcp, const string& name, AstBasicDTypeKwd kwd) { AstVar* newvarp = new AstVar(funcp->fileline(), AstVarType::BLOCKTEMP, name, @@ -335,49 +338,50 @@ private: newvarp->funcLocal(true); newvarp->direction(VDirection::INPUT); funcp->addArgsp(newvarp); - AstVarScope* newvscp = new AstVarScope(funcp->fileline(), m_scopep, newvarp); - m_scopep->addVarp(newvscp); - return newvscp; + AstVarScope* newvscp = new AstVarScope(funcp->fileline(), m_scopep, newvarp); + m_scopep->addVarp(newvscp); + return newvscp; } AstVarScope* createVarScope(AstVar* invarp, const string& name) { - // We could create under either the ref's scope or the ftask's scope. - // It shouldn't matter, as they are only local variables. - // We choose to do it under whichever called this function, which results - // in more cache locality. + // We could create under either the ref's scope or the ftask's scope. + // It shouldn't matter, as they are only local variables. + // We choose to do it under whichever called this function, which results + // in more cache locality. AstVar* newvarp = new AstVar(invarp->fileline(), AstVarType::BLOCKTEMP, name, invarp); - newvarp->funcLocal(false); - newvarp->propagateAttrFrom(invarp); - m_modp->addStmtp(newvarp); + newvarp->funcLocal(false); + newvarp->propagateAttrFrom(invarp); + m_modp->addStmtp(newvarp); AstVarScope* newvscp = new AstVarScope(newvarp->fileline(), m_scopep, newvarp); - m_scopep->addVarp(newvscp); - return newvscp; + m_scopep->addVarp(newvscp); + return newvscp; } - AstNode* createInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix, AstVarScope* outvscp) { - // outvscp is the variable for functions only, if NULL, it's a task - if (!refp->taskp()) refp->v3fatalSrc("Unlinked?"); + AstNode* createInlinedFTask(AstNodeFTaskRef* refp, + const string& namePrefix, AstVarScope* outvscp) { + // outvscp is the variable for functions only, if NULL, it's a task + if (!refp->taskp()) refp->v3fatalSrc("Unlinked?"); AstNode* newbodysp = AstNode::cloneTreeNull(refp->taskp()->stmtsp(), true); // Maybe NULL AstNode* beginp = new AstComment(refp->fileline(), string("Function: ")+refp->name()); - if (newbodysp) beginp->addNext(newbodysp); - if (debug()>=9) { beginp->dumpTreeAndNext(cout,"-newbegi:"); } - // - // Create input variables - AstNode::user2ClearTree(); - V3TaskConnects tconnects = V3Task::taskConnects(refp, beginp); - for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { - AstVar* portp = it->first; - AstArg* argp = it->second; - AstNode* pinp = argp->exprp(); - portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original) - if (pinp==NULL) { - // Too few arguments in function call - } else { - UINFO(9, " Port "<unlinkFrBack(); // Relinked to assignment below - argp->unlinkFrBack()->deleteTree(); // Args no longer needed - // + if (newbodysp) beginp->addNext(newbodysp); + if (debug()>=9) { beginp->dumpTreeAndNext(cout, "-newbegi:"); } + // + // Create input variables + AstNode::user2ClearTree(); + V3TaskConnects tconnects = V3Task::taskConnects(refp, beginp); + for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { + AstVar* portp = it->first; + AstArg* argp = it->second; + AstNode* pinp = argp->exprp(); + portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original) + if (pinp==NULL) { + // Too few arguments in function call + } else { + UINFO(9, " Port "<unlinkFrBack(); // Relinked to assignment below + argp->unlinkFrBack()->deleteTree(); // Args no longer needed + // if (portp->isWritable() && VN_IS(pinp, Const)) { pinp->v3error("Function/task " +portp->direction().prettyName() // e.g. "output" @@ -385,107 +389,112 @@ private: +portp->prettyName()); } else if (portp->isInoutish()) { - // Correct lvalue; see comments below - V3LinkLValue::linkLValueSet(pinp); + // Correct lvalue; see comments below + V3LinkLValue::linkLValueSet(pinp); if (AstVarRef* varrefp = VN_CAST(pinp, VarRef)) { - // Connect to this exact variable - AstVarScope* localVscp = varrefp->varScopep(); if (!localVscp) varrefp->v3fatalSrc("Null var scope"); - portp->user2p(localVscp); - pushDeletep(pinp); - } else { - pinp->v3warn(E_TASKNSVAR,"Unsupported: Function/task input argument is not simple variable"); - } + // Connect to this exact variable + AstVarScope* localVscp = varrefp->varScopep(); + if (!localVscp) varrefp->v3fatalSrc("Null var scope"); + portp->user2p(localVscp); + pushDeletep(pinp); + } else { + pinp->v3warn(E_TASKNSVAR, "Unsupported: Function/task input argument is not simple variable"); + } } else if (portp->isWritable()) { // Make output variables - // Correct lvalue; we didn't know when we linked - // This is slightly scary; are we sure no decisions were made - // before here based on this not being a lvalue? - // Doesn't seem so; V3Unknown uses it earlier, but works ok. - V3LinkLValue::linkLValueSet(pinp); + // Correct lvalue; we didn't know when we linked + // This is slightly scary; are we sure no decisions were made + // before here based on this not being a lvalue? + // Doesn't seem so; V3Unknown uses it earlier, but works ok. + V3LinkLValue::linkLValueSet(pinp); - // Even if it's referencing a varref, we still make a temporary - // Else task(x,x,x) might produce incorrect results - AstVarScope* outvscp = createVarScope(portp, namePrefix+"__"+portp->shortName()); - portp->user2p(outvscp); + // Even if it's referencing a varref, we still make a temporary + // Else task(x,x,x) might produce incorrect results + AstVarScope* outvscp + = createVarScope(portp, namePrefix+"__"+portp->shortName()); + portp->user2p(outvscp); AstAssign* assp = new AstAssign(pinp->fileline(), pinp, new AstVarRef(outvscp->fileline(), outvscp, false)); - assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block - // Put assignment BEHIND of all other statements - beginp->addNext(assp); + assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block + // Put assignment BEHIND of all other statements + beginp->addNext(assp); } else if (portp->isNonOutput()) { // Make input variable - AstVarScope* inVscp = createVarScope(portp, namePrefix+"__"+portp->shortName()); - portp->user2p(inVscp); + AstVarScope* inVscp + = createVarScope(portp, namePrefix+"__"+portp->shortName()); + portp->user2p(inVscp); AstAssign* assp = new AstAssign(pinp->fileline(), new AstVarRef(inVscp->fileline(), inVscp, true), pinp); - assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block - // Put assignment in FRONT of all other statements - if (AstNode* afterp = beginp->nextp()) { - afterp->unlinkFrBackWithNext(); - assp->addNext(afterp); - } - beginp->addNext(assp); - } - } - } - if (refp->pinsp()) refp->v3fatalSrc("Pin wasn't removed by above loop"); - { - AstNode* nextstmtp; - for (AstNode* stmtp = beginp; stmtp; stmtp=nextstmtp) { - nextstmtp = stmtp->nextp(); + assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block + // Put assignment in FRONT of all other statements + if (AstNode* afterp = beginp->nextp()) { + afterp->unlinkFrBackWithNext(); + assp->addNext(afterp); + } + beginp->addNext(assp); + } + } + } + if (refp->pinsp()) refp->v3fatalSrc("Pin wasn't removed by above loop"); + { + AstNode* nextstmtp; + for (AstNode* stmtp = beginp; stmtp; stmtp=nextstmtp) { + nextstmtp = stmtp->nextp(); if (AstVar* portp = VN_CAST(stmtp, Var)) { - // Any I/O variables that fell out of above loop were already linked - if (!portp->user2p()) { - // Move it to a new localized variable - portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original) - AstVarScope* localVscp = createVarScope(portp, namePrefix+"__"+portp->shortName()); - portp->user2p(localVscp); - } - } - } - } - // Create function output variables - if (outvscp) { - //UINFO(0, "setflag on "<fvarp()<<" to "<taskp()->fvarp()->user2p(outvscp); - } - // Replace variable refs - // Iteration requires a back, so put under temporary node - { - AstBegin* tempp = new AstBegin(beginp->fileline(),"[EditWrapper]",beginp); - TaskRelinkVisitor visit (tempp); - tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); VL_DANGLING(tempp); - } - // - if (debug()>=9) { beginp->dumpTreeAndNext(cout,"-iotask: "); } - return beginp; + // Any I/O variables that fell out of above loop were already linked + if (!portp->user2p()) { + // Move it to a new localized variable + portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original) + AstVarScope* localVscp + = createVarScope(portp, namePrefix+"__"+portp->shortName()); + portp->user2p(localVscp); + } + } + } + } + // Create function output variables + if (outvscp) { + //UINFO(0, "setflag on "<fvarp()<<" to "<taskp()->fvarp()->user2p(outvscp); + } + // Replace variable refs + // Iteration requires a back, so put under temporary node + { + AstBegin* tempp = new AstBegin(beginp->fileline(), "[EditWrapper]", beginp); + TaskRelinkVisitor visit (tempp); + tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); VL_DANGLING(tempp); + } + // + if (debug()>=9) { beginp->dumpTreeAndNext(cout, "-iotask: "); } + return beginp; } - AstNode* createNonInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix, AstVarScope* outvscp) { - // outvscp is the variable for functions only, if NULL, it's a task - if (!refp->taskp()) refp->v3fatalSrc("Unlinked?"); - AstCFunc* cfuncp = m_statep->ftaskCFuncp(refp->taskp()); - if (!cfuncp) refp->v3fatalSrc("No non-inline task associated with this task call?"); - // + AstNode* createNonInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix, + AstVarScope* outvscp) { + // outvscp is the variable for functions only, if NULL, it's a task + if (!refp->taskp()) refp->v3fatalSrc("Unlinked?"); + AstCFunc* cfuncp = m_statep->ftaskCFuncp(refp->taskp()); + if (!cfuncp) refp->v3fatalSrc("No non-inline task associated with this task call?"); + // AstNode* beginp = new AstComment(refp->fileline(), string("Function: ")+refp->name()); - AstCCall* ccallp = new AstCCall(refp->fileline(), cfuncp, NULL); - beginp->addNext(ccallp); - // Convert complicated outputs to temp signals + AstCCall* ccallp = new AstCCall(refp->fileline(), cfuncp, NULL); + beginp->addNext(ccallp); + // Convert complicated outputs to temp signals - V3TaskConnects tconnects = V3Task::taskConnects(refp, refp->taskp()->stmtsp()); - for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { - AstVar* portp = it->first; - AstNode* pinp = it->second->exprp(); - if (!pinp) { - // Too few arguments in function call - } else { - UINFO(9, " Port "<taskp()->stmtsp()); + for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { + AstVar* portp = it->first; + AstNode* pinp = it->second->exprp(); + if (!pinp) { + // Too few arguments in function call + } else { + UINFO(9, " Port "<isWritable() && VN_IS(pinp, Const)) { pinp->v3error("Function/task " +portp->direction().prettyName() // e.g. "output" @@ -493,112 +502,115 @@ private: +portp->prettyName()); } else if (portp->isInoutish()) { - // Correct lvalue; see comments below - V3LinkLValue::linkLValueSet(pinp); + // Correct lvalue; see comments below + V3LinkLValue::linkLValueSet(pinp); if (VN_IS(pinp, VarRef)) { - // Connect to this exact variable - } else { - pinp->v3warn(E_TASKNSVAR,"Unsupported: Function/task input argument is not simple variable"); - } + // Connect to this exact variable + } else { + pinp->v3warn(E_TASKNSVAR, "Unsupported: Function/task input argument is not simple variable"); + } } else if (portp->isWritable()) { // Make output variables - // Correct lvalue; we didn't know when we linked - // This is slightly scary; are we sure no decisions were made - // before here based on this not being a lvalue? - // Doesn't seem so; V3Unknown uses it earlier, but works ok. - V3LinkLValue::linkLValueSet(pinp); + // Correct lvalue; we didn't know when we linked + // This is slightly scary; are we sure no decisions were made + // before here based on this not being a lvalue? + // Doesn't seem so; V3Unknown uses it earlier, but works ok. + V3LinkLValue::linkLValueSet(pinp); - // Even if it's referencing a varref, we still make a temporary - // Else task(x,x,x) might produce incorrect results - AstVarScope* outvscp = createVarScope(portp, namePrefix+"__"+portp->shortName()); - portp->user2p(outvscp); - pinp->replaceWith(new AstVarRef(outvscp->fileline(), outvscp, true)); + // Even if it's referencing a varref, we still make a temporary + // Else task(x,x,x) might produce incorrect results + AstVarScope* outvscp + = createVarScope(portp, namePrefix+"__"+portp->shortName()); + portp->user2p(outvscp); + pinp->replaceWith(new AstVarRef(outvscp->fileline(), outvscp, true)); AstAssign* assp = new AstAssign(pinp->fileline(), pinp, new AstVarRef(outvscp->fileline(), outvscp, false)); - assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block - // Put assignment BEHIND of all other statements - beginp->addNext(assp); - } - } - } - // First argument is symbol table, then output if a function - bool needSyms = !refp->taskp()->dpiImport(); - if (needSyms) ccallp->argTypes("vlSymsp"); + assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block + // Put assignment BEHIND of all other statements + beginp->addNext(assp); + } + } + } + // First argument is symbol table, then output if a function + bool needSyms = !refp->taskp()->dpiImport(); + if (needSyms) ccallp->argTypes("vlSymsp"); - if (refp->taskp()->dpiContext()) { - // __Vscopep - AstNode* snp = refp->scopeNamep()->unlinkFrBack(); if (!snp) refp->v3fatalSrc("Missing scoping context"); - ccallp->addArgsp(snp); - // __Vfilenamep - ccallp->addArgsp(new AstCMath(refp->fileline(), "\""+refp->fileline()->filename()+"\"", 64, true)); - // __Vlineno - ccallp->addArgsp(new AstConst(refp->fileline(), refp->fileline()->lineno())); - } + if (refp->taskp()->dpiContext()) { + // __Vscopep + AstNode* snp = refp->scopeNamep()->unlinkFrBack(); + if (!snp) refp->v3fatalSrc("Missing scoping context"); + ccallp->addArgsp(snp); + // __Vfilenamep + ccallp->addArgsp(new AstCMath(refp->fileline(), + "\""+refp->fileline()->filename()+"\"", 64, true)); + // __Vlineno + ccallp->addArgsp(new AstConst(refp->fileline(), refp->fileline()->lineno())); + } - // Create connections - AstNode* nextpinp; - for (AstNode* pinp = refp->pinsp(); pinp; pinp=nextpinp) { - nextpinp = pinp->nextp(); - // Move pin to the CCall, removing all Arg's + // Create connections + AstNode* nextpinp; + for (AstNode* pinp = refp->pinsp(); pinp; pinp=nextpinp) { + nextpinp = pinp->nextp(); + // Move pin to the CCall, removing all Arg's AstNode* exprp = VN_CAST(pinp, Arg)->exprp(); - exprp->unlinkFrBack(); - ccallp->addArgsp(exprp); - } + exprp->unlinkFrBack(); + ccallp->addArgsp(exprp); + } - if (outvscp) { - ccallp->addArgsp(new AstVarRef(refp->fileline(), outvscp, true)); - } + if (outvscp) { + ccallp->addArgsp(new AstVarRef(refp->fileline(), outvscp, true)); + } - if (debug()>=9) { beginp->dumpTreeAndNext(cout,"-nitask: "); } - return beginp; + if (debug()>=9) { beginp->dumpTreeAndNext(cout, "-nitask: "); } + return beginp; } string dpiprotoName(AstNodeFTask* nodep, AstVar* rtnvarp) const { - // Return fancy export-ish name for DPI function - // Variable names are NOT included so differences in only IO names won't matter - string dpiproto; - if (nodep->pure()) dpiproto += "pure "; - if (nodep->dpiContext()) dpiproto += "context "; - dpiproto += rtnvarp ? rtnvarp->dpiArgType(true,true):"void"; - dpiproto += " "+nodep->cname()+" ("; - string args; - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { + // Return fancy export-ish name for DPI function + // Variable names are NOT included so differences in only IO names won't matter + string dpiproto; + if (nodep->pure()) dpiproto += "pure "; + if (nodep->dpiContext()) dpiproto += "context "; + dpiproto += rtnvarp ? rtnvarp->dpiArgType(true, true) : "void"; + dpiproto += " "+nodep->cname()+" ("; + string args; + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (const AstVar* portp = VN_CAST(stmtp, Var)) { - if (portp->isIO() && !portp->isFuncReturn() && portp!=rtnvarp) { - if (args != "") { args+= ", "; dpiproto+= ", "; } - args += portp->name(); // Leftover so ,'s look nice - if (nodep->dpiImport()) dpiproto += portp->dpiArgType(false,false); - } - } - } - dpiproto += ")"; - return dpiproto; + if (portp->isIO() && !portp->isFuncReturn() && portp!=rtnvarp) { + if (args != "") { args+= ", "; dpiproto+= ", "; } + args += portp->name(); // Leftover so ,'s look nice + if (nodep->dpiImport()) dpiproto += portp->dpiArgType(false, false); + } + } + } + dpiproto += ")"; + return dpiproto; } AstNode* createDpiTemp(AstVar* portp, const string& suffix) { bool bitvec = (portp->basicp()->keyword().isDpiBitVal() && portp->width() > 32); bool logicvec = (portp->basicp()->keyword().isDpiLogicVal() && portp->width() > 1); - string stmt; - if (bitvec) { - stmt += "svBitVecVal "+portp->name()+suffix; - stmt += " ["+cvtToStr(portp->widthWords())+"]"; + string stmt; + if (bitvec) { + stmt += "svBitVecVal "+portp->name()+suffix; + stmt += " ["+cvtToStr(portp->widthWords())+"]"; } else if (logicvec) { stmt += "svLogicVecVal "+portp->name()+suffix; stmt += " ["+cvtToStr(portp->widthWords())+"]"; } else { - stmt += portp->dpiArgType(true,true); - stmt += " "+portp->name()+suffix; - } - stmt += ";\n"; - return new AstCStmt(portp->fileline(), stmt); + stmt += portp->dpiArgType(true, true); + stmt += " "+portp->name()+suffix; + } + stmt += ";\n"; + return new AstCStmt(portp->fileline(), stmt); } AstNode* createAssignInternalToDpi(AstVar* portp, bool isRtn, bool isPtr, - const string& frSuffix, const string& toSuffix) { - // Create assignment from internal format into DPI temporary + const string& frSuffix, const string& toSuffix) { + // Create assignment from internal format into DPI temporary bool bitvec = (portp->basicp()->keyword().isDpiBitVal() && portp->width() > 32); bool logicvec = (portp->basicp()->keyword().isDpiLogicVal() && portp->width() > 1); if (isRtn && (bitvec || logicvec)) { @@ -606,59 +618,64 @@ private: " use a two-state type or task instead: "<prettyName()); // Code below works, but won't compile right, and IEEE illegal } - string stmt; - string ket; - // Someday we'll have better type support, and this can make variables and casts. - // But for now, we'll just text-bash it. - if (bitvec) { - if (portp->isWide()) { - stmt += ("VL_SET_SVBV_W("+cvtToStr(portp->width()) - +", "+portp->name()+toSuffix+", "+portp->name()+frSuffix+")"); - } else { - stmt += "VL_SET_WQ("+portp->name()+toSuffix+", "+portp->name()+frSuffix+")"; - } + string stmt; + string ket; + // Someday we'll have better type support, and this can make variables and casts. + // But for now, we'll just text-bash it. + if (bitvec) { + if (portp->isWide()) { + stmt += ("VL_SET_SVBV_W("+cvtToStr(portp->width()) + +", "+portp->name()+toSuffix+", "+portp->name()+frSuffix+")"); + } else { + stmt += "VL_SET_WQ("+portp->name()+toSuffix+", "+portp->name()+frSuffix+")"; + } } else if (logicvec) { stmt += ("VL_SET_SVLV_" + string(portp->dtypep()->charIQWN()) + "(" + cvtToStr(portp->width()) + ", "+portp->name()+toSuffix+", "+portp->name()+frSuffix+")"); - } else { - if (isPtr) stmt += "*"; // DPI outputs are pointers - stmt += portp->name()+toSuffix+" = "; - if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) { - stmt += "VL_CVT_Q_VP("; - ket += ")"; - } - stmt += portp->name()+frSuffix; - if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING) { - stmt += ".c_str()"; - } - } - stmt += ket + ";\n"; - return new AstCStmt(portp->fileline(), stmt); + } else { + if (isPtr) stmt += "*"; // DPI outputs are pointers + stmt += portp->name()+toSuffix+" = "; + if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) { + stmt += "VL_CVT_Q_VP("; + ket += ")"; + } + stmt += portp->name()+frSuffix; + if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING) { + stmt += ".c_str()"; + } + } + stmt += ket + ";\n"; + return new AstCStmt(portp->fileline(), stmt); } AstNode* createAssignDpiToInternal(AstVarScope* portvscp, const string& frName, bool cvt) { - // Create assignment from DPI temporary into internal format - AstVar* portp = portvscp->varp(); + // Create assignment from DPI temporary into internal format + AstVar* portp = portvscp->varp(); string frstmt; if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) { frstmt = "VL_CVT_VP_Q("+frName+")"; } - else if (portp->basicp() && portp->basicp()->keyword().isDpiBitVal() && portp->width() != 1 && portp->isQuad()) { + else if (portp->basicp() && portp->basicp()->keyword().isDpiBitVal() + && portp->width() != 1 && portp->isQuad()) { // SV is vector, Verilator isn't frstmt = "VL_SET_QW("+frName+")"; } - else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal() && portp->width() != 1 && portp->isQuad()) { + else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal() + && portp->width() != 1 && portp->isQuad()) { frstmt = "VL_SET_Q_SVLV("+frName+")"; } - else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal() && portp->width() != 1 && !portp->isWide()) { + else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal() + && portp->width() != 1 && !portp->isWide()) { frstmt = "VL_SET_I_SVLV("+frName+")"; } else if (!cvt - && portp->basicp() && portp->basicp()->keyword().isDpiBitVal() && portp->width() != 1 && !portp->isWide()) { + && portp->basicp() && portp->basicp()->keyword().isDpiBitVal() + && portp->width() != 1 && !portp->isWide()) { frstmt = "*"+frName; // it's a svBitVecVal, which other code won't think is arrayed (as WData aren't), but really is } - else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal() && portp->width() != 1 && portp->isWide()) { + else if (portp->basicp() && portp->basicp()->keyword().isDpiLogicVal() + && portp->width() != 1 && portp->isWide()) { // Need to convert to wide, using special function AstNode* linesp = new AstText(portp->fileline(), "VL_SET_W_SVLV("+cvtToStr(portp->width()) + ","); linesp->addNext(new AstVarRef(portp->fileline(), portvscp, true)); @@ -667,67 +684,71 @@ private: } else { frstmt = frName; - } - // Use a AstCMath, as we want V3Clean to mask off bits that don't make sense. - int cwidth = VL_WORDSIZE; if (portp->basicp()) cwidth = portp->basicp()->keyword().width(); - if (portp->basicp() && portp->basicp()->keyword().isBitLogic()) cwidth = VL_WORDSIZE*portp->widthWords(); - AstNode* newp = new AstAssign(portp->fileline(), - new AstVarRef(portp->fileline(), portvscp, true), - new AstSel(portp->fileline(), + } + // Use a AstCMath, as we want V3Clean to mask off bits that don't make sense. + int cwidth = VL_WORDSIZE; + if (portp->basicp()) cwidth = portp->basicp()->keyword().width(); + if (portp->basicp() + && portp->basicp()->keyword().isBitLogic()) cwidth = VL_WORDSIZE*portp->widthWords(); + AstNode* newp = new AstAssign(portp->fileline(), + new AstVarRef(portp->fileline(), portvscp, true), + new AstSel(portp->fileline(), new AstCMath(portp->fileline(), frstmt, cwidth, false), - 0, portp->width())); - return newp; + 0, portp->width())); + return newp; } void makeDpiExportWrapper(AstNodeFTask* nodep, AstVar* rtnvarp) { - AstCFunc* dpip = new AstCFunc(nodep->fileline(), - nodep->cname(), - m_scopep, - (rtnvarp ? rtnvarp->dpiArgType(true,true) : "")); - dpip->dontCombine(true); - dpip->entryPoint(true); - dpip->isStatic(true); - dpip->dpiExportWrapper(true); - dpip->cname(nodep->cname()); - // Add DPI reference to top, since it's a global function - m_topScopep->scopep()->addActivep(dpip); + AstCFunc* dpip = new AstCFunc(nodep->fileline(), + nodep->cname(), + m_scopep, + (rtnvarp ? rtnvarp->dpiArgType(true, true) : "")); + dpip->dontCombine(true); + dpip->entryPoint(true); + dpip->isStatic(true); + dpip->dpiExportWrapper(true); + dpip->cname(nodep->cname()); + // Add DPI reference to top, since it's a global function + m_topScopep->scopep()->addActivep(dpip); - {// Create dispatch wrapper - // Note this function may dispatch to myfunc on a different class. - // Thus we need to be careful not to assume a particular function layout. - // - // Func numbers must be the same for each function, even when there are - // completely different models with the same function name. - // Thus we can't just use a constant computed at Verilation time. - // We could use 64-bits of a MD5/SHA hash rather than a string here, - // but the compare is only done on first call then memoized, so it's not worth optimizing. - string stmt; - stmt += "static int __Vfuncnum = -1;\n"; // Static doesn't need save-restore as if below will re-fill proper value - // First time init (faster than what the compiler does if we did a singleton - stmt += "if (VL_UNLIKELY(__Vfuncnum==-1)) { __Vfuncnum = Verilated::exportFuncNum(\""+nodep->cname()+"\"); }\n"; - // If the find fails, it will throw an error - stmt += "const VerilatedScope* __Vscopep = Verilated::dpiScope();\n"; - // If dpiScope is fails and is null; the exportFind function throws and error - string cbtype = v3Global.opt.prefix()+"__Vcb_"+nodep->cname()+"_t"; - stmt += cbtype+" __Vcb = ("+cbtype+")(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));\n"; // Can't use static_cast - // If __Vcb is null the exportFind function throws and error - dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - } + { // Create dispatch wrapper + // Note this function may dispatch to myfunc on a different class. + // Thus we need to be careful not to assume a particular function layout. + // + // Func numbers must be the same for each function, even when there are + // completely different models with the same function name. + // Thus we can't just use a constant computed at Verilation time. + // We could use 64-bits of a MD5/SHA hash rather than a string here, + // but the compare is only done on first call then memoized, so + // it's not worth optimizing. + string stmt; + stmt += "static int __Vfuncnum = -1;\n"; // Static doesn't need save-restore as if below will re-fill proper value + // First time init (faster than what the compiler does if we did a singleton + stmt += "if (VL_UNLIKELY(__Vfuncnum==-1)) { __Vfuncnum = Verilated::exportFuncNum(\"" + +nodep->cname()+"\"); }\n"; + // If the find fails, it will throw an error + stmt += "const VerilatedScope* __Vscopep = Verilated::dpiScope();\n"; + // If dpiScope is fails and is null; the exportFind function throws and error + string cbtype = v3Global.opt.prefix()+"__Vcb_"+nodep->cname()+"_t"; + stmt += cbtype+" __Vcb = ("+cbtype+")(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));\n"; // Can't use static_cast + // If __Vcb is null the exportFind function throws and error + dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + } - // Convert input/inout DPI arguments to Internal types - string args; - args += "("+v3Global.opt.prefix()+"__Syms*)(__Vscopep->symsp())"; // Upcast w/o overhead - AstNode* argnodesp = NULL; - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { + // Convert input/inout DPI arguments to Internal types + string args; + args += "("+v3Global.opt.prefix()+"__Syms*)(__Vscopep->symsp())"; // Upcast w/o overhead + AstNode* argnodesp = NULL; + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { - if (portp->isIO() && !portp->isFuncReturn() && portp != rtnvarp) { - // No createDpiTemp; we make a real internal variable instead - // SAME CODE BELOW - args+= ", "; + if (portp->isIO() && !portp->isFuncReturn() && portp != rtnvarp) { + // No createDpiTemp; we make a real internal variable instead + // SAME CODE BELOW + args+= ", "; if (args != "") { argnodesp = argnodesp->addNext( new AstText(portp->fileline(), args, true)); - args=""; + args = ""; } AstVarScope* outvscp = createFuncVar(dpip, portp->name()+"__Vcvt", portp); AstVarRef* refp = new AstVarRef(portp->fileline(), outvscp, @@ -735,71 +756,76 @@ private: argnodesp = argnodesp->addNextNull(refp); if (portp->isNonOutput()) { - dpip->addStmtsp(createAssignDpiToInternal(outvscp, portp->name(), false)); - } - } - } - } + dpip->addStmtsp(createAssignDpiToInternal(outvscp, portp->name(), false)); + } + } + } + } - if (rtnvarp) { - AstVar* portp = rtnvarp; - // SAME CODE ABOVE - args+= ", "; - if (args != "") { argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true)); args=""; } + if (rtnvarp) { + AstVar* portp = rtnvarp; + // SAME CODE ABOVE + args+= ", "; + if (args != "") { + argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true)); + args=""; + } AstVarScope* outvscp = createFuncVar(dpip, portp->name()+"__Vcvt", portp); AstVarRef* refp = new AstVarRef(portp->fileline(), outvscp, portp->isWritable()); argnodesp = argnodesp->addNextNull(refp); } - {// Call the user function - // Add the variables referenced as VarRef's so that lifetime analysis - // doesn't rip up the variables on us - string stmt; - stmt += "(*__Vcb)("; - args += ");\n"; - AstCStmt* newp = new AstCStmt(nodep->fileline(), stmt); - newp->addBodysp(argnodesp); VL_DANGLING(argnodesp); - newp->addBodysp(new AstText(nodep->fileline(), args, true)); - dpip->addStmtsp(newp); - } + { // Call the user function + // Add the variables referenced as VarRef's so that lifetime analysis + // doesn't rip up the variables on us + string stmt; + stmt += "(*__Vcb)("; + args += ");\n"; + AstCStmt* newp = new AstCStmt(nodep->fileline(), stmt); + newp->addBodysp(argnodesp); VL_DANGLING(argnodesp); + newp->addBodysp(new AstText(nodep->fileline(), args, true)); + dpip->addStmtsp(newp); + } - // Convert output/inout arguments back to internal type - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { + // Convert output/inout arguments back to internal type + for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { if (portp->isIO() && portp->isWritable() && !portp->isFuncReturn()) { - dpip->addStmtsp(createAssignInternalToDpi(portp,false,true,"__Vcvt","")); + dpip->addStmtsp(createAssignInternalToDpi(portp, false, true, "__Vcvt", "")); } } } - if (rtnvarp) { - dpip->addStmtsp(createDpiTemp(rtnvarp,"")); - dpip->addStmtsp(createAssignInternalToDpi(rtnvarp,true,false,"__Vcvt","")); - string stmt = "return "+rtnvarp->name()+";\n"; - dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - } + if (rtnvarp) { + dpip->addStmtsp(createDpiTemp(rtnvarp, "")); + dpip->addStmtsp(createAssignInternalToDpi(rtnvarp, true, false, "__Vcvt", "")); + string stmt = "return "+rtnvarp->name()+";\n"; + dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + } makePortList(nodep, dpip); } void makeDpiImportProto(AstNodeFTask* nodep, AstVar* rtnvarp) { - if (nodep->cname() != AstNode::prettyName(nodep->cname())) { - nodep->v3error("DPI function has illegal characters in C identifier name: "<cname())); - } - AstCFunc* dpip = new AstCFunc(nodep->fileline(), - nodep->cname(), - m_scopep, - (rtnvarp ? rtnvarp->dpiArgType(true,true) - // Tasks (but not void functions) return bool indicating disabled - : nodep->dpiTask() ? "int" - : "")); - dpip->dontCombine(true); + if (nodep->cname() != AstNode::prettyName(nodep->cname())) { + nodep->v3error("DPI function has illegal characters in C identifier name: " + <cname())); + } + AstCFunc* dpip = new AstCFunc(nodep->fileline(), + nodep->cname(), + m_scopep, + (rtnvarp ? rtnvarp->dpiArgType(true, true) + // Tasks (but not void functions) + // return bool indicating disabled + : nodep->dpiTask() ? "int" + : "")); + dpip->dontCombine(true); dpip->entryPoint(false); dpip->funcPublic(true); dpip->isStatic(false); dpip->pure(nodep->pure()); dpip->dpiImport(true); - // Add DPI reference to top, since it's a global function - m_topScopep->scopep()->addActivep(dpip); + // Add DPI reference to top, since it's a global function + m_topScopep->scopep()->addActivep(dpip); makePortList(nodep, dpip); } @@ -812,9 +838,11 @@ private: return false; } else if (iter->second.second != dpiproto) { - nodep->v3error("Duplicate declaration of DPI function with different formal arguments: "<prettyName()<v3error("Duplicate declaration of DPI function with different formal arguments: " + <prettyName()<warnMore()<<"... New prototype: "<second.first->warnMore()<<"... Original prototype: "<second.second); + <second.first->warnMore()<<"... Original prototype: " + <second.second); return true; } else { @@ -832,7 +860,8 @@ private: newPortp->funcLocal(true); dpip->addArgsp(newPortp); if (!portp->basicp()) { - portp->v3error("Unsupported: DPI argument of type "<basicp()->prettyTypeName()<v3error("Unsupported: DPI argument of type " + <basicp()->prettyTypeName()<warnMore()<<"... For best portability, use bit, byte, int, or longint"); // We don't warn on logic either, although the 4-stateness is lost. // That's what other simulators do. @@ -843,31 +872,35 @@ private: } void bodyDpiImportFunc(AstNodeFTask* nodep, AstVarScope* rtnvscp, AstCFunc* cfuncp) { - // Convert input/inout arguments to DPI types - string args; - for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { + // Convert input/inout arguments to DPI types + string args; + for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { AstVarScope* portvscp = VN_CAST(portp->user2p(), VarScope); // Remembered when we created it earlier - if (portp->isIO() && !portp->isFuncReturn() && portvscp != rtnvscp - && portp->name() != "__Vscopep" // Passed to dpiContext, not callee - && portp->name() != "__Vfilenamep" - && portp->name() != "__Vlineno") { + if (portp->isIO() && !portp->isFuncReturn() && portvscp != rtnvscp + && portp->name() != "__Vscopep" // Passed to dpiContext, not callee + && portp->name() != "__Vfilenamep" + && portp->name() != "__Vlineno") { bool openarray = portp->isDpiOpenArray(); - bool bitvec = (portp->basicp()->keyword().isDpiBitVal() && portp->width() > 32); - bool logicvec = (portp->basicp()->keyword().isDpiLogicVal() && portp->width() > 1); + bool bitvec = (portp->basicp()->keyword().isDpiBitVal() + && portp->width() > 32); + bool logicvec = (portp->basicp()->keyword().isDpiLogicVal() + && portp->width() > 1); - if (args != "") { args+= ", "; } + if (args != "") { args+= ", "; } if (openarray) { - // Ideally we'd make a table of variable characteristics, and reuse it wherever we can + // Ideally we'd make a table of variable + // characteristics, and reuse it wherever we can // At least put them into the module's CTOR as static? string propName = portp->name()+"__Vopenprops"; string propCode = ("static const VerilatedVarProps "+propName +"("+portp->vlPropInit()+");\n"); cfuncp->addStmtsp(new AstCStmt(portp->fileline(), propCode)); // - // At runtime we need the svOpenArrayHandle to point to this task & thread's data, - // in addition to static info about the variable + // At runtime we need the svOpenArrayHandle to + // point to this task & thread's data, in addition + // to static info about the variable string name = portp->name()+"__Vopenarray"; string varCode = ("VerilatedDpiOpenVar " // NOLINTNEXTLINE(performance-inefficient-string-concatenation) @@ -884,49 +917,51 @@ private: args += portp->name()+"__Vcvt"; - cfuncp->addStmtsp(createDpiTemp(portp,"__Vcvt")); + cfuncp->addStmtsp(createDpiTemp(portp, "__Vcvt")); if (portp->isNonOutput()) { - cfuncp->addStmtsp(createAssignInternalToDpi(portp,false,false,"","__Vcvt")); + cfuncp->addStmtsp(createAssignInternalToDpi( + portp, false, false, "", "__Vcvt")); } - } - } - } - } + } + } + } + } - // Store context, if needed - if (nodep->dpiContext()) { - string stmt = "Verilated::dpiContext(__Vscopep, __Vfilenamep, __Vlineno);\n"; - cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - } + // Store context, if needed + if (nodep->dpiContext()) { + string stmt = "Verilated::dpiContext(__Vscopep, __Vfilenamep, __Vlineno);\n"; + cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + } - {// Call the user function - string stmt; - if (rtnvscp) { // isFunction will no longer work as we unlinked the return var - stmt += rtnvscp->varp()->dpiArgType(true,true) + " "+rtnvscp->varp()->name()+"__Vcvt = "; - } - stmt += nodep->cname()+"("+args+");\n"; - cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); - } + { // Call the user function + string stmt; + if (rtnvscp) { // isFunction will no longer work as we unlinked the return var + stmt += (rtnvscp->varp()->dpiArgType(true, true) + + " "+rtnvscp->varp()->name()+"__Vcvt = "); + } + stmt += nodep->cname()+"("+args+");\n"; + cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); + } - // Convert output/inout arguments back to internal type - for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { + // Convert output/inout arguments back to internal type + for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { if (portp->isIO() && (portp->isWritable() || portp->isFuncReturn()) && !portp->isDpiOpenArray()) { AstVarScope* portvscp = VN_CAST(portp->user2p(), VarScope); // Remembered when we created it earlier - cfuncp->addStmtsp(createAssignDpiToInternal(portvscp,portp->name()+"__Vcvt",true)); - } - } - } + cfuncp->addStmtsp(createAssignDpiToInternal(portvscp, portp->name()+"__Vcvt", true)); + } + } + } } AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool ftaskNoInline) { - // Given a already cloned node, make a public C function, or a non-inline C function - // Probably some of this work should be done later, but... - // should the type of the function be bool/uint32/64 etc (based on lookup) or IData? - AstNode::user2ClearTree(); - AstVar* rtnvarp = NULL; - if (nodep->isFunction()) { + // Given a already cloned node, make a public C function, or a non-inline C function + // Probably some of this work should be done later, but... + // should the type of the function be bool/uint32/64 etc (based on lookup) or IData? + AstNode::user2ClearTree(); + AstVar* rtnvarp = NULL; + if (nodep->isFunction()) { AstVar* portp = VN_CAST(nodep->fvarp(), Var); if (!portp) nodep->v3fatalSrc("function without function output variable"); if (!portp->isFuncReturn()) nodep->v3error("Not marked as function return var"); @@ -935,14 +970,15 @@ private: if ((nodep->dpiImport() || nodep->dpiExport()) && portp->dtypep()->basicp() && portp->dtypep()->basicp()->keyword().isDpiUnreturnable()) { - portp->v3error("DPI function may not return type "<basicp()->prettyTypeName() + portp->v3error("DPI function may not return type " + <basicp()->prettyTypeName() <<" (IEEE 2017 35.5.5)"); - } + } portp->unlinkFrBack(); rtnvarp = portp; rtnvarp->funcLocal(true); rtnvarp->name(rtnvarp->name()+"__Vfuncrtn"); // Avoid conflict with DPI function name - } + } if (nodep->dpiImport()) { if (nodep->dpiOpenChild()) { // The parent will make the dpi proto @@ -975,200 +1011,204 @@ private: } string prefix; - if (nodep->dpiImport()) prefix = "__Vdpiimwrap_"; - else if (nodep->dpiExport()) prefix = "__Vdpiexp_"; - else if (ftaskNoInline) prefix = "__VnoInFunc_"; - // Unless public, v3Descope will not uniquify function names even if duplicate per-scope, - // so make it unique now. + if (nodep->dpiImport()) prefix = "__Vdpiimwrap_"; + else if (nodep->dpiExport()) prefix = "__Vdpiexp_"; + else if (ftaskNoInline) prefix = "__VnoInFunc_"; + // Unless public, v3Descope will not uniquify function names even if duplicate per-scope, + // so make it unique now. string suffix; // So, make them unique - if (!nodep->taskPublic()) suffix = "_"+m_scopep->nameDotless(); - AstCFunc* cfuncp = new AstCFunc(nodep->fileline(), - prefix + nodep->name() + suffix, - m_scopep, - ((nodep->taskPublic() && rtnvarp) ? rtnvarp->cPubArgType(true,true) - : "")); - // It's ok to combine imports because this is just a wrapper; duplicate wrappers can get merged. - cfuncp->dontCombine(!nodep->dpiImport()); + if (!nodep->taskPublic()) suffix = "_"+m_scopep->nameDotless(); + AstCFunc* cfuncp = new AstCFunc(nodep->fileline(), + prefix + nodep->name() + suffix, + m_scopep, + ((nodep->taskPublic() && rtnvarp) + ? rtnvarp->cPubArgType(true, true) + : "")); + // It's ok to combine imports because this is just a wrapper; + // duplicate wrappers can get merged. + cfuncp->dontCombine(!nodep->dpiImport()); cfuncp->entryPoint(!nodep->dpiImport()); cfuncp->funcPublic(nodep->taskPublic()); cfuncp->dpiExport(nodep->dpiExport()); cfuncp->dpiImportWrapper(nodep->dpiImport()); cfuncp->isStatic(!(nodep->dpiImport()||nodep->taskPublic())); cfuncp->pure(nodep->pure()); - //cfuncp->dpiImport // Not set in the wrapper - the called function has it set - if (cfuncp->dpiExport()) cfuncp->cname (nodep->cname()); + //cfuncp->dpiImport // Not set in the wrapper - the called function has it set + if (cfuncp->dpiExport()) cfuncp->cname(nodep->cname()); - bool needSyms = !nodep->dpiImport(); - if (needSyms) { - if (nodep->taskPublic()) { - // We need to get a pointer to all of our variables (may have eval'ed something else earlier) - cfuncp->addInitsp( - new AstCStmt(nodep->fileline(), - EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); - } else { - // Need symbol table - cfuncp->argTypes(EmitCBaseVisitor::symClassVar()); - } - } - if (nodep->dpiContext()) { - // First three args go to dpiContext call + bool needSyms = !nodep->dpiImport(); + if (needSyms) { + if (nodep->taskPublic()) { + // We need to get a pointer to all of our variables (may + // have eval'ed something else earlier) + cfuncp->addInitsp( + new AstCStmt(nodep->fileline(), + EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n")); + } else { + // Need symbol table + cfuncp->argTypes(EmitCBaseVisitor::symClassVar()); + } + } + if (nodep->dpiContext()) { + // First three args go to dpiContext call createInputVar(cfuncp, "__Vscopep", AstBasicDTypeKwd::SCOPEPTR); createInputVar(cfuncp, "__Vfilenamep", AstBasicDTypeKwd::CHARPTR); createInputVar(cfuncp, "__Vlineno", AstBasicDTypeKwd::INT); - } + } - if (!nodep->dpiImport()) { - cfuncp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n")); - } + if (!nodep->dpiImport()) { + cfuncp->addInitsp(new AstCStmt(nodep->fileline(), + EmitCBaseVisitor::symTopAssign()+"\n")); + } - if (nodep->dpiExport()) { - AstScopeName* snp = nodep->scopeNamep(); if (!snp) nodep->v3fatalSrc("Missing scoping context"); - snp->dpiExport(true); // The AstScopeName is really a statement(ish) for tracking, not a function - snp->unlinkFrBack(); - cfuncp->addInitsp(snp); - } + if (nodep->dpiExport()) { + AstScopeName* snp = nodep->scopeNamep(); if (!snp) nodep->v3fatalSrc("Missing scoping context"); + snp->dpiExport(true); // The AstScopeName is really a statement(ish) for tracking, not a function + snp->unlinkFrBack(); + cfuncp->addInitsp(snp); + } - // Create list of arguments and move to function - for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) { - nextp = stmtp->nextp(); + // Create list of arguments and move to function + for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp = nextp) { + nextp = stmtp->nextp(); if (AstVar* portp = VN_CAST(stmtp, Var)) { - if (portp->isIO()) { - // Move it to new function - portp->unlinkFrBack(); - portp->funcLocal(true); - cfuncp->addArgsp(portp); - } else { - // "Normal" variable, mark inside function - portp->funcLocal(true); - } + if (portp->isIO()) { + // Move it to new function + portp->unlinkFrBack(); + portp->funcLocal(true); + cfuncp->addArgsp(portp); + } else { + // "Normal" variable, mark inside function + portp->funcLocal(true); + } AstVarScope* newvscp = new AstVarScope(portp->fileline(), m_scopep, portp); - m_scopep->addVarp(newvscp); - portp->user2p(newvscp); - } - } + m_scopep->addVarp(newvscp); + portp->user2p(newvscp); + } + } - // Fake output variable if was a function. It's more efficient to - // have it last, rather than first, as the C compiler can sometimes - // avoid copying variables when calling shells if argument 1 - // remains argument 1 (which it wouldn't if a return got added). - if (rtnvarp) cfuncp->addArgsp(rtnvarp); + // Fake output variable if was a function. It's more efficient to + // have it last, rather than first, as the C compiler can sometimes + // avoid copying variables when calling shells if argument 1 + // remains argument 1 (which it wouldn't if a return got added). + if (rtnvarp) cfuncp->addArgsp(rtnvarp); - // Move body - AstNode* bodysp = nodep->stmtsp(); - if (bodysp) { bodysp->unlinkFrBackWithNext(); cfuncp->addStmtsp(bodysp); } - if (nodep->dpiImport()) { - bodyDpiImportFunc(nodep, rtnvscp, cfuncp); - } + // Move body + AstNode* bodysp = nodep->stmtsp(); + if (bodysp) { bodysp->unlinkFrBackWithNext(); cfuncp->addStmtsp(bodysp); } + if (nodep->dpiImport()) { + bodyDpiImportFunc(nodep, rtnvscp, cfuncp); + } - // Return statement - if (rtnvscp && nodep->taskPublic()) { - cfuncp->addFinalsp(new AstCReturn(rtnvscp->fileline(), - new AstVarRef(rtnvscp->fileline(), rtnvscp, false))); - } - // Replace variable refs - // Iteration requires a back, so put under temporary node - { - AstBegin* tempp = new AstBegin(cfuncp->fileline(),"[EditWrapper]",cfuncp); - TaskRelinkVisitor visit (tempp); - tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); VL_DANGLING(tempp); - } - // Delete rest of cloned task and return new func - pushDeletep(nodep); VL_DANGLING(nodep); - if (debug()>=9) { cfuncp->dumpTree(cout,"-userFunc: "); } - return cfuncp; + // Return statement + if (rtnvscp && nodep->taskPublic()) { + cfuncp->addFinalsp(new AstCReturn(rtnvscp->fileline(), + new AstVarRef(rtnvscp->fileline(), rtnvscp, false))); + } + // Replace variable refs + // Iteration requires a back, so put under temporary node + { + AstBegin* tempp = new AstBegin(cfuncp->fileline(), "[EditWrapper]", cfuncp); + TaskRelinkVisitor visit (tempp); + tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); VL_DANGLING(tempp); + } + // Delete rest of cloned task and return new func + pushDeletep(nodep); VL_DANGLING(nodep); + if (debug()>=9) { cfuncp->dumpTree(cout, "-userFunc: "); } + return cfuncp; } void iterateIntoFTask(AstNodeFTask* nodep) { - // Iterate into the FTask we are calling. Note it may be under a different - // scope then the caller, so we need to restore state. - AstScope* oldscopep = m_scopep; - InsertMode prevInsMode = m_insMode; - AstNode* prevInsStmtp = m_insStmtp; - m_scopep = m_statep->getScope(nodep); + // Iterate into the FTask we are calling. Note it may be under a different + // scope then the caller, so we need to restore state. + AstScope* oldscopep = m_scopep; + InsertMode prevInsMode = m_insMode; + AstNode* prevInsStmtp = m_insStmtp; + m_scopep = m_statep->getScope(nodep); iterate(nodep); - m_scopep = oldscopep; - m_insMode = prevInsMode; - m_insStmtp = prevInsStmtp; + m_scopep = oldscopep; + m_insMode = prevInsMode; + m_insStmtp = prevInsStmtp; } AstNode* insertBeforeStmt(AstNode* nodep, AstNode* newp) { // Return node that must be visited, if any - // See also AstNode::addBeforeStmt; this predates that function - if (debug()>=9) { nodep->dumpTree(cout,"-newstmt:"); } - if (!m_insStmtp) nodep->v3fatalSrc("Function not underneath a statement"); + // See also AstNode::addBeforeStmt; this predates that function + if (debug()>=9) { nodep->dumpTree(cout, "-newstmt:"); } + if (!m_insStmtp) nodep->v3fatalSrc("Function not underneath a statement"); AstNode* visitp = NULL; - if (m_insMode == IM_BEFORE) { - // Add the whole thing before insertAt - UINFO(5," IM_Before "<=9) { newp->dumpTree(cout,"-newfunc:"); } - m_insStmtp->addHereThisAsNext(newp); - } - else if (m_insMode == IM_AFTER) { + if (m_insMode == IM_BEFORE) { + // Add the whole thing before insertAt + UINFO(5," IM_Before "<=9) { newp->dumpTree(cout, "-newfunc:"); } + m_insStmtp->addHereThisAsNext(newp); + } + else if (m_insMode == IM_AFTER) { UINFO(5," IM_After "<addNextHere(newp); - } - else if (m_insMode == IM_WHILE_PRECOND) { + m_insStmtp->addNextHere(newp); + } + else if (m_insMode == IM_WHILE_PRECOND) { UINFO(5," IM_While_Precond "<v3fatalSrc("Insert should be under WHILE"); - whilep->addPrecondsp(newp); + if (!whilep) nodep->v3fatalSrc("Insert should be under WHILE"); + whilep->addPrecondsp(newp); visitp = newp; - } - else { - nodep->v3fatalSrc("Unknown InsertMode"); - } - m_insMode = IM_AFTER; - m_insStmtp = newp; + } + else { + nodep->v3fatalSrc("Unknown InsertMode"); + } + m_insMode = IM_AFTER; + m_insStmtp = newp; return visitp; } // VISITORS virtual void visit(AstNodeModule* nodep) { - m_modp = nodep; - m_insStmtp = NULL; - m_modNCalls = 0; + m_modp = nodep; + m_insStmtp = NULL; + m_modNCalls = 0; iterateChildren(nodep); - m_modp = NULL; + m_modp = NULL; } virtual void visit(AstTopScope* nodep) { - m_topScopep = nodep; + m_topScopep = nodep; iterateChildren(nodep); } virtual void visit(AstScope* nodep) { - m_scopep = nodep; - m_insStmtp = NULL; + m_scopep = nodep; + m_insStmtp = NULL; iterateChildren(nodep); - m_scopep = NULL; + m_scopep = NULL; } virtual void visit(AstNodeFTaskRef* nodep) { - if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?"); - iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs - UINFO(4," FTask REF "<=9) { nodep->dumpTree(cout,"-inlfunc:"); } - if (!m_scopep) nodep->v3fatalSrc("func ref not under scope"); + if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?"); + iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs + UINFO(4," FTask REF "<=9) { nodep->dumpTree(cout, "-inlfunc:"); } + if (!m_scopep) nodep->v3fatalSrc("func ref not under scope"); string namePrefix = ((VN_IS(nodep, FuncRef) ? "__Vfunc_":"__Vtask_") - +nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++)); - // Create output variable - AstVarScope* outvscp = NULL; - if (nodep->taskp()->isFunction()) { - // Not that it's a FUNCREF, but that we're calling a function (perhaps as a task) + +nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++)); + // Create output variable + AstVarScope* outvscp = NULL; + if (nodep->taskp()->isFunction()) { + // Not that it's a FUNCREF, but that we're calling a function (perhaps as a task) outvscp = createVarScope(VN_CAST(nodep->taskp()->fvarp(), Var), namePrefix+"__Vfuncout"); - } - // Create cloned statements - AstNode* beginp; - if (m_statep->ftaskNoInline(nodep->taskp())) { - // This may share VarScope's with a public task, if any. Yuk. - beginp = createNonInlinedFTask(nodep, namePrefix, outvscp); - } else { - beginp = createInlinedFTask(nodep, namePrefix, outvscp); - } - // Replace the ref + } + // Create cloned statements + AstNode* beginp; + if (m_statep->ftaskNoInline(nodep->taskp())) { + // This may share VarScope's with a public task, if any. Yuk. + beginp = createNonInlinedFTask(nodep, namePrefix, outvscp); + } else { + beginp = createInlinedFTask(nodep, namePrefix, outvscp); + } + // Replace the ref AstNode* visitp = NULL; if (VN_IS(nodep, FuncRef)) { - if (!nodep->taskp()->isFunction()) nodep->v3fatalSrc("func reference to non-function"); + if (!nodep->taskp()->isFunction()) nodep->v3fatalSrc("func reference to non-function"); AstVarRef* outrefp = new AstVarRef(nodep->fileline(), outvscp, false); - nodep->replaceWith(outrefp); - // Insert new statements + nodep->replaceWith(outrefp); + // Insert new statements visitp = insertBeforeStmt(nodep, beginp); } else { if (nodep->taskp()->isFunction()) { @@ -1179,95 +1219,97 @@ private: // but if so we want to simply ignore the function result nodep->replaceWith(beginp); } - // Cleanup - nodep->deleteTree(); VL_DANGLING(nodep); - UINFO(4," FTask REF Done.\n"); + // Cleanup + nodep->deleteTree(); VL_DANGLING(nodep); + UINFO(4," FTask REF Done.\n"); // Visit nodes that normal iteration won't find if (visitp) iterateAndNextNull(visitp); } virtual void visit(AstNodeFTask* nodep) { - UINFO(4," Inline "<stmtsp(); // Might be null if no statements, but we won't use it - if (!nodep->user1SetOnce()) { // Just one creation needed per function - // Expand functions in it - int modes = 0; - if (nodep->dpiImport()) modes++; - if (nodep->dpiExport()) modes++; - if (nodep->taskPublic()) modes++; - if (modes > 1) nodep->v3error("Cannot mix DPI import, DPI export and/or public on same function: "<prettyName()); + UINFO(4," Inline "<stmtsp(); // Might be null if no statements, but we won't use it + if (!nodep->user1SetOnce()) { // Just one creation needed per function + // Expand functions in it + int modes = 0; + if (nodep->dpiImport()) modes++; + if (nodep->dpiExport()) modes++; + if (nodep->taskPublic()) modes++; + if (modes > 1) nodep->v3error("Cannot mix DPI import, DPI export and/or public on same function: " + <prettyName()); - if (nodep->dpiImport() || nodep->dpiExport() - || nodep->taskPublic() || m_statep->ftaskNoInline(nodep)) { - // Clone it first, because we may have later FTaskRef's that still need - // the original version. - if (m_statep->ftaskNoInline(nodep)) m_statep->checkPurity(nodep); - AstNodeFTask* clonedFuncp = nodep->cloneTree(false); - AstCFunc* cfuncp = makeUserFunc(clonedFuncp, m_statep->ftaskNoInline(nodep)); + if (nodep->dpiImport() || nodep->dpiExport() + || nodep->taskPublic() || m_statep->ftaskNoInline(nodep)) { + // Clone it first, because we may have later FTaskRef's that still need + // the original version. + if (m_statep->ftaskNoInline(nodep)) m_statep->checkPurity(nodep); + AstNodeFTask* clonedFuncp = nodep->cloneTree(false); + AstCFunc* cfuncp = makeUserFunc(clonedFuncp, m_statep->ftaskNoInline(nodep)); if (cfuncp) { nodep->addNextHere(cfuncp); if (nodep->dpiImport() || m_statep->ftaskNoInline(nodep)) { m_statep->ftaskCFuncp(nodep, cfuncp); } iterateIntoFTask(clonedFuncp); // Do the clone too - } - } + } + } - // Any variables inside the function still have varscopes pointing to them. - // We're going to delete the vars, so delete the varscopes. - if (nodep->isFunction()) { + // Any variables inside the function still have varscopes pointing to them. + // We're going to delete the vars, so delete the varscopes. + if (nodep->isFunction()) { if (AstVar* portp = VN_CAST(nodep->fvarp(), Var)) { - AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp); - UINFO(9," funcremovevsc "<unlinkFrBack()); VL_DANGLING(vscp); - } - } - for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) { - nextp = stmtp->nextp(); + AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp); + UINFO(9," funcremovevsc "<unlinkFrBack()); VL_DANGLING(vscp); + } + } + for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp = nextp) { + nextp = stmtp->nextp(); if (AstVar* portp = VN_CAST(stmtp, Var)) { - AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp); - UINFO(9," funcremovevsc "<unlinkFrBack()); VL_DANGLING(vscp); - } - } - // Just push for deletion, as other references to func may - // remain until visitor exits - nodep->unlinkFrBack(); - pushDeletep(nodep); VL_DANGLING(nodep); - } - m_insMode = prevInsMode; - m_insStmtp = prevInsStmtp; + AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp); + UINFO(9," funcremovevsc "<unlinkFrBack()); VL_DANGLING(vscp); + } + } + // Just push for deletion, as other references to func may + // remain until visitor exits + nodep->unlinkFrBack(); + pushDeletep(nodep); VL_DANGLING(nodep); + } + m_insMode = prevInsMode; + m_insStmtp = prevInsStmtp; } virtual void visit(AstWhile* nodep) { - // Special, as statements need to be put in different places - // Preconditions insert first just before themselves (the normal rule for other statement types) - m_insStmtp = NULL; // First thing should be new statement + // Special, as statements need to be put in different places + // Preconditions insert first just before themselves (the normal + // rule for other statement types) + m_insStmtp = NULL; // First thing should be new statement iterateAndNextNull(nodep->precondsp()); - // Conditions insert first at end of precondsp. - m_insMode = IM_WHILE_PRECOND; - m_insStmtp = nodep; + // Conditions insert first at end of precondsp. + m_insMode = IM_WHILE_PRECOND; + m_insStmtp = nodep; iterateAndNextNull(nodep->condp()); - // Body insert just before themselves - m_insStmtp = NULL; // First thing should be new statement + // Body insert just before themselves + m_insStmtp = NULL; // First thing should be new statement iterateAndNextNull(nodep->bodysp()); iterateAndNextNull(nodep->incsp()); - // Done the loop - m_insStmtp = NULL; // Next thing should be new statement + // Done the loop + m_insStmtp = NULL; // Next thing should be new statement } virtual void visit(AstNodeFor* nodep) { - nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin.cpp"); + nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin.cpp"); } virtual void visit(AstNodeStmt* nodep) { if (!nodep->isStatement()) { iterateChildren(nodep); return; } - m_insMode = IM_BEFORE; - m_insStmtp = nodep; + m_insMode = IM_BEFORE; + m_insStmtp = nodep; iterateChildren(nodep); - m_insStmtp = NULL; // Next thing should be new statement + m_insStmtp = NULL; // Next thing should be new statement } //-------------------- // Default: Just iterate @@ -1278,14 +1320,14 @@ private: public: // CONSTUCTORS TaskVisitor(AstNetlist* nodep, TaskStateVisitor* statep) - : m_statep(statep) { - m_modp = NULL; - m_topScopep = NULL; - m_scopep = NULL; + : m_statep(statep) { + m_modp = NULL; + m_topScopep = NULL; + m_scopep = NULL; m_insMode = IM_BEFORE; - m_insStmtp = NULL; + m_insStmtp = NULL; m_modNCalls = 0; - AstNode::user1ClearTree(); + AstNode::user1ClearTree(); iterate(nodep); } virtual ~TaskVisitor() {} @@ -1295,7 +1337,8 @@ public: // Task class functions V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp) { - // Output list will be in order of the port declaration variables (so func calls are made right in C) + // Output list will be in order of the port declaration variables (so + // func calls are made right in C) // Missing pin/expr? We return (pinvar, NULL) // Extra pin/expr? We clean it up @@ -1305,116 +1348,121 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp) if (!nodep->taskp()) nodep->v3fatalSrc("unlinked"); // Find ports - //map name_to_pinnum; int tpinnum = 0; AstVar* sformatp = NULL; for (AstNode* stmtp = taskStmtsp; stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = VN_CAST(stmtp, Var)) { - if (portp->isIO()) { + if (portp->isIO()) { tconnects.push_back(make_pair(portp, static_cast(NULL))); - nameToIndex.insert(make_pair(portp->name(), tpinnum)); // For name based connections - tpinnum++; - if (portp->attrSFormat()) { - sformatp = portp; - } else if (sformatp) { - nodep->v3error("/*verilator sformat*/ can only be applied to last argument of a function"); - } - } - } + nameToIndex.insert(make_pair(portp->name(), tpinnum)); // For name based connections + tpinnum++; + if (portp->attrSFormat()) { + sformatp = portp; + } else if (sformatp) { + nodep->v3error("/*verilator sformat*/ can only be applied to last argument of a function"); + } + } + } } // Find pins int ppinnum = 0; bool reorganize = false; for (AstNode* nextp, *pinp = nodep->pinsp(); pinp; pinp=nextp) { - nextp = pinp->nextp(); + nextp = pinp->nextp(); AstArg* argp = VN_CAST(pinp, Arg); if (!argp) pinp->v3fatalSrc("Non-arg under ftask reference"); - if (argp->name() != "") { - // By name - NameToIndex::iterator it = nameToIndex.find(argp->name()); - if (it == nameToIndex.end()) { - pinp->v3error("No such argument '"<prettyName() - <<"' in function call to "<taskp()->prettyTypeName()); - // We'll just delete it; seems less error prone than making a false argument - pinp->unlinkFrBack()->deleteTree(); VL_DANGLING(pinp); - } else { - if (tconnects[it->second].second) { - pinp->v3error("Duplicate argument '"<prettyName() - <<"' in function call to "<taskp()->prettyTypeName()); - } - argp->name(""); // Can forget name as will add back in pin order - tconnects[it->second].second = argp; - reorganize = true; - } - } else { // By pin number - if (ppinnum >= tpinnum) { - if (sformatp) { + if (argp->name() != "") { + // By name + NameToIndex::iterator it = nameToIndex.find(argp->name()); + if (it == nameToIndex.end()) { + pinp->v3error("No such argument '"<prettyName() + <<"' in function call to "<taskp()->prettyTypeName()); + // We'll just delete it; seems less error prone than making a false argument + pinp->unlinkFrBack()->deleteTree(); VL_DANGLING(pinp); + } else { + if (tconnects[it->second].second) { + pinp->v3error("Duplicate argument '"<prettyName() + <<"' in function call to "<taskp()->prettyTypeName()); + } + argp->name(""); // Can forget name as will add back in pin order + tconnects[it->second].second = argp; + reorganize = true; + } + } else { // By pin number + if (ppinnum >= tpinnum) { + if (sformatp) { tconnects.push_back(make_pair(sformatp, static_cast(NULL))); - tconnects[ppinnum].second = argp; - tpinnum++; - } else { - pinp->v3error("Too many arguments in function call to "<taskp()->prettyTypeName()); - // We'll just delete it; seems less error prone than making a false argument - pinp->unlinkFrBack()->deleteTree(); VL_DANGLING(pinp); - } - } else { - tconnects[ppinnum].second = argp; - } - } - ppinnum++; + tconnects[ppinnum].second = argp; + tpinnum++; + } else { + pinp->v3error("Too many arguments in function call to " + <taskp()->prettyTypeName()); + // We'll just delete it; seems less error prone than making a false argument + pinp->unlinkFrBack()->deleteTree(); VL_DANGLING(pinp); + } + } else { + tconnects[ppinnum].second = argp; + } + } + ppinnum++; } // Connect missing ones for (int i=0; iexprp()) { - AstNode* newvaluep = NULL; - if (!portp->valuep()) { - nodep->v3error("Missing argument on non-defaulted argument '"<prettyName() - <<"' in function call to "<taskp()->prettyTypeName()); - newvaluep = new AstConst(nodep->fileline(), AstConst::Unsized32(), 0); + AstVar* portp = tconnects[i].first; + if (!tconnects[i].second || !tconnects[i].second->exprp()) { + AstNode* newvaluep = NULL; + if (!portp->valuep()) { + nodep->v3error("Missing argument on non-defaulted argument '"<prettyName() + <<"' in function call to "<taskp()->prettyTypeName()); + newvaluep = new AstConst(nodep->fileline(), AstConst::Unsized32(), 0); } else if (!VN_IS(portp->valuep(), Const)) { - // The default value for this port might be a constant - // expression that hasn't been folded yet. Try folding it - // now; we don't have much to lose if it fails. - newvaluep = V3Const::constifyParamsEdit(portp->valuep()); + // The default value for this port might be a constant + // expression that hasn't been folded yet. Try folding it + // now; we don't have much to lose if it fails. + newvaluep = V3Const::constifyParamsEdit(portp->valuep()); if (!VN_IS(newvaluep, Const)) { - // Problem otherwise is we might have a varref, task call, or something else that only - // makes sense in the domain of the function, not the callee. - nodep->v3error("Unsupported: Non-constant default value in missing argument '"<prettyName() - <<"' in function call to "<taskp()->prettyTypeName()); - newvaluep = new AstConst(nodep->fileline(), AstConst::Unsized32(), 0); - } - else { - newvaluep = newvaluep->cloneTree(true); - } - } else { - newvaluep = portp->valuep()->cloneTree(true); - } - // To avoid problems with callee needing to know to deleteTree or not, we make this into a pin - UINFO(9,"Default pin for "<fileline(), portp->name(), newvaluep); - if (tconnects[i].second) { // Have a "NULL" pin already defined for it - tconnects[i].second->unlinkFrBack()->deleteTree(); tconnects[i].second=NULL; - } - tconnects[i].second = newp; - reorganize = true; - } - if (tconnects[i].second) { UINFO(9,"Connect "< "< NONE"<v3error("Unsupported: Non-constant default value in missing argument '" + <prettyName() + <<"' in function call to "<taskp()->prettyTypeName()); + newvaluep = new AstConst(nodep->fileline(), AstConst::Unsized32(), 0); + } + else { + newvaluep = newvaluep->cloneTree(true); + } + } else { + newvaluep = portp->valuep()->cloneTree(true); + } + // To avoid problems with callee needing to know to deleteTree + // or not, we make this into a pin + UINFO(9,"Default pin for "<fileline(), portp->name(), newvaluep); + if (tconnects[i].second) { // Have a "NULL" pin already defined for it + tconnects[i].second->unlinkFrBack()->deleteTree(); tconnects[i].second = NULL; + } + tconnects[i].second = newp; + reorganize = true; + } + if (tconnects[i].second) { UINFO(9,"Connect "< "< NONE"<pinsp()) nodep->pinsp()->unlinkFrBack(); // Must unlink each pin, not all pins linked together as one list - for (int i=0; iv3fatalSrc("Lost argument in func conversion"); - nodep->addPinsp(argp); - } + // To simplify downstream, put argument list back into pure pinnumber ordering + while (nodep->pinsp()) nodep->pinsp()->unlinkFrBack(); // Must unlink each pin, not all pins linked together as one list + for (int i=0; iv3fatalSrc("Lost argument in func conversion"); + nodep->addPinsp(argp); + } } if (debug()>=9) { - nodep->dumpTree(cout,"-ftref-out: "); + nodep->dumpTree(cout, "-ftref-out: "); for (int i=0; iname(); } virtual string dotColor() const { return slow()?"yellowGreen":"green"; } bool activityCodeValid() const { return m_activityCodeValid; } vlsint32_t activityCode() const { return m_activityCode; } bool activityAlways() const { return activityCode()==ACTIVITY_ALWAYS; } - void activityCode(vlsint32_t code) { m_activityCode=code; m_activityCodeValid=true;} + void activityCode(vlsint32_t code) { m_activityCode = code; m_activityCodeValid = true;} bool slow() const { return m_slow; } - void slow(bool flag) { if (!flag) m_slow=false; } + void slow(bool flag) { if (!flag) m_slow = false; } }; class TraceCFuncVertex : public V3GraphVertex { - AstCFunc* m_nodep; + AstCFunc* m_nodep; public: TraceCFuncVertex(V3Graph* graphp, AstCFunc* nodep) - : V3GraphVertex(graphp), m_nodep(nodep) { + : V3GraphVertex(graphp), m_nodep(nodep) { } virtual ~TraceCFuncVertex() {} // ACCESSORS @@ -118,11 +118,11 @@ public: }; class TraceTraceVertex : public V3GraphVertex { - AstTraceInc* m_nodep; // TRACEINC this represents - TraceTraceVertex* m_duplicatep; // NULL, or other vertex with the real code() that duplicates this one + AstTraceInc* m_nodep; // TRACEINC this represents + TraceTraceVertex* m_duplicatep; // NULL, or other vertex with the real code() that duplicates this one public: TraceTraceVertex(V3Graph* graphp, AstTraceInc* nodep) - : V3GraphVertex(graphp), m_nodep(nodep), m_duplicatep(NULL) {} + : V3GraphVertex(graphp), m_nodep(nodep), m_duplicatep(NULL) {} virtual ~TraceTraceVertex() {} // ACCESSORS AstTraceInc* nodep() const { return m_nodep; } @@ -131,15 +131,16 @@ public: virtual FileLine* fileline() const { return nodep()->fileline(); } TraceTraceVertex* duplicatep() const { return m_duplicatep; } void duplicatep(TraceTraceVertex* dupp) { - if (duplicatep()) nodep()->v3fatalSrc("Assigning duplicatep() to already duplicated node"); - m_duplicatep=dupp; } + if (duplicatep()) nodep()->v3fatalSrc("Assigning duplicatep() to already duplicated node"); + m_duplicatep = dupp; + } }; class TraceVarVertex : public V3GraphVertex { - AstVarScope* m_nodep; + AstVarScope* m_nodep; public: TraceVarVertex(V3Graph* graphp, AstVarScope* nodep) - : V3GraphVertex(graphp), m_nodep(nodep) {} + : V3GraphVertex(graphp), m_nodep(nodep) {} virtual ~TraceVarVertex() {} // ACCESSORS AstVarScope* nodep() const { return m_nodep; } @@ -155,162 +156,167 @@ class TraceVisitor : public EmitCBaseVisitor { private: // NODE STATE // V3Hashed - // Ast*::user4() // V3Hashed calculation + // Ast*::user4() // V3Hashed calculation // Cleared entire netlist - // AstCFunc::user1() // V3GraphVertex* for this node - // AstTraceInc::user1() // V3GraphVertex* for this node - // AstVarScope::user1() // V3GraphVertex* for this node - // AstCCall::user2() // bool; walked next list for other ccalls - // Ast*::user3() // TraceActivityVertex* for this node - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; - //AstUser4InUse In V3Hashed + // AstCFunc::user1() // V3GraphVertex* for this node + // AstTraceInc::user1() // V3GraphVertex* for this node + // AstVarScope::user1() // V3GraphVertex* for this node + // AstCCall::user2() // bool; walked next list for other ccalls + // Ast*::user3() // TraceActivityVertex* for this node + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; + //AstUser4InUse In V3Hashed // STATE - AstNodeModule* m_topModp; // Module to add variables to - AstScope* m_highScopep; // Scope to add variables to - AstCFunc* m_funcp; // C function adding to graph - AstTraceInc* m_tracep; // Trace function adding to graph - AstCFunc* m_initFuncp; // Trace function we add statements to - AstCFunc* m_fullFuncp; // Trace function we add statements to - AstCFunc* m_fullSubFuncp; // Trace function we add statements to (under full) - int m_fullSubStmts; // Statements under function being built - AstCFunc* m_chgFuncp; // Trace function we add statements to - AstCFunc* m_chgSubFuncp; // Trace function we add statements to (under full) - AstNode* m_chgSubParentp;// Which node has call to m_chgSubFuncp - int m_chgSubStmts; // Statements under function being built - AstVarScope* m_activityVscp; // Activity variable + AstNodeModule* m_topModp; // Module to add variables to + AstScope* m_highScopep; // Scope to add variables to + AstCFunc* m_funcp; // C function adding to graph + AstTraceInc* m_tracep; // Trace function adding to graph + AstCFunc* m_initFuncp; // Trace function we add statements to + AstCFunc* m_fullFuncp; // Trace function we add statements to + AstCFunc* m_fullSubFuncp; // Trace function we add statements to (under full) + int m_fullSubStmts; // Statements under function being built + AstCFunc* m_chgFuncp; // Trace function we add statements to + AstCFunc* m_chgSubFuncp; // Trace function we add statements to (under full) + AstNode* m_chgSubParentp;// Which node has call to m_chgSubFuncp + int m_chgSubStmts; // Statements under function being built + AstVarScope* m_activityVscp; // Activity variable uint32_t m_activityNumber; // Count of fields in activity variable - uint32_t m_code; // Trace ident code# being assigned - V3Graph m_graph; // Var/CFunc tracking - TraceActivityVertex* m_alwaysVtxp; // "Always trace" vertex - bool m_finding; // Pass one of algorithm? - int m_funcNum; // Function number being built + uint32_t m_code; // Trace ident code# being assigned + V3Graph m_graph; // Var/CFunc tracking + TraceActivityVertex* m_alwaysVtxp; // "Always trace" vertex + bool m_finding; // Pass one of algorithm? + int m_funcNum; // Function number being built - V3Double0 m_statChgSigs; // Statistic tracking - V3Double0 m_statUniqSigs; // Statistic tracking - V3Double0 m_statUniqCodes;// Statistic tracking + V3Double0 m_statChgSigs; // Statistic tracking + V3Double0 m_statUniqSigs; // Statistic tracking + V3Double0 m_statUniqCodes;// Statistic tracking // METHODS VL_DEBUG_FUNC; // Declare debug() void detectDuplicates() { - UINFO(9,"Finding duplicates\n"); - // Note uses user4 - V3Hashed hashed; // Duplicate code detection - // Hash all of the values the traceIncs need - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { - AstTraceInc* nodep = vvertexp->nodep(); - if (nodep->valuep()) { + UINFO(9,"Finding duplicates\n"); + // Note uses user4 + V3Hashed hashed; // Duplicate code detection + // Hash all of the values the traceIncs need + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + AstTraceInc* nodep = vvertexp->nodep(); + if (nodep->valuep()) { if (nodep->valuep()->backp() != nodep) { nodep->v3fatalSrc("Trace duplicate back needs consistency," " so we can map duplicates back to TRACEINCs"); } - hashed.hash(nodep->valuep()); - UINFO(8, " Hashed "<valuep())<<" "<valuep()); + UINFO(8, " Hashed "<valuep()) + <<" "<valuep()) == hashed.end()) { - hashed.hashAndInsert(nodep->valuep()); - } - } - } - } - // Find if there are any duplicates - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { - AstTraceInc* nodep = vvertexp->nodep(); - if (nodep->valuep() && !vvertexp->duplicatep()) { - V3Hashed::iterator dupit = hashed.findDuplicate(nodep->valuep()); - if (dupit != hashed.end()) { - AstTraceInc* dupincp = VN_CAST(hashed.iteratorNodep(dupit)->backp(), TraceInc); - if (!dupincp) nodep->v3fatalSrc("Trace duplicate of wrong type"); - TraceTraceVertex* dupvertexp = dynamic_cast(dupincp->user1u().toGraphVertex()); - UINFO(8," Orig "<duplicatep(dupvertexp); - } - } - } - } - hashed.clear(); + // Just keep one node in the map and point all duplicates to this node + if (hashed.findDuplicate(nodep->valuep()) == hashed.end()) { + hashed.hashAndInsert(nodep->valuep()); + } + } + } + } + // Find if there are any duplicates + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + AstTraceInc* nodep = vvertexp->nodep(); + if (nodep->valuep() && !vvertexp->duplicatep()) { + V3Hashed::iterator dupit = hashed.findDuplicate(nodep->valuep()); + if (dupit != hashed.end()) { + AstTraceInc* dupincp + = VN_CAST(hashed.iteratorNodep(dupit)->backp(), TraceInc); + if (!dupincp) nodep->v3fatalSrc("Trace duplicate of wrong type"); + TraceTraceVertex* dupvertexp + = dynamic_cast(dupincp->user1u().toGraphVertex()); + UINFO(8," Orig "<duplicatep(dupvertexp); + } + } + } + } + hashed.clear(); } void graphSimplify() { - // Remove all variable nodes - for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { - nextp = itp->verticesNextp(); - if (TraceVarVertex* vvertexp = dynamic_cast(itp)) { - vvertexp->rerouteEdges(&m_graph); - vvertexp->unlinkDelete(&m_graph); - } - } - // Remove multiple variables connecting funcs to traces - // We do this twice, as then we have fewer edges to multiply out in the below expansion. - m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); - // Remove all Cfunc nodes - for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { - nextp = itp->verticesNextp(); - if (TraceCFuncVertex* vvertexp = dynamic_cast(itp)) { - vvertexp->rerouteEdges(&m_graph); - vvertexp->unlinkDelete(&m_graph); - } - } + // Remove all variable nodes + for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { + nextp = itp->verticesNextp(); + if (TraceVarVertex* vvertexp = dynamic_cast(itp)) { + vvertexp->rerouteEdges(&m_graph); + vvertexp->unlinkDelete(&m_graph); + } + } + // Remove multiple variables connecting funcs to traces + // We do this twice, as then we have fewer edges to multiply out in the below expansion. + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + // Remove all Cfunc nodes + for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { + nextp = itp->verticesNextp(); + if (TraceCFuncVertex* vvertexp = dynamic_cast(itp)) { + vvertexp->rerouteEdges(&m_graph); + vvertexp->unlinkDelete(&m_graph); + } + } - // Remove multiple variables connecting funcs to traces - m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); + // Remove multiple variables connecting funcs to traces + m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); - // If there are any edges from a always, keep only the always - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { - V3GraphEdge* alwaysEdgep = NULL; - for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { - TraceActivityVertex* actVtxp = dynamic_cast(edgep->fromp()); - if (!actVtxp) vvertexp->nodep()->v3fatalSrc("Tracing a node with FROM non activity"); - if (actVtxp->activityAlways()) { - alwaysEdgep = edgep; - break; - } - } - if (alwaysEdgep) { - for (V3GraphEdge* nextp, *edgep = vvertexp->inBeginp(); edgep; edgep=nextp) { - nextp = edgep->inNextp(); - if (edgep!=alwaysEdgep) edgep->unlinkDelete(); - } - } - } - } + // If there are any edges from a always, keep only the always + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + V3GraphEdge* alwaysEdgep = NULL; + for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + TraceActivityVertex* actVtxp + = dynamic_cast(edgep->fromp()); + if (!actVtxp) vvertexp->nodep()->v3fatalSrc("Tracing a node with FROM non activity"); + if (actVtxp->activityAlways()) { + alwaysEdgep = edgep; + break; + } + } + if (alwaysEdgep) { + for (V3GraphEdge* nextp, *edgep = vvertexp->inBeginp(); edgep; edgep=nextp) { + nextp = edgep->inNextp(); + if (edgep!=alwaysEdgep) edgep->unlinkDelete(); + } + } + } + } - // Activity points with no outputs can be removed - for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { - nextp = itp->verticesNextp(); - if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { - if (!vvertexp->outBeginp()) { - vvertexp->unlinkDelete(&m_graph); - } - } - } + // Activity points with no outputs can be removed + for (V3GraphVertex* nextp, *itp = m_graph.verticesBeginp(); itp; itp=nextp) { + nextp = itp->verticesNextp(); + if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { + if (!vvertexp->outBeginp()) { + vvertexp->unlinkDelete(&m_graph); + } + } + } } void assignActivity() { - // Select activity numbers and put into each CFunc vertex + // Select activity numbers and put into each CFunc vertex m_activityNumber = 1; // Note 0 indicates "slow" - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { - if (!vvertexp->activityCodeValid()) { - if (vvertexp->slow()) { - // If all functions in the calls are slow, we'll share the same code. - // This makes us need less activityNumbers and so speeds up the fast path. - vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW); - } else { + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { + if (!vvertexp->activityCodeValid()) { + if (vvertexp->slow()) { + // If all functions in the calls are slow, we'll share the same code. + // This makes us need less activityNumbers and so speeds up the fast path. + vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW); + } else { vvertexp->activityCode(m_activityNumber++); - } - } - } - } + } + } + } + } AstVar* newvarp; if (v3Global.opt.mtasks()) { @@ -339,23 +345,23 @@ private: newvarp = new AstVar(m_chgFuncp->fileline(), AstVarType::MODULETEMP, "__Vm_traceActivity", VFlagBitPacked(), activityBits); } - m_topModp->addStmtp(newvarp); - AstVarScope* newvscp = new AstVarScope(newvarp->fileline(), m_highScopep, newvarp); - m_highScopep->addVarp(newvscp); - m_activityVscp = newvscp; + m_topModp->addStmtp(newvarp); + AstVarScope* newvscp = new AstVarScope(newvarp->fileline(), m_highScopep, newvarp); + m_highScopep->addVarp(newvscp); + m_activityVscp = newvscp; - // Insert activity setter - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { - if (!vvertexp->activityAlways()) { - FileLine* fl = vvertexp->insertp()->fileline(); - uint32_t acode = vvertexp->activityCode(); - vvertexp->insertp()->addNextHere + // Insert activity setter + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceActivityVertex* vvertexp = dynamic_cast(itp)) { + if (!vvertexp->activityAlways()) { + FileLine* fl = vvertexp->insertp()->fileline(); + uint32_t acode = vvertexp->activityCode(); + vvertexp->insertp()->addNextHere (new AstAssign(fl, selectActivity(fl, acode, true), new AstConst(fl, AstConst::LogicTrue()))); - } - } - } + } + } + } } AstNode* selectActivity(FileLine* flp, uint32_t acode, bool lvalue) { @@ -369,139 +375,144 @@ private: } AstCFunc* newCFunc(AstCFuncType type, const string& name, AstCFunc* basep) { - AstCFunc* funcp = new AstCFunc(basep->fileline(), name, basep->scopep()); - funcp->slow(basep->slow()); - funcp->argTypes(EmitCBaseVisitor::symClassVar() - +", "+v3Global.opt.traceClassBase()+"* vcdp, uint32_t code"); - funcp->funcType(type); - funcp->symProlog(true); - basep->addNext(funcp); - UINFO(5," Newfunc "<fileline(), name, basep->scopep()); + funcp->slow(basep->slow()); + funcp->argTypes(EmitCBaseVisitor::symClassVar() + +", "+v3Global.opt.traceClassBase()+"* vcdp, uint32_t code"); + funcp->funcType(type); + funcp->symProlog(true); + basep->addNext(funcp); + UINFO(5," Newfunc "<name()+"__"+cvtToStr(++m_funcNum); - AstCFunc* funcp = NULL; - if (basep->funcType()==AstCFuncType::TRACE_FULL) { - funcp = newCFunc(AstCFuncType::TRACE_FULL_SUB, name, basep); - } else if (basep->funcType()==AstCFuncType::TRACE_CHANGE) { - funcp = newCFunc(AstCFuncType::TRACE_CHANGE_SUB, name, basep); - } else { - basep->v3fatalSrc("Strange base function type"); - } - AstCCall* callp = new AstCCall(funcp->fileline(), funcp); - callp->argTypes("vlSymsp, vcdp, code"); + string name = basep->name()+"__"+cvtToStr(++m_funcNum); + AstCFunc* funcp = NULL; + if (basep->funcType()==AstCFuncType::TRACE_FULL) { + funcp = newCFunc(AstCFuncType::TRACE_FULL_SUB, name, basep); + } else if (basep->funcType()==AstCFuncType::TRACE_CHANGE) { + funcp = newCFunc(AstCFuncType::TRACE_CHANGE_SUB, name, basep); + } else { + basep->v3fatalSrc("Strange base function type"); + } + AstCCall* callp = new AstCCall(funcp->fileline(), funcp); + callp->argTypes("vlSymsp, vcdp, code"); if (VN_IS(callfromp, CFunc)) { VN_CAST(callfromp, CFunc)->addStmtsp(callp); } else if (VN_IS(callfromp, If)) { VN_CAST(callfromp, If)->addIfsp(callp); - } else { - callfromp->v3fatalSrc("Unknown caller node type"); // Where to add it?? - } - return funcp; + } else { + callfromp->v3fatalSrc("Unknown caller node type"); // Where to add it?? + } + return funcp; } void addToChgSub(AstNode* underp, AstNode* stmtsp) { - if (!m_chgSubFuncp - || (m_chgSubParentp != underp) - || (m_chgSubStmts && v3Global.opt.outputSplitCTrace() - && m_chgSubStmts > v3Global.opt.outputSplitCTrace())) { - m_chgSubFuncp = newCFuncSub(m_chgFuncp, underp); - m_chgSubParentp = underp; - m_chgSubStmts = 0; - } - m_chgSubFuncp->addStmtsp(stmtsp); - m_chgSubStmts += EmitCBaseCounterVisitor(stmtsp).count(); + if (!m_chgSubFuncp + || (m_chgSubParentp != underp) + || (m_chgSubStmts && v3Global.opt.outputSplitCTrace() + && m_chgSubStmts > v3Global.opt.outputSplitCTrace())) { + m_chgSubFuncp = newCFuncSub(m_chgFuncp, underp); + m_chgSubParentp = underp; + m_chgSubStmts = 0; + } + m_chgSubFuncp->addStmtsp(stmtsp); + m_chgSubStmts += EmitCBaseCounterVisitor(stmtsp).count(); } void putTracesIntoTree() { - // Form a sorted list of the traces we are interested in - UINFO(9,"Making trees\n"); + // Form a sorted list of the traces we are interested in + UINFO(9,"Making trees\n"); typedef std::set ActCodeSet; // All activity numbers applying to a given trace typedef std::multimap TraceVec; // For activity set, what traces apply - TraceVec traces; + TraceVec traces; - // Form sort structure - // If a trace doesn't have activity, it's constant, and we don't need to track changes on it. - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { - ActCodeSet actset; - UINFO(9," Add to sort: "<=9) vvertexp->nodep()->dumpTree(cout,"- trnode: "); - for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { - TraceActivityVertex* cfvertexp = dynamic_cast(edgep->fromp()); - if (!cfvertexp) vvertexp->nodep()->v3fatalSrc("Should have been function pointing to this trace"); - UINFO(9," Activity: "<activityAlways()) { - // If code 0, we always trace; ignore other codes - actset.clear(); - actset.insert(TraceActivityVertex::ACTIVITY_ALWAYS); - break; - } else { - uint32_t acode = cfvertexp->activityCode(); - if (actset.find(acode) == actset.end()) { - actset.insert(acode); - } - } - } - // If a trace doesn't have activity, it's constant, and we don't need to track changes on it. - // We put constants and non-changers last, as then the prevvalue vector is more compacted - if (actset.empty()) actset.insert(TraceActivityVertex::ACTIVITY_NEVER); - traces.insert(make_pair(actset, vvertexp)); - } - } + // Form sort structure + // If a trace doesn't have activity, it's constant, and we don't + // need to track changes on it. + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + if (TraceTraceVertex* vvertexp = dynamic_cast(itp)) { + ActCodeSet actset; + UINFO(9," Add to sort: "<=9) vvertexp->nodep()->dumpTree(cout, "- trnode: "); + for (V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { + TraceActivityVertex* cfvertexp + = dynamic_cast(edgep->fromp()); + if (!cfvertexp) vvertexp->nodep()->v3fatalSrc("Should have been function pointing to this trace"); + UINFO(9," Activity: "<activityAlways()) { + // If code 0, we always trace; ignore other codes + actset.clear(); + actset.insert(TraceActivityVertex::ACTIVITY_ALWAYS); + break; + } else { + uint32_t acode = cfvertexp->activityCode(); + if (actset.find(acode) == actset.end()) { + actset.insert(acode); + } + } + } + // If a trace doesn't have activity, it's constant, and we + // don't need to track changes on it. + // We put constants and non-changers last, as then the + // prevvalue vector is more compacted + if (actset.empty()) actset.insert(TraceActivityVertex::ACTIVITY_NEVER); + traces.insert(make_pair(actset, vvertexp)); + } + } - // Our keys are now sorted to have same activity number adjacent, - // then by trace order. (Better would be execution order for cache efficiency....) - // Last are constants and non-changers, as then the last value vector is more compact + // Our keys are now sorted to have same activity number adjacent, + // then by trace order. (Better would be execution order for cache efficiency....) + // Last are constants and non-changers, as then the last value vector is more compact - // Put TRACEs back into the tree - const ActCodeSet* lastactp = NULL; - AstNode* ifnodep = NULL; - for (TraceVec::iterator it = traces.begin(); it!=traces.end(); ++it) { - const ActCodeSet& actset = it->first; - TraceTraceVertex* vvertexp = it->second; - UINFO(9," Done sort: "<nodep(), needChg); - if (addp) { // Else no activity or duplicate - if (actset.find(TraceActivityVertex::ACTIVITY_NEVER) != actset.end()) { - vvertexp->nodep()->v3fatalSrc("If never, needChg=0 and shouldn't need to add."); - } else if (actset.find(TraceActivityVertex::ACTIVITY_ALWAYS) != actset.end()) { - // Must always set it; add to base of function - addToChgSub(m_chgFuncp, addp); - } else if (lastactp && actset == *lastactp && ifnodep) { - // Add to last statement we built - addToChgSub(ifnodep, addp); - } else { - // Build a new IF statement - FileLine* fl = addp->fileline(); - AstNode* condp = NULL; - for (ActCodeSet::const_iterator csit = actset.begin(); csit!=actset.end(); ++csit) { - uint32_t acode = *csit; + // Put TRACEs back into the tree + const ActCodeSet* lastactp = NULL; + AstNode* ifnodep = NULL; + for (TraceVec::iterator it = traces.begin(); it!=traces.end(); ++it) { + const ActCodeSet& actset = it->first; + TraceTraceVertex* vvertexp = it->second; + UINFO(9," Done sort: "<nodep(), needChg); + if (addp) { // Else no activity or duplicate + if (actset.find(TraceActivityVertex::ACTIVITY_NEVER) != actset.end()) { + vvertexp->nodep()->v3fatalSrc("If never, needChg=0 and shouldn't need to add."); + } else if (actset.find(TraceActivityVertex::ACTIVITY_ALWAYS) != actset.end()) { + // Must always set it; add to base of function + addToChgSub(m_chgFuncp, addp); + } else if (lastactp && actset == *lastactp && ifnodep) { + // Add to last statement we built + addToChgSub(ifnodep, addp); + } else { + // Build a new IF statement + FileLine* fl = addp->fileline(); + AstNode* condp = NULL; + for (ActCodeSet::const_iterator csit = actset.begin(); + csit != actset.end(); ++csit) { + uint32_t acode = *csit; AstNode* selp = selectActivity(fl, acode, false); - if (condp) condp = new AstOr (fl, condp, selp); - else condp = selp; - } + if (condp) condp = new AstOr(fl, condp, selp); + else condp = selp; + } AstIf* ifp = new AstIf(fl, condp, NULL, NULL); - ifp->branchPred(AstBranchPred::BP_UNLIKELY); - m_chgFuncp->addStmtsp(ifp); - lastactp = &actset; - ifnodep = ifp; + ifp->branchPred(AstBranchPred::BP_UNLIKELY); + m_chgFuncp->addStmtsp(ifp); + lastactp = &actset; + ifnodep = ifp; - addToChgSub(ifnodep, addp); - } - } - } + addToChgSub(ifnodep, addp); + } + } + } - // Set in initializer + // Set in initializer - // Clear activity after tracing completes - FileLine* fl = m_chgFuncp->fileline(); + // Clear activity after tracing completes + FileLine* fl = m_chgFuncp->fileline(); if (v3Global.opt.mtasks()) { for (uint32_t i = 0; i < m_activityNumber; ++i) { AstNode* clrp = new AstAssign(fl, selectActivity(fl, i, true), @@ -519,200 +530,203 @@ private: } uint32_t assignDeclCode(AstTraceDecl* nodep) { - if (!nodep->code()) { - nodep->code(m_code); - m_code += nodep->codeInc(); - m_statUniqCodes += nodep->codeInc(); - ++m_statUniqSigs; - } - return nodep->code(); + if (!nodep->code()) { + nodep->code(m_code); + m_code += nodep->codeInc(); + m_statUniqCodes += nodep->codeInc(); + ++m_statUniqSigs; + } + return nodep->code(); } AstNode* assignTraceCode(TraceTraceVertex* vvertexp, AstTraceInc* nodep, bool needChg) { - // Assign trace code, add to tree, return node for change tree or null - // Look for identical copies - uint32_t codePreassigned = 0; - //if (debug()>=9) nodep->dumpTree(cout,"- assnnode: "); - // Find non-duplicated node; note some nodep's maybe null, as they were deleted below - TraceTraceVertex* dupvertexp = vvertexp; - if (dupvertexp->duplicatep()) { - dupvertexp = dupvertexp->duplicatep(); + // Assign trace code, add to tree, return node for change tree or null + // Look for identical copies + uint32_t codePreassigned = 0; + //if (debug()>=9) nodep->dumpTree(cout, "- assnnode: "); + // Find non-duplicated node; note some nodep's maybe null, as they were deleted below + TraceTraceVertex* dupvertexp = vvertexp; + if (dupvertexp->duplicatep()) { + dupvertexp = dupvertexp->duplicatep(); UINFO(9," dupOf "<nodep()) - <<" "<duplicatep()) dupvertexp->nodep()->v3fatalSrc("Original node was marked as a duplicate"); - } + <<" "<duplicatep()) dupvertexp->nodep()->v3fatalSrc("Original node was marked as a duplicate"); + } - if (dupvertexp != vvertexp) { - // It's an exact copy. We'll assign the code to the master on - // the first one we hit; the later ones will share the code. - codePreassigned = assignDeclCode(dupvertexp->nodep()->declp()); - nodep->declp()->code(codePreassigned); - } else { - assignDeclCode(nodep->declp()); - } - UINFO(8," Created code="<declp()->code() - <<" "<<(codePreassigned?"[PREASS]":"") - <<" "<<(needChg?"[CHG]":"")<<" "<nodep()->declp()); + nodep->declp()->code(codePreassigned); + } else { + assignDeclCode(nodep->declp()); + } + UINFO(8," Created code="<declp()->code() + <<" "<<(codePreassigned?"[PREASS]":"") + <<" "<<(needChg?"[CHG]":"")<<" "<cloneTree(true); - } + AstNode* incAddp = NULL; + if (!codePreassigned) { + // Add to trace cfuncs + if (needChg) { + ++m_statChgSigs; + incAddp = nodep->cloneTree(true); + } - if (!m_fullSubFuncp - || (m_fullSubStmts && v3Global.opt.outputSplitCTrace() - && m_fullSubStmts > v3Global.opt.outputSplitCTrace())) { - m_fullSubFuncp = newCFuncSub(m_fullFuncp, m_fullFuncp); - m_fullSubStmts = 0; - } + if (!m_fullSubFuncp + || (m_fullSubStmts && v3Global.opt.outputSplitCTrace() + && m_fullSubStmts > v3Global.opt.outputSplitCTrace())) { + m_fullSubFuncp = newCFuncSub(m_fullFuncp, m_fullFuncp); + m_fullSubStmts = 0; + } - m_fullSubFuncp->addStmtsp(nodep); - m_fullSubStmts += EmitCBaseCounterVisitor(nodep).count(); - } else { - // Duplicates don't need a TraceInc - pushDeletep(nodep); VL_DANGLING(nodep); - } - return incAddp; + m_fullSubFuncp->addStmtsp(nodep); + m_fullSubStmts += EmitCBaseCounterVisitor(nodep).count(); + } else { + // Duplicates don't need a TraceInc + pushDeletep(nodep); VL_DANGLING(nodep); + } + return incAddp; } TraceCFuncVertex* getCFuncVertexp(AstCFunc* nodep) { - TraceCFuncVertex* vertexp = dynamic_cast(nodep->user1u().toGraphVertex()); - if (!vertexp) { - vertexp = new TraceCFuncVertex(&m_graph, nodep); - nodep->user1p(vertexp); - } - return vertexp; + TraceCFuncVertex* vertexp + = dynamic_cast(nodep->user1u().toGraphVertex()); + if (!vertexp) { + vertexp = new TraceCFuncVertex(&m_graph, nodep); + nodep->user1p(vertexp); + } + return vertexp; } TraceActivityVertex* getActivityVertexp(AstNode* nodep, bool slow) { - TraceActivityVertex* vertexp = dynamic_cast(nodep->user3u().toGraphVertex()); - if (!vertexp) { - vertexp = new TraceActivityVertex(&m_graph, nodep, slow); - nodep->user3p(vertexp); - } - vertexp->slow(slow); - return vertexp; + TraceActivityVertex* vertexp + = dynamic_cast(nodep->user3u().toGraphVertex()); + if (!vertexp) { + vertexp = new TraceActivityVertex(&m_graph, nodep, slow); + nodep->user3p(vertexp); + } + vertexp->slow(slow); + return vertexp; } // VISITORS virtual void visit(AstNetlist* nodep) { - m_code = 1; // Multiple TopScopes will require fixing how code#s - // are assigned as duplicate varscopes must result in the same tracing code#. + m_code = 1; // Multiple TopScopes will require fixing how code#s + // are assigned as duplicate varscopes must result in the same tracing code#. - // Make a always vertex - m_alwaysVtxp = new TraceActivityVertex(&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS); + // Make a always vertex + m_alwaysVtxp = new TraceActivityVertex(&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS); - // Add vertexes for all TRACES, and edges from VARs each trace looks at - m_finding = false; + // Add vertexes for all TRACES, and edges from VARs each trace looks at + m_finding = false; iterateChildren(nodep); - // Add vertexes for all CFUNCs, and edges to VARs the func sets - m_finding = true; + // Add vertexes for all CFUNCs, and edges to VARs the func sets + m_finding = true; iterateChildren(nodep); - m_finding = false; + m_finding = false; - // Detect and remove duplicate values - detectDuplicates(); + // Detect and remove duplicate values + detectDuplicates(); - // Simplify it - if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_pre"); - graphSimplify(); - if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_opt"); + // Simplify it + if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_pre"); + graphSimplify(); + if (debug()>=6) m_graph.dumpDotFilePrefixed("trace_opt"); - // Create new TRACEINCs - assignActivity(); - putTracesIntoTree(); + // Create new TRACEINCs + assignActivity(); + putTracesIntoTree(); } virtual void visit(AstNodeModule* nodep) { - if (nodep->isTop()) m_topModp = nodep; + if (nodep->isTop()) m_topModp = nodep; iterateChildren(nodep); } virtual void visit(AstTopScope* nodep) { - AstScope* scopep = nodep->scopep(); - if (!scopep) nodep->v3fatalSrc("No scope found on top level"); - m_highScopep = scopep; + AstScope* scopep = nodep->scopep(); + if (!scopep) nodep->v3fatalSrc("No scope found on top level"); + m_highScopep = scopep; iterateChildren(nodep); } virtual void visit(AstCCall* nodep) { - UINFO(8," CCALL "<user2()) { - // See if there are other calls in same statement list; - // If so, all funcs might share the same activity code - TraceActivityVertex* activityVtxp = getActivityVertexp(nodep, nodep->funcp()->slow()); - for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { + UINFO(8," CCALL "<user2()) { + // See if there are other calls in same statement list; + // If so, all funcs might share the same activity code + TraceActivityVertex* activityVtxp = getActivityVertexp(nodep, nodep->funcp()->slow()); + for (AstNode* nextp=nodep; nextp; nextp=nextp->nextp()) { if (AstCCall* ccallp = VN_CAST(nextp, CCall)) { - ccallp->user2(true); // Processed - UINFO(8," SubCCALL "<funcp()); - activityVtxp->slow(ccallp->funcp()->slow()); + ccallp->user2(true); // Processed + UINFO(8," SubCCALL "<funcp()); + activityVtxp->slow(ccallp->funcp()->slow()); new V3GraphEdge(&m_graph, activityVtxp, ccallFuncVtxp, 1); - } - } - } + } + } + } iterateChildren(nodep); } virtual void visit(AstCFunc* nodep) { - UINFO(8," CFUNC "<funcType() == AstCFuncType::TRACE_INIT) { - m_initFuncp = nodep; - } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) { - m_fullFuncp = nodep; - } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) { - m_chgFuncp = nodep; - } - V3GraphVertex* funcVtxp = getCFuncVertexp(nodep); - if (!m_finding) { // If public, we need a unique activity code to allow for sets directly in this func - if (nodep->funcPublic() || nodep->dpiExport() - || nodep == v3Global.rootp()->evalp()) { - // Need a non-null place to remember to later add a statement; make one - if (!nodep->stmtsp()) nodep->addStmtsp(new AstComment(nodep->fileline(), "Tracing activity check")); - V3GraphVertex* activityVtxp = getActivityVertexp(nodep->stmtsp(), nodep->slow()); + UINFO(8," CFUNC "<funcType() == AstCFuncType::TRACE_INIT) { + m_initFuncp = nodep; + } else if (nodep->funcType() == AstCFuncType::TRACE_FULL) { + m_fullFuncp = nodep; + } else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) { + m_chgFuncp = nodep; + } + V3GraphVertex* funcVtxp = getCFuncVertexp(nodep); + if (!m_finding) { // If public, we need a unique activity code to allow for sets directly in this func + if (nodep->funcPublic() || nodep->dpiExport() + || nodep == v3Global.rootp()->evalp()) { + // Need a non-null place to remember to later add a statement; make one + if (!nodep->stmtsp()) nodep->addStmtsp( + new AstComment(nodep->fileline(), "Tracing activity check")); + V3GraphVertex* activityVtxp = getActivityVertexp(nodep->stmtsp(), nodep->slow()); new V3GraphEdge(&m_graph, activityVtxp, funcVtxp, 1); - } - } - m_funcp = nodep; + } + } + m_funcp = nodep; iterateChildren(nodep); - m_funcp = NULL; + m_funcp = NULL; } virtual void visit(AstTraceInc* nodep) { - UINFO(8," TRACE "<v3fatalSrc("Traces should have been removed in prev step."); - nodep->unlinkFrBack(); + UINFO(8," TRACE "<v3fatalSrc("Traces should have been removed in prev step."); + nodep->unlinkFrBack(); - V3GraphVertex* vertexp = new TraceTraceVertex(&m_graph, nodep); - nodep->user1p(vertexp); + V3GraphVertex* vertexp = new TraceTraceVertex(&m_graph, nodep); + nodep->user1p(vertexp); - if (!m_funcp || (!m_chgFuncp || !m_fullFuncp)) nodep->v3fatalSrc("Trace not under func"); - m_tracep = nodep; + if (!m_funcp || (!m_chgFuncp || !m_fullFuncp)) nodep->v3fatalSrc("Trace not under func"); + m_tracep = nodep; iterateChildren(nodep); - m_tracep = NULL; + m_tracep = NULL; } virtual void visit(AstVarRef* nodep) { - if (m_tracep) { - if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?"); - if (nodep->lvalue()) nodep->v3fatalSrc("Lvalue in trace? Should be const."); - V3GraphVertex* varVtxp = nodep->varScopep()->user1u().toGraphVertex(); - if (!varVtxp) { - varVtxp = new TraceVarVertex(&m_graph, nodep->varScopep()); - nodep->varScopep()->user1p(varVtxp); - } - V3GraphVertex* traceVtxp = m_tracep->user1u().toGraphVertex(); - new V3GraphEdge(&m_graph, varVtxp, traceVtxp, 1); + if (m_tracep) { + if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?"); + if (nodep->lvalue()) nodep->v3fatalSrc("Lvalue in trace? Should be const."); + V3GraphVertex* varVtxp = nodep->varScopep()->user1u().toGraphVertex(); + if (!varVtxp) { + varVtxp = new TraceVarVertex(&m_graph, nodep->varScopep()); + nodep->varScopep()->user1p(varVtxp); + } + V3GraphVertex* traceVtxp = m_tracep->user1u().toGraphVertex(); + new V3GraphEdge(&m_graph, varVtxp, traceVtxp, 1); if (nodep->varp()->isPrimaryInish() // Always need to trace primary inputs || nodep->varp()->isSigPublic()) { // Or ones user can change new V3GraphEdge(&m_graph, m_alwaysVtxp, traceVtxp, 1); } } - else if (m_funcp && m_finding && nodep->lvalue()) { - if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?"); - V3GraphVertex* funcVtxp = getCFuncVertexp(m_funcp); - V3GraphVertex* varVtxp = nodep->varScopep()->user1u().toGraphVertex(); - if (varVtxp) { // else we're not tracing this signal - new V3GraphEdge(&m_graph, funcVtxp, varVtxp, 1); - } - } + else if (m_funcp && m_finding && nodep->lvalue()) { + if (!nodep->varScopep()) nodep->v3fatalSrc("No var scope?"); + V3GraphVertex* funcVtxp = getCFuncVertexp(m_funcp); + V3GraphVertex* varVtxp = nodep->varScopep()->user1u().toGraphVertex(); + if (varVtxp) { // else we're not tracing this signal + new V3GraphEdge(&m_graph, funcVtxp, varVtxp, 1); + } + } } //-------------------- virtual void visit(AstNode* nodep) { @@ -722,31 +736,31 @@ private: public: // CONSTUCTORS explicit TraceVisitor(AstNetlist* nodep) { - m_funcp = NULL; - m_tracep = NULL; - m_topModp = NULL; - m_highScopep = NULL; - m_finding = false; - m_activityVscp = NULL; - m_alwaysVtxp = NULL; - m_initFuncp = NULL; - m_fullFuncp = NULL; - m_fullSubFuncp = NULL; - m_fullSubStmts = 0; - m_chgFuncp = NULL; - m_chgSubFuncp = NULL; - m_chgSubParentp = NULL; - m_chgSubStmts = 0; + m_funcp = NULL; + m_tracep = NULL; + m_topModp = NULL; + m_highScopep = NULL; + m_finding = false; + m_activityVscp = NULL; + m_alwaysVtxp = NULL; + m_initFuncp = NULL; + m_fullFuncp = NULL; + m_fullSubFuncp = NULL; + m_fullSubStmts = 0; + m_chgFuncp = NULL; + m_chgSubFuncp = NULL; + m_chgSubParentp = NULL; + m_chgSubStmts = 0; m_activityNumber = 0; m_code = 0; m_finding = false; - m_funcNum = 0; + m_funcNum = 0; iterate(nodep); } virtual ~TraceVisitor() { - V3Stats::addStat("Tracing, Unique changing signals", m_statChgSigs); - V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs); - V3Stats::addStat("Tracing, Unique trace codes", m_statUniqCodes); + V3Stats::addStat("Tracing, Unique changing signals", m_statChgSigs); + V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs); + V3Stats::addStat("Tracing, Unique trace codes", m_statUniqCodes); } }; diff --git a/src/V3Trace.h b/src/V3Trace.h index 03e2bedbc..279050394 100644 --- a/src/V3Trace.h +++ b/src/V3Trace.h @@ -34,4 +34,4 @@ public: static void traceAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 9c60ea5bd..1cf84cd20 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -18,9 +18,9 @@ // //************************************************************************* // V3TraceDecl's Transformations: -// Create trace CFUNCs -// For each VARSCOPE -// If appropriate type of signal, create a TRACE +// Create trace CFUNCs +// For each VARSCOPE +// If appropriate type of signal, create a TRACE // //************************************************************************* @@ -42,257 +42,263 @@ private: // NODE STATE // STATE - AstScope* m_scopetopp; // Current top scope - AstCFunc* m_initFuncp; // Trace function being built - AstCFunc* m_initSubFuncp; // Trace function being built (under m_init) - int m_initSubStmts; // Number of statements in function - AstCFunc* m_fullFuncp; // Trace function being built - AstCFunc* m_chgFuncp; // Trace function being built - int m_funcNum; // Function number being built - AstVarScope* m_traVscp; // Signal being trace constructed - AstNode* m_traValuep; // Signal being traced's value to trace in it - string m_traShowname; // Signal being traced's component name + AstScope* m_scopetopp; // Current top scope + AstCFunc* m_initFuncp; // Trace function being built + AstCFunc* m_initSubFuncp; // Trace function being built (under m_init) + int m_initSubStmts; // Number of statements in function + AstCFunc* m_fullFuncp; // Trace function being built + AstCFunc* m_chgFuncp; // Trace function being built + int m_funcNum; // Function number being built + AstVarScope* m_traVscp; // Signal being trace constructed + AstNode* m_traValuep; // Signal being traced's value to trace in it + string m_traShowname; // Signal being traced's component name - V3Double0 m_statSigs; // Statistic tracking - V3Double0 m_statIgnSigs; // Statistic tracking + V3Double0 m_statSigs; // Statistic tracking + V3Double0 m_statIgnSigs; // Statistic tracking // METHODS VL_DEBUG_FUNC; // Declare debug() const char* vscIgnoreTrace(AstVarScope* nodep) { - // Return true if this shouldn't be traced - // See also similar rule in V3Coverage::varIgnoreToggle - AstVar* varp = nodep->varp(); - if (!varp->isTrace()) { - return "Verilator trace_off"; - } - else if (!nodep->isTrace()) { - return "Verilator cell trace_off"; - } - else if (!v3Global.opt.traceUnderscore()) { - string prettyName = varp->prettyName(); - if (!prettyName.empty() && prettyName[0] == '_') - return "Leading underscore"; - if (prettyName.find("._") != string::npos) - return "Inlined leading underscore"; + // Return true if this shouldn't be traced + // See also similar rule in V3Coverage::varIgnoreToggle + AstVar* varp = nodep->varp(); + if (!varp->isTrace()) { + return "Verilator trace_off"; } - return NULL; + else if (!nodep->isTrace()) { + return "Verilator cell trace_off"; + } + else if (!v3Global.opt.traceUnderscore()) { + string prettyName = varp->prettyName(); + if (!prettyName.empty() && prettyName[0] == '_') + return "Leading underscore"; + if (prettyName.find("._") != string::npos) + return "Inlined leading underscore"; + } + return NULL; } AstCFunc* newCFunc(AstCFuncType type, const string& name, bool slow) { - AstCFunc* funcp = new AstCFunc(m_scopetopp->fileline(), name, m_scopetopp); - funcp->slow(slow); - funcp->argTypes(EmitCBaseVisitor::symClassVar()+", "+v3Global.opt.traceClassBase()+"* vcdp, uint32_t code"); - funcp->funcType(type); - funcp->symProlog(true); - m_scopetopp->addActivep(funcp); - UINFO(5," Newfunc "<fileline(), name, m_scopetopp); + funcp->slow(slow); + funcp->argTypes(EmitCBaseVisitor::symClassVar()+", "+v3Global.opt.traceClassBase()+"* vcdp, uint32_t code"); + funcp->funcType(type); + funcp->symProlog(true); + m_scopetopp->addActivep(funcp); + UINFO(5," Newfunc "<name()+"__"+cvtToStr(++m_funcNum); - AstCFunc* funcp = NULL; - if (basep->funcType()==AstCFuncType::TRACE_INIT) { - funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name, basep->slow()); - } else { - basep->v3fatalSrc("Strange base function type"); - } - // cppcheck-suppress nullPointer // above fatal prevents it - AstCCall* callp = new AstCCall(funcp->fileline(), funcp); - callp->argTypes("vlSymsp, vcdp, code"); - basep->addStmtsp(callp); - return funcp; + string name = basep->name()+"__"+cvtToStr(++m_funcNum); + AstCFunc* funcp = NULL; + if (basep->funcType()==AstCFuncType::TRACE_INIT) { + funcp = newCFunc(AstCFuncType::TRACE_INIT_SUB, name, basep->slow()); + } else { + basep->v3fatalSrc("Strange base function type"); + } + // cppcheck-suppress nullPointer // above fatal prevents it + AstCCall* callp = new AstCCall(funcp->fileline(), funcp); + callp->argTypes("vlSymsp, vcdp, code"); + basep->addStmtsp(callp); + return funcp; } void addTraceDecl(const VNumRange& arrayRange, - int widthOverride) { // If !=0, is packed struct/array where basicp size misreflects one element - VNumRange bitRange; - AstBasicDType* bdtypep = m_traValuep->dtypep()->basicp(); - if (widthOverride) bitRange = VNumRange(widthOverride-1,0,false); - else if (bdtypep) bitRange = bdtypep->nrange(); + int widthOverride) { // If !=0, is packed struct/array where basicp size misreflects one element + VNumRange bitRange; + AstBasicDType* bdtypep = m_traValuep->dtypep()->basicp(); + if (widthOverride) bitRange = VNumRange(widthOverride-1, 0, false); + else if (bdtypep) bitRange = bdtypep->nrange(); AstTraceDecl* declp = new AstTraceDecl(m_traVscp->fileline(), m_traShowname, m_traVscp->varp(), m_traValuep, - bitRange, arrayRange); - UINFO(9,"Decl "< v3Global.opt.outputSplitCTrace()) { - m_initSubFuncp = newCFuncSub(m_initFuncp); - m_initSubStmts = 0; - } + if (m_initSubStmts && v3Global.opt.outputSplitCTrace() + && m_initSubStmts > v3Global.opt.outputSplitCTrace()) { + m_initSubFuncp = newCFuncSub(m_initFuncp); + m_initSubStmts = 0; + } - m_initSubFuncp->addStmtsp(declp); - m_initSubStmts += EmitCBaseCounterVisitor(declp).count(); + m_initSubFuncp->addStmtsp(declp); + m_initSubStmts += EmitCBaseCounterVisitor(declp).count(); - m_chgFuncp->addStmtsp(new AstTraceInc(m_traVscp->fileline(), declp, m_traValuep->cloneTree(true))); - // The full version will get constructed in V3Trace + m_chgFuncp->addStmtsp(new AstTraceInc(m_traVscp->fileline(), + declp, m_traValuep->cloneTree(true))); + // The full version will get constructed in V3Trace } void addIgnore(const char* why) { - ++m_statIgnSigs; - m_initSubFuncp->addStmtsp( - new AstComment(m_traVscp->fileline(), "Tracing: "+m_traShowname+" // Ignored: "+why)); + ++m_statIgnSigs; + m_initSubFuncp->addStmtsp( + new AstComment(m_traVscp->fileline(), "Tracing: "+m_traShowname+" // Ignored: "+why)); } // VISITORS virtual void visit(AstTopScope* nodep) { - m_scopetopp = nodep->scopep(); - // Make containers for TRACEDECLs first - m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "traceInitThis", true); - m_fullFuncp = newCFunc(AstCFuncType::TRACE_FULL, "traceFullThis", true); - m_chgFuncp = newCFunc(AstCFuncType::TRACE_CHANGE, "traceChgThis", false); - // - m_initSubFuncp = newCFuncSub(m_initFuncp); - // And find variables + m_scopetopp = nodep->scopep(); + // Make containers for TRACEDECLs first + m_initFuncp = newCFunc(AstCFuncType::TRACE_INIT, "traceInitThis", true); + m_fullFuncp = newCFunc(AstCFuncType::TRACE_FULL, "traceFullThis", true); + m_chgFuncp = newCFunc(AstCFuncType::TRACE_CHANGE, "traceChgThis", false); + // + m_initSubFuncp = newCFuncSub(m_initFuncp); + // And find variables iterateChildren(nodep); } virtual void visit(AstVarScope* nodep) { iterateChildren(nodep); - // Avoid updating this if (), instead see varp->isTrace() - if (!nodep->varp()->isTemp() && !nodep->varp()->isFuncLocal()) { - UINFO(5, " vsc "<varp(); - AstScope* scopep = nodep->scopep(); - // Compute show name - // This code assumes SPTRACEVCDC_VERSION >= 1330; - // it uses spaces to separate hierarchy components. - m_traShowname = AstNode::vcdName(scopep->name() + " " + varp->name()); - if (m_traShowname.substr(0,4) == "TOP ") m_traShowname.replace(0,4,""); - if (!m_initSubFuncp) nodep->v3fatalSrc("NULL"); + // Avoid updating this if (), instead see varp->isTrace() + if (!nodep->varp()->isTemp() && !nodep->varp()->isFuncLocal()) { + UINFO(5, " vsc "<varp(); + AstScope* scopep = nodep->scopep(); + // Compute show name + // This code assumes SPTRACEVCDC_VERSION >= 1330; + // it uses spaces to separate hierarchy components. + m_traShowname = AstNode::vcdName(scopep->name() + " " + varp->name()); + if (m_traShowname.substr(0, 4) == "TOP ") m_traShowname.replace(0, 4, ""); + if (!m_initSubFuncp) nodep->v3fatalSrc("NULL"); - m_traVscp = nodep; - m_traValuep = NULL; - if (vscIgnoreTrace(nodep)) { - addIgnore(vscIgnoreTrace(nodep)); - } else { - ++m_statSigs; - if (nodep->valuep()) m_traValuep = nodep->valuep()->cloneTree(true); - else m_traValuep = new AstVarRef(nodep->fileline(), nodep, false); - { - // Recurse into data type of the signal; the visitors will call addTraceDecl() + m_traVscp = nodep; + m_traValuep = NULL; + if (vscIgnoreTrace(nodep)) { + addIgnore(vscIgnoreTrace(nodep)); + } else { + ++m_statSigs; + if (nodep->valuep()) m_traValuep = nodep->valuep()->cloneTree(true); + else m_traValuep = new AstVarRef(nodep->fileline(), nodep, false); + { + // Recurse into data type of the signal; the visitors will call addTraceDecl() iterate(varp->dtypeSkipRefp()); - } - // Cleanup - if (m_traValuep) { m_traValuep->deleteTree(); m_traValuep=NULL; } - } - m_traVscp = NULL; - m_traValuep = NULL; - m_traShowname = ""; - } + } + // Cleanup + if (m_traValuep) { m_traValuep->deleteTree(); m_traValuep = NULL; } + } + m_traVscp = NULL; + m_traValuep = NULL; + m_traShowname = ""; + } } // VISITORS - Data types when tracing virtual void visit(AstConstDType* nodep) { - if (m_traVscp) { + if (m_traVscp) { iterate(nodep->subDTypep()->skipRefp()); - } + } } virtual void visit(AstRefDType* nodep) { - if (m_traVscp) { + if (m_traVscp) { iterate(nodep->subDTypep()->skipRefp()); - } + } } virtual void visit(AstUnpackArrayDType* nodep) { - // Note more specific dtypes above - if (m_traVscp) { + // Note more specific dtypes above + if (m_traVscp) { if (static_cast(nodep->arrayUnpackedElements()) > v3Global.opt.traceMaxArray()) { - addIgnore("Wide memory > --trace-max-array ents"); + addIgnore("Wide memory > --trace-max-array ents"); } else if (VN_IS(nodep->subDTypep()->skipRefp(), BasicDType) // Nothing lower than this array - && m_traVscp->dtypep()->skipRefp() == nodep) { // Nothing above this array - // Simple 1-D array, use exising V3EmitC runtime loop rather than unrolling - // This will put "(index)" at end of signal name for us + && m_traVscp->dtypep()->skipRefp() == nodep) { // Nothing above this array + // Simple 1-D array, use exising V3EmitC runtime loop rather than unrolling + // This will put "(index)" at end of signal name for us if (m_traVscp->dtypep()->skipRefp()->isString()) { addIgnore("Unsupported: strings"); } else { addTraceDecl(nodep->declRange(), 0); } - } else { - // Unroll now, as have no other method to get right signal names - AstNodeDType* subtypep = nodep->subDTypep()->skipRefp(); - for (int i=nodep->lsb(); i<=nodep->msb(); ++i) { - string oldShowname = m_traShowname; - AstNode* oldValuep = m_traValuep; - { - m_traShowname += string("(")+cvtToStr(i)+string(")"); - m_traValuep = new AstArraySel(nodep->fileline(), m_traValuep->cloneTree(true), - i - nodep->lsb()); + } else { + // Unroll now, as have no other method to get right signal names + AstNodeDType* subtypep = nodep->subDTypep()->skipRefp(); + for (int i=nodep->lsb(); i<=nodep->msb(); ++i) { + string oldShowname = m_traShowname; + AstNode* oldValuep = m_traValuep; + { + m_traShowname += string("(")+cvtToStr(i)+string(")"); + m_traValuep = new AstArraySel(nodep->fileline(), + m_traValuep->cloneTree(true), + i - nodep->lsb()); iterate(subtypep); - m_traValuep->deleteTree(); m_traValuep = NULL; - } - m_traShowname = oldShowname; - m_traValuep = oldValuep; - } - } - } + m_traValuep->deleteTree(); m_traValuep = NULL; + } + m_traShowname = oldShowname; + m_traValuep = oldValuep; + } + } + } } virtual void visit(AstPackArrayDType* nodep) { - if (m_traVscp) { - if (!v3Global.opt.traceStructs()) { - // Everything downstream is packed, so deal with as one trace unit - // This may not be the nicest for user presentation, but is a much faster way to trace - addTraceDecl(VNumRange(), nodep->width()); - } else { - AstNodeDType* subtypep = nodep->subDTypep()->skipRefp(); - for (int i=nodep->lsb(); i<=nodep->msb(); ++i) { - string oldShowname = m_traShowname; - AstNode* oldValuep = m_traValuep; - { - m_traShowname += string("(")+cvtToStr(i)+string(")"); - m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true), - (i - nodep->lsb())*subtypep->width(), - subtypep->width()); + if (m_traVscp) { + if (!v3Global.opt.traceStructs()) { + // Everything downstream is packed, so deal with as one trace unit. + // This may not be the nicest for user presentation, but is + // a much faster way to trace + addTraceDecl(VNumRange(), nodep->width()); + } else { + AstNodeDType* subtypep = nodep->subDTypep()->skipRefp(); + for (int i=nodep->lsb(); i<=nodep->msb(); ++i) { + string oldShowname = m_traShowname; + AstNode* oldValuep = m_traValuep; + { + m_traShowname += string("(")+cvtToStr(i)+string(")"); + m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true), + (i - nodep->lsb())*subtypep->width(), + subtypep->width()); iterate(subtypep); - m_traValuep->deleteTree(); m_traValuep = NULL; - } - m_traShowname = oldShowname; - m_traValuep = oldValuep; - } - } - } + m_traValuep->deleteTree(); m_traValuep = NULL; + } + m_traShowname = oldShowname; + m_traValuep = oldValuep; + } + } + } } virtual void visit(AstNodeClassDType* nodep) { - if (m_traVscp) { - if (nodep->packed() && !v3Global.opt.traceStructs()) { - // Everything downstream is packed, so deal with as one trace unit - // This may not be the nicest for user presentation, but is a much faster way to trace - addTraceDecl(VNumRange(), nodep->width()); - } else { - if (!nodep->packed()) { - addIgnore("Unsupported: Unpacked struct/union"); - } else { - for (AstMemberDType* itemp = nodep->membersp(); itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { - AstNodeDType* subtypep = itemp->subDTypep()->skipRefp(); - string oldShowname = m_traShowname; - AstNode* oldValuep = m_traValuep; - { - m_traShowname += string(" ")+itemp->prettyName(); + if (m_traVscp) { + if (nodep->packed() && !v3Global.opt.traceStructs()) { + // Everything downstream is packed, so deal with as one trace unit + // This may not be the nicest for user presentation, but is + // a much faster way to trace + addTraceDecl(VNumRange(), nodep->width()); + } else { + if (!nodep->packed()) { + addIgnore("Unsupported: Unpacked struct/union"); + } else { + for (AstMemberDType* itemp = nodep->membersp(); + itemp; itemp=VN_CAST(itemp->nextp(), MemberDType)) { + AstNodeDType* subtypep = itemp->subDTypep()->skipRefp(); + string oldShowname = m_traShowname; + AstNode* oldValuep = m_traValuep; + { + m_traShowname += string(" ")+itemp->prettyName(); if (VN_IS(nodep, StructDType)) { - m_traValuep = new AstSel(nodep->fileline(), m_traValuep->cloneTree(true), - itemp->lsb(), subtypep->width()); + m_traValuep = new AstSel(nodep->fileline(), + m_traValuep->cloneTree(true), + itemp->lsb(), subtypep->width()); iterate(subtypep); - m_traValuep->deleteTree(); m_traValuep = NULL; - } else { // Else union, replicate fields + m_traValuep->deleteTree(); m_traValuep = NULL; + } else { // Else union, replicate fields iterate(subtypep); - } - } - m_traShowname = oldShowname; - m_traValuep = oldValuep; - } - } - } - } + } + } + m_traShowname = oldShowname; + m_traValuep = oldValuep; + } + } + } + } } virtual void visit(AstBasicDType* nodep) { - if (m_traVscp) { + if (m_traVscp) { if (nodep->isString()) { - addIgnore("Unsupported: strings"); - } else { - addTraceDecl(VNumRange(), 0); - } - } + addIgnore("Unsupported: strings"); + } else { + addTraceDecl(VNumRange(), 0); + } + } } virtual void visit(AstNodeDType* nodep) { - // Note more specific dtypes above - if (!m_traVscp) return; - addIgnore("Unsupported: data type"); + // Note more specific dtypes above + if (!m_traVscp) return; + addIgnore("Unsupported: data type"); } //-------------------- @@ -303,20 +309,20 @@ private: public: // CONSTUCTORS explicit TraceDeclVisitor(AstNetlist* nodep) { - m_scopetopp = NULL; - m_initFuncp = NULL; - m_initSubFuncp = NULL; - m_initSubStmts = 0; - m_fullFuncp = NULL; - m_chgFuncp = NULL; - m_funcNum = 0; - m_traVscp = NULL; - m_traValuep = NULL; + m_scopetopp = NULL; + m_initFuncp = NULL; + m_initSubFuncp = NULL; + m_initSubStmts = 0; + m_fullFuncp = NULL; + m_chgFuncp = NULL; + m_funcNum = 0; + m_traVscp = NULL; + m_traValuep = NULL; iterate(nodep); } virtual ~TraceDeclVisitor() { - V3Stats::addStat("Tracing, Traced signals", m_statSigs); - V3Stats::addStat("Tracing, Ignored signals", m_statIgnSigs); + V3Stats::addStat("Tracing, Traced signals", m_statSigs); + V3Stats::addStat("Tracing, Ignored signals", m_statIgnSigs); } }; diff --git a/src/V3TraceDecl.h b/src/V3TraceDecl.h index d373dff89..0116aa352 100644 --- a/src/V3TraceDecl.h +++ b/src/V3TraceDecl.h @@ -34,4 +34,4 @@ public: static void traceDeclAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 556cb7f4a..e2523ee30 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -31,9 +31,10 @@ // VAR->isPullup/isPulldown (converted to AstPullup/AstPulldown // BufIf0/1 // All variables on the LHS need to become tristate when there is: -// CONST-> with Z value on the RHS of an assignment -// AstPin with lower connection a tristate -// A tristate signal on the RHS (this can't generally be determined until that signal is resolved) +// CONST-> with Z value on the RHS of an assignment +// AstPin with lower connection a tristate +// A tristate signal on the RHS +// (this can't generally be determined until that signal is resolved) // When LHS becomes tristate, then mark all RHS nodes as tristate // so any tristate varrefs on the right will propagate. // @@ -85,28 +86,28 @@ public: // Graph support classes class TristateVertex : public V3GraphVertex { - AstNode* m_nodep; - bool m_isTristate; // Logic indicates a tristate - bool m_feedsTri; // Propagates to a tristate node (on RHS) - bool m_processed; // Tristating was cleaned up + AstNode* m_nodep; + bool m_isTristate; // Logic indicates a tristate + bool m_feedsTri; // Propagates to a tristate node (on RHS) + bool m_processed; // Tristating was cleaned up public: TristateVertex(V3Graph* graphp, AstNode* nodep) - : V3GraphVertex(graphp) - , m_nodep(nodep), m_isTristate(false), m_feedsTri(false), m_processed(false) {} + : V3GraphVertex(graphp) + , m_nodep(nodep), m_isTristate(false), m_feedsTri(false), m_processed(false) {} virtual ~TristateVertex() {} // ACCESSORS AstNode* nodep() const { return m_nodep; } AstVar* varp() const { return VN_CAST(nodep(), Var); } virtual string name() const { - return ((isTristate() ? "tri\\n" + return ((isTristate() ? "tri\\n" : feedsTri() ? "feed\\n" : "-\\n") +(nodep()->prettyTypeName()+" "+cvtToHex(nodep()))); } virtual string dotColor() const { - return (varp() - ? (isTristate() ? "darkblue" - :feedsTri() ? "blue" : "lightblue") - : (isTristate() ? "darkgreen" - :feedsTri() ? "green" : "lightgreen")); } + return (varp() + ? (isTristate() ? "darkblue" + :feedsTri() ? "blue" : "lightblue") + : (isTristate() ? "darkgreen" + :feedsTri() ? "green" : "lightgreen")); } virtual FileLine* fileline() const { return nodep()->fileline(); } void isTristate(bool flag) { m_isTristate = flag; } bool isTristate() const { return m_isTristate; } @@ -120,8 +121,8 @@ public: class TristateGraph { // NODE STATE - // AstVar::user5p -> TristateVertex* for variable being built - //AstUser5InUse m_inuser5; // In visitor below + // AstVar::user5p -> TristateVertex* for variable being built + //AstUser5InUse m_inuser5; // In visitor below // TYPES public: @@ -129,7 +130,7 @@ public: private: // MEMBERS - V3Graph m_graph; // Logic graph + V3Graph m_graph; // Logic graph public: // CONSTUCTORS @@ -142,132 +143,138 @@ private: TristateVertex* makeVertex(AstNode* nodep) { TristateVertex* vertexp = reinterpret_cast(nodep->user5p()); - if (!vertexp) { - UINFO(6," New vertex "<user5p(vertexp); - } - return vertexp; + if (!vertexp) { + UINFO(6," New vertex "<user5p(vertexp); + } + return vertexp; } // METHODS - Graph optimization void graphWalkRecurseFwd(TristateVertex* vtxp, int level) { - // Propagate tristate forward to all sinks - // For example if on a CONST, propagate through CONCATS to ASSIGN to LHS VARREF of signal to tristate - if (!vtxp->isTristate()) return; // tristate involved - if (vtxp->user() == 1) return; - vtxp->user(1); // Recursed - UINFO(9," Mark tri "<varp()) { // not a var where we stop the recursion - for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep=edgep->outNextp()) { - TristateVertex* vvertexp = dynamic_cast(edgep->top()); - // Doesn't hurt to not check if already set, but by doing so when we - // print out the debug messages, we'll see this node at level 0 instead. - if (!vvertexp->isTristate()) { - vvertexp->isTristate(true); - graphWalkRecurseFwd(vvertexp, level+1); - } - } - } else { - // A variable is tristated. Find all of the LHS VARREFs that drive this signal now need tristate drivers - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep=edgep->inNextp()) { - TristateVertex* vvertexp = dynamic_cast(edgep->fromp()); + // Propagate tristate forward to all sinks + // For example if on a CONST, propagate through CONCATS to ASSIGN + // to LHS VARREF of signal to tristate + if (!vtxp->isTristate()) return; // tristate involved + if (vtxp->user() == 1) return; + vtxp->user(1); // Recursed + UINFO(9," Mark tri "<varp()) { // not a var where we stop the recursion + for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep=edgep->outNextp()) { + TristateVertex* vvertexp = dynamic_cast(edgep->top()); + // Doesn't hurt to not check if already set, but by doing so when we + // print out the debug messages, we'll see this node at level 0 instead. + if (!vvertexp->isTristate()) { + vvertexp->isTristate(true); + graphWalkRecurseFwd(vvertexp, level+1); + } + } + } else { + // A variable is tristated. Find all of the LHS VARREFs that + // drive this signal now need tristate drivers + for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep=edgep->inNextp()) { + TristateVertex* vvertexp = dynamic_cast(edgep->fromp()); if (const AstVarRef* refp = VN_CAST(vvertexp->nodep(), VarRef)) { - if (refp->lvalue() - // Doesn't hurt to not check if already set, but by doing so when we - // print out the debug messages, we'll see this node at level 0 instead. - && !vvertexp->isTristate()) { - vvertexp->isTristate(true); - graphWalkRecurseFwd(vvertexp, level+1); - } - } - } - } + if (refp->lvalue() + // Doesn't hurt to not check if already set, but by doing so when we + // print out the debug messages, we'll see this node at level 0 instead. + && !vvertexp->isTristate()) { + vvertexp->isTristate(true); + graphWalkRecurseFwd(vvertexp, level+1); + } + } + } + } } void graphWalkRecurseBack(TristateVertex* vtxp, int level) { - // Called only on a tristate node; propagate a feedsTri attribute "backwards" - // towards any driving nodes, i.e. from a LHS VARREF back to a driving RHS VARREF - // This way if the RHS VARREF is also tristated we'll connect the enables up to the LHS VARREF. - // Otherwise if not marked feedsTri() we'll drop the LHS' enables, if any - if (!(vtxp->isTristate() || vtxp->feedsTri())) return; // tristate involved - if (vtxp->user() == 3) return; - vtxp->user(3); // Recursed - UINFO(9," Mark feedstri "<varp()) { // not a var where we stop the recursion - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep=edgep->inNextp()) { - TristateVertex* vvertexp = dynamic_cast(edgep->fromp()); - // Doesn't hurt to not check if already set, but by doing so when we - // print out the debug messages, we'll see this node at level 0 instead. - if (!vvertexp->feedsTri()) { - vvertexp->feedsTri(true); - graphWalkRecurseBack(vvertexp, level+1); - } - } - } + // Called only on a tristate node; propagate a feedsTri attribute "backwards" + // towards any driving nodes, i.e. from a LHS VARREF back to a driving RHS VARREF + // This way if the RHS VARREF is also tristated we'll connect the + // enables up to the LHS VARREF. Otherwise if not marked + // feedsTri() we'll drop the LHS' enables, if any + if (!(vtxp->isTristate() || vtxp->feedsTri())) return; // tristate involved + if (vtxp->user() == 3) return; + vtxp->user(3); // Recursed + UINFO(9," Mark feedstri "<varp()) { // not a var where we stop the recursion + for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep=edgep->inNextp()) { + TristateVertex* vvertexp = dynamic_cast(edgep->fromp()); + // Doesn't hurt to not check if already set, but by doing so when we + // print out the debug messages, we'll see this node at level 0 instead. + if (!vvertexp->feedsTri()) { + vvertexp->feedsTri(true); + graphWalkRecurseBack(vvertexp, level+1); + } + } + } } public: // METHODS void clear() { - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - TristateVertex* vvertexp = static_cast(itp); - if (vvertexp->isTristate() && !vvertexp->processed()) { - // Not v3errorSrc as no reason to stop the world + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + TristateVertex* vvertexp = static_cast(itp); + if (vvertexp->isTristate() && !vvertexp->processed()) { + // Not v3errorSrc as no reason to stop the world vvertexp->nodep()->v3error("Unsupported tristate construct" - " (in graph; not converted): "<nodep()->prettyTypeName()); - } - } - m_graph.clear(); - AstNode::user5ClearTree(); // Wipe all node user5p's that point to vertexes + " (in graph; not converted): " + <nodep()->prettyTypeName()); + } + } + m_graph.clear(); + AstNode::user5ClearTree(); // Wipe all node user5p's that point to vertexes } void graphWalk(AstNodeModule* nodep) { - //if (debug()>=9) m_graph.dumpDotFilePrefixed("tri_pre__"+nodep->name()); - UINFO(9," Walking "<verticesNextp()) { + //if (debug()>=9) m_graph.dumpDotFilePrefixed("tri_pre__"+nodep->name()); + UINFO(9," Walking "<verticesNextp()) { graphWalkRecurseFwd(static_cast(itp), 0); - } - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + } + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { graphWalkRecurseBack(static_cast(itp), 0); - } - if (debug()>=9) m_graph.dumpDotFilePrefixed("tri_pos__"+nodep->name()); + } + if (debug()>=9) m_graph.dumpDotFilePrefixed("tri_pos__"+nodep->name()); } void setTristate(AstNode* nodep) { - makeVertex(nodep)->isTristate(true); + makeVertex(nodep)->isTristate(true); } void associate(AstNode* fromp, AstNode* top) { - new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1); + new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1); } bool isTristate(AstNode* nodep) { TristateVertex* vertexp = reinterpret_cast(nodep->user5p()); - return vertexp && vertexp->isTristate(); + return vertexp && vertexp->isTristate(); } bool feedsTri(AstNode* nodep) { TristateVertex* vertexp = reinterpret_cast(nodep->user5p()); - return vertexp && vertexp->feedsTri(); + return vertexp && vertexp->feedsTri(); } void didProcess(AstNode* nodep) { TristateVertex* vertexp = reinterpret_cast(nodep->user5p()); - if (!vertexp) { - // Not v3errorSrc as no reason to stop the world - nodep->v3error("Unsupported tristate construct (not in propagation graph): "<prettyTypeName()); - } else { - // We don't warn if no vertexp->isTristate() as the creation process makes midling nodes that don't have it set - vertexp->processed(true); - } + if (!vertexp) { + // Not v3errorSrc as no reason to stop the world + nodep->v3error("Unsupported tristate construct (not in propagation graph): " + <prettyTypeName()); + } else { + // We don't warn if no vertexp->isTristate() as the creation + // process makes midling nodes that don't have it set + vertexp->processed(true); + } } // ITERATOR METHODS VarVec tristateVars() { - // Return all tristate variables - VarVec v; - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { - TristateVertex* vvertexp = static_cast(itp); - if (vvertexp->isTristate()) { + // Return all tristate variables + VarVec v; + for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) { + TristateVertex* vvertexp = static_cast(itp); + if (vvertexp->isTristate()) { if (AstVar* nodep = VN_CAST(vvertexp->nodep(), Var)) { - v.push_back(nodep); - } - } - } - return v; + v.push_back(nodep); + } + } + } + return v; } }; @@ -277,23 +284,23 @@ public: class TristatePinVisitor : public TristateBaseVisitor { TristateGraph& m_tgraph; - bool m_lvalue; // Flip to be an LVALUE + bool m_lvalue; // Flip to be an LVALUE // VISITORS virtual void visit(AstVarRef* nodep) { - if (m_lvalue && !nodep->lvalue()) { - UINFO(9," Flip-to-LValue "<lvalue(true); - } else if (!m_lvalue && nodep->lvalue()) { - UINFO(9," Flip-to-RValue "<lvalue(false); - // Mark the ex-output as tristated - UINFO(9," setTristate-subpin "<varp()<varp()); - } + if (m_lvalue && !nodep->lvalue()) { + UINFO(9," Flip-to-LValue "<lvalue(true); + } else if (!m_lvalue && nodep->lvalue()) { + UINFO(9," Flip-to-RValue "<lvalue(false); + // Mark the ex-output as tristated + UINFO(9," setTristate-subpin "<varp()<varp()); + } } virtual void visit(AstArraySel* nodep) { - // Doesn't work because we'd set lvalue on the array index's var - if (m_lvalue) nodep->v3fatalSrc("ArraySel conversion to output, under tristate node"); + // Doesn't work because we'd set lvalue on the array index's var + if (m_lvalue) nodep->v3fatalSrc("ArraySel conversion to output, under tristate node"); iterateChildren(nodep); } virtual void visit(AstSliceSel* nodep) { @@ -307,7 +314,7 @@ class TristatePinVisitor : public TristateBaseVisitor { public: // CONSTUCTORS TristatePinVisitor(AstNode* nodep, TristateGraph& tgraph, bool lvalue) - : m_tgraph(tgraph), m_lvalue(lvalue) { + : m_tgraph(tgraph), m_lvalue(lvalue) { iterate(nodep); } virtual ~TristatePinVisitor() {} @@ -317,281 +324,285 @@ public: class TristateVisitor : public TristateBaseVisitor { // NODE STATE - // *::user1p -> pointer to output enable __en expressions - // *::user2 -> int - already visited, see U2_ enum - // AstVar::user3p -> AstPull* pullup/pulldown direction (input Var's user3p) - // AstVar::user4p -> AstVar* pointer to output __out var (input Var's user2p) + // *::user1p -> pointer to output enable __en expressions + // *::user2 -> int - already visited, see U2_ enum + // AstVar::user3p -> AstPull* pullup/pulldown direction (input Var's user3p) + // AstVar::user4p -> AstVar* pointer to output __out var (input Var's user2p) // See TristateGraph: - // AstVar::user5p -> TristateVertex* for variable being built - // AstStmt*::user5p -> TristateVertex* for this statement - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; - AstUser4InUse m_inuser4; - AstUser5InUse m_inuser5; + // AstVar::user5p -> TristateVertex* for variable being built + // AstStmt*::user5p -> TristateVertex* for this statement + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; + AstUser4InUse m_inuser4; + AstUser5InUse m_inuser5; // TYPES typedef std::vector RefVec; typedef std::map VarMap; - enum { U2_GRAPHING=1, // bit[0] if did m_graphing visit - U2_NONGRAPH=2, // bit[1] if did !m_graphing visit - U2_BOTH=3 }; // Both bits set + enum { U2_GRAPHING=1, // bit[0] if did m_graphing visit + U2_NONGRAPH=2, // bit[1] if did !m_graphing visit + U2_BOTH=3 }; // Both bits set // MEMBERS - bool m_graphing; // Major mode - creating graph + bool m_graphing; // Major mode - creating graph // - AstNodeModule* m_modp; // Current module - AstCell* m_cellp; // current cell - VarMap m_lhsmap; // LHS driver map - int m_unique; - bool m_alhs; // On LHS of assignment - AstNode* m_logicp; // Current logic being built - TristateGraph m_tgraph; // Logic graph + AstNodeModule* m_modp; // Current module + AstCell* m_cellp; // current cell + VarMap m_lhsmap; // LHS driver map + int m_unique; + bool m_alhs; // On LHS of assignment + AstNode* m_logicp; // Current logic being built + TristateGraph m_tgraph; // Logic graph // STATS - V3Double0 m_statTriSigs; // stat tracking + V3Double0 m_statTriSigs; // stat tracking // METHODS string dbgState() { - string o = (m_graphing?" gr ":" ng "); - if (m_alhs) o += "alhs "; - return o; + string o = (m_graphing?" gr ":" ng "); + if (m_alhs) o += "alhs "; + return o; } void associateLogic(AstNode* fromp, AstNode* top) { - if (m_logicp) { - m_tgraph.associate(fromp, top); - } + if (m_logicp) { + m_tgraph.associate(fromp, top); + } } AstNode* getEnp(AstNode* nodep) { - // checks if user1p() is null, and if so, adds a constant output - // enable driver of all 1's. Otherwise returns the user1p() data. + // checks if user1p() is null, and if so, adds a constant output + // enable driver of all 1's. Otherwise returns the user1p() data. if (!nodep->user1p()) { V3Number num(nodep, nodep->width()); num.setAllBits1(); - AstNode* enp = new AstConst(nodep->fileline(), num); - nodep->user1p(enp); - } - return nodep->user1p(); + AstNode* enp = new AstConst(nodep->fileline(), num); + nodep->user1p(enp); + } + return nodep->user1p(); } AstVar* getCreateEnVarp(AstVar* invarp) { - // Return the master __en for the specified input variable - if (!invarp->user1p()) { - AstVar* newp = new AstVar(invarp->fileline(), - AstVarType::MODULETEMP, - invarp->name()+"__en", - invarp); - UINFO(9," newenv "<v3error("Unsupported: Creating tristate signal not underneath a module: "<prettyName()); } - else m_modp->addStmtp(newp); - invarp->user1p(newp); // find envar given invarp - } + // Return the master __en for the specified input variable + if (!invarp->user1p()) { + AstVar* newp = new AstVar(invarp->fileline(), + AstVarType::MODULETEMP, + invarp->name()+"__en", + invarp); + UINFO(9," newenv "<v3error("Unsupported: Creating tristate signal not underneath a module: " + <prettyName()); } + else m_modp->addStmtp(newp); + invarp->user1p(newp); // find envar given invarp + } return VN_CAST(invarp->user1p(), Var); } AstVar* getCreateOutVarp(AstVar* invarp) { - // Return the master __out for the specified input variable - if (!invarp->user4p()) { - AstVar* newp = new AstVar(invarp->fileline(), - AstVarType::MODULETEMP, - invarp->name()+"__out", - invarp); - UINFO(9," newout "<v3error("Unsupported: Creating tristate signal not underneath a module: "<prettyName()); } - else m_modp->addStmtp(newp); - invarp->user4p(newp); // find outvar given invarp - } + // Return the master __out for the specified input variable + if (!invarp->user4p()) { + AstVar* newp = new AstVar(invarp->fileline(), + AstVarType::MODULETEMP, + invarp->name()+"__out", + invarp); + UINFO(9," newout "<v3error("Unsupported: Creating tristate signal not underneath a module: " + <prettyName()); } + else m_modp->addStmtp(newp); + invarp->user4p(newp); // find outvar given invarp + } return VN_CAST(invarp->user4p(), Var); } AstVar* getCreateUnconnVarp(AstNode* fromp, AstNodeDType* dtypep) { - AstVar* newp = new AstVar(fromp->fileline(), - AstVarType::MODULETEMP, - "__Vtriunconn"+cvtToStr(m_unique++), - dtypep); - UINFO(9," newunc "<v3error("Unsupported: Creating tristate signal not underneath a module"); } - else m_modp->addStmtp(newp); - return newp; + AstVar* newp = new AstVar(fromp->fileline(), + AstVarType::MODULETEMP, + "__Vtriunconn"+cvtToStr(m_unique++), + dtypep); + UINFO(9," newunc "<v3error("Unsupported: Creating tristate signal not underneath a module"); } + else m_modp->addStmtp(newp); + return newp; } void mapInsertLhsVarRef(AstVarRef* nodep) { - AstVar* key = nodep->varp(); - VarMap::iterator it = m_lhsmap.find(key); - UINFO(9," mapInsertLhsVarRef "<push_back(nodep); - m_lhsmap.insert(make_pair(key, refs)); - } else { + AstVar* key = nodep->varp(); + VarMap::iterator it = m_lhsmap.find(key); + UINFO(9," mapInsertLhsVarRef "<push_back(nodep); + m_lhsmap.insert(make_pair(key, refs)); + } else { it->second->push_back(nodep); } } AstNode* newEnableDeposit(AstSel* selp, AstNode* enp) { - // Form a "deposit" instruction for given enable, using existing select as a template. - // Would be nicer if we made this a new AST type - AstNode* newp = new AstShiftL(selp->fileline(), - new AstExtend(selp->fileline(), - enp, - selp->fromp()->width()), - selp->lsbp()->cloneTree(false), - selp->fromp()->width()); - return newp; + // Form a "deposit" instruction for given enable, using existing select as a template. + // Would be nicer if we made this a new AST type + AstNode* newp = new AstShiftL(selp->fileline(), + new AstExtend(selp->fileline(), + enp, + selp->fromp()->width()), + selp->lsbp()->cloneTree(false), + selp->fromp()->width()); + return newp; } void setPullDirection(AstVar* varp, AstPull* pullp) { AstPull* oldpullp = static_cast(varp->user3p()); - if (!oldpullp) { - varp->user3p(pullp); //save off to indicate the pull direction - } else { - if (oldpullp->direction() != pullp->direction()) { - pullp->v3error("Unsupported: Conflicting pull directions."); - oldpullp->v3error("... Location of conflicting pull."); - } - } + if (!oldpullp) { + varp->user3p(pullp); // save off to indicate the pull direction + } else { + if (oldpullp->direction() != pullp->direction()) { + pullp->v3error("Unsupported: Conflicting pull directions."); + oldpullp->v3error("... Location of conflicting pull."); + } + } } void checkUnhandled(AstNode* nodep) { - // Check for unsupported tristate constructs. This is not a 100% check. - // The best way would be to visit the tree again and find any user1p() - // pointers that did not get picked up and expanded. - if (m_alhs && nodep->user1p()) { - nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); - } - // Ignore Var's because they end up adjacent to statements + // Check for unsupported tristate constructs. This is not a 100% check. + // The best way would be to visit the tree again and find any user1p() + // pointers that did not get picked up and expanded. + if (m_alhs && nodep->user1p()) { + nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); + } + // Ignore Var's because they end up adjacent to statements if ((nodep->op1p() && nodep->op1p()->user1p() && !VN_IS(nodep->op1p(), Var)) || (nodep->op2p() && nodep->op2p()->user1p() && !VN_IS(nodep->op1p(), Var)) || (nodep->op3p() && nodep->op3p()->user1p() && !VN_IS(nodep->op1p(), Var)) || (nodep->op4p() && nodep->op4p()->user1p() && !VN_IS(nodep->op1p(), Var))) { - nodep->v3error("Unsupported tristate construct: "<prettyTypeName()); - } + nodep->v3error("Unsupported tristate construct: "<prettyTypeName()); + } } void insertTristates(AstNodeModule* nodep) { - // Go through all the vars and find any that are outputs without drivers - // or inouts without high-Z logic and put a 1'bz driver on them and add - // them to the lhs map so they get expanded correctly. - TristateGraph::VarVec vars = m_tgraph.tristateVars(); - for (TristateGraph::VarVec::iterator ii = vars.begin(); ii != vars.end(); ++ii) { - AstVar* varp = (*ii); - if (m_tgraph.isTristate(varp)) { - VarMap::iterator it = m_lhsmap.find(varp); - if (it == m_lhsmap.end()) { - // set output enable to always be off on this assign statement so that this var is floating + // Go through all the vars and find any that are outputs without drivers + // or inouts without high-Z logic and put a 1'bz driver on them and add + // them to the lhs map so they get expanded correctly. + TristateGraph::VarVec vars = m_tgraph.tristateVars(); + for (TristateGraph::VarVec::iterator ii = vars.begin(); ii != vars.end(); ++ii) { + AstVar* varp = (*ii); + if (m_tgraph.isTristate(varp)) { + VarMap::iterator it = m_lhsmap.find(varp); + if (it == m_lhsmap.end()) { + // set output enable to always be off on this assign + // statement so that this var is floating UINFO(8," Adding driver to var "<width()); zeros.setAllBits0(); - AstConst* constp = new AstConst(varp->fileline(), zeros); - AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, true); - AstNode* newp = new AstAssignW(varp->fileline(), varrefp, constp); - UINFO(9," newoev "<user1p(new AstConst(varp->fileline(),zeros)); - nodep->addStmtp(newp); - mapInsertLhsVarRef(varrefp); // insertTristates will convert - // // to a varref to the __out# variable - } - } - } + AstConst* constp = new AstConst(varp->fileline(), zeros); + AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, true); + AstNode* newp = new AstAssignW(varp->fileline(), varrefp, constp); + UINFO(9," newoev "<user1p(new AstConst(varp->fileline(), zeros)); + nodep->addStmtp(newp); + mapInsertLhsVarRef(varrefp); // insertTristates will convert + // // to a varref to the __out# variable + } + } + } - // Now go through the lhs driver map and generate the output - // enable logic for any tristates. - // Note there might not be any drivers. - for (VarMap::iterator nextit, it = m_lhsmap.begin(); it != m_lhsmap.end(); it = nextit) { - nextit = it; ++nextit; + // Now go through the lhs driver map and generate the output + // enable logic for any tristates. + // Note there might not be any drivers. + for (VarMap::iterator nextit, it = m_lhsmap.begin(); it != m_lhsmap.end(); it = nextit) { + nextit = it; ++nextit; AstVar* invarp = it->first; RefVec* refs = it->second; - // Figure out if this var needs tristate expanded. - if (!m_tgraph.isTristate(invarp)) { - // This var has no tristate logic, so we leave it alone. - UINFO(8, " NO TRISTATE ON:" << invarp << endl); - m_lhsmap.erase(invarp); - delete refs; - continue; - } + // Figure out if this var needs tristate expanded. + if (!m_tgraph.isTristate(invarp)) { + // This var has no tristate logic, so we leave it alone. + UINFO(8, " NO TRISTATE ON:" << invarp << endl); + m_lhsmap.erase(invarp); + delete refs; + continue; + } - ++m_statTriSigs; - m_tgraph.didProcess(invarp); - UINFO(8, " TRISTATE EXPANDING:" << invarp << endl); + ++m_statTriSigs; + m_tgraph.didProcess(invarp); + UINFO(8, " TRISTATE EXPANDING:" << invarp << endl); - // If the lhs var is a port, then we need to create ports for - // the output (__out) and output enable (__en) signals. The - // original port gets converted to an input. Don't tristate expand - // if this is the top level so that we can force the final - // tristate resolution at the top. - AstVar* envarp = NULL; - AstVar* outvarp = NULL; // __out - AstVar* lhsp = invarp; // Variable to assign drive-value to ( or __out) - if (!nodep->isTop() && invarp->isIO()) { - // This var becomes an input - invarp->varType2In(); // convert existing port to type input - // Create an output port (__out) - outvarp = getCreateOutVarp(invarp); - outvarp->varType2Out(); - lhsp = outvarp; // Must assign to __out, not to normal input signal - UINFO(9, " TRISTATE propagates up with "<varType2Out(); - // - outvarp->user1p(envarp); - outvarp->user3p(invarp->user3p()); // AstPull* propagation - if (invarp->user3p()) UINFO(9, "propagate pull to "<user1p()) { + // If the lhs var is a port, then we need to create ports for + // the output (__out) and output enable (__en) signals. The + // original port gets converted to an input. Don't tristate expand + // if this is the top level so that we can force the final + // tristate resolution at the top. + AstVar* envarp = NULL; + AstVar* outvarp = NULL; // __out + AstVar* lhsp = invarp; // Variable to assign drive-value to ( or __out) + if (!nodep->isTop() && invarp->isIO()) { + // This var becomes an input + invarp->varType2In(); // convert existing port to type input + // Create an output port (__out) + outvarp = getCreateOutVarp(invarp); + outvarp->varType2Out(); + lhsp = outvarp; // Must assign to __out, not to normal input signal + UINFO(9, " TRISTATE propagates up with "<varType2Out(); + // + outvarp->user1p(envarp); + outvarp->user3p(invarp->user3p()); // AstPull* propagation + if (invarp->user3p()) UINFO(9, "propagate pull to "<user1p()) { envarp = VN_CAST(invarp->user1p(), Var); // From CASEEQ, foo === 1'bz - } + } - AstNode* orp = NULL; - AstNode* enp = NULL; - AstNode* undrivenp = NULL; + AstNode* orp = NULL; + AstNode* enp = NULL; + AstNode* undrivenp = NULL; - // loop through the lhs drivers to build the driver resolution logic - for (RefVec::iterator ii=refs->begin(); ii != refs->end(); ++ii) { - AstVarRef* refp = (*ii); - int w = lhsp->width(); + // loop through the lhs drivers to build the driver resolution logic + for (RefVec::iterator ii=refs->begin(); ii != refs->end(); ++ii) { + AstVarRef* refp = (*ii); + int w = lhsp->width(); - // create the new lhs driver for this var - AstVar* newlhsp = new AstVar(lhsp->fileline(), - AstVarType::MODULETEMP, - lhsp->name()+"__out"+cvtToStr(m_unique), - VFlagBitPacked(), w); // 2-state ok; sep enable - UINFO(9," newout "<addStmtp(newlhsp); - refp->varp(newlhsp); // assign the new var to the varref - refp->name(newlhsp->name()); + // create the new lhs driver for this var + AstVar* newlhsp = new AstVar(lhsp->fileline(), + AstVarType::MODULETEMP, + lhsp->name()+"__out"+cvtToStr(m_unique), + VFlagBitPacked(), w); // 2-state ok; sep enable + UINFO(9," newout "<addStmtp(newlhsp); + refp->varp(newlhsp); // assign the new var to the varref + refp->name(newlhsp->name()); - // create a new var for this drivers enable signal - AstVar* newenp = new AstVar(lhsp->fileline(), - AstVarType::MODULETEMP, - lhsp->name()+"__en"+cvtToStr(m_unique++), - VFlagBitPacked(), w); // 2-state ok - UINFO(9," newenp "<addStmtp(newenp); + // create a new var for this drivers enable signal + AstVar* newenp = new AstVar(lhsp->fileline(), + AstVarType::MODULETEMP, + lhsp->name()+"__en"+cvtToStr(m_unique++), + VFlagBitPacked(), w); // 2-state ok + UINFO(9," newenp "<addStmtp(newenp); - AstNode* enassp = new AstAssignW(refp->fileline(), - new AstVarRef(refp->fileline(), newenp, true), - getEnp(refp)); - UINFO(9," newass "<addStmtp(enassp); + AstNode* enassp = new AstAssignW(refp->fileline(), + new AstVarRef(refp->fileline(), newenp, true), + getEnp(refp)); + UINFO(9," newass "<addStmtp(enassp); - // now append this driver to the driver logic. - AstNode* ref1p = new AstVarRef(refp->fileline(), newlhsp,false); - AstNode* ref2p = new AstVarRef(refp->fileline(), newenp, false); - AstNode* andp = new AstAnd(refp->fileline(), ref1p, ref2p); + // now append this driver to the driver logic. + AstNode* ref1p = new AstVarRef(refp->fileline(), newlhsp, false); + AstNode* ref2p = new AstVarRef(refp->fileline(), newenp, false); + AstNode* andp = new AstAnd(refp->fileline(), ref1p, ref2p); - // or this to the others - orp = (!orp) ? andp : new AstOr(refp->fileline(), orp, andp); + // or this to the others + orp = (!orp) ? andp : new AstOr(refp->fileline(), orp, andp); - if (envarp) { - AstNode* ref3p = new AstVarRef(refp->fileline(), newenp, false); - enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p); - } - AstNode* tmp = new AstNot(newenp->fileline(), new AstVarRef(newenp->fileline(), newenp, false)); - undrivenp = ((!undrivenp) ? tmp - : new AstAnd(refp->fileline(), tmp, undrivenp)); - } - if (!undrivenp) { // No drivers on the bus + if (envarp) { + AstNode* ref3p = new AstVarRef(refp->fileline(), newenp, false); + enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p); + } + AstNode* tmp = new AstNot(newenp->fileline(), + new AstVarRef(newenp->fileline(), newenp, false)); + undrivenp = ((!undrivenp) ? tmp + : new AstAnd(refp->fileline(), tmp, undrivenp)); + } + if (!undrivenp) { // No drivers on the bus V3Number ones(invarp, lhsp->width()); ones.setAllBits1(); undrivenp = new AstConst(invarp->fileline(), ones); } @@ -600,386 +611,401 @@ class TristateVisitor : public TristateBaseVisitor { // the pull direction to any undriven pins. V3Number pull(invarp, lhsp->width()); AstPull* pullp = static_cast(lhsp->user3p()); - if (pullp && pullp->direction() == 1) { - pull.setAllBits1(); - UINFO(9,"Has pullup "<fileline(), undrivenp, - new AstConst(invarp->fileline(), pull)); - orp = new AstOr(invarp->fileline(), orp, undrivenp); - } else { - undrivenp->deleteTree(); VL_DANGLING(undrivenp); - } - if (envarp) { - nodep->addStmtp(new AstAssignW(enp->fileline(), - new AstVarRef(envarp->fileline(), - envarp, true), enp)); - } - // __out (child) or (parent) = drive-value expression - AstNode* assp = new AstAssignW(lhsp->fileline(), - new AstVarRef(lhsp->fileline(), - lhsp, - true), - orp); - assp->user2(U2_BOTH); // Don't process further; already resolved - if (debug()>=9) assp->dumpTree(cout,"-lhsp-eqn: "); - nodep->addStmtp(assp); - // Delete the map and vector list now that we have expanded it. - m_lhsmap.erase(invarp); - delete refs; - } + if (pullp && pullp->direction() == 1) { + pull.setAllBits1(); + UINFO(9,"Has pullup "<fileline(), undrivenp, + new AstConst(invarp->fileline(), pull)); + orp = new AstOr(invarp->fileline(), orp, undrivenp); + } else { + undrivenp->deleteTree(); VL_DANGLING(undrivenp); + } + if (envarp) { + nodep->addStmtp(new AstAssignW(enp->fileline(), + new AstVarRef(envarp->fileline(), + envarp, true), enp)); + } + // __out (child) or (parent) = drive-value expression + AstNode* assp = new AstAssignW(lhsp->fileline(), + new AstVarRef(lhsp->fileline(), + lhsp, + true), + orp); + assp->user2(U2_BOTH); // Don't process further; already resolved + if (debug()>=9) assp->dumpTree(cout, "-lhsp-eqn: "); + nodep->addStmtp(assp); + // Delete the map and vector list now that we have expanded it. + m_lhsmap.erase(invarp); + delete refs; + } } // VISITORS virtual void visit(AstConst* nodep) { - UINFO(9,dbgState()<num().hasZ()) { - m_tgraph.setTristate(nodep); - } - } else { - // Detect any Z consts and convert them to 0's with an enable that is also 0. - if (m_alhs && nodep->user1p()) { - // A pin with 1'b0 or similar connection results in an assign with constant on LHS - // due to the pinReconnectSimple call in visit AstPin. - // We can ignore the output override by making a temporary - AstVar* varp = getCreateUnconnVarp(nodep, nodep->dtypep()); - AstNode* newp = new AstVarRef(nodep->fileline(), varp, true); - UINFO(9," const->"<replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - } - else if (m_tgraph.isTristate(nodep)) { - m_tgraph.didProcess(nodep); - FileLine* fl = nodep->fileline(); + UINFO(9,dbgState()<num().hasZ()) { + m_tgraph.setTristate(nodep); + } + } else { + // Detect any Z consts and convert them to 0's with an enable that is also 0. + if (m_alhs && nodep->user1p()) { + // A pin with 1'b0 or similar connection results in an assign with constant on LHS + // due to the pinReconnectSimple call in visit AstPin. + // We can ignore the output override by making a temporary + AstVar* varp = getCreateUnconnVarp(nodep, nodep->dtypep()); + AstNode* newp = new AstVarRef(nodep->fileline(), varp, true); + UINFO(9," const->"<replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + } + else if (m_tgraph.isTristate(nodep)) { + m_tgraph.didProcess(nodep); + FileLine* fl = nodep->fileline(); V3Number numz (nodep, nodep->width()); numz.opBitsZ(nodep->num()); // Z->1, else 0 V3Number numz0(nodep, nodep->width()); numz0.opNot(numz); // Z->0, else 1 V3Number num1 (nodep, nodep->width()); num1.opAnd(nodep->num(), numz0); // 01X->01X, Z->0 - AstConst* newconstp = new AstConst(fl, num1); - AstConst* enp = new AstConst(fl, numz0); - nodep->replaceWith(newconstp); - pushDeletep(nodep); VL_DANGLING(nodep); - newconstp->user1p(enp); // propagate up constant with non-Z bits as 1 - } - } + AstConst* newconstp = new AstConst(fl, num1); + AstConst* enp = new AstConst(fl, numz0); + nodep->replaceWith(newconstp); + pushDeletep(nodep); VL_DANGLING(nodep); + newconstp->user1p(enp); // Propagate up constant with non-Z bits as 1 + } + } } virtual void visit(AstCond* nodep) { - if (m_graphing) { + if (m_graphing) { iterateChildren(nodep); - if (m_alhs) { - associateLogic(nodep, nodep->expr1p()); - associateLogic(nodep, nodep->expr2p()); - } else { - associateLogic(nodep->expr1p(), nodep); - associateLogic(nodep->expr2p(), nodep); - } - } else { - if (m_alhs && nodep->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } + if (m_alhs) { + associateLogic(nodep, nodep->expr1p()); + associateLogic(nodep, nodep->expr2p()); + } else { + associateLogic(nodep->expr1p(), nodep); + associateLogic(nodep->expr2p(), nodep); + } + } else { + if (m_alhs && nodep->user1p()) { + nodep->v3error("Unsupported LHS tristate construct: " + <prettyTypeName()); + return; + } iterateChildren(nodep); - UINFO(9,dbgState()<condp(); - if (condp->user1p()) { - condp->v3error("Unsupported: don't know how to deal with tristate logic in the conditional expression"); - } - AstNode* expr1p = nodep->expr1p(); - AstNode* expr2p = nodep->expr2p(); - if (expr1p->user1p() || expr2p->user1p()) { // else no tristates - m_tgraph.didProcess(nodep); - AstNode* en1p = getEnp(expr1p); - AstNode* en2p = getEnp(expr2p); - // The output enable of a cond is a cond of the output enable of the - // two expressions with the same conditional. - AstNode* enp = new AstCond(nodep->fileline(), condp->cloneTree(false), en1p, en2p); - UINFO(9," newcond "<user1p(enp); // propagate up COND(lhsp->enable, rhsp->enable) - expr1p->user1p(NULL); - expr2p->user1p(NULL); - } - } + UINFO(9,dbgState()<condp(); + if (condp->user1p()) { + condp->v3error("Unsupported: don't know how to deal with tristate logic in the conditional expression"); + } + AstNode* expr1p = nodep->expr1p(); + AstNode* expr2p = nodep->expr2p(); + if (expr1p->user1p() || expr2p->user1p()) { // else no tristates + m_tgraph.didProcess(nodep); + AstNode* en1p = getEnp(expr1p); + AstNode* en2p = getEnp(expr2p); + // The output enable of a cond is a cond of the output enable of the + // two expressions with the same conditional. + AstNode* enp = new AstCond(nodep->fileline(), condp->cloneTree(false), en1p, en2p); + UINFO(9," newcond "<user1p(enp); // propagate up COND(lhsp->enable, rhsp->enable) + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } + } } virtual void visit(AstSel* nodep) { - if (m_graphing) { + if (m_graphing) { iterateChildren(nodep); - if (m_alhs) { - associateLogic(nodep, nodep->fromp()); - } else { - associateLogic(nodep->fromp(), nodep); - } - } else { - if (m_alhs) { - UINFO(9,dbgState()<user1p()) { - // Form a "deposit" instruction. Would be nicer if we made this a new AST type - AstNode* newp = newEnableDeposit(nodep, nodep->user1p()); - nodep->fromp()->user1p(newp); // Push to varref (etc) - if (debug()>=9) newp->dumpTree(cout,"-assign-sel; "); - m_tgraph.didProcess(nodep); - } + if (m_alhs) { + associateLogic(nodep, nodep->fromp()); + } else { + associateLogic(nodep->fromp(), nodep); + } + } else { + if (m_alhs) { + UINFO(9,dbgState()<user1p()) { + // Form a "deposit" instruction. Would be nicer if we made this a new AST type + AstNode* newp = newEnableDeposit(nodep, nodep->user1p()); + nodep->fromp()->user1p(newp); // Push to varref (etc) + if (debug()>=9) newp->dumpTree(cout, "-assign-sel; "); + m_tgraph.didProcess(nodep); + } iterateChildren(nodep); - } else { + } else { iterateChildren(nodep); - UINFO(9,dbgState()<lsbp()->user1p()) { - nodep->v3error("Unsupported RHS tristate construct: "<prettyTypeName()); - } - if (nodep->fromp()->user1p()) { // SEL(VARREF,lsb) - AstNode* en1p = getEnp(nodep->fromp()); - AstNode* enp = new AstSel(nodep->fileline(), en1p, - nodep->lsbp()->cloneTree(true), nodep->widthp()->cloneTree(true)); - UINFO(9," newsel "<user1p(enp); // propagate up SEL(fromp->enable, value) - m_tgraph.didProcess(nodep); - } - } - } + UINFO(9,dbgState()<lsbp()->user1p()) { + nodep->v3error("Unsupported RHS tristate construct: "<prettyTypeName()); + } + if (nodep->fromp()->user1p()) { // SEL(VARREF, lsb) + AstNode* en1p = getEnp(nodep->fromp()); + AstNode* enp = new AstSel(nodep->fileline(), en1p, + nodep->lsbp()->cloneTree(true), + nodep->widthp()->cloneTree(true)); + UINFO(9," newsel "<user1p(enp); // propagate up SEL(fromp->enable, value) + m_tgraph.didProcess(nodep); + } + } + } } virtual void visit(AstConcat* nodep) { - if (m_graphing) { + if (m_graphing) { iterateChildren(nodep); - if (m_alhs) { - associateLogic(nodep, nodep->lhsp()); - associateLogic(nodep, nodep->rhsp()); - } else { - associateLogic(nodep->lhsp(), nodep); - associateLogic(nodep->rhsp(), nodep); - } - } - else { - if (m_alhs) { - UINFO(9,dbgState()<user1p()) { - // Each half of the concat gets a select of the enable expression - AstNode* enp = nodep->user1p(); - nodep->user1p(NULL); - nodep->lhsp()->user1p(new AstSel(nodep->fileline(), - enp->cloneTree(true), - nodep->rhsp()->width(), - nodep->lhsp()->width())); - nodep->rhsp()->user1p(new AstSel(nodep->fileline(), - enp, - 0, - nodep->rhsp()->width())); - m_tgraph.didProcess(nodep); - } + if (m_alhs) { + associateLogic(nodep, nodep->lhsp()); + associateLogic(nodep, nodep->rhsp()); + } else { + associateLogic(nodep->lhsp(), nodep); + associateLogic(nodep->rhsp(), nodep); + } + } + else { + if (m_alhs) { + UINFO(9,dbgState()<user1p()) { + // Each half of the concat gets a select of the enable expression + AstNode* enp = nodep->user1p(); + nodep->user1p(NULL); + nodep->lhsp()->user1p(new AstSel(nodep->fileline(), + enp->cloneTree(true), + nodep->rhsp()->width(), + nodep->lhsp()->width())); + nodep->rhsp()->user1p(new AstSel(nodep->fileline(), + enp, + 0, + nodep->rhsp()->width())); + m_tgraph.didProcess(nodep); + } iterateChildren(nodep); - } else { + } else { iterateChildren(nodep); - UINFO(9,dbgState()<lhsp(); - AstNode* expr2p = nodep->rhsp(); - if (expr1p->user1p() || expr2p->user1p()) { // else no tristates - m_tgraph.didProcess(nodep); - AstNode* en1p = getEnp(expr1p); - AstNode* en2p = getEnp(expr2p); - AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p); - UINFO(9," newconc "<user1p(enp); // propagate up CONCAT(lhsp->enable, rhsp->enable) - expr1p->user1p(NULL); - expr2p->user1p(NULL); - } - } - } + UINFO(9,dbgState()<lhsp(); + AstNode* expr2p = nodep->rhsp(); + if (expr1p->user1p() || expr2p->user1p()) { // else no tristates + m_tgraph.didProcess(nodep); + AstNode* en1p = getEnp(expr1p); + AstNode* en2p = getEnp(expr2p); + AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p); + UINFO(9," newconc "<user1p(enp); // propagate up CONCAT(lhsp->enable, rhsp->enable) + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } + } + } } virtual void visit(AstBufIf1* nodep) { - // For BufIf1, the enable is the LHS expression + // For BufIf1, the enable is the LHS expression iterateChildren(nodep); - UINFO(9,dbgState()<rhsp(), nodep); - m_tgraph.setTristate(nodep); - } else { - if (debug()>=9) nodep->backp()->dumpTree(cout,"-bufif: "); - if (m_alhs) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } - m_tgraph.didProcess(nodep); - AstNode* expr1p = nodep->lhsp()->unlinkFrBack(); - AstNode* expr2p = nodep->rhsp()->unlinkFrBack(); - AstNode* enp; - if (AstNode* en2p = expr2p->user1p()) { - enp = new AstAnd(nodep->fileline(), expr1p, en2p); - } else { - enp = expr1p; - } - expr1p->user1p(NULL); - expr2p->user1p(enp); // Becomes new node - // Don't need the BufIf any more, can just have the data direct - nodep->replaceWith(expr2p); - UINFO(9," bufif datap="<v3error("Unsupported LHS tristate construct: "<prettyTypeName()); + return; + } + m_tgraph.didProcess(nodep); + AstNode* expr1p = nodep->lhsp()->unlinkFrBack(); + AstNode* expr2p = nodep->rhsp()->unlinkFrBack(); + AstNode* enp; + if (AstNode* en2p = expr2p->user1p()) { + enp = new AstAnd(nodep->fileline(), expr1p, en2p); + } else { + enp = expr1p; + } + expr1p->user1p(NULL); + expr2p->user1p(enp); // Becomes new node + // Don't need the BufIf any more, can just have the data direct + nodep->replaceWith(expr2p); + UINFO(9," bufif datap="<prettyTypeName()); return; } - // ANDs and Z's have issues. Earlier optimizations convert - // expressions like "(COND) ? 1'bz : 1'b0" to "COND & 1'bz". So we - // have to define what is means to AND 1'bz with other - // expressions. I don't think this is spec, but here I take the - // approach that when one expression is 1, that the Z passes. This - // makes the COND's work. It is probably better to not perform the - // conditional optimization if the bits are Z. - // - // ORs have the same issues as ANDs. Earlier optimizations convert - // expressions like "(COND) ? 1'bz : 1'b1" to "COND | 1'bz". So we - // have to define what is means to OR 1'bz with other - // expressions. Here I take the approach that when one expression - // is 0, that is passes the other. - AstNode* expr1p = nodep->lhsp(); - AstNode* expr2p = nodep->rhsp(); - if (!expr1p->user1p() && !expr2p->user1p()) { - return; // no tristates in either expression, so nothing to do - } - m_tgraph.didProcess(nodep); - AstNode* en1p = getEnp(expr1p); - AstNode* en2p = getEnp(expr2p); - AstNode* subexpr1p = expr1p->cloneTree(false); - AstNode* subexpr2p = expr2p->cloneTree(false); - if (isAnd) { - subexpr1p = new AstNot(nodep->fileline(), subexpr1p); - subexpr2p = new AstNot(nodep->fileline(), subexpr2p); - } - // calc new output enable - AstNode* enp = new AstOr(nodep->fileline(), - new AstAnd(nodep->fileline(), en1p, en2p), - new AstOr(nodep->fileline(), - new AstAnd(nodep->fileline(), - en1p->cloneTree(false), - subexpr1p), - new AstAnd(nodep->fileline(), - en2p->cloneTree(false), - subexpr2p))); - UINFO(9," neweqn "<user1p(enp); - expr1p->user1p(NULL); - expr2p->user1p(NULL); - } + UINFO(9,dbgState()<lhsp(), nodep); + associateLogic(nodep->rhsp(), nodep); + } else { + if (m_alhs && nodep->user1p()) { + nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); + return; + } + // ANDs and Z's have issues. Earlier optimizations convert + // expressions like "(COND) ? 1'bz : 1'b0" to "COND & 1'bz". So we + // have to define what is means to AND 1'bz with other + // expressions. I don't think this is spec, but here I take the + // approach that when one expression is 1, that the Z passes. This + // makes the COND's work. It is probably better to not perform the + // conditional optimization if the bits are Z. + // + // ORs have the same issues as ANDs. Earlier optimizations convert + // expressions like "(COND) ? 1'bz : 1'b1" to "COND | 1'bz". So we + // have to define what is means to OR 1'bz with other + // expressions. Here I take the approach that when one expression + // is 0, that is passes the other. + AstNode* expr1p = nodep->lhsp(); + AstNode* expr2p = nodep->rhsp(); + if (!expr1p->user1p() && !expr2p->user1p()) { + return; // no tristates in either expression, so nothing to do + } + m_tgraph.didProcess(nodep); + AstNode* en1p = getEnp(expr1p); + AstNode* en2p = getEnp(expr2p); + AstNode* subexpr1p = expr1p->cloneTree(false); + AstNode* subexpr2p = expr2p->cloneTree(false); + if (isAnd) { + subexpr1p = new AstNot(nodep->fileline(), subexpr1p); + subexpr2p = new AstNot(nodep->fileline(), subexpr2p); + } + // calc new output enable + AstNode* enp = new AstOr(nodep->fileline(), + new AstAnd(nodep->fileline(), en1p, en2p), + new AstOr(nodep->fileline(), + new AstAnd(nodep->fileline(), + en1p->cloneTree(false), + subexpr1p), + new AstAnd(nodep->fileline(), + en2p->cloneTree(false), + subexpr2p))); + UINFO(9," neweqn "<user1p(enp); + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } } virtual void visit(AstAnd* nodep) { - visitAndOr(nodep,true); + visitAndOr(nodep, true); } virtual void visit(AstOr* nodep) { - visitAndOr(nodep,false); + visitAndOr(nodep, false); } void visitAssign(AstNodeAssign* nodep) { - if (m_graphing) { - if (nodep->user2() & U2_GRAPHING) return; - nodep->user2(U2_GRAPHING); - m_logicp = nodep; + if (m_graphing) { + if (nodep->user2() & U2_GRAPHING) return; + nodep->user2(U2_GRAPHING); + m_logicp = nodep; iterateAndNextNull(nodep->rhsp()); - m_alhs = true; + m_alhs = true; iterateAndNextNull(nodep->lhsp()); - m_alhs = false; - associateLogic(nodep->rhsp(), nodep); - associateLogic(nodep, nodep->lhsp()); - m_logicp = NULL; - } else { - if (nodep->user2() & U2_NONGRAPH) return; // Iterated here, or created assignment to ignore - nodep->user2(U2_NONGRAPH); + m_alhs = false; + associateLogic(nodep->rhsp(), nodep); + associateLogic(nodep, nodep->lhsp()); + m_logicp = NULL; + } else { + if (nodep->user2() & U2_NONGRAPH) return; // Iterated here, or created assignment to ignore + nodep->user2(U2_NONGRAPH); iterateAndNextNull(nodep->rhsp()); - UINFO(9,dbgState()<=9) nodep->dumpTree(cout,"-assign: "); - // if the rhsp of this assign statement has an output enable driver, - // then propage the corresponding output enable assign statement. - // down the lvalue tree by recursion for eventual attachment to - // the appropriate output signal's VarRef. - if (nodep->rhsp()->user1p()) { - nodep->lhsp()->user1p(nodep->rhsp()->user1p()); - nodep->rhsp()->user1p(NULL); - UINFO(9," enp<-rhs "<lhsp()->user1p()<=9) nodep->dumpTree(cout, "-assign: "); + // if the rhsp of this assign statement has an output enable driver, + // then propage the corresponding output enable assign statement. + // down the lvalue tree by recursion for eventual attachment to + // the appropriate output signal's VarRef. + if (nodep->rhsp()->user1p()) { + nodep->lhsp()->user1p(nodep->rhsp()->user1p()); + nodep->rhsp()->user1p(NULL); + UINFO(9," enp<-rhs "<lhsp()->user1p()<lhsp()); - m_alhs = false; - } + m_alhs = false; + } } virtual void visit(AstAssignW* nodep) { - visitAssign(nodep); + visitAssign(nodep); } virtual void visit(AstAssign* nodep) { - visitAssign(nodep); + visitAssign(nodep); } void visitCaseEq(AstNodeBiop* nodep, bool neq) { - if (m_graphing) { + if (m_graphing) { iterateChildren(nodep); - } else { - checkUnhandled(nodep); - // Unsupported: A === 3'b000 should compare with the enables, but we don't do - // so at present, we only compare if there is a z in the equation. - // Otherwise we'd need to attach an enable to every signal, then optimize them - // away later when we determine the signal has no tristate + } else { + checkUnhandled(nodep); + // Unsupported: A === 3'b000 should compare with the enables, but we don't do + // so at present, we only compare if there is a z in the equation. + // Otherwise we'd need to attach an enable to every signal, then optimize them + // away later when we determine the signal has no tristate iterateChildren(nodep); - UINFO(9,dbgState()<lhsp(), Const); // Constification always moves const to LHS AstVarRef* varrefp = VN_CAST(nodep->rhsp(), VarRef); // Input variable - if (constp && constp->user1p() && varrefp) { - // 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in)) - varrefp->unlinkFrBack(); - FileLine* fl = nodep->fileline(); + if (constp && constp->user1p() && varrefp) { + // 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in)) + varrefp->unlinkFrBack(); + FileLine* fl = nodep->fileline(); V3Number oneIfEn = VN_CAST(constp->user1p(), Const)->num(); // visit(AstConst) already split into en/ones - V3Number oneIfEnOne = constp->num(); - AstVar* envarp = getCreateEnVarp(varrefp->varp()); + V3Number oneIfEnOne = constp->num(); + AstVar* envarp = getCreateEnVarp(varrefp->varp()); AstNode* newp = new AstLogAnd(fl, new AstEq(fl, new AstConst(fl, oneIfEn), new AstVarRef(fl, envarp, false)), // Keep the caseeq if there are X's present new AstEqCase(fl, new AstConst(fl, oneIfEnOne), varrefp)); - if (neq) newp = new AstLogNot(fl, newp); - UINFO(9," newceq "<=9) nodep->dumpTree(cout,"-caseeq-old: "); - if (debug()>=9) newp->dumpTree(cout,"-caseeq-new: "); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - } else { - checkUnhandled(nodep); - } - } + if (neq) newp = new AstLogNot(fl, newp); + UINFO(9," newceq "<=9) nodep->dumpTree(cout, "-caseeq-old: "); + if (debug()>=9) newp->dumpTree(cout, "-caseeq-new: "); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + } else { + checkUnhandled(nodep); + } + } } void visitEqNeqWild(AstNodeBiop* nodep) { if (!VN_IS(nodep->rhsp(), Const)) { - nodep->v3error("Unsupported: RHS of ==? or !=? must be constant to be synthesizable"); // Says spec. - // rhs we want to keep X/Z intact, so otherwise ignore - } + nodep->v3error("Unsupported: RHS of ==? or !=? must be constant to be synthesizable"); // Says spec. + // rhs we want to keep X/Z intact, so otherwise ignore + } iterateAndNextNull(nodep->lhsp()); - if (nodep->lhsp()->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } + if (nodep->lhsp()->user1p()) { + nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); + return; + } } virtual void visit(AstEqCase* nodep) { - visitCaseEq(nodep,false); + visitCaseEq(nodep, false); } virtual void visit(AstNeqCase* nodep) { - visitCaseEq(nodep,true); + visitCaseEq(nodep, true); } virtual void visit(AstEqWild* nodep) { - visitEqNeqWild(nodep); + visitEqNeqWild(nodep); } virtual void visit(AstNeqWild* nodep) { - visitEqNeqWild(nodep); + visitEqNeqWild(nodep); } virtual void visit(AstPull* nodep) { - UINFO(9,dbgState()<lhsp(), VarRef)) { varrefp = VN_CAST(nodep->lhsp(), VarRef); @@ -1011,27 +1037,27 @@ class TristateVisitor : public TristateBaseVisitor { } } if (!m_graphing) { - nodep->unlinkFrBack(); - pushDeletep(nodep); VL_DANGLING(nodep); // Node must persist as user3p points to it - } + nodep->unlinkFrBack(); + pushDeletep(nodep); VL_DANGLING(nodep); // Node must persist as user3p points to it + } } void iteratePinGuts(AstPin* nodep) { - if (m_graphing) { - m_logicp = nodep; - if (nodep->exprp()) { - associateLogic(nodep->exprp(), nodep); - associateLogic(nodep, nodep->exprp()); - } + if (m_graphing) { + m_logicp = nodep; + if (nodep->exprp()) { + associateLogic(nodep->exprp(), nodep); + associateLogic(nodep, nodep->exprp()); + } iterateChildren(nodep); - m_logicp = NULL; - } else { - // All heavy lifting completed in graph visitor. - if (nodep->exprp()) { - m_tgraph.didProcess(nodep); - } + m_logicp = NULL; + } else { + // All heavy lifting completed in graph visitor. + if (nodep->exprp()) { + m_tgraph.didProcess(nodep); + } iterateChildren(nodep); - } + } } // .tri(SEL(trisig,x)) becomes @@ -1039,36 +1065,39 @@ class TristateVisitor : public TristateBaseVisitor { // trisig__pinin = SEL(trisig,x) // via pinReconnectSimple // OUTPUT: -> (VARREF(trisig__pinout)) // SEL(trisig,x) = trisig__pinout - // ^-- ->user1p() == trisig__pinen + // ^-- ->user1p() == trisig__pinen // ENABLE: -> (VARREF(trisig__pinen) - // Added complication is the signal may be an output/inout or just input with tie off (or not) up top - // PIN PORT NEW PORTS AND CONNECTIONS - // N/C input in(from-resolver), __en(to-resolver-only), __out(to-resolver-only) - // N/C inout Spec says illegal - // N/C output Unsupported; Illegal? - // wire input in(from-resolver-with-wire-value), __en(from-resolver-wire), __out(to-resolver-only) - // wire inout in, __en, __out - // wire output in, __en, __out - // const input in(from-resolver-with-const-value), __en(from-resolver-const), __out(to-resolver-only) - // const inout Spec says illegal - // const output Unsupported; Illegal? + // Added complication is the signal may be an output/inout or just + // input with tie off (or not) up top + // PIN PORT NEW PORTS AND CONNECTIONS + // N/C input in(from-resolver), __en(to-resolver-only), __out(to-resolver-only) + // N/C inout Spec says illegal + // N/C output Unsupported; Illegal? + // wire input in(from-resolver-with-wire-value), __en(from-resolver-wire), + // __out(to-resolver-only) + // wire inout in, __en, __out + // wire output in, __en, __out + // const input in(from-resolver-with-const-value), __en(from-resolver-const), + // __out(to-resolver-only) + // const inout Spec says illegal + // const output Unsupported; Illegal? virtual void visit(AstPin* nodep) { - if (m_graphing) { - if (nodep->user2() & U2_GRAPHING) return; // This pin is already expanded - nodep->user2(U2_GRAPHING); - // Find child module's new variables. + if (m_graphing) { + if (nodep->user2() & U2_GRAPHING) return; // This pin is already expanded + nodep->user2(U2_GRAPHING); + // Find child module's new variables. AstVar* enModVarp = static_cast(nodep->modVarp()->user1p()); - if (!enModVarp) { - if (nodep->exprp()) { - // May have an output only that later connects to a tristate, so simplify now. + if (!enModVarp) { + if (nodep->exprp()) { + // May have an output only that later connects to a tristate, so simplify now. V3Inst::pinReconnectSimple(nodep, m_cellp, false); - } - iteratePinGuts(nodep); - return; // No __en signals on this pin - } - // Tristate exists: - UINFO(9,dbgState()<=9) nodep->dumpTree(cout,"-pin-pre: "); + } + iteratePinGuts(nodep); + return; // No __en signals on this pin + } + // Tristate exists: + UINFO(9,dbgState()<=9) nodep->dumpTree(cout, "-pin-pre: "); // Empty/in-only; need Z to propagate bool inDeclProcessing = (nodep->exprp() @@ -1084,228 +1113,232 @@ class TristateVisitor : public TristateBaseVisitor { nodep->exprp(new AstVarRef(nodep->fileline(), ucVarp, // We converted, so use declaration output state nodep->modVarp()->declDirection().isWritable())); - m_tgraph.setTristate(ucVarp); - // We don't need a driver on the wire; the lack of one will default to tristate - } else if (inDeclProcessing) { // Not an input that was a converted tristate - // Input only may have driver in underneath module which would stomp - // the input value. So make a temporary connection. + m_tgraph.setTristate(ucVarp); + // We don't need a driver on the wire; the lack of one will default to tristate + } else if (inDeclProcessing) { // Not an input that was a converted tristate + // Input only may have driver in underneath module which would stomp + // the input value. So make a temporary connection. AstAssignW* reAssignp = V3Inst::pinReconnectSimple(nodep, m_cellp, true, true); - UINFO(5,"Input pin buffering: "<lhsp()); - } + UINFO(5,"Input pin buffering: "<lhsp()); + } - // pinReconnectSimple needs to presume input or output behavior; we need both - // Therefore, create the enable, output and separate input pin, then pinReconnectSimple all - - // Create the output enable pin, connect to new signal - AstNode* enrefp; - { - AstVar* enVarp = new AstVar(nodep->fileline(), - AstVarType::MODULETEMP, - nodep->name() + "__en" + cvtToStr(m_unique++), - VFlagBitPacked(), enModVarp->width()); - UINFO(9," newenv "<fileline(), - nodep->pinNum(), - enModVarp->name(), // should be {var}"__en" - new AstVarRef(nodep->fileline(), enVarp, true)); - enpinp->modVarp(enModVarp); - UINFO(9," newpin "<user2(U2_BOTH); // don't iterate the pin later - nodep->addNextHere(enpinp); - m_modp->addStmtp(enVarp); - enrefp = new AstVarRef(nodep->fileline(), enVarp, false); - UINFO(9," newvrf "<=9) enpinp->dumpTree(cout,"-pin-ena: "); - } - // Create new output pin - AstAssignW* outAssignp = NULL; // If reconnected, the related assignment + // pinReconnectSimple needs to presume input or output behavior; we need both + // Therefore, create the enable, output and separate input pin, + // then pinReconnectSimple all + // Create the output enable pin, connect to new signal + AstNode* enrefp; + { + AstVar* enVarp = new AstVar(nodep->fileline(), + AstVarType::MODULETEMP, + nodep->name() + "__en" + cvtToStr(m_unique++), + VFlagBitPacked(), enModVarp->width()); + UINFO(9," newenv "<fileline(), + nodep->pinNum(), + enModVarp->name(), // should be {var}"__en" + new AstVarRef(nodep->fileline(), enVarp, true)); + enpinp->modVarp(enModVarp); + UINFO(9," newpin "<user2(U2_BOTH); // don't iterate the pin later + nodep->addNextHere(enpinp); + m_modp->addStmtp(enVarp); + enrefp = new AstVarRef(nodep->fileline(), enVarp, false); + UINFO(9," newvrf "<=9) enpinp->dumpTree(cout, "-pin-ena: "); + } + // Create new output pin + AstAssignW* outAssignp = NULL; // If reconnected, the related assignment AstPin* outpinp = NULL; AstVar* outModVarp = static_cast(nodep->modVarp()->user4p()); if (!outModVarp) { // At top, no need for __out as might be input only. Otherwise resolvable. if (!m_modp->isTop()) nodep->v3fatalSrc("Unlinked"); } else { - AstNode* outexprp = nodep->exprp()->cloneTree(false); // Note has lvalue() set - outpinp = new AstPin(nodep->fileline(), - nodep->pinNum(), - outModVarp->name(), // should be {var}"__out" - outexprp); - outpinp->modVarp(outModVarp); - UINFO(9," newpin "<user2(U2_BOTH); // don't iterate the pin later - nodep->addNextHere(outpinp); - // Simplify - if (inDeclProcessing) { // Not an input that was a converted tristate - // The pin is an input, but we need an output - // The if() above is needed because the Visitor is simple, it will flip ArraySel's and such, - // but if the pin is an input the earlier reconnectSimple made it a VarRef without any ArraySel, etc - TristatePinVisitor visitor (outexprp, m_tgraph, true); - } - if (debug()>=9) outpinp->dumpTree(cout,"-pin-opr: "); + AstNode* outexprp = nodep->exprp()->cloneTree(false); // Note has lvalue() set + outpinp = new AstPin(nodep->fileline(), + nodep->pinNum(), + outModVarp->name(), // should be {var}"__out" + outexprp); + outpinp->modVarp(outModVarp); + UINFO(9," newpin "<user2(U2_BOTH); // don't iterate the pin later + nodep->addNextHere(outpinp); + // Simplify + if (inDeclProcessing) { // Not an input that was a converted tristate + // The pin is an input, but we need an output + // The if() above is needed because the Visitor is + // simple, it will flip ArraySel's and such, but if the + // pin is an input the earlier reconnectSimple made it + // a VarRef without any ArraySel, etc + TristatePinVisitor visitor (outexprp, m_tgraph, true); + } + if (debug()>=9) outpinp->dumpTree(cout, "-pin-opr: "); outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, true); // Note may change outpinp->exprp() - if (debug()>=9) outpinp->dumpTree(cout,"-pin-out: "); - if (debug()>=9 && outAssignp) outAssignp->dumpTree(cout,"-pin-out: "); - // Must still iterate the outAssignp, as need to build output equation - } + if (debug()>=9) outpinp->dumpTree(cout, "-pin-out: "); + if (debug()>=9 && outAssignp) outAssignp->dumpTree(cout, "-pin-out: "); + // Must still iterate the outAssignp, as need to build output equation + } - // Existing pin becomes an input, and we mark each resulting signal as tristate - TristatePinVisitor visitor (nodep->exprp(), m_tgraph, false); + // Existing pin becomes an input, and we mark each resulting signal as tristate + TristatePinVisitor visitor (nodep->exprp(), m_tgraph, false); AstNode* inAssignp = V3Inst::pinReconnectSimple(nodep, m_cellp, true); // Note may change nodep->exprp() - if (debug()>=9) nodep->dumpTree(cout,"-pin-in: "); - if (debug()>=9 && inAssignp) inAssignp->dumpTree(cout,"-pin-as: "); + if (debug()>=9) nodep->dumpTree(cout, "-pin-in: "); + if (debug()>=9 && inAssignp) inAssignp->dumpTree(cout, "-pin-as: "); - // Connect enable to output signal - AstVarRef* exprrefp; // Tristate variable that the Pin's expression refers to - if (!outAssignp) { + // Connect enable to output signal + AstVarRef* exprrefp; // Tristate variable that the Pin's expression refers to + if (!outAssignp) { if (!outpinp) { exprrefp = NULL; // Primary input only } else { // pinReconnect should have converted this exprrefp = VN_CAST(outpinp->exprp(), VarRef); - if (!exprrefp) nodep->v3error("Unsupported tristate port expression: "<exprp()->prettyTypeName()); + if (!exprrefp) nodep->v3error("Unsupported tristate port expression: " + <exprp()->prettyTypeName()); } - } else { + } else { // pinReconnect should have converted this exprrefp = VN_CAST(outAssignp->rhsp(), VarRef); // This should be the same var as the output pin - if (!exprrefp) nodep->v3error("Unsupported tristate port expression: "<exprp()->prettyTypeName()); - } + if (!exprrefp) nodep->v3error("Unsupported tristate port expression: " + <exprp()->prettyTypeName()); + } if (exprrefp) { - UINFO(9,"outref "<user1p(enrefp); // Mark as now tristated; iteration will pick it up from there - if (!outAssignp) { - mapInsertLhsVarRef(exprrefp); // insertTristates will convert - // // to a varref to the __out# variable - } // else the assignment deals with the connection - } + UINFO(9,"outref "<user1p(enrefp); // Mark as now tristated; iteration will pick it up from there + if (!outAssignp) { + mapInsertLhsVarRef(exprrefp); // insertTristates will convert + // // to a varref to the __out# variable + } // else the assignment deals with the connection + } - // Propagate any pullups/pulldowns upwards if necessary - if (exprrefp) { + // Propagate any pullups/pulldowns upwards if necessary + if (exprrefp) { if (AstPull* pullp = static_cast(nodep->modVarp()->user3p())) { - UINFO(9, "propagate pull on "<varp(), pullp); - } - } - - // Don't need to visit the created assigns, as it was added at the end of the next links - // and normal iterateChild recursion will come back to them eventually. - // Mark the original signal as tristated - iteratePinGuts(nodep); - } - // Not graph building - else { - if (nodep->user2() & U2_NONGRAPH) return; // This pin is already expanded - nodep->user2(U2_NONGRAPH); - UINFO(9," "<varp(), pullp); + } + } + // Don't need to visit the created assigns, as it was added at + // the end of the next links and normal iterateChild recursion + // will come back to them eventually. + // Mark the original signal as tristated + iteratePinGuts(nodep); + } + // Not graph building + else { + if (nodep->user2() & U2_NONGRAPH) return; // This pin is already expanded + nodep->user2(U2_NONGRAPH); + UINFO(9," "<lvalue()) { - associateLogic(nodep, nodep->varp()); - } else { - associateLogic(nodep->varp(), nodep); - } - } else { - if (nodep->user2() & U2_NONGRAPH) return; // Processed - nodep->user2(U2_NONGRAPH); - // Detect all var lhs drivers and adds them to the - // VarMap so that after the walk through the module we can expand - // any tristate logic on the driver. - if (nodep->lvalue() && m_tgraph.isTristate(nodep->varp())) { - UINFO(9," Ref-to-lvalue "<lvalue() - && !nodep->user1p() // Not already processed, nor varref from visit(AstPin) creation - // Reference to another tristate variable - && m_tgraph.isTristate(nodep->varp()) - // and in a position where it feeds upstream to another tristate - && m_tgraph.feedsTri(nodep)) { - // Then propage the enable from the original variable - UINFO(9," Ref-to-tri "<varp()); - nodep->user1p(new AstVarRef(nodep->fileline(), enVarp, false)); - } - if (m_alhs) {} // NOP; user1() already passed down from assignment - } + UINFO(9,dbgState()<lvalue()) { + associateLogic(nodep, nodep->varp()); + } else { + associateLogic(nodep->varp(), nodep); + } + } else { + if (nodep->user2() & U2_NONGRAPH) return; // Processed + nodep->user2(U2_NONGRAPH); + // Detect all var lhs drivers and adds them to the + // VarMap so that after the walk through the module we can expand + // any tristate logic on the driver. + if (nodep->lvalue() && m_tgraph.isTristate(nodep->varp())) { + UINFO(9," Ref-to-lvalue "<lvalue() + && !nodep->user1p() // Not already processed, nor varref from visit(AstPin) creation + // Reference to another tristate variable + && m_tgraph.isTristate(nodep->varp()) + // and in a position where it feeds upstream to another tristate + && m_tgraph.feedsTri(nodep)) { + // Then propage the enable from the original variable + UINFO(9," Ref-to-tri "<varp()); + nodep->user1p(new AstVarRef(nodep->fileline(), enVarp, false)); + } + if (m_alhs) {} // NOP; user1() already passed down from assignment + } } virtual void visit(AstVar* nodep) { iterateChildren(nodep); - UINFO(9,dbgState()<user2() & U2_GRAPHING) return; // Already processed - nodep->user2(U2_GRAPHING); - if (nodep->isPulldown() || nodep->isPullup()) { - AstNode* newp = new AstPull(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, true), - nodep->isPullup()); - UINFO(9," newpul "<addNextHere(newp); - // We'll iterate on the new AstPull later - } + UINFO(9,dbgState()<user2() & U2_GRAPHING) return; // Already processed + nodep->user2(U2_GRAPHING); + if (nodep->isPulldown() || nodep->isPullup()) { + AstNode* newp = new AstPull(nodep->fileline(), + new AstVarRef(nodep->fileline(), nodep, true), + nodep->isPullup()); + UINFO(9," newpul "<addNextHere(newp); + // We'll iterate on the new AstPull later + } if (nodep->isInoutish() //|| varp->isOutput() // Note unconnected output only changes behavior vs. previous // versions and causes outputs that don't come from anywhere to // possibly create connection errors. // One example of problems is this: "output z; task t; z <= {something}; endtask" - ) { - UINFO(9," setTristate-inout "<isPulldown() || nodep->isPullup() handled in TristateGraphVisitor - m_tgraph.didProcess(nodep); - } - } + ) { + UINFO(9," setTristate-inout "<isPulldown() || nodep->isPullup() handled in TristateGraphVisitor + m_tgraph.didProcess(nodep); + } + } } virtual void visit(AstNodeModule* nodep) { - UINFO(8, nodep<v3fatalSrc("Modules under modules not supported"); // Lots of per-module state breaks - // Clear state - m_tgraph.clear(); - m_unique = 0; - m_logicp = NULL; - m_lhsmap.clear(); - m_modp = nodep; - // Walk the graph, finding all variables and tristate constructs - { - m_graphing = true; + UINFO(8, nodep<v3fatalSrc("Modules under modules not supported"); // Lots of per-module state breaks + // Clear state + m_tgraph.clear(); + m_unique = 0; + m_logicp = NULL; + m_lhsmap.clear(); + m_modp = nodep; + // Walk the graph, finding all variables and tristate constructs + { + m_graphing = true; iterateChildren(nodep); - m_graphing = false; - } - // Use graph to find tristate signals - m_tgraph.graphWalk(nodep); - // Build the LHS drivers map for this module + m_graphing = false; + } + // Use graph to find tristate signals + m_tgraph.graphWalk(nodep); + // Build the LHS drivers map for this module iterateChildren(nodep); - // Insert new logic for all tristates - insertTristates(nodep); - m_modp = NULL; + // Insert new logic for all tristates + insertTristates(nodep); + m_modp = NULL; } virtual void visit(AstNodeFTask* nodep) { - // don't deal with functions + // don't deal with functions } virtual void visit(AstCaseItem* nodep) { - // don't deal with casez compare '???? values + // don't deal with casez compare '???? values iterateAndNextNull(nodep->bodysp()); } virtual void visit(AstCell* nodep) { - m_cellp = nodep; - m_alhs = false; + m_cellp = nodep; + m_alhs = false; iterateChildren(nodep); - m_cellp = NULL; + m_cellp = NULL; } virtual void visit(AstNetlist* nodep) { @@ -1315,23 +1348,23 @@ class TristateVisitor : public TristateBaseVisitor { // Default: Just iterate virtual void visit(AstNode* nodep) { iterateChildren(nodep); - checkUnhandled(nodep); + checkUnhandled(nodep); } public: // CONSTUCTORS explicit TristateVisitor(AstNode* nodep) { - m_graphing = false; - m_modp = NULL; - m_cellp = NULL; - m_unique = 0; - m_alhs = false; - m_logicp = NULL; - m_tgraph.clear(); + m_graphing = false; + m_modp = NULL; + m_cellp = NULL; + m_unique = 0; + m_alhs = false; + m_logicp = NULL; + m_tgraph.clear(); iterate(nodep); } virtual ~TristateVisitor() { - V3Stats::addStat("Tristate, Tristate resolved nets", m_statTriSigs); + V3Stats::addStat("Tristate, Tristate resolved nets", m_statTriSigs); } }; diff --git a/src/V3Tristate.h b/src/V3Tristate.h index 780142136..432d87357 100644 --- a/src/V3Tristate.h +++ b/src/V3Tristate.h @@ -34,4 +34,4 @@ public: static void tristateAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 2b35a5ac2..8f80d7e18 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -21,9 +21,9 @@ // // Netlist: // Make vector for all variables -// SEL(VARREF(...))) mark only some bits as used/driven -// else VARREF(...) mark all bits as used/driven -// Report unused/undriven nets +// SEL(VARREF(...))) mark only some bits as used/driven +// else VARREF(...) mark all bits as used/driven +// Report unused/undriven nets // //************************************************************************* @@ -44,9 +44,9 @@ class UndrivenVarEntry { // MEMBERS - AstVar* m_varp; // Variable this tracks - bool m_usedWhole; // True if whole vector used - bool m_drivenWhole; // True if whole vector driven + AstVar* m_varp; // Variable this tracks + bool m_usedWhole; // True if whole vector used + bool m_drivenWhole; // True if whole vector driven std::vector m_flags; // Used/Driven on each subbit enum { FLAG_USED = 0, FLAG_DRIVEN = 1, FLAGS_PER_BIT = 2 }; @@ -56,15 +56,15 @@ class UndrivenVarEntry { public: // CONSTRUCTORS explicit UndrivenVarEntry(AstVar* varp) { // Construction for when a var is used - UINFO(9, "create "<width()*FLAGS_PER_BIT); - for (int i=0; iwidth()*FLAGS_PER_BIT; i++) { - m_flags[i] = false; - } + m_flags.resize(varp->width()*FLAGS_PER_BIT); + for (int i=0; iwidth()*FLAGS_PER_BIT; i++) { + m_flags[i] = false; + } } ~UndrivenVarEntry() {} @@ -72,148 +72,151 @@ private: // METHODS inline bool bitNumOk(int bit) const { return bit>=0 && (bit*FLAGS_PER_BIT < static_cast(m_flags.size())); } - inline bool usedFlag(int bit) const { return m_usedWhole || m_flags[bit*FLAGS_PER_BIT + FLAG_USED]; } - inline bool drivenFlag(int bit) const { return m_drivenWhole || m_flags[bit*FLAGS_PER_BIT + FLAG_DRIVEN]; } + inline bool usedFlag(int bit) const { + return m_usedWhole || m_flags[bit*FLAGS_PER_BIT + FLAG_USED]; } + inline bool drivenFlag(int bit) const { + return m_drivenWhole || m_flags[bit*FLAGS_PER_BIT + FLAG_DRIVEN]; } enum BitNamesWhich { BN_UNUSED, BN_UNDRIVEN, BN_BOTH }; string bitNames(BitNamesWhich which) { string bits; - bool prev = false; - int msb = 0; - // bit==-1 loops below; we do one extra iteration so end with prev=false - for (int bit=(m_flags.size()/FLAGS_PER_BIT)-1; bit >= -1; --bit) { - if (bit>=0 - && ((which == BN_UNUSED && !usedFlag(bit) && drivenFlag(bit)) - || (which == BN_UNDRIVEN && usedFlag(bit) && !drivenFlag(bit)) - || (which == BN_BOTH && !usedFlag(bit) && !drivenFlag(bit)))) { - if (!prev) { prev=true; msb = bit; } - } else if (prev) { - AstBasicDType* bdtypep = m_varp->basicp(); - int lsb = bit+1; - if (bits != "") bits += ","; - if (lsb==msb) { - bits += cvtToStr(lsb+bdtypep->lsb()); - } else { - if (bdtypep->littleEndian()) { - bits += cvtToStr(lsb+bdtypep->lsb())+":"+cvtToStr(msb+bdtypep->lsb()); - } else { - bits += cvtToStr(msb+bdtypep->lsb())+":"+cvtToStr(lsb+bdtypep->lsb()); - } - } - prev = false; - } - } - return "["+bits+"]"; + bool prev = false; + int msb = 0; + // bit==-1 loops below; we do one extra iteration so end with prev=false + for (int bit=(m_flags.size()/FLAGS_PER_BIT)-1; bit >= -1; --bit) { + if (bit>=0 + && ((which == BN_UNUSED && !usedFlag(bit) && drivenFlag(bit)) + || (which == BN_UNDRIVEN && usedFlag(bit) && !drivenFlag(bit)) + || (which == BN_BOTH && !usedFlag(bit) && !drivenFlag(bit)))) { + if (!prev) { prev=true; msb = bit; } + } else if (prev) { + AstBasicDType* bdtypep = m_varp->basicp(); + int lsb = bit+1; + if (bits != "") bits += ","; + if (lsb==msb) { + bits += cvtToStr(lsb+bdtypep->lsb()); + } else { + if (bdtypep->littleEndian()) { + bits += cvtToStr(lsb+bdtypep->lsb())+":"+cvtToStr(msb+bdtypep->lsb()); + } else { + bits += cvtToStr(msb+bdtypep->lsb())+":"+cvtToStr(lsb+bdtypep->lsb()); + } + } + prev = false; + } + } + return "["+bits+"]"; } public: void usedWhole() { - UINFO(9, "set u[*] "<name()<name()<name()<name()<name()<name()<name()<name()<prettyName(); - return VString::wildmatch(prettyName.c_str(), regexp.c_str()); + string regexp = v3Global.opt.unusedRegexp(); + if (regexp == "") return false; + string prettyName = nodep->prettyName(); + return VString::wildmatch(prettyName.c_str(), regexp.c_str()); } void reportViolations() { - // Combine bits into overall state - AstVar* nodep = m_varp; - if (!nodep->isParam() && !nodep->isGenVar()) { - bool allU=true; - bool allD=true; - bool anyU=m_usedWhole; - bool anyD=m_drivenWhole; - bool anyUnotD=false; - bool anyDnotU=false; - bool anynotDU=false; - for (unsigned bit=0; bitisIfaceRef()) { - // For interface top level we don't do any tracking - // Ideally we'd report unused instance cells, but presumably a signal inside one - // would get reported as unused - } else if (allU && allD) { - // It's fine - } else if (!anyD && !anyU) { - // UNDRIVEN is considered more serious - as is more likely a bug, - // thus undriven+unused bits get UNUSED warnings, as they're not as buggy. - if (!unusedMatch(nodep)) { - nodep->v3warn(UNUSED, "Signal is not driven, nor used: "<prettyName()); - nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once - } - } else if (allD && !anyU) { - if (!unusedMatch(nodep)) { - nodep->v3warn(UNUSED, "Signal is not used: "<prettyName()); - nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once - } - } else if (!anyD && allU) { - nodep->v3warn(UNDRIVEN, "Signal is not driven: "<prettyName()); - nodep->fileline()->modifyWarnOff(V3ErrorCode::UNDRIVEN, true); // Warn only once - } else { - // Bits have different dispositions - bool setU=false; bool setD=false; - if (anynotDU && !unusedMatch(nodep)) { - nodep->v3warn(UNUSED, "Bits of signal are not driven, nor used: "<prettyName() - <v3warn(UNUSED, "Bits of signal are not used: "<prettyName() - <v3warn(UNDRIVEN, "Bits of signal are not driven: "<prettyName() - <fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once - if (setD) nodep->fileline()->modifyWarnOff(V3ErrorCode::UNDRIVEN, true); // Warn only once - } - } + // Combine bits into overall state + AstVar* nodep = m_varp; + if (!nodep->isParam() && !nodep->isGenVar()) { + bool allU = true; + bool allD = true; + bool anyU = m_usedWhole; + bool anyD = m_drivenWhole; + bool anyUnotD = false; + bool anyDnotU = false; + bool anynotDU = false; + for (unsigned bit=0; bitisIfaceRef()) { + // For interface top level we don't do any tracking + // Ideally we'd report unused instance cells, but presumably a signal inside one + // would get reported as unused + } else if (allU && allD) { + // It's fine + } else if (!anyD && !anyU) { + // UNDRIVEN is considered more serious - as is more likely a bug, + // thus undriven+unused bits get UNUSED warnings, as they're not as buggy. + if (!unusedMatch(nodep)) { + nodep->v3warn(UNUSED, "Signal is not driven, nor used: "<prettyName()); + nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once + } + } else if (allD && !anyU) { + if (!unusedMatch(nodep)) { + nodep->v3warn(UNUSED, "Signal is not used: "<prettyName()); + nodep->fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once + } + } else if (!anyD && allU) { + nodep->v3warn(UNDRIVEN, "Signal is not driven: "<prettyName()); + nodep->fileline()->modifyWarnOff(V3ErrorCode::UNDRIVEN, true); // Warn only once + } else { + // Bits have different dispositions + bool setU=false; bool setD=false; + if (anynotDU && !unusedMatch(nodep)) { + nodep->v3warn(UNUSED, "Bits of signal are not driven, nor used: " + <prettyName() + <v3warn(UNUSED, "Bits of signal are not used: "<prettyName() + <v3warn(UNDRIVEN, "Bits of signal are not driven: "<prettyName() + <fileline()->modifyWarnOff(V3ErrorCode::UNUSED, true); // Warn only once + if (setD) nodep->fileline()->modifyWarnOff(V3ErrorCode::UNDRIVEN, true); // Warn only once + } + } } }; @@ -224,11 +227,11 @@ class UndrivenVisitor : public AstNVisitor { private: // NODE STATE // Netlist: - // AstVar::user1p -> UndrivenVar* for usage var, 0=not set yet - AstUser1InUse m_inuser1; + // AstVar::user1p -> UndrivenVar* for usage var, 0=not set yet + AstUser1InUse m_inuser1; // Each always: - // AstNode::user2p -> UndrivenVar* for usage var, 0=not set yet - AstUser2InUse m_inuser2; + // AstNode::user2p -> UndrivenVar* for usage var, 0=not set yet + AstUser2InUse m_inuser2; // STATE std::vector m_entryps[3]; // Nodes to delete when we are finished @@ -242,37 +245,40 @@ private: VL_DEBUG_FUNC; // Declare debug() UndrivenVarEntry* getEntryp(AstVar* nodep, int which_user) { - if (!(which_user==1 ? nodep->user1p() : nodep->user2p())) { + if (!(which_user==1 ? nodep->user1p() : nodep->user2p())) { UndrivenVarEntry* entryp = new UndrivenVarEntry(nodep); //UINFO(9," Associate u="<name()<user1p(entryp); - else if (which_user==2) nodep->user2p(entryp); - else nodep->v3fatalSrc("Bad case"); - return entryp; - } else { + m_entryps[which_user].push_back(entryp); + if (which_user==1) nodep->user1p(entryp); + else if (which_user==2) nodep->user2p(entryp); + else nodep->v3fatalSrc("Bad case"); + return entryp; + } else { UndrivenVarEntry* entryp = reinterpret_cast (which_user==1 ? nodep->user1p() : nodep->user2p()); - return entryp; - } + return entryp; + } } void warnAlwCombOrder(AstNodeVarRef* nodep) { - AstVar* varp = nodep->varp(); - if (!varp->isParam() && !varp->isGenVar() && !varp->isUsedLoopIdx() - && !m_inBBox // We may have falsely considered a SysIgnore as a driver + AstVar* varp = nodep->varp(); + if (!varp->isParam() && !varp->isGenVar() && !varp->isUsedLoopIdx() + && !m_inBBox // We may have falsely considered a SysIgnore as a driver && !VN_IS(nodep, VarXRef) // Xrefs might point at two different instances - && !varp->fileline()->warnIsOff(V3ErrorCode::ALWCOMBORDER)) { // Warn only once per variable - nodep->v3warn(ALWCOMBORDER, "Always_comb variable driven after use: "<prettyName()); - varp->fileline()->modifyWarnOff(V3ErrorCode::ALWCOMBORDER, true); // Complain just once for any usage - } + && !varp->fileline()->warnIsOff(V3ErrorCode::ALWCOMBORDER)) { // Warn only once per variable + nodep->v3warn(ALWCOMBORDER, "Always_comb variable driven after use: " + <prettyName()); + varp->fileline()->modifyWarnOff(V3ErrorCode::ALWCOMBORDER, true); // Complain just once for any usage + } } // VISITORS virtual void visit(AstVar* nodep) { for (int usr=1; usr<(m_alwaysCombp?3:2); ++usr) { - // For assigns and non-combo always, do just usr==1, to look for module-wide undriven etc - // For non-combo always, run both usr==1 for above, and also usr==2 for always-only checks + // For assigns and non-combo always, do just usr==1, to look + // for module-wide undriven etc. + // For non-combo always, run both usr==1 for above, and also + // usr==2 for always-only checks. UndrivenVarEntry* entryp = getEntryp(nodep, usr); if (nodep->isNonOutput() || nodep->isSigPublic() || nodep->isSigUserRWPublic() @@ -280,17 +286,17 @@ private: entryp->drivenWhole(); } if (nodep->isWritable() - || nodep->isSigPublic() || nodep->isSigUserRWPublic() - || nodep->isSigUserRdPublic() - || (m_taskp && (m_taskp->dpiImport() || m_taskp->dpiExport()))) { - entryp->usedWhole(); - } - } - // Discover variables used in bit definitions, etc + || nodep->isSigPublic() || nodep->isSigUserRWPublic() + || nodep->isSigUserRdPublic() + || (m_taskp && (m_taskp->dpiImport() || m_taskp->dpiExport()))) { + entryp->usedWhole(); + } + } + // Discover variables used in bit definitions, etc iterateChildren(nodep); } virtual void visit(AstArraySel* nodep) { - // Arrays are rarely constant assigned, so for now we punt and do all entries + // Arrays are rarely constant assigned, so for now we punt and do all entries iterateChildren(nodep); } virtual void visit(AstSliceSel* nodep) { @@ -300,27 +306,28 @@ private: virtual void visit(AstSel* nodep) { AstNodeVarRef* varrefp = VN_CAST(nodep->fromp(), NodeVarRef); AstConst* constp = VN_CAST(nodep->lsbp(), Const); - if (varrefp && constp && !constp->num().isFourState()) { + if (varrefp && constp && !constp->num().isFourState()) { for (int usr=1; usr<(m_alwaysCombp?3:2); ++usr) { UndrivenVarEntry* entryp = getEntryp(varrefp->varp(), usr); int lsb = constp->toUInt(); if (m_inBBox || varrefp->lvalue()) { // Don't warn if already driven earlier as "a=0; if(a) a=1;" is fine. - if (usr==2 && m_alwaysCombp && entryp->isUsedNotDrivenBit(lsb, nodep->width())) { + if (usr==2 && m_alwaysCombp + && entryp->isUsedNotDrivenBit(lsb, nodep->width())) { UINFO(9," Select. Entryp="<drivenBit(lsb, nodep->width()); - } - if (m_inBBox || !varrefp->lvalue()) entryp->usedBit(lsb, nodep->width()); - } - } else { - // else other varrefs handled as unknown mess in AstVarRef + warnAlwCombOrder(varrefp); + } + entryp->drivenBit(lsb, nodep->width()); + } + if (m_inBBox || !varrefp->lvalue()) entryp->usedBit(lsb, nodep->width()); + } + } else { + // else other varrefs handled as unknown mess in AstVarRef iterateChildren(nodep); - } + } } virtual void visit(AstNodeVarRef* nodep) { - // Any variable + // Any variable if (nodep->lvalue() && !VN_IS(nodep, VarXRef)) { // Ignore interface variables and similar ugly items if (m_inProcAssign && !nodep->varp()->varType().isProcAssignable()) { @@ -341,20 +348,20 @@ private: if (m_inBBox || nodep->lvalue()) { if (usr==2 && m_alwaysCombp && entryp->isUsedNotDrivenAny()) { UINFO(9," Full bus. Entryp="<drivenWhole(); - } - if (m_inBBox || !nodep->lvalue() || fdrv) entryp->usedWhole(); - } + warnAlwCombOrder(nodep); + } + entryp->drivenWhole(); + } + if (m_inBBox || !nodep->lvalue() || fdrv) entryp->usedWhole(); + } } // Don't know what black boxed calls do, assume in+out virtual void visit(AstSysIgnore* nodep) { - bool prevMark = m_inBBox; - m_inBBox = true; + bool prevMark = m_inBBox; + m_inBBox = true; iterateChildren(nodep); - m_inBBox = prevMark; + m_inBBox = prevMark; } virtual void visit(AstAssign* nodep) { @@ -398,10 +405,10 @@ private: } virtual void visit(AstNodeFTask* nodep) { - AstNodeFTask* prevTaskp = m_taskp; - m_taskp = nodep; + AstNodeFTask* prevTaskp = m_taskp; + m_taskp = nodep; iterateChildren(nodep); - m_taskp = prevTaskp; + m_taskp = prevTaskp; } // Until we support tables, primitives will have undriven and unused I/Os @@ -430,14 +437,16 @@ public: iterate(nodep); } virtual ~UndrivenVisitor() { - for (std::vector::iterator it = m_entryps[1].begin(); it != m_entryps[1].end(); ++it) { - (*it)->reportViolations(); - } - for (int usr=1; usr<3; ++usr) { - for (std::vector::iterator it = m_entryps[usr].begin(); it != m_entryps[usr].end(); ++it) { - delete (*it); - } - } + for (std::vector::iterator it = m_entryps[1].begin(); + it != m_entryps[1].end(); ++it) { + (*it)->reportViolations(); + } + for (int usr=1; usr<3; ++usr) { + for (std::vector::iterator it = m_entryps[usr].begin(); + it != m_entryps[usr].end(); ++it) { + delete (*it); + } + } } }; diff --git a/src/V3Undriven.h b/src/V3Undriven.h index 83101795f..1dcbf3816 100644 --- a/src/V3Undriven.h +++ b/src/V3Undriven.h @@ -34,4 +34,4 @@ public: static void undrivenAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 085c89205..1edb50510 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -20,14 +20,15 @@ // V3Unknown's Transformations: // // Each module: -// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel -// Need __en in changed list if a signal is on the LHS of a assign -// Constants: -// RHS, Replace 5'bx_1_x with a module global we init to a random value -// CONST(5'bx_1_x) -> VARREF(_{numberedtemp}) -// -> VAR(_{numberedtemp}) -// -> INITIAL(VARREF(_{numberedtemp}), OR(5'bx_1_x,AND(random,5'b0_1_x)) -// OPTIMIZE: Must not collapse this initial back into the equation. +// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel +// Need __en in changed list if a signal is on the LHS of a assign +// Constants: +// RHS, Replace 5'bx_1_x with a module global we init to a random value +// CONST(5'bx_1_x) -> VARREF(_{numberedtemp}) +// -> VAR(_{numberedtemp}) +// -> INITIAL(VARREF(_{numberedtemp}), +// OR(5'bx_1_x,AND(random,5'b0_1_x)) +// OPTIMIZE: Must not collapse this initial back into the equation. // //************************************************************************* @@ -49,82 +50,82 @@ class UnknownVisitor : public AstNVisitor { private: // NODE STATE // Cleared on Netlist - // AstSel::user() -> bool. Set true if already processed - // AstArraySel::user() -> bool. Set true if already processed - // AstNode::user2p() -> AstIf* Inserted if assignment for conditional - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; + // AstSel::user() -> bool. Set true if already processed + // AstArraySel::user() -> bool. Set true if already processed + // AstNode::user2p() -> AstIf* Inserted if assignment for conditional + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; // STATE - AstNodeModule* m_modp; // Current module - bool m_constXCvt; // Convert X's - V3Double0 m_statUnkVars; // Statistic tracking - AstAssignW* m_assignwp; // Current assignment - AstAssignDly* m_assigndlyp; // Current assignment + AstNodeModule* m_modp; // Current module + bool m_constXCvt; // Convert X's + V3Double0 m_statUnkVars; // Statistic tracking + AstAssignW* m_assignwp; // Current assignment + AstAssignDly* m_assigndlyp; // Current assignment // METHODS VL_DEBUG_FUNC; // Declare debug() void replaceBoundLvalue(AstNode* nodep, AstNode* condp) { - // Spec says a out-of-range LHS SEL results in a NOP. - // This is a PITA. We could: - // 1. IF(...) around an ASSIGN, - // but that would break a "foo[TOO_BIG]=$fopen(...)". - // 2. Hack to extend the size of the output structure - // by one bit, and write to that temporary, but never read it. - // That makes there be two widths() and is likely a bug farm. - // 3. Make a special SEL to choose between the real lvalue - // and a temporary NOP register. - // 4. Assign to a temp, then IF that assignment. - // This is suspected to be nicest to later optimizations. - // 4 seems best but breaks later optimizations. 3 was tried, - // but makes a mess in the emitter as lvalue switching is needed. So 4. - // SEL(...) -> temp - // if (COND(LTE(bit<=maxlsb))) ASSIGN(SEL(...)),temp) - if (m_assignwp) { - // Wire assigns must become always statements to deal with insertion - // of multiple statements. Perhaps someday make all wassigns into always's? - UINFO(5," IM_WireRep "<convertToAlways(); pushDeletep(m_assignwp); m_assignwp=NULL; - } - bool needDly = (m_assigndlyp != NULL); - if (m_assigndlyp) { - // Delayed assignments become normal assignments, - // then the temp created becomes the delayed assignment - AstNode* newp = new AstAssign(m_assigndlyp->fileline(), - m_assigndlyp->lhsp()->unlinkFrBackWithNext(), - m_assigndlyp->rhsp()->unlinkFrBackWithNext()); - m_assigndlyp->replaceWith(newp); pushDeletep(m_assigndlyp); m_assigndlyp=NULL; - } - AstNode* prep = nodep; + // Spec says a out-of-range LHS SEL results in a NOP. + // This is a PITA. We could: + // 1. IF(...) around an ASSIGN, + // but that would break a "foo[TOO_BIG]=$fopen(...)". + // 2. Hack to extend the size of the output structure + // by one bit, and write to that temporary, but never read it. + // That makes there be two widths() and is likely a bug farm. + // 3. Make a special SEL to choose between the real lvalue + // and a temporary NOP register. + // 4. Assign to a temp, then IF that assignment. + // This is suspected to be nicest to later optimizations. + // 4 seems best but breaks later optimizations. 3 was tried, + // but makes a mess in the emitter as lvalue switching is needed. So 4. + // SEL(...) -> temp + // if (COND(LTE(bit<=maxlsb))) ASSIGN(SEL(...)),temp) + if (m_assignwp) { + // Wire assigns must become always statements to deal with insertion + // of multiple statements. Perhaps someday make all wassigns into always's? + UINFO(5," IM_WireRep "<convertToAlways(); pushDeletep(m_assignwp); m_assignwp = NULL; + } + bool needDly = (m_assigndlyp != NULL); + if (m_assigndlyp) { + // Delayed assignments become normal assignments, + // then the temp created becomes the delayed assignment + AstNode* newp = new AstAssign(m_assigndlyp->fileline(), + m_assigndlyp->lhsp()->unlinkFrBackWithNext(), + m_assigndlyp->rhsp()->unlinkFrBackWithNext()); + m_assigndlyp->replaceWith(newp); pushDeletep(m_assigndlyp); m_assigndlyp = NULL; + } + AstNode* prep = nodep; - // Scan back to put the condlvalue above all selects (IE top of the lvalue) + // Scan back to put the condlvalue above all selects (IE top of the lvalue) while (VN_IS(prep->backp(), NodeSel) || VN_IS(prep->backp(), Sel)) { - prep=prep->backp(); - } - FileLine* fl = nodep->fileline(); - VL_DANGLING(nodep); // Zap it so we don't use it by mistake - use prep + prep = prep->backp(); + } + FileLine* fl = nodep->fileline(); + VL_DANGLING(nodep); // Zap it so we don't use it by mistake - use prep - // Already exists; rather than IF(a,... IF(b... optimize to IF(a&&b, - // Saves us teaching V3Const how to optimize, and it won't be needed again. + // Already exists; rather than IF(a,... IF(b... optimize to IF(a&&b, + // Saves us teaching V3Const how to optimize, and it won't be needed again. if (AstIf* ifp = VN_CAST(prep->user2p(), If)) { - if (needDly) prep->v3fatalSrc("Should have already converted to non-delay"); - AstNRelinker replaceHandle; - AstNode* earliercondp = ifp->condp()->unlinkFrBack(&replaceHandle); + if (needDly) prep->v3fatalSrc("Should have already converted to non-delay"); + AstNRelinker replaceHandle; + AstNode* earliercondp = ifp->condp()->unlinkFrBack(&replaceHandle); AstNode* newp = new AstLogAnd(condp->fileline(), condp, earliercondp); - UINFO(4, "Edit BOUNDLVALUE "<varNumGetInc())); - AstVar* varp = new AstVar(fl, AstVarType::MODULETEMP, name, - prep->dtypep()); - m_modp->addStmtp(varp); + AstVar* varp = new AstVar(fl, AstVarType::MODULETEMP, name, + prep->dtypep()); + m_modp->addStmtp(varp); - AstNode* abovep = prep->backp(); // Grab above point before lose it w/ next replace - prep->replaceWith(new AstVarRef(fl, varp, true)); + AstNode* abovep = prep->backp(); // Grab above point before lose it w/ next replace + prep->replaceWith(new AstVarRef(fl, varp, true)); AstIf* newp = new AstIf(fl, condp, (needDly ? static_cast @@ -135,7 +136,7 @@ private: new AstVarRef(fl, varp, false)))), NULL); newp->branchPred(AstBranchPred::BP_LIKELY); - if (debug()>=9) newp->dumpTree(cout," _new: "); + if (debug()>=9) newp->dumpTree(cout, " _new: "); abovep->addNextStmt(newp, abovep); prep->user2p(newp); // Save so we may LogAnd it next time } @@ -143,129 +144,129 @@ private: // VISITORS virtual void visit(AstNodeModule* nodep) { - UINFO(4," MOD "<condsp()); - m_constXCvt = true; + m_constXCvt = true; iterateAndNextNull(nodep->bodysp()); } virtual void visit(AstNodeDType* nodep) { - m_constXCvt = false; // Avoid losing the X's in casex + m_constXCvt = false; // Avoid losing the X's in casex iterateChildren(nodep); - m_constXCvt = true; + m_constXCvt = true; } void visitEqNeqCase(AstNodeBiop* nodep) { - UINFO(4," N/EQCASE->EQ "<lhsp()); // lhsp may change - V3Const::constifyEdit(nodep->rhsp()); // rhsp may change + UINFO(4," N/EQCASE->EQ "<lhsp()); // lhsp may change + V3Const::constifyEdit(nodep->rhsp()); // rhsp may change if (VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const)) { - // Both sides are constant, node can be constant - V3Const::constifyEdit(nodep); VL_DANGLING(nodep); - return; - } else { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* newp; - // If we got ==1'bx it can never be true (but 1'bx==1'bx can be!) + // Both sides are constant, node can be constant + V3Const::constifyEdit(nodep); VL_DANGLING(nodep); + return; + } else { + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* newp; + // If we got ==1'bx it can never be true (but 1'bx==1'bx can be!) if (((VN_IS(lhsp, Const) && VN_CAST(lhsp, Const)->num().isFourState()) || (VN_IS(rhsp, Const) && VN_CAST(rhsp, Const)->num().isFourState()))) { V3Number num(nodep, 1, (VN_IS(nodep, EqCase) ? 0:1)); newp = new AstConst(nodep->fileline(), num); - lhsp->deleteTree(); VL_DANGLING(lhsp); - rhsp->deleteTree(); VL_DANGLING(rhsp); - } else { + lhsp->deleteTree(); VL_DANGLING(lhsp); + rhsp->deleteTree(); VL_DANGLING(rhsp); + } else { if (VN_IS(nodep, EqCase)) { newp = new AstEq(nodep->fileline(), lhsp, rhsp); } else { newp = new AstNeq(nodep->fileline(), lhsp, rhsp); } - } - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); - // Iterate tree now that we may have gotten rid of Xs + } + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); + // Iterate tree now that we may have gotten rid of Xs iterateChildren(newp); - } + } } void visitEqNeqWild(AstNodeBiop* nodep) { - UINFO(4," N/EQWILD->EQ "<lhsp()); // lhsp may change - V3Const::constifyEdit(nodep->rhsp()); // rhsp may change + UINFO(4," N/EQWILD->EQ "<lhsp()); // lhsp may change + V3Const::constifyEdit(nodep->rhsp()); // rhsp may change if (VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const)) { - // Both sides are constant, node can be constant - V3Const::constifyEdit(nodep); VL_DANGLING(nodep); - return; - } else { - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* newp; + // Both sides are constant, node can be constant + V3Const::constifyEdit(nodep); VL_DANGLING(nodep); + return; + } else { + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* newp; if (!VN_IS(rhsp, Const)) { - nodep->v3error("Unsupported: RHS of ==? or !=? must be constant to be synthesizable"); // Says spec. - // Replace with anything that won't cause more errors + nodep->v3error("Unsupported: RHS of ==? or !=? must be constant to be synthesizable"); // Says spec. + // Replace with anything that won't cause more errors newp = new AstEq(nodep->fileline(), lhsp, rhsp); - } else { - // X or Z's become mask, ala case statements. + } else { + // X or Z's become mask, ala case statements. V3Number nummask (rhsp, rhsp->width()); nummask.opBitsNonX(VN_CAST(rhsp, Const)->num()); V3Number numval (rhsp, rhsp->width()); numval.opBitsOne(VN_CAST(rhsp, Const)->num()); - AstNode* and1p = new AstAnd(nodep->fileline(), lhsp, - new AstConst(nodep->fileline(), nummask)); - AstNode* and2p = new AstConst(nodep->fileline(), numval); + AstNode* and1p = new AstAnd(nodep->fileline(), lhsp, + new AstConst(nodep->fileline(), nummask)); + AstNode* and2p = new AstConst(nodep->fileline(), numval); if (VN_IS(nodep, EqWild)) { newp = new AstEq(nodep->fileline(), and1p, and2p); } else { newp = new AstNeq(nodep->fileline(), and1p, and2p); } - rhsp->deleteTree(); VL_DANGLING(rhsp); - } - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); - // Iterate tree now that we may have gotten rid of the compare + rhsp->deleteTree(); VL_DANGLING(rhsp); + } + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); + // Iterate tree now that we may have gotten rid of the compare iterateChildren(newp); - } + } } virtual void visit(AstEqCase* nodep) { - visitEqNeqCase(nodep); + visitEqNeqCase(nodep); } virtual void visit(AstNeqCase* nodep) { - visitEqNeqCase(nodep); + visitEqNeqCase(nodep); } virtual void visit(AstEqWild* nodep) { - visitEqNeqWild(nodep); + visitEqNeqWild(nodep); } virtual void visit(AstNeqWild* nodep) { - visitEqNeqWild(nodep); + visitEqNeqWild(nodep); } virtual void visit(AstIsUnknown* nodep) { iterateChildren(nodep); - // Ahh, we're two state, so this is easy + // Ahh, we're two state, so this is easy UINFO(4," ISUNKNOWN->0 "<fileline(), zero); - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstConst* nodep) { - if (m_constXCvt - && nodep->num().isFourState()) { - UINFO(4," CONST4 "<=9) nodep->dumpTree(cout," Const_old: "); - // CONST(num) -> VARREF(newvarp) - // -> VAR(newvarp) - // -> INITIAL(VARREF(newvarp, OR(num_No_Xs,AND(random,num_1s_Where_X)) + if (m_constXCvt + && nodep->num().isFourState()) { + UINFO(4," CONST4 "<=9) nodep->dumpTree(cout, " Const_old: "); + // CONST(num) -> VARREF(newvarp) + // -> VAR(newvarp) + // -> INITIAL(VARREF(newvarp, OR(num_No_Xs,AND(random,num_1s_Where_X)) V3Number numb1 (nodep, nodep->width()); numb1.opBitsOne(nodep->num()); V3Number numbx (nodep, nodep->width()); @@ -273,84 +274,84 @@ private: if (v3Global.opt.xAssign()!="unique") { // All X bits just become 0; fastest simulation, but not nice V3Number numnew (nodep, numb1.width()); - if (v3Global.opt.xAssign()=="1") { - numnew.opOr(numb1, numbx); - } else { - numnew.opAssign(numb1); - } - AstConst* newp = new AstConst(nodep->fileline(), numnew); - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); - UINFO(4," -> "<v3fatalSrc("X number not under module"); + if (v3Global.opt.xAssign()=="1") { + numnew.opOr(numb1, numbx); + } else { + numnew.opAssign(numb1); + } + AstConst* newp = new AstConst(nodep->fileline(), numnew); + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); + UINFO(4," -> "<v3fatalSrc("X number not under module"); string newvarname = (string("__Vxrand") - +cvtToStr(m_modp->varNumGetInc())); - AstVar* newvarp + +cvtToStr(m_modp->varNumGetInc())); + AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::XTEMP, newvarname, VFlagLogicPacked(), nodep->width()); - ++m_statUnkVars; - AstNRelinker replaceHandle; - nodep->unlinkFrBack(&replaceHandle); - AstNodeVarRef* newref1p = new AstVarRef(nodep->fileline(), newvarp, false); - replaceHandle.relink(newref1p); // Replace const with varref - AstInitial* newinitp - = new AstInitial( - nodep->fileline(), - new AstAssign( - nodep->fileline(), - new AstVarRef(nodep->fileline(), newvarp, true), - new AstOr(nodep->fileline(), - new AstConst(nodep->fileline(),numb1), - new AstAnd(nodep->fileline(), - new AstConst(nodep->fileline(),numbx), - new AstRand(nodep->fileline(), - nodep->dtypep(), true))))); - // Add inits in front of other statement. - // In the future, we should stuff the initp into the module's constructor. - AstNode* afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); - m_modp->addStmtp(newvarp); - m_modp->addStmtp(newinitp); - m_modp->addStmtp(afterp); - if (debug()>=9) newref1p->dumpTree(cout," _new: "); - if (debug()>=9) newvarp->dumpTree(cout," _new: "); - if (debug()>=9) newinitp->dumpTree(cout," _new: "); - nodep->deleteTree(); VL_DANGLING(nodep); - } - } + ++m_statUnkVars; + AstNRelinker replaceHandle; + nodep->unlinkFrBack(&replaceHandle); + AstNodeVarRef* newref1p = new AstVarRef(nodep->fileline(), newvarp, false); + replaceHandle.relink(newref1p); // Replace const with varref + AstInitial* newinitp + = new AstInitial( + nodep->fileline(), + new AstAssign( + nodep->fileline(), + new AstVarRef(nodep->fileline(), newvarp, true), + new AstOr(nodep->fileline(), + new AstConst(nodep->fileline(), numb1), + new AstAnd(nodep->fileline(), + new AstConst(nodep->fileline(), numbx), + new AstRand(nodep->fileline(), + nodep->dtypep(), true))))); + // Add inits in front of other statement. + // In the future, we should stuff the initp into the module's constructor. + AstNode* afterp = m_modp->stmtsp()->unlinkFrBackWithNext(); + m_modp->addStmtp(newvarp); + m_modp->addStmtp(newinitp); + m_modp->addStmtp(afterp); + if (debug()>=9) newref1p->dumpTree(cout, " _new: "); + if (debug()>=9) newvarp->dumpTree(cout, " _new: "); + if (debug()>=9) newinitp->dumpTree(cout, " _new: "); + nodep->deleteTree(); VL_DANGLING(nodep); + } + } } virtual void visit(AstSel* nodep) { iterateChildren(nodep); - if (!nodep->user1SetOnce()) { - // Guard against reading/writing past end of bit vector array - AstNode* basefromp = AstArraySel::baseFromp(nodep); - bool lvalue = false; + if (!nodep->user1SetOnce()) { + // Guard against reading/writing past end of bit vector array + AstNode* basefromp = AstArraySel::baseFromp(nodep); + bool lvalue = false; if (const AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) { - lvalue = varrefp->lvalue(); - } - // Find range of dtype we are selecting from - // Similar code in V3Const::warnSelect - int maxmsb = nodep->fromp()->dtypep()->width()-1; - if (debug()>=9) nodep->dumpTree(cout,"sel_old: "); + lvalue = varrefp->lvalue(); + } + // Find range of dtype we are selecting from + // Similar code in V3Const::warnSelect + int maxmsb = nodep->fromp()->dtypep()->width()-1; + if (debug()>=9) nodep->dumpTree(cout, "sel_old: "); V3Number maxmsbnum (nodep, nodep->lsbp()->width(), maxmsb); - // If (maxmsb >= selected), we're in bound + // If (maxmsb >= selected), we're in bound AstNode* condp = new AstGte(nodep->fileline(), new AstConst(nodep->fileline(), maxmsbnum), nodep->lsbp()->cloneTree(false)); - // See if the condition is constant true (e.g. always in bound due to constant select) - // Note below has null backp(); the Edit function knows how to deal with that. - condp = V3Const::constifyEdit(condp); - if (condp->isOne()) { - // We don't need to add a conditional; we know the existing expression is ok - condp->deleteTree(); - } - else if (!lvalue) { - // SEL(...) -> COND(LTE(bit<=maxmsb), ARRAYSEL(...), {width{1'bx}}) - AstNRelinker replaceHandle; + // See if the condition is constant true (e.g. always in bound due to constant select) + // Note below has null backp(); the Edit function knows how to deal with that. + condp = V3Const::constifyEdit(condp); + if (condp->isOne()) { + // We don't need to add a conditional; we know the existing expression is ok + condp->deleteTree(); + } + else if (!lvalue) { + // SEL(...) -> COND(LTE(bit<=maxmsb), ARRAYSEL(...), {width{1'bx}}) + AstNRelinker replaceHandle; nodep->unlinkFrBack(&replaceHandle); V3Number xnum (nodep, nodep->width()); xnum.setAllBitsX(); @@ -358,16 +359,16 @@ private: condp, nodep, new AstConst(nodep->fileline(), xnum)); - if (debug()>=9) newp->dumpTree(cout," _new: "); - // Link in conditional - replaceHandle.relink(newp); - // Added X's, tristate them too + if (debug()>=9) newp->dumpTree(cout, " _new: "); + // Link in conditional + replaceHandle.relink(newp); + // Added X's, tristate them too iterate(newp); - } - else { // lvalue - replaceBoundLvalue(nodep, condp); - } - } + } + else { // lvalue + replaceBoundLvalue(nodep, condp); + } + } } // visit(AstSliceSel) not needed as its bounds are constant and checked @@ -375,77 +376,77 @@ private: virtual void visit(AstArraySel* nodep) { iterateChildren(nodep); - if (!nodep->user1SetOnce()) { - if (debug()==9) nodep->dumpTree(cout,"-in: "); - // Guard against reading/writing past end of arrays - AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); - bool lvalue = false; + if (!nodep->user1SetOnce()) { + if (debug()==9) nodep->dumpTree(cout, "-in: "); + // Guard against reading/writing past end of arrays + AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp()); + bool lvalue = false; if (const AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) { - lvalue = varrefp->lvalue(); + lvalue = varrefp->lvalue(); } else if (VN_IS(basefromp, Const)) { - // If it's a PARAMETER[bit], then basefromp may be a constant instead of a varrefp - } else { - nodep->v3fatalSrc("No VarRef or Const under ArraySel"); - } - // Find range of dtype we are selecting from - int declElements = -1; - AstNodeDType* dtypep = nodep->fromp()->dtypep()->skipRefp(); - if (!dtypep) nodep->v3fatalSrc("Select of non-selectable type"); + // If it's a PARAMETER[bit], then basefromp may be a constant instead of a varrefp + } else { + nodep->v3fatalSrc("No VarRef or Const under ArraySel"); + } + // Find range of dtype we are selecting from + int declElements = -1; + AstNodeDType* dtypep = nodep->fromp()->dtypep()->skipRefp(); + if (!dtypep) nodep->v3fatalSrc("Select of non-selectable type"); if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) { - declElements = adtypep->elementsConst(); - } else { - nodep->v3error("Select from non-array "<prettyTypeName()); - } - if (debug()>=9) nodep->dumpTree(cout,"arraysel_old: "); + declElements = adtypep->elementsConst(); + } else { + nodep->v3error("Select from non-array "<prettyTypeName()); + } + if (debug()>=9) nodep->dumpTree(cout, "arraysel_old: "); V3Number widthnum (nodep, nodep->bitp()->width(), declElements-1); - // See if the condition is constant true + // See if the condition is constant true AstNode* condp = new AstGte(nodep->fileline(), new AstConst(nodep->fileline(), widthnum), nodep->bitp()->cloneTree(false)); - // Note below has null backp(); the Edit function knows how to deal with that. - condp = V3Const::constifyEdit(condp); - if (condp->isOne()) { - // We don't need to add a conditional; we know the existing expression is ok - condp->deleteTree(); - } - else if (!lvalue + // Note below has null backp(); the Edit function knows how to deal with that. + condp = V3Const::constifyEdit(condp); + if (condp->isOne()) { + // We don't need to add a conditional; we know the existing expression is ok + condp->deleteTree(); + } + else if (!lvalue && !VN_IS(nodep->backp(), ArraySel)) { // Too complicated and slow if mid-multidimension - // ARRAYSEL(...) -> COND(LT(bitunlinkFrBack(&replaceHandle); + // ARRAYSEL(...) -> COND(LT(bitunlinkFrBack(&replaceHandle); V3Number xnum (nodep, nodep->width()); if (nodep->isString()) { xnum = V3Number(V3Number::String(), nodep, ""); - } else { - xnum.setAllBitsX(); - } + } else { + xnum.setAllBitsX(); + } AstNode* newp = new AstCondBound(nodep->fileline(), condp, nodep, new AstConst(nodep->fileline(), xnum)); - if (debug()>=9) newp->dumpTree(cout," _new: "); - // Link in conditional, can blow away temp xor - replaceHandle.relink(newp); - // Added X's, tristate them too + if (debug()>=9) newp->dumpTree(cout, " _new: "); + // Link in conditional, can blow away temp xor + replaceHandle.relink(newp); + // Added X's, tristate them too iterate(newp); - } - else if (!lvalue) { // Mid-multidimension read, just use zero - // ARRAYSEL(...) -> ARRAYSEL(COND(LT(bit ARRAYSEL(COND(LT(bitbitp()->unlinkFrBack(&replaceHandle); V3Number zeronum (nodep, bitp->width(), 0); AstNode* newp = new AstCondBound(bitp->fileline(), condp, bitp, new AstConst(bitp->fileline(), zeronum)); - // Added X's, tristate them too - if (debug()>=9) newp->dumpTree(cout," _new: "); - replaceHandle.relink(newp); + // Added X's, tristate them too + if (debug()>=9) newp->dumpTree(cout, " _new: "); + replaceHandle.relink(newp); iterate(newp); - } - else { // lvalue - replaceBoundLvalue(nodep, condp); - } - } + } + else { // lvalue + replaceBoundLvalue(nodep, condp); + } + } } //-------------------- // Default: Just iterate @@ -456,14 +457,14 @@ private: public: // CONSTUCTORS explicit UnknownVisitor(AstNetlist* nodep) { - m_modp = NULL; - m_assigndlyp = NULL; - m_assignwp = NULL; - m_constXCvt = false; + m_modp = NULL; + m_assigndlyp = NULL; + m_assignwp = NULL; + m_constXCvt = false; iterate(nodep); } virtual ~UnknownVisitor() { - V3Stats::addStat("Unknowns, variables created", m_statUnkVars); + V3Stats::addStat("Unknowns, variables created", m_statUnkVars); } }; diff --git a/src/V3Unknown.h b/src/V3Unknown.h index a1a074c18..77750fe02 100644 --- a/src/V3Unknown.h +++ b/src/V3Unknown.h @@ -34,4 +34,4 @@ public: static void unknownAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index dfc3a2ead..d289af6d6 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -18,13 +18,13 @@ // //************************************************************************* // V3Unroll's Transformations: -// Note is called twice. Once on modules for GenFor unrolling, -// Again after V3Scope for normal for loop unrolling. +// Note is called twice. Once on modules for GenFor unrolling, +// Again after V3Scope for normal for loop unrolling. // // Each module: -// Look for "FOR" loops and unroll them if <= 32 loops. -// (Eventually, a better way would be to simulate the entire loop; ala V3Table.) -// Convert remaining FORs to WHILEs +// Look for "FOR" loops and unroll them if <= 32 loops. +// (Eventually, a better way would be to simulate the entire loop; ala V3Table.) +// Convert remaining FORs to WHILEs // //************************************************************************* @@ -47,136 +47,136 @@ class UnrollVisitor : public AstNVisitor { private: // STATE - AstVar* m_forVarp; // Iterator variable - AstVarScope* m_forVscp; // Iterator variable scope (NULL for generate pass) - AstConst* m_varValuep; // Current value of loop - AstNode* m_ignoreIncp; // Increment node to ignore - bool m_varModeCheck; // Just checking RHS assignments - bool m_varModeReplace; // Replacing varrefs - bool m_varAssignHit; // Assign var hit - bool m_generate; // Expand single generate For loop - string m_beginName; // What name to give begin iterations - V3Double0 m_statLoops; // Statistic tracking - V3Double0 m_statIters; // Statistic tracking + AstVar* m_forVarp; // Iterator variable + AstVarScope* m_forVscp; // Iterator variable scope (NULL for generate pass) + AstConst* m_varValuep; // Current value of loop + AstNode* m_ignoreIncp; // Increment node to ignore + bool m_varModeCheck; // Just checking RHS assignments + bool m_varModeReplace; // Replacing varrefs + bool m_varAssignHit; // Assign var hit + bool m_generate; // Expand single generate For loop + string m_beginName; // What name to give begin iterations + V3Double0 m_statLoops; // Statistic tracking + V3Double0 m_statIters; // Statistic tracking // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS bool cantUnroll(AstNode* nodep, const char* reason) { - if (m_generate) { - nodep->v3error("Unsupported: Can't unroll generate for; "<=9) nodep->dumpTree(cout,"-cant-"); - V3Stats::addStatSum(string("Unrolling gave up, ")+reason, 1); - return false; + if (m_generate) { + nodep->v3error("Unsupported: Can't unroll generate for; "<=9) nodep->dumpTree(cout, "-cant-"); + V3Stats::addStatSum(string("Unrolling gave up, ")+reason, 1); + return false; } int unrollCount() { - return m_generate ? v3Global.opt.unrollCount()*16 - : v3Global.opt.unrollCount(); + return m_generate ? v3Global.opt.unrollCount()*16 + : v3Global.opt.unrollCount(); } bool bodySizeOverRecurse(AstNode* nodep, int& bodySize, int bodyLimit) { - if (!nodep) return false; - bodySize++; - // Exit once exceeds limits, rather than always total - // so don't go O(n^2) when can't unroll - if (bodySize > bodyLimit) return true; - if (bodySizeOverRecurse(nodep->op1p(), bodySize, bodyLimit)) return true; - if (bodySizeOverRecurse(nodep->op2p(), bodySize, bodyLimit)) return true; - if (bodySizeOverRecurse(nodep->op3p(), bodySize, bodyLimit)) return true; - if (bodySizeOverRecurse(nodep->op4p(), bodySize, bodyLimit)) return true; - // Tail recurse. - return bodySizeOverRecurse(nodep->nextp(), bodySize, bodyLimit); + if (!nodep) return false; + bodySize++; + // Exit once exceeds limits, rather than always total + // so don't go O(n^2) when can't unroll + if (bodySize > bodyLimit) return true; + if (bodySizeOverRecurse(nodep->op1p(), bodySize, bodyLimit)) return true; + if (bodySizeOverRecurse(nodep->op2p(), bodySize, bodyLimit)) return true; + if (bodySizeOverRecurse(nodep->op3p(), bodySize, bodyLimit)) return true; + if (bodySizeOverRecurse(nodep->op4p(), bodySize, bodyLimit)) return true; + // Tail recurse. + return bodySizeOverRecurse(nodep->nextp(), bodySize, bodyLimit); } bool forUnrollCheck(AstNode* nodep, - AstNode* initp, // Maybe under nodep (no nextp), or standalone (ignore nextp) - AstNode* precondsp, AstNode* condp, - AstNode* incp, // Maybe under nodep or in bodysp - AstNode* bodysp) { - // To keep the IF levels low, we return as each test fails. - UINFO(4, " FOR Check "<nextp() && initp->nextp()!=nodep) nodep->v3fatalSrc("initial assignment shouldn't be a list"); + if (!initAssp) return cantUnroll(nodep, "no initial assignment"); + if (initp->nextp() && initp->nextp()!=nodep) nodep->v3fatalSrc("initial assignment shouldn't be a list"); if (!VN_IS(initAssp->lhsp(), VarRef)) return cantUnroll(nodep, "no initial assignment to simple variable"); - // - // Condition check - if (condp->nextp()) nodep->v3fatalSrc("conditional shouldn't be a list"); - // - // Assignment of next value check + // + // Condition check + if (condp->nextp()) nodep->v3fatalSrc("conditional shouldn't be a list"); + // + // Assignment of next value check AstAssign* incAssp = VN_CAST(incp, Assign); - if (!incAssp) return cantUnroll(nodep, "no increment assignment"); - if (incAssp->nextp()) nodep->v3fatalSrc("increment shouldn't be a list"); + if (!incAssp) return cantUnroll(nodep, "no increment assignment"); + if (incAssp->nextp()) nodep->v3fatalSrc("increment shouldn't be a list"); m_forVarp = VN_CAST(initAssp->lhsp(), VarRef)->varp(); m_forVscp = VN_CAST(initAssp->lhsp(), VarRef)->varScopep(); if (VN_IS(nodep, GenFor) && !m_forVarp->isGenVar()) { - nodep->v3error("Non-genvar used in generate for: "<prettyName()<rhsp()); // rhsp may change + nodep->v3error("Non-genvar used in generate for: "<prettyName()<rhsp()); // rhsp may change - // This check shouldn't be needed when using V3Simulate - // however, for repeat loops, the loop variable is auto-generated - // and the initp statements will reference a variable outside of the initp scope - // alas, failing to simulate. + // This check shouldn't be needed when using V3Simulate + // however, for repeat loops, the loop variable is auto-generated + // and the initp statements will reference a variable outside of the initp scope + // alas, failing to simulate. AstConst* constInitp = VN_CAST(initAssp->rhsp(), Const); - if (!constInitp) return cantUnroll(nodep, "non-constant initializer"); + if (!constInitp) return cantUnroll(nodep, "non-constant initializer"); - // - // Now, make sure there's no assignment to this variable in the loop - m_varModeCheck = true; - m_varAssignHit = false; - m_ignoreIncp = incp; + // + // Now, make sure there's no assignment to this variable in the loop + m_varModeCheck = true; + m_varAssignHit = false; + m_ignoreIncp = incp; iterateAndNextNull(precondsp); iterateAndNextNull(bodysp); iterateAndNextNull(incp); - m_varModeCheck = false; - m_ignoreIncp = NULL; - if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); + m_varModeCheck = false; + m_ignoreIncp = NULL; + if (m_varAssignHit) return cantUnroll(nodep, "genvar assigned *inside* loop"); - // - if (m_forVscp) { UINFO(8, " Loop Variable: "<=9) nodep->dumpTree(cout,"- for: "); + // + if (m_forVscp) { UINFO(8, " Loop Variable: "<=9) nodep->dumpTree(cout, "- for: "); - if (!m_generate) { + if (!m_generate) { AstAssign *incpAssign = VN_CAST(incp, Assign); - if (!canSimulate(incpAssign->rhsp())) return cantUnroll(incp, "Unable to simulate increment"); - if (!canSimulate(condp)) return cantUnroll(condp, "Unable to simulate condition"); + if (!canSimulate(incpAssign->rhsp())) return cantUnroll(incp, "Unable to simulate increment"); + if (!canSimulate(condp)) return cantUnroll(condp, "Unable to simulate condition"); - // Check whether to we actually want to try and unroll. - int loops; - if (!countLoops(initAssp, condp, incp, unrollCount(), loops)) - return cantUnroll(nodep, "Unable to simulate loop"); + // Check whether to we actually want to try and unroll. + int loops; + if (!countLoops(initAssp, condp, incp, unrollCount(), loops)) + return cantUnroll(nodep, "Unable to simulate loop"); - // Less than 10 statements in the body? - int bodySize = 0; - int bodyLimit = v3Global.opt.unrollStmts(); - if (loops>0) bodyLimit = v3Global.opt.unrollStmts() / loops; - if (bodySizeOverRecurse(precondsp, bodySize/*ref*/, bodyLimit) - || bodySizeOverRecurse(bodysp, bodySize/*ref*/, bodyLimit) - || bodySizeOverRecurse(incp, bodySize/*ref*/, bodyLimit)) { - return cantUnroll(nodep, "too many statements"); - } - } - // Finally, we can do it - if (!forUnroller(nodep, initAssp, condp, precondsp, incp, bodysp)) { - return cantUnroll(nodep, "Unable to unroll loop"); - } - VL_DANGLING(nodep); - // Cleanup - return true; + // Less than 10 statements in the body? + int bodySize = 0; + int bodyLimit = v3Global.opt.unrollStmts(); + if (loops>0) bodyLimit = v3Global.opt.unrollStmts() / loops; + if (bodySizeOverRecurse(precondsp, bodySize/*ref*/, bodyLimit) + || bodySizeOverRecurse(bodysp, bodySize/*ref*/, bodyLimit) + || bodySizeOverRecurse(incp, bodySize/*ref*/, bodyLimit)) { + return cantUnroll(nodep, "too many statements"); + } + } + // Finally, we can do it + if (!forUnroller(nodep, initAssp, condp, precondsp, incp, bodysp)) { + return cantUnroll(nodep, "Unable to unroll loop"); + } + VL_DANGLING(nodep); + // Cleanup + return true; } bool canSimulate(AstNode *nodep) { @@ -187,268 +187,272 @@ private: return simvis.optimizable(); } - bool simulateTree(AstNode *nodep, const V3Number *loopValue, AstNode *dtypep, V3Number &outNum) { - AstNode* clone = nodep->cloneTree(true); - if (!clone) { - nodep->v3fatalSrc("Failed to clone tree"); - return false; - } - if (loopValue) { - m_varValuep = new AstConst (nodep->fileline(), *loopValue); - // Iteration requires a back, so put under temporary node - AstBegin* tempp = new AstBegin (nodep->fileline(), "[EditWrapper]", clone); - m_varModeReplace = true; + bool simulateTree(AstNode *nodep, const V3Number *loopValue, + AstNode *dtypep, V3Number &outNum) { + AstNode* clone = nodep->cloneTree(true); + if (!clone) { + nodep->v3fatalSrc("Failed to clone tree"); + return false; + } + if (loopValue) { + m_varValuep = new AstConst(nodep->fileline(), *loopValue); + // Iteration requires a back, so put under temporary node + AstBegin* tempp = new AstBegin(nodep->fileline(), "[EditWrapper]", clone); + m_varModeReplace = true; iterateAndNextNull(tempp->stmtsp()); - m_varModeReplace = false; - clone = tempp->stmtsp()->unlinkFrBackWithNext(); - tempp->deleteTree(); - tempp = NULL; - pushDeletep(m_varValuep); m_varValuep = NULL; - } - SimulateVisitor simvis; - simvis.mainParamEmulate(clone); - if (!simvis.optimizable()) { - UINFO(3, "Unable to simulate" << endl); - if (debug()>=9) nodep->dumpTree(cout,"- _simtree: "); - return false; - } - // Fetch the result - V3Number* res = simvis.fetchNumberNull(clone); - if (!res) { - UINFO(3, "No number returned from simulation" << endl); - return false; - } - // Patch up datatype - if (dtypep) { - AstConst new_con (clone->fileline(), *res); - new_con.dtypeFrom(dtypep); - outNum = new_con.num(); - return true; - } - outNum = *res; - return true; + m_varModeReplace = false; + clone = tempp->stmtsp()->unlinkFrBackWithNext(); + tempp->deleteTree(); + tempp = NULL; + pushDeletep(m_varValuep); m_varValuep = NULL; + } + SimulateVisitor simvis; + simvis.mainParamEmulate(clone); + if (!simvis.optimizable()) { + UINFO(3, "Unable to simulate" << endl); + if (debug()>=9) nodep->dumpTree(cout, "- _simtree: "); + return false; + } + // Fetch the result + V3Number* res = simvis.fetchNumberNull(clone); + if (!res) { + UINFO(3, "No number returned from simulation" << endl); + return false; + } + // Patch up datatype + if (dtypep) { + AstConst new_con (clone->fileline(), *res); + new_con.dtypeFrom(dtypep); + outNum = new_con.num(); + return true; + } + outNum = *res; + return true; } bool countLoops(AstAssign *initp, AstNode *condp, AstNode *incp, int max, int &outLoopsr) { - outLoopsr = 0; + outLoopsr = 0; V3Number loopValue = V3Number(initp); if (!simulateTree(initp->rhsp(), NULL, initp, loopValue)) { return false; } while (1) { V3Number res = V3Number(initp); - if (!simulateTree(condp, &loopValue, NULL, res)) { - return false; - } - if (!res.isEqOne()) { - break; - } + if (!simulateTree(condp, &loopValue, NULL, res)) { + return false; + } + if (!res.isEqOne()) { + break; + } - outLoopsr++; + outLoopsr++; - // Run inc + // Run inc AstAssign* incpass = VN_CAST(incp, Assign); V3Number newLoopValue = V3Number(initp); if (!simulateTree(incpass->rhsp(), &loopValue, incpass, newLoopValue)) { return false; } - loopValue.opAssign(newLoopValue); - if (outLoopsr > max) { - return false; - } - } - return true; + loopValue.opAssign(newLoopValue); + if (outLoopsr > max) { + return false; + } + } + return true; } bool forUnroller(AstNode* nodep, - AstAssign* initp, - AstNode* condp, - AstNode* precondsp, - AstNode* incp, AstNode* bodysp) { + AstAssign* initp, + AstNode* condp, + AstNode* precondsp, + AstNode* incp, AstNode* bodysp) { UINFO(9, "forUnroller "<rhsp(), NULL, initp, loopValue)) { - return false; - } - AstNode* stmtsp = NULL; - if (initp) { - initp->unlinkFrBack(); // Always a single statement; nextp() may be nodep - // Don't add to list, we do it once, and setting loop index isn't needed as we're constant propagating it - } - if (precondsp) { - precondsp->unlinkFrBackWithNext(); - stmtsp = AstNode::addNextNull(stmtsp, precondsp); - } - if (bodysp) { - bodysp->unlinkFrBackWithNext(); - stmtsp = AstNode::addNextNull(stmtsp, bodysp); // Maybe null if no body - } + return false; + } + AstNode* stmtsp = NULL; + if (initp) { + initp->unlinkFrBack(); // Always a single statement; nextp() may be nodep + // Don't add to list, we do it once, and setting loop index isn't + // needed as we're constant propagating it + } + if (precondsp) { + precondsp->unlinkFrBackWithNext(); + stmtsp = AstNode::addNextNull(stmtsp, precondsp); + } + if (bodysp) { + bodysp->unlinkFrBackWithNext(); + stmtsp = AstNode::addNextNull(stmtsp, bodysp); // Maybe null if no body + } if (incp && !VN_IS(nodep, GenFor)) { // Generates don't need to increment loop index - incp->unlinkFrBackWithNext(); - stmtsp = AstNode::addNextNull(stmtsp, incp); // Maybe null if no body - } - // Mark variable to disable some later warnings - m_forVarp->usedLoopIdx(true); + incp->unlinkFrBackWithNext(); + stmtsp = AstNode::addNextNull(stmtsp, incp); // Maybe null if no body + } + // Mark variable to disable some later warnings + m_forVarp->usedLoopIdx(true); - AstNode* newbodysp = NULL; - ++m_statLoops; - if (stmtsp) { - int times = 0; - while (1) { + AstNode* newbodysp = NULL; + ++m_statLoops; + if (stmtsp) { + int times = 0; + while (1) { UINFO(8," Looping "<v3error("Loop unrolling failed."); - return false; - } - if (!res.isEqOne()) { - break; // Done with the loop - } - else { - // Replace iterator values with constant. - AstNode* oneloopp = stmtsp->cloneTree(true); + nodep->v3error("Loop unrolling failed."); + return false; + } + if (!res.isEqOne()) { + break; // Done with the loop + } + else { + // Replace iterator values with constant. + AstNode* oneloopp = stmtsp->cloneTree(true); - m_varValuep = new AstConst(nodep->fileline(), loopValue); + m_varValuep = new AstConst(nodep->fileline(), loopValue); - // Iteration requires a back, so put under temporary node - if (oneloopp) { - AstBegin* tempp = new AstBegin(oneloopp->fileline(),"[EditWrapper]",oneloopp); - m_varModeReplace = true; + // Iteration requires a back, so put under temporary node + if (oneloopp) { + AstBegin* tempp = new AstBegin(oneloopp->fileline(), + "[EditWrapper]", oneloopp); + m_varModeReplace = true; iterateAndNextNull(tempp->stmtsp()); - m_varModeReplace = false; - oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); VL_DANGLING(tempp); - } - if (m_generate) { - string index = AstNode::encodeNumber(m_varValuep->toSInt()); - string nname = m_beginName + "__BRA__" + index + "__KET__"; - oneloopp = new AstBegin(oneloopp->fileline(),nname,oneloopp,true); - } - pushDeletep(m_varValuep); m_varValuep=NULL; - if (newbodysp) newbodysp->addNext(oneloopp); - else newbodysp = oneloopp; + m_varModeReplace = false; + oneloopp = tempp->stmtsp()->unlinkFrBackWithNext(); + tempp->deleteTree(); VL_DANGLING(tempp); + } + if (m_generate) { + string index = AstNode::encodeNumber(m_varValuep->toSInt()); + string nname = m_beginName + "__BRA__" + index + "__KET__"; + oneloopp = new AstBegin(oneloopp->fileline(), nname, oneloopp, true); + } + pushDeletep(m_varValuep); m_varValuep = NULL; + if (newbodysp) newbodysp->addNext(oneloopp); + else newbodysp = oneloopp; - ++m_statIters; - if (++times > unrollCount()*3) { + ++m_statIters; + if (++times > unrollCount()*3) { nodep->v3error("Loop unrolling took too long;" " probably this is an infinite loop, or set --unroll-count above " <rhsp(), &loopValue, incpass, newLoopValue)) { nodep->v3error("Loop unrolling failed"); - return false; - } - loopValue.opAssign(newLoopValue); - } - } - } - // Replace the FOR() - if (newbodysp) nodep->replaceWith(newbodysp); - else nodep->unlinkFrBack(); - if (bodysp) { pushDeletep(bodysp); VL_DANGLING(bodysp); } - if (precondsp) { pushDeletep(precondsp); VL_DANGLING(precondsp); } - if (initp) { pushDeletep(initp); VL_DANGLING(initp); } - if (incp && !incp->backp()) { pushDeletep(incp); VL_DANGLING(incp); } - if (debug()>=9 && newbodysp) newbodysp->dumpTree(cout,"- _new: "); - return true; + return false; + } + loopValue.opAssign(newLoopValue); + } + } + } + // Replace the FOR() + if (newbodysp) nodep->replaceWith(newbodysp); + else nodep->unlinkFrBack(); + if (bodysp) { pushDeletep(bodysp); VL_DANGLING(bodysp); } + if (precondsp) { pushDeletep(precondsp); VL_DANGLING(precondsp); } + if (initp) { pushDeletep(initp); VL_DANGLING(initp); } + if (incp && !incp->backp()) { pushDeletep(incp); VL_DANGLING(incp); } + if (debug()>=9 && newbodysp) newbodysp->dumpTree(cout, "- _new: "); + return true; } virtual void visit(AstWhile* nodep) { iterateChildren(nodep); - if (m_varModeCheck || m_varModeReplace) { - } else { - // Constify before unroll call, as it may change what is underneath. - if (nodep->precondsp()) V3Const::constifyEdit(nodep->precondsp()); // precondsp may change - if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change - // Grab initial value - AstNode* initp = NULL; // Should be statement before the while. - if (nodep->backp()->nextp() == nodep) initp=nodep->backp(); - if (initp) { V3Const::constifyEdit(initp); VL_DANGLING(initp); } - if (nodep->backp()->nextp() == nodep) initp=nodep->backp(); - // Grab assignment - AstNode* incp = NULL; // Should be last statement - if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp()); - if (nodep->incsp()) incp = nodep->incsp(); - else { - for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} - if (incp) { V3Const::constifyEdit(incp); VL_DANGLING(incp); } - for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed - } - // And check it - if (forUnrollCheck(nodep, initp, - nodep->precondsp(), nodep->condp(), - incp, nodep->bodysp())) { - pushDeletep(nodep); VL_DANGLING(nodep); // Did replacement - } - } + if (m_varModeCheck || m_varModeReplace) { + } else { + // Constify before unroll call, as it may change what is underneath. + if (nodep->precondsp()) V3Const::constifyEdit(nodep->precondsp()); // precondsp may change + if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change + // Grab initial value + AstNode* initp = NULL; // Should be statement before the while. + if (nodep->backp()->nextp() == nodep) initp = nodep->backp(); + if (initp) { V3Const::constifyEdit(initp); VL_DANGLING(initp); } + if (nodep->backp()->nextp() == nodep) initp = nodep->backp(); + // Grab assignment + AstNode* incp = NULL; // Should be last statement + if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp()); + if (nodep->incsp()) incp = nodep->incsp(); + else { + for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} + if (incp) { V3Const::constifyEdit(incp); VL_DANGLING(incp); } + for (incp = nodep->bodysp(); incp && incp->nextp(); incp = incp->nextp()) {} // Again, as may have changed + } + // And check it + if (forUnrollCheck(nodep, initp, + nodep->precondsp(), nodep->condp(), + incp, nodep->bodysp())) { + pushDeletep(nodep); VL_DANGLING(nodep); // Did replacement + } + } } virtual void visit(AstGenFor* nodep) { - if (!m_generate || m_varModeReplace) { + if (!m_generate || m_varModeReplace) { iterateChildren(nodep); - } // else V3Param will recursively call each for loop to be unrolled for us - if (m_varModeCheck || m_varModeReplace) { - } else { - // Constify before unroll call, as it may change what is underneath. - if (nodep->initsp()) V3Const::constifyEdit(nodep->initsp()); // initsp may change - if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change - if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp()); // incsp may change - if (nodep->condp()->isZero()) { - // We don't need to do any loops. Remove the GenFor, - // Genvar's don't care about any initial assignments. - // - // Note normal For's can't do exactly this deletion, as - // we'd need to initialize the variable to the initial - // condition, but they'll become while's which can be - // deleted by V3Const. + } // else V3Param will recursively call each for loop to be unrolled for us + if (m_varModeCheck || m_varModeReplace) { + } else { + // Constify before unroll call, as it may change what is underneath. + if (nodep->initsp()) V3Const::constifyEdit(nodep->initsp()); // initsp may change + if (nodep->condp()) V3Const::constifyEdit(nodep->condp()); // condp may change + if (nodep->incsp()) V3Const::constifyEdit(nodep->incsp()); // incsp may change + if (nodep->condp()->isZero()) { + // We don't need to do any loops. Remove the GenFor, + // Genvar's don't care about any initial assignments. + // + // Note normal For's can't do exactly this deletion, as + // we'd need to initialize the variable to the initial + // condition, but they'll become while's which can be + // deleted by V3Const. pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep); } else if (forUnrollCheck(nodep, nodep->initsp(), NULL, nodep->condp(), nodep->incsp(), nodep->bodysp())) { pushDeletep(nodep); VL_DANGLING(nodep); // Did replacement - } else { - nodep->v3error("For loop doesn't have genvar index, or is malformed"); - } - } + } else { + nodep->v3error("For loop doesn't have genvar index, or is malformed"); + } + } } virtual void visit(AstNodeFor* nodep) { - if (m_generate) { // Ignore for's when expanding genfor's + if (m_generate) { // Ignore for's when expanding genfor's iterateChildren(nodep); - } else { - nodep->v3error("V3Begin should have removed standard FORs"); - } + } else { + nodep->v3error("V3Begin should have removed standard FORs"); + } } virtual void visit(AstVarRef* nodep) { - if (m_varModeCheck - && nodep->varp() == m_forVarp - && nodep->varScopep() == m_forVscp - && nodep->lvalue()) { - UINFO(8," Itervar assigned to: "<varp() == m_forVarp + && nodep->varScopep() == m_forVscp + && nodep->lvalue()) { + UINFO(8," Itervar assigned to: "<varp() == m_forVarp - && nodep->varScopep() == m_forVscp - && !nodep->lvalue()) { - AstNode* newconstp = m_varValuep->cloneTree(false); - nodep->replaceWith(newconstp); - pushDeletep(nodep); - } + if (m_varModeReplace + && nodep->varp() == m_forVarp + && nodep->varScopep() == m_forVscp + && !nodep->lvalue()) { + AstNode* newconstp = m_varValuep->cloneTree(false); + nodep->replaceWith(newconstp); + pushDeletep(nodep); + } } //-------------------- // Default: Just iterate virtual void visit(AstNode* nodep) { - if (m_varModeCheck && nodep == m_ignoreIncp) { - // Ignore subtree that is the increment - } else { + if (m_varModeCheck && nodep == m_ignoreIncp) { + // Ignore subtree that is the increment + } else { iterateChildren(nodep); - } + } } public: @@ -460,15 +464,15 @@ public: } // METHORS void init(bool generate, const string& beginName) { - m_forVarp = NULL; - m_forVscp = NULL; + m_forVarp = NULL; + m_forVscp = NULL; m_varValuep = NULL; - m_ignoreIncp = NULL; - m_varModeCheck = false; - m_varModeReplace = false; + m_ignoreIncp = NULL; + m_varModeCheck = false; + m_varModeReplace = false; m_varAssignHit = false; - m_generate = generate; - m_beginName = beginName; + m_generate = generate; + m_beginName = beginName; } void process(AstNode* nodep, bool generate, const string& beginName) { init(generate, beginName); diff --git a/src/V3Unroll.h b/src/V3Unroll.h index 3a6bf5e67..b26387eb6 100644 --- a/src/V3Unroll.h +++ b/src/V3Unroll.h @@ -52,4 +52,4 @@ public: static void unrollAll(AstNetlist* nodep); }; -#endif // Guard +#endif // Guard diff --git a/src/V3Width.cpp b/src/V3Width.cpp index b54e10474..b6823afd5 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -18,44 +18,44 @@ // //************************************************************************* // V3Width's Transformations: -// Top down traversal: -// Determine width of sub-expressions +// Top down traversal: +// Determine width of sub-expressions // width() = # bits upper expression wants, 0 for anything-goes -// widthUnsized() = # bits for unsized constant, or 0 if it's sized -// widthMin() = Alternative acceptable width for linting, or width() if sized -// Determine this subop's width, can be either: +// widthUnsized() = # bits for unsized constant, or 0 if it's sized +// widthMin() = Alternative acceptable width for linting, or width() if sized +// Determine this subop's width, can be either: // Fixed width X -// Unsized, min width X ('d5 is unsized, min 3 bits.) -// Pass up: -// width() = # bits this expression generates -// widthSized() = true if all constants sized, else false +// Unsized, min width X ('d5 is unsized, min 3 bits.) +// Pass up: +// width() = # bits this expression generates +// widthSized() = true if all constants sized, else false // Compute size of this expression -// Lint warn about mismatches +// Lint warn about mismatches // If expr size != subop fixed, bad // If expr size < subop unsized minimum, bad // If expr size != subop, edit netlist // For == and similar ops, if multibit underneath, add a REDOR // If subop larger, add a EXTRACT -// If subop smaller, add a EXTEND -// Pass size to sub-expressions if required (+/-* etc) +// If subop smaller, add a EXTEND +// Pass size to sub-expressions if required (+/-* etc) // FINAL = true. // Subexpressions lint and extend as needed // //************************************************************************* // Signedness depends on: -// Decimal numbers are signed -// Based numbers are unsigned unless 's' prefix -// Comparison results are unsigned -// Bit&Part selects are unsigned, even if whole -// Concatenates are unsigned +// Decimal numbers are signed +// Based numbers are unsigned unless 's' prefix +// Comparison results are unsigned +// Bit&Part selects are unsigned, even if whole +// Concatenates are unsigned // Ignore signedness of self-determined: // shift rhs, ** rhs, x?: lhs, concat and replicate members -// Else, if any operand unsigned, output unsigned +// Else, if any operand unsigned, output unsigned // // Real number rules: // Real numbers are real (duh) -// Reals convert to integers by rounding -// Reals init to 0.0 +// Reals convert to integers by rounding +// Reals init to 0.0 // Logicals convert compared to zero // If any operand is real, result is real //************************************************************************* @@ -85,18 +85,18 @@ //###################################################################### -enum Stage { PRELIM=1,FINAL=2,BOTH=3 }; // Numbers are a bitmask <0>=prelim, <1>=final +enum Stage { PRELIM = 1, FINAL = 2, BOTH = 3 }; // Numbers are a bitmask <0>=prelim, <1>=final std::ostream& operator<<(std::ostream& str, const Stage& rhs) { return str<<("-PFB"[static_cast(rhs)]); } enum Determ { - SELF, // Self-determined - CONTEXT, // Context-determined - ASSIGN // Assignment-like where sign comes from RHS only + SELF, // Self-determined + CONTEXT, // Context-determined + ASSIGN // Assignment-like where sign comes from RHS only }; std::ostream& operator<<(std::ostream& str, const Determ& rhs) { - static const char* const s_det[] = {"SELF","CNTX","ASSN"}; + static const char* const s_det[] = {"SELF", "CNTX", "ASSN"}; return str<width(); + if (!m_dtypep) v3fatalSrc("Width request on self-determined or preliminary VUP"); + return m_dtypep->width(); } int widthMin() const { - if (!m_dtypep) v3fatalSrc("Width request on self-determined or preliminary VUP"); - return m_dtypep->widthMin(); + if (!m_dtypep) v3fatalSrc("Width request on self-determined or preliminary VUP"); + return m_dtypep->widthMin(); } bool prelim() const { return m_stage & PRELIM; } bool final() const { return m_stage & FINAL; } void dump(std::ostream& str) const { - if (!m_dtypep) { - str<<" VUP(s="< PatVecMap; // STATE - WidthVP* m_vup; // Current node state - bool m_paramsOnly; // Computing parameter value; limit operation - AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations + WidthVP* m_vup; // Current node state + bool m_paramsOnly; // Computing parameter value; limit operation + AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations AstNodeFTask* m_ftaskp; // Current function/task - AstFunc* m_funcp; // Current function - AstInitial* m_initialp; // Current initial block - AstAttrOf* m_attrp; // Current attribute - bool m_doGenerate; // Do errors later inside generate statement - int m_dtTables; // Number of created data type tables - TableMap m_tableMap; // Created tables so can remove duplicates + AstFunc* m_funcp; // Current function + AstInitial* m_initialp; // Current initial block + AstAttrOf* m_attrp; // Current attribute + bool m_doGenerate; // Do errors later inside generate statement + int m_dtTables; // Number of created data type tables + TableMap m_tableMap; // Created tables so can remove duplicates // ENUMS enum ExtendRule { - EXTEND_EXP, // Extend if expect sign and node signed, e.g. node=y in ADD(x,y), "x + y" - EXTEND_ZERO, // Extend with zeros. e.g. node=y in EQ(x,y), "x == y" - EXTEND_LHS, // Extend with sign if node signed. e.g. node=y in ASSIGN(y,x), "x = y" - EXTEND_OFF // No extension + EXTEND_EXP, // Extend if expect sign and node signed, e.g. node=y in ADD(x,y), "x + y" + EXTEND_ZERO, // Extend with zeros. e.g. node=y in EQ(x,y), "x == y" + EXTEND_LHS, // Extend with sign if node signed. e.g. node=y in ASSIGN(y,x), "x = y" + EXTEND_OFF // No extension }; // METHODS @@ -209,506 +210,520 @@ private: // VISITORS // Naming: width_O{outputtype}_L{lhstype}_R{rhstype}_W{widthing}_S{signing} - // Where type: - // _O1=boolean (width 1 unsigned) - // _Ou=unsigned - // _Os=signed - // _Ous=unsigned or signed - // _Or=real - // _Ox=anything + // Where type: + // _O1=boolean (width 1 unsigned) + // _Ou=unsigned + // _Os=signed + // _Ous=unsigned or signed + // _Or=real + // _Ox=anything // Widths: 1 bit out, lhs 1 bit; Real: converts via compare with 0 - virtual void visit(AstLogNot* nodep) { visit_log_not(nodep); } + virtual void visit(AstLogNot* nodep) { visit_log_not(nodep); } // Widths: 1 bit out, lhs 1 bit, rhs 1 bit; Real: converts via compare with 0 - virtual void visit(AstLogAnd* nodep) { visit_log_and_or(nodep); } - virtual void visit(AstLogOr* nodep) { visit_log_and_or(nodep); } - virtual void visit(AstLogIf* nodep) { visit_log_and_or(nodep); } // Conversion from real not in IEEE, but a fallout - virtual void visit(AstLogIff* nodep) { visit_log_and_or(nodep); } // Conversion from real not in IEEE, but a fallout + virtual void visit(AstLogAnd* nodep) { visit_log_and_or(nodep); } + virtual void visit(AstLogOr* nodep) { visit_log_and_or(nodep); } + virtual void visit(AstLogIf* nodep) { visit_log_and_or(nodep); } // Conversion from real not in IEEE, but a fallout + virtual void visit(AstLogIff* nodep) { visit_log_and_or(nodep); } // Conversion from real not in IEEE, but a fallout // Widths: 1 bit out, Any width lhs - virtual void visit(AstRedAnd* nodep) { visit_red_and_or(nodep); } - virtual void visit(AstRedOr* nodep) { visit_red_and_or(nodep); } - virtual void visit(AstRedXnor* nodep){ visit_red_and_or(nodep); } - virtual void visit(AstRedXor* nodep) { visit_red_and_or(nodep); } - virtual void visit(AstOneHot* nodep) { visit_red_and_or(nodep); } - virtual void visit(AstOneHot0* nodep) { visit_red_and_or(nodep); } - virtual void visit(AstIsUnknown* nodep) { visit_red_unknown(nodep); } // Allow real + virtual void visit(AstRedAnd* nodep) { visit_red_and_or(nodep); } + virtual void visit(AstRedOr* nodep) { visit_red_and_or(nodep); } + virtual void visit(AstRedXnor* nodep){ visit_red_and_or(nodep); } + virtual void visit(AstRedXor* nodep) { visit_red_and_or(nodep); } + virtual void visit(AstOneHot* nodep) { visit_red_and_or(nodep); } + virtual void visit(AstOneHot0* nodep) { visit_red_and_or(nodep); } + virtual void visit(AstIsUnknown* nodep) { visit_red_unknown(nodep); } // Allow real // These have different node types, as they operate differently // Must add to case statement below, // Widths: 1 bit out, lhs width == rhs width. real if lhs|rhs real - virtual void visit(AstEq* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstNeq* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstGt* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstGte* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstLt* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstLte* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstGtS* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstGteS* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstLtS* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstLteS* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstEqCase* nodep) { visit_cmp_eq_gt(nodep, true); } - virtual void visit(AstNeqCase* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstEq* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstNeq* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstGt* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstGte* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstLt* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstLte* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstGtS* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstGteS* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstLtS* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstLteS* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstEqCase* nodep) { visit_cmp_eq_gt(nodep, true); } + virtual void visit(AstNeqCase* nodep) { visit_cmp_eq_gt(nodep, true); } // ... These comparisons don't allow reals - virtual void visit(AstEqWild* nodep) { visit_cmp_eq_gt(nodep, false); } - virtual void visit(AstNeqWild* nodep) { visit_cmp_eq_gt(nodep, false); } + virtual void visit(AstEqWild* nodep) { visit_cmp_eq_gt(nodep, false); } + virtual void visit(AstNeqWild* nodep) { visit_cmp_eq_gt(nodep, false); } // ... Real compares - virtual void visit(AstEqD* nodep) { visit_cmp_real(nodep); } - virtual void visit(AstNeqD* nodep) { visit_cmp_real(nodep); } - virtual void visit(AstLtD* nodep) { visit_cmp_real(nodep); } - virtual void visit(AstLteD* nodep) { visit_cmp_real(nodep); } - virtual void visit(AstGtD* nodep) { visit_cmp_real(nodep); } - virtual void visit(AstGteD* nodep) { visit_cmp_real(nodep); } + virtual void visit(AstEqD* nodep) { visit_cmp_real(nodep); } + virtual void visit(AstNeqD* nodep) { visit_cmp_real(nodep); } + virtual void visit(AstLtD* nodep) { visit_cmp_real(nodep); } + virtual void visit(AstLteD* nodep) { visit_cmp_real(nodep); } + virtual void visit(AstGtD* nodep) { visit_cmp_real(nodep); } + virtual void visit(AstGteD* nodep) { visit_cmp_real(nodep); } // ... String compares - virtual void visit(AstEqN* nodep) { visit_cmp_string(nodep); } - virtual void visit(AstNeqN* nodep) { visit_cmp_string(nodep); } - virtual void visit(AstLtN* nodep) { visit_cmp_string(nodep); } - virtual void visit(AstLteN* nodep) { visit_cmp_string(nodep); } - virtual void visit(AstGtN* nodep) { visit_cmp_string(nodep); } - virtual void visit(AstGteN* nodep) { visit_cmp_string(nodep); } + virtual void visit(AstEqN* nodep) { visit_cmp_string(nodep); } + virtual void visit(AstNeqN* nodep) { visit_cmp_string(nodep); } + virtual void visit(AstLtN* nodep) { visit_cmp_string(nodep); } + virtual void visit(AstLteN* nodep) { visit_cmp_string(nodep); } + virtual void visit(AstGtN* nodep) { visit_cmp_string(nodep); } + virtual void visit(AstGteN* nodep) { visit_cmp_string(nodep); } // Widths: out width = lhs width = rhs width // Signed: Output signed iff LHS & RHS signed. // Real: Not allowed - virtual void visit(AstAnd* nodep) { visit_boolmath_and_or(nodep); } - virtual void visit(AstOr* nodep) { visit_boolmath_and_or(nodep); } - virtual void visit(AstXnor* nodep) { visit_boolmath_and_or(nodep); } - virtual void visit(AstXor* nodep) { visit_boolmath_and_or(nodep); } - virtual void visit(AstBufIf1* nodep) { visit_boolmath_and_or(nodep); } // Signed behavior changing in 3.814 + virtual void visit(AstAnd* nodep) { visit_boolmath_and_or(nodep); } + virtual void visit(AstOr* nodep) { visit_boolmath_and_or(nodep); } + virtual void visit(AstXnor* nodep) { visit_boolmath_and_or(nodep); } + virtual void visit(AstXor* nodep) { visit_boolmath_and_or(nodep); } + virtual void visit(AstBufIf1* nodep) { visit_boolmath_and_or(nodep); } // Signed behavior changing in 3.814 // Width: Max(Lhs,Rhs) sort of. // Real: If either side real // Signed: If both sides real - virtual void visit(AstAdd* nodep) { visit_add_sub_replace(nodep, true); } - virtual void visit(AstSub* nodep) { visit_add_sub_replace(nodep, true); } - virtual void visit(AstDiv* nodep) { visit_add_sub_replace(nodep, true); } - virtual void visit(AstMul* nodep) { visit_add_sub_replace(nodep, true); } + virtual void visit(AstAdd* nodep) { visit_add_sub_replace(nodep, true); } + virtual void visit(AstSub* nodep) { visit_add_sub_replace(nodep, true); } + virtual void visit(AstDiv* nodep) { visit_add_sub_replace(nodep, true); } + virtual void visit(AstMul* nodep) { visit_add_sub_replace(nodep, true); } // These can't promote to real - virtual void visit(AstModDiv* nodep) { visit_add_sub_replace(nodep, false); } - virtual void visit(AstModDivS* nodep) { visit_add_sub_replace(nodep, false); } - virtual void visit(AstMulS* nodep) { visit_add_sub_replace(nodep, false); } - virtual void visit(AstDivS* nodep) { visit_add_sub_replace(nodep, false); } + virtual void visit(AstModDiv* nodep) { visit_add_sub_replace(nodep, false); } + virtual void visit(AstModDivS* nodep) { visit_add_sub_replace(nodep, false); } + virtual void visit(AstMulS* nodep) { visit_add_sub_replace(nodep, false); } + virtual void visit(AstDivS* nodep) { visit_add_sub_replace(nodep, false); } // Widths: out width = lhs width, but upper matters // Signed: Output signed iff LHS signed; unary operator // Unary promote to real - virtual void visit(AstNegate* nodep) { visit_negate_not(nodep, true); } + virtual void visit(AstNegate* nodep) { visit_negate_not(nodep, true); } // Unary never real - virtual void visit(AstNot* nodep) { visit_negate_not(nodep, false); } + virtual void visit(AstNot* nodep) { visit_negate_not(nodep, false); } // Real: inputs and output real - virtual void visit(AstAddD* nodep) { visit_real_add_sub(nodep); } - virtual void visit(AstSubD* nodep) { visit_real_add_sub(nodep); } - virtual void visit(AstDivD* nodep) { visit_real_add_sub(nodep); } - virtual void visit(AstMulD* nodep) { visit_real_add_sub(nodep); } - virtual void visit(AstPowD* nodep) { visit_real_add_sub(nodep); } + virtual void visit(AstAddD* nodep) { visit_real_add_sub(nodep); } + virtual void visit(AstSubD* nodep) { visit_real_add_sub(nodep); } + virtual void visit(AstDivD* nodep) { visit_real_add_sub(nodep); } + virtual void visit(AstMulD* nodep) { visit_real_add_sub(nodep); } + virtual void visit(AstPowD* nodep) { visit_real_add_sub(nodep); } virtual void visit(AstNodeSystemBiop* nodep) { visit_real_add_sub(nodep); } // Real: Output real - virtual void visit(AstNegateD* nodep) { visit_real_neg_ceil(nodep); } + virtual void visit(AstNegateD* nodep) { visit_real_neg_ceil(nodep); } virtual void visit(AstNodeSystemUniop* nodep) { visit_real_neg_ceil(nodep); } // Widths: out signed/unsigned width = lhs width, input un|signed - virtual void visit(AstSigned* nodep) { visit_signed_unsigned(nodep, AstNumeric::SIGNED); } - virtual void visit(AstUnsigned* nodep) { visit_signed_unsigned(nodep, AstNumeric::UNSIGNED); } + virtual void visit(AstSigned* nodep) { + visit_signed_unsigned(nodep, AstNumeric::SIGNED); } + virtual void visit(AstUnsigned* nodep) { + visit_signed_unsigned(nodep, AstNumeric::UNSIGNED); } // Widths: Output width from lhs, rhs<33 bits // Signed: If lhs signed - virtual void visit(AstShiftL* nodep) { visit_shift(nodep); } - virtual void visit(AstShiftR* nodep) { visit_shift(nodep); } + virtual void visit(AstShiftL* nodep) { visit_shift(nodep); } + virtual void visit(AstShiftR* nodep) { visit_shift(nodep); } // ShiftRS converts to ShiftR, but not vice-versa - virtual void visit(AstShiftRS* nodep) { visit_shift(nodep); } + virtual void visit(AstShiftRS* nodep) { visit_shift(nodep); } //======== // Widths: Output real, input integer signed - virtual void visit(AstBitsToRealD* nodep) { visit_Or_Lu64(nodep); } - virtual void visit(AstIToRD* nodep) { visit_Or_Ls32(nodep); } + virtual void visit(AstBitsToRealD* nodep) { visit_Or_Lu64(nodep); } + virtual void visit(AstIToRD* nodep) { visit_Or_Ls32(nodep); } // Widths: Output integer signed, input real - virtual void visit(AstRToIS* nodep) { visit_Os32_Lr(nodep); } - virtual void visit(AstRToIRoundS* nodep) { visit_Os32_Lr(nodep); } + virtual void visit(AstRToIS* nodep) { visit_Os32_Lr(nodep); } + virtual void visit(AstRToIRoundS* nodep) { visit_Os32_Lr(nodep); } // Widths: Output integer unsigned, input real - virtual void visit(AstRealToBits* nodep) { visit_Ou64_Lr(nodep); } + virtual void visit(AstRealToBits* nodep) { visit_Ou64_Lr(nodep); } // Output integer, input string virtual void visit(AstLenN* nodep) { visit_Os32_string(nodep); } // Widths: Constant, terminal - virtual void visit(AstTime* nodep) { nodep->dtypeSetUInt64(); } - virtual void visit(AstTimeD* nodep) { nodep->dtypeSetDouble(); } + virtual void visit(AstTime* nodep) { nodep->dtypeSetUInt64(); } + virtual void visit(AstTimeD* nodep) { nodep->dtypeSetDouble(); } virtual void visit(AstTestPlusArgs* nodep) { nodep->dtypeSetSigned32(); } - virtual void visit(AstScopeName* nodep) { nodep->dtypeSetUInt64(); } // A pointer, but not that it matters + virtual void visit(AstScopeName* nodep) { nodep->dtypeSetUInt64(); } // A pointer, but not that it matters // Special cases. So many.... virtual void visit(AstNodeCond* nodep) { - // op=cond?expr1:expr2 - // Signed: Output signed iff RHS & THS signed (presumed, not in IEEE) - // See IEEE-2012 11.4.11 and Table 11-21. - // LHS is self-determined - // Width: max(RHS,THS) - // Real: Output real if either expression is real, non-real argument gets converted - if (m_vup->prelim()) { // First stage evaluation - // Just once, do the conditional, expect one bit out. - iterateCheckBool(nodep,"Conditional Test",nodep->condp(),BOTH); - // Determine sub expression widths only relying on what's in the subops - // CONTEXT determined, but need data type for pattern assignments - userIterateAndNext(nodep->expr1p(), WidthVP(m_vup->dtypeNullp(),PRELIM).p()); - userIterateAndNext(nodep->expr2p(), WidthVP(m_vup->dtypeNullp(),PRELIM).p()); - // Calculate width of this expression. - // First call (prelim()) m_vup->width() is probably zero, so we'll return - // the size of this subexpression only. - // Second call (final()) m_vup->width() is probably the expression size, so - // the expression includes the size of the output too. - if (nodep->expr1p()->isDouble() || nodep->expr2p()->isDouble()) { - nodep->dtypeSetDouble(); + // op=cond?expr1:expr2 + // Signed: Output signed iff RHS & THS signed (presumed, not in IEEE) + // See IEEE-2012 11.4.11 and Table 11-21. + // LHS is self-determined + // Width: max(RHS,THS) + // Real: Output real if either expression is real, non-real argument gets converted + if (m_vup->prelim()) { // First stage evaluation + // Just once, do the conditional, expect one bit out. + iterateCheckBool(nodep, "Conditional Test", nodep->condp(), BOTH); + // Determine sub expression widths only relying on what's in the subops + // CONTEXT determined, but need data type for pattern assignments + userIterateAndNext(nodep->expr1p(), WidthVP(m_vup->dtypeNullp(), PRELIM).p()); + userIterateAndNext(nodep->expr2p(), WidthVP(m_vup->dtypeNullp(), PRELIM).p()); + // Calculate width of this expression. + // First call (prelim()) m_vup->width() is probably zero, so we'll return + // the size of this subexpression only. + // Second call (final()) m_vup->width() is probably the expression size, so + // the expression includes the size of the output too. + if (nodep->expr1p()->isDouble() || nodep->expr2p()->isDouble()) { + nodep->dtypeSetDouble(); } else if (nodep->expr1p()->isString() || nodep->expr2p()->isString()) { nodep->dtypeSetString(); - } else { + } else { int width = std::max(nodep->expr1p()->width(), nodep->expr2p()->width()); int mwidth = std::max(nodep->expr1p()->widthMin(), nodep->expr2p()->widthMin()); - bool issigned = nodep->expr1p()->isSigned() && nodep->expr2p()->isSigned(); - nodep->dtypeSetLogicSized(width,mwidth,AstNumeric::fromBool(issigned)); - } - } - if (m_vup->final()) { - AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); - AstNodeDType* subDTypep = expDTypep; - nodep->dtypeFrom(expDTypep); - // Error report and change sizes for suboperands of this node. - iterateCheck(nodep,"Conditional True", nodep->expr1p(),CONTEXT,FINAL,subDTypep,EXTEND_EXP); - iterateCheck(nodep,"Conditional False",nodep->expr2p(),CONTEXT,FINAL,subDTypep,EXTEND_EXP); - } + bool issigned = nodep->expr1p()->isSigned() && nodep->expr2p()->isSigned(); + nodep->dtypeSetLogicSized(width, mwidth, AstNumeric::fromBool(issigned)); + } + } + if (m_vup->final()) { + AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); + AstNodeDType* subDTypep = expDTypep; + nodep->dtypeFrom(expDTypep); + // Error report and change sizes for suboperands of this node. + iterateCheck(nodep, "Conditional True", nodep->expr1p(), CONTEXT, FINAL, subDTypep, EXTEND_EXP); + iterateCheck(nodep, "Conditional False", nodep->expr2p(), CONTEXT, FINAL, subDTypep, EXTEND_EXP); + } } virtual void visit(AstConcat* nodep) { - // Real: Not allowed (assumed) - // Signed: unsigned output, input either (assumed) - // IEEE-2012 Table 11-21, and 11.8.1: - // LHS, RHS is self-determined - // signed: Unsigned (11.8.1) - // width: LHS + RHS - if (m_vup->prelim()) { - iterateCheckSizedSelf(nodep,"LHS",nodep->lhsp(),SELF,BOTH); - iterateCheckSizedSelf(nodep,"RHS",nodep->rhsp(),SELF,BOTH); - nodep->dtypeSetLogicSized(nodep->lhsp()->width() + nodep->rhsp()->width(), - nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin(), - AstNumeric::UNSIGNED); - // Cleanup zero width Verilog2001 {x,{0{foo}}} now, - // otherwise having width(0) will cause later assertions to fire - if (AstReplicate* repp=VN_CAST(nodep->lhsp(), Replicate)) { - if (repp->width()==0) { // Keep rhs - nodep->replaceWith(nodep->rhsp()->unlinkFrBack()); - pushDeletep(nodep); VL_DANGLING(nodep); - return; - } - } - if (AstReplicate* repp=VN_CAST(nodep->rhsp(), Replicate)) { - if (repp->width()==0) { // Keep lhs - nodep->replaceWith(nodep->lhsp()->unlinkFrBack()); - pushDeletep(nodep); VL_DANGLING(nodep); - return; - } - } - if (nodep->lhsp()->isString() - || nodep->rhsp()->isString()) { - AstNode* newp = new AstConcatN(nodep->fileline(),nodep->lhsp()->unlinkFrBack(), + // Real: Not allowed (assumed) + // Signed: unsigned output, input either (assumed) + // IEEE-2012 Table 11-21, and 11.8.1: + // LHS, RHS is self-determined + // signed: Unsigned (11.8.1) + // width: LHS + RHS + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); + iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); + nodep->dtypeSetLogicSized(nodep->lhsp()->width() + nodep->rhsp()->width(), + nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin(), + AstNumeric::UNSIGNED); + // Cleanup zero width Verilog2001 {x,{0{foo}}} now, + // otherwise having width(0) will cause later assertions to fire + if (AstReplicate* repp = VN_CAST(nodep->lhsp(), Replicate)) { + if (repp->width()==0) { // Keep rhs + nodep->replaceWith(nodep->rhsp()->unlinkFrBack()); + pushDeletep(nodep); VL_DANGLING(nodep); + return; + } + } + if (AstReplicate* repp = VN_CAST(nodep->rhsp(), Replicate)) { + if (repp->width()==0) { // Keep lhs + nodep->replaceWith(nodep->lhsp()->unlinkFrBack()); + pushDeletep(nodep); VL_DANGLING(nodep); + return; + } + } + if (nodep->lhsp()->isString() + || nodep->rhsp()->isString()) { + AstNode* newp = new AstConcatN(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - return; - } - } - if (m_vup->final()) { - if (!nodep->dtypep()->widthSized()) { - // See also error in V3Number - nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in concatenations."); - } - } + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + return; + } + } + if (m_vup->final()) { + if (!nodep->dtypep()->widthSized()) { + // See also error in V3Number + nodep->v3warn(WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations."); + } + } } virtual void visit(AstConcatN* nodep) { - // String concatenate. - // Already did AstConcat simplifications - if (m_vup->prelim()) { - iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH); - iterateCheckString(nodep,"RHS",nodep->rhsp(),BOTH); - nodep->dtypeSetString(); - } - if (m_vup->final()) { - if (!nodep->dtypep()->widthSized()) { - // See also error in V3Number - nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in concatenations."); - } - } + // String concatenate. + // Already did AstConcat simplifications + if (m_vup->prelim()) { + iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); + iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH); + nodep->dtypeSetString(); + } + if (m_vup->final()) { + if (!nodep->dtypep()->widthSized()) { + // See also error in V3Number + nodep->v3warn(WIDTHCONCAT, "Unsized numbers/parameters not allowed in concatenations."); + } + } } virtual void visit(AstReplicate* nodep) { - // IEEE-2012 Table 11-21: - // LHS, RHS is self-determined - // width: value(LHS) * width(RHS) - if (m_vup->prelim()) { - iterateCheckSizedSelf(nodep,"LHS",nodep->lhsp(),SELF,BOTH); - iterateCheckSizedSelf(nodep,"RHS",nodep->rhsp(),SELF,BOTH); - V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change + // IEEE-2012 Table 11-21: + // LHS, RHS is self-determined + // width: value(LHS) * width(RHS) + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); + iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); + V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change const AstConst* constp = VN_CAST(nodep->rhsp(), Const); - if (!constp) { nodep->v3error("Replication value isn't a constant."); return; } - uint32_t times = constp->toUInt(); + if (!constp) { nodep->v3error("Replication value isn't a constant."); return; } + uint32_t times = constp->toUInt(); if (times==0 && !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up. - nodep->v3error("Replication value of 0 is only legal under a concatenation (IEEE 2017 11.4.12.1)"); times=1; - } - if (nodep->lhsp()->isString()) { - AstNode* newp = new AstReplicateN(nodep->fileline(),nodep->lhsp()->unlinkFrBack(), - nodep->rhsp()->unlinkFrBack()); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - return; - } else { - nodep->dtypeSetLogicSized((nodep->lhsp()->width() * times), - (nodep->lhsp()->widthMin() * times), - AstNumeric::UNSIGNED); - } - } - if (m_vup->final()) { - if (!nodep->dtypep()->widthSized()) { - // See also error in V3Number - nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in replications."); - } - } + nodep->v3error("Replication value of 0 is only legal under a concatenation (IEEE 2017 11.4.12.1)"); + times = 1; + } + if (nodep->lhsp()->isString()) { + AstNode* newp = new AstReplicateN(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), + nodep->rhsp()->unlinkFrBack()); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + return; + } else { + nodep->dtypeSetLogicSized((nodep->lhsp()->width() * times), + (nodep->lhsp()->widthMin() * times), + AstNumeric::UNSIGNED); + } + } + if (m_vup->final()) { + if (!nodep->dtypep()->widthSized()) { + // See also error in V3Number + nodep->v3warn(WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications."); + } + } } virtual void visit(AstReplicateN* nodep) { - // Replicate with string - if (m_vup->prelim()) { - iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH); - iterateCheckSizedSelf(nodep,"RHS",nodep->rhsp(),SELF,BOTH); - V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change + // Replicate with string + if (m_vup->prelim()) { + iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); + iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); + V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change const AstConst* constp = VN_CAST(nodep->rhsp(), Const); - if (!constp) { nodep->v3error("Replication value isn't a constant."); return; } - uint32_t times = constp->toUInt(); + if (!constp) { nodep->v3error("Replication value isn't a constant."); return; } + uint32_t times = constp->toUInt(); if (times==0 && !VN_IS(nodep->backp(), Concat)) { // Concat Visitor will clean it up. - nodep->v3error("Replication value of 0 is only legal under a concatenation (IEEE 2017 11.4.12.1)"); - } - nodep->dtypeSetString(); - } - if (m_vup->final()) { - if (!nodep->dtypep()->widthSized()) { - // See also error in V3Number - nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in replications."); - } - } + nodep->v3error("Replication value of 0 is only legal under a concatenation (IEEE 2017 11.4.12.1)"); + } + nodep->dtypeSetString(); + } + if (m_vup->final()) { + if (!nodep->dtypep()->widthSized()) { + // See also error in V3Number + nodep->v3warn(WIDTHCONCAT, "Unsized numbers/parameters not allowed in replications."); + } + } } virtual void visit(AstNodeStream* nodep) { - if (m_vup->prelim()) { - iterateCheckSizedSelf(nodep,"LHS",nodep->lhsp(),SELF,BOTH); - iterateCheckSizedSelf(nodep,"RHS",nodep->rhsp(),SELF,BOTH); - V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); + iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); + V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change const AstConst* constp = VN_CAST(nodep->rhsp(), Const); AstBasicDType* basicp = VN_CAST(nodep->rhsp(), BasicDType); - if (!constp && !basicp) { nodep->v3error("Slice size isn't a constant or basic data type."); return; } - if (basicp) { // Convert data type to a constant size - AstConst* newp = new AstConst(basicp->fileline(), basicp->width()); - nodep->rhsp()->replaceWith(newp); - pushDeletep(basicp); - } else { - uint32_t sliceSize = constp->toUInt(); - if (!sliceSize) { nodep->v3error("Slice size cannot be zero."); return; } - } - nodep->dtypeSetLogicSized((nodep->lhsp()->width()), - (nodep->lhsp()->widthMin()), - AstNumeric::UNSIGNED); - } - if (m_vup->final()) { - if (!nodep->dtypep()->widthSized()) { - // See also error in V3Number - nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in streams."); - } - } + if (!constp && !basicp) { nodep->v3error("Slice size isn't a constant or basic data type."); return; } + if (basicp) { // Convert data type to a constant size + AstConst* newp = new AstConst(basicp->fileline(), basicp->width()); + nodep->rhsp()->replaceWith(newp); + pushDeletep(basicp); + } else { + uint32_t sliceSize = constp->toUInt(); + if (!sliceSize) { nodep->v3error("Slice size cannot be zero."); return; } + } + nodep->dtypeSetLogicSized((nodep->lhsp()->width()), + (nodep->lhsp()->widthMin()), + AstNumeric::UNSIGNED); + } + if (m_vup->final()) { + if (!nodep->dtypep()->widthSized()) { + // See also error in V3Number + nodep->v3warn(WIDTHCONCAT, "Unsized numbers/parameters not allowed in streams."); + } + } } virtual void visit(AstRange* nodep) { - // Real: Not allowed - // Signed: unsigned output, input either - // Convert all range values to constants - UINFO(6,"RANGE "<msbp()); // May relink pointed to node - V3Const::constifyParamsEdit(nodep->lsbp()); // May relink pointed to node - checkConstantOrReplace(nodep->msbp(), "MSB of bit range isn't a constant"); - checkConstantOrReplace(nodep->lsbp(), "LSB of bit range isn't a constant"); - int msb = nodep->msbConst(); - int lsb = nodep->lsbConst(); - if (msblittleEndian(!nodep->littleEndian()); - // Internally we'll always have msb() be the greater number - // We only need to correct when doing [] AstSel extraction, - // and when tracing the vector. - nodep->msbp()->swapWith(nodep->lsbp()); - } - if (m_vup->prelim()) { - // Don't need to iterate because V3Const already constified - int width = nodep->elementsConst(); - if (width > (1<<28)) nodep->v3error("Width of bit range is huge; vector of over 1billion bits: 0x"<msbp()); // May relink pointed to node + V3Const::constifyParamsEdit(nodep->lsbp()); // May relink pointed to node + checkConstantOrReplace(nodep->msbp(), "MSB of bit range isn't a constant"); + checkConstantOrReplace(nodep->lsbp(), "LSB of bit range isn't a constant"); + int msb = nodep->msbConst(); + int lsb = nodep->lsbConst(); + if (msblittleEndian(!nodep->littleEndian()); + // Internally we'll always have msb() be the greater number + // We only need to correct when doing [] AstSel extraction, + // and when tracing the vector. + nodep->msbp()->swapWith(nodep->lsbp()); + } + if (m_vup->prelim()) { + // Don't need to iterate because V3Const already constified + int width = nodep->elementsConst(); + if (width > (1<<28)) nodep->v3error("Width of bit range is huge; vector of over 1billion bits: 0x" + <littleEndian() && !VN_IS(nodep->backp(), UnpackArrayDType) && !VN_IS(nodep->backp(), Cell)) { // For cells we warn in V3Inst - nodep->v3warn(LITENDIAN,"Little bit endian vector: MSB < LSB of bit range: "<lsbConst()<<":"<msbConst()); - } - } + nodep->v3warn(LITENDIAN, "Little bit endian vector: MSB < LSB of bit range: " + <lsbConst()<<":"<msbConst()); + } + } } virtual void visit(AstSel* nodep) { - // Signed: always unsigned; Real: Not allowed - // LSB is self-determined (IEEE 2012 11.5.1) - // We also use SELs to shorten a signed constant etc, in this case they are signed. - if (nodep->didWidth()) return; + // Signed: always unsigned; Real: Not allowed + // LSB is self-determined (IEEE 2012 11.5.1) + // We also use SELs to shorten a signed constant etc, in this case they are signed. + if (nodep->didWidth()) return; if (!m_vup) nodep->v3fatalSrc("Select under an unexpected context"); - if (m_vup->prelim()) { - if (debug()>=9) nodep->dumpTree(cout,"-selWidth: "); - userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT,PRELIM).p()); - userIterateAndNext(nodep->lsbp(), WidthVP(SELF,PRELIM).p()); - checkCvtUS(nodep->fromp()); - iterateCheckSizedSelf(nodep,"Select Width",nodep->widthp(),SELF,BOTH); - iterateCheckSizedSelf(nodep,"Select LHS",nodep->lhsp(),SELF,BOTH); - V3Const::constifyParamsEdit(nodep->widthp()); // widthp may change + if (m_vup->prelim()) { + if (debug()>=9) nodep->dumpTree(cout, "-selWidth: "); + userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p()); + userIterateAndNext(nodep->lsbp(), WidthVP(SELF, PRELIM).p()); + checkCvtUS(nodep->fromp()); + iterateCheckSizedSelf(nodep, "Select Width", nodep->widthp(), SELF, BOTH); + iterateCheckSizedSelf(nodep, "Select LHS", nodep->lhsp(), SELF, BOTH); + V3Const::constifyParamsEdit(nodep->widthp()); // widthp may change AstConst* widthConstp = VN_CAST(nodep->widthp(), Const); - if (!widthConstp) { - nodep->v3error("Width of bit extract isn't a constant"); - nodep->dtypeSetLogicBool(); return; - } - int width = nodep->widthConst(); - if (!nodep->dtypep()) nodep->v3fatalSrc("dtype wasn't set") // by V3WidthSel + if (!widthConstp) { + nodep->v3error("Width of bit extract isn't a constant"); + nodep->dtypeSetLogicBool(); return; + } + int width = nodep->widthConst(); + if (!nodep->dtypep()) nodep->v3fatalSrc("dtype wasn't set") // by V3WidthSel if (VN_IS(nodep->lsbp(), Const) - && nodep->msbConst() < nodep->lsbConst()) { - nodep->v3error("Unsupported: MSB < LSB of bit extract: " - <msbConst()<<"<"<lsbConst()); - width = (nodep->lsbConst() - nodep->msbConst() + 1); - nodep->dtypeSetLogicSized(width,width,AstNumeric::UNSIGNED); - nodep->widthp()->replaceWith(new AstConst(nodep->widthp()->fileline(), - width)); - nodep->lsbp()->replaceWith(new AstConst(nodep->lsbp()->fileline(), 0)); - } - // We're extracting, so just make sure the expression is at least wide enough. - if (nodep->fromp()->width() < width) { - nodep->v3error("Extracting "<fromp()->width()<<" bit number"); - // Extend it. - AstNodeDType* subDTypep = nodep->findLogicDType(width,width,nodep->fromp()->dtypep()->numeric()); - widthCheckSized(nodep,"errorless...",nodep->fromp(),subDTypep,EXTEND_EXP, false/*noerror*/); - } - // Check bit indexes. - // What is the MSB? We want the true MSB, not one starting at 0, - // because a 4 bit index is required to look at a one-bit variable[15:15] and 5 bits for [15:-2] - int frommsb = nodep->fromp()->width() - 1; - int fromlsb = 0; - int elw = nodep->declElWidth(); // Must adjust to tell user bit ranges - if (nodep->declRange().ranged()) { - frommsb = nodep->declRange().hiMaxSelect()*elw + (elw-1); // Corrected for negative lsb - fromlsb = nodep->declRange().lo()*elw; - } else { - //nodep->v3fatalSrc("Should have been declRanged in V3WidthSel"); - } - int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit - AstNodeDType* selwidthDTypep = nodep->findLogicDType(selwidth,selwidth,nodep->lsbp()->dtypep()->numeric()); - userIterateAndNext(nodep->fromp(), WidthVP(SELF,FINAL).p()); - userIterateAndNext(nodep->lsbp(), WidthVP(SELF,FINAL).p()); - if (widthBad(nodep->lsbp(),selwidthDTypep) - && nodep->lsbp()->width()!=32) { - if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) { - nodep->v3warn(WIDTH,"Bit extraction of var["<<(frommsb/elw)<<":"<<(fromlsb/elw)<<"] requires " - <<(selwidth/elw)<<" bit index, not " - <<(nodep->lsbp()->width()/elw) - <<(nodep->lsbp()->width()!=nodep->lsbp()->widthMin() - ?" or "+cvtToStr(nodep->lsbp()->widthMin()/elw):"") - <<" bits."); - UINFO(1," Related node: "<msbConst() < nodep->lsbConst()) { + nodep->v3error("Unsupported: MSB < LSB of bit extract: " + <msbConst()<<"<"<lsbConst()); + width = (nodep->lsbConst() - nodep->msbConst() + 1); + nodep->dtypeSetLogicSized(width, width, AstNumeric::UNSIGNED); + nodep->widthp()->replaceWith(new AstConst(nodep->widthp()->fileline(), + width)); + nodep->lsbp()->replaceWith(new AstConst(nodep->lsbp()->fileline(), 0)); + } + // We're extracting, so just make sure the expression is at least wide enough. + if (nodep->fromp()->width() < width) { + nodep->v3error("Extracting "<fromp()->width()<<" bit number"); + // Extend it. + AstNodeDType* subDTypep + = nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric()); + widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP, false/*noerror*/); + } + // Check bit indexes. + // What is the MSB? We want the true MSB, not one starting at + // 0, because a 4 bit index is required to look at a one-bit + // variable[15:15] and 5 bits for [15:-2] + int frommsb = nodep->fromp()->width() - 1; + int fromlsb = 0; + int elw = nodep->declElWidth(); // Must adjust to tell user bit ranges + if (nodep->declRange().ranged()) { + frommsb = nodep->declRange().hiMaxSelect()*elw + (elw-1); // Corrected for negative lsb + fromlsb = nodep->declRange().lo()*elw; + } else { + //nodep->v3fatalSrc("Should have been declRanged in V3WidthSel"); + } + int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit + AstNodeDType* selwidthDTypep + = nodep->findLogicDType(selwidth, selwidth, nodep->lsbp()->dtypep()->numeric()); + userIterateAndNext(nodep->fromp(), WidthVP(SELF, FINAL).p()); + userIterateAndNext(nodep->lsbp(), WidthVP(SELF, FINAL).p()); + if (widthBad(nodep->lsbp(), selwidthDTypep) + && nodep->lsbp()->width()!=32) { + if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) { + nodep->v3warn(WIDTH, "Bit extraction of var[" + <<(frommsb/elw)<<":"<<(fromlsb/elw)<<"] requires " + <<(selwidth/elw)<<" bit index, not " + <<(nodep->lsbp()->width()/elw) + <<(nodep->lsbp()->width()!=nodep->lsbp()->widthMin() + ?" or "+cvtToStr(nodep->lsbp()->widthMin()/elw):"") + <<" bits."); + UINFO(1," Related node: "<lsbp(), Const) && nodep->msbConst() > frommsb) { - // See also warning in V3Const - // We need to check here, because the widthCheckSized may silently - // add another SEL which will lose the out-of-range check - // - // We don't want to trigger an error here if we are just - // evaluating type sizes for a generate block condition. We - // should only trigger the error if the out-of-range access is - // actually generated. - if (m_doGenerate) { - UINFO(5, "Selection index out of range inside generate."<v3warn(SELRANGE,"Selection index out of range: " - <msbConst()<<":"<lsbConst() - <<" outside "<lsbp(),selwidthDTypep,EXTEND_EXP, false/*NOWARN*/); - } - } + // See also warning in V3Const + // We need to check here, because the widthCheckSized may silently + // add another SEL which will lose the out-of-range check + // + // We don't want to trigger an error here if we are just + // evaluating type sizes for a generate block condition. We + // should only trigger the error if the out-of-range access is + // actually generated. + if (m_doGenerate) { + UINFO(5, "Selection index out of range inside generate."<v3warn(SELRANGE, "Selection index out of range: " + <msbConst()<<":"<lsbConst() + <<" outside "<lsbp(), selwidthDTypep, EXTEND_EXP, false/*NOWARN*/); + } + } } virtual void visit(AstArraySel* nodep) { - // Signed/Real: Output signed iff LHS signed/real; binary operator - // Note by contrast, bit extract selects are unsigned - // LSB is self-determined (IEEE 2012 11.5.1) - if (m_vup->prelim()) { - iterateCheckSizedSelf(nodep,"Bit select",nodep->bitp(),SELF,BOTH); - userIterateAndNext(nodep->fromp(), WidthVP(SELF,BOTH).p()); - // - int frommsb; - int fromlsb; - AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp(); + // Signed/Real: Output signed iff LHS signed/real; binary operator + // Note by contrast, bit extract selects are unsigned + // LSB is self-determined (IEEE 2012 11.5.1) + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "Bit select", nodep->bitp(), SELF, BOTH); + userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p()); + // + int frommsb; + int fromlsb; + AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp(); if (const AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { - frommsb = adtypep->msb(); - fromlsb = adtypep->lsb(); - if (fromlsb>frommsb) {int t=frommsb; frommsb=fromlsb; fromlsb=t; } - // However, if the lsb<0 we may go negative, so need more bits! - if (fromlsb < 0) frommsb += -fromlsb; - nodep->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference - } - else { - // Note PackArrayDType doesn't use an ArraySel but a normal Sel. - UINFO(1," Related dtype: "<v3fatalSrc("Array reference exceeds dimension of array"); - frommsb = fromlsb = 0; - } - int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit - AstNodeDType* selwidthDTypep = nodep->findLogicDType(selwidth,selwidth,nodep->bitp()->dtypep()->numeric()); - if (widthBad(nodep->bitp(),selwidthDTypep) - && nodep->bitp()->width()!=32) { - nodep->v3warn(WIDTH,"Bit extraction of array["<bitp()->width() - <<(nodep->bitp()->width()!=nodep->bitp()->widthMin() - ?" or "+cvtToStr(nodep->bitp()->widthMin()):"") - <<" bits."); - if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) { - UINFO(1," Related node: "<dtypep()<msb(); + fromlsb = adtypep->lsb(); + if (fromlsb>frommsb) {int t = frommsb; frommsb = fromlsb; fromlsb = t; } + // However, if the lsb<0 we may go negative, so need more bits! + if (fromlsb < 0) frommsb += -fromlsb; + nodep->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference + } + else { + // Note PackArrayDType doesn't use an ArraySel but a normal Sel. + UINFO(1," Related dtype: "<v3fatalSrc("Array reference exceeds dimension of array"); + frommsb = fromlsb = 0; + } + int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit + AstNodeDType* selwidthDTypep + = nodep->findLogicDType(selwidth, selwidth, nodep->bitp()->dtypep()->numeric()); + if (widthBad(nodep->bitp(), selwidthDTypep) + && nodep->bitp()->width()!=32) { + nodep->v3warn(WIDTH, "Bit extraction of array[" + <bitp()->width() + <<(nodep->bitp()->width()!=nodep->bitp()->widthMin() + ?" or "+cvtToStr(nodep->bitp()->widthMin()):"") + <<" bits."); + if (!nodep->fileline()->warnIsOff(V3ErrorCode::WIDTH)) { + UINFO(1," Related node: "<dtypep()<bitp(), Const) && (VN_CAST(nodep->bitp(), Const)->toSInt() > (frommsb-fromlsb) || VN_CAST(nodep->bitp(), Const)->toSInt() < 0)) { - nodep->v3warn(SELRANGE,"Selection index out of range: " + nodep->v3warn(SELRANGE, "Selection index out of range: " <<(VN_CAST(nodep->bitp(), Const)->toSInt()+fromlsb) - <<" outside "<bitp(),selwidthDTypep,EXTEND_EXP, false/*NOWARN*/); - } - } + <<" outside "<bitp(), selwidthDTypep, EXTEND_EXP, false/*NOWARN*/); + } + } } virtual void visit(AstSliceSel* nodep) { // Always creates as output an unpacked array if (m_vup->prelim()) { - userIterateAndNext(nodep->fromp(), WidthVP(SELF,BOTH).p()); + userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p()); // // Array indices are always constant AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp(); @@ -734,70 +749,76 @@ private: nodep->v3error("Slice selection index '"<< nodep->declRange() << "'" <<" outside data type's '"<< adtypep->declRange() << "'"); } - else if ((nodep->declRange().littleEndian() != adtypep->declRange().littleEndian())) { + else if ((nodep->declRange().littleEndian() + != adtypep->declRange().littleEndian())) { nodep->v3error("Slice selection '"<< nodep->declRange() << "'" - <<" has backward indexing versus data type's '"<< adtypep->declRange() << "'"); + <<" has backward indexing versus data type's '" + <declRange() << "'"); } } } } virtual void visit(AstSelBit* nodep) { - // Just a quick check as after V3Param these nodes instead are AstSel's - userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->attrp(), WidthVP(SELF,BOTH).p()); - AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; userIterate(selp, m_vup); return; } - nodep->v3fatalSrc("AstSelBit should disappear after widthSel"); + // Just a quick check as after V3Param these nodes instead are AstSel's + userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p()); + AstNode* selp = V3Width::widthSelNoIterEdit(nodep); + if (selp!=nodep) { nodep = NULL; userIterate(selp, m_vup); return; } + nodep->v3fatalSrc("AstSelBit should disappear after widthSel"); } virtual void visit(AstSelExtract* nodep) { - // Just a quick check as after V3Param these nodes instead are AstSel's - userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->attrp(), WidthVP(SELF,BOTH).p()); - AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; userIterate(selp, m_vup); return; } - nodep->v3fatalSrc("AstSelExtract should disappear after widthSel"); + // Just a quick check as after V3Param these nodes instead are AstSel's + userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p()); + AstNode* selp = V3Width::widthSelNoIterEdit(nodep); + if (selp!=nodep) { nodep = NULL; userIterate(selp, m_vup); return; } + nodep->v3fatalSrc("AstSelExtract should disappear after widthSel"); } virtual void visit(AstSelPlus* nodep) { - userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->attrp(), WidthVP(SELF,BOTH).p()); - AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; userIterate(selp, m_vup); return; } - nodep->v3fatalSrc("AstSelPlus should disappear after widthSel"); + userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p()); + AstNode* selp = V3Width::widthSelNoIterEdit(nodep); + if (selp!=nodep) { nodep = NULL; userIterate(selp, m_vup); return; } + nodep->v3fatalSrc("AstSelPlus should disappear after widthSel"); } virtual void visit(AstSelMinus* nodep) { - userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel - userIterateAndNext(nodep->attrp(), WidthVP(SELF,BOTH).p()); - AstNode* selp = V3Width::widthSelNoIterEdit(nodep); if (selp!=nodep) { nodep=NULL; userIterate(selp, m_vup); return; } - nodep->v3fatalSrc("AstSelMinus should disappear after widthSel"); + userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->thsp(), WidthVP(CONTEXT, PRELIM).p()); // FINAL in AstSel + userIterateAndNext(nodep->attrp(), WidthVP(SELF, BOTH).p()); + AstNode* selp = V3Width::widthSelNoIterEdit(nodep); + if (selp!=nodep) { nodep = NULL; userIterate(selp, m_vup); return; } + nodep->v3fatalSrc("AstSelMinus should disappear after widthSel"); } virtual void visit(AstExtend* nodep) { - // Only created by this process, so we know width from here down is correct. + // Only created by this process, so we know width from here down is correct. } virtual void visit(AstExtendS* nodep) { - // Only created by this process, so we know width from here down is correct. + // Only created by this process, so we know width from here down is correct. } virtual void visit(AstConst* nodep) { - // The node got setup with the signed/real state of the node. - // However a later operation may have changed the node->signed w/o changing - // the number's sign. So we don't: nodep->dtypeChgSigned(nodep->num().isSigned()); - if (m_vup && m_vup->prelim()) { - if (nodep->num().isString()) { - nodep->dtypeSetString(); - } else if (nodep->num().sized()) { - nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width()); - } else { - nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthMin()); - } - } - // We don't size the constant until we commit the widths, as need parameters - // to remain unsized, and numbers to remain unsized to avoid backp() warnings + // The node got setup with the signed/real state of the node. + // However a later operation may have changed the node->signed w/o changing + // the number's sign. So we don't: nodep->dtypeChgSigned(nodep->num().isSigned()); + if (m_vup && m_vup->prelim()) { + if (nodep->num().isString()) { + nodep->dtypeSetString(); + } else if (nodep->num().sized()) { + nodep->dtypeChgWidth(nodep->num().width(), nodep->num().width()); + } else { + nodep->dtypeChgWidth(nodep->num().width(), nodep->num().widthMin()); + } + } + // We don't size the constant until we commit the widths, as need parameters + // to remain unsized, and numbers to remain unsized to avoid backp() warnings } virtual void visit(AstPast* nodep) { if (m_vup->prelim()) { @@ -820,181 +841,187 @@ private: } } virtual void visit(AstRand* nodep) { - if (m_vup->prelim()) { - nodep->dtypeSetSigned32(); // Says the spec - } + if (m_vup->prelim()) { + nodep->dtypeSetSigned32(); // Says the spec + } } virtual void visit(AstUCFunc* nodep) { - // Give it the size the user wants. - if (m_vup && m_vup->prelim()) { - nodep->dtypeSetLogicSized(32,1,AstNumeric::UNSIGNED); // We don't care - // All arguments seek their natural sizes - userIterateChildren(nodep, WidthVP(SELF,BOTH).p()); - } - if (m_vup->final()) { - AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); - nodep->dtypeFrom(expDTypep); // Assume user knows the rules; go with the flow - if (nodep->width()>64) nodep->v3error("Unsupported: $c can't generate wider than 64 bits"); - } + // Give it the size the user wants. + if (m_vup && m_vup->prelim()) { + nodep->dtypeSetLogicSized(32, 1, AstNumeric::UNSIGNED); // We don't care + // All arguments seek their natural sizes + userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); + } + if (m_vup->final()) { + AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); + nodep->dtypeFrom(expDTypep); // Assume user knows the rules; go with the flow + if (nodep->width()>64) nodep->v3error("Unsupported: $c can't generate wider than 64 bits"); + } } virtual void visit(AstCLog2* nodep) { - if (m_vup->prelim()) { - iterateCheckSizedSelf(nodep,"LHS",nodep->lhsp(),SELF,BOTH); - nodep->dtypeSetSigned32(); - } + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); + nodep->dtypeSetSigned32(); + } } virtual void visit(AstPow* nodep) { - // Pow is special, output sign only depends on LHS sign, but function result depends on both signs - // RHS is self-determined (IEEE) - // Real if either side is real (as with AstAdd) - if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT,PRELIM).p()); - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); - if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { - spliceCvtD(nodep->lhsp()); - spliceCvtD(nodep->rhsp()); - replaceWithDVersion(nodep); VL_DANGLING(nodep); - return; - } + // Pow is special, output sign only depends on LHS sign, but + // function result depends on both signs + // RHS is self-determined (IEEE) + // Real if either side is real (as with AstAdd) + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p()); + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); + if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { + spliceCvtD(nodep->lhsp()); + spliceCvtD(nodep->rhsp()); + replaceWithDVersion(nodep); VL_DANGLING(nodep); + return; + } - checkCvtUS(nodep->lhsp()); - iterateCheckSizedSelf(nodep,"RHS",nodep->rhsp(),SELF,BOTH); - nodep->dtypeFrom(nodep->lhsp()); - } + checkCvtUS(nodep->lhsp()); + iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); + nodep->dtypeFrom(nodep->lhsp()); + } - if (m_vup->final()) { - AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); - nodep->dtypeFrom(expDTypep); - // rhs already finalized in iterate_shift_prelim - iterateCheck(nodep,"LHS",nodep->lhsp(),SELF,FINAL,nodep->dtypep(),EXTEND_EXP); - AstNode* newp = NULL; // No change - if (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) { + if (m_vup->final()) { + AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); + nodep->dtypeFrom(expDTypep); + // rhs already finalized in iterate_shift_prelim + iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, nodep->dtypep(), EXTEND_EXP); + AstNode* newp = NULL; // No change + if (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) { newp = new AstPowSS(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()); - } else if (nodep->lhsp()->isSigned() && !nodep->rhsp()->isSigned()) { + } else if (nodep->lhsp()->isSigned() && !nodep->rhsp()->isSigned()) { newp = new AstPowSU(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()); - } else if (!nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) { + } else if (!nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) { newp = new AstPowUS(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()); - } - if (newp) { - newp->dtypeFrom(nodep); - UINFO(9,"powOld "<replaceWith(newp); VL_DANGLING(nodep); - } - } + } + if (newp) { + newp->dtypeFrom(nodep); + UINFO(9,"powOld "<replaceWith(newp); VL_DANGLING(nodep); + } + } } virtual void visit(AstPowSU* nodep) { - // POWSU/SS/US only created here, dtype already determined, so nothing to do in this function - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->rhsp(), WidthVP(SELF,BOTH).p()); + // POWSU/SS/US only created here, dtype already determined, so + // nothing to do in this function + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstPowSS* nodep) { - // POWSU/SS/US only created here, dtype already determined, so nothing to do in this function - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->rhsp(), WidthVP(SELF,BOTH).p()); + // POWSU/SS/US only created here, dtype already determined, so + // nothing to do in this function + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstPowUS* nodep) { - // POWSU/SS/US only created here, dtype already determined, so nothing to do in this function - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->rhsp(), WidthVP(SELF,BOTH).p()); + // POWSU/SS/US only created here, dtype already determined, so + // nothing to do in this function + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstCountOnes* nodep) { - if (m_vup->prelim()) { - iterateCheckSizedSelf(nodep,"LHS",nodep->lhsp(),SELF,BOTH); - // If it's a 32 bit number, we need a 6 bit number as we need to return '32'. - int selwidth = V3Number::log2b(nodep->lhsp()->width())+1; - nodep->dtypeSetLogicSized(selwidth,selwidth,AstNumeric::UNSIGNED); // Spec doesn't indicate if an integer - } + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); + // If it's a 32 bit number, we need a 6 bit number as we need to return '32'. + int selwidth = V3Number::log2b(nodep->lhsp()->width())+1; + nodep->dtypeSetLogicSized(selwidth, selwidth, AstNumeric::UNSIGNED); // Spec doesn't indicate if an integer + } } virtual void visit(AstCvtPackString* nodep) { - // Opaque returns, so arbitrary - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - // Type set in constructor + // Opaque returns, so arbitrary + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + // Type set in constructor } virtual void visit(AstAttrOf* nodep) { - AstAttrOf* oldAttr = m_attrp; - m_attrp = nodep; - userIterateAndNext(nodep->fromp(), WidthVP(SELF,BOTH).p()); - // Don't iterate children, don't want to lose VarRef. - switch (nodep->attrType()) { - case AstAttrType::VAR_BASE: - case AstAttrType::MEMBER_BASE: - case AstAttrType::ENUM_BASE: - // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf - break; - case AstAttrType::DIM_DIMENSIONS: - case AstAttrType::DIM_UNPK_DIMENSIONS: { - if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); + AstAttrOf* oldAttr = m_attrp; + m_attrp = nodep; + userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p()); + // Don't iterate children, don't want to lose VarRef. + switch (nodep->attrType()) { + case AstAttrType::VAR_BASE: + case AstAttrType::MEMBER_BASE: + case AstAttrType::ENUM_BASE: + // Soon to be handled in V3LinkWidth SEL generation, under attrp() and newSubLsbOf + break; + case AstAttrType::DIM_DIMENSIONS: + case AstAttrType::DIM_UNPK_DIMENSIONS: { + if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); std::pair dim = nodep->fromp()->dtypep()->dimensions(true); - int val = (nodep->attrType()==AstAttrType::DIM_UNPK_DIMENSIONS - ? dim.second : (dim.first+dim.second)); - nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Signed32(), val)); nodep->deleteTree(); VL_DANGLING(nodep); - break; - } - case AstAttrType::DIM_BITS: - case AstAttrType::DIM_HIGH: - case AstAttrType::DIM_INCREMENT: - case AstAttrType::DIM_LEFT: - case AstAttrType::DIM_LOW: - case AstAttrType::DIM_RIGHT: - case AstAttrType::DIM_SIZE: { - if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); - std::pair dim = nodep->fromp()->dtypep()->skipRefp()->dimensions(true); - uint32_t msbdim = dim.first+dim.second; + int val = (nodep->attrType()==AstAttrType::DIM_UNPK_DIMENSIONS + ? dim.second : (dim.first+dim.second)); + nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Signed32(), val)); + nodep->deleteTree(); VL_DANGLING(nodep); + break; + } + case AstAttrType::DIM_BITS: + case AstAttrType::DIM_HIGH: + case AstAttrType::DIM_INCREMENT: + case AstAttrType::DIM_LEFT: + case AstAttrType::DIM_LOW: + case AstAttrType::DIM_RIGHT: + case AstAttrType::DIM_SIZE: { + if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); + std::pair dim + = nodep->fromp()->dtypep()->skipRefp()->dimensions(true); + uint32_t msbdim = dim.first+dim.second; if (!nodep->dimp() || VN_IS(nodep->dimp(), Const) || msbdim<1) { int dim = !nodep->dimp() ? 1 : VN_CAST(nodep->dimp(), Const)->toSInt(); - AstConst* newp = dimensionValue(nodep->fromp()->dtypep(), nodep->attrType(), dim); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - } - else { // Need a runtime lookup table. Yuk. - if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); - AstVar* varp = dimensionVarp(nodep->fromp()->dtypep(), nodep->attrType(), msbdim); - AstNode* dimp = nodep->dimp()->unlinkFrBack(); - AstVarRef* varrefp = new AstVarRef(nodep->fileline(), varp, false); - varrefp->packagep(v3Global.rootp()->dollarUnitPkgAddp()); - AstNode* newp = new AstArraySel(nodep->fileline(), varrefp, dimp); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - } - break; - } - default: { - // Everything else resolved earlier - nodep->dtypeSetLogicSized(32,1,AstNumeric::UNSIGNED); // Approximation, unsized 32 - UINFO(1,"Missing ATTR type case node: "<v3fatalSrc("Missing ATTR type case"); - break; - } - } - m_attrp = oldAttr; + AstConst* newp = dimensionValue(nodep->fromp()->dtypep(), nodep->attrType(), dim); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + } + else { // Need a runtime lookup table. Yuk. + if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); + AstVar* varp = dimensionVarp(nodep->fromp()->dtypep(), nodep->attrType(), msbdim); + AstNode* dimp = nodep->dimp()->unlinkFrBack(); + AstVarRef* varrefp = new AstVarRef(nodep->fileline(), varp, false); + varrefp->packagep(v3Global.rootp()->dollarUnitPkgAddp()); + AstNode* newp = new AstArraySel(nodep->fileline(), varrefp, dimp); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + } + break; + } + default: { + // Everything else resolved earlier + nodep->dtypeSetLogicSized(32, 1, AstNumeric::UNSIGNED); // Approximation, unsized 32 + UINFO(1,"Missing ATTR type case node: "<v3fatalSrc("Missing ATTR type case"); + break; + } + } + m_attrp = oldAttr; } virtual void visit(AstPull* nodep) { // May have select underneath, let seek natural size - userIterateChildren(nodep, WidthVP(SELF,BOTH).p()); + userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); } virtual void visit(AstText* nodep) { - // Only used in CStmts which don't care.... + // Only used in CStmts which don't care.... } // DTYPES virtual void visit(AstNodeArrayDType* nodep) { - if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); - // Iterate into subDTypep() to resolve that type and update pointer. - nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); - // Cleanup array size - userIterateAndNext(nodep->rangep(), WidthVP(SELF,BOTH).p()); - nodep->dtypep(nodep); // The array itself, not subDtype + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + // Cleanup array size + userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p()); + nodep->dtypep(nodep); // The array itself, not subDtype if (VN_IS(nodep, UnpackArrayDType)) { - // Historically array elements have width of the ref type not the full array - nodep->widthFromSub(nodep->subDTypep()); - } else { - int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst(); - nodep->widthForce(width,width); - } - UINFO(4,"dtWidthed "<widthFromSub(nodep->subDTypep()); + } else { + int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst(); + nodep->widthForce(width, width); + } + UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed @@ -1006,41 +1033,42 @@ private: UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - if (nodep->generic()) return; // Already perfect - if (nodep->rangep()) { - userIterateAndNext(nodep->rangep(), WidthVP(SELF,BOTH).p()); - // Because this DType has a unique child range, we know it's not pointed at by - // other nodes unless they are referencing this type. Furthermore the width() - // calculation would return identical values. Therefore we can directly replace the width - nodep->widthForce(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst()); - } - else if (nodep->isRanged()) { - nodep->widthForce(nodep->nrange().elements(), nodep->nrange().elements()); - } - else if (nodep->implicit()) { - // Parameters may notice implicitness and change to different dtype - nodep->widthForce(1,1); - } - // else width in node is correct; it was set based on keyword().width() - // at construction time. Ditto signed, so "unsigned byte" etc works right. - nodep->cvtRangeConst(); - // TODO: If BasicDType now looks like a generic type, we can convert to a real generic dtype - // Instead for now doing this in V3WidthCommit - UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->generic()) return; // Already perfect + if (nodep->rangep()) { + userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p()); + // Because this DType has a unique child range, we know it's not + // pointed at by other nodes unless they are referencing this type. + // Furthermore the width() calculation would return identical + // values. Therefore we can directly replace the width + nodep->widthForce(nodep->rangep()->elementsConst(), nodep->rangep()->elementsConst()); + } + else if (nodep->isRanged()) { + nodep->widthForce(nodep->nrange().elements(), nodep->nrange().elements()); + } + else if (nodep->implicit()) { + // Parameters may notice implicitness and change to different dtype + nodep->widthForce(1, 1); + } + // else width in node is correct; it was set based on keyword().width() + // at construction time. Ditto signed, so "unsigned byte" etc works right. + nodep->cvtRangeConst(); + // TODO: If BasicDType now looks like a generic type, we can convert to a real generic dtype + // Instead for now doing this in V3WidthCommit + UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - // Move any childDTypep to instead be in global AstTypeTable. - // This way if this node gets deleted and some other dtype points to it - // it won't become a dead pointer. This doesn't change the object pointed to. - if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); - // Iterate into subDTypep() to resolve that type and update pointer. - nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); - userIterateChildren(nodep, NULL); - nodep->dtypep(nodep); // Should already be set, but be clear it's not the subDType - nodep->widthFromSub(nodep->subDTypep()); - UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + // Move any childDTypep to instead be in global AstTypeTable. + // This way if this node gets deleted and some other dtype points to it + // it won't become a dead pointer. This doesn't change the object pointed to. + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + userIterateChildren(nodep, NULL); + nodep->dtypep(nodep); // Should already be set, but be clear it's not the subDType + nodep->widthFromSub(nodep->subDTypep()); + UINFO(4,"dtWidthed "<doingWidth()) { // Early exit if have circular parameter definition @@ -1063,141 +1091,143 @@ private: nodep->doingWidth(false); } virtual void visit(AstTypedef* nodep) { - if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); - userIterateChildren(nodep, NULL); - nodep->dtypep(iterateEditDTypep(nodep, nodep->subDTypep())); + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); + userIterateChildren(nodep, NULL); + nodep->dtypep(iterateEditDTypep(nodep, nodep->subDTypep())); } virtual void visit(AstParamTypeDType* nodep) { - if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); - userIterateChildren(nodep, NULL); - nodep->dtypep(iterateEditDTypep(nodep, nodep->subDTypep())); - nodep->widthFromSub(nodep->subDTypep()); + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); + userIterateChildren(nodep, NULL); + nodep->dtypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->widthFromSub(nodep->subDTypep()); } virtual void visit(AstCastParse* nodep) { - // nodep->dtp could be data type, or a primary_constant - // Don't iterate lhsp, will deal with that once convert the type - V3Const::constifyParamsEdit(nodep->dtp()); // itemp may change + // nodep->dtp could be data type, or a primary_constant + // Don't iterate lhsp, will deal with that once convert the type + V3Const::constifyParamsEdit(nodep->dtp()); // itemp may change if (AstConst* constp = VN_CAST(nodep->dtp(), Const)) { - constp->unlinkFrBack(); - AstNode* newp = new AstCastSize(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), constp); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - userIterate(newp, m_vup); - } else { - nodep->v3error("Unsupported: Cast to "<dtp()->prettyTypeName()); - nodep->replaceWith(nodep->lhsp()->unlinkFrBack()); - } + constp->unlinkFrBack(); + AstNode* newp = new AstCastSize(nodep->fileline(), + nodep->lhsp()->unlinkFrBack(), constp); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + userIterate(newp, m_vup); + } else { + nodep->v3error("Unsupported: Cast to "<dtp()->prettyTypeName()); + nodep->replaceWith(nodep->lhsp()->unlinkFrBack()); + } } virtual void visit(AstCast* nodep) { - if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); - nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep())); - //if (debug()) nodep->dumpTree(cout," CastPre: "); - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - // When more general casts are supported, the cast elimination will be done later. - // For now, replace it ASAP, so widthing can propagate easily - // The cast may change signing, but we don't know the sign yet. Make it so. - // Note we don't sign lhsp() that would make the algorithm O(n^2) if lots of casting. - AstBasicDType* basicp = nodep->dtypep()->basicp(); - if (!basicp) nodep->v3fatalSrc("Unimplemented: Casting non-simple data type"); - // When implement more complicated types need to convert childDTypep to dtypep() not as a child - if (!basicp->isDouble() && !nodep->lhsp()->isDouble()) { - // Note widthCheckSized might modify nodep->lhsp() - AstNodeDType* subDTypep = nodep->findLogicDType(nodep->width(),nodep->width(), - nodep->lhsp()->dtypep()->numeric()); - widthCheckSized(nodep,"Cast",nodep->lhsp(),subDTypep,EXTEND_EXP, false); - } - AstNode* newp = nodep->lhsp()->unlinkFrBack(); - if (basicp->isDouble() && !newp->isDouble()) { - newp = new AstIToRD(nodep->fileline(), newp); - } else if (!basicp->isDouble() && newp->isDouble()) { - if (basicp->isSigned()) { - newp = new AstRToIRoundS(nodep->fileline(), newp); - } else { - newp = new AstUnsigned(nodep->fileline(), - new AstRToIS(nodep->fileline(), newp)); - } - } else if (basicp->isSigned() && !newp->isSigned()) { - newp = new AstSigned(nodep->fileline(), newp); - } else if (!basicp->isSigned() && newp->isSigned()) { - newp = new AstUnsigned(nodep->fileline(), newp); - } else { - //newp = newp; // Can just remove cast - } - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - //if (debug()) newp->dumpTree(cout," CastOut: "); + if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); + nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep())); + //if (debug()) nodep->dumpTree(cout, " CastPre: "); + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + // When more general casts are supported, the cast elimination will be done later. + // For now, replace it ASAP, so widthing can propagate easily + // The cast may change signing, but we don't know the sign yet. Make it so. + // Note we don't sign lhsp() that would make the algorithm O(n^2) if lots of casting. + AstBasicDType* basicp = nodep->dtypep()->basicp(); + if (!basicp) nodep->v3fatalSrc("Unimplemented: Casting non-simple data type"); + // When implement more complicated types need to convert childDTypep to + // dtypep() not as a child + if (!basicp->isDouble() && !nodep->lhsp()->isDouble()) { + // Note widthCheckSized might modify nodep->lhsp() + AstNodeDType* subDTypep = nodep->findLogicDType(nodep->width(), nodep->width(), + nodep->lhsp()->dtypep()->numeric()); + widthCheckSized(nodep, "Cast", nodep->lhsp(), subDTypep, EXTEND_EXP, false); + } + AstNode* newp = nodep->lhsp()->unlinkFrBack(); + if (basicp->isDouble() && !newp->isDouble()) { + newp = new AstIToRD(nodep->fileline(), newp); + } else if (!basicp->isDouble() && newp->isDouble()) { + if (basicp->isSigned()) { + newp = new AstRToIRoundS(nodep->fileline(), newp); + } else { + newp = new AstUnsigned(nodep->fileline(), + new AstRToIS(nodep->fileline(), newp)); + } + } else if (basicp->isSigned() && !newp->isSigned()) { + newp = new AstSigned(nodep->fileline(), newp); + } else if (!basicp->isSigned() && newp->isSigned()) { + newp = new AstUnsigned(nodep->fileline(), newp); + } else { + //newp = newp; // Can just remove cast + } + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + //if (debug()) newp->dumpTree(cout, " CastOut: "); } virtual void visit(AstCastSize* nodep) { - // IEEE: Signedness of result is same as self-determined signedness - // However, the result is same as BITSEL, so we do not sign extend the LHS + // IEEE: Signedness of result is same as self-determined signedness + // However, the result is same as BITSEL, so we do not sign extend the LHS if (!VN_IS(nodep->rhsp(), Const)) nodep->v3fatalSrc("Unsupported: Non-const cast of size"); - //if (debug()) nodep->dumpTree(cout," CastSizePre: "); - if (m_vup->prelim()) { + //if (debug()) nodep->dumpTree(cout, " CastSizePre: "); + if (m_vup->prelim()) { int width = VN_CAST(nodep->rhsp(), Const)->toSInt(); - if (width < 1) { nodep->v3error("Size-changing cast to zero or negative size"); width=1; } - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,PRELIM).p()); + if (width < 1) { nodep->v3error("Size-changing cast to zero or negative size"); width=1; } + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p()); AstBasicDType* underDtp = VN_CAST(nodep->lhsp()->dtypep(), BasicDType); - if (!underDtp) { - underDtp = nodep->lhsp()->dtypep()->basicp(); - } - if (!underDtp) { - nodep->v3error("Unsupported: Size-changing cast on non-basic data type"); + if (!underDtp) { + underDtp = nodep->lhsp()->dtypep()->basicp(); + } + if (!underDtp) { + nodep->v3error("Unsupported: Size-changing cast on non-basic data type"); underDtp = VN_CAST(nodep->findLogicBoolDType(), BasicDType); - } - // A cast propagates its size to the lower expression and is included in the maximum - // width, so 23'(1'b1 + 1'b1) uses 23-bit math, but 1'(2'h2 * 2'h1) uses two-bit math. - // However the output width is exactly that requested. - // So two steps, first do the calculation's width (max of the two widths) - { + } + // A cast propagates its size to the lower expression and is included in the maximum + // width, so 23'(1'b1 + 1'b1) uses 23-bit math, but 1'(2'h2 * 2'h1) uses two-bit math. + // However the output width is exactly that requested. + // So two steps, first do the calculation's width (max of the two widths) + { int calcWidth = std::max(width, underDtp->width()); AstNodeDType* calcDtp = (underDtp->isFourstate() - ? nodep->findLogicDType(calcWidth, calcWidth, underDtp->numeric()) - : nodep->findBitDType(calcWidth, calcWidth, underDtp->numeric())); - nodep->dtypep(calcDtp); - // We ignore warnings as that is sort of the point of a cast - iterateCheck(nodep,"Cast expr",nodep->lhsp(),CONTEXT,FINAL,calcDtp,EXTEND_EXP, false); - } - //if (debug()) nodep->dumpTree(cout," CastSizeClc: "); - // Next step, make the proper output width - { + ? nodep->findLogicDType(calcWidth, calcWidth, underDtp->numeric()) + : nodep->findBitDType(calcWidth, calcWidth, underDtp->numeric())); + nodep->dtypep(calcDtp); + // We ignore warnings as that is sort of the point of a cast + iterateCheck(nodep, "Cast expr", nodep->lhsp(), CONTEXT, FINAL, calcDtp, EXTEND_EXP, false); + } + //if (debug()) nodep->dumpTree(cout, " CastSizeClc: "); + // Next step, make the proper output width + { AstNodeDType* outDtp = (underDtp->isFourstate() - ? nodep->findLogicDType(width, width, underDtp->numeric()) - : nodep->findBitDType(width, width, underDtp->numeric())); - nodep->dtypep(outDtp); - // We ignore warnings as that is sort of the point of a cast - widthCheckSized(nodep,"Cast expr",nodep->lhsp(),outDtp,EXTEND_EXP, false); - } - } - if (m_vup->final()) { - // CastSize not needed once sizes determined - AstNode* underp = nodep->lhsp()->unlinkFrBack(); - nodep->replaceWith(underp); - pushDeletep(nodep); VL_DANGLING(nodep); - } - //if (debug()) nodep->dumpTree(cout," CastSizeOut: "); + ? nodep->findLogicDType(width, width, underDtp->numeric()) + : nodep->findBitDType(width, width, underDtp->numeric())); + nodep->dtypep(outDtp); + // We ignore warnings as that is sort of the point of a cast + widthCheckSized(nodep, "Cast expr", nodep->lhsp(), outDtp, EXTEND_EXP, false); + } + } + if (m_vup->final()) { + // CastSize not needed once sizes determined + AstNode* underp = nodep->lhsp()->unlinkFrBack(); + nodep->replaceWith(underp); + pushDeletep(nodep); VL_DANGLING(nodep); + } + //if (debug()) nodep->dumpTree(cout, " CastSizeOut: "); } virtual void visit(AstVar* nodep) { - //if (debug()) nodep->dumpTree(cout," InitPre: "); - // Must have deterministic constant width - // We can't skip this step when width()!=0, as creating a AstVar - // with non-constant range gets size 1, not size 0. So use didWidth(). - if (nodep->didWidth()) return; - if (nodep->doingWidth()) { // Early exit if have circular parameter definition - if (!nodep->valuep()) nodep->v3fatalSrc("circular, but without value"); - nodep->v3error("Variable's initial value is circular: "<prettyName()); - pushDeletep(nodep->valuep()->unlinkFrBack()); - nodep->valuep(new AstConst(nodep->fileline(), AstConst::LogicTrue())); - nodep->dtypeFrom(nodep->valuep()); - nodep->didWidth(true); - return; - } - nodep->doingWidth(true); - // Make sure dtype is sized - if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); - nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep())); - if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype determined for var"); + //if (debug()) nodep->dumpTree(cout, " InitPre: "); + // Must have deterministic constant width + // We can't skip this step when width()!=0, as creating a AstVar + // with non-constant range gets size 1, not size 0. So use didWidth(). + if (nodep->didWidth()) return; + if (nodep->doingWidth()) { // Early exit if have circular parameter definition + if (!nodep->valuep()) nodep->v3fatalSrc("circular, but without value"); + nodep->v3error("Variable's initial value is circular: "<prettyName()); + pushDeletep(nodep->valuep()->unlinkFrBack()); + nodep->valuep(new AstConst(nodep->fileline(), AstConst::LogicTrue())); + nodep->dtypeFrom(nodep->valuep()); + nodep->didWidth(true); + return; + } + nodep->doingWidth(true); + // Make sure dtype is sized + if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); + nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep())); + if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype determined for var"); if (VN_IS(nodep->dtypeSkipRefp(), UnsizedArrayDType)) { if (!(m_ftaskp && m_ftaskp->dpiImport())) { nodep->v3error("Unsized/open arrays ('[]') are only supported in DPI imports"); @@ -1206,480 +1236,495 @@ private: else if (nodep->isIO() && !(VN_IS(nodep->dtypeSkipRefp(), BasicDType) || VN_IS(nodep->dtypeSkipRefp(), NodeArrayDType) || VN_IS(nodep->dtypeSkipRefp(), NodeClassDType))) { - nodep->v3error("Unsupported: Inputs and outputs must be simple data types"); - } + nodep->v3error("Unsupported: Inputs and outputs must be simple data types"); + } if (VN_IS(nodep->dtypep()->skipRefToConstp(), ConstDType)) { - nodep->isConst(true); - } - // Parameters if implicit untyped inherit from what they are assigned to + nodep->isConst(true); + } + // Parameters if implicit untyped inherit from what they are assigned to AstBasicDType* bdtypep = VN_CAST(nodep->dtypep(), BasicDType); - bool didchk = false; - bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit(); - if (implicitParam) { - if (nodep->valuep()) { - userIterateAndNext(nodep->valuep(), WidthVP(nodep->dtypep(),PRELIM).p()); - UINFO(9,"implicitParamPRELIMIV "<valuep()<valuep()->isDouble()) { - nodep->dtypeSetDouble(); VL_DANGLING(bdtypep); - } else { - int width=0; - AstBasicDType* valueBdtypep = nodep->valuep()->dtypep()->basicp(); - bool issigned = false; - if (bdtypep->isNosign()) { - if (valueBdtypep && valueBdtypep->isSigned()) issigned=true; - } else issigned = bdtypep->isSigned(); - if (nodep->valuep()->dtypep()->widthSized()) { - width = nodep->valuep()->width(); - } else { + bool didchk = false; + bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit(); + if (implicitParam) { + if (nodep->valuep()) { + userIterateAndNext(nodep->valuep(), WidthVP(nodep->dtypep(), PRELIM).p()); + UINFO(9,"implicitParamPRELIMIV "<valuep()<valuep()->isDouble()) { + nodep->dtypeSetDouble(); VL_DANGLING(bdtypep); + } else { + int width = 0; + AstBasicDType* valueBdtypep = nodep->valuep()->dtypep()->basicp(); + bool issigned = false; + if (bdtypep->isNosign()) { + if (valueBdtypep && valueBdtypep->isSigned()) issigned = true; + } else issigned = bdtypep->isSigned(); + if (nodep->valuep()->dtypep()->widthSized()) { + width = nodep->valuep()->width(); + } else { if (nodep->valuep()->width()>32) { - nodep->valuep()->v3warn(WIDTH,"Assigning >32 bit to unranged parameter (defaults to 32 bits)"); + nodep->valuep()->v3warn(WIDTH, "Assigning >32 bit to unranged parameter (defaults to 32 bits)"); } - width = 32; - } - // Can't just inherit valuep()->dtypep() as mwidth might not equal width - if (width==1) { - // one bit parameter is same as "parameter [0] foo", not "parameter logic foo" - // as you can extract "foo[0]" from a parameter but not a wire - nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(), - AstNumeric::fromBool(issigned)); - nodep->dtypep(nodep->findLogicRangeDType - (VNumRange(0,0,false), - nodep->valuep()->widthMin(), - AstNumeric::fromBool(issigned))); - } else { - nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(), - AstNumeric::fromBool(issigned)); - } - didchk = true; - } - iterateCheckAssign(nodep,"Initial value",nodep->valuep(),FINAL,nodep->dtypep()); - UINFO(9,"implicitParamFromIV "<valuep()<dtypeSetSigned32(); VL_DANGLING(bdtypep); - } - } - else if (bdtypep && bdtypep->implicit()) { // Implicits get converted to size 1 - nodep->dtypeSetLogicSized(1,1,bdtypep->numeric()); VL_DANGLING(bdtypep); - } - if (nodep->valuep() && !didchk) { - //if (debug()) nodep->dumpTree(cout," final: "); - // AstPattern requires assignments to pass datatype on PRELIM - userIterateAndNext(nodep->valuep(), WidthVP(nodep->dtypep(),PRELIM).p()); - iterateCheckAssign(nodep,"Initial value",nodep->valuep(),FINAL,nodep->dtypep()); - } - UINFO(4,"varWidthed "<dumpTree(cout," InitOut: "); - nodep->didWidth(true); - nodep->doingWidth(false); + width = 32; + } + // Can't just inherit valuep()->dtypep() as mwidth might not equal width + if (width==1) { + // one bit parameter is same as "parameter [0] foo", + // not "parameter logic foo" as you can extract + // "foo[0]" from a parameter but not a wire + nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(), + AstNumeric::fromBool(issigned)); + nodep->dtypep(nodep->findLogicRangeDType + (VNumRange(0, 0, false), + nodep->valuep()->widthMin(), + AstNumeric::fromBool(issigned))); + } else { + nodep->dtypeChgWidthSigned(width, nodep->valuep()->widthMin(), + AstNumeric::fromBool(issigned)); + } + didchk = true; + } + iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep()); + UINFO(9,"implicitParamFromIV "<valuep()<dtypeSetSigned32(); VL_DANGLING(bdtypep); + } + } + else if (bdtypep && bdtypep->implicit()) { // Implicits get converted to size 1 + nodep->dtypeSetLogicSized(1, 1, bdtypep->numeric()); VL_DANGLING(bdtypep); + } + if (nodep->valuep() && !didchk) { + //if (debug()) nodep->dumpTree(cout, " final: "); + // AstPattern requires assignments to pass datatype on PRELIM + userIterateAndNext(nodep->valuep(), WidthVP(nodep->dtypep(), PRELIM).p()); + iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep()); + } + UINFO(4,"varWidthed "<dumpTree(cout, " InitOut: "); + nodep->didWidth(true); + nodep->doingWidth(false); } virtual void visit(AstNodeVarRef* nodep) { - if (nodep->didWidth()) return; - if (!nodep->varp()) { + if (nodep->didWidth()) return; + if (!nodep->varp()) { if (m_paramsOnly && VN_IS(nodep, VarXRef)) { checkConstantOrReplace(nodep, "Parameter-resolved constants must not use dotted references: " +nodep->prettyName()); VL_DANGLING(nodep); - return; - } else { - nodep->v3fatalSrc("Unlinked varref"); - } - } - if (!nodep->varp()->didWidth()) { - // Var hasn't been widthed, so make it so. - userIterate(nodep->varp(), NULL); - } - //if (debug()>=9) { nodep->dumpTree(cout," VRin "); nodep->varp()->dumpTree(cout," forvar "); } - // Note genvar's are also entered as integers - nodep->dtypeFrom(nodep->varp()); + return; + } else { + nodep->v3fatalSrc("Unlinked varref"); + } + } + if (!nodep->varp()->didWidth()) { + // Var hasn't been widthed, so make it so. + userIterate(nodep->varp(), NULL); + } + //if (debug()>=9) { nodep->dumpTree(cout, " VRin "); nodep->varp()->dumpTree(cout, " forvar "); } + // Note genvar's are also entered as integers + nodep->dtypeFrom(nodep->varp()); if (VN_IS(nodep->backp(), NodeAssign) && nodep->lvalue()) { // On LHS - if (!nodep->widthMin()) nodep->v3fatalSrc("LHS var should be size complete"); - } - //if (debug()>=9) nodep->dumpTree(cout," VRout "); + if (!nodep->widthMin()) nodep->v3fatalSrc("LHS var should be size complete"); + } + //if (debug()>=9) nodep->dumpTree(cout, " VRout "); if (nodep->lvalue() && nodep->varp()->direction() == VDirection::CONSTREF) { nodep->v3error("Assigning to const ref variable: "<prettyName()); } else if (nodep->lvalue() && nodep->varp()->isConst() - && !m_paramsOnly - && !m_initialp) { // Too loose, but need to allow our generated first assignment - // // Move this to a property of the AstInitial block - nodep->v3error("Assigning to const variable: "<prettyName()); - } - nodep->didWidth(true); + && !m_paramsOnly + && !m_initialp) { // Too loose, but need to allow our generated first assignment + // // Move this to a property of the AstInitial block + nodep->v3error("Assigning to const variable: "<prettyName()); + } + nodep->didWidth(true); } virtual void visit(AstEnumDType* nodep) { - if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - UINFO(5," ENUMDTYPE "<childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); - nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); - nodep->dtypep(nodep); - nodep->widthFromSub(nodep->subDTypep()); - // Assign widths - userIterateAndNext(nodep->itemsp(), WidthVP(nodep->dtypep(),BOTH).p()); - // Assign missing values + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + UINFO(5," ENUMDTYPE "<childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->dtypep(nodep); + nodep->widthFromSub(nodep->subDTypep()); + // Assign widths + userIterateAndNext(nodep->itemsp(), WidthVP(nodep->dtypep(), BOTH).p()); + // Assign missing values V3Number num (nodep, nodep->width(), 0); V3Number one (nodep, nodep->width(), 1); std::map inits; - for (AstEnumItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), EnumItem)) { - if (itemp->valuep()) { - if (debug()>=9) { UINFO(0,"EnumInit "<valuep()->dumpTree(cout,"-EnumInit: "); } - V3Const::constifyParamsEdit(itemp->valuep()); // itemp may change + for (AstEnumItem* itemp = nodep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), EnumItem)) { + if (itemp->valuep()) { + if (debug()>=9) { UINFO(0,"EnumInit "<valuep()->dumpTree(cout, "-EnumInit: "); } + V3Const::constifyParamsEdit(itemp->valuep()); // itemp may change if (!VN_IS(itemp->valuep(), Const)) { - itemp->valuep()->v3error("Enum value isn't a constant"); - itemp->valuep()->unlinkFrBack()->deleteTree(); - continue; - } - // TODO IEEE says assigning sized number that is not same size as enum is illegal - } - if (!itemp->valuep()) { - if (num.isEqZero() && itemp != nodep->itemsp()) - itemp->v3error("Enum value illegally wrapped around (IEEE 2017 6.19)"); - if (!nodep->dtypep()->basicp() - && !nodep->dtypep()->basicp()->keyword().isIntNumeric()) { - itemp->v3error("Enum names without values only allowed on numeric types"); - // as can't +1 to resolve them. - } - itemp->valuep(new AstConst(itemp->fileline(), num)); - } + itemp->valuep()->v3error("Enum value isn't a constant"); + itemp->valuep()->unlinkFrBack()->deleteTree(); + continue; + } + // TODO IEEE says assigning sized number that is not same size as enum is illegal + } + if (!itemp->valuep()) { + if (num.isEqZero() && itemp != nodep->itemsp()) + itemp->v3error("Enum value illegally wrapped around (IEEE 2017 6.19)"); + if (!nodep->dtypep()->basicp() + && !nodep->dtypep()->basicp()->keyword().isIntNumeric()) { + itemp->v3error("Enum names without values only allowed on numeric types"); + // as can't +1 to resolve them. + } + itemp->valuep(new AstConst(itemp->fileline(), num)); + } num.opAssign(VN_CAST(itemp->valuep(), Const)->num()); - // Look for duplicates - if (inits.find(num) != inits.end()) { // IEEE says illegal - itemp->v3error("Overlapping enumeration value: "<prettyName()<second->warnMore() - <<"... Location of original declaration"); - } else { - inits.insert(make_pair(num,itemp)); - } + // Look for duplicates + if (inits.find(num) != inits.end()) { // IEEE says illegal + itemp->v3error("Overlapping enumeration value: "<prettyName()<second->warnMore() + <<"... Location of original declaration"); + } else { + inits.insert(make_pair(num, itemp)); + } num.opAdd(one, VN_CAST(itemp->valuep(), Const)->num()); - } + } } virtual void visit(AstEnumItem* nodep) { - UINFO(5," ENUMITEM "<dtypep(); - if (!vdtypep) nodep->v3fatalSrc("ENUMITEM not under ENUM"); - nodep->dtypep(vdtypep); - if (nodep->valuep()) { // else the value will be assigned sequentially - // Default type is int, but common to assign narrower values, so minwidth from value - userIterateAndNext(nodep->valuep(), WidthVP(CONTEXT,PRELIM).p()); - int mwidth = nodep->valuep()->widthMin(); // Value determines minwidth - nodep->dtypeChgWidth(nodep->width(), mwidth); - iterateCheck(nodep,"Enum value",nodep->valuep(),CONTEXT,FINAL,nodep->dtypep(),EXTEND_EXP); - } + UINFO(5," ENUMITEM "<dtypep(); + if (!vdtypep) nodep->v3fatalSrc("ENUMITEM not under ENUM"); + nodep->dtypep(vdtypep); + if (nodep->valuep()) { // else the value will be assigned sequentially + // Default type is int, but common to assign narrower values, so minwidth from value + userIterateAndNext(nodep->valuep(), WidthVP(CONTEXT, PRELIM).p()); + int mwidth = nodep->valuep()->widthMin(); // Value determines minwidth + nodep->dtypeChgWidth(nodep->width(), mwidth); + iterateCheck(nodep, "Enum value", nodep->valuep(), CONTEXT, FINAL, nodep->dtypep(), EXTEND_EXP); + } } virtual void visit(AstEnumItemRef* nodep) { - if (!nodep->itemp()->didWidth()) { - // We need to do the whole enum en-mass - AstNode* enump = nodep->itemp(); - if (!enump) nodep->v3fatalSrc("EnumItemRef not linked"); - for (; enump; enump=enump->backp()) { + if (!nodep->itemp()->didWidth()) { + // We need to do the whole enum en-mass + AstNode* enump = nodep->itemp(); + if (!enump) nodep->v3fatalSrc("EnumItemRef not linked"); + for (; enump; enump=enump->backp()) { if (VN_IS(enump, EnumDType)) break; - } - if (!enump) nodep->v3fatalSrc("EnumItemRef can't deref back to an Enum"); - userIterate(enump, m_vup); VL_DANGLING(enump); // parent's connection to enump may be relinked - } - nodep->dtypeFrom(nodep->itemp()); + } + if (!enump) nodep->v3fatalSrc("EnumItemRef can't deref back to an Enum"); + userIterate(enump, m_vup); VL_DANGLING(enump); // parent's connection to enump may be relinked + } + nodep->dtypeFrom(nodep->itemp()); } virtual void visit(AstInitArray* nodep) { - // InitArray has type of the array; children are array values - if (m_vup->prelim()) { // First stage evaluation - AstNodeDType* vdtypep = m_vup->dtypep(); - if (!vdtypep) nodep->v3fatalSrc("InitArray type not assigned by AstPattern/Var visitor"); - nodep->dtypep(vdtypep); + // InitArray has type of the array; children are array values + if (m_vup->prelim()) { // First stage evaluation + AstNodeDType* vdtypep = m_vup->dtypep(); + if (!vdtypep) nodep->v3fatalSrc("InitArray type not assigned by AstPattern/Var visitor"); + nodep->dtypep(vdtypep); if (AstNodeArrayDType* arrayp = VN_CAST(vdtypep->skipRefp(), NodeArrayDType)) { - userIterateChildren(nodep, WidthVP(arrayp->subDTypep(),BOTH).p()); - } else { - nodep->v3fatalSrc("InitArray on non-array"); - } - } + userIterateChildren(nodep, WidthVP(arrayp->subDTypep(), BOTH).p()); + } else { + nodep->v3fatalSrc("InitArray on non-array"); + } + } } virtual void visit(AstInside* nodep) { - userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT,PRELIM).p()); - for (AstNode* nextip, *itemp = nodep->itemsp(); itemp; itemp=nextip) { - nextip = itemp->nextp(); // Prelim may cause the node to get replaced - userIterate(itemp, WidthVP(CONTEXT,PRELIM).p()); VL_DANGLING(itemp); - } - // Take width as maximum across all items - int width = nodep->exprp()->width(); - int mwidth = nodep->exprp()->widthMin(); - for (AstNode* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()) { - width = std::max(width,itemp->width()); - mwidth = std::max(mwidth,itemp->widthMin()); - } - // Apply width - AstNodeDType* subDTypep = nodep->findLogicDType(width,mwidth,nodep->exprp()->dtypep()->numeric()); - iterateCheck(nodep,"Inside expression",nodep->exprp(),CONTEXT,FINAL,subDTypep,EXTEND_EXP); - for (AstNode* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()) { - iterateCheck(nodep,"Inside Item",itemp,CONTEXT,FINAL,subDTypep,EXTEND_EXP); - } - nodep->dtypeSetLogicBool(); - if (debug()>=9) nodep->dumpTree(cout,"-inside-in: "); - // Now rip out the inside and replace with simple math - AstNode* newp = NULL; - for (AstNode* nextip, *itemp = nodep->itemsp(); itemp; itemp=nextip) { - nextip = itemp->nextp(); // Will be unlinking - AstNode* inewp; + userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT, PRELIM).p()); + for (AstNode* nextip, *itemp = nodep->itemsp(); itemp; itemp=nextip) { + nextip = itemp->nextp(); // Prelim may cause the node to get replaced + userIterate(itemp, WidthVP(CONTEXT, PRELIM).p()); VL_DANGLING(itemp); + } + // Take width as maximum across all items + int width = nodep->exprp()->width(); + int mwidth = nodep->exprp()->widthMin(); + for (AstNode* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()) { + width = std::max(width, itemp->width()); + mwidth = std::max(mwidth, itemp->widthMin()); + } + // Apply width + AstNodeDType* subDTypep + = nodep->findLogicDType(width, mwidth, nodep->exprp()->dtypep()->numeric()); + iterateCheck(nodep, "Inside expression", nodep->exprp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP); + for (AstNode* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()) { + iterateCheck(nodep, "Inside Item", itemp, CONTEXT, FINAL, subDTypep, EXTEND_EXP); + } + nodep->dtypeSetLogicBool(); + if (debug()>=9) nodep->dumpTree(cout, "-inside-in: "); + // Now rip out the inside and replace with simple math + AstNode* newp = NULL; + for (AstNode* nextip, *itemp = nodep->itemsp(); itemp; itemp=nextip) { + nextip = itemp->nextp(); // Will be unlinking + AstNode* inewp; if (AstInsideRange* irangep = VN_CAST(itemp, InsideRange)) { - // Similar logic in V3Case - inewp = new AstAnd(itemp->fileline(), - new AstGte(itemp->fileline(), - nodep->exprp()->cloneTree(true), - irangep->lhsp()->unlinkFrBack()), - new AstLte(itemp->fileline(), - nodep->exprp()->cloneTree(true), - irangep->rhsp()->unlinkFrBack())); - } else { - inewp = new AstEqWild(itemp->fileline(), - nodep->exprp()->cloneTree(true), - itemp->unlinkFrBack()); - } - if (newp) newp = new AstOr(nodep->fileline(), newp, inewp); - else newp = inewp; - } - if (!newp) newp = new AstConst(nodep->fileline(), AstConst::LogicFalse()); - if (debug()>=9) newp->dumpTree(cout,"-inside-out: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + // Similar logic in V3Case + inewp = new AstAnd(itemp->fileline(), + new AstGte(itemp->fileline(), + nodep->exprp()->cloneTree(true), + irangep->lhsp()->unlinkFrBack()), + new AstLte(itemp->fileline(), + nodep->exprp()->cloneTree(true), + irangep->rhsp()->unlinkFrBack())); + } else { + inewp = new AstEqWild(itemp->fileline(), + nodep->exprp()->cloneTree(true), + itemp->unlinkFrBack()); + } + if (newp) newp = new AstOr(nodep->fileline(), newp, inewp); + else newp = inewp; + } + if (!newp) newp = new AstConst(nodep->fileline(), AstConst::LogicFalse()); + if (debug()>=9) newp->dumpTree(cout, "-inside-out: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); } virtual void visit(AstInsideRange* nodep) { - // Just do each side; AstInside will rip these nodes out later - userIterateAndNext(nodep->lhsp(), m_vup); - userIterateAndNext(nodep->rhsp(), m_vup); - nodep->dtypeFrom(nodep->lhsp()); + // Just do each side; AstInside will rip these nodes out later + userIterateAndNext(nodep->lhsp(), m_vup); + userIterateAndNext(nodep->rhsp(), m_vup); + nodep->dtypeFrom(nodep->lhsp()); } virtual void visit(AstIfaceRefDType* nodep) { - if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - UINFO(5," IFACEREF "<dtypep(nodep); - nodep->widthForce(1, 1); // Not really relevant - UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + UINFO(5," IFACEREF "<dtypep(nodep); + nodep->widthForce(1, 1); // Not really relevant + UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - UINFO(5," NODECLASS "<=9) nodep->dumpTree("-class-in--"); - if (!nodep->packed()) { - nodep->v3warn(UNPACKED, "Unsupported: Unpacked struct/union"); - } - userIterateChildren(nodep, NULL); // First size all members - nodep->repairMemberCache(); - // Determine bit assignments and width - nodep->dtypep(nodep); - int lsb = 0; - int width = 0; + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + UINFO(5," NODECLASS "<=9) nodep->dumpTree("-class-in--"); + if (!nodep->packed()) { + nodep->v3warn(UNPACKED, "Unsupported: Unpacked struct/union"); + } + userIterateChildren(nodep, NULL); // First size all members + nodep->repairMemberCache(); + // Determine bit assignments and width + nodep->dtypep(nodep); + int lsb = 0; + int width = 0; nodep->isFourstate(false); - // MSB is first, so go backwards - AstMemberDType* itemp; - for (itemp = nodep->membersp(); itemp && itemp->nextp(); itemp=VN_CAST(itemp->nextp(), MemberDType)) ; - for (AstMemberDType* backip; itemp; itemp=backip) { + // MSB is first, so go backwards + AstMemberDType* itemp; + for (itemp = nodep->membersp(); + itemp && itemp->nextp(); itemp=VN_CAST(itemp->nextp(), MemberDType)) ; + for (AstMemberDType* backip; itemp; itemp=backip) { if (nodep->isFourstate()) nodep->isFourstate(true); backip = VN_CAST(itemp->backp(), MemberDType); - itemp->lsb(lsb); + itemp->lsb(lsb); if (VN_IS(nodep, UnionDType)) { width = std::max(width, itemp->width()); - } else { - lsb += itemp->width(); - width += itemp->width(); - } - } - nodep->widthForce(width,width); // Signing stays as-is, as parsed from declaration - //if (debug()>=9) nodep->dumpTree("-class-out-"); + } else { + lsb += itemp->width(); + width += itemp->width(); + } + } + nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration + //if (debug()>=9) nodep->dumpTree("-class-out-"); } virtual void visit(AstMemberDType* nodep) { - if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed - if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); - // Iterate into subDTypep() to resolve that type and update pointer. - nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); - nodep->dtypep(nodep); // The member itself, not subDtype - nodep->widthFromSub(nodep->subDTypep()); + if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->dtypep(nodep); // The member itself, not subDtype + nodep->widthFromSub(nodep->subDTypep()); } virtual void visit(AstMemberSel* nodep) { - UINFO(5," MEMBERSEL "<=9) nodep->dumpTree("-mbs-in: "); - userIterateChildren(nodep, WidthVP(SELF,BOTH).p()); - if (debug()>=9) nodep->dumpTree("-mbs-ic: "); - // Find the fromp dtype - should be a class - AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); - UINFO(9," from dt "<=9) nodep->dumpTree("-mbs-in: "); + userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); + if (debug()>=9) nodep->dumpTree("-mbs-ic: "); + // Find the fromp dtype - should be a class + AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); + UINFO(9," from dt "<findMember(nodep->name()); - if (!memberp) { - nodep->v3error("Member '"<prettyName()<<"' not found in structure"); - } - } + // No need to width-resolve the class, as it was done when we did the child + memberp = adtypep->findMember(nodep->name()); + if (!memberp) { + nodep->v3error("Member '"<prettyName()<<"' not found in structure"); + } + } else if (VN_IS(fromDtp, EnumDType)) { - // Method call on enum without following parenthesis, e.g. "ENUM.next" - // Convert this into a method call, and let that visitor figure out what to do next - AstNode* newp = new AstMethodSel(nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), NULL); - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - userIterate(newp, m_vup); - return; - } - else { - nodep->v3error("Member selection of non-struct/union object '" - <fromp()->prettyTypeName()<<"' which is a '"<fromp()->dtypep()->prettyTypeName()<<"'"); - } - if (memberp) { - if (m_attrp) { // Looking for the base of the attribute - nodep->dtypep(memberp); - UINFO(9," MEMBERSEL(attr) -> "< "<dtypep()<fileline(), nodep->fromp()->unlinkFrBack(), - memberp->lsb(), memberp->width()); + // Method call on enum without following parenthesis, e.g. "ENUM.next" + // Convert this into a method call, and let that visitor figure out what to do next + AstNode* newp = new AstMethodSel(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), nodep->name(), NULL); + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + userIterate(newp, m_vup); + return; + } + else { + nodep->v3error("Member selection of non-struct/union object '" + <fromp()->prettyTypeName() + <<"' which is a '"<fromp()->dtypep()->prettyTypeName()<<"'"); + } + if (memberp) { + if (m_attrp) { // Looking for the base of the attribute + nodep->dtypep(memberp); + UINFO(9," MEMBERSEL(attr) -> "< "<dtypep()<fileline(), nodep->fromp()->unlinkFrBack(), + memberp->lsb(), memberp->width()); // Must skip over the member to find the union; as the member may disappear later newp->dtypep(memberp->subDTypep()->skipRefToEnump()); - newp->didWidth(true); // Don't replace dtype with basic type - UINFO(9," MEMBERSEL -> "< "<dtypep()<replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); + newp->didWidth(true); // Don't replace dtype with basic type + UINFO(9," MEMBERSEL -> "< "<dtypep()<replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); // Should be able to treat it as a normal-ish nodesel - maybe. // The lhsp() will be strange until this stage; create the number here? - } - } - if (!memberp) { // Very bogus, but avoids core dump - nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::LogicFalse())); - pushDeletep(nodep); VL_DANGLING(nodep); - } + } + } + if (!memberp) { // Very bogus, but avoids core dump + nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::LogicFalse())); + pushDeletep(nodep); VL_DANGLING(nodep); + } } virtual void visit(AstMethodSel* nodep) { - UINFO(5," METHODSEL "<=9) nodep->dumpTree("-mts-in: "); - // Should check types the method requires, but at present we don't do much - userIterate(nodep->fromp(), WidthVP(SELF,BOTH).p()); - for (AstArg* argp = VN_CAST(nodep->pinsp(), Arg); argp; argp = VN_CAST(argp->nextp(), Arg)) { - if (argp->exprp()) userIterate(argp->exprp(), WidthVP(SELF,BOTH).p()); - } - // Find the fromp dtype - should be a class - if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); - AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); - AstBasicDType* basicp = fromDtp ? fromDtp->basicp() : NULL; - UINFO(9," from dt "<=9) nodep->dumpTree("-mts-in: "); + // Should check types the method requires, but at present we don't do much + userIterate(nodep->fromp(), WidthVP(SELF, BOTH).p()); + for (AstArg* argp = VN_CAST(nodep->pinsp(), Arg); + argp; argp = VN_CAST(argp->nextp(), Arg)) { + if (argp->exprp()) userIterate(argp->exprp(), WidthVP(SELF, BOTH).p()); + } + // Find the fromp dtype - should be a class + if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression"); + AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump(); + AstBasicDType* basicp = fromDtp ? fromDtp->basicp() : NULL; + UINFO(9," from dt "<name() == "num" - || nodep->name() == "first" - || nodep->name() == "last") { - // Constant value - AstConst* newp = NULL; - if (nodep->pinsp()) nodep->v3error("Arguments passed to enum.num method, but it does not take arguments"); - if (nodep->name() == "num") { - int items = 0; - for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items; - newp = new AstConst(nodep->fileline(), AstConst::Signed32(), items); - } else if (nodep->name() == "first") { - AstEnumItem* itemp = adtypep->itemsp(); - if (!itemp) newp = new AstConst(nodep->fileline(), AstConst::Signed32(), 0); // Spec doesn't say what to do + // Method call on enum without following parenthesis, e.g. "ENUM.next" + // Convert this into a method call, and let that visitor figure out what to do next + if (adtypep) {} + if (nodep->name() == "num" + || nodep->name() == "first" + || nodep->name() == "last") { + // Constant value + AstConst* newp = NULL; + if (nodep->pinsp()) nodep->v3error("Arguments passed to enum.num method, but it does not take arguments"); + if (nodep->name() == "num") { + int items = 0; + for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items; + newp = new AstConst(nodep->fileline(), AstConst::Signed32(), items); + } else if (nodep->name() == "first") { + AstEnumItem* itemp = adtypep->itemsp(); + if (!itemp) newp = new AstConst( + nodep->fileline(), AstConst::Signed32(), 0); // Spec doesn't say what to do else newp = VN_CAST(itemp->valuep()->cloneTree(false), Const); // A const - } else if (nodep->name() == "last") { - AstEnumItem* itemp = adtypep->itemsp(); + } else if (nodep->name() == "last") { + AstEnumItem* itemp = adtypep->itemsp(); while (itemp && itemp->nextp()) itemp = VN_CAST(itemp->nextp(), EnumItem); - if (!itemp) newp = new AstConst(nodep->fileline(), AstConst::Signed32(), 0); // Spec doesn't say what to do + if (!itemp) newp = new AstConst( + nodep->fileline(), AstConst::Signed32(), 0); // Spec doesn't say what to do else newp = VN_CAST(itemp->valuep()->cloneTree(false), Const); // A const - } - if (!newp) nodep->v3fatalSrc("Enum method (perhaps enum item) not const"); - newp->fileline(nodep->fileline()); // Use method's filename/line number to be clearer; may have warning disables - nodep->replaceWith(newp); - pushDeletep(nodep); VL_DANGLING(nodep); - } - else if (nodep->name() == "name" - || nodep->name() == "next" - || nodep->name() == "prev") { - AstAttrType attrType; - if (nodep->name() == "name") attrType = AstAttrType::ENUM_NAME; - else if (nodep->name() == "next") attrType = AstAttrType::ENUM_NEXT; - else if (nodep->name() == "prev") attrType = AstAttrType::ENUM_PREV; - else nodep->v3fatalSrc("Bad case"); + } + if (!newp) nodep->v3fatalSrc("Enum method (perhaps enum item) not const"); + newp->fileline(nodep->fileline()); // Use method's filename/line number to be clearer; may have warning disables + nodep->replaceWith(newp); + pushDeletep(nodep); VL_DANGLING(nodep); + } + else if (nodep->name() == "name" + || nodep->name() == "next" + || nodep->name() == "prev") { + AstAttrType attrType; + if (nodep->name() == "name") attrType = AstAttrType::ENUM_NAME; + else if (nodep->name() == "next") attrType = AstAttrType::ENUM_NEXT; + else if (nodep->name() == "prev") attrType = AstAttrType::ENUM_PREV; + else nodep->v3fatalSrc("Bad case"); - if (nodep->pinsp() && nodep->name() == "name") { - nodep->v3error("Arguments passed to enum.name method, but it does not take arguments"); - } else if (nodep->pinsp() && !(VN_IS(VN_CAST(nodep->pinsp(), Arg)->exprp(), Const) - && VN_CAST(VN_CAST(nodep->pinsp(), Arg)->exprp(), Const)->toUInt()==1 - && !nodep->pinsp()->nextp())) { - nodep->v3error("Unsupported: Arguments passed to enum.next method"); - } - // Need a runtime lookup table. Yuk. - // Most enums unless overridden are 32 bits, so we size array based on max enum value used. - // Ideally we would have a fast algorithm when a number is - // of small width and complete and so can use an array, and - // a map for when the value is many bits and sparse. - uint64_t msbdim = 0; - { - for (AstEnumItem* itemp = adtypep->itemsp(); itemp; itemp = VN_CAST(itemp->nextp(), EnumItem)) { + if (nodep->pinsp() && nodep->name() == "name") { + nodep->v3error("Arguments passed to enum.name method, but it does not take arguments"); + } else if (nodep->pinsp() + && !(VN_IS(VN_CAST(nodep->pinsp(), Arg)->exprp(), Const) + && VN_CAST(VN_CAST(nodep->pinsp(), Arg)->exprp(), Const)->toUInt()==1 + && !nodep->pinsp()->nextp())) { + nodep->v3error("Unsupported: Arguments passed to enum.next method"); + } + // Need a runtime lookup table. Yuk. + // Most enums unless overridden are 32 bits, so we size array + // based on max enum value used. + // Ideally we would have a fast algorithm when a number is + // of small width and complete and so can use an array, and + // a map for when the value is many bits and sparse. + uint64_t msbdim = 0; + { + for (AstEnumItem* itemp = adtypep->itemsp(); + itemp; itemp = VN_CAST(itemp->nextp(), EnumItem)) { const AstConst* vconstp = VN_CAST(itemp->valuep(), Const); - if (!vconstp) nodep->v3fatalSrc("Enum item without constified value"); - if (vconstp->toUQuad() >= msbdim) msbdim = vconstp->toUQuad(); - } - if (adtypep->itemsp()->width() > 64 || msbdim >= (1<<16)) { - nodep->v3error("Unsupported; enum next/prev method on enum with > 10 bits"); - return; - } - } - int selwidth = V3Number::log2b(msbdim)+1; // Width to address a bit - AstVar* varp = enumVarp(adtypep, attrType, (VL_ULL(1)<fileline(), varp, false); - varrefp->packagep(v3Global.rootp()->dollarUnitPkgAddp()); - AstNode* newp = new AstArraySel(nodep->fileline(), varrefp, - // Select in case widths are off due to msblen!=width - new AstSel(nodep->fileline(), - nodep->fromp()->unlinkFrBack(), - 0, selwidth)); - nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); - } else { + if (!vconstp) nodep->v3fatalSrc("Enum item without constified value"); + if (vconstp->toUQuad() >= msbdim) msbdim = vconstp->toUQuad(); + } + if (adtypep->itemsp()->width() > 64 || msbdim >= (1<<16)) { + nodep->v3error("Unsupported; enum next/prev method on enum with > 10 bits"); + return; + } + } + int selwidth = V3Number::log2b(msbdim)+1; // Width to address a bit + AstVar* varp = enumVarp(adtypep, attrType, (VL_ULL(1)<fileline(), varp, false); + varrefp->packagep(v3Global.rootp()->dollarUnitPkgAddp()); + AstNode* newp = new AstArraySel(nodep->fileline(), varrefp, + // Select in case widths are + // off due to msblen!=width + new AstSel(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + 0, selwidth)); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + } else { nodep->v3error("Unknown built-in enum method '"<prettyName()<<"'"); - } - } + } + } else if (AstUnpackArrayDType* arrayType = VN_CAST(fromDtp, UnpackArrayDType)) { - enum { - UNKNOWN = 0, - ARRAY_OR, - ARRAY_AND, - ARRAY_XOR - } methodId; + enum { + UNKNOWN = 0, + ARRAY_OR, + ARRAY_AND, + ARRAY_XOR + } methodId; - methodId = UNKNOWN; - if (nodep->name() == "or") methodId = ARRAY_OR; - else if (nodep->name() == "and") methodId = ARRAY_AND; - else if (nodep->name() == "xor") methodId = ARRAY_XOR; + methodId = UNKNOWN; + if (nodep->name() == "or") methodId = ARRAY_OR; + else if (nodep->name() == "and") methodId = ARRAY_AND; + else if (nodep->name() == "xor") methodId = ARRAY_XOR; - if (methodId) { - if (nodep->pinsp()) nodep->v3error("Arguments passed to array method, but it does not take arguments"); + if (methodId) { + if (nodep->pinsp()) nodep->v3error("Arguments passed to array method, but it does not take arguments"); - FileLine* fl = nodep->fileline(); - AstNode* newp = NULL; - for (int i = 0; i < arrayType->elementsConst(); ++i) { - AstNode* arrayRef = nodep->fromp()->cloneTree(false); - AstNode* selector = new AstArraySel(fl, arrayRef, i); + FileLine* fl = nodep->fileline(); + AstNode* newp = NULL; + for (int i = 0; i < arrayType->elementsConst(); ++i) { + AstNode* arrayRef = nodep->fromp()->cloneTree(false); + AstNode* selector = new AstArraySel(fl, arrayRef, i); if (!newp) { - newp = selector; + newp = selector; } else { - switch (methodId) { - case ARRAY_OR: newp = new AstOr(fl, newp, selector); break; - case ARRAY_AND: newp = new AstAnd(fl, newp, selector); break; - case ARRAY_XOR: newp = new AstXor(fl, newp, selector); break; - default: nodep->v3fatalSrc("bad case"); - } - } - } - nodep->replaceWith(newp); - nodep->deleteTree(); VL_DANGLING(nodep); - } - else { + switch (methodId) { + case ARRAY_OR: newp = new AstOr(fl, newp, selector); break; + case ARRAY_AND: newp = new AstAnd(fl, newp, selector); break; + case ARRAY_XOR: newp = new AstXor(fl, newp, selector); break; + default: nodep->v3fatalSrc("bad case"); + } + } + } + nodep->replaceWith(newp); + nodep->deleteTree(); VL_DANGLING(nodep); + } + else { nodep->v3error("Unknown built-in array method '"<prettyName()<<"'"); - } - } - else if (basicp && basicp->isString()) { + } + } + else if (basicp && basicp->isString()) { // Method call on string if (nodep->name() == "len") { // Constant value @@ -1700,74 +1745,82 @@ private: } else { nodep->v3error("Unsupported: built-in string method '"<prettyName()<<"'"); } - } - else { - nodep->v3error("Unsupported: Member call on non-enum object '" - <fromp()->prettyTypeName()<<"' which is a '"<fromp()->dtypep()->prettyTypeName()<<"'"); - } + } + else { + nodep->v3error("Unsupported: Member call on non-enum object '" + <fromp()->prettyTypeName() + <<"' which is a '"<fromp()->dtypep()->prettyTypeName()<<"'"); + } } virtual void visit(AstPattern* nodep) { - if (nodep->didWidthAndSet()) return; - UINFO(9,"PATTERN "<childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); // data_type '{ pattern } - if (!nodep->dtypep() && m_vup->dtypeNullp()) { // Get it from parent assignment/pin/etc - nodep->dtypep(m_vup->dtypep()); - } - AstNodeDType* vdtypep = nodep->dtypep(); + if (nodep->didWidthAndSet()) return; + UINFO(9,"PATTERN "<childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep)); // data_type '{ pattern } + if (!nodep->dtypep() && m_vup->dtypeNullp()) { // Get it from parent assignment/pin/etc + nodep->dtypep(m_vup->dtypep()); + } + AstNodeDType* vdtypep = nodep->dtypep(); if (!vdtypep) nodep->v3error("Unsupported/Illegal: Assignment pattern" " member not underneath a supported construct: " <backp()->prettyTypeName()); - { - vdtypep = vdtypep->skipRefp(); - nodep->dtypep(vdtypep); - UINFO(9," adtypep "<dtypep(vdtypep); - // Determine replication count, and replicate initial value as widths need to be individually determined - for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp; patp = VN_CAST(patp->nextp(), PatMember)) { - int times = visitPatMemberRep(patp); - for (int i=1; icloneTree(false); - patp->addNextHere(newp); - // This loop will see the new elements as part of nextp() - } - } - // Convert any PatMember with multiple items to multiple PatMembers - for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp; patp = VN_CAST(patp->nextp(), PatMember)) { - if (patp->lhssp()->nextp()) { - // Can't just addNext, as would add to end of all members. So detach, add next and reattach - AstNRelinker relinkHandle; - patp->unlinkFrBack(&relinkHandle); - while (AstNode* movep = patp->lhssp()->nextp()) { - movep->unlinkFrBack(); // Not unlinkFrBackWithNext, just one - AstPatMember* newp = new AstPatMember(patp->fileline(), movep, patp->keyp()->cloneTree(true), NULL); - patp->addNext(newp); - } - relinkHandle.relink(patp); - } - } - AstPatMember* defaultp = NULL; - for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp; patp = VN_CAST(patp->nextp(), PatMember)) { - if (patp->isDefault()) { - if (defaultp) nodep->v3error("Multiple '{ default: } clauses"); - defaultp = patp; - patp->unlinkFrBack(); - } - } + { + vdtypep = vdtypep->skipRefp(); + nodep->dtypep(vdtypep); + UINFO(9," adtypep "<dtypep(vdtypep); + // Determine replication count, and replicate initial value as + // widths need to be individually determined + for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); + patp; patp = VN_CAST(patp->nextp(), PatMember)) { + int times = visitPatMemberRep(patp); + for (int i=1; icloneTree(false); + patp->addNextHere(newp); + // This loop will see the new elements as part of nextp() + } + } + // Convert any PatMember with multiple items to multiple PatMembers + for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); + patp; patp = VN_CAST(patp->nextp(), PatMember)) { + if (patp->lhssp()->nextp()) { + // Can't just addNext, as would add to end of all members. + // So detach, add next and reattach + AstNRelinker relinkHandle; + patp->unlinkFrBack(&relinkHandle); + while (AstNode* movep = patp->lhssp()->nextp()) { + movep->unlinkFrBack(); // Not unlinkFrBackWithNext, just one + AstPatMember* newp + = new AstPatMember(patp->fileline(), movep, + patp->keyp()->cloneTree(true), NULL); + patp->addNext(newp); + } + relinkHandle.relink(patp); + } + } + AstPatMember* defaultp = NULL; + for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); + patp; patp = VN_CAST(patp->nextp(), PatMember)) { + if (patp->isDefault()) { + if (defaultp) nodep->v3error("Multiple '{ default: } clauses"); + defaultp = patp; + patp->unlinkFrBack(); + } + } while (const AstConstDType* classp = VN_CAST(vdtypep, ConstDType)) { - vdtypep = classp->subDTypep()->skipRefp(); - } + vdtypep = classp->subDTypep()->skipRefp(); + } if (AstNodeClassDType* classp = VN_CAST(vdtypep, NodeClassDType)) { - // Due to "default" and tagged patterns, we need to determine - // which member each AstPatMember corresponds to before we can - // determine the dtypep for that PatMember's value, and then - // width the initial value appropriately. + // Due to "default" and tagged patterns, we need to determine + // which member each AstPatMember corresponds to before we can + // determine the dtypep for that PatMember's value, and then + // width the initial value appropriately. typedef std::map PatMap; - PatMap patmap; - { - AstMemberDType* memp = classp->membersp(); + PatMap patmap; + { + AstMemberDType* memp = classp->membersp(); AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); - for (; memp || patp; ) { + for (; memp || patp; ) { do { if (patp) { if (patp->keyp()) { @@ -1788,10 +1841,10 @@ private: } if (memp && !patp) { // Missing init elements, warn below - memp=NULL; patp=NULL; break; + memp = NULL; patp = NULL; break; } else if (!memp && patp) { patp->v3error("Assignment pattern contains too many elements"); - memp=NULL; patp=NULL; break; + memp = NULL; patp = NULL; break; } else { std::pair ret = patmap.insert(make_pair(memp, patp)); @@ -1801,377 +1854,389 @@ private: } } } while(0); - // Next + // Next if (memp) memp = VN_CAST(memp->nextp(), MemberDType); if (patp) patp = VN_CAST(patp->nextp(), PatMember); - } - } - AstNode* newp = NULL; - for (AstMemberDType* memp = classp->membersp(); memp; memp=VN_CAST(memp->nextp(), MemberDType)) { - PatMap::iterator it = patmap.find(memp); - AstPatMember* newpatp = NULL; - AstPatMember* patp = NULL; - if (it == patmap.end()) { - if (defaultp) { - newpatp = defaultp->cloneTree(false); - patp = newpatp; - } - else { + } + } + AstNode* newp = NULL; + for (AstMemberDType* memp = classp->membersp(); + memp; memp = VN_CAST(memp->nextp(), MemberDType)) { + PatMap::iterator it = patmap.find(memp); + AstPatMember* newpatp = NULL; + AstPatMember* patp = NULL; + if (it == patmap.end()) { + if (defaultp) { + newpatp = defaultp->cloneTree(false); + patp = newpatp; + } + else { if (!VN_IS(classp, UnionDType)) { nodep->v3error("Assignment pattern missed initializing elements: " <prettyTypeName()); } } - } else { - patp = it->second; - } - if (patp) { - // Determine initial values - vdtypep = memp; if (vdtypep) {} - patp->dtypep(memp); - userIterate(patp, WidthVP(memp,BOTH).p()); // See visit(AstPatMember* + } else { + patp = it->second; + } + if (patp) { + // Determine initial values + vdtypep = memp; if (vdtypep) {} + patp->dtypep(memp); + userIterate(patp, WidthVP(memp, BOTH).p()); // See visit(AstPatMember* - // Convert to concat for now - AstNode* valuep = patp->lhssp()->unlinkFrBack(); + // Convert to concat for now + AstNode* valuep = patp->lhssp()->unlinkFrBack(); if (VN_IS(valuep, Const)) { - // Forming a AstConcat will cause problems with unsized (uncommitted sized) constants + // Forming a AstConcat will cause problems with + // unsized (uncommitted sized) constants if (AstNode* newp = WidthCommitVisitor::newIfConstCommitSize(VN_CAST(valuep, Const))) { - pushDeletep(valuep); VL_DANGLING(valuep); - valuep = newp; - } - } - if (!newp) newp = valuep; - else { - AstConcat* concatp = new AstConcat(patp->fileline(), newp, valuep); - newp = concatp; - newp->dtypeSetLogicSized(concatp->lhsp()->width()+concatp->rhsp()->width(), - concatp->lhsp()->width()+concatp->rhsp()->width(), - nodep->dtypep()->numeric()); - } - } - if (newpatp) { pushDeletep(newpatp); VL_DANGLING(newpatp); } - } - if (newp) nodep->replaceWith(newp); - else nodep->v3error("Assignment pattern with no members"); - pushDeletep(nodep); VL_DANGLING(nodep); // Deletes defaultp also, if present - } + pushDeletep(valuep); VL_DANGLING(valuep); + valuep = newp; + } + } + if (!newp) newp = valuep; + else { + AstConcat* concatp = new AstConcat(patp->fileline(), newp, valuep); + newp = concatp; + newp->dtypeSetLogicSized(concatp->lhsp()->width()+concatp->rhsp()->width(), + concatp->lhsp()->width()+concatp->rhsp()->width(), + nodep->dtypep()->numeric()); + } + } + if (newpatp) { pushDeletep(newpatp); VL_DANGLING(newpatp); } + } + if (newp) nodep->replaceWith(newp); + else nodep->v3error("Assignment pattern with no members"); + pushDeletep(nodep); VL_DANGLING(nodep); // Deletes defaultp also, if present + } else if (VN_IS(vdtypep, NodeArrayDType)) { AstNodeArrayDType* arrayp = VN_CAST(vdtypep, NodeArrayDType); - VNumRange range = arrayp->declRange(); - PatVecMap patmap = patVectorMap(nodep, range); - UINFO(9,"ent "<=range.lo(); --ent) { - AstPatMember* newpatp = NULL; - AstPatMember* patp = NULL; - PatVecMap::iterator it=patmap.find(ent); - if (it == patmap.end()) { - if (defaultp) { - newpatp = defaultp->cloneTree(false); - patp = newpatp; - } - else { - nodep->v3error("Assignment pattern missed initializing elements: "<second; - patmap.erase(it); - } + VNumRange range = arrayp->declRange(); + PatVecMap patmap = patVectorMap(nodep, range); + UINFO(9,"ent "<=range.lo(); --ent) { + AstPatMember* newpatp = NULL; + AstPatMember* patp = NULL; + PatVecMap::iterator it = patmap.find(ent); + if (it == patmap.end()) { + if (defaultp) { + newpatp = defaultp->cloneTree(false); + patp = newpatp; + } + else { + nodep->v3error("Assignment pattern missed initializing elements: " + <second; + patmap.erase(it); + } - if (patp) { - // Determine initial values - vdtypep = arrayp->subDTypep(); - // Don't want the RHS an array - patp->dtypep(vdtypep); - // Determine values - might be another InitArray - userIterate(patp, WidthVP(patp->dtypep(),BOTH).p()); // See visit(AstPatMember* - // Convert to InitArray or constify immediately - AstNode* valuep = patp->lhssp()->unlinkFrBack(); + if (patp) { + // Determine initial values + vdtypep = arrayp->subDTypep(); + // Don't want the RHS an array + patp->dtypep(vdtypep); + // Determine values - might be another InitArray + userIterate(patp, WidthVP(patp->dtypep(), BOTH).p()); // See visit(AstPatMember* + // Convert to InitArray or constify immediately + AstNode* valuep = patp->lhssp()->unlinkFrBack(); if (VN_IS(valuep, Const)) { - // Forming a AstConcat will cause problems with unsized (uncommitted sized) constants + // Forming a AstConcat will cause problems with + // unsized (uncommitted sized) constants if (AstNode* newp = WidthCommitVisitor::newIfConstCommitSize(VN_CAST(valuep, Const))) { - pushDeletep(valuep); VL_DANGLING(valuep); - valuep = newp; - } - } + pushDeletep(valuep); VL_DANGLING(valuep); + valuep = newp; + } + } if (VN_IS(arrayp, UnpackArrayDType)) { - if (!newp) { - AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayp, NULL); - newap->addValuep(valuep); - newp = newap; - } else { - // We iterate hi()..lo() as that is what packed needs, - // but INITARRAY needs lo() first + if (!newp) { + AstInitArray* newap + = new AstInitArray(nodep->fileline(), arrayp, NULL); + newap->addValuep(valuep); + newp = newap; + } else { + // We iterate hi()..lo() as that is what packed needs, + // but INITARRAY needs lo() first VN_CAST(newp, InitArray)->addFrontValuep(valuep); - } - } else { // Packed. Convert to concat for now. - if (!newp) newp = valuep; - else { - AstConcat* concatp = new AstConcat(patp->fileline(), newp, valuep); - newp = concatp; - newp->dtypeSetLogicSized(concatp->lhsp()->width()+concatp->rhsp()->width(), - concatp->lhsp()->width()+concatp->rhsp()->width(), - nodep->dtypep()->numeric()); - } - } - } - if (newpatp) { pushDeletep(newpatp); VL_DANGLING(newpatp); } - } - if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements"); - if (newp) nodep->replaceWith(newp); - else nodep->v3error("Assignment pattern with no members"); - //if (debug()>=9) newp->dumpTree("-apat-out: "); - pushDeletep(nodep); VL_DANGLING(nodep); // Deletes defaultp also, if present - } + } + } else { // Packed. Convert to concat for now. + if (!newp) newp = valuep; + else { + AstConcat* concatp = new AstConcat(patp->fileline(), newp, valuep); + newp = concatp; + newp->dtypeSetLogicSized( + concatp->lhsp()->width()+concatp->rhsp()->width(), + concatp->lhsp()->width()+concatp->rhsp()->width(), + nodep->dtypep()->numeric()); + } + } + } + if (newpatp) { pushDeletep(newpatp); VL_DANGLING(newpatp); } + } + if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements"); + if (newp) nodep->replaceWith(newp); + else nodep->v3error("Assignment pattern with no members"); + //if (debug()>=9) newp->dumpTree("-apat-out: "); + pushDeletep(nodep); VL_DANGLING(nodep); // Deletes defaultp also, if present + } else if (VN_IS(vdtypep, BasicDType) && VN_CAST(vdtypep, BasicDType)->isRanged()) { AstBasicDType* bdtypep = VN_CAST(vdtypep, BasicDType); - VNumRange range = bdtypep->declRange(); - PatVecMap patmap = patVectorMap(nodep, range); - UINFO(9,"ent "<=range.lo(); --ent) { - AstPatMember* newpatp = NULL; - AstPatMember* patp = NULL; - PatVecMap::iterator it=patmap.find(ent); - if (it == patmap.end()) { - if (defaultp) { - newpatp = defaultp->cloneTree(false); - patp = newpatp; - } - else { - nodep->v3error("Assignment pattern missed initializing elements: "<second; - patmap.erase(it); - } - if (patp) { - // Determine initial values - vdtypep = nodep->findLogicBoolDType(); - // Don't want the RHS an array - patp->dtypep(vdtypep); - // Determine values - might be another InitArray - userIterate(patp, WidthVP(patp->dtypep(),BOTH).p()); - // Convert to InitArray or constify immediately - AstNode* valuep = patp->lhssp()->unlinkFrBack(); + VNumRange range = bdtypep->declRange(); + PatVecMap patmap = patVectorMap(nodep, range); + UINFO(9,"ent "<=range.lo(); --ent) { + AstPatMember* newpatp = NULL; + AstPatMember* patp = NULL; + PatVecMap::iterator it = patmap.find(ent); + if (it == patmap.end()) { + if (defaultp) { + newpatp = defaultp->cloneTree(false); + patp = newpatp; + } + else { + nodep->v3error("Assignment pattern missed initializing elements: " + <second; + patmap.erase(it); + } + if (patp) { + // Determine initial values + vdtypep = nodep->findLogicBoolDType(); + // Don't want the RHS an array + patp->dtypep(vdtypep); + // Determine values - might be another InitArray + userIterate(patp, WidthVP(patp->dtypep(), BOTH).p()); + // Convert to InitArray or constify immediately + AstNode* valuep = patp->lhssp()->unlinkFrBack(); if (VN_IS(valuep, Const)) { - // Forming a AstConcat will cause problems with unsized (uncommitted sized) constants + // Forming a AstConcat will cause problems with + // unsized (uncommitted sized) constants if (AstNode* newp = WidthCommitVisitor::newIfConstCommitSize(VN_CAST(valuep, Const))) { - pushDeletep(valuep); VL_DANGLING(valuep); - valuep = newp; - } - } - { // Packed. Convert to concat for now. - if (!newp) newp = valuep; - else { - AstConcat* concatp = new AstConcat(patp->fileline(), newp, valuep); - newp = concatp; - newp->dtypeSetLogicSized(concatp->lhsp()->width()+concatp->rhsp()->width(), - concatp->lhsp()->width()+concatp->rhsp()->width(), - nodep->dtypep()->numeric()); - } - } - } - if (newpatp) { pushDeletep(newpatp); VL_DANGLING(newpatp); } - } - if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements"); - if (newp) nodep->replaceWith(newp); - else nodep->v3error("Assignment pattern with no members"); - //if (debug()>=9) newp->dumpTree("-apat-out: "); - pushDeletep(nodep); VL_DANGLING(nodep); // Deletes defaultp also, if present - } else { - nodep->v3error("Unsupported: Assignment pattern applies against non struct/union: "<prettyTypeName()); - } - } + pushDeletep(valuep); VL_DANGLING(valuep); + valuep = newp; + } + } + { // Packed. Convert to concat for now. + if (!newp) newp = valuep; + else { + AstConcat* concatp = new AstConcat(patp->fileline(), newp, valuep); + newp = concatp; + newp->dtypeSetLogicSized( + concatp->lhsp()->width()+concatp->rhsp()->width(), + concatp->lhsp()->width()+concatp->rhsp()->width(), + nodep->dtypep()->numeric()); + } + } + } + if (newpatp) { pushDeletep(newpatp); VL_DANGLING(newpatp); } + } + if (!patmap.empty()) nodep->v3error("Assignment pattern with too many elements"); + if (newp) nodep->replaceWith(newp); + else nodep->v3error("Assignment pattern with no members"); + //if (debug()>=9) newp->dumpTree("-apat-out: "); + pushDeletep(nodep); VL_DANGLING(nodep); // Deletes defaultp also, if present + } else { + nodep->v3error("Unsupported: Assignment pattern applies against non struct/union: " + <prettyTypeName()); + } + } } virtual void visit(AstPatMember* nodep) { - AstNodeDType* vdtypep = m_vup->dtypeNullp(); - if (!vdtypep) nodep->v3fatalSrc("Pattern member type not assigned by AstPattern visitor"); - nodep->dtypep(vdtypep); - UINFO(9," PATMEMBER "<lhssp()->nextp()) nodep->v3fatalSrc("PatMember value should be singular w/replicates removed"); - // Need to propagate assignment type downwards, even on prelim - userIterateChildren(nodep, WidthVP(nodep->dtypep(),PRELIM).p()); - iterateCheck(nodep,"Pattern value",nodep->lhssp(),ASSIGN,FINAL,vdtypep,EXTEND_LHS); + AstNodeDType* vdtypep = m_vup->dtypeNullp(); + if (!vdtypep) nodep->v3fatalSrc("Pattern member type not assigned by AstPattern visitor"); + nodep->dtypep(vdtypep); + UINFO(9," PATMEMBER "<lhssp()->nextp()) nodep->v3fatalSrc("PatMember value should be singular w/replicates removed"); + // Need to propagate assignment type downwards, even on prelim + userIterateChildren(nodep, WidthVP(nodep->dtypep(), PRELIM).p()); + iterateCheck(nodep, "Pattern value", nodep->lhssp(), ASSIGN, FINAL, vdtypep, EXTEND_LHS); } int visitPatMemberRep(AstPatMember* nodep) { - uint32_t times = 1; - if (nodep->repp()) { // else repp()==NULL shorthand for rep count 1 - iterateCheckSizedSelf(nodep,"LHS",nodep->repp(),SELF,BOTH); - V3Const::constifyParamsEdit(nodep->repp()); // repp may change + uint32_t times = 1; + if (nodep->repp()) { // else repp()==NULL shorthand for rep count 1 + iterateCheckSizedSelf(nodep, "LHS", nodep->repp(), SELF, BOTH); + V3Const::constifyParamsEdit(nodep->repp()); // repp may change const AstConst* constp = VN_CAST(nodep->repp(), Const); - if (!constp) { nodep->v3error("Replication value isn't a constant."); times=0; } - else times = constp->toUInt(); - if (times==0) { nodep->v3error("Pattern replication value of 0 is not legal."); times=1; } - nodep->repp()->unlinkFrBackWithNext()->deleteTree(); // Done with replicate before cloning - } - return times; + if (!constp) { nodep->v3error("Replication value isn't a constant."); times = 0; } + else times = constp->toUInt(); + if (times==0) { nodep->v3error("Pattern replication value of 0 is not legal."); times=1; } + nodep->repp()->unlinkFrBackWithNext()->deleteTree(); // Done with replicate before cloning + } + return times; } virtual void visit(AstPslClocked* nodep) { - if (m_vup->prelim()) { // First stage evaluation - iterateCheckBool(nodep,"Property",nodep->propp(),BOTH); - userIterateAndNext(nodep->sensesp(), NULL); - if (nodep->disablep()) { - iterateCheckBool(nodep,"Disable",nodep->disablep(),BOTH); // it's like an if() condition. - } - nodep->dtypeSetLogicBool(); - } + if (m_vup->prelim()) { // First stage evaluation + iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); + userIterateAndNext(nodep->sensesp(), NULL); + if (nodep->disablep()) { + iterateCheckBool(nodep, "Disable", nodep->disablep(), BOTH); // it's like an if() condition. + } + nodep->dtypeSetLogicBool(); + } } //-------------------- // Top levels virtual void visit(AstNodeCase* nodep) { - // IEEE-2012 12.5: - // Width: MAX(expr, all items) - // Signed: Only if expr, and all items signed - assertAtStatement(nodep); - userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT,PRELIM).p()); - for (AstCaseItem* nextip, *itemp = nodep->itemsp(); itemp; itemp=nextip) { + // IEEE-2012 12.5: + // Width: MAX(expr, all items) + // Signed: Only if expr, and all items signed + assertAtStatement(nodep); + userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT, PRELIM).p()); + for (AstCaseItem* nextip, *itemp = nodep->itemsp(); itemp; itemp=nextip) { nextip = VN_CAST(itemp->nextp(), CaseItem); // Prelim may cause the node to get replaced if (!VN_IS(nodep, GenCase)) userIterateAndNext(itemp->bodysp(), NULL); - for (AstNode* nextcp, *condp = itemp->condsp(); condp; condp=nextcp) { - nextcp = condp->nextp(); // Prelim may cause the node to get replaced - userIterate(condp, WidthVP(CONTEXT,PRELIM).p()); VL_DANGLING(condp); - } - } + for (AstNode* nextcp, *condp = itemp->condsp(); condp; condp=nextcp) { + nextcp = condp->nextp(); // Prelim may cause the node to get replaced + userIterate(condp, WidthVP(CONTEXT, PRELIM).p()); VL_DANGLING(condp); + } + } - // Take width as maximum across all items, if any is real whole thing is real - AstNodeDType* subDTypep = nodep->exprp()->dtypep(); + // Take width as maximum across all items, if any is real whole thing is real + AstNodeDType* subDTypep = nodep->exprp()->dtypep(); for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - for (AstNode* condp = itemp->condsp(); condp; condp=condp->nextp()) { - if (condp->dtypep() != subDTypep) { - if (condp->dtypep()->isDouble()) { - subDTypep = nodep->findDoubleDType(); - } else { - int width = std::max(subDTypep->width(),condp->width()); - int mwidth = std::max(subDTypep->widthMin(),condp->widthMin()); - bool issigned = subDTypep->isSigned() && condp->isSigned(); - subDTypep = nodep->findLogicDType(width,mwidth,AstNumeric::fromBool(issigned)); - } - } - } - } - // Apply width - iterateCheck(nodep,"Case expression",nodep->exprp(),CONTEXT,FINAL,subDTypep,EXTEND_LHS); + for (AstNode* condp = itemp->condsp(); condp; condp=condp->nextp()) { + if (condp->dtypep() != subDTypep) { + if (condp->dtypep()->isDouble()) { + subDTypep = nodep->findDoubleDType(); + } else { + int width = std::max(subDTypep->width(), condp->width()); + int mwidth = std::max(subDTypep->widthMin(), condp->widthMin()); + bool issigned = subDTypep->isSigned() && condp->isSigned(); + subDTypep = nodep->findLogicDType(width, mwidth, + AstNumeric::fromBool(issigned)); + } + } + } + } + // Apply width + iterateCheck(nodep, "Case expression", nodep->exprp(), CONTEXT, FINAL, subDTypep, EXTEND_LHS); for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) { - for (AstNode* nextcp, *condp = itemp->condsp(); condp; condp=nextcp) { - nextcp = condp->nextp(); // Final may cause the node to get replaced - iterateCheck(nodep,"Case Item",condp,CONTEXT,FINAL,subDTypep,EXTEND_LHS); - } - } + for (AstNode* nextcp, *condp = itemp->condsp(); condp; condp=nextcp) { + nextcp = condp->nextp(); // Final may cause the node to get replaced + iterateCheck(nodep, "Case Item", condp, CONTEXT, FINAL, subDTypep, EXTEND_LHS); + } + } } virtual void visit(AstNodeFor* nodep) { - assertAtStatement(nodep); - userIterateAndNext(nodep->initsp(), NULL); - iterateCheckBool(nodep,"For Test Condition",nodep->condp(),BOTH); // it's like an if() condition. + assertAtStatement(nodep); + userIterateAndNext(nodep->initsp(), NULL); + iterateCheckBool(nodep, "For Test Condition", nodep->condp(), BOTH); // it's like an if() condition. if (!VN_IS(nodep, GenFor)) userIterateAndNext(nodep->bodysp(), NULL); - userIterateAndNext(nodep->incsp(), NULL); + userIterateAndNext(nodep->incsp(), NULL); } virtual void visit(AstRepeat* nodep) { - assertAtStatement(nodep); - userIterateAndNext(nodep->countp(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->bodysp(), NULL); + assertAtStatement(nodep); + userIterateAndNext(nodep->countp(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->bodysp(), NULL); } virtual void visit(AstWhile* nodep) { - assertAtStatement(nodep); - userIterateAndNext(nodep->precondsp(), NULL); - iterateCheckBool(nodep,"For Test Condition",nodep->condp(),BOTH); // it's like an if() condition. - userIterateAndNext(nodep->bodysp(), NULL); - userIterateAndNext(nodep->incsp(), NULL); + assertAtStatement(nodep); + userIterateAndNext(nodep->precondsp(), NULL); + iterateCheckBool(nodep, "For Test Condition", nodep->condp(), BOTH); // it's like an if() condition. + userIterateAndNext(nodep->bodysp(), NULL); + userIterateAndNext(nodep->incsp(), NULL); } virtual void visit(AstNodeIf* nodep) { - assertAtStatement(nodep); - //if (debug()) nodep->dumpTree(cout," IfPre: "); + assertAtStatement(nodep); + //if (debug()) nodep->dumpTree(cout, " IfPre: "); if (!VN_IS(nodep, GenIf)) { // for m_paramsOnly - userIterateAndNext(nodep->ifsp(), NULL); - userIterateAndNext(nodep->elsesp(), NULL); - } - iterateCheckBool(nodep,"If",nodep->condp(),BOTH); // it's like an if() condition. - //if (debug()) nodep->dumpTree(cout," IfOut: "); + userIterateAndNext(nodep->ifsp(), NULL); + userIterateAndNext(nodep->elsesp(), NULL); + } + iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition. + //if (debug()) nodep->dumpTree(cout, " IfOut: "); } virtual void visit(AstNodeAssign* nodep) { - // IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5: (Careful of 11.8.1 which is - // only one step; final dtype depends on assign LHS.) - // Determine RHS type width and signing - // Propagate type down to *non-self-determined* operators - // Real propagates only across one operator if one side is real - handled in each visitor. - // Then LHS sign-extends only if *RHS* is signed - assertAtStatement(nodep); - //if (debug()) nodep->dumpTree(cout," AssignPre: "); - { - //if (debug()) nodep->dumpTree(cout,"- assin: "); - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - if (!nodep->lhsp()->dtypep()) nodep->v3fatalSrc("How can LHS be untyped?"); - if (!nodep->lhsp()->dtypep()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?"); - nodep->dtypeFrom(nodep->lhsp()); - // - // AstPattern needs to know the proposed data type of the lhs, so pass on the prelim - userIterateAndNext(nodep->rhsp(), WidthVP(nodep->dtypep(),PRELIM).p()); - // - //if (debug()) nodep->dumpTree(cout,"- assign: "); - AstNodeDType* lhsDTypep = nodep->lhsp()->dtypep(); // Note we use rhsp for context determined - iterateCheckAssign(nodep,"Assign RHS",nodep->rhsp(),FINAL,lhsDTypep); - //if (debug()) nodep->dumpTree(cout," AssignOut: "); - } + // IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5: (Careful of 11.8.1 which is + // only one step; final dtype depends on assign LHS.) + // Determine RHS type width and signing + // Propagate type down to *non-self-determined* operators + // Real propagates only across one operator if one side is real - + // handled in each visitor. + // Then LHS sign-extends only if *RHS* is signed + assertAtStatement(nodep); + //if (debug()) nodep->dumpTree(cout, " AssignPre: "); + { + //if (debug()) nodep->dumpTree(cout, "- assin: "); + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + if (!nodep->lhsp()->dtypep()) nodep->v3fatalSrc("How can LHS be untyped?"); + if (!nodep->lhsp()->dtypep()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?"); + nodep->dtypeFrom(nodep->lhsp()); + // + // AstPattern needs to know the proposed data type of the lhs, so pass on the prelim + userIterateAndNext(nodep->rhsp(), WidthVP(nodep->dtypep(), PRELIM).p()); + // + //if (debug()) nodep->dumpTree(cout, "- assign: "); + AstNodeDType* lhsDTypep = nodep->lhsp()->dtypep(); // Note we use rhsp for context determined + iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep); + //if (debug()) nodep->dumpTree(cout, " AssignOut: "); + } } virtual void visit(AstSFormatF* nodep) { - // Excludes NodeDisplay, see below - if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function - // Just let all arguments seek their natural sizes - userIterateChildren(nodep, WidthVP(SELF,BOTH).p()); - // - UINFO(9," Display in "<text()<exprsp(); - string txt = nodep->text(); - string fmt; - for (string::const_iterator it = txt.begin(); it!=txt.end(); ++it) { - char ch = *it; - if (!inPct && ch=='%') { - inPct = true; - fmt = ch; - } else if (inPct && (isdigit(ch) || ch=='.')) { - fmt += ch; - } else if (tolower(inPct)) { - inPct = false; - bool added = false; - switch (tolower(ch)) { - case '%': break; // %% - just output a % - case 'm': break; // %m - auto insert "name" - case 'l': break; // %m - auto insert "library" - case 'd': { // Convert decimal to either 'd' or '#' - if (argp && argp->isSigned()) { // Convert it - ch = '~'; - } - if (argp) argp=argp->nextp(); - break; - } - case 'p': { // Packed - // Very hacky and non-compliant; print strings as strings, otherwise as hex - if (argp && argp->dtypep()->basicp()->isString()) { // Convert it - added = true; - newFormat += "\"%@\""; - } else { - added = true; - newFormat += "'h%0h"; - } - if (argp) argp=argp->nextp(); - break; - } - case 's': { // Convert string to pack string - if (argp && argp->dtypep()->basicp()->isString()) { // Convert it - ch = '@'; - } - if (argp) argp=argp->nextp(); - break; - } + // Excludes NodeDisplay, see below + if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function + // Just let all arguments seek their natural sizes + userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); + // + UINFO(9," Display in "<text()<exprsp(); + string txt = nodep->text(); + string fmt; + for (string::const_iterator it = txt.begin(); it!=txt.end(); ++it) { + char ch = *it; + if (!inPct && ch=='%') { + inPct = true; + fmt = ch; + } else if (inPct && (isdigit(ch) || ch=='.')) { + fmt += ch; + } else if (tolower(inPct)) { + inPct = false; + bool added = false; + switch (tolower(ch)) { + case '%': break; // %% - just output a % + case 'm': break; // %m - auto insert "name" + case 'l': break; // %m - auto insert "library" + case 'd': { // Convert decimal to either 'd' or '#' + if (argp && argp->isSigned()) { // Convert it + ch = '~'; + } + if (argp) argp = argp->nextp(); + break; + } + case 'p': { // Packed + // Very hacky and non-compliant; print strings as strings, otherwise as hex + if (argp && argp->dtypep()->basicp()->isString()) { // Convert it + added = true; + newFormat += "\"%@\""; + } else { + added = true; + newFormat += "'h%0h"; + } + if (argp) argp = argp->nextp(); + break; + } + case 's': { // Convert string to pack string + if (argp && argp->dtypep()->basicp()->isString()) { // Convert it + ch = '@'; + } + if (argp) argp = argp->nextp(); + break; + } case 't': { // Convert decimal time to realtime if (argp && argp->isDouble()) { // Convert it ch = '^'; @@ -2190,70 +2255,70 @@ private: if (argp) argp = argp->nextp(); break; } - default: { // Most operators, just move to next argument - if (argp) argp=argp->nextp(); - break; - } - } // switch - if (!added) { - fmt += ch; - newFormat += fmt; - } - } else { - newFormat += ch; - } - } - nodep->text(newFormat); - UINFO(9," Display out "<text()<nextp(); + break; + } + } // switch + if (!added) { + fmt += ch; + newFormat += fmt; + } + } else { + newFormat += ch; + } + } + nodep->text(newFormat); + UINFO(9," Display out "<text()<filep()) { - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); - } - // Just let all arguments seek their natural sizes - userIterateChildren(nodep, WidthVP(SELF,BOTH).p()); + assertAtStatement(nodep); + if (nodep->filep()) { + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + } + // Just let all arguments seek their natural sizes + userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); } virtual void visit(AstFOpen* nodep) { - // Although a system function in IEEE, here a statement which sets the file pointer (MCD) - assertAtStatement(nodep); - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); - userIterateAndNext(nodep->filenamep(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->modep(), WidthVP(SELF,BOTH).p()); + // Although a system function in IEEE, here a statement which sets the file pointer (MCD) + assertAtStatement(nodep); + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->modep(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstFClose* nodep) { - assertAtStatement(nodep); - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); + assertAtStatement(nodep); + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); } virtual void visit(AstFEof* nodep) { - if (m_vup->prelim()) { - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); - nodep->dtypeSetLogicSized(32,1,AstNumeric::SIGNED); // Spec says integer return - } + if (m_vup->prelim()) { + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + nodep->dtypeSetLogicSized(32, 1, AstNumeric::SIGNED); // Spec says integer return + } } virtual void visit(AstFFlush* nodep) { - assertAtStatement(nodep); - if (nodep->filep()) { - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); - } + assertAtStatement(nodep); + if (nodep->filep()) { + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + } } virtual void visit(AstFGetC* nodep) { - if (m_vup->prelim()) { - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); - nodep->dtypeSetLogicSized(32,8,AstNumeric::SIGNED); // Spec says integer return - } + if (m_vup->prelim()) { + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + nodep->dtypeSetLogicSized(32, 8, AstNumeric::SIGNED); // Spec says integer return + } } virtual void visit(AstFGetS* nodep) { - if (m_vup->prelim()) { - nodep->dtypeSetSigned32(); // Spec says integer return - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); - userIterateAndNext(nodep->strgp(), WidthVP(SELF,BOTH).p()); - } + if (m_vup->prelim()) { + nodep->dtypeSetSigned32(); // Spec says integer return + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + userIterateAndNext(nodep->strgp(), WidthVP(SELF, BOTH).p()); + } } virtual void visit(AstFRead* nodep) { if (m_vup->prelim()) { nodep->dtypeSetSigned32(); // Spec says integer return - userIterateAndNext(nodep->memp(), WidthVP(SELF,BOTH).p()); + userIterateAndNext(nodep->memp(), WidthVP(SELF, BOTH).p()); iterateCheckFileDesc(nodep, nodep->filep(), BOTH); if (nodep->startp()) { iterateCheckSigned32(nodep, "$fread start", nodep->startp(), BOTH); @@ -2264,115 +2329,116 @@ private: } } virtual void visit(AstFScanF* nodep) { - if (m_vup->prelim()) { - nodep->dtypeSetSigned32(); // Spec says integer return - iterateCheckFileDesc(nodep,nodep->filep(),BOTH); - userIterateAndNext(nodep->exprsp(), WidthVP(SELF,BOTH).p()); - } + if (m_vup->prelim()) { + nodep->dtypeSetSigned32(); // Spec says integer return + iterateCheckFileDesc(nodep, nodep->filep(), BOTH); + userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p()); + } } virtual void visit(AstSScanF* nodep) { - if (m_vup->prelim()) { - nodep->dtypeSetSigned32(); // Spec says integer return - userIterateAndNext(nodep->fromp(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->exprsp(), WidthVP(SELF,BOTH).p()); - } + if (m_vup->prelim()) { + nodep->dtypeSetSigned32(); // Spec says integer return + userIterateAndNext(nodep->fromp(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p()); + } } virtual void visit(AstSysIgnore* nodep) { - userIterateAndNext(nodep->exprsp(), WidthVP(SELF,BOTH).p()); + userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstSystemF* nodep) { - if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - nodep->dtypeSetSigned32(); // Spec says integer return - } + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + nodep->dtypeSetSigned32(); // Spec says integer return + } } virtual void visit(AstSysFuncAsTask* nodep) { assertAtStatement(nodep); - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstSystemT* nodep) { - assertAtStatement(nodep); - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); + assertAtStatement(nodep); + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstNodeReadWriteMem* nodep) { - assertAtStatement(nodep); - userIterateAndNext(nodep->filenamep(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->memp(), WidthVP(SELF,BOTH).p()); + assertAtStatement(nodep); + userIterateAndNext(nodep->filenamep(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->memp(), WidthVP(SELF, BOTH).p()); if (!VN_IS(nodep->memp()->dtypep()->skipRefp(), UnpackArrayDType)) { - nodep->memp()->v3error("Unsupported: " << nodep->verilogKwd() + nodep->memp()->v3error("Unsupported: " << nodep->verilogKwd() << " into other than unpacked array"); - } - userIterateAndNext(nodep->lsbp(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->msbp(), WidthVP(SELF,BOTH).p()); + } + userIterateAndNext(nodep->lsbp(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->msbp(), WidthVP(SELF, BOTH).p()); } virtual void visit(AstValuePlusArgs* nodep) { - if (m_vup->prelim()) { - userIterateAndNext(nodep->searchp(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->outp(), WidthVP(SELF,BOTH).p()); - nodep->dtypeChgWidthSigned(32,1,AstNumeric::SIGNED); // Spec says integer return - } + if (m_vup->prelim()) { + userIterateAndNext(nodep->searchp(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->outp(), WidthVP(SELF, BOTH).p()); + nodep->dtypeChgWidthSigned(32, 1, AstNumeric::SIGNED); // Spec says integer return + } } virtual void visit(AstUCStmt* nodep) { - // Just let all arguments seek their natural sizes - assertAtStatement(nodep); - userIterateChildren(nodep, WidthVP(SELF,BOTH).p()); + // Just let all arguments seek their natural sizes + assertAtStatement(nodep); + userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); } virtual void visit(AstNodePslCoverOrAssert* nodep) { - assertAtStatement(nodep); - iterateCheckBool(nodep,"Property",nodep->propp(),BOTH); // it's like an if() condition. - userIterateAndNext(nodep->stmtsp(), NULL); + assertAtStatement(nodep); + iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. + userIterateAndNext(nodep->stmtsp(), NULL); } virtual void visit(AstVAssert* nodep) { - assertAtStatement(nodep); - iterateCheckBool(nodep,"Property",nodep->propp(),BOTH); // it's like an if() condition. - userIterateAndNext(nodep->passsp(), NULL); - userIterateAndNext(nodep->failsp(), NULL); + assertAtStatement(nodep); + iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. + userIterateAndNext(nodep->passsp(), NULL); + userIterateAndNext(nodep->failsp(), NULL); } virtual void visit(AstPin* nodep) { - //if (debug()) nodep->dumpTree(cout,"- PinPre: "); - // TOP LEVEL NODE - if (nodep->modVarp() && nodep->modVarp()->isGParam()) { - // Widthing handled as special init() case - userIterateChildren(nodep, WidthVP(SELF,BOTH).p()); - } else if (!m_paramsOnly) { - if (!nodep->modVarp()->didWidth()) { - // Var hasn't been widthed, so make it so. - userIterate(nodep->modVarp(), NULL); - } - if (!nodep->exprp()) { // No-connect - return; - } - // Very much like like an assignment, but which side is LH/RHS - // depends on pin being a in/output/inout. - userIterateAndNext(nodep->exprp(), WidthVP(nodep->modVarp()->dtypep(),PRELIM).p()); - AstNodeDType* pinDTypep = nodep->modVarp()->dtypep(); - AstNodeDType* conDTypep = nodep->exprp()->dtypep(); - AstNodeDType* subDTypep = pinDTypep; - int pinwidth = pinDTypep->width(); - int conwidth = conDTypep->width(); - if (conDTypep == pinDTypep // If match, we're golden - || similarDTypeRecurse(conDTypep, pinDTypep)) { - userIterateAndNext(nodep->exprp(), WidthVP(subDTypep,FINAL).p()); - } - else if (m_cellRangep) { - int numInsts = m_cellRangep->elementsConst(); - if (conwidth == pinwidth) { - // Arrayed instants: widths match so connect to each instance - subDTypep = conDTypep; // = same expr dtype - } else if (conwidth == numInsts*pinwidth) { - // Arrayed instants: one bit for each of the instants (each assign is 1 pinwidth wide) - subDTypep = conDTypep; // = same expr dtype (but numInst*pin_dtype) - } else { - // Must be a error according to spec - // (Because we need to know if to connect to one or all instants) - nodep->v3error(ucfirst(nodep->prettyOperatorName())<<" as part of a module instance array" - <<" requires "<exprp()->prettyTypeName() - <<" generates "<exprp(), WidthVP(subDTypep,FINAL).p()); - } else { + //if (debug()) nodep->dumpTree(cout, "- PinPre: "); + // TOP LEVEL NODE + if (nodep->modVarp() && nodep->modVarp()->isGParam()) { + // Widthing handled as special init() case + userIterateChildren(nodep, WidthVP(SELF, BOTH).p()); + } else if (!m_paramsOnly) { + if (!nodep->modVarp()->didWidth()) { + // Var hasn't been widthed, so make it so. + userIterate(nodep->modVarp(), NULL); + } + if (!nodep->exprp()) { // No-connect + return; + } + // Very much like like an assignment, but which side is LH/RHS + // depends on pin being a in/output/inout. + userIterateAndNext(nodep->exprp(), WidthVP(nodep->modVarp()->dtypep(), PRELIM).p()); + AstNodeDType* pinDTypep = nodep->modVarp()->dtypep(); + AstNodeDType* conDTypep = nodep->exprp()->dtypep(); + AstNodeDType* subDTypep = pinDTypep; + int pinwidth = pinDTypep->width(); + int conwidth = conDTypep->width(); + if (conDTypep == pinDTypep // If match, we're golden + || similarDTypeRecurse(conDTypep, pinDTypep)) { + userIterateAndNext(nodep->exprp(), WidthVP(subDTypep, FINAL).p()); + } + else if (m_cellRangep) { + int numInsts = m_cellRangep->elementsConst(); + if (conwidth == pinwidth) { + // Arrayed instants: widths match so connect to each instance + subDTypep = conDTypep; // = same expr dtype + } else if (conwidth == numInsts*pinwidth) { + // Arrayed instants: one bit for each of the instants (each + // assign is 1 pinwidth wide) + subDTypep = conDTypep; // = same expr dtype (but numInst*pin_dtype) + } else { + // Must be a error according to spec + // (Because we need to know if to connect to one or all instants) + nodep->v3error(ucfirst(nodep->prettyOperatorName())<<" as part of a module instance array" + <<" requires "<exprp()->prettyTypeName() + <<" generates "<exprp(), WidthVP(subDTypep, FINAL).p()); + } else { if (nodep->modVarp()->direction() == VDirection::REF) { nodep->v3error("Ref connection '"<modVarp()->prettyName()<<"'" <<" requires matching types;" @@ -2380,20 +2446,21 @@ private: <<" but connection is " <prettyTypeName()<<"."<modVarp()->isTristate()) { - if (pinwidth != conwidth) { - nodep->v3error("Unsupported: "<prettyOperatorName()) - <<" to inout signal requires "<exprp()->prettyTypeName() - <<" generates "<v3error("Unsupported: "<prettyOperatorName()) + <<" to inout signal requires "<exprp()->prettyTypeName() + <<" generates "<modVarp()->dtypep(); AstNodeDType* exprDTypep = nodep->exprp()->dtypep(); if ((VN_IS(modDTypep, IfaceRefDType) && !VN_IS(exprDTypep, IfaceRefDType)) || (VN_IS(exprDTypep, IfaceRefDType) && !VN_IS(modDTypep, IfaceRefDType))) { - nodep->v3error("Illegal "<prettyOperatorName()<<"," + nodep->v3error("Illegal "<prettyOperatorName()<<"," <<" mismatch between port which is" <<(VN_CAST(modDTypep, IfaceRefDType)?"":" not") <<" an interface," @@ -2402,96 +2469,99 @@ private: <<" an interface."); } - // TODO Simple dtype checking, should be a more general check + // TODO Simple dtype checking, should be a more general check AstNodeArrayDType* exprArrayp = VN_CAST(exprDTypep->skipRefp(), UnpackArrayDType); AstNodeArrayDType* modArrayp = VN_CAST(modDTypep->skipRefp(), UnpackArrayDType); - if (exprArrayp && modArrayp && VN_IS(exprArrayp->subDTypep()->skipRefp(), IfaceRefDType) - && exprArrayp->declRange().elements() != modArrayp->declRange().elements()) { - int exprSize = exprArrayp->declRange().elements(); - int modSize = modArrayp->declRange().elements(); - nodep->v3error("Illegal "<prettyOperatorName()<<"," - <<" mismatch between port which is an interface array of size "<skipRefp()<skipRefp()<v3error("Illegal "<prettyOperatorName()<<"," - <<" mismatch between port which is"<<(modArrayp?"":" not")<<" an array," - <<" and expression which is"<<(exprArrayp?"":" not")<<" an array."); - UINFO(1," Related lo: "<skipRefp()<skipRefp()<exprp(),FINAL,subDTypep); - } - } - //if (debug()) nodep->dumpTree(cout,"- PinOut: "); + if (exprArrayp && modArrayp + && VN_IS(exprArrayp->subDTypep()->skipRefp(), IfaceRefDType) + && exprArrayp->declRange().elements() != modArrayp->declRange().elements()) { + int exprSize = exprArrayp->declRange().elements(); + int modSize = modArrayp->declRange().elements(); + nodep->v3error("Illegal "<prettyOperatorName()<<"," + <<" mismatch between port which is an interface array of size " + <skipRefp()<skipRefp()<v3error("Illegal "<prettyOperatorName()<<"," + <<" mismatch between port which is"<<(modArrayp?"":" not")<<" an array," + <<" and expression which is"<<(exprArrayp?"":" not")<<" an array."); + UINFO(1," Related lo: "<skipRefp()<skipRefp()<exprp(), FINAL, subDTypep); + } + } + //if (debug()) nodep->dumpTree(cout, "- PinOut: "); } virtual void visit(AstCell* nodep) { - if (!m_paramsOnly) { + if (!m_paramsOnly) { if (VN_IS(nodep->modp(), NotFoundModule)) { - // We've resolved parameters and hit a module that we couldn't resolve. It's - // finally time to report it. - // Note only here in V3Width as this is first visitor after V3Dead. - nodep->v3error("Cannot find file containing module: "<modName()); - v3Global.opt.filePathLookedMsg(nodep->fileline(), nodep->modName()); - } - if (nodep->rangep()) { - m_cellRangep = nodep->rangep(); - userIterateAndNext(nodep->rangep(), WidthVP(SELF,BOTH).p()); - } - userIterateAndNext(nodep->pinsp(), NULL); - } - userIterateAndNext(nodep->paramsp(), NULL); - m_cellRangep = NULL; + // We've resolved parameters and hit a module that we couldn't resolve. It's + // finally time to report it. + // Note only here in V3Width as this is first visitor after V3Dead. + nodep->v3error("Cannot find file containing module: "<modName()); + v3Global.opt.filePathLookedMsg(nodep->fileline(), nodep->modName()); + } + if (nodep->rangep()) { + m_cellRangep = nodep->rangep(); + userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p()); + } + userIterateAndNext(nodep->pinsp(), NULL); + } + userIterateAndNext(nodep->paramsp(), NULL); + m_cellRangep = NULL; } virtual void visit(AstGatePin* nodep) { - if (m_vup->prelim()) { - userIterateAndNext(nodep->rangep(), WidthVP(SELF,BOTH).p()); - userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT,PRELIM).p()); - nodep->dtypeFrom(nodep->rangep()); - // Very much like like an pin - AstNodeDType* conDTypep = nodep->exprp()->dtypep(); - int numInsts = nodep->rangep()->elementsConst(); - int pinwidth = numInsts; - int conwidth = conDTypep->width(); - if (conwidth == 1 && pinwidth > 1) { // Multiple connections - AstNodeDType* subDTypep = nodep->findLogicDType(1,1, conDTypep->numeric()); - userIterateAndNext(nodep->exprp(), WidthVP(subDTypep,FINAL).p()); - AstNode* newp = new AstReplicate(nodep->fileline(), - nodep->exprp()->unlinkFrBack(), - numInsts); - nodep->replaceWith(newp); - } - else { - // Eliminating so pass down all of vup - userIterateAndNext(nodep->exprp(), m_vup); - nodep->replaceWith(nodep->exprp()->unlinkFrBack()); - } - pushDeletep(nodep); VL_DANGLING(nodep); - } + if (m_vup->prelim()) { + userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p()); + userIterateAndNext(nodep->exprp(), WidthVP(CONTEXT, PRELIM).p()); + nodep->dtypeFrom(nodep->rangep()); + // Very much like like an pin + AstNodeDType* conDTypep = nodep->exprp()->dtypep(); + int numInsts = nodep->rangep()->elementsConst(); + int pinwidth = numInsts; + int conwidth = conDTypep->width(); + if (conwidth == 1 && pinwidth > 1) { // Multiple connections + AstNodeDType* subDTypep = nodep->findLogicDType(1, 1, conDTypep->numeric()); + userIterateAndNext(nodep->exprp(), WidthVP(subDTypep, FINAL).p()); + AstNode* newp = new AstReplicate(nodep->fileline(), + nodep->exprp()->unlinkFrBack(), + numInsts); + nodep->replaceWith(newp); + } + else { + // Eliminating so pass down all of vup + userIterateAndNext(nodep->exprp(), m_vup); + nodep->replaceWith(nodep->exprp()->unlinkFrBack()); + } + pushDeletep(nodep); VL_DANGLING(nodep); + } } virtual void visit(AstNodeFTask* nodep) { - // Grab width from the output variable (if it's a function) - if (nodep->didWidth()) return; - if (nodep->doingWidth()) { - nodep->v3error("Unsupported: Recursive function or task call"); - nodep->dtypeSetLogicBool(); - nodep->didWidth(true); - return; - } - // Function hasn't been widthed, so make it so. - nodep->doingWidth(true); // Would use user1 etc, but V3Width called from too many places to spend a user + // Grab width from the output variable (if it's a function) + if (nodep->didWidth()) return; + if (nodep->doingWidth()) { + nodep->v3error("Unsupported: Recursive function or task call"); + nodep->dtypeSetLogicBool(); + nodep->didWidth(true); + return; + } + // Function hasn't been widthed, so make it so. + nodep->doingWidth(true); // Would use user1 etc, but V3Width called from too many places to spend a user m_ftaskp = nodep; - userIterateChildren(nodep, NULL); - if (nodep->fvarp()) { + userIterateChildren(nodep, NULL); + if (nodep->fvarp()) { m_funcp = VN_CAST(nodep, Func); - if (!m_funcp) nodep->v3fatalSrc("FTask with function variable, but isn't a function"); - nodep->dtypeFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() - } - nodep->didWidth(true); - nodep->doingWidth(false); - m_funcp = NULL; + if (!m_funcp) nodep->v3fatalSrc("FTask with function variable, but isn't a function"); + nodep->dtypeFrom(nodep->fvarp()); // Which will get it from fvarp()->dtypep() + } + nodep->didWidth(true); + nodep->doingWidth(false); + m_funcp = NULL; m_ftaskp = NULL; if (nodep->dpiImport() && !nodep->dpiOpenParent() @@ -2500,37 +2570,37 @@ private: } } virtual void visit(AstReturn* nodep) { - // IEEE: Assignment-like context - assertAtStatement(nodep); - if (!m_funcp) { - if (nodep->lhsp()) { // Return w/o value ok other places - nodep->v3error("Return with return value isn't underneath a function"); - } - } else { - if (nodep->lhsp()) { - // Function hasn't been widthed, so make it so. - nodep->dtypeFrom(m_funcp->fvarp()); - // AstPattern requires assignments to pass datatype on PRELIM - userIterateAndNext(nodep->lhsp(), WidthVP(nodep->dtypep(),PRELIM).p()); - iterateCheckAssign(nodep,"Return value",nodep->lhsp(),FINAL,nodep->dtypep()); - } - } + // IEEE: Assignment-like context + assertAtStatement(nodep); + if (!m_funcp) { + if (nodep->lhsp()) { // Return w/o value ok other places + nodep->v3error("Return with return value isn't underneath a function"); + } + } else { + if (nodep->lhsp()) { + // Function hasn't been widthed, so make it so. + nodep->dtypeFrom(m_funcp->fvarp()); + // AstPattern requires assignments to pass datatype on PRELIM + userIterateAndNext(nodep->lhsp(), WidthVP(nodep->dtypep(), PRELIM).p()); + iterateCheckAssign(nodep, "Return value", nodep->lhsp(), FINAL, nodep->dtypep()); + } + } } virtual void visit(AstFuncRef* nodep) { visit(VN_CAST(nodep, NodeFTaskRef)); - nodep->dtypeFrom(nodep->taskp()); - //if (debug()) nodep->dumpTree(cout," FuncOut: "); + nodep->dtypeFrom(nodep->taskp()); + //if (debug()) nodep->dumpTree(cout, " FuncOut: "); } virtual void visit(AstNodeFTaskRef* nodep) { - // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign - // Function hasn't been widthed, so make it so. - UINFO(5, " FTASKREF "<taskp()) nodep->v3fatalSrc("Unlinked"); - if (nodep->didWidth()) return; - userIterate(nodep->taskp(), NULL); - // - // And do the arguments to the task/function too + // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign + // Function hasn't been widthed, so make it so. + UINFO(5, " FTASKREF "<taskp()) nodep->v3fatalSrc("Unlinked"); + if (nodep->didWidth()) return; + userIterate(nodep->taskp(), NULL); + // + // And do the arguments to the task/function too do { reloop: V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); @@ -2548,7 +2618,8 @@ private: argp->unlinkFrBackWithNext(&handle); // Format + additional args, if any AstNode* argsp = NULL; while (AstArg* nextargp = VN_CAST(argp->nextp(), Arg)) { - argsp = AstNode::addNext(argsp, nextargp->exprp()->unlinkFrBackWithNext()); // Expression goes to SFormatF + argsp = AstNode::addNext( + argsp, nextargp->exprp()->unlinkFrBackWithNext()); // Expression goes to SFormatF nextargp->unlinkFrBack()->deleteTree(); // Remove the call's Arg wrapper } string format; @@ -2576,23 +2647,23 @@ private: pinp = newp; } // AstPattern requires assignments to pass datatype on PRELIM - userIterate(pinp, WidthVP(portp->dtypep(),PRELIM).p()); VL_DANGLING(pinp); + userIterate(pinp, WidthVP(portp->dtypep(), PRELIM).p()); VL_DANGLING(pinp); } } while (0); // Stage 2 { - V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); - for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { - AstVar* portp = it->first; - AstArg* argp = it->second; - AstNode* pinp = argp->exprp(); + V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); + for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { + AstVar* portp = it->first; + AstArg* argp = it->second; + AstNode* pinp = argp->exprp(); if (!pinp) continue; // Argument error we'll find later // Change data types based on above accept completion if (portp->isDouble()) { spliceCvtD(pinp); VL_DANGLING(pinp); - } - } - } + } + } + } // Stage 3 { V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); @@ -2601,8 +2672,9 @@ private: AstArg* argp = it->second; AstNode* pinp = argp->exprp(); if (!pinp) continue; // Argument error we'll find later - // Do PRELIM again, because above accept may have exited early due to node replacement - userIterate(pinp, WidthVP(portp->dtypep(),PRELIM).p()); + // Do PRELIM again, because above accept may have exited early + // due to node replacement + userIterate(pinp, WidthVP(portp->dtypep(), PRELIM).p()); } } // Cleanup any open arrays @@ -2634,206 +2706,212 @@ private: // (get an ASSIGN with EXTEND on the lhs instead of rhs) } if (!portp->basicp() || portp->basicp()->isOpaque()) { - userIterate(pinp, WidthVP(portp->dtypep(),FINAL).p()); + userIterate(pinp, WidthVP(portp->dtypep(), FINAL).p()); } else { - iterateCheckAssign(nodep,"Function Argument",pinp,FINAL,portp->dtypep()); + iterateCheckAssign(nodep, "Function Argument", pinp, FINAL, portp->dtypep()); } } } - nodep->didWidth(true); + nodep->didWidth(true); } virtual void visit(AstInitial* nodep) { - assertAtStatement(nodep); - m_initialp = nodep; - userIterateChildren(nodep, NULL); - m_initialp = NULL; + assertAtStatement(nodep); + m_initialp = nodep; + userIterateChildren(nodep, NULL); + m_initialp = NULL; } virtual void visit(AstNetlist* nodep) { - // Iterate modules backwards, in bottom-up order. That's faster - userIterateChildrenBackwards(nodep, NULL); + // Iterate modules backwards, in bottom-up order. That's faster + userIterateChildrenBackwards(nodep, NULL); } //-------------------- // Default virtual void visit(AstNodeMath* nodep) { - nodep->v3fatalSrc("Visit function missing? Widthed function missing for math node: "<v3fatalSrc("Visit function missing? Widthed function missing for math node: " + <v3fatalSrc("Visit function missing? Widthed expectation for this node: "<v3fatalSrc("Visit function missing? Widthed expectation for this node: " + <prelim()) { // First stage evaluation - nodep->dtypeSetDouble(); - AstNodeDType* subDTypep = nodep->findLogicDType(64,64, AstNumeric::UNSIGNED); - // Self-determined operand - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,PRELIM).p()); - iterateCheck(nodep,"LHS",nodep->lhsp(),SELF,FINAL,subDTypep,EXTEND_EXP); - } + // CALLER: AstBitsToRealD + // Real: Output real + // LHS presumed self-determined, then coerced to real + if (m_vup->prelim()) { // First stage evaluation + nodep->dtypeSetDouble(); + AstNodeDType* subDTypep = nodep->findLogicDType(64, 64, AstNumeric::UNSIGNED); + // Self-determined operand + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p()); + iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP); + } } void visit_Or_Ls32(AstNodeUniop* nodep) { - // CALLER: AstIToRD - // Real: Output real - // LHS presumed self-determined, then coerced to real - if (m_vup->prelim()) { // First stage evaluation - nodep->dtypeSetDouble(); - AstNodeDType* subDTypep = nodep->findLogicDType(32,32, AstNumeric::SIGNED); - // Self-determined operand - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,PRELIM).p()); - iterateCheck(nodep,"LHS",nodep->lhsp(),SELF,FINAL,subDTypep,EXTEND_EXP); - } + // CALLER: AstIToRD + // Real: Output real + // LHS presumed self-determined, then coerced to real + if (m_vup->prelim()) { // First stage evaluation + nodep->dtypeSetDouble(); + AstNodeDType* subDTypep = nodep->findLogicDType(32, 32, AstNumeric::SIGNED); + // Self-determined operand + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p()); + iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP); + } } void visit_Os32_Lr(AstNodeUniop* nodep) { - // CALLER: RToI - // Real: LHS real - // LHS presumed self-determined, then coerced to real - if (m_vup->prelim()) { // First stage evaluation - iterateCheckReal(nodep,"LHS",nodep->lhsp(),BOTH); - nodep->dtypeSetSigned32(); - } + // CALLER: RToI + // Real: LHS real + // LHS presumed self-determined, then coerced to real + if (m_vup->prelim()) { // First stage evaluation + iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); + nodep->dtypeSetSigned32(); + } } void visit_Ou64_Lr(AstNodeUniop* nodep) { - // CALLER: RealToBits - // Real: LHS real - // LHS presumed self-determined, then coerced to real - if (m_vup->prelim()) { // First stage evaluation - iterateCheckReal(nodep,"LHS",nodep->lhsp(),BOTH); - nodep->dtypeSetUInt64(); - } + // CALLER: RealToBits + // Real: LHS real + // LHS presumed self-determined, then coerced to real + if (m_vup->prelim()) { // First stage evaluation + iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); + nodep->dtypeSetUInt64(); + } } void visit_log_not(AstNode* nodep) { - // CALLER: LogNot - // Width-check: lhs 1 bit - // Real: Allowed; implicitly compares with zero - // We calculate the width of the UNDER expression. - // We then check its width to see if it's legal, and edit if not - // We finally set the width of our output - // IEEE-2012: Table 11-21 and 11.8.1 (same as RedAnd): - // LHS is self-determined - // Width: 1 bit out - // Sign: unsigned out (11.8.1) - if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); - if (m_vup->prelim()) { - iterateCheckBool(nodep,"LHS",nodep->op1p(),BOTH); - nodep->dtypeSetLogicBool(); - } + // CALLER: LogNot + // Width-check: lhs 1 bit + // Real: Allowed; implicitly compares with zero + // We calculate the width of the UNDER expression. + // We then check its width to see if it's legal, and edit if not + // We finally set the width of our output + // IEEE-2012: Table 11-21 and 11.8.1 (same as RedAnd): + // LHS is self-determined + // Width: 1 bit out + // Sign: unsigned out (11.8.1) + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (m_vup->prelim()) { + iterateCheckBool(nodep, "LHS", nodep->op1p(), BOTH); + nodep->dtypeSetLogicBool(); + } } void visit_log_and_or(AstNodeBiop* nodep) { - // CALLER: LogAnd, LogOr, LogIf, LogIff - // Widths: 1 bit out, lhs 1 bit, rhs 1 bit - // IEEE-2012 Table 11-21: - // LHS is self-determined - // RHS is self-determined - if (m_vup->prelim()) { - iterateCheckBool(nodep,"LHS",nodep->lhsp(),BOTH); - iterateCheckBool(nodep,"RHS",nodep->rhsp(),BOTH); - nodep->dtypeSetLogicBool(); - } + // CALLER: LogAnd, LogOr, LogIf, LogIff + // Widths: 1 bit out, lhs 1 bit, rhs 1 bit + // IEEE-2012 Table 11-21: + // LHS is self-determined + // RHS is self-determined + if (m_vup->prelim()) { + iterateCheckBool(nodep, "LHS", nodep->lhsp(), BOTH); + iterateCheckBool(nodep, "RHS", nodep->rhsp(), BOTH); + nodep->dtypeSetLogicBool(); + } } void visit_red_and_or(AstNodeUniop* nodep) { - // CALLER: RedAnd, RedOr, ... - // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE) - // IEEE-2012: Table 11-21 and 11.8.1: - // LHS is self-determined - // Width: 1 bit out - // Sign: unsigned out (11.8.1) - if (m_vup->prelim()) { - iterateCheckSizedSelf(nodep,"LHS",nodep->lhsp(),SELF,BOTH); - nodep->dtypeSetLogicBool(); - } + // CALLER: RedAnd, RedOr, ... + // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE) + // IEEE-2012: Table 11-21 and 11.8.1: + // LHS is self-determined + // Width: 1 bit out + // Sign: unsigned out (11.8.1) + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); + nodep->dtypeSetLogicBool(); + } } void visit_red_unknown(AstNodeUniop* nodep) { - // CALLER: IsUnknown - // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE) - // IEEE-2012: Table 11-21 and 11.8.1: - // LHS is self-determined - // Width: 1 bit out - // Sign: unsigned out (11.8.1) - if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,BOTH).p()); - nodep->dtypeSetLogicBool(); - } + // CALLER: IsUnknown + // Signed: Output unsigned, Lhs/Rhs/etc non-real (presumed, not in IEEE) + // IEEE-2012: Table 11-21 and 11.8.1: + // LHS is self-determined + // Width: 1 bit out + // Sign: unsigned out (11.8.1) + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + nodep->dtypeSetLogicBool(); + } } void visit_cmp_eq_gt(AstNodeBiop* nodep, bool realok) { - // CALLER: AstEq, AstGt, ..., AstLtS - // Real allowed if and only if real_lhs set - // See IEEE-2012 11.4.4, and 11.8.1: - // Widths: 1 bit out, width is max of LHS or RHS - // Sign: signed compare (not output) if both signed, compare is signed, width mismatches sign extend - // else, compare is unsigned, **zero-extends** - // Real: If either real, other side becomes real and real compare - // TODO: chandle/class handle/iface handle: WildEq/WildNeq same as Eq/Neq - // TODO: chandle/class handle/iface handle only allowed to self-compare or against null - // TODO: chandle/class handle/iface handle no relational compares - if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); - if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT,PRELIM).p()); - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); - if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { - if (!realok) nodep->v3error("Real not allowed as operand to in ?== operator"); - if (AstNodeBiop* newp=replaceWithDVersion(nodep)) { VL_DANGLING(nodep); - nodep = newp; // Process new node instead - iterateCheckReal(nodep,"LHS",nodep->lhsp(),FINAL); - iterateCheckReal(nodep,"RHS",nodep->rhsp(),FINAL); - } - } else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) { - if (AstNodeBiop* newp=replaceWithNVersion(nodep)) { VL_DANGLING(nodep); - nodep = newp; // Process new node instead - iterateCheckString(nodep,"LHS",nodep->lhsp(),FINAL); - iterateCheckString(nodep,"RHS",nodep->rhsp(),FINAL); - } - } else { - bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned(); - if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, signedFl)) { VL_DANGLING(nodep); - nodep = newp; // Process new node instead - } + // CALLER: AstEq, AstGt, ..., AstLtS + // Real allowed if and only if real_lhs set + // See IEEE-2012 11.4.4, and 11.8.1: + // Widths: 1 bit out, width is max of LHS or RHS + // Sign: signed compare (not output) if both signed, compare is signed, + // width mismatches sign extend + // else, compare is unsigned, **zero-extends** + // Real: If either real, other side becomes real and real compare + // TODO: chandle/class handle/iface handle: WildEq/WildNeq same as Eq/Neq + // TODO: chandle/class handle/iface handle only allowed to self-compare or against null + // TODO: chandle/class handle/iface handle no relational compares + if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p()); + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); + if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { + if (!realok) nodep->v3error("Real not allowed as operand to in ?== operator"); + if (AstNodeBiop* newp = replaceWithDVersion(nodep)) { VL_DANGLING(nodep); + nodep = newp; // Process new node instead + iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL); + iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL); + } + } else if (nodep->lhsp()->isString() || nodep->rhsp()->isString()) { + if (AstNodeBiop* newp = replaceWithNVersion(nodep)) { VL_DANGLING(nodep); + nodep = newp; // Process new node instead + iterateCheckString(nodep, "LHS", nodep->lhsp(), FINAL); + iterateCheckString(nodep, "RHS", nodep->rhsp(), FINAL); + } + } else { + bool signedFl = nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned(); + if (AstNodeBiop* newp = replaceWithUOrSVersion(nodep, signedFl)) { VL_DANGLING(nodep); + nodep = newp; // Process new node instead + } int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width()); int ewidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); - AstNodeDType* subDTypep = nodep->findLogicDType(width, ewidth, - AstNumeric::fromBool(signedFl)); - iterateCheck(nodep,"LHS",nodep->lhsp(),CONTEXT,FINAL,subDTypep,signedFl?EXTEND_LHS:EXTEND_ZERO); - iterateCheck(nodep,"RHS",nodep->rhsp(),CONTEXT,FINAL,subDTypep,signedFl?EXTEND_LHS:EXTEND_ZERO); - } - nodep->dtypeSetLogicBool(); - } + AstNodeDType* subDTypep = nodep->findLogicDType(width, ewidth, + AstNumeric::fromBool(signedFl)); + iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, + signedFl ? EXTEND_LHS:EXTEND_ZERO); + iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT, FINAL, subDTypep, + signedFl ? EXTEND_LHS:EXTEND_ZERO); + } + nodep->dtypeSetLogicBool(); + } } void visit_cmp_real(AstNodeBiop* nodep) { - // CALLER: EqD, LtD - // Widths: 1 bit out, lhs width == rhs width - // Signed compare (not output) if both sides signed - // Real if and only if real_allow set - // IEEE, 11.4.4: relational compares (<,>,<=,>=,==,===,!=,!==) use "zero padding" on unsigned - if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); - if (m_vup->prelim()) { - // See similar handling in visit_cmp_eq_gt where created - iterateCheckReal(nodep,"LHS",nodep->lhsp(),BOTH); - iterateCheckReal(nodep,"RHS",nodep->rhsp(),BOTH); - nodep->dtypeSetLogicBool(); - } + // CALLER: EqD, LtD + // Widths: 1 bit out, lhs width == rhs width + // Signed compare (not output) if both sides signed + // Real if and only if real_allow set + // IEEE, 11.4.4: relational compares (<,>,<=,>=,==,===,!=,!==) use + // "zero padding" on unsigned + if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); + if (m_vup->prelim()) { + // See similar handling in visit_cmp_eq_gt where created + iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); + iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH); + nodep->dtypeSetLogicBool(); + } } void visit_cmp_string(AstNodeBiop* nodep) { - // CALLER: EqN, LtN - // Widths: 1 bit out, lhs width == rhs width - // String compare (not output) - // Real if and only if real_allow set - if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); - if (m_vup->prelim()) { - // See similar handling in visit_cmp_eq_gt where created - iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH); - iterateCheckString(nodep,"RHS",nodep->rhsp(),BOTH); - nodep->dtypeSetLogicBool(); - } + // CALLER: EqN, LtN + // Widths: 1 bit out, lhs width == rhs width + // String compare (not output) + // Real if and only if real_allow set + if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); + if (m_vup->prelim()) { + // See similar handling in visit_cmp_eq_gt where created + iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); + iterateCheckString(nodep, "RHS", nodep->rhsp(), BOTH); + nodep->dtypeSetLogicBool(); + } } void visit_Os32_string(AstNodeUniop* nodep) { // CALLER: LenN @@ -2841,353 +2919,354 @@ private: if (!nodep->lhsp()) nodep->v3fatalSrc("For unary ops only!"); if (m_vup->prelim()) { // See similar handling in visit_cmp_eq_gt where created - iterateCheckString(nodep,"LHS",nodep->lhsp(),BOTH); + iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH); nodep->dtypeSetSigned32(); } } void visit_negate_not(AstNodeUniop* nodep, bool real_ok) { - // CALLER: (real_ok=false) Not - // CALLER: (real_ok=true) Negate - // Signed: From lhs - // IEEE-2012 Table 11-21: - // Widths: out width = lhs width - if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); - if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT,PRELIM).p()); - if (!real_ok) checkCvtUS(nodep->lhsp()); - } - if (real_ok && nodep->lhsp()->isDouble()) { - spliceCvtD(nodep->lhsp()); - if (AstNodeUniop* newp=replaceWithDVersion(nodep)) { VL_DANGLING(nodep); - nodep = newp; // Process new node instead - iterateCheckReal(nodep,"LHS",nodep->lhsp(),BOTH); - nodep->dtypeSetDouble(); - return; - } - } else { - // Note there aren't yet uniops that need version changes - // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned()) - } - if (m_vup->prelim()) { - nodep->dtypeFrom(nodep->lhsp()); - } - if (m_vup->final()) { - AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); - nodep->dtypep(expDTypep); // Propagate expression type to negation - AstNodeDType* subDTypep = expDTypep; - iterateCheck(nodep,"LHS",nodep->lhsp(),CONTEXT,FINAL,subDTypep,EXTEND_EXP); - } + // CALLER: (real_ok=false) Not + // CALLER: (real_ok=true) Negate + // Signed: From lhs + // IEEE-2012 Table 11-21: + // Widths: out width = lhs width + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p()); + if (!real_ok) checkCvtUS(nodep->lhsp()); + } + if (real_ok && nodep->lhsp()->isDouble()) { + spliceCvtD(nodep->lhsp()); + if (AstNodeUniop* newp = replaceWithDVersion(nodep)) { VL_DANGLING(nodep); + nodep = newp; // Process new node instead + iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); + nodep->dtypeSetDouble(); + return; + } + } else { + // Note there aren't yet uniops that need version changes + // So no need to call replaceWithUOrSVersion(nodep, nodep->isSigned()) + } + if (m_vup->prelim()) { + nodep->dtypeFrom(nodep->lhsp()); + } + if (m_vup->final()) { + AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); + nodep->dtypep(expDTypep); // Propagate expression type to negation + AstNodeDType* subDTypep = expDTypep; + iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP); + } } void visit_signed_unsigned(AstNodeUniop* nodep, AstNumeric rs_out) { - // CALLER: Signed, Unsigned - // Width: lhs is self determined width - // See IEEE-2012 6.24.1: - // Width: Returns packed array, of size $bits(expression). - // Sign: Output sign is as specified by operation - // TODO: Type: Two-state if input is two-state, else four-state - if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); - if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,PRELIM).p()); - checkCvtUS(nodep->lhsp()); - int width = nodep->lhsp()->width(); - AstNodeDType* expDTypep = nodep->findLogicDType(width,width,rs_out); - nodep->dtypep(expDTypep); - AstNodeDType* subDTypep = expDTypep; - // The child's width is self determined - iterateCheck(nodep,"LHS",nodep->lhsp(),SELF,FINAL,subDTypep,EXTEND_EXP); - } + // CALLER: Signed, Unsigned + // Width: lhs is self determined width + // See IEEE-2012 6.24.1: + // Width: Returns packed array, of size $bits(expression). + // Sign: Output sign is as specified by operation + // TODO: Type: Two-state if input is two-state, else four-state + if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!"); + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p()); + checkCvtUS(nodep->lhsp()); + int width = nodep->lhsp()->width(); + AstNodeDType* expDTypep = nodep->findLogicDType(width, width, rs_out); + nodep->dtypep(expDTypep); + AstNodeDType* subDTypep = expDTypep; + // The child's width is self determined + iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, subDTypep, EXTEND_EXP); + } } void visit_shift(AstNodeBiop* nodep) { - // CALLER: ShiftL, ShiftR, ShiftRS - // Widths: Output width from lhs, rhs<33 bits - // Signed: Output signed iff LHS signed; unary operator - // See IEEE 2012 11.4.10: - // RHS is self-determined. RHS is always treated as unsigned, has no effect on result. - iterate_shift_prelim(nodep); - nodep->dtypeChgSigned(nodep->lhsp()->isSigned()); - AstNodeBiop* newp = iterate_shift_final(nodep); VL_DANGLING(nodep); - if (newp) {} // Ununused + // CALLER: ShiftL, ShiftR, ShiftRS + // Widths: Output width from lhs, rhs<33 bits + // Signed: Output signed iff LHS signed; unary operator + // See IEEE 2012 11.4.10: + // RHS is self-determined. RHS is always treated as unsigned, has no effect on result. + iterate_shift_prelim(nodep); + nodep->dtypeChgSigned(nodep->lhsp()->isSigned()); + AstNodeBiop* newp = iterate_shift_final(nodep); VL_DANGLING(nodep); + if (newp) {} // Ununused } void iterate_shift_prelim(AstNodeBiop* nodep) { - // Shifts - // See IEEE-2012 11.4.10 and Table 11-21. - // RHS is self-determined. RHS is always treated as unsigned, has no effect on result. - if (m_vup->prelim()) { - userIterateAndNext(nodep->lhsp(), WidthVP(SELF,PRELIM).p()); - checkCvtUS(nodep->lhsp()); - iterateCheckSizedSelf(nodep,"RHS",nodep->rhsp(),SELF,BOTH); - nodep->dtypeFrom(nodep->lhsp()); - } + // Shifts + // See IEEE-2012 11.4.10 and Table 11-21. + // RHS is self-determined. RHS is always treated as unsigned, has no effect on result. + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, PRELIM).p()); + checkCvtUS(nodep->lhsp()); + iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH); + nodep->dtypeFrom(nodep->lhsp()); + } } AstNodeBiop* iterate_shift_final(AstNodeBiop* nodep) { - // Nodep maybe edited - if (m_vup->final()) { - AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); - AstNodeDType* subDTypep = expDTypep; - nodep->dtypeFrom(expDTypep); - // ShiftRS converts to ShiftR, but not vice-versa + // Nodep maybe edited + if (m_vup->final()) { + AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); + AstNodeDType* subDTypep = expDTypep; + nodep->dtypeFrom(expDTypep); + // ShiftRS converts to ShiftR, but not vice-versa if (VN_IS(nodep, ShiftRS)) { - if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, nodep->isSigned())) { VL_DANGLING(nodep); - nodep = newp; // Process new node instead - } - } - bool warnOn = true; - // No warning if "X = 1'b1<isSigned())) { VL_DANGLING(nodep); + nodep = newp; // Process new node instead + } + } + bool warnOn = true; + // No warning if "X = 1'b1<lhsp()->isOne() && VN_IS(nodep->backp(), NodeAssign)) warnOn = false; - iterateCheck(nodep,"LHS",nodep->lhsp(),CONTEXT,FINAL,subDTypep,EXTEND_EXP,warnOn); - if (nodep->rhsp()->width()>32) { + iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP, warnOn); + if (nodep->rhsp()->width()>32) { AstConst* shiftp = VN_CAST(nodep->rhsp(), Const); - if (shiftp && shiftp->num().mostSetBitP1() <= 32) { + if (shiftp && shiftp->num().mostSetBitP1() <= 32) { // If (number)<<96'h1, then make it into (number)<<32'h1 V3Number num (shiftp, 32, 0); num.opAssign(shiftp->num()); AstNode* shiftp = nodep->rhsp(); - nodep->rhsp()->replaceWith(new AstConst(shiftp->fileline(), num)); - shiftp->deleteTree(); VL_DANGLING(shiftp); - } - } - } - return nodep; // May edit + nodep->rhsp()->replaceWith(new AstConst(shiftp->fileline(), num)); + shiftp->deleteTree(); VL_DANGLING(shiftp); + } + } + } + return nodep; // May edit } void visit_boolmath_and_or(AstNodeBiop* nodep) { - // CALLER: And, Or, Xor, ... - // Lint widths: out width = lhs width = rhs width - // Signed: if lhs & rhs signed - // IEEE-2012 Table 11-21: - // Width: max(LHS, RHS) - if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); - // If errors are off, we need to follow the spec; thus we really need to do the max() - // because the rhs could be larger, and we need to have proper editing to get the widths - // to be the same for our operations. - if (m_vup->prelim()) { // First stage evaluation - // Determine expression widths only relying on what's in the subops - userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT,PRELIM).p()); - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); - checkCvtUS(nodep->lhsp()); - checkCvtUS(nodep->rhsp()); + // CALLER: And, Or, Xor, ... + // Lint widths: out width = lhs width = rhs width + // Signed: if lhs & rhs signed + // IEEE-2012 Table 11-21: + // Width: max(LHS, RHS) + if (!nodep->rhsp()) nodep->v3fatalSrc("For binary ops only!"); + // If errors are off, we need to follow the spec; thus we really need to do the max() + // because the rhs could be larger, and we need to have proper editing to get the widths + // to be the same for our operations. + if (m_vup->prelim()) { // First stage evaluation + // Determine expression widths only relying on what's in the subops + userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p()); + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width()); int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); - bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); - nodep->dtypeChgWidthSigned(width,mwidth,AstNumeric::fromBool(expSigned)); - } - if (m_vup->final()) { - AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); - AstNodeDType* subDTypep = expDTypep; - nodep->dtypeFrom(expDTypep); - // Error report and change sizes for suboperands of this node. - iterateCheck(nodep,"LHS",nodep->lhsp(),CONTEXT,FINAL,subDTypep,EXTEND_EXP); - iterateCheck(nodep,"RHS",nodep->rhsp(),CONTEXT,FINAL,subDTypep,EXTEND_EXP); - } + bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + nodep->dtypeChgWidthSigned(width, mwidth, AstNumeric::fromBool(expSigned)); + } + if (m_vup->final()) { + AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); + AstNodeDType* subDTypep = expDTypep; + nodep->dtypeFrom(expDTypep); + // Error report and change sizes for suboperands of this node. + iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP); + iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP); + } } void visit_add_sub_replace(AstNodeBiop* nodep, bool real_ok) { - // CALLER: (real_ok=false) AddS, SubS, ... - // CALLER: (real_ok=true) Add, Sub, ... - // Widths: out width = lhs width = rhs width - // Signed: Replace operator with signed operator, or signed to unsigned - // Real: Replace operator with real operator - // IEEE-2012 Table 11-21: - // Width: max(LHS, RHS) - // If errors are off, we need to follow the spec; thus we really need to do the max() - // because the rhs could be larger, and we need to have proper editing to get the widths - // to be the same for our operations. - // - //if (debug()>=9) { UINFO(0,"-rus "<dumpTree(cout,"-rusin-"); } - if (m_vup->prelim()) { // First stage evaluation - // Determine expression widths only relying on what's in the subops - userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT,PRELIM).p()); - userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT,PRELIM).p()); - if (!real_ok) { - checkCvtUS(nodep->lhsp()); - checkCvtUS(nodep->rhsp()); - } - if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { - spliceCvtD(nodep->lhsp()); - spliceCvtD(nodep->rhsp()); - if (AstNodeBiop* newp=replaceWithDVersion(nodep)) { VL_DANGLING(nodep); - nodep = newp; // Process new node instead - } - nodep->dtypeSetDouble(); - iterateCheckReal(nodep,"LHS",nodep->lhsp(),FINAL); - iterateCheckReal(nodep,"RHS",nodep->rhsp(),FINAL); - return; - } else { + // CALLER: (real_ok=false) AddS, SubS, ... + // CALLER: (real_ok=true) Add, Sub, ... + // Widths: out width = lhs width = rhs width + // Signed: Replace operator with signed operator, or signed to unsigned + // Real: Replace operator with real operator + // IEEE-2012 Table 11-21: + // Width: max(LHS, RHS) + // If errors are off, we need to follow the spec; thus we really need to do the max() + // because the rhs could be larger, and we need to have proper editing to get the widths + // to be the same for our operations. + // + //if (debug()>=9) { UINFO(0,"-rus "<dumpTree(cout, "-rusin-"); } + if (m_vup->prelim()) { // First stage evaluation + // Determine expression widths only relying on what's in the subops + userIterateAndNext(nodep->lhsp(), WidthVP(CONTEXT, PRELIM).p()); + userIterateAndNext(nodep->rhsp(), WidthVP(CONTEXT, PRELIM).p()); + if (!real_ok) { + checkCvtUS(nodep->lhsp()); + checkCvtUS(nodep->rhsp()); + } + if (nodep->lhsp()->isDouble() || nodep->rhsp()->isDouble()) { + spliceCvtD(nodep->lhsp()); + spliceCvtD(nodep->rhsp()); + if (AstNodeBiop* newp = replaceWithDVersion(nodep)) { VL_DANGLING(nodep); + nodep = newp; // Process new node instead + } + nodep->dtypeSetDouble(); + iterateCheckReal(nodep, "LHS", nodep->lhsp(), FINAL); + iterateCheckReal(nodep, "RHS", nodep->rhsp(), FINAL); + return; + } else { int width = std::max(nodep->lhsp()->width(), nodep->rhsp()->width()); int mwidth = std::max(nodep->lhsp()->widthMin(), nodep->rhsp()->widthMin()); - bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); - nodep->dtypeChgWidthSigned(width,mwidth,AstNumeric::fromBool(expSigned)); - } - } - if (m_vup->final()) { - // Parent's data type was computed using the max(upper, nodep->dtype) - AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); - AstNodeDType* subDTypep = expDTypep; - nodep->dtypeFrom(expDTypep); - // We don't use LHS && RHS -- unspecified language corner, see t_math_signed5 test - //bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); - if (AstNodeBiop* newp=replaceWithUOrSVersion(nodep, expDTypep->isSigned())) { VL_DANGLING(nodep); - nodep = newp; // Process new node instead - } - // Some warning suppressions - bool lhsWarn=true; bool rhsWarn = true; + bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + nodep->dtypeChgWidthSigned(width, mwidth, AstNumeric::fromBool(expSigned)); + } + } + if (m_vup->final()) { + // Parent's data type was computed using the max(upper, nodep->dtype) + AstNodeDType* expDTypep = m_vup->dtypeOverridep(nodep->dtypep()); + AstNodeDType* subDTypep = expDTypep; + nodep->dtypeFrom(expDTypep); + // We don't use LHS && RHS -- unspecified language corner, see t_math_signed5 test + //bool expSigned = (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()); + if (AstNodeBiop* newp = replaceWithUOrSVersion(nodep, expDTypep->isSigned())) { VL_DANGLING(nodep); + nodep = newp; // Process new node instead + } + // Some warning suppressions + bool lhsWarn = true; bool rhsWarn = true; if (VN_IS(nodep, Add) || VN_IS(nodep, Sub)) { - if (subDTypep->widthMin() == (nodep->lhsp()->widthMin()+1)) lhsWarn=false; // Warn if user wants extra bit from carry - if (subDTypep->widthMin() == (nodep->rhsp()->widthMin()+1)) rhsWarn=false; // Warn if user wants extra bit from carry + if (subDTypep->widthMin() == (nodep->lhsp()->widthMin()+1)) lhsWarn = false; // Warn if user wants extra bit from carry + if (subDTypep->widthMin() == (nodep->rhsp()->widthMin()+1)) rhsWarn = false; // Warn if user wants extra bit from carry } else if (VN_IS(nodep, Mul) || VN_IS(nodep, MulS)) { - if (subDTypep->widthMin() >= (nodep->lhsp()->widthMin())) lhsWarn=false; - if (subDTypep->widthMin() >= (nodep->rhsp()->widthMin())) rhsWarn=false; - } - // Final call, so make sure children check their sizes - // Error report and change sizes for suboperands of this node. - iterateCheck(nodep,"LHS",nodep->lhsp(),CONTEXT,FINAL,subDTypep,EXTEND_EXP,lhsWarn); - iterateCheck(nodep,"RHS",nodep->rhsp(),CONTEXT,FINAL,subDTypep,EXTEND_EXP,rhsWarn); - } - //if (debug()>=9) nodep->dumpTree(cout,"-rusou-"); + if (subDTypep->widthMin() >= (nodep->lhsp()->widthMin())) lhsWarn = false; + if (subDTypep->widthMin() >= (nodep->rhsp()->widthMin())) rhsWarn = false; + } + // Final call, so make sure children check their sizes + // Error report and change sizes for suboperands of this node. + iterateCheck(nodep, "LHS", nodep->lhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP, lhsWarn); + iterateCheck(nodep, "RHS", nodep->rhsp(), CONTEXT, FINAL, subDTypep, EXTEND_EXP, rhsWarn); + } + //if (debug()>=9) nodep->dumpTree(cout, "-rusou-"); } void visit_real_add_sub(AstNodeBiop* nodep) { - // CALLER: AddD, MulD, ... - if (m_vup->prelim()) { // First stage evaluation - // Note similar steps in visit_add_sub_replace promotion to double - iterateCheckReal(nodep,"LHS",nodep->lhsp(),BOTH); - iterateCheckReal(nodep,"RHS",nodep->rhsp(),BOTH); - nodep->dtypeSetDouble(); - } + // CALLER: AddD, MulD, ... + if (m_vup->prelim()) { // First stage evaluation + // Note similar steps in visit_add_sub_replace promotion to double + iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); + iterateCheckReal(nodep, "RHS", nodep->rhsp(), BOTH); + nodep->dtypeSetDouble(); + } } void visit_real_neg_ceil(AstNodeUniop* nodep) { - // CALLER: Negate, Ceil, Log, ... - if (m_vup->prelim()) { // First stage evaluation - // See alsl visit_negate_not conversion - iterateCheckReal(nodep,"LHS",nodep->lhsp(),BOTH); - nodep->dtypeSetDouble(); - } + // CALLER: Negate, Ceil, Log, ... + if (m_vup->prelim()) { // First stage evaluation + // See alsl visit_negate_not conversion + iterateCheckReal(nodep, "LHS", nodep->lhsp(), BOTH); + nodep->dtypeSetDouble(); + } } //---------------------------------------------------------------------- // LOWER LEVEL WIDTH METHODS (none iterate) bool widthBad(AstNode* nodep, AstNodeDType* expDTypep) { - int expWidth = expDTypep->width(); - int expWidthMin = expDTypep->widthMin(); + int expWidth = expDTypep->width(); + int expWidthMin = expDTypep->widthMin(); if (!nodep->dtypep()) nodep->v3fatalSrc("Under node "<prettyTypeName() <<" has no dtype?? Missing Visitor func?"); if (nodep->width()==0) nodep->v3fatalSrc("Under node "<prettyTypeName() <<" has no expected width?? Missing Visitor func?"); if (expWidth==0) nodep->v3fatalSrc("Node "<prettyTypeName() <<" has no expected width?? Missing Visitor func?"); - if (expWidthMin==0) expWidthMin = expWidth; - if (nodep->dtypep()->width() == expWidth) return false; - if (nodep->dtypep()->widthSized() && nodep->width() != expWidthMin) return true; - if (!nodep->dtypep()->widthSized() && nodep->widthMin() > expWidthMin) return true; - return false; + if (expWidthMin==0) expWidthMin = expWidth; + if (nodep->dtypep()->width() == expWidth) return false; + if (nodep->dtypep()->widthSized() && nodep->width() != expWidthMin) return true; + if (!nodep->dtypep()->widthSized() && nodep->widthMin() > expWidthMin) return true; + return false; } void fixWidthExtend(AstNode* nodep, AstNodeDType* expDTypep, ExtendRule extendRule) { - // Fix the width mismatch by extending or truncating bits - // *ONLY* call this from checkWidth() - // Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312; - // A(CONSTwide)+B becomes A(CONSTwidened)+B - // A(somewide)+B becomes A(TRUNC(somewide,width))+B - // or A(EXTRACT(somewide,width,0))+B - // Sign extension depends on the type of the *present* - // node, while the output dtype is the *expected* sign. - // It is reasonable to have sign extension with unsigned output, - // for example $unsigned(a)+$signed(b), the SIGNED(B) will be unsigned dtype out - UINFO(4," widthExtend_(r="<width(); - if (constp && !constp->num().isNegative()) { + int expWidth = expDTypep->width(); + if (constp && !constp->num().isNegative()) { // Save later constant propagation work, just right-size it. V3Number num (nodep, expWidth); num.opAssign(constp->num()); - num.isSigned(false); - AstNode* newp = new AstConst(nodep->fileline(), num); - constp->replaceWith(newp); - pushDeletep(constp); VL_DANGLING(constp); VL_DANGLING(nodep); - nodep=newp; - } else if (expWidthwidth()) { - // Trunc - Extract - AstNRelinker linker; - nodep->unlinkFrBack(&linker); - AstNode* newp = new AstSel(nodep->fileline(), nodep, 0, expWidth); - newp->didWidth(true); // Don't replace dtype with unsigned - linker.relink(newp); - nodep=newp; - } else { - // Extend - AstNRelinker linker; - nodep->unlinkFrBack(&linker); - bool doSigned = false; - switch (extendRule) { - case EXTEND_ZERO: doSigned = false; break; - case EXTEND_EXP: doSigned = nodep->isSigned() && expDTypep->isSigned(); break; - case EXTEND_LHS: doSigned = nodep->isSigned(); break; - default: nodep->v3fatalSrc("bad case"); - } - AstNode* newp = (doSigned - ? static_cast(new AstExtendS(nodep->fileline(), nodep)) - : static_cast(new AstExtend (nodep->fileline(), nodep))); - linker.relink(newp); - nodep=newp; - } - if (expDTypep->isDouble() && !nodep->isDouble()) { - // For AstVar init() among others - // TODO do all to-real and to-integer conversions in this function rather than in callers - AstNode* newp = spliceCvtD(nodep); - nodep = newp; - } - nodep->dtypeFrom(expDTypep); - UINFO(4," _new: "<fileline(), num); + constp->replaceWith(newp); + pushDeletep(constp); VL_DANGLING(constp); VL_DANGLING(nodep); + nodep = newp; + } else if (expWidthwidth()) { + // Trunc - Extract + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = new AstSel(nodep->fileline(), nodep, 0, expWidth); + newp->didWidth(true); // Don't replace dtype with unsigned + linker.relink(newp); + nodep = newp; + } else { + // Extend + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + bool doSigned = false; + switch (extendRule) { + case EXTEND_ZERO: doSigned = false; break; + case EXTEND_EXP: doSigned = nodep->isSigned() && expDTypep->isSigned(); break; + case EXTEND_LHS: doSigned = nodep->isSigned(); break; + default: nodep->v3fatalSrc("bad case"); + } + AstNode* newp = (doSigned + ? static_cast(new AstExtendS(nodep->fileline(), nodep)) + : static_cast(new AstExtend (nodep->fileline(), nodep))); + linker.relink(newp); + nodep = newp; + } + if (expDTypep->isDouble() && !nodep->isDouble()) { + // For AstVar init() among others + // TODO do all to-real and to-integer conversions in this function + // rather than in callers + AstNode* newp = spliceCvtD(nodep); + nodep = newp; + } + nodep->dtypeFrom(expDTypep); + UINFO(4," _new: "<num()); - num.isSigned(expSigned); - AstNode* newp = new AstConst(nodep->fileline(), num); - constp->replaceWith(newp); - constp->deleteTree(); VL_DANGLING(constp); VL_DANGLING(nodep); - nodep=newp; - } else { - AstNRelinker linker; - nodep->unlinkFrBack(&linker); - AstNode* newp = new AstRedOr(nodep->fileline(), nodep); - linker.relink(newp); - nodep=newp; - } - nodep->dtypeChgWidthSigned(expWidth,expWidth,AstNumeric::fromBool(expSigned)); - UINFO(4," _new: "<fileline(), num); + constp->replaceWith(newp); + constp->deleteTree(); VL_DANGLING(constp); VL_DANGLING(nodep); + nodep = newp; + } else { + AstNRelinker linker; + nodep->unlinkFrBack(&linker); + AstNode* newp = new AstRedOr(nodep->fileline(), nodep); + linker.relink(newp); + nodep = newp; + } + nodep->dtypeChgWidthSigned(expWidth, expWidth, AstNumeric::fromBool(expSigned)); + UINFO(4," _new: "<num().autoExtend() && !constp->num().sized() && constp->width()==1) { + if (constp->num().autoExtend() && !constp->num().sized() && constp->width()==1) { // Make it the proper size. Careful of proper extension of 0's/1's V3Number num (constp, expWidth); num.opRepl(constp->num(), expWidth); // {width{'1}} - AstNode* newp = new AstConst(constp->fileline(), num); - // Spec says always unsigned with proper width - if (debug()>4) constp->dumpTree(cout," fixAutoExtend_old: "); - if (debug()>4) newp->dumpTree(cout," _new: "); - constp->replaceWith(newp); - constp->deleteTree(); VL_DANGLING(constp); - // Tell caller the new constp, and that we changed it. - nodepr = newp; - return true; - } + AstNode* newp = new AstConst(constp->fileline(), num); + // Spec says always unsigned with proper width + if (debug()>4) constp->dumpTree(cout, " fixAutoExtend_old: "); + if (debug()>4) newp->dumpTree(cout, " _new: "); + constp->replaceWith(newp); + constp->deleteTree(); VL_DANGLING(constp); + // Tell caller the new constp, and that we changed it. + nodepr = newp; + return true; + } // X/Z also upper bit extend. In pre-SV only to 32-bits, SV forever. else if (!constp->num().sized() // Make it the proper size. Careful of proper extension of 0's/1's @@ -3198,8 +3277,8 @@ private: num.opExtendXZ(constp->num(), constp->width()); AstNode* newp = new AstConst(constp->fileline(), num); // Spec says always unsigned with proper width - if (debug()>4) constp->dumpTree(cout," fixUnszExtend_old: "); - if (debug()>4) newp->dumpTree(cout," _new: "); + if (debug()>4) constp->dumpTree(cout, " fixUnszExtend_old: "); + if (debug()>4) newp->dumpTree(cout, " _new: "); constp->replaceWith(newp); constp->deleteTree(); VL_DANGLING(constp); // Tell caller the new constp, and that we changed it. @@ -3211,15 +3290,15 @@ private: } bool similarDTypeRecurse(AstNodeDType* node1p, AstNodeDType* node2p) { - return node1p->skipRefp()->similarDType(node2p->skipRefp()); + return node1p->skipRefp()->similarDType(node2p->skipRefp()); } void iterateCheckFileDesc(AstNode* nodep, AstNode* underp, Stage stage) { - if (stage != BOTH) nodep->v3fatalSrc("Bad call"); - // underp may change as a result of replacement - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,PRELIM).p()); - AstNodeDType* expDTypep = underp->findUInt32DType(); - underp = iterateCheck(nodep,"file_descriptor",underp,SELF,FINAL,expDTypep,EXTEND_EXP); - if (underp) {} // cppcheck + if (stage != BOTH) nodep->v3fatalSrc("Bad call"); + // underp may change as a result of replacement + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p()); + AstNodeDType* expDTypep = underp->findUInt32DType(); + underp = iterateCheck(nodep, "file_descriptor", underp, SELF, FINAL, expDTypep, EXTEND_EXP); + if (underp) {} // cppcheck } void iterateCheckSigned32(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { // Coerce child to signed32 if not already. Child is self-determined @@ -3234,150 +3313,156 @@ private: if (underp) {} // cppcheck } void iterateCheckReal(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { - // Coerce child to real if not already. Child is self-determined - // e.g. nodep=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST) - // Don't need separate PRELIM and FINAL(double) calls; - // as if resolves to double, the BOTH correctly resolved double, - // otherwise self-determined was correct - // underp may change as a result of replacement - if (stage & PRELIM) { - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,PRELIM).p()); - } - if (stage & FINAL) { - AstNodeDType* expDTypep = nodep->findDoubleDType(); - underp = iterateCheck(nodep,side,underp,SELF,FINAL,expDTypep,EXTEND_EXP); - } - if (underp) {} // cppcheck + // Coerce child to real if not already. Child is self-determined + // e.g. nodep=ADDD, underp=ADD in ADDD(ADD(a,b), real-CONST) + // Don't need separate PRELIM and FINAL(double) calls; + // as if resolves to double, the BOTH correctly resolved double, + // otherwise self-determined was correct + // underp may change as a result of replacement + if (stage & PRELIM) { + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p()); + } + if (stage & FINAL) { + AstNodeDType* expDTypep = nodep->findDoubleDType(); + underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); + } + if (underp) {} // cppcheck } void iterateCheckString(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { - if (stage & PRELIM) { - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,PRELIM).p()); - } - if (stage & FINAL) { - AstNodeDType* expDTypep = nodep->findStringDType(); - underp = iterateCheck(nodep,side,underp,SELF,FINAL,expDTypep,EXTEND_EXP); - } - if (underp) {} // cppcheck + if (stage & PRELIM) { + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p()); + } + if (stage & FINAL) { + AstNodeDType* expDTypep = nodep->findStringDType(); + underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); + } + if (underp) {} // cppcheck } void iterateCheckSizedSelf(AstNode* nodep, const char* side, AstNode* underp, Determ determ, Stage stage) { - // Coerce child to any sized-number data type; child is self-determined i.e. isolated from expected type. - // e.g. nodep=CONCAT, underp=lhs in CONCAT(lhs,rhs) - if (determ != SELF) nodep->v3fatalSrc("Bad call"); - if (stage != FINAL && stage != BOTH) nodep->v3fatalSrc("Bad call"); - // underp may change as a result of replacement - if (stage & PRELIM) underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,PRELIM).p()); - underp = checkCvtUS(underp); - AstNodeDType* expDTypep = underp->dtypep(); - underp = iterateCheck(nodep,side,underp,SELF,FINAL,expDTypep,EXTEND_EXP); - if (underp) {} // cppcheck + // Coerce child to any sized-number data type; child is self-determined + // i.e. isolated from expected type. + // e.g. nodep=CONCAT, underp=lhs in CONCAT(lhs,rhs) + if (determ != SELF) nodep->v3fatalSrc("Bad call"); + if (stage != FINAL && stage != BOTH) nodep->v3fatalSrc("Bad call"); + // underp may change as a result of replacement + if (stage & PRELIM) underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, PRELIM).p()); + underp = checkCvtUS(underp); + AstNodeDType* expDTypep = underp->dtypep(); + underp = iterateCheck(nodep, side, underp, SELF, FINAL, expDTypep, EXTEND_EXP); + if (underp) {} // cppcheck } void iterateCheckAssign(AstNode* nodep, const char* side, - AstNode* rhsp, Stage stage, AstNodeDType* lhsDTypep) { - // Check using assignment-like context rules - //if (debug()) nodep->dumpTree(cout,"-checkass: "); - if (stage != FINAL) nodep->v3fatalSrc("Bad width call"); - // We iterate and size the RHS based on the result of RHS evaluation + AstNode* rhsp, Stage stage, AstNodeDType* lhsDTypep) { + // Check using assignment-like context rules + //if (debug()) nodep->dumpTree(cout, "-checkass: "); + if (stage != FINAL) nodep->v3fatalSrc("Bad width call"); + // We iterate and size the RHS based on the result of RHS evaluation bool lhsStream = (VN_IS(nodep, NodeAssign) && VN_IS(VN_CAST(nodep, NodeAssign)->lhsp(), NodeStream)); - rhsp = iterateCheck(nodep,side,rhsp,ASSIGN,FINAL,lhsDTypep,lhsStream?EXTEND_OFF:EXTEND_LHS); - //if (debug()) nodep->dumpTree(cout,"-checkout: "); - if (rhsp) {} // cppcheck + rhsp = iterateCheck(nodep, side, rhsp, ASSIGN, FINAL, lhsDTypep, + lhsStream ? EXTEND_OFF : EXTEND_LHS); + //if (debug()) nodep->dumpTree(cout, "-checkout: "); + if (rhsp) {} // cppcheck } void iterateCheckBool(AstNode* nodep, const char* side, AstNode* underp, Stage stage) { - if (stage != BOTH) nodep->v3fatalSrc("Bad call"); // Booleans always self-determined so do BOTH at once - // Underp is used in a self-determined but boolean context, reduce a multibit number to one bit - // stage is always BOTH so not passed as argument - // underp may change as a result of replacement - if (!underp) nodep->v3fatalSrc("Node has no type"); - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,BOTH).p()); - if (!underp || !underp->dtypep()) nodep->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it? - // - // For DOUBLE under a logical op, add implied test against zero, never a warning - if (underp && underp->isDouble()) { - UINFO(6," spliceCvtCmpD0: "<unlinkFrBack(&linker); - AstNode* newp = new AstNeqD(nodep->fileline(), underp, - new AstConst(nodep->fileline(), AstConst::RealDouble(), 0.0)); - linker.relink(newp); - } else if (!underp->dtypep()->basicp()) { - nodep->v3error("Logical Operator "<prettyTypeName() - <<" expects a non-complex data type on the "<replaceWith(new AstConst(nodep->fileline(), AstConst::LogicFalse())); - pushDeletep(underp); VL_DANGLING(underp); - } else { - bool bad = widthBad(underp,nodep->findLogicBoolDType()); - if (bad) { + if (stage != BOTH) nodep->v3fatalSrc("Bad call"); // Booleans always self-determined so do BOTH at once + // Underp is used in a self-determined but boolean context, reduce a + // multibit number to one bit + // stage is always BOTH so not passed as argument + // underp may change as a result of replacement + if (!underp) nodep->v3fatalSrc("Node has no type"); + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, BOTH).p()); + if (!underp || !underp->dtypep()) nodep->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it? + // + // For DOUBLE under a logical op, add implied test against zero, never a warning + if (underp && underp->isDouble()) { + UINFO(6," spliceCvtCmpD0: "<unlinkFrBack(&linker); + AstNode* newp = new AstNeqD( + nodep->fileline(), underp, + new AstConst(nodep->fileline(), AstConst::RealDouble(), 0.0)); + linker.relink(newp); + } else if (!underp->dtypep()->basicp()) { + nodep->v3error("Logical Operator "<prettyTypeName() + <<" expects a non-complex data type on the "<replaceWith(new AstConst(nodep->fileline(), AstConst::LogicFalse())); + pushDeletep(underp); VL_DANGLING(underp); + } else { + bool bad = widthBad(underp, nodep->findLogicBoolDType()); + if (bad) { { // if (warnOn), but not needed here - if (debug()>4) nodep->backp()->dumpTree(cout," back: "); - nodep->v3warn(WIDTH,"Logical Operator "<prettyTypeName() - <<" expects 1 bit on the "<prettyTypeName()<<" generates "<width() - <<(underp->width()!=underp->widthMin() - ?" or "+cvtToStr(underp->widthMin()):"") - <<" bits."); - } - fixWidthReduce(underp); VL_DANGLING(underp);//Changed - } - } + if (debug()>4) nodep->backp()->dumpTree(cout, " back: "); + nodep->v3warn(WIDTH, "Logical Operator "<prettyTypeName() + <<" expects 1 bit on the "<prettyTypeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + fixWidthReduce(underp); VL_DANGLING(underp); //Changed + } + } } AstNode* iterateCheck(AstNode* nodep, const char* side, AstNode* underp, Determ determ, Stage stage, AstNodeDType* expDTypep, ExtendRule extendRule, bool warnOn=true) { - // Perform data type check on underp, which is underneath nodep used for error reporting - // Returns the new underp - // Conversion to/from doubles and integers are before iterating. - if (stage != FINAL) nodep->v3fatalSrc("Bad state to iterateCheck"); - if (!underp || !underp->dtypep()) nodep->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it? - if (expDTypep == underp->dtypep()) { // Perfect - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,FINAL).p()); - } else if (expDTypep->isDouble() && underp->isDouble()) { // Also good - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,FINAL).p()); - } else if (expDTypep->isDouble() && !underp->isDouble()) { - underp = spliceCvtD(underp); - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,FINAL).p()); - } else if (!expDTypep->isDouble() && underp->isDouble()) { - underp = spliceCvtS(underp, true); // Round RHS - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,FINAL).p()); - } else if (expDTypep->isString() && !underp->dtypep()->isString()) { - underp = spliceCvtString(underp); - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,FINAL).p()); - } else { - AstBasicDType* expBasicp = expDTypep->basicp(); - AstBasicDType* underBasicp = underp->dtypep()->basicp(); - if (expBasicp && underBasicp) { - AstNodeDType* subDTypep = expDTypep; - // We then iterate FINAL before width fixes, as if the under-operation - // is e.g. an ADD, the ADD will auto-adjust to the proper data type - // or if another operation e.g. ATOI will not. - if (determ == SELF) { - underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF,FINAL).p()); - } else if (determ == ASSIGN) { - // IEEE: Signedness is solely determined by the RHS (underp), not by the LHS (expDTypep) - if (underp->isSigned() != subDTypep->isSigned() - || underp->width() != subDTypep->width()) { - subDTypep = nodep->findLogicDType(std::max(subDTypep->width(), underp->width()), - std::max(subDTypep->widthMin(), underp->widthMin()), - AstNumeric::fromBool(underp->isSigned())); - UINFO(9,"Assignment of opposite-signed RHS to LHS: "<v3fatalSrc("Bad state to iterateCheck"); + if (!underp || !underp->dtypep()) nodep->v3fatalSrc("Node has no type"); // Perhaps forgot to do a prelim visit on it? + if (expDTypep == underp->dtypep()) { // Perfect + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p()); + } else if (expDTypep->isDouble() && underp->isDouble()) { // Also good + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p()); + } else if (expDTypep->isDouble() && !underp->isDouble()) { + underp = spliceCvtD(underp); + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p()); + } else if (!expDTypep->isDouble() && underp->isDouble()) { + underp = spliceCvtS(underp, true); // Round RHS + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p()); + } else if (expDTypep->isString() && !underp->dtypep()->isString()) { + underp = spliceCvtString(underp); + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p()); + } else { + AstBasicDType* expBasicp = expDTypep->basicp(); + AstBasicDType* underBasicp = underp->dtypep()->basicp(); + if (expBasicp && underBasicp) { + AstNodeDType* subDTypep = expDTypep; + // We then iterate FINAL before width fixes, as if the under-operation + // is e.g. an ADD, the ADD will auto-adjust to the proper data type + // or if another operation e.g. ATOI will not. + if (determ == SELF) { + underp = userIterateSubtreeReturnEdits(underp, WidthVP(SELF, FINAL).p()); + } else if (determ == ASSIGN) { + // IEEE: Signedness is solely determined by the RHS + // (underp), not by the LHS (expDTypep) + if (underp->isSigned() != subDTypep->isSigned() + || underp->width() != subDTypep->width()) { + subDTypep = nodep->findLogicDType( + std::max(subDTypep->width(), underp->width()), + std::max(subDTypep->widthMin(), underp->widthMin()), + AstNumeric::fromBool(underp->isSigned())); + UINFO(9,"Assignment of opposite-signed RHS to LHS: "<basicp(); - AstBasicDType* underBasicp = underp->dtypep()->basicp(); - if (expDTypep == underp->dtypep()) { - return; // Same type must match - } else if (!expBasicp || expBasicp->isDouble() - || !underBasicp || underBasicp->isDouble()) { - // This is perhaps a v3fatalSrc as we should have checked the types before calling widthCheck, - // but we may have missed a non-sized check in earlier code, so might as well assume it is the users' fault. - nodep->v3error(ucfirst(nodep->prettyOperatorName())<<" expected non-complex non-double "<basicp(); + AstBasicDType* underBasicp = underp->dtypep()->basicp(); + if (expDTypep == underp->dtypep()) { + return; // Same type must match + } else if (!expBasicp || expBasicp->isDouble() + || !underBasicp || underBasicp->isDouble()) { + // This is perhaps a v3fatalSrc as we should have checked the types + // before calling widthCheck, but we may have missed a non-sized + // check in earlier code, so might as well assume it is the users' + // fault. + nodep->v3error(ucfirst(nodep->prettyOperatorName())<<" expected non-complex non-double "<v3fatalSrc("widthCheckSized should not be called on doubles/complex types"); + nodep->v3fatalSrc("widthCheckSized should not be called on doubles/complex types"); #endif - return; - } else { - int expWidth = expDTypep->width(); - int expWidthMin = expDTypep->widthMin(); - if (expWidthMin==0) expWidthMin = expWidth; - bool bad = widthBad(underp,expDTypep); - if ((bad || underp->width() != expWidth) - && fixAutoExtend(underp/*ref*/,expWidth)) { - underp=NULL; // Changes underp - return; - } + return; + } else { + int expWidth = expDTypep->width(); + int expWidthMin = expDTypep->widthMin(); + if (expWidthMin==0) expWidthMin = expWidth; + bool bad = widthBad(underp, expDTypep); + if ((bad || underp->width() != expWidth) + && fixAutoExtend(underp/*ref*/, expWidth)) { + underp = NULL; // Changes underp + return; + } if (VN_IS(underp, Const) && VN_CAST(underp, Const)->num().isFromString() - && expWidth > underp->width() - && (((expWidth - underp->width()) % 8) == 0)) { // At least it's character sized - // reg [31:0] == "foo" we'll consider probably fine. - // Maybe this should be a special warning? Not for now. - warnOn = false; - } + && expWidth > underp->width() + && (((expWidth - underp->width()) % 8) == 0)) { // At least it's character sized + // reg [31:0] == "foo" we'll consider probably fine. + // Maybe this should be a special warning? Not for now. + warnOn = false; + } if ((VN_IS(nodep, Add) && underp->width()==1 && underp->isOne()) - || (VN_IS(nodep, Sub) && underp->width()==1 && underp->isOne() && 0==strcmp(side,"RHS"))) { - // "foo + 1'b1", or "foo - 1'b1" are very common, people assume they extend correctly - warnOn = false; - } - if (bad && warnOn) { - if (debug()>4) nodep->backp()->dumpTree(cout," back: "); - nodep->v3warn(WIDTH,ucfirst(nodep->prettyOperatorName()) - <<" expects "<prettyTypeName()<<" generates "<width() - <<(underp->width()!=underp->widthMin() - ?" or "+cvtToStr(underp->widthMin()):"") - <<" bits."); - } - if (bad || underp->width()!=expWidth) { - // If we're in an NodeAssign, don't truncate the RHS if the LHS is - // a NodeStream. The streaming operator changes the rules regarding - // which bits to truncate. + || (VN_IS(nodep, Sub) && underp->width()==1 && underp->isOne() + && 0==strcmp(side, "RHS"))) { + // "foo + 1'b1", or "foo - 1'b1" are very common, people assume + // they extend correctly + warnOn = false; + } + if (bad && warnOn) { + if (debug()>4) nodep->backp()->dumpTree(cout, " back: "); + nodep->v3warn(WIDTH, ucfirst(nodep->prettyOperatorName()) + <<" expects "<prettyTypeName()<<" generates "<width() + <<(underp->width()!=underp->widthMin() + ?" or "+cvtToStr(underp->widthMin()):"") + <<" bits."); + } + if (bad || underp->width()!=expWidth) { + // If we're in an NodeAssign, don't truncate the RHS if the LHS is + // a NodeStream. The streaming operator changes the rules regarding + // which bits to truncate. AstNodeAssign* assignp = VN_CAST(nodep, NodeAssign); AstPin* pinp = VN_CAST(nodep, Pin); if (assignp && VN_IS(assignp->lhsp(), NodeStream)) { } else if (pinp && pinp->modVarp()->direction() != VDirection::INPUT) { // V3Inst::pinReconnectSimple must deal UINFO(5,"pinInSizeMismatch: "<isDouble()) { - nodep->v3error("Expected integral (non-real) input to "<backp()->prettyTypeName()); - nodep = spliceCvtS(nodep, true); - } - return nodep; + if (nodep && nodep->isDouble()) { + nodep->v3error("Expected integral (non-real) input to " + <backp()->prettyTypeName()); + nodep = spliceCvtS(nodep, true); + } + return nodep; } AstNode* spliceCvtD(AstNode* nodep) { - // For integer used in REAL context, convert to real - // We don't warn here, "2.0 * 2" is common and reasonable - if (nodep && !nodep->isDouble()) { - UINFO(6," spliceCvtD: "<unlinkFrBack(&linker); - AstNode* newp = new AstIToRD(nodep->fileline(), nodep); - linker.relink(newp); - return newp; - } else { - return nodep; - } + // For integer used in REAL context, convert to real + // We don't warn here, "2.0 * 2" is common and reasonable + if (nodep && !nodep->isDouble()) { + UINFO(6," spliceCvtD: "<unlinkFrBack(&linker); + AstNode* newp = new AstIToRD(nodep->fileline(), nodep); + linker.relink(newp); + return newp; + } else { + return nodep; + } } AstNode* spliceCvtS(AstNode* nodep, bool warnOn) { - // IEEE-2012 11.8.1: Signed: Type coercion creates signed - // 11.8.2: Argument to convert is self-determined - if (nodep && nodep->isDouble()) { - UINFO(6," spliceCvtS: "<unlinkFrBack(&linker); - if (warnOn) nodep->v3warn(REALCVT,"Implicit conversion of real to integer"); - AstNode* newp = new AstRToIRoundS(nodep->fileline(), nodep); - linker.relink(newp); - return newp; - } else { - return nodep; - } + // IEEE-2012 11.8.1: Signed: Type coercion creates signed + // 11.8.2: Argument to convert is self-determined + if (nodep && nodep->isDouble()) { + UINFO(6," spliceCvtS: "<unlinkFrBack(&linker); + if (warnOn) nodep->v3warn(REALCVT, "Implicit conversion of real to integer"); + AstNode* newp = new AstRToIRoundS(nodep->fileline(), nodep); + linker.relink(newp); + return newp; + } else { + return nodep; + } } AstNode* spliceCvtString(AstNode* nodep) { - // IEEE-2012 11.8.1: Signed: Type coercion creates signed - // 11.8.2: Argument to convert is self-determined - if (nodep && !nodep->dtypep()->basicp()->isString()) { - UINFO(6," spliceCvtString: "<unlinkFrBack(&linker); - AstNode* newp = new AstCvtPackString(nodep->fileline(), nodep); - linker.relink(newp); - return newp; - } else { - return nodep; - } + // IEEE-2012 11.8.1: Signed: Type coercion creates signed + // 11.8.2: Argument to convert is self-determined + if (nodep && !nodep->dtypep()->basicp()->isString()) { + UINFO(6," spliceCvtString: "<unlinkFrBack(&linker); + AstNode* newp = new AstCvtPackString(nodep->fileline(), nodep); + linker.relink(newp); + return newp; + } else { + return nodep; + } } AstNodeBiop* replaceWithUOrSVersion(AstNodeBiop* nodep, bool signedFlavorNeeded) { - // Given a signed/unsigned node type, create the opposite type - // Return new node or NULL if nothing - if (signedFlavorNeeded == nodep->signedFlavor()) { - return NULL; - } - if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp()); - // To simplify callers, some node types don't need to change - switch (nodep->type()) { - case AstType::atEq: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; - case AstType::atNeq: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; - case AstType::atEqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; - case AstType::atNeqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; - case AstType::atAdd: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; - case AstType::atSub: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; - case AstType::atShiftL: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; - default: break; - } - FileLine* fl = nodep->fileline(); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNodeBiop* newp = NULL; - switch (nodep->type()) { - case AstType::atGt: newp = new AstGtS (fl,lhsp,rhsp); break; - case AstType::atGtS: newp = new AstGt (fl,lhsp,rhsp); break; - case AstType::atGte: newp = new AstGteS (fl,lhsp,rhsp); break; - case AstType::atGteS: newp = new AstGte (fl,lhsp,rhsp); break; - case AstType::atLt: newp = new AstLtS (fl,lhsp,rhsp); break; - case AstType::atLtS: newp = new AstLt (fl,lhsp,rhsp); break; - case AstType::atLte: newp = new AstLteS (fl,lhsp,rhsp); break; - case AstType::atLteS: newp = new AstLte (fl,lhsp,rhsp); break; - case AstType::atDiv: newp = new AstDivS (fl,lhsp,rhsp); break; - case AstType::atDivS: newp = new AstDiv (fl,lhsp,rhsp); break; - case AstType::atModDiv: newp = new AstModDivS (fl,lhsp,rhsp); break; - case AstType::atModDivS: newp = new AstModDiv (fl,lhsp,rhsp); break; - case AstType::atMul: newp = new AstMulS (fl,lhsp,rhsp); break; - case AstType::atMulS: newp = new AstMul (fl,lhsp,rhsp); break; - case AstType::atShiftR: newp = new AstShiftRS (fl,lhsp,rhsp); break; - case AstType::atShiftRS: newp = new AstShiftR (fl,lhsp,rhsp); break; - default: - nodep->v3fatalSrc("Node needs sign change, but bad case: "<replaceWith(newp); - newp->dtypeFrom(nodep); - pushDeletep(nodep); VL_DANGLING(nodep); - return newp; + // Given a signed/unsigned node type, create the opposite type + // Return new node or NULL if nothing + if (signedFlavorNeeded == nodep->signedFlavor()) { + return NULL; + } + if (!nodep->dtypep()) nodep->dtypeFrom(nodep->lhsp()); + // To simplify callers, some node types don't need to change + switch (nodep->type()) { + case AstType::atEq: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; + case AstType::atNeq: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; + case AstType::atEqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; + case AstType::atNeqCase: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; + case AstType::atAdd: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; + case AstType::atSub: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; + case AstType::atShiftL: nodep->dtypeChgSigned(signedFlavorNeeded); return NULL; + default: break; + } + FileLine* fl = nodep->fileline(); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNodeBiop* newp = NULL; + switch (nodep->type()) { + case AstType::atGt: newp = new AstGtS (fl, lhsp, rhsp); break; + case AstType::atGtS: newp = new AstGt (fl, lhsp, rhsp); break; + case AstType::atGte: newp = new AstGteS (fl, lhsp, rhsp); break; + case AstType::atGteS: newp = new AstGte (fl, lhsp, rhsp); break; + case AstType::atLt: newp = new AstLtS (fl, lhsp, rhsp); break; + case AstType::atLtS: newp = new AstLt (fl, lhsp, rhsp); break; + case AstType::atLte: newp = new AstLteS (fl, lhsp, rhsp); break; + case AstType::atLteS: newp = new AstLte (fl, lhsp, rhsp); break; + case AstType::atDiv: newp = new AstDivS (fl, lhsp, rhsp); break; + case AstType::atDivS: newp = new AstDiv (fl, lhsp, rhsp); break; + case AstType::atModDiv: newp = new AstModDivS (fl, lhsp, rhsp); break; + case AstType::atModDivS: newp = new AstModDiv (fl, lhsp, rhsp); break; + case AstType::atMul: newp = new AstMulS (fl, lhsp, rhsp); break; + case AstType::atMulS: newp = new AstMul (fl, lhsp, rhsp); break; + case AstType::atShiftR: newp = new AstShiftRS (fl, lhsp, rhsp); break; + case AstType::atShiftRS: newp = new AstShiftR (fl, lhsp, rhsp); break; + default: + nodep->v3fatalSrc("Node needs sign change, but bad case: "<replaceWith(newp); + newp->dtypeFrom(nodep); + pushDeletep(nodep); VL_DANGLING(nodep); + return newp; } AstNodeBiop* replaceWithDVersion(AstNodeBiop* nodep) { - // Given a signed/unsigned node type, create the opposite type - // Return new node or NULL if nothing - if (nodep->doubleFlavor()) { - return NULL; - } - FileLine* fl = nodep->fileline(); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNodeBiop* newp = NULL; - // No width change on output;... // All below have bool or double outputs - switch (nodep->type()) { - case AstType::atAdd: newp = new AstAddD (fl,lhsp,rhsp); break; - case AstType::atSub: newp = new AstSubD (fl,lhsp,rhsp); break; - case AstType::atPow: newp = new AstPowD (fl,lhsp,rhsp); break; - case AstType::atEq: case AstType::atEqCase: newp = new AstEqD (fl,lhsp,rhsp); break; - case AstType::atNeq: case AstType::atNeqCase: newp = new AstNeqD (fl,lhsp,rhsp); break; - case AstType::atGt: case AstType::atGtS: newp = new AstGtD (fl,lhsp,rhsp); break; - case AstType::atGte: case AstType::atGteS: newp = new AstGteD (fl,lhsp,rhsp); break; - case AstType::atLt: case AstType::atLtS: newp = new AstLtD (fl,lhsp,rhsp); break; - case AstType::atLte: case AstType::atLteS: newp = new AstLteD (fl,lhsp,rhsp); break; - case AstType::atDiv: case AstType::atDivS: newp = new AstDivD (fl,lhsp,rhsp); break; - case AstType::atMul: case AstType::atMulS: newp = new AstMulD (fl,lhsp,rhsp); break; - default: - nodep->v3fatalSrc("Node needs conversion to double, but bad case: "<replaceWith(newp); - // No width change; the default created type (bool or double) is correct - pushDeletep(nodep); VL_DANGLING(nodep); - return newp; + // Given a signed/unsigned node type, create the opposite type + // Return new node or NULL if nothing + if (nodep->doubleFlavor()) { + return NULL; + } + FileLine* fl = nodep->fileline(); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNodeBiop* newp = NULL; + // No width change on output;... // All below have bool or double outputs + switch (nodep->type()) { + case AstType::atAdd: newp = new AstAddD(fl, lhsp, rhsp); break; + case AstType::atSub: newp = new AstSubD(fl, lhsp, rhsp); break; + case AstType::atPow: newp = new AstPowD(fl, lhsp, rhsp); break; + case AstType::atEq: case AstType::atEqCase: newp = new AstEqD (fl, lhsp, rhsp); break; + case AstType::atNeq: case AstType::atNeqCase: newp = new AstNeqD(fl, lhsp, rhsp); break; + case AstType::atGt: case AstType::atGtS: newp = new AstGtD (fl, lhsp, rhsp); break; + case AstType::atGte: case AstType::atGteS: newp = new AstGteD(fl, lhsp, rhsp); break; + case AstType::atLt: case AstType::atLtS: newp = new AstLtD (fl, lhsp, rhsp); break; + case AstType::atLte: case AstType::atLteS: newp = new AstLteD(fl, lhsp, rhsp); break; + case AstType::atDiv: case AstType::atDivS: newp = new AstDivD(fl, lhsp, rhsp); break; + case AstType::atMul: case AstType::atMulS: newp = new AstMulD(fl, lhsp, rhsp); break; + default: + nodep->v3fatalSrc("Node needs conversion to double, but bad case: "<replaceWith(newp); + // No width change; the default created type (bool or double) is correct + pushDeletep(nodep); VL_DANGLING(nodep); + return newp; } AstNodeBiop* replaceWithNVersion(AstNodeBiop* nodep) { - // Given a signed/unsigned node type, replace with string version - // Return new node or NULL if nothing - if (nodep->stringFlavor()) { - return NULL; - } - FileLine* fl = nodep->fileline(); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNodeBiop* newp = NULL; - // No width change on output;... // All below have bool or double outputs - switch (nodep->type()) { - case AstType::atEq: case AstType::atEqCase: newp = new AstEqN (fl,lhsp,rhsp); break; - case AstType::atNeq: case AstType::atNeqCase: newp = new AstNeqN (fl,lhsp,rhsp); break; - case AstType::atGt: case AstType::atGtS: newp = new AstGtN (fl,lhsp,rhsp); break; - case AstType::atGte: case AstType::atGteS: newp = new AstGteN (fl,lhsp,rhsp); break; - case AstType::atLt: case AstType::atLtS: newp = new AstLtN (fl,lhsp,rhsp); break; - case AstType::atLte: case AstType::atLteS: newp = new AstLteN (fl,lhsp,rhsp); break; - default: - nodep->v3fatalSrc("Node needs conversion to string, but bad case: "<replaceWith(newp); - // No width change; the default created type (bool or string) is correct - pushDeletep(nodep); VL_DANGLING(nodep); - return newp; + // Given a signed/unsigned node type, replace with string version + // Return new node or NULL if nothing + if (nodep->stringFlavor()) { + return NULL; + } + FileLine* fl = nodep->fileline(); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNodeBiop* newp = NULL; + // No width change on output;... // All below have bool or double outputs + switch (nodep->type()) { + case AstType::atEq: case AstType::atEqCase: newp = new AstEqN (fl, lhsp, rhsp); break; + case AstType::atNeq: case AstType::atNeqCase: newp = new AstNeqN(fl, lhsp, rhsp); break; + case AstType::atGt: case AstType::atGtS: newp = new AstGtN (fl, lhsp, rhsp); break; + case AstType::atGte: case AstType::atGteS: newp = new AstGteN(fl, lhsp, rhsp); break; + case AstType::atLt: case AstType::atLtS: newp = new AstLtN (fl, lhsp, rhsp); break; + case AstType::atLte: case AstType::atLteS: newp = new AstLteN(fl, lhsp, rhsp); break; + default: + nodep->v3fatalSrc("Node needs conversion to string, but bad case: "<replaceWith(newp); + // No width change; the default created type (bool or string) is correct + pushDeletep(nodep); VL_DANGLING(nodep); + return newp; } AstNodeUniop* replaceWithDVersion(AstNodeUniop* nodep) { - // Given a signed/unsigned node type, create the opposite type - // Return new node or NULL if nothing - if (nodep->doubleFlavor()) { - return NULL; - } - FileLine* fl = nodep->fileline(); - AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); - AstNodeUniop* newp = NULL; - switch (nodep->type()) { - case AstType::atNegate: newp = new AstNegateD (fl,lhsp); break; - default: - nodep->v3fatalSrc("Node needs conversion to double, but bad case: "<replaceWith(newp); - newp->dtypeFrom(nodep); - pushDeletep(nodep); VL_DANGLING(nodep); - return newp; + // Given a signed/unsigned node type, create the opposite type + // Return new node or NULL if nothing + if (nodep->doubleFlavor()) { + return NULL; + } + FileLine* fl = nodep->fileline(); + AstNode* lhsp = nodep->lhsp()->unlinkFrBack(); + AstNodeUniop* newp = NULL; + switch (nodep->type()) { + case AstType::atNegate: newp = new AstNegateD(fl, lhsp); break; + default: + nodep->v3fatalSrc("Node needs conversion to double, but bad case: "<replaceWith(newp); + newp->dtypeFrom(nodep); + pushDeletep(nodep); VL_DANGLING(nodep); + return newp; } //---------------------------------------------------------------------- @@ -3661,230 +3751,235 @@ private: // METHODS - data types AstNodeDType* moveChildDTypeEdit(AstNode* nodep) { - // DTypes at parse time get added as a childDType to some node types such as AstVars. - // We move them to global scope, so removing/changing a variable won't lose the dtype. - AstNodeDType* dtnodep = nodep->getChildDTypep(); - if (!dtnodep) nodep->v3fatalSrc("Caller should check for NULL before calling moveChild"); - UINFO(9,"moveChildDTypeEdit "<unlinkFrBack(); // Make non-child - v3Global.rootp()->typeTablep()->addTypesp(dtnodep); - return dtnodep; + // DTypes at parse time get added as a childDType to some node types such as AstVars. + // We move them to global scope, so removing/changing a variable won't lose the dtype. + AstNodeDType* dtnodep = nodep->getChildDTypep(); + if (!dtnodep) nodep->v3fatalSrc("Caller should check for NULL before calling moveChild"); + UINFO(9,"moveChildDTypeEdit "<unlinkFrBack(); // Make non-child + v3Global.rootp()->typeTablep()->addTypesp(dtnodep); + return dtnodep; } AstNodeDType* iterateEditDTypep(AstNode* parentp, AstNodeDType* nodep) { - // Iterate into a data type to resolve that type. This process - // may eventually create a new data type, but not today - // it may make a new datatype, need subChildDType() to point to it; - // maybe we have user5p indicate a "replace me with" pointer. - // Need to be careful with "implicit" types though. - // - // Alternative is to have WidthVP return information. - // or have a call outside of normal visitor land. - // or have a m_return type (but need to return if width called multiple times) - if (!nodep) parentp->v3fatalSrc("Null dtype when widthing dtype"); - userIterate(nodep, NULL); - return nodep; + // Iterate into a data type to resolve that type. This process + // may eventually create a new data type, but not today + // it may make a new datatype, need subChildDType() to point to it; + // maybe we have user5p indicate a "replace me with" pointer. + // Need to be careful with "implicit" types though. + // + // Alternative is to have WidthVP return information. + // or have a call outside of normal visitor land. + // or have a m_return type (but need to return if width called multiple times) + if (!nodep) parentp->v3fatalSrc("Null dtype when widthing dtype"); + userIterate(nodep, NULL); + return nodep; } AstConst* dimensionValue(AstNodeDType* nodep, AstAttrType attrType, int dim) { - // Return the dimension value for the specified attribute and constant dimension - AstNodeDType* dtypep = nodep->skipRefp(); - VNumRange declRange; // ranged() set false - for (int i = 1; i <= dim; ++i) { - //UINFO(9, " dim at "<skipRefp(); + VNumRange declRange; // ranged() set false + for (int i = 1; i <= dim; ++i) { + //UINFO(9, " dim at "<declRange(); - if (isubDTypep()->skipRefp(); - continue; + declRange = adtypep->declRange(); + if (isubDTypep()->skipRefp(); + continue; } else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) { - declRange = adtypep->declRange(); - if (adtypep) {} // UNUSED - break; // Sub elements don't look like arrays and can't iterate into + declRange = adtypep->declRange(); + if (adtypep) {} // UNUSED + break; // Sub elements don't look like arrays and can't iterate into } else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) { - if (adtypep->isRanged()) declRange = adtypep->declRange(); - break; - } - break; - } - AstConst* valp = NULL; // If NULL, construct from val - int val = 0; - switch (attrType) { - case AstAttrType::DIM_BITS: { - int bits = 1; - while (dtypep) { - //UINFO(9, " bits at "<isRanged()) declRange = adtypep->declRange(); + break; + } + break; + } + AstConst* valp = NULL; // If NULL, construct from val + int val = 0; + switch (attrType) { + case AstAttrType::DIM_BITS: { + int bits = 1; + while (dtypep) { + //UINFO(9, " bits at "<declRange().elements(); - dtypep = adtypep->subDTypep()->skipRefp(); - continue; + bits *= adtypep->declRange().elements(); + dtypep = adtypep->subDTypep()->skipRefp(); + continue; } else if (AstNodeClassDType* adtypep = VN_CAST(dtypep, NodeClassDType)) { - bits *= adtypep->width(); - break; + bits *= adtypep->width(); + break; } else if (AstBasicDType* adtypep = VN_CAST(dtypep, BasicDType)) { - bits *= adtypep->width(); - break; - } - break; - } - if (dim == 0) { - val = 0; - } else if (dim == 1 && !declRange.ranged() && bits==1) { // $bits should be sane for non-arrays - val = nodep->width(); - } else { - val = bits; - } - break; } - case AstAttrType::DIM_HIGH: - val = !declRange.ranged() ? 0 : declRange.hi(); - break; - case AstAttrType::DIM_LEFT: - val = !declRange.ranged() ? 0 : declRange.left(); - break; - case AstAttrType::DIM_LOW: - val = !declRange.ranged() ? 0 : declRange.lo(); - break; - case AstAttrType::DIM_RIGHT: - val = !declRange.ranged() ? 0 : declRange.right(); - break; - case AstAttrType::DIM_INCREMENT: - val = (declRange.ranged() && declRange.littleEndian()) ? -1 : 1; - break; - case AstAttrType::DIM_SIZE: - val = !declRange.ranged() ? 0 : declRange.elements(); - break; - default: - nodep->v3fatalSrc("Missing DIM ATTR type case"); - break; - } - if (!valp) valp = new AstConst(nodep->fileline(), AstConst::Signed32(), val); + bits *= adtypep->width(); + break; + } + break; + } + if (dim == 0) { + val = 0; + } else if (dim == 1 && !declRange.ranged() && bits==1) { // $bits should be sane for non-arrays + val = nodep->width(); + } else { + val = bits; + } + break; } + case AstAttrType::DIM_HIGH: + val = !declRange.ranged() ? 0 : declRange.hi(); + break; + case AstAttrType::DIM_LEFT: + val = !declRange.ranged() ? 0 : declRange.left(); + break; + case AstAttrType::DIM_LOW: + val = !declRange.ranged() ? 0 : declRange.lo(); + break; + case AstAttrType::DIM_RIGHT: + val = !declRange.ranged() ? 0 : declRange.right(); + break; + case AstAttrType::DIM_INCREMENT: + val = (declRange.ranged() && declRange.littleEndian()) ? -1 : 1; + break; + case AstAttrType::DIM_SIZE: + val = !declRange.ranged() ? 0 : declRange.elements(); + break; + default: + nodep->v3fatalSrc("Missing DIM ATTR type case"); + break; + } + if (!valp) valp = new AstConst(nodep->fileline(), AstConst::Signed32(), val); UINFO(9," $dimension "<isConst(true); - varp->isStatic(true); - varp->valuep(initp); - // Add to root, as don't know module we are in, and aids later structure sharing - v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp); - // Element 0 is a non-index and has speced values - initp->addValuep(dimensionValue(nodep, attrType, 0)); - for (unsigned i=1; iaddValuep(dimensionValue(nodep, attrType, i)); - } - userIterate(varp, NULL); // May have already done $unit so must do this var - m_tableMap.insert(make_pair(make_pair(nodep,attrType), varp)); - return varp; + varp->isConst(true); + varp->isStatic(true); + varp->valuep(initp); + // Add to root, as don't know module we are in, and aids later structure sharing + v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp); + // Element 0 is a non-index and has speced values + initp->addValuep(dimensionValue(nodep, attrType, 0)); + for (unsigned i=1; iaddValuep(dimensionValue(nodep, attrType, i)); + } + userIterate(varp, NULL); // May have already done $unit so must do this var + m_tableMap.insert(make_pair(make_pair(nodep, attrType), varp)); + return varp; } AstVar* enumVarp(AstEnumDType* nodep, AstAttrType attrType, uint32_t msbdim) { - // Return a variable table which has specified dimension properties for this variable - TableMap::iterator pos = m_tableMap.find(make_pair(nodep, attrType)); - if (pos != m_tableMap.end()) { - return pos->second; - } - UINFO(9, "Construct Venumtab attr="<findStringDType(); - } else { - basep = nodep->dtypep(); - } - AstNodeArrayDType* vardtypep = new AstUnpackArrayDType(nodep->fileline(), - basep, - new AstRange(nodep->fileline(), msbdim, 0)); + // Return a variable table which has specified dimension properties for this variable + TableMap::iterator pos = m_tableMap.find(make_pair(nodep, attrType)); + if (pos != m_tableMap.end()) { + return pos->second; + } + UINFO(9, "Construct Venumtab attr="<findStringDType(); + } else { + basep = nodep->dtypep(); + } + AstNodeArrayDType* vardtypep + = new AstUnpackArrayDType(nodep->fileline(), + basep, + new AstRange(nodep->fileline(), msbdim, 0)); AstInitArray* initp = new AstInitArray(nodep->fileline(), vardtypep, NULL); - v3Global.rootp()->typeTablep()->addTypesp(vardtypep); + v3Global.rootp()->typeTablep()->addTypesp(vardtypep); AstVar* varp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP, - "__Venumtab_" + VString::downcase(attrType.ascii()) + cvtToStr(m_dtTables++), + "__Venumtab_" + VString::downcase(attrType.ascii()) + + cvtToStr(m_dtTables++), vardtypep); - varp->isConst(true); - varp->isStatic(true); - varp->valuep(initp); - // Add to root, as don't know module we are in, and aids later structure sharing - v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp); + varp->isConst(true); + varp->isStatic(true); + varp->valuep(initp); + // Add to root, as don't know module we are in, and aids later structure sharing + v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp); - // Default for all unspecified values - if (attrType == AstAttrType::ENUM_NAME) { - initp->defaultp(new AstConst(nodep->fileline(), AstConst::String(), "")); - } else if (attrType == AstAttrType::ENUM_NEXT + // Default for all unspecified values + if (attrType == AstAttrType::ENUM_NAME) { + initp->defaultp(new AstConst(nodep->fileline(), AstConst::String(), "")); + } else if (attrType == AstAttrType::ENUM_NEXT || attrType == AstAttrType::ENUM_PREV) { initp->defaultp(new AstConst(nodep->fileline(), V3Number(nodep, nodep->width(), 0))); } else { - nodep->v3fatalSrc("Bad case"); - } + nodep->v3fatalSrc("Bad case"); + } - // Find valid values and populate - if (!nodep->itemsp()) nodep->v3fatalSrc("enum without items"); + // Find valid values and populate + if (!nodep->itemsp()) nodep->v3fatalSrc("enum without items"); std::vector values; values.resize(msbdim+1); - for (unsigned i=0; i<(msbdim+1); ++i) { - values[i] = NULL; - } - { - AstEnumItem* firstp = nodep->itemsp(); - AstEnumItem* prevp = firstp; // Prev must start with last item + for (unsigned i=0; i<(msbdim+1); ++i) { + values[i] = NULL; + } + { + AstEnumItem* firstp = nodep->itemsp(); + AstEnumItem* prevp = firstp; // Prev must start with last item while (prevp->nextp()) prevp = VN_CAST(prevp->nextp(), EnumItem); - for (AstEnumItem* itemp = firstp; itemp;) { + for (AstEnumItem* itemp = firstp; itemp;) { AstEnumItem* nextp = VN_CAST(itemp->nextp(), EnumItem); const AstConst* vconstp = VN_CAST(itemp->valuep(), Const); - if (!vconstp) nodep->v3fatalSrc("Enum item without constified value"); - uint32_t i = vconstp->toUInt(); - if (attrType == AstAttrType::ENUM_NAME) { - values[i] = new AstConst(nodep->fileline(), AstConst::String(), itemp->name()); - } else if (attrType == AstAttrType::ENUM_NEXT) { - values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const - } else if (attrType == AstAttrType::ENUM_PREV) { - values[i] = prevp->valuep()->cloneTree(false); // A const - } else { - nodep->v3fatalSrc("Bad case"); - } - prevp = itemp; - itemp = nextp; - } - } - // Add all specified values to table - for (unsigned i=0; i<(msbdim+1); ++i) { - AstNode* valp = values[i]; - if (valp) initp->addIndexValuep(i, valp); - } - userIterate(varp, NULL); // May have already done $unit so must do this var - m_tableMap.insert(make_pair(make_pair(nodep,attrType), varp)); - return varp; + if (!vconstp) nodep->v3fatalSrc("Enum item without constified value"); + uint32_t i = vconstp->toUInt(); + if (attrType == AstAttrType::ENUM_NAME) { + values[i] = new AstConst(nodep->fileline(), AstConst::String(), itemp->name()); + } else if (attrType == AstAttrType::ENUM_NEXT) { + values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const + } else if (attrType == AstAttrType::ENUM_PREV) { + values[i] = prevp->valuep()->cloneTree(false); // A const + } else { + nodep->v3fatalSrc("Bad case"); + } + prevp = itemp; + itemp = nextp; + } + } + // Add all specified values to table + for (unsigned i=0; i<(msbdim+1); ++i) { + AstNode* valp = values[i]; + if (valp) initp->addIndexValuep(i, valp); + } + userIterate(varp, NULL); // May have already done $unit so must do this var + m_tableMap.insert(make_pair(make_pair(nodep, attrType), varp)); + return varp; } PatVecMap patVectorMap(AstPattern* nodep, const VNumRange& range) { - PatVecMap patmap; - int element = range.left(); + PatVecMap patmap; + int element = range.left(); for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp; patp = VN_CAST(patp->nextp(), PatMember)) { - if (patp->keyp()) { + if (patp->keyp()) { if (const AstConst* constp = VN_CAST(patp->keyp(), Const)) { - element = constp->toSInt(); - } else { - patp->keyp()->v3error("Assignment pattern key not supported/understood: "<keyp()->prettyTypeName()); - } - } - if (patmap.find(element) != patmap.end()) { - patp->v3error("Assignment pattern key used multiple times: "<toSInt(); + } else { + patp->keyp()->v3error("Assignment pattern key not supported/understood: " + <keyp()->prettyTypeName()); + } + } + if (patmap.find(element) != patmap.end()) { + patp->v3error("Assignment pattern key used multiple times: "<selfDtm())) { - UINFO(1,"-: "<v3fatalSrc("No dtype expected at statement "<prettyTypeName()); - } + if (VL_UNLIKELY(m_vup && !m_vup->selfDtm())) { + UINFO(1,"-: "<v3fatalSrc("No dtype expected at statement "<prettyTypeName()); + } } void checkConstantOrReplace(AstNode* nodep, const string& message) { - // See also V3WidthSel::checkConstantOrReplace - // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us! + // See also V3WidthSel::checkConstantOrReplace + // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us! if (!VN_IS(nodep, Const)) { - nodep->v3error(message); - nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Unsized32(), 1)); - pushDeletep(nodep); VL_DANGLING(nodep); - } + nodep->v3error(message); + nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Unsized32(), 1)); + pushDeletep(nodep); VL_DANGLING(nodep); + } } //---------------------------------------------------------------------- @@ -3955,70 +4050,70 @@ private: // These functions save/restore the AstNUser information so it can pass to child nodes. AstNode* userIterateSubtreeReturnEdits(AstNode* nodep, WidthVP* vup) { - if (!nodep) return NULL; - WidthVP* saveVup = m_vup; - AstNode* ret; - { - m_vup = vup; + if (!nodep) return NULL; + WidthVP* saveVup = m_vup; + AstNode* ret; + { + m_vup = vup; ret = iterateSubtreeReturnEdits(nodep); - } - m_vup = saveVup; - return ret; + } + m_vup = saveVup; + return ret; } void userIterate(AstNode* nodep, WidthVP* vup) { - if (!nodep) return; - WidthVP* saveVup = m_vup; - { - m_vup = vup; + if (!nodep) return; + WidthVP* saveVup = m_vup; + { + m_vup = vup; iterate(nodep); - } - m_vup = saveVup; + } + m_vup = saveVup; } void userIterateAndNext(AstNode* nodep, WidthVP* vup) { - if (!nodep) return; - WidthVP* saveVup = m_vup; - { - m_vup = vup; + if (!nodep) return; + WidthVP* saveVup = m_vup; + { + m_vup = vup; iterateAndNextNull(nodep); - } - m_vup = saveVup; + } + m_vup = saveVup; } void userIterateChildren(AstNode* nodep, WidthVP* vup) { - if (!nodep) return; - WidthVP* saveVup = m_vup; - { - m_vup = vup; + if (!nodep) return; + WidthVP* saveVup = m_vup; + { + m_vup = vup; iterateChildren(nodep); - } - m_vup = saveVup; + } + m_vup = saveVup; } void userIterateChildrenBackwards(AstNode* nodep, WidthVP* vup) { - if (!nodep) return; - WidthVP* saveVup = m_vup; - { - m_vup = vup; + if (!nodep) return; + WidthVP* saveVup = m_vup; + { + m_vup = vup; iterateChildrenBackwards(nodep); - } - m_vup = saveVup; + } + m_vup = saveVup; } public: // CONSTUCTORS - WidthVisitor(bool paramsOnly, // [in] TRUE if we are considering parameters only. - bool doGenerate) { // [in] TRUE if we are inside a generate statement and - // // don't wish to trigger errors - m_paramsOnly = paramsOnly; - m_cellRangep = NULL; + WidthVisitor(bool paramsOnly, // [in] TRUE if we are considering parameters only. + bool doGenerate) { // [in] TRUE if we are inside a generate statement and + // // don't wish to trigger errors + m_paramsOnly = paramsOnly; + m_cellRangep = NULL; m_ftaskp = NULL; - m_funcp = NULL; - m_initialp = NULL; - m_attrp = NULL; - m_doGenerate = doGenerate; - m_dtTables = 0; - m_vup = NULL; + m_funcp = NULL; + m_initialp = NULL; + m_attrp = NULL; + m_doGenerate = doGenerate; + m_dtTables = 0; + m_vup = NULL; } AstNode* mainAcceptEdit(AstNode* nodep) { - return userIterateSubtreeReturnEdits(nodep, WidthVP(SELF,BOTH).p()); + return userIterateSubtreeReturnEdits(nodep, WidthVP(SELF, BOTH).p()); } virtual ~WidthVisitor() {} }; @@ -4066,7 +4161,7 @@ AstNode* V3Width::widthParamsEdit(AstNode* nodep) { //! later to do the width check. //! @return Pointer to the edited node. AstNode* V3Width::widthGenerateParamsEdit( - AstNode* nodep) { //!< [in] AST whose parameters widths are to be analysed. + AstNode* nodep) { //!< [in] AST whose parameters widths are to be analysed. UINFO(4,__FUNCTION__<<": "<lhsp()->unlinkFrBack()); VL_DANGLING(nodep); + replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); VL_DANGLING(nodep); } virtual void visit(AstUnsigned* nodep) { - replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); VL_DANGLING(nodep); + replaceWithSignedVersion(nodep, nodep->lhsp()->unlinkFrBack()); VL_DANGLING(nodep); } virtual void visit(AstNode* nodep) { iterateChildren(nodep); } void replaceWithSignedVersion(AstNode* nodep, AstNode* newp) { - UINFO(6," Replace "<replaceWith(newp); - newp->dtypeFrom(nodep); - pushDeletep(nodep); VL_DANGLING(nodep); + UINFO(6," Replace "<replaceWith(newp); + newp->dtypeFrom(nodep); + pushDeletep(nodep); VL_DANGLING(nodep); } public: // CONSTRUCTORS @@ -69,109 +69,109 @@ public: class WidthCommitVisitor : public AstNVisitor { // NODE STATE - // AstVar::user1p -> bool, processed - AstUser1InUse m_inuser1; + // AstVar::user1p -> bool, processed + AstUser1InUse m_inuser1; public: // METHODS static AstConst* newIfConstCommitSize(AstConst* nodep) { - if (((nodep->dtypep()->width() != nodep->num().width()) - || !nodep->num().sized()) + if (((nodep->dtypep()->width() != nodep->num().width()) + || !nodep->num().sized()) && !nodep->num().isString()) { // Need to force the number from unsized to sized V3Number num (nodep, nodep->dtypep()->width()); num.opAssign(nodep->num()); - num.isSigned(nodep->isSigned()); - AstConst* newp = new AstConst(nodep->fileline(), num); - newp->dtypeFrom(nodep); - return newp; - } else { - return NULL; - } + num.isSigned(nodep->isSigned()); + AstConst* newp = new AstConst(nodep->fileline(), num); + newp->dtypeFrom(nodep); + return newp; + } else { + return NULL; + } } private: // METHODS void editDType(AstNode* nodep) { - // Edit dtypes for this node - nodep->dtypep(editOneDType(nodep->dtypep())); + // Edit dtypes for this node + nodep->dtypep(editOneDType(nodep->dtypep())); } AstNodeDType* editOneDType(AstNodeDType* nodep) { - // See if the dtype/refDType can be converted to a standard one - // This reduces the number of dtypes in the system, and since - // dtypep() figures into sameTree() results in better optimizations - if (!nodep) return NULL; - // Recurse to handle the data type, as may change the size etc of this type + // See if the dtype/refDType can be converted to a standard one + // This reduces the number of dtypes in the system, and since + // dtypep() figures into sameTree() results in better optimizations + if (!nodep) return NULL; + // Recurse to handle the data type, as may change the size etc of this type if (!nodep->user1()) iterate(nodep); - // Look for duplicate + // Look for duplicate if (AstBasicDType* bdtypep = VN_CAST(nodep, BasicDType)) { - AstBasicDType* newp = nodep->findInsertSameDType(bdtypep); - if (newp != bdtypep && debug()>=9) { - UINFO(9,"dtype replacement "); nodep->dumpSmall(cout); - cout<<" ----> "; newp->dumpSmall(cout); cout<findInsertSameDType(bdtypep); + if (newp != bdtypep && debug()>=9) { + UINFO(9,"dtype replacement "); nodep->dumpSmall(cout); + cout<<" ----> "; newp->dumpSmall(cout); cout<dtypep()) nodep->v3fatalSrc("No dtype"); + if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype"); iterate(nodep->dtypep()); // Do datatype first - if (AstConst* newp = newIfConstCommitSize(nodep)) { - nodep->replaceWith(newp); - AstNode* oldp = nodep; nodep = newp; - //if (debug()>4) oldp->dumpTree(cout," fixConstSize_old: "); - //if (debug()>4) newp->dumpTree(cout," _new: "); - pushDeletep(oldp); VL_DANGLING(oldp); - } - editDType(nodep); + if (AstConst* newp = newIfConstCommitSize(nodep)) { + nodep->replaceWith(newp); + AstNode* oldp = nodep; nodep = newp; + //if (debug()>4) oldp->dumpTree(cout, " fixConstSize_old: "); + //if (debug()>4) newp->dumpTree(cout, " _new: "); + pushDeletep(oldp); VL_DANGLING(oldp); + } + editDType(nodep); } virtual void visit(AstNodeDType* nodep) { - visitIterateNodeDType(nodep); + visitIterateNodeDType(nodep); } virtual void visit(AstNodeClassDType* nodep) { - if (nodep->user1SetOnce()) return; // Process once - visitIterateNodeDType(nodep); - nodep->clearCache(); + if (nodep->user1SetOnce()) return; // Process once + visitIterateNodeDType(nodep); + nodep->clearCache(); } virtual void visit(AstParamTypeDType* nodep) { - if (nodep->user1SetOnce()) return; // Process once - visitIterateNodeDType(nodep); - // Move to type table as all dtype pointers must resolve there - nodep->unlinkFrBack(); // Make non-child - v3Global.rootp()->typeTablep()->addTypesp(nodep); + if (nodep->user1SetOnce()) return; // Process once + visitIterateNodeDType(nodep); + // Move to type table as all dtype pointers must resolve there + nodep->unlinkFrBack(); // Make non-child + v3Global.rootp()->typeTablep()->addTypesp(nodep); } void visitIterateNodeDType(AstNodeDType* nodep) { - // Rather than use dtypeChg which may make new nodes, we simply edit in place, - // as we don't need to preserve any widthMin's, and every dtype with the same width - // gets an identical edit. - if (nodep->user1SetOnce()) return; // Process once - nodep->widthMinFromWidth(); - // Too late to any unspecified sign to be anything but unsigned - if (nodep->numeric().isNosign()) nodep->numeric(AstNumeric::UNSIGNED); + // Rather than use dtypeChg which may make new nodes, we simply edit in place, + // as we don't need to preserve any widthMin's, and every dtype with the same width + // gets an identical edit. + if (nodep->user1SetOnce()) return; // Process once + nodep->widthMinFromWidth(); + // Too late to any unspecified sign to be anything but unsigned + if (nodep->numeric().isNosign()) nodep->numeric(AstNumeric::UNSIGNED); iterateChildren(nodep); - nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep())); + nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep())); } virtual void visit(AstNodePreSel* nodep) { - // This check could go anywhere after V3Param - nodep->v3fatalSrc("Presels should have been removed before this point"); + // This check could go anywhere after V3Param + nodep->v3fatalSrc("Presels should have been removed before this point"); } virtual void visit(AstNode* nodep) { iterateChildren(nodep); - editDType(nodep); + editDType(nodep); } public: // CONSTUCTORS explicit WidthCommitVisitor(AstNetlist* nodep) { - // Were changing widthMin's, so the table is now somewhat trashed - nodep->typeTablep()->clearCache(); + // Were changing widthMin's, so the table is now somewhat trashed + nodep->typeTablep()->clearCache(); iterate(nodep); - // Don't want to repairCache, as all needed nodes have been added back in - // a repair would prevent dead nodes from being detected + // Don't want to repairCache, as all needed nodes have been added back in + // a repair would prevent dead nodes from being detected } virtual ~WidthCommitVisitor() {} }; //###################################################################### -#endif // Guard +#endif // Guard diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 4228c08fb..b0f1da711 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -18,10 +18,10 @@ // //************************************************************************* // V3WidthSel's Transformations: -// Top down traversal: -// Replace SELPLUS/SELMINUS with SEL -// Replace SELEXTRACT with SEL -// Replace SELBIT with SEL or ARRAYSEL +// Top down traversal: +// Replace SELPLUS/SELMINUS with SEL +// Replace SELEXTRACT with SEL +// Replace SELBIT with SEL or ARRAYSEL // // This code was once in V3LinkResolve, but little endian bit vectors won't // work that early. It was considered for V3Width and V3Param, but is @@ -55,249 +55,254 @@ private: VL_DEBUG_FUNC; // Declare debug() void checkConstantOrReplace(AstNode* nodep, const string& message) { - // See also V3Width::checkConstantOrReplace - // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us! + // See also V3Width::checkConstantOrReplace + // Note can't call V3Const::constifyParam(nodep) here, as constify may change nodep on us! if (!VN_IS(nodep, Const)) { - nodep->v3error(message); - nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Unsized32(), 1)); - pushDeletep(nodep); VL_DANGLING(nodep); - } + nodep->v3error(message); + nodep->replaceWith(new AstConst(nodep->fileline(), AstConst::Unsized32(), 1)); + pushDeletep(nodep); VL_DANGLING(nodep); + } } // RETURN TYPE struct FromData { - AstNode* m_errp; // Node that was found, for error reporting if not known type - AstNodeDType* m_dtypep; // Data type for the 'from' slice - VNumRange m_fromRange; // Numeric range bounds for the 'from' slice + AstNode* m_errp; // Node that was found, for error reporting if not known type + AstNodeDType* m_dtypep; // Data type for the 'from' slice + VNumRange m_fromRange; // Numeric range bounds for the 'from' slice FromData(AstNode* errp, AstNodeDType* dtypep, const VNumRange& fromRange) - { m_errp=errp; m_dtypep=dtypep; m_fromRange=fromRange; } - ~FromData() {} + { m_errp = errp; m_dtypep = dtypep; m_fromRange = fromRange; } + ~FromData() {} }; FromData fromDataForArray(AstNode* nodep, AstNode* basefromp) { - // What is the data type and information for this SEL-ish's from()? - UINFO(9," fromData start ddtypep = "<dtypep()->skipRefp(); - AstNode* errp = ddtypep; - UINFO(9," fromData.ddtypep = "<dtypep()) nodep->v3fatalSrc("Select with no from dtype"); + AstNodeDType* ddtypep = basefromp->dtypep()->skipRefp(); + AstNode* errp = ddtypep; + UINFO(9," fromData.ddtypep = "<declRange(); - } + fromRange = adtypep->declRange(); + } else if (const AstNodeClassDType* adtypep = VN_CAST(ddtypep, NodeClassDType)) { - fromRange = adtypep->declRange(); - } + fromRange = adtypep->declRange(); + } else if (AstBasicDType* adtypep = VN_CAST(ddtypep, BasicDType)) { - if (adtypep->isRanged()) { - if (adtypep->rangep() + if (adtypep->isRanged()) { + if (adtypep->rangep() && (!VN_IS(adtypep->rangep()->msbp(), Const) || !VN_IS(adtypep->rangep()->lsbp(), Const))) - nodep->v3fatalSrc("Non-constant variable range; errored earlier"); // in constifyParam(bfdtypep) - fromRange = adtypep->declRange(); - } else { - nodep->v3error("Illegal bit or array select; type does not have a bit range, or bad dimension: type is " - <prettyName()); - } - } - else { - nodep->v3error("Illegal bit or array select; type already selected, or bad dimension: type is " - <prettyName()); - } - return FromData(errp,ddtypep,fromRange); + nodep->v3fatalSrc("Non-constant variable range; errored earlier"); // in constifyParam(bfdtypep) + fromRange = adtypep->declRange(); + } else { + nodep->v3error("Illegal bit or array select; type does not have a bit range, or bad dimension: type is " + <prettyName()); + } + } + else { + nodep->v3error("Illegal bit or array select; type already selected, or bad dimension: type is " + <prettyName()); + } + return FromData(errp, ddtypep, fromRange); } AstNode* newSubNeg(AstNode* lhsp, vlsint32_t rhs) { - // Return lhs-rhs, but if rhs is negative use an add, so we won't - // have to deal with signed math and related 32bit sign extension problems - if (rhs == 0) { - return lhsp; + // Return lhs-rhs, but if rhs is negative use an add, so we won't + // have to deal with signed math and related 32bit sign extension problems + if (rhs == 0) { + return lhsp; } else if (VN_IS(lhsp, Const)) { - // Optional vs just making add/sub below, but saves constification some work + // Optional vs just making add/sub below, but saves constification some work V3Number num (lhsp, lhsp->width()); num.opSub(VN_CAST(lhsp, Const)->num(), V3Number(lhsp, 32, rhs)); num.isSigned(lhsp->isSigned()); - AstNode* newp = new AstConst(lhsp->fileline(), num); - return newp; - } else if (rhs > 0) { - AstNode* newp = new AstSub(lhsp->fileline(), lhsp, - new AstConst(lhsp->fileline(), AstConst::Unsized32(), rhs)); - // We must make sure sub gets sign of original value, not from the constant - newp->dtypeFrom(lhsp); - return newp; - } else { // rhs < 0; - AstNode* newp = new AstAdd(lhsp->fileline(), lhsp, - new AstConst(lhsp->fileline(), AstConst::Unsized32(), -rhs)); - // We must make sure sub gets sign of original value, not from the constant - newp->dtypeFrom(lhsp); - return newp; - } + AstNode* newp = new AstConst(lhsp->fileline(), num); + return newp; + } else if (rhs > 0) { + AstNode* newp = new AstSub(lhsp->fileline(), lhsp, + new AstConst(lhsp->fileline(), AstConst::Unsized32(), rhs)); + // We must make sure sub gets sign of original value, not from the constant + newp->dtypeFrom(lhsp); + return newp; + } else { // rhs < 0; + AstNode* newp = new AstAdd(lhsp->fileline(), lhsp, + new AstConst(lhsp->fileline(), AstConst::Unsized32(), -rhs)); + // We must make sure sub gets sign of original value, not from the constant + newp->dtypeFrom(lhsp); + return newp; + } } AstNode* newSubNeg(vlsint32_t lhs, AstNode* rhsp) { - // Return lhs-rhs - // We must make sure sub gets sign of original value - AstNode* newp = new AstSub(rhsp->fileline(), - new AstConst(rhsp->fileline(), AstConst::Unsized32(), lhs), - rhsp); - newp->dtypeFrom(rhsp); // Important as AstSub default is lhs's sign - return newp; + // Return lhs-rhs + // We must make sure sub gets sign of original value + AstNode* newp = new AstSub(rhsp->fileline(), + new AstConst(rhsp->fileline(), AstConst::Unsized32(), lhs), + rhsp); + newp->dtypeFrom(rhsp); // Important as AstSub default is lhs's sign + return newp; } AstNode* newSubLsbOf(AstNode* underp, const VNumRange& fromRange) { - // Account for a variable's LSB in bit selections - // Will likely become SUB(underp, lsb_of_signal) - // Don't report WIDTH warnings etc here, as may be inside a generate branch that will be deleted - // SUB #'s Not needed when LSB==0 and MSB>=0 (ie [0:-13] must still get added!) - if (!fromRange.ranged()) { - // vector without range, or 0 lsb is ok, for example a INTEGER x; y = x[21:0]; - return underp; - } else { - if (fromRange.littleEndian()) { - // reg [1:3] was swapped to [3:1] (lsbEndianedp==3) and needs a SUB(3,under) - AstNode* newp = newSubNeg(fromRange.hi(), underp); - return newp; - } else { - // reg [3:1] needs a SUB(under,1) - AstNode* newp = newSubNeg(underp, fromRange.lo()); - return newp; - } - } + // Account for a variable's LSB in bit selections + // Will likely become SUB(underp, lsb_of_signal). + // Don't report WIDTH warnings etc here, as may be inside a + // generate branch that will be deleted. + // SUB #'s Not needed when LSB==0 and MSB>=0 (ie [0:-13] must still get added!) + if (!fromRange.ranged()) { + // vector without range, or 0 lsb is ok, for example a INTEGER x; y = x[21:0]; + return underp; + } else { + if (fromRange.littleEndian()) { + // reg [1:3] was swapped to [3:1] (lsbEndianedp==3) and needs a SUB(3,under) + AstNode* newp = newSubNeg(fromRange.hi(), underp); + return newp; + } else { + // reg [3:1] needs a SUB(under,1) + AstNode* newp = newSubNeg(underp, fromRange.lo()); + return newp; + } + } } AstNodeDType* sliceDType(AstPackArrayDType* nodep, int msb, int lsb) { - // Return slice needed for msb/lsb, either as original dtype or a new slice dtype - if (nodep->declRange().elements() == (msb-lsb+1) // Extracting whole of original array - && nodep->declRange().lo() == lsb) { - return nodep; - } else { - // Need a slice data type, which is an array of the extracted type, but with (presumably) different size - VNumRange newRange (msb, lsb, nodep->declRange().littleEndian()); - AstNodeDType* vardtypep = new AstPackArrayDType(nodep->fileline(), - nodep->subDTypep(), // Need to strip off array reference - new AstRange(nodep->fileline(), newRange)); - v3Global.rootp()->typeTablep()->addTypesp(vardtypep); - return vardtypep; - } + // Return slice needed for msb/lsb, either as original dtype or a new slice dtype + if (nodep->declRange().elements() == (msb-lsb+1) // Extracting whole of original array + && nodep->declRange().lo() == lsb) { + return nodep; + } else { + // Need a slice data type, which is an array of the extracted + // type, but with (presumably) different size + VNumRange newRange (msb, lsb, nodep->declRange().littleEndian()); + AstNodeDType* vardtypep + = new AstPackArrayDType(nodep->fileline(), + nodep->subDTypep(), // Need to strip off array reference + new AstRange(nodep->fileline(), newRange)); + v3Global.rootp()->typeTablep()->addTypesp(vardtypep); + return vardtypep; + } } // VISITORS // If adding new visitors, insure V3Width's visit(TYPE) calls into here virtual void visit(AstSelBit* nodep) { - // Select of a non-width specified part of an array, i.e. "array[2]" - // This select style has a lsb and msb (no user specified width) - UINFO(6,"SELBIT "<=9) nodep->backp()->dumpTree(cout,"--SELBT0: "); - // lhsp/rhsp do not need to be constant - AstNode* fromp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); // bit we're extracting - if (debug()>=9) nodep->dumpTree(cout,"--SELBT2: "); + // Select of a non-width specified part of an array, i.e. "array[2]" + // This select style has a lsb and msb (no user specified width) + UINFO(6,"SELBIT "<=9) nodep->backp()->dumpTree(cout, "--SELBT0: "); + // lhsp/rhsp do not need to be constant + AstNode* fromp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); // bit we're extracting + if (debug()>=9) nodep->dumpTree(cout, "--SELBT2: "); FromData fromdata = fromDataForArray(nodep, fromp); - AstNodeDType* ddtypep = fromdata.m_dtypep; - VNumRange fromRange = fromdata.m_fromRange; - UINFO(6," ddtypep "< ARRAYSEL(array, index) - AstNode* subp = rhsp; - if (fromRange.lo()!=0 || fromRange.hi()<0) { + // SELBIT(array, index) -> ARRAYSEL(array, index) + AstNode* subp = rhsp; + if (fromRange.lo()!=0 || fromRange.hi()<0) { subp = newSubNeg(subp, fromRange.lo()); - } + } AstArraySel* newp = new AstArraySel(nodep->fileline(), fromp, subp); - newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference - if (debug()>=9) newp->dumpTree(cout,"--SELBTn: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } + newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference + if (debug()>=9) newp->dumpTree(cout, "--SELBTn: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } else if (AstPackArrayDType* adtypep = VN_CAST(ddtypep, PackArrayDType)) { - // SELBIT(array, index) -> SEL(array, index*width-of-subindex, width-of-subindex) - AstNode* subp = rhsp; - if (fromRange.lo()!=0 || fromRange.hi()<0) { - if (fromRange.littleEndian()) { - subp = newSubNeg(fromRange.hi(), subp); - } else { - subp = newSubNeg(subp, fromRange.lo()); - } - } - if (!fromRange.elements() || (adtypep->width() % fromRange.elements())!=0) - adtypep->v3fatalSrc("Array extraction with width miscomputed " - <width()<<"/"<width() / fromRange.elements(); - AstSel* newp = new AstSel(nodep->fileline(), - fromp, - new AstMul(nodep->fileline(), - new AstConst(nodep->fileline(),AstConst::Unsized32(),elwidth), - subp), - new AstConst(nodep->fileline(),AstConst::Unsized32(),elwidth)); - newp->declRange(fromRange); - newp->declElWidth(elwidth); - newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference - if (debug()>=9) newp->dumpTree(cout,"--SELBTn: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } + // SELBIT(array, index) -> SEL(array, index*width-of-subindex, width-of-subindex) + AstNode* subp = rhsp; + if (fromRange.lo()!=0 || fromRange.hi()<0) { + if (fromRange.littleEndian()) { + subp = newSubNeg(fromRange.hi(), subp); + } else { + subp = newSubNeg(subp, fromRange.lo()); + } + } + if (!fromRange.elements() || (adtypep->width() % fromRange.elements())!=0) + adtypep->v3fatalSrc("Array extraction with width miscomputed " + <width()<<"/"<width() / fromRange.elements(); + AstSel* newp + = new AstSel(nodep->fileline(), + fromp, + new AstMul(nodep->fileline(), + new AstConst(nodep->fileline(), + AstConst::Unsized32(), elwidth), + subp), + new AstConst(nodep->fileline(), AstConst::Unsized32(), elwidth)); + newp->declRange(fromRange); + newp->declElWidth(elwidth); + newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference + if (debug()>=9) newp->dumpTree(cout, "--SELBTn: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } else if (VN_IS(ddtypep, BasicDType)) { - // SELBIT(range, index) -> SEL(array, index, 1) + // SELBIT(range, index) -> SEL(array, index, 1) AstSel* newp = new AstSel(nodep->fileline(), fromp, newSubLsbOf(rhsp, fromRange), // Unsized so width from user new AstConst(nodep->fileline(), AstConst::Unsized32(), 1)); - newp->declRange(fromRange); - UINFO(6," new "<=9) newp->dumpTree(cout,"--SELBTn: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } + newp->declRange(fromRange); + UINFO(6," new "<=9) newp->dumpTree(cout, "--SELBTn: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } else if (VN_IS(ddtypep, NodeClassDType)) { // It's packed, so a bit from the packed struct - // SELBIT(range, index) -> SEL(array, index, 1) + // SELBIT(range, index) -> SEL(array, index, 1) AstSel* newp = new AstSel(nodep->fileline(), fromp, newSubLsbOf(rhsp, fromRange), // Unsized so width from user new AstConst(nodep->fileline(), AstConst::Unsized32(), 1)); - newp->declRange(fromRange); - UINFO(6," new "<=9) newp->dumpTree(cout,"--SELBTn: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } - else { // NULL=bad extract, or unknown node type - nodep->v3error("Illegal bit or array select; type already selected, or bad dimension: type is" - <prettyName()); - // How to recover? We'll strip a dimension. - nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); - } - if (!rhsp->backp()) { pushDeletep(rhsp); VL_DANGLING(rhsp); } + newp->declRange(fromRange); + UINFO(6," new "<=9) newp->dumpTree(cout, "--SELBTn: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } + else { // NULL=bad extract, or unknown node type + nodep->v3error("Illegal bit or array select; type already selected, or bad dimension: type is" + <prettyName()); + // How to recover? We'll strip a dimension. + nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); + } + if (!rhsp->backp()) { pushDeletep(rhsp); VL_DANGLING(rhsp); } } virtual void visit(AstSelExtract* nodep) { - // Select of a range specified part of an array, i.e. "array[2:3]" - // SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb) - // This select style has a (msb or lsb) and width - UINFO(6,"SELEXTRACT "<=9) nodep->dumpTree(cout,"--SELEX0: "); - // Below 2 lines may change nodep->widthp() - V3Const::constifyParamsEdit(nodep->lsbp()); // May relink pointed to node - V3Const::constifyParamsEdit(nodep->msbp()); // May relink pointed to node - //if (debug()>=9) nodep->dumpTree(cout,"--SELEX3: "); - checkConstantOrReplace(nodep->lsbp(), "First value of [a:b] isn't a constant, maybe you want +: or -:"); - checkConstantOrReplace(nodep->msbp(), "Second value of [a:b] isn't a constant, maybe you want +: or -:"); - AstNode* fromp = nodep->lhsp()->unlinkFrBack(); - AstNode* msbp = nodep->rhsp()->unlinkFrBack(); - AstNode* lsbp = nodep->thsp()->unlinkFrBack(); + // Select of a range specified part of an array, i.e. "array[2:3]" + // SELEXTRACT(from,msb,lsb) -> SEL(from, lsb, 1+msb-lsb) + // This select style has a (msb or lsb) and width + UINFO(6,"SELEXTRACT "<=9) nodep->dumpTree(cout, "--SELEX0: "); + // Below 2 lines may change nodep->widthp() + V3Const::constifyParamsEdit(nodep->lsbp()); // May relink pointed to node + V3Const::constifyParamsEdit(nodep->msbp()); // May relink pointed to node + //if (debug()>=9) nodep->dumpTree(cout, "--SELEX3: "); + checkConstantOrReplace(nodep->lsbp(), "First value of [a:b] isn't a constant, maybe you want +: or -:"); + checkConstantOrReplace(nodep->msbp(), "Second value of [a:b] isn't a constant, maybe you want +: or -:"); + AstNode* fromp = nodep->lhsp()->unlinkFrBack(); + AstNode* msbp = nodep->rhsp()->unlinkFrBack(); + AstNode* lsbp = nodep->thsp()->unlinkFrBack(); vlsint32_t msb = VN_CAST(msbp, Const)->toSInt(); vlsint32_t lsb = VN_CAST(lsbp, Const)->toSInt(); - vlsint32_t elem = (msb>lsb) ? (msb-lsb+1) : (lsb-msb+1); + vlsint32_t elem = (msb>lsb) ? (msb-lsb+1) : (lsb-msb+1); FromData fromdata = fromDataForArray(nodep, fromp); - AstNodeDType* ddtypep = fromdata.m_dtypep; - VNumRange fromRange = fromdata.m_fromRange; + AstNodeDType* ddtypep = fromdata.m_dtypep; + VNumRange fromRange = fromdata.m_fromRange; if (VN_IS(ddtypep, UnpackArrayDType)) { - // Slice extraction - if (fromRange.elements() == elem - && fromRange.lo() == lsb) { // Extracting whole of original array - nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); + // Slice extraction + if (fromRange.elements() == elem + && fromRange.lo() == lsb) { // Extracting whole of original array + nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); } else if (fromRange.elements() == 1) { // Extracting single element AstArraySel* newp = new AstArraySel(nodep->fileline(), fromp, lsbp); nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); @@ -307,42 +312,47 @@ private: msb, lsb)); nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); } - } + } else if (AstPackArrayDType* adtypep = VN_CAST(ddtypep, PackArrayDType)) { - // SELEXTRACT(array, msb, lsb) -> SEL(array, lsb*width-of-subindex, width-of-subindex*(msb-lsb)) - if (!fromRange.elements() || (adtypep->width() % fromRange.elements())!=0) - adtypep->v3fatalSrc("Array extraction with width miscomputed " - <width()<<"/"< msb) { - nodep->v3error("["<width() / fromRange.elements(); + // SELEXTRACT(array, msb, lsb) -> SEL(array, + // lsb*width-of-subindex, width-of-subindex*(msb-lsb)) + if (!fromRange.elements() || (adtypep->width() % fromRange.elements())!=0) + adtypep->v3fatalSrc("Array extraction with width miscomputed " + <width()<<"/"< msb) { + nodep->v3error("["<width() / fromRange.elements(); AstSel* newp = new AstSel(nodep->fileline(), fromp, new AstMul(nodep->fileline(), newSubLsbOf(lsbp, fromRange), - new AstConst(nodep->fileline(), AstConst::Unsized32(), elwidth)), - new AstConst(nodep->fileline(), AstConst::Unsized32(), (msb-lsb+1)*elwidth)); - newp->declRange(fromRange); - newp->declElWidth(elwidth); - newp->dtypeFrom(sliceDType(adtypep, msb, lsb)); - //if (debug()>=9) newp->dumpTree(cout,"--EXTBTn: "); + new AstConst(nodep->fileline(), + AstConst::Unsized32(), elwidth)), + new AstConst(nodep->fileline(), + AstConst::Unsized32(), (msb-lsb+1)*elwidth)); + newp->declRange(fromRange); + newp->declElWidth(elwidth); + newp->dtypeFrom(sliceDType(adtypep, msb, lsb)); + //if (debug()>=9) newp->dumpTree(cout, "--EXTBTn: "); if (newp->widthMin() != newp->widthConst()) nodep->v3fatalSrc("Width mismatch"); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } else if (VN_IS(ddtypep, BasicDType)) { - if (fromRange.littleEndian()) { - // Below code assumes big bit endian; just works out if we swap - int x = msb; msb = lsb; lsb = x; - } - if (lsb > msb) { - nodep->v3error("["< msb) { + nodep->v3error("["<fileline(), AstConst::Unsized32(), // Unsized so width from user msb +1-lsb); @@ -350,17 +360,18 @@ private: fromp, newSubLsbOf(lsbp, fromRange), widthp); - newp->declRange(fromRange); - UINFO(6," new "<=9) newp->dumpTree(cout,"--SELEXnew: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } + newp->declRange(fromRange); + UINFO(6," new "<=9) newp->dumpTree(cout, "--SELEXnew: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } else if (VN_IS(ddtypep, NodeClassDType)) { - // Classes aren't little endian - if (lsb > msb) { - nodep->v3error("["< msb) { + nodep->v3error("["<fileline(), AstConst::Unsized32(), // Unsized so width from user msb +1-lsb); @@ -368,107 +379,108 @@ private: fromp, newSubLsbOf(lsbp, fromRange), widthp); - newp->declRange(fromRange); - UINFO(6," new "<=9) newp->dumpTree(cout,"--SELEXnew: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } - else { // NULL=bad extract, or unknown node type - nodep->v3error("Illegal range select; type already selected, or bad dimension: type is " - <prettyName()); - UINFO(1," Related ddtype: "<replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); - } - // delete whataver we didn't use in reconstruction - if (!fromp->backp()) { pushDeletep(fromp); VL_DANGLING(fromp); } - if (!msbp->backp()) { pushDeletep(msbp); VL_DANGLING(msbp); } - if (!lsbp->backp()) { pushDeletep(lsbp); VL_DANGLING(lsbp); } + newp->declRange(fromRange); + UINFO(6," new "<=9) newp->dumpTree(cout, "--SELEXnew: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } + else { // NULL=bad extract, or unknown node type + nodep->v3error("Illegal range select; type already selected, or bad dimension: type is " + <prettyName()); + UINFO(1," Related ddtype: "<replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); + } + // delete whataver we didn't use in reconstruction + if (!fromp->backp()) { pushDeletep(fromp); VL_DANGLING(fromp); } + if (!msbp->backp()) { pushDeletep(msbp); VL_DANGLING(msbp); } + if (!lsbp->backp()) { pushDeletep(lsbp); VL_DANGLING(lsbp); } } void replaceSelPlusMinus(AstNodePreSel* nodep) { - // Select of a range specified with +: or -:, i.e. "array[2+:3], [2-:3]" - // This select style has a lsb and width - UINFO(6,"SELPLUS/MINUS "<widthp() - if (debug()>=9) nodep->dumpTree(cout,"--SELPM0: "); - V3Const::constifyParamsEdit(nodep->thsp()); // May relink pointed to node - checkConstantOrReplace(nodep->thsp(), "Width of :+ or :- bit extract isn't a constant"); - if (debug()>=9) nodep->dumpTree(cout,"--SELPM3: "); - // Now replace it with an AstSel - AstNode* fromp = nodep->lhsp()->unlinkFrBack(); - AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); - AstNode* widthp = nodep->thsp()->unlinkFrBack(); + // Select of a range specified with +: or -:, i.e. "array[2+:3], [2-:3]" + // This select style has a lsb and width + UINFO(6,"SELPLUS/MINUS "<widthp() + if (debug()>=9) nodep->dumpTree(cout, "--SELPM0: "); + V3Const::constifyParamsEdit(nodep->thsp()); // May relink pointed to node + checkConstantOrReplace(nodep->thsp(), "Width of :+ or :- bit extract isn't a constant"); + if (debug()>=9) nodep->dumpTree(cout, "--SELPM3: "); + // Now replace it with an AstSel + AstNode* fromp = nodep->lhsp()->unlinkFrBack(); + AstNode* rhsp = nodep->rhsp()->unlinkFrBack(); + AstNode* widthp = nodep->thsp()->unlinkFrBack(); int width = VN_CAST(widthp, Const)->toSInt(); - if (width > (1<<28)) nodep->v3error("Width of :+ or :- is huge; vector of over 1billion bits: "<prettyName()); - if (width<0) nodep->v3error("Width of :+ or :- is < 0: "<prettyName()); + if (width > (1<<28)) nodep->v3error("Width of :+ or :- is huge; vector of over 1billion bits: " + <prettyName()); + if (width<0) nodep->v3error("Width of :+ or :- is < 0: "<prettyName()); FromData fromdata = fromDataForArray(nodep, fromp); - AstNodeDType* ddtypep = fromdata.m_dtypep; - VNumRange fromRange = fromdata.m_fromRange; + AstNodeDType* ddtypep = fromdata.m_dtypep; + VNumRange fromRange = fromdata.m_fromRange; if (VN_IS(ddtypep, BasicDType) || VN_IS(ddtypep, PackArrayDType) || (VN_IS(ddtypep, NodeClassDType) && VN_CAST(ddtypep, NodeClassDType)->packedUnsup())) { - int elwidth = 1; - AstNode* newwidthp = widthp; + int elwidth = 1; + AstNode* newwidthp = widthp; if (const AstPackArrayDType* adtypep = VN_CAST(ddtypep, PackArrayDType)) { - elwidth = adtypep->width() / fromRange.elements(); - newwidthp = new AstConst(nodep->fileline(),AstConst::Unsized32(), width * elwidth); - } - AstNode* newlsbp = NULL; + elwidth = adtypep->width() / fromRange.elements(); + newwidthp = new AstConst(nodep->fileline(), AstConst::Unsized32(), width * elwidth); + } + AstNode* newlsbp = NULL; if (VN_IS(nodep, SelPlus)) { - if (fromRange.littleEndian()) { - // SELPLUS(from,lsb,width) -> SEL(from, (vector_msb-width+1)-sel, width) - newlsbp = newSubNeg((fromRange.hi()-width+1), rhsp); - } else { - // SELPLUS(from,lsb,width) -> SEL(from, lsb-vector_lsb, width) - newlsbp = newSubNeg(rhsp, fromRange.lo()); - } + if (fromRange.littleEndian()) { + // SELPLUS(from,lsb,width) -> SEL(from, (vector_msb-width+1)-sel, width) + newlsbp = newSubNeg((fromRange.hi()-width+1), rhsp); + } else { + // SELPLUS(from,lsb,width) -> SEL(from, lsb-vector_lsb, width) + newlsbp = newSubNeg(rhsp, fromRange.lo()); + } } else if (VN_IS(nodep, SelMinus)) { - if (fromRange.littleEndian()) { - // SELMINUS(from,msb,width) -> SEL(from, msb-[bit]) - newlsbp = newSubNeg(fromRange.hi(), rhsp); - } else { - // SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#) - newlsbp = newSubNeg(rhsp, fromRange.lo()+(width-1)); - } - } else { - nodep->v3fatalSrc("Bad Case"); - } + if (fromRange.littleEndian()) { + // SELMINUS(from,msb,width) -> SEL(from, msb-[bit]) + newlsbp = newSubNeg(fromRange.hi(), rhsp); + } else { + // SELMINUS(from,msb,width) -> SEL(from, msb-(width-1)-lsb#) + newlsbp = newSubNeg(rhsp, fromRange.lo()+(width-1)); + } + } else { + nodep->v3fatalSrc("Bad Case"); + } if (elwidth != 1) newlsbp = new AstMul(nodep->fileline(), newlsbp, new AstConst(nodep->fileline(), elwidth)); AstSel* newp = new AstSel(nodep->fileline(), fromp, newlsbp, newwidthp); - newp->declRange(fromRange); - newp->declElWidth(elwidth); - UINFO(6," new "<=9) newp->dumpTree(cout,"--SELNEW: "); - nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - } - else { // NULL=bad extract, or unknown node type - nodep->v3error("Illegal +: or -: select; type already selected, or bad dimension: type is " - <prettyTypeName()); - // How to recover? We'll strip a dimension. - nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); - } - // delete whataver we didn't use in reconstruction - if (!fromp->backp()) { pushDeletep(fromp); VL_DANGLING(fromp); } - if (!rhsp->backp()) { pushDeletep(rhsp); VL_DANGLING(rhsp); } - if (!widthp->backp()) { pushDeletep(widthp); VL_DANGLING(widthp); } + newp->declRange(fromRange); + newp->declElWidth(elwidth); + UINFO(6," new "<=9) newp->dumpTree(cout, "--SELNEW: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } + else { // NULL=bad extract, or unknown node type + nodep->v3error("Illegal +: or -: select; type already selected, or bad dimension: type is " + <prettyTypeName()); + // How to recover? We'll strip a dimension. + nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep); + } + // delete whataver we didn't use in reconstruction + if (!fromp->backp()) { pushDeletep(fromp); VL_DANGLING(fromp); } + if (!rhsp->backp()) { pushDeletep(rhsp); VL_DANGLING(rhsp); } + if (!widthp->backp()) { pushDeletep(widthp); VL_DANGLING(widthp); } } virtual void visit(AstSelPlus* nodep) { - replaceSelPlusMinus(nodep); + replaceSelPlusMinus(nodep); } virtual void visit(AstSelMinus* nodep) { - replaceSelPlusMinus(nodep); + replaceSelPlusMinus(nodep); } // If adding new visitors, insure V3Width's visit(TYPE) calls into here //-------------------- // Default virtual void visit(AstNode* nodep) { - // See notes above; we never iterate - nodep->v3fatalSrc("Shouldn't iterate in V3WidthSel"); + // See notes above; we never iterate + nodep->v3fatalSrc("Shouldn't iterate in V3WidthSel"); } public: diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 8a4b3f994..243cb74d9 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -111,13 +111,13 @@ AstNetlist* V3Global::makeNetlist() { void V3Global::checkTree() { rootp()->checkTree(); } void V3Global::clear() { - if (m_rootp) { m_rootp->deleteTree(); m_rootp=NULL; } + if (m_rootp) { m_rootp->deleteTree(); m_rootp = NULL; } } void V3Global::readFiles() { // NODE STATE - // AstNode::user4p() // VSymEnt* Package and typedef symbol names - AstUser4InUse inuser4; + // AstNode::user4p() // VSymEnt* Package and typedef symbol names + AstUser4InUse inuser4; V3InFilter filter (v3Global.opt.pipeFilter()); V3ParseSym parseSyms (v3Global.rootp()); // Symbol table must be common across all parsing @@ -126,9 +126,9 @@ void V3Global::readFiles() { // Read top module const V3StringList& vFiles = v3Global.opt.vFiles(); for (V3StringList::const_iterator it = vFiles.begin(); it != vFiles.end(); ++it) { - string filename = *it; - parser.parseFile(new FileLine("COMMAND_LINE",0), filename, false, - "Cannot find file containing module: "); + string filename = *it; + parser.parseFile(new FileLine("COMMAND_LINE", 0), filename, false, + "Cannot find file containing module: "); } // Read libraries @@ -136,21 +136,22 @@ void V3Global::readFiles() { // this needs to be done after the top file is read const V3StringSet& libraryFiles = v3Global.opt.libraryFiles(); for (V3StringSet::const_iterator it = libraryFiles.begin(); it != libraryFiles.end(); ++it) { - string filename = *it; - parser.parseFile(new FileLine("COMMAND_LINE",0), filename, true, - "Cannot find file containing library module: "); + string filename = *it; + parser.parseFile(new FileLine("COMMAND_LINE", 0), filename, true, + "Cannot find file containing library module: "); } //v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree")); V3Error::abortIfErrors(); if (!v3Global.opt.preprocOnly()) { - // Resolve all modules cells refer to - V3LinkCells::link(v3Global.rootp(), &filter, &parseSyms); + // Resolve all modules cells refer to + V3LinkCells::link(v3Global.rootp(), &filter, &parseSyms); } } void V3Global::dumpCheckGlobalTree(const string& stagename, int newNumber, bool doDump) { - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename(stagename+".tree", newNumber), false, doDump); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename(stagename+".tree", newNumber), + false, doDump); if (v3Global.opt.stats()) V3Stats::statsStage(stagename); } @@ -182,7 +183,7 @@ void process() { // Remove parameters by cloning modules to de-parameterized versions // This requires some width calculations and constant propagation V3Param::param(v3Global.rootp()); - V3LinkDot::linkDotParamed(v3Global.rootp()); // Cleanup as made new modules + V3LinkDot::linkDotParamed(v3Global.rootp()); // Cleanup as made new modules V3Error::abortIfErrors(); // Remove any modules that were parameterized and are no longer referenced. @@ -202,7 +203,7 @@ void process() { // Coverage insertion // Before we do dead code elimination and inlining, or we'll lose it. if (v3Global.opt.coverage()) { - V3Coverage::coverage(v3Global.rootp()); + V3Coverage::coverage(v3Global.rootp()); } // Push constants, but only true constants preserving liveness @@ -220,42 +221,42 @@ void process() { V3Assert::assertAll(v3Global.rootp()); if (!v3Global.opt.xmlOnly()) { - // Add top level wrapper with instance pointing to old top - // Move packages to under new top - // Must do this after we know parameters and dtypes (as don't clone dtype decls) - V3LinkLevel::wrapTop(v3Global.rootp()); + // Add top level wrapper with instance pointing to old top + // Move packages to under new top + // Must do this after we know parameters and dtypes (as don't clone dtype decls) + V3LinkLevel::wrapTop(v3Global.rootp()); } // Propagate constants into expressions V3Const::constifyAllLint(v3Global.rootp()); if (!v3Global.opt.xmlOnly()) { - // Remove cell arrays (must be between V3Width and scoping) - V3Inst::dearrayAll(v3Global.rootp()); - V3LinkDot::linkDotArrayed(v3Global.rootp()); + // Remove cell arrays (must be between V3Width and scoping) + V3Inst::dearrayAll(v3Global.rootp()); + V3LinkDot::linkDotArrayed(v3Global.rootp()); } if (!v3Global.opt.xmlOnly()) { - // Task inlining & pushing BEGINs names to variables/cells - // Begin processing must be after Param, before module inlining - V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner + // Task inlining & pushing BEGINs names to variables/cells + // Begin processing must be after Param, before module inlining + V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner // Expand inouts, stage 2 // Also simplify pin connections to always be AssignWs in prep for V3Unknown V3Tristate::tristateAll(v3Global.rootp()); - // Move assignments from X into MODULE temps. - // (Before flattening, so each new X variable is shared between all scopes of that module.) - V3Unknown::unknownAll(v3Global.rootp()); - v3Global.constRemoveXs(true); + // Move assignments from X into MODULE temps. + // (Before flattening, so each new X variable is shared between all scopes of that module.) + V3Unknown::unknownAll(v3Global.rootp()); + v3Global.constRemoveXs(true); - // Module inlining - // Cannot remove dead variables after this, as alias information for final - // V3Scope's V3LinkDot is in the AstVar. - if (v3Global.opt.oInline()) { - V3Inline::inlineAll(v3Global.rootp()); - V3LinkDot::linkDotArrayed(v3Global.rootp()); // Cleanup as made new modules - } + // Module inlining + // Cannot remove dead variables after this, as alias information for final + // V3Scope's V3LinkDot is in the AstVar. + if (v3Global.opt.oInline()) { + V3Inline::inlineAll(v3Global.rootp()); + V3LinkDot::linkDotArrayed(v3Global.rootp()); // Cleanup as made new modules + } } //--PRE-FLAT OPTIMIZATIONS------------------ @@ -272,180 +273,183 @@ void process() { //--FLATTENING--------------- if (!v3Global.opt.xmlOnly()) { - // We're going to flatten the hierarchy, so as many optimizations that - // can be done as possible should be before this.... + // We're going to flatten the hierarchy, so as many optimizations that + // can be done as possible should be before this.... - // Convert instantiations to wassigns and always blocks - V3Inst::instAll(v3Global.rootp()); + // Convert instantiations to wassigns and always blocks + V3Inst::instAll(v3Global.rootp()); - // Inst may have made lots of concats; fix them - V3Const::constifyAll(v3Global.rootp()); + // Inst may have made lots of concats; fix them + V3Const::constifyAll(v3Global.rootp()); - // Flatten hierarchy, creating a SCOPE for each module's usage as a cell - V3Scope::scopeAll(v3Global.rootp()); - V3LinkDot::linkDotScope(v3Global.rootp()); + // Flatten hierarchy, creating a SCOPE for each module's usage as a cell + V3Scope::scopeAll(v3Global.rootp()); + V3LinkDot::linkDotScope(v3Global.rootp()); } //--SCOPE BASED OPTIMIZATIONS-------------- if (!v3Global.opt.xmlOnly()) { - // Cleanup - V3Const::constifyAll(v3Global.rootp()); - V3Dead::deadifyDTypesScoped(v3Global.rootp()); - v3Global.checkTree(); + // Cleanup + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyDTypesScoped(v3Global.rootp()); + v3Global.checkTree(); - // Convert case statements to if() blocks. Must be after V3Unknown - // Must be before V3Task so don't need to deal with task in case value compares - V3Case::caseAll(v3Global.rootp()); + // Convert case statements to if() blocks. Must be after V3Unknown + // Must be before V3Task so don't need to deal with task in case value compares + V3Case::caseAll(v3Global.rootp()); - // Inline all tasks - V3Task::taskAll(v3Global.rootp()); + // Inline all tasks + V3Task::taskAll(v3Global.rootp()); - // Add __PVT's - // After V3Task so task internal variables will get renamed - V3Name::nameAll(v3Global.rootp()); + // Add __PVT's + // After V3Task so task internal variables will get renamed + V3Name::nameAll(v3Global.rootp()); - // Loop unrolling & convert FORs to WHILEs - V3Unroll::unrollAll(v3Global.rootp()); + // Loop unrolling & convert FORs to WHILEs + V3Unroll::unrollAll(v3Global.rootp()); - // Expand slices of arrays - V3Slice::sliceAll(v3Global.rootp()); + // Expand slices of arrays + V3Slice::sliceAll(v3Global.rootp()); - // Push constants across variables and remove redundant assignments - V3Const::constifyAll(v3Global.rootp()); + // Push constants across variables and remove redundant assignments + V3Const::constifyAll(v3Global.rootp()); - if (v3Global.opt.oLife()) { - V3Life::lifeAll(v3Global.rootp()); - } + if (v3Global.opt.oLife()) { + V3Life::lifeAll(v3Global.rootp()); + } - // Make large low-fanin logic blocks into lookup tables - // This should probably be done much later, once we have common logic elimination. - if (!v3Global.opt.lintOnly() && v3Global.opt.oTable()) { - V3Table::tableAll(v3Global.rootp()); - } + // Make large low-fanin logic blocks into lookup tables + // This should probably be done much later, once we have common logic elimination. + if (!v3Global.opt.lintOnly() && v3Global.opt.oTable()) { + V3Table::tableAll(v3Global.rootp()); + } - // Cleanup - V3Const::constifyAll(v3Global.rootp()); - V3Dead::deadifyDTypesScoped(v3Global.rootp()); - v3Global.checkTree(); + // Cleanup + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyDTypesScoped(v3Global.rootp()); + v3Global.checkTree(); - // Detect clock enables and mode into sensitives, and split always based on clocks - // (so this is a good prelude to splitAlways.) - if (v3Global.opt.oFlopGater()) { - V3ClkGater::clkGaterAll(v3Global.rootp()); - } + // Detect clock enables and mode into sensitives, and split always based on clocks + // (so this is a good prelude to splitAlways.) + if (v3Global.opt.oFlopGater()) { + V3ClkGater::clkGaterAll(v3Global.rootp()); + } - // Move assignments/sensitives into a SBLOCK for each unique sensitivity list - // (May convert some ALWAYS to combo blocks, so should be before V3Gate step.) - V3Active::activeAll(v3Global.rootp()); + // Move assignments/sensitives into a SBLOCK for each unique sensitivity list + // (May convert some ALWAYS to combo blocks, so should be before V3Gate step.) + V3Active::activeAll(v3Global.rootp()); - // Split single ALWAYS blocks into multiple blocks for better ordering chances - if (v3Global.opt.oSplit()) { - V3Split::splitAlwaysAll(v3Global.rootp()); - } - V3SplitAs::splitAsAll(v3Global.rootp()); + // Split single ALWAYS blocks into multiple blocks for better ordering chances + if (v3Global.opt.oSplit()) { + V3Split::splitAlwaysAll(v3Global.rootp()); + } + V3SplitAs::splitAsAll(v3Global.rootp()); - // Create tracing sample points, before we start eliminating signals - if (v3Global.opt.trace()) { - V3TraceDecl::traceDeclAll(v3Global.rootp()); - } + // Create tracing sample points, before we start eliminating signals + if (v3Global.opt.trace()) { + V3TraceDecl::traceDeclAll(v3Global.rootp()); + } - // Gate-based logic elimination; eliminate signals and push constant across cell boundaries - // Instant propagation makes lots-o-constant reduction possibilities. - if (v3Global.opt.oGate()) { - V3Gate::gateAll(v3Global.rootp()); - // V3Gate calls constant propagation itself. - } else { - v3info("Command Line disabled gate optimization with -Og/-O0. This may cause ordering problems."); - } + // Gate-based logic elimination; eliminate signals and push constant across cell boundaries + // Instant propagation makes lots-o-constant reduction possibilities. + if (v3Global.opt.oGate()) { + V3Gate::gateAll(v3Global.rootp()); + // V3Gate calls constant propagation itself. + } else { + v3info("Command Line disabled gate optimization with -Og/-O0. This may cause ordering problems."); + } - // Combine COVERINCs with duplicate terms - if (v3Global.opt.coverage()) { - V3CoverageJoin::coverageJoin(v3Global.rootp()); - } + // Combine COVERINCs with duplicate terms + if (v3Global.opt.coverage()) { + V3CoverageJoin::coverageJoin(v3Global.rootp()); + } - // Remove unused vars - V3Const::constifyAll(v3Global.rootp()); - V3Dead::deadifyAllScoped(v3Global.rootp()); + // Remove unused vars + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyAllScoped(v3Global.rootp()); - // Clock domain crossing analysis - if (v3Global.opt.cdc()) { - V3Cdc::cdcAll(v3Global.rootp()); - V3Error::abortIfErrors(); - return; - } + // Clock domain crossing analysis + if (v3Global.opt.cdc()) { + V3Cdc::cdcAll(v3Global.rootp()); + V3Error::abortIfErrors(); + return; + } - // Reorder assignments in pipelined blocks - if (v3Global.opt.oReorder()) { - V3Split::splitReorderAll(v3Global.rootp()); - } + // Reorder assignments in pipelined blocks + if (v3Global.opt.oReorder()) { + V3Split::splitReorderAll(v3Global.rootp()); + } - // Create delayed assignments - // This creates lots of duplicate ACTIVES so ActiveTop needs to be after this step - V3Delayed::delayedAll(v3Global.rootp()); + // Create delayed assignments + // This creates lots of duplicate ACTIVES so ActiveTop needs to be after this step + V3Delayed::delayedAll(v3Global.rootp()); - // Make Active's on the top level - // Differs from V3Active, because identical clocks may be pushed down to a module and now be identical - V3ActiveTop::activeTopAll(v3Global.rootp()); + // Make Active's on the top level. + // Differs from V3Active, because identical clocks may be pushed + // down to a module and now be identical + V3ActiveTop::activeTopAll(v3Global.rootp()); - if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "PreOrder"); + if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "PreOrder"); - // Order the code; form SBLOCKs and BLOCKCALLs - V3Order::orderAll(v3Global.rootp()); + // Order the code; form SBLOCKs and BLOCKCALLs + V3Order::orderAll(v3Global.rootp()); - // Change generated clocks to look at delayed signals - V3GenClk::genClkAll(v3Global.rootp()); + // Change generated clocks to look at delayed signals + V3GenClk::genClkAll(v3Global.rootp()); - // Convert sense lists into IF statements. - V3Clock::clockAll(v3Global.rootp()); + // Convert sense lists into IF statements. + V3Clock::clockAll(v3Global.rootp()); - // Cleanup any dly vars or other temps that are simple assignments - // Life must be done before Subst, as it assumes each CFunc under _eval is called only once. - if (v3Global.opt.oLife()) { - V3Const::constifyAll(v3Global.rootp()); - V3Life::lifeAll(v3Global.rootp()); - } - if (v3Global.opt.oLifePost()) { - V3LifePost::lifepostAll(v3Global.rootp()); - } + // Cleanup any dly vars or other temps that are simple assignments + // Life must be done before Subst, as it assumes each CFunc under + // _eval is called only once. + if (v3Global.opt.oLife()) { + V3Const::constifyAll(v3Global.rootp()); + V3Life::lifeAll(v3Global.rootp()); + } + if (v3Global.opt.oLifePost()) { + V3LifePost::lifepostAll(v3Global.rootp()); + } - // Remove unused vars - V3Const::constifyAll(v3Global.rootp()); - V3Dead::deadifyAllScoped(v3Global.rootp()); + // Remove unused vars + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyAllScoped(v3Global.rootp()); - // Detect change loop - V3Changed::changedAll(v3Global.rootp()); + // Detect change loop + V3Changed::changedAll(v3Global.rootp()); - // Create tracing logic, since we ripped out some signals the user might want to trace - // Note past this point, we presume traced variables won't move between CFuncs - // (It's OK if untraced temporaries move around, or vars "effectively" activate the same way.) - if (v3Global.opt.trace()) { - V3Trace::traceAll(v3Global.rootp()); - } + // Create tracing logic, since we ripped out some signals the user might want to trace + // Note past this point, we presume traced variables won't move between CFuncs + // (It's OK if untraced temporaries move around, or vars + // "effectively" activate the same way.) + if (v3Global.opt.trace()) { + V3Trace::traceAll(v3Global.rootp()); + } - if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Scoped"); + if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "Scoped"); - // Remove scopes; make varrefs/funccalls relative to current module - V3Descope::descopeAll(v3Global.rootp()); + // Remove scopes; make varrefs/funccalls relative to current module + V3Descope::descopeAll(v3Global.rootp()); } //--MODULE OPTIMIZATIONS-------------- if (!v3Global.opt.xmlOnly()) { - // Split deep blocks to appease MSVC++. Must be before Localize. - if (!v3Global.opt.lintOnly() && v3Global.opt.compLimitBlocks()) { - V3DepthBlock::depthBlockAll(v3Global.rootp()); - } + // Split deep blocks to appease MSVC++. Must be before Localize. + if (!v3Global.opt.lintOnly() && v3Global.opt.compLimitBlocks()) { + V3DepthBlock::depthBlockAll(v3Global.rootp()); + } - // Move BLOCKTEMPS from class to local variables - if (v3Global.opt.oLocalize()) { - V3Localize::localizeAll(v3Global.rootp()); - } + // Move BLOCKTEMPS from class to local variables + if (v3Global.opt.oLocalize()) { + V3Localize::localizeAll(v3Global.rootp()); + } - // Icache packing; combine common code in each module's functions into subroutines - if (v3Global.opt.oCombine()) { - V3Combine::combineAll(v3Global.rootp()); - } + // Icache packing; combine common code in each module's functions into subroutines + if (v3Global.opt.oCombine()) { + V3Combine::combineAll(v3Global.rootp()); + } } V3Error::abortIfErrors(); @@ -453,41 +457,41 @@ void process() { //--GENERATION------------------ if (!v3Global.opt.xmlOnly()) { - // Remove unused vars - V3Const::constifyAll(v3Global.rootp()); - V3Dead::deadifyAll(v3Global.rootp()); + // Remove unused vars + V3Const::constifyAll(v3Global.rootp()); + V3Dead::deadifyAll(v3Global.rootp()); - // Here down, widthMin() is the Verilog width, and width() is the C++ width - // Bits between widthMin() and width() are irrelevant, but may be non zero. - v3Global.widthMinUsage(VWidthMinUsage::VERILOG_WIDTH); + // Here down, widthMin() is the Verilog width, and width() is the C++ width + // Bits between widthMin() and width() are irrelevant, but may be non zero. + v3Global.widthMinUsage(VWidthMinUsage::VERILOG_WIDTH); - // Make all math operations either 8, 16, 32 or 64 bits - V3Clean::cleanAll(v3Global.rootp()); + // Make all math operations either 8, 16, 32 or 64 bits + V3Clean::cleanAll(v3Global.rootp()); - // Move wide constants to BLOCK temps. - V3Premit::premitAll(v3Global.rootp()); + // Move wide constants to BLOCK temps. + V3Premit::premitAll(v3Global.rootp()); } // Expand macros and wide operators into C++ primitives if (!v3Global.opt.xmlOnly() - && v3Global.opt.oExpand()) { - V3Expand::expandAll(v3Global.rootp()); + && v3Global.opt.oExpand()) { + V3Expand::expandAll(v3Global.rootp()); } // Propagate constants across WORDSEL arrayed temporaries if (!v3Global.opt.xmlOnly() - && v3Global.opt.oSubst()) { - // Constant folding of expanded stuff - V3Const::constifyCpp(v3Global.rootp()); - V3Subst::substituteAll(v3Global.rootp()); + && v3Global.opt.oSubst()) { + // Constant folding of expanded stuff + V3Const::constifyCpp(v3Global.rootp()); + V3Subst::substituteAll(v3Global.rootp()); } if (!v3Global.opt.xmlOnly() - && v3Global.opt.oSubstConst()) { - // Constant folding of substitutions - V3Const::constifyCpp(v3Global.rootp()); + && v3Global.opt.oSubstConst()) { + // Constant folding of substitutions + V3Const::constifyCpp(v3Global.rootp()); - V3Dead::deadifyAll(v3Global.rootp()); + V3Dead::deadifyAll(v3Global.rootp()); } if (!v3Global.opt.lintOnly() @@ -499,32 +503,32 @@ void process() { } if (!v3Global.opt.lintOnly() - && !v3Global.opt.xmlOnly()) { - // Fix very deep expressions - // Mark evaluation functions as member functions, if needed. - V3Depth::depthAll(v3Global.rootp()); + && !v3Global.opt.xmlOnly()) { + // Fix very deep expressions + // Mark evaluation functions as member functions, if needed. + V3Depth::depthAll(v3Global.rootp()); - // Branch prediction - V3Branch::branchAll(v3Global.rootp()); + // Branch prediction + V3Branch::branchAll(v3Global.rootp()); - // Add C casts when longs need to become long-long and vice-versa - // Note depth may insert something needing a cast, so this must be last. - V3Cast::castAll(v3Global.rootp()); + // Add C casts when longs need to become long-long and vice-versa + // Note depth may insert something needing a cast, so this must be last. + V3Cast::castAll(v3Global.rootp()); } V3Error::abortIfErrors(); if (!v3Global.opt.lintOnly() - && !v3Global.opt.xmlOnly()) { - V3CCtors::cctorsAll(); + && !v3Global.opt.xmlOnly()) { + V3CCtors::cctorsAll(); } // Output the text if (!v3Global.opt.lintOnly() - && !v3Global.opt.xmlOnly()) { - // emitcInlines is first, as it may set needHInlines which other emitters read - V3EmitC::emitcInlines(); - V3EmitC::emitcSyms(); - V3EmitC::emitcTrace(); + && !v3Global.opt.xmlOnly()) { + // emitcInlines is first, as it may set needHInlines which other emitters read + V3EmitC::emitcInlines(); + V3EmitC::emitcSyms(); + V3EmitC::emitcTrace(); } if (!v3Global.opt.xmlOnly() && v3Global.opt.mtasks()) { @@ -534,25 +538,25 @@ void process() { // costs of mtasks. V3Partition::finalize(); } - if (!v3Global.opt.xmlOnly()) { // Unfortunately we have some lint checks in emitc. - V3EmitC::emitc(); + if (!v3Global.opt.xmlOnly()) { // Unfortunately we have some lint checks in emitc. + V3EmitC::emitc(); } if (v3Global.opt.xmlOnly() - // Check XML when debugging to make sure no missing node types - || (v3Global.opt.debugCheck() && !v3Global.opt.lintOnly())) { - V3EmitXml::emitxml(); + // Check XML when debugging to make sure no missing node types + || (v3Global.opt.debugCheck() && !v3Global.opt.lintOnly())) { + V3EmitXml::emitxml(); } // Statistics if (v3Global.opt.stats()) { - V3Stats::statsFinalAll(v3Global.rootp()); - V3Stats::statsReport(); + V3Stats::statsFinalAll(v3Global.rootp()); + V3Stats::statsReport(); } if (!v3Global.opt.lintOnly() - && !v3Global.opt.xmlOnly()) { - // Makefile must be after all other emitters - V3EmitMk::emitmk(v3Global.rootp()); + && !v3Global.opt.xmlOnly()) { + // Makefile must be after all other emitters + V3EmitMk::emitmk(v3Global.rootp()); } // Note early return above when opt.cdc() @@ -578,13 +582,13 @@ int main(int argc, char** argv, char** env) { // Command option parsing v3Global.opt.bin(argv[0]); string argString = V3Options::argString(argc-1, argv+1); - v3Global.opt.parseOpts(new FileLine("COMMAND_LINE",0), argc-1, argv+1); + v3Global.opt.parseOpts(new FileLine("COMMAND_LINE", 0), argc-1, argv+1); if (!v3Global.opt.outFormatOk() - && !v3Global.opt.preprocOnly() - && !v3Global.opt.lintOnly() - && !v3Global.opt.xmlOnly() - && !v3Global.opt.cdc()) { - v3fatal("verilator: Need --cc, --sc, --cdc, --lint-only, --xml_only or --E option"); + && !v3Global.opt.preprocOnly() + && !v3Global.opt.lintOnly() + && !v3Global.opt.xmlOnly() + && !v3Global.opt.cdc()) { + v3fatal("verilator: Need --cc, --sc, --cdc, --lint-only, --xml_only or --E option"); } // Check environment V3Options::getenvSYSTEMC(); @@ -597,12 +601,13 @@ int main(int argc, char** argv, char** env) { // Can we skip doing everything if times are ok? V3File::addSrcDepend(v3Global.opt.bin()); if (v3Global.opt.skipIdentical() - && !v3Global.opt.preprocOnly() - && !v3Global.opt.lintOnly() - && !v3Global.opt.cdc() - && V3File::checkTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__verFiles.dat", argString)) { - UINFO(1,"--skip-identical: No change to any source files, exiting\n"); - exit(0); + && !v3Global.opt.preprocOnly() + && !v3Global.opt.lintOnly() + && !v3Global.opt.cdc() + && V3File::checkTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix() + +"__verFiles.dat", argString)) { + UINFO(1,"--skip-identical: No change to any source files, exiting\n"); + exit(0); } //--FRONTEND------------------ @@ -628,7 +633,7 @@ int main(int argc, char** argv, char** env) { // Link, etc, if needed if (!v3Global.opt.preprocOnly()) { - process(); + process(); } // Final steps @@ -636,12 +641,13 @@ int main(int argc, char** argv, char** env) { V3Error::abortIfWarnings(); if (!v3Global.opt.lintOnly() && !v3Global.opt.cdc() - && v3Global.opt.makeDepend()) { - V3File::writeDepend(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__ver.d"); + && v3Global.opt.makeDepend()) { + V3File::writeDepend(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__ver.d"); } if (!v3Global.opt.lintOnly() && !v3Global.opt.cdc() - && (v3Global.opt.skipIdentical() || v3Global.opt.makeDepend())) { - V3File::writeTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__verFiles.dat", argString); + && (v3Global.opt.skipIdentical() || v3Global.opt.makeDepend())) { + V3File::writeTimes(v3Global.opt.makeDir()+"/"+v3Global.opt.prefix() + +"__verFiles.dat", argString); } // Final writing shouldn't throw warnings, but... diff --git a/src/VlcMain.cpp b/src/VlcMain.cpp index 2a50d5a46..67d7a5507 100644 --- a/src/VlcMain.cpp +++ b/src/VlcMain.cpp @@ -59,9 +59,9 @@ bool VlcOptions::onoff(const char* sw, const char* arg, bool& flag) { // if sw=="-noarg", then return true (found it), and flag=false // else return false if (arg[0]!='-') v3fatalSrc("OnOff switches must have leading dash."); - if (0==strcmp(sw,arg)) { flag = true; return true; } - else if (0==strncmp(sw,"-no",3) && (0==strcmp(sw+3,arg+1))) { flag = false; return true; } - else if (0==strncmp(sw,"-no-",4) && (0==strcmp(sw+4,arg+1))) { flag = false; return true; } + if (0==strcmp(sw, arg)) { flag = true; return true; } + else if (0==strncmp(sw, "-no", 3) && (0==strcmp(sw+3, arg+1))) { flag = false; return true; } + else if (0==strncmp(sw, "-no-", 4) && (0==strcmp(sw+4, arg+1))) { flag = false; return true; } return false; } @@ -79,34 +79,34 @@ void VlcOptions::parseOptsList(int argc, char** argv) { if (sw[0]=='-' && sw[1]=='-') ++sw; if (0) {} // Single switches - else if ( onoff (sw, "-annotate-all", flag/*ref*/) ) { m_annotateAll = flag; } - else if ( onoff (sw, "-rank", flag/*ref*/) ) { m_rank = flag; } - else if ( onoff (sw, "-unlink", flag/*ref*/) ) { m_unlink = flag; } + else if (onoff (sw, "-annotate-all", flag/*ref*/) ) { m_annotateAll = flag; } + else if (onoff (sw, "-rank", flag/*ref*/) ) { m_rank = flag; } + else if (onoff (sw, "-unlink", flag/*ref*/) ) { m_unlink = flag; } // Parameterized switches - else if ( !strcmp (sw, "-annotate-min") && (i+1)second; for (VlcSource::ColumnMap::iterator cit=cmap.begin(); cit!=cmap.end(); ++cit) { VlcSourceCount& col = cit->second; - //UINFO(0,"Source "<second; for (VlcSource::ColumnMap::iterator cit=cmap.begin(); cit!=cmap.end(); ++cit) { VlcSourceCount& col = cit->second; - //UINFO(0,"Source "<