diff --git a/Changes b/Changes index bc886515c..843fc5ae2 100644 --- a/Changes +++ b/Changes @@ -24,6 +24,7 @@ The contributors that suggested a given feature are shown in []. Thanks! **** Fix parsing error on bad missing #, bug1308. [Dan Kirkham] +**** Fix Verilation performance issues, bug1316. [John Coiner] * Verilator 3.922 2018-03-17 diff --git a/src/V3Active.cpp b/src/V3Active.cpp index bf50c350c..23d78cf3c 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -44,6 +44,8 @@ #include "V3Ast.h" #include "V3EmitCBase.h" #include "V3Const.h" +#include "V3SenTree.h" // for SenTreeSet +#include VL_INCLUDE_UNORDERED_MAP //***** See below for main transformation engine @@ -62,7 +64,11 @@ private: 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 - std::vector m_activeVec; // List of sensitive actives, for folding + + 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"); @@ -73,7 +79,8 @@ private: m_scopep = nodep; m_iActivep = NULL; m_cActivep = NULL; - m_activeVec.clear(); + m_activeSens.clear(); + m_activeMap.clear(); iterateChildren(nodep); // Don't clear scopep, the namer persists beyond this visit } @@ -112,22 +119,15 @@ public: } AstActive* getActive(FileLine* fl, AstSenTree* sensesp) { // Return a sentree in this scope that matches given sense list. - // Not the fastest, but scopes tend to have few clocks + AstActive* activep = NULL; - //sitemsp->dumpTree(cout," Lookingfor: "); - for (std::vector::iterator it = m_activeVec.begin(); it!=m_activeVec.end(); ++it) { - activep = *it; - if (activep) { // Not deleted - // Compare the list - AstSenTree* asenp = activep->sensesp(); - if (asenp->sameTree(sensesp)) { - UINFO(8," Found ACTIVE "<second; + } + // Not found, form a new one if (!activep) { AstSenTree* newsenp = sensesp->cloneTree(false); @@ -136,7 +136,8 @@ public: UINFO(8," New ACTIVE "<fileline(), "sequentdly", m_activep->sensesp()); - m_activep->addNext(newactp); + // Was addNext(), but addNextHere() avoids a linear search. + m_activep->addNextHere(newactp); return newactp; } void checkActivePost(AstVarRef* varrefp, AstActive* oldactivep) { diff --git a/src/V3Hashed.cpp b/src/V3Hashed.cpp index 36d2fefb6..4069acaa4 100644 --- a/src/V3Hashed.cpp +++ b/src/V3Hashed.cpp @@ -52,17 +52,19 @@ private: // STATE V3Hash m_lowerHash; // Hash of the statement we're building + bool m_cacheInUser4; // Use user4 to cache each V3Hash? // METHODS VL_DEBUG_FUNC; // Declare debug() void nodeHashIterate(AstNode* nodep) { - if (!nodep->user4()) { + V3Hash thisHash; + 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)"); } - V3Hash oldHash = m_lowerHash; + V3Hash oldHash = m_lowerHash; { m_lowerHash = nodep->sameHash(); if (m_lowerHash.isIllegal()) { @@ -76,10 +78,12 @@ private: nodep->user4(m_lowerHash.fullValue()); //UINFO(9, " hashnode "<(nodep)); + } + V3Hash finalHash() const { return m_lowerHash; } virtual ~HashedVisitor() {} }; //###################################################################### // Hashed class functions +V3Hash V3Hashed::uncachedHash(const AstNode* nodep) { + HashedVisitor visitor(nodep); + return visitor.finalHash(); +} + V3Hashed::iterator V3Hashed::hashAndInsert(AstNode* nodep) { hash(nodep); return m_hashMmap.insert(make_pair(nodeHash(nodep), nodep)); diff --git a/src/V3Hashed.h b/src/V3Hashed.h index 7a2c157e5..d020a4d85 100644 --- a/src/V3Hashed.h +++ b/src/V3Hashed.h @@ -83,6 +83,8 @@ public: void dumpFile(const string& filename, bool tree); void dumpFilePrefixed(const string& nameComment, bool tree=false); static V3Hash nodeHash(AstNode* nodep) { return V3Hash(nodep->user4p()); } + // Hash of the nodep tree, without caching in user4. + static V3Hash uncachedHash(const AstNode* nodep); }; #endif // Guard diff --git a/src/V3SenTree.h b/src/V3SenTree.h index f27177819..49dfab33a 100644 --- a/src/V3SenTree.h +++ b/src/V3SenTree.h @@ -41,19 +41,69 @@ #include #include #include +#include VL_INCLUDE_UNORDERED_SET #include "V3Global.h" #include "V3Ast.h" +#include "V3Hashed.h" //###################################################################### // Collect SenTrees under the entire scope // And provide functions to find/add a new one +class SenTreeSet { + // Hash table of sensitive blocks. +private: + // TYPES + class HashSenTree { + public: + HashSenTree() {} + size_t operator() (const AstSenTree* kp) const { + return V3Hashed::uncachedHash(kp).fullValue(); + } + private: + VL_UNCOPYABLE(HashSenTree); + }; + + class EqSenTree { + public: + EqSenTree() {} + bool operator() (const AstSenTree* ap, const AstSenTree* bp) const { + return ap->sameTree(bp); + } + private: + VL_UNCOPYABLE(EqSenTree); + }; + + // MEMBERS + typedef vl_unordered_set Set; + Set m_trees; // Set of sensitive blocks, for folding. + +public: + // CONSTRUCTORS + SenTreeSet() {} + + // METHODS + void add(AstSenTree* nodep) { m_trees.insert(nodep); } + AstSenTree* find(AstSenTree* likep) { + AstSenTree* resultp = NULL; + Set::iterator it = m_trees.find(likep); + if (it != m_trees.end()) { + resultp = *it; + } + return resultp; + } + void clear() { m_trees.clear(); } + +private: + VL_UNCOPYABLE(SenTreeSet); +}; + class SenTreeFinder : public AstNVisitor { private: // STATE - AstTopScope* m_topscopep; // Top scope to add statement to - std::vector m_treesp; // List of sensitive blocks, for folding + AstTopScope* m_topscopep; // Top scope to add statement to + SenTreeSet m_trees; // Set of sensitive blocks, for folding // VISITORS VL_DEBUG_FUNC; // Declare debug() @@ -77,9 +127,7 @@ private: // Don't grab SenTrees under Actives, only those that are global (under Scope directly) iterateChildren(nodep); } - virtual void visit(AstSenTree* nodep) { - m_treesp.push_back(nodep); - } + virtual void visit(AstSenTree* nodep) { m_trees.add(nodep); } // Empty visitors, speed things up virtual void visit(AstNodeStmt* nodep) { } virtual void visit(AstNode* nodep) { @@ -89,31 +137,18 @@ private: public: void clear() { m_topscopep = NULL; - m_treesp.clear(); + m_trees.clear(); } AstSenTree* getSenTree(FileLine* fl, AstSenTree* sensesp) { // Return a global sentree that matches given sense list. - // Not the fastest, but there tend to be few clocks - AstSenTree* treep = NULL; - //sensesp->dumpTree(cout," Lookingfor: "); - for (std::vector::iterator it = m_treesp.begin(); it!=m_treesp.end(); ++it) { - treep = *it; - if (treep) { // Not deleted - if (treep->sameTree(sensesp)) { - UINFO(8," Found SBLOCK "<cloneTree(false); m_topscopep->addStmtsp(treep); UINFO(8," New SENTREE "<