diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 17cb7d36c..beec3fb81 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1410,8 +1410,7 @@ struct AstRepeat : public AstNodeStmt { ASTNODE_NODE_FUNCS(Repeat, REPEAT) AstNode* countp() const { return op2p()->castNode(); } // op2= condition to continue AstNode* bodysp() const { return op3p()->castNode(); } // op3= body of loop - virtual bool isGateOptimizable() const { return false; } - virtual bool isPredictOptimizable() const { return false; } + virtual bool isGateOptimizable() const { return false; } // Not releavant - converted to FOR virtual int instrCount() const { return instrCountBranch(); } virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(AstNode* samep) const { return true; } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 41258613f..a9c273918 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -830,10 +830,8 @@ private: void replaceWithSimulation(AstNode* nodep) { SimulateVisitor simvis; - simvis.mainParamCheck(nodep); - if (simvis.optimizable()) { // Run it - may also be unoptimizable due to large for loop - simvis.mainParamEmulate(nodep); - } + // 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 " diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 4dc2548ce..0c8a2ff98 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -74,7 +74,7 @@ private: // STATE // Major mode - bool m_checking; ///< Checking vs. simulation mode + bool m_checkOnly; ///< Checking only (no simulation) mode bool m_scoped; ///< Running with AstVarScopes instead of AstVars bool m_params; ///< Doing parameter propagation // Checking: @@ -102,6 +102,8 @@ 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) { @@ -112,7 +114,7 @@ public: m_whyNotOptimizable = why; } } - bool optimizable() const { return m_whyNotNodep==NULL; } + inline bool optimizable() const { return m_whyNotNodep==NULL; } string whyNotMessage() const { return m_whyNotOptimizable; } AstNode* whyNotNodep() const { return m_whyNotNodep; } @@ -189,8 +191,10 @@ private: } void checkNodeInfo(AstNode* nodep) { - m_instrCount += nodep->instrCount(); - m_dataCount += nodep->width(); + if (m_checkOnly) { + m_instrCount += nodep->instrCount(); + m_dataCount += nodep->width(); + } if (!nodep->isPredictOptimizable()) { //UINFO(9," !predictopt "<prettyTypeName()<prettyTypeName()<v3fatalSrc("Optimizable should have been cleared in check step, and never reach simulation."); } } @@ -229,42 +229,41 @@ private: // VISITORS virtual void visit(AstAlways* nodep, AstNUser*) { - if (m_checking) checkNodeInfo(nodep); + checkNodeInfo(nodep); nodep->iterateChildren(*this); } virtual void visit(AstSenTree* nodep, AstNUser*) { // Sensitivities aren't inputs per se; we'll keep our tree under the same sens. } virtual void visit(AstVarRef* nodep, AstNUser*) { + if (!optimizable()) return; // Accelerate AstNode* vscp = varOrScope(nodep); - if (m_checking) { - if (m_checking && !optimizable()) return; // Accelerate - // 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 (nodep->varp()->arraysp()) clearOptimizable(nodep,"Array references"); - if (nodep->lvalue()) { - if (m_inDlyAssign) { - if (!(vscp->user1() & VU_LVDLY)) { - vscp->user1( vscp->user1() | VU_LVDLY); - 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); - varRefCb (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. + if (nodep->varp()->arraysp()) clearOptimizable(nodep,"Array references"); + if (nodep->lvalue()) { + if (m_inDlyAssign) { + if (!(vscp->user1() & VU_LVDLY)) { + vscp->user1( vscp->user1() | VU_LVDLY); + 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); - 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); + if (m_checkOnly) varRefCb (nodep); + } } - else { // simulating + if (!m_checkOnly && optimizable()) { // simulating if (nodep->lvalue()) { nodep->v3fatalSrc("LHS varref should be handled in AstAssign visitor."); } else { @@ -293,52 +292,47 @@ private: } virtual void visit(AstNodeIf* nodep, AstNUser*) { UINFO(5," IF "<iterateChildren(*this); } else { nodep->condp()->iterateAndNext(*this); - if (fetchNumber(nodep->condp())->isNeqZero()) { - nodep->ifsp()->iterateAndNext(*this); - } else { - nodep->elsesp()->iterateAndNext(*this); + if (optimizable()) { + if (fetchNumber(nodep->condp())->isNeqZero()) { + nodep->ifsp()->iterateAndNext(*this); + } else { + nodep->elsesp()->iterateAndNext(*this); + } } } } virtual void visit(AstConst* nodep, AstNUser*) { - if (m_checking) { - checkNodeInfo(nodep); - } else { + checkNodeInfo(nodep); + if (!m_checkOnly && optimizable()) { setNumber(nodep, &(nodep->num())); } } virtual void visit(AstNodeUniop* nodep, AstNUser*) { - if (m_checking) { - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); - nodep->iterateChildren(*this); - } else { - nodep->iterateChildren(*this); + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp())); } } virtual void visit(AstNodeBiop* nodep, AstNUser*) { - if (m_checking) { - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); - nodep->iterateChildren(*this); - } else { - nodep->iterateChildren(*this); + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()), *fetchNumber(nodep->rhsp())); } } virtual void visit(AstNodeTriop* nodep, AstNUser*) { - if (m_checking) { - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); - nodep->iterateChildren(*this); - } else { - nodep->iterateChildren(*this); + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + if (!m_checkOnly && optimizable()) { nodep->numberOperate(*newNumber(nodep), *fetchNumber(nodep->lhsp()), *fetchNumber(nodep->rhsp()), @@ -348,57 +342,65 @@ private: virtual void visit(AstNodeCond* nodep, AstNUser*) { // We could use above visit(AstNodeTriop), but it's slower even O(n^2) to evaluate // both sides when we really only need to evaluate one side. - if (m_checking) { - if (!optimizable()) return; // Accelerate - checkNodeInfo(nodep); + if (!optimizable()) return; // Accelerate + checkNodeInfo(nodep); + if (m_checkOnly) { nodep->iterateChildren(*this); } else { nodep->condp()->accept(*this); - if (fetchNumber(nodep->condp())->isNeqZero()) { - nodep->expr1p()->accept(*this); - newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p())); - } else { - nodep->expr2p()->accept(*this); - newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p())); + if (optimizable()) { + if (fetchNumber(nodep->condp())->isNeqZero()) { + nodep->expr1p()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->expr1p())); + } else { + nodep->expr2p()->accept(*this); + newNumber(nodep)->opAssign(*fetchNumber(nodep->expr2p())); + } } } } virtual void visit(AstNodeAssign* nodep, AstNUser*) { - if (m_checking) { - if (!optimizable()) return; // Accelerate - if (nodep->castAssignDly()) { - 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; - } - nodep->iterateChildren(*this); + if (!optimizable()) return; // Accelerate + if (nodep->castAssignDly()) { + 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 (!nodep->lhsp()->castVarRef()) { clearOptimizable(nodep, "LHS isn't simple variable"); } - else if (!m_checking) { + else if (m_checkOnly) { + nodep->iterateChildren(*this); + } + else if (optimizable()) { nodep->rhsp()->iterateAndNext(*this); - AstNode* vscp = varOrScope(nodep->lhsp()->castVarRef()); - // Copy by value, not reference, as we don't want a=a+1 to get right results - if (nodep->castAssignDly()) { - // Don't do setNumber, as value isn't yet visible to following statements - newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); - } else { - newNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); - newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); + if (optimizable()) { + AstNode* vscp = varOrScope(nodep->lhsp()->castVarRef()); + // Copy by value, not reference, as we don't want a=a+1 to get right results + if (nodep->castAssignDly()) { + // Don't do setNumber, as value isn't yet visible to following statements + newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); + } else { + newNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); + newOutNumber(vscp)->opAssign(*fetchNumber(nodep->rhsp())); + } } } m_inDlyAssign = false; } + virtual void visit(AstBegin* nodep, AstNUser*) { + checkNodeInfo(nodep); + nodep->iterateChildren(*this); + } virtual void visit(AstNodeCase* nodep, AstNUser*) { UINFO(5," CASE "<iterateChildren(*this); - } else { + } else if (optimizable()) { nodep->exprp()->iterateAndNext(*this); bool hit = false; for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) { @@ -406,11 +408,13 @@ private: for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) { if (hit) break; ep->iterateAndNext(*this); - V3Number match (nodep->fileline(), 1); - match.opEq(*fetchNumber(nodep->exprp()), *fetchNumber(ep)); - if (match.isNeqZero()) { - itemp->bodysp()->iterateAndNext(*this); - hit = true; + if (optimizable()) { + V3Number match (nodep->fileline(), 1); + match.opEq(*fetchNumber(nodep->exprp()), *fetchNumber(ep)); + if (match.isNeqZero()) { + itemp->bodysp()->iterateAndNext(*this); + hit = true; + } } } } @@ -434,19 +438,28 @@ private: virtual void visit(AstComment*, AstNUser*) {} + virtual void visit(AstStop* nodep, AstNUser*) { + if (m_params) { // This message seems better than an obscure $stop + // The spec says $stop is just ignored, it seems evil to ignore assertions + clearOptimizable(nodep,"$stop executed during function constification; maybe indicates assertion firing"); + } + checkNodeInfo(nodep); + } + virtual void visit(AstNodeFor* nodep, AstNUser*) { // Doing lots of Whiles is slow, so only for parameters UINFO(5," FOR "<iterateChildren(*this); - } else { + } else if (optimizable()) { int loops = 0; nodep->initsp()->iterateAndNext(*this); while (1) { UINFO(5," FOR-ITER "<condp()->iterateAndNext(*this); + if (!optimizable()) break; if (!fetchNumber(nodep->condp())->isNeqZero()) { break; } @@ -464,15 +477,16 @@ private: // Doing lots of Whiles is slow, so only for parameters UINFO(5," WHILE "<iterateChildren(*this); - } else { + } else if (optimizable()) { int loops = 0; while (1) { UINFO(5," WHILE-ITER "<precondsp()->iterateAndNext(*this); nodep->condp()->iterateAndNext(*this); + if (!optimizable()) break; if (!fetchNumber(nodep->condp())->isNeqZero()) { break; } @@ -507,7 +521,7 @@ private: // Evaluate pin value pinp->accept(*this); // Apply value to the function - if (!m_checking) { + if (!m_checkOnly && optimizable()) { newNumber(stmtp)->opAssign(*fetchNumber(pinp)); } } @@ -516,7 +530,7 @@ private: } // Evaluate the function funcp->accept(*this); - if (!m_checking) { + if (!m_checkOnly && optimizable()) { // Grab return value from output variable newNumber(nodep)->opAssign(*fetchNumber(funcp->fvarp())); } @@ -540,8 +554,8 @@ public: setMode(false,false,false); clear(); // We reuse this structure in the main loop, so put initializers inside clear() } - void setMode(bool scoped, bool checking, bool params) { - m_checking = checking; + void setMode(bool scoped, bool checkOnly, bool params) { + m_checkOnly = checkOnly; m_scoped = scoped; m_params = params; } @@ -569,10 +583,6 @@ public: setMode(true/*scoped*/,false/*checking*/, false/*params*/); nodep->accept(*this); } - void mainParamCheck (AstNode* nodep) { - setMode(false/*scoped*/,true/*checking*/, true/*params*/); - nodep->accept(*this); - } void mainParamEmulate (AstNode* nodep) { setMode(false/*scoped*/,false/*checking*/, true/*params*/); nodep->accept(*this); diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 9b0e8268a..1423d69cf 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -295,6 +295,7 @@ private: // Simulate simvis.mainTableEmulate(nodep); + if (!simvis.optimizable()) simvis.whyNotNodep()->v3fatalSrc("Optimizable cleared, even though earlier test run said not: "<