diff --git a/src/V3Ast.h b/src/V3Ast.h index 741d4d461..5f0cb5df4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1287,30 +1287,24 @@ void AstNode::foreachImpl(ConstCorrectAstNode* nodep, const T_Callable& f using T_Arg_NonConst = typename std::remove_const::type; using Node = ConstCorrectAstNode; - // Traversal stack - std::vector stack; // Kept as a vector for easy resizing - Node** basep = nullptr; // Pointer to base of stack - Node** topp = nullptr; // Pointer to top of stack - Node** limp = nullptr; // Pointer to stack limit (when need growing) - // We prefetch this far into the stack - constexpr int prefetchDistance = 2; + constexpr int PrefetchDistance = 2; + // We push max 5 items per iteration + constexpr size_t MaxPushesPerIteration = 5; + // Initial stack sie + constexpr size_t InitialStackSize = 32; - // Grow stack to given size - const auto grow = [&](size_t size) { - const ptrdiff_t occupancy = topp - basep; - stack.resize(size); - basep = stack.data() + prefetchDistance; - topp = basep + occupancy; - limp = basep + size - 5; // We push max 5 items per iteration - }; - - // Initial stack size - grow(32); + // Traversal stack. Because we often iterate over small trees, use an in-line initial stack + size_t stackSize = InitialStackSize; + Node* inLineStack[InitialStackSize]; // In-line initial stack storage + std::unique_ptr outOfLineStackp{nullptr}; // In case we need more, we will allocate + Node** basep = inLineStack + PrefetchDistance; // Pointer to base of stack + Node** topp = basep; // Pointer to top of stack + Node** limp = inLineStack + stackSize - MaxPushesPerIteration; // Pointer to grow limit // We want some non-null pointers at the beginning. These will be prefetched, but not // visited, so the root node will suffice. This eliminates needing branches in the loop. - for (int i = -prefetchDistance; i; ++i) basep[i] = nodep; + for (int i = -PrefetchDistance; i; ++i) basep[i] = nodep; // Visit given node, enqueue children for traversal const auto visit = [&](Node* currp) { @@ -1343,10 +1337,23 @@ void AstNode::foreachImpl(ConstCorrectAstNode* nodep, const T_Callable& f Node* const headp = *--topp; // Prefetch in case we are ascending the tree - ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]); + ASTNODE_PREFETCH_NON_NULL(topp[-PrefetchDistance]); // Ensure we have stack space for nextp and the 4 children - if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2); + if (VL_UNLIKELY(topp >= limp)) { + const ptrdiff_t occupancy = topp - basep; + { + const size_t newStackSize = stackSize * 2; + Node** const newStackp = new Node*[newStackSize]; + std::memcpy(newStackp, basep - PrefetchDistance, + sizeof(Node**) * (occupancy + PrefetchDistance)); + stackSize = newStackSize; + outOfLineStackp.reset(newStackp); + } + basep = outOfLineStackp.get() + PrefetchDistance; + topp = basep + occupancy; + limp = outOfLineStackp.get() + stackSize - MaxPushesPerIteration; + } // Enqueue the next node if (headp->nextp()) *topp++ = headp->nextp(); @@ -1363,30 +1370,24 @@ bool AstNode::predicateImpl(ConstCorrectAstNode* nodep, const T_Callable& using T_Arg_NonConst = typename std::remove_const::type; using Node = ConstCorrectAstNode; - // Traversal stack - std::vector stack; // Kept as a vector for easy resizing - Node** basep = nullptr; // Pointer to base of stack - Node** topp = nullptr; // Pointer to top of stack - Node** limp = nullptr; // Pointer to stack limit (when need growing) - // We prefetch this far into the stack - constexpr int prefetchDistance = 2; + constexpr int PrefetchDistance = 2; + // We push max 5 items per iteration + constexpr size_t MaxPushesPerIteration = 5; + // Initial stack sie + constexpr size_t InitialStackSize = 32; - // Grow stack to given size - const auto grow = [&](size_t size) { - const ptrdiff_t occupancy = topp - basep; - stack.resize(size); - basep = stack.data() + prefetchDistance; - topp = basep + occupancy; - limp = basep + size - 5; // We push max 5 items per iteration - }; - - // Initial stack size - grow(32); + // Traversal stack. Because we often iterate over small trees, use an in-line initial stack + size_t stackSize = InitialStackSize; + Node* inLineStack[InitialStackSize]; // In-line initial stack storage + std::unique_ptr outOfLineStackp{nullptr}; // In case we need more, we will allocate + Node** basep = inLineStack + PrefetchDistance; // Pointer to base of stack + Node** topp = basep; // Pointer to top of stack + Node** limp = inLineStack + stackSize - MaxPushesPerIteration; // Pointer to grow limit // We want some non-null pointers at the beginning. These will be prefetched, but not // visited, so the root node will suffice. This eliminates needing branches in the loop. - for (int i = -prefetchDistance; i; ++i) basep[i] = nodep; + for (int i = -PrefetchDistance; i; ++i) basep[i] = nodep; // Visit given node, enqueue children for traversal, return true if result determined. const auto visit = [&](Node* currp) { @@ -1418,10 +1419,23 @@ bool AstNode::predicateImpl(ConstCorrectAstNode* nodep, const T_Callable& Node* const headp = *--topp; // Prefetch in case we are ascending the tree - ASTNODE_PREFETCH_NON_NULL(topp[-prefetchDistance]); + ASTNODE_PREFETCH_NON_NULL(topp[-PrefetchDistance]); // Ensure we have stack space for nextp and the 4 children - if (VL_UNLIKELY(topp >= limp)) grow(stack.size() * 2); + if (VL_UNLIKELY(topp >= limp)) { + const ptrdiff_t occupancy = topp - basep; + { + const size_t newStackSize = stackSize * 2; + Node** const newStackp = new Node*[newStackSize]; + std::memcpy(newStackp, basep - PrefetchDistance, + sizeof(Node**) * (occupancy + PrefetchDistance)); + stackSize = newStackSize; + outOfLineStackp.reset(newStackp); + } + basep = outOfLineStackp.get() + PrefetchDistance; + topp = basep + occupancy; + limp = outOfLineStackp.get() + stackSize - MaxPushesPerIteration; + } // Enqueue the next node if (headp->nextp()) *topp++ = headp->nextp(); diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index d6f3444d7..6061ce315 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -170,6 +170,11 @@ AstCExpr::AstCExpr(FileLine* fl, const string& textStmt, int setwidth, bool clea if (setwidth) dtypeSetLogicSized(setwidth, VSigning::UNSIGNED); } +bool AstVar::sameNode(const AstNode* samep) const { + const AstVar* const asamep = VN_DBG_AS(samep, Var); + return m_name == asamep->m_name && varType() == asamep->varType(); +} + AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access) : ASTGEN_SUPER_VarRef(fl, varp, access) {} AstVarRef::AstVarRef(FileLine* fl, AstNodeModule* pkgp, AstVar* varp, const VAccess& access) @@ -190,7 +195,7 @@ bool AstVarRef::sameNode(const AstVarRef* samep) const { } else { return (selfPointer() == samep->selfPointer() && classOrPackagep() == samep->classOrPackagep() && access() == samep->access() - && (varp() && samep->varp() && varp()->name() == samep->varp()->name())); + && (varp() && samep->varp() && varp()->sameNode(samep->varp()))); } } bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const { @@ -200,7 +205,7 @@ bool AstVarRef::sameNoLvalue(const AstVarRef* samep) const { return (selfPointer() == samep->selfPointer() && classOrPackagep() == samep->classOrPackagep() && (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty()) - && varp()->name() == samep->varp()->name()); + && varp()->sameNode(samep->varp())); } } diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index ed3f46ee1..d9dc1976c 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1704,6 +1704,7 @@ public: VSigning numeric); AstBasicDType* findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, const VNumRange& range, int widthMin, VSigning numeric); + AstBasicDType* findCreateSameDType(AstBasicDType& node); AstBasicDType* findInsertSameDType(AstBasicDType* nodep); AstConstraintRefDType* findConstraintRefDType(FileLine* fl); AstEmptyQueueDType* findEmptyQueueDType(FileLine* fl); @@ -2003,7 +2004,7 @@ public: ASTGEN_MEMBERS_AstVar; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; - bool sameNode(const AstNode* samep) const override; + inline bool sameNode(const AstNode* samep) const override; string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool hasDType() const override VL_MT_SAFE { return true; } bool maybePointedTo() const override VL_MT_SAFE { return true; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 3c0b6e3ca..bd50f7c53 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1355,46 +1355,38 @@ AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) { } AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, VBasicDTypeKwd kwd) { - if (m_basicps[kwd]) return m_basicps[kwd]; - // - AstBasicDType* const new1p = new AstBasicDType{fl, kwd}; - // Because the detailed map doesn't update this map, - // check the detailed map for this same node - // Also adds this new node to the detailed map - AstBasicDType* const newp = findInsertSameDType(new1p); - if (newp != new1p) { - VL_DO_DANGLING(new1p->deleteTree(), new1p); - } else { - addTypesp(newp); + // Because the detailed map doesn't update m_basicps, check the detailed + // map for this same node. Also adds this new node to the detailed map + if (!m_basicps[kwd]) { + AstBasicDType basic{fl, kwd}; + m_basicps[kwd] = findCreateSameDType(basic); } - // - m_basicps[kwd] = newp; - return newp; + return m_basicps[kwd]; } AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, int width, int widthMin, VSigning numeric) { - AstBasicDType* const new1p = new AstBasicDType{fl, kwd, numeric, width, widthMin}; - AstBasicDType* const newp = findInsertSameDType(new1p); - if (newp != new1p) { - VL_DO_DANGLING(new1p->deleteTree(), new1p); - } else { - addTypesp(newp); - } - return newp; + AstBasicDType basic{fl, kwd, numeric, width, widthMin}; + return findCreateSameDType(basic); } AstBasicDType* AstTypeTable::findLogicBitDType(FileLine* fl, VBasicDTypeKwd kwd, const VNumRange& range, int widthMin, VSigning numeric) { - AstBasicDType* const new1p = new AstBasicDType{fl, kwd, numeric, range, widthMin}; - AstBasicDType* const newp = findInsertSameDType(new1p); - if (newp != new1p) { - VL_DO_DANGLING(new1p->deleteTree(), new1p); - } else { - addTypesp(newp); + AstBasicDType basic{fl, kwd, numeric, range, widthMin}; + return findCreateSameDType(basic); +} + +AstBasicDType* AstTypeTable::findCreateSameDType(AstBasicDType& node) { + const VBasicTypeKey key{node.width(), node.widthMin(), node.numeric(), node.keyword(), + node.nrange()}; + AstBasicDType*& entryr = m_detailedMap[key]; + if (!entryr) { + entryr = node.cloneTree(false); + entryr->generic(true); + addTypesp(entryr); } - return newp; + return entryr; } // cppcheck-suppress duplInheritedMember @@ -2785,10 +2777,6 @@ void AstVar::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, ignoreSchedWrite); dumpJsonGen(str); } -bool AstVar::sameNode(const AstNode* samep) const { - const AstVar* const asamep = VN_DBG_AS(samep, Var); - return name() == asamep->name() && varType() == asamep->varType(); -} void AstScope::dump(std::ostream& str) const { this->AstNode::dump(str); str << " [abovep=" << nodeAddr(aboveScopep()) << "]"; diff --git a/src/V3DfgCache.h b/src/V3DfgCache.h index 10c31764d..03aad9892 100644 --- a/src/V3DfgCache.h +++ b/src/V3DfgCache.h @@ -167,55 +167,47 @@ using CacheTernary = Cache; // These return a reference to the mapped entry, inserting a nullptr if not yet exists inline DfgSel*& getEntry(CacheSel& cache, const DfgDataType& dtype, DfgVertex* src0p, uint32_t lsb) { - return cache - .emplace(std::piecewise_construct, // - std::forward_as_tuple(src0p, lsb, dtype.size()), // - std::forward_as_tuple(nullptr)) - .first->second; + const KeySel key{src0p, lsb, dtype.size()}; + return cache[key]; } inline DfgVertexUnary*& getEntry(CacheUnary& cache, const DfgDataType&, DfgVertex* src0p) { - return cache - .emplace(std::piecewise_construct, // - std::forward_as_tuple(src0p), // - std::forward_as_tuple(nullptr)) - .first->second; + const KeyUnary key{src0p}; + return cache[key]; } inline DfgVertexBinary*& getEntry(CacheBinary& cache, const DfgDataType&, DfgVertex* src0p, DfgVertex* src1p) { - return cache - .emplace(std::piecewise_construct, // - std::forward_as_tuple(src0p, src1p), // - std::forward_as_tuple(nullptr)) - .first->second; + const KeyBinary key{src0p, src1p}; + return cache[key]; } inline DfgVertexTernary*& getEntry(CacheTernary& cache, const DfgDataType&, DfgVertex* src0p, DfgVertex* src1p, DfgVertex* src2p) { - return cache - .emplace(std::piecewise_construct, // - std::forward_as_tuple(src0p, src1p, src2p), // - std::forward_as_tuple(nullptr)) - .first->second; + const KeyTernary key{src0p, src1p, src2p}; + return cache[key]; } // These return a reference to the mapped entry, inserting a nullptr if not yet exists inline CacheSel::iterator find(CacheSel& cache, DfgVertex* src0p, uint32_t lsb, uint32_t size) { - return cache.find({src0p, lsb, size}); + const KeySel key{src0p, lsb, size}; + return cache.find(key); } inline CacheUnary::iterator find(CacheUnary& cache, DfgVertex* src0p) { - return cache.find({src0p}); + const KeyUnary key{src0p}; + return cache.find(key); } inline CacheBinary::iterator find(CacheBinary& cache, DfgVertex* src0p, DfgVertex* src1p) { - return cache.find({src0p, src1p}); + const KeyBinary key{src0p, src1p}; + return cache.find(key); } inline CacheTernary::iterator find(CacheTernary& cache, DfgVertex* src0p, DfgVertex* src1p, DfgVertex* src2p) { - return cache.find({src0p, src1p, src2p}); + const KeyTernary key{src0p, src1p, src2p}; + return cache.find(key); } // These set the operands of a new vertex diff --git a/src/V3DfgDataType.h b/src/V3DfgDataType.h index 450795f3f..4a84f576c 100644 --- a/src/V3DfgDataType.h +++ b/src/V3DfgDataType.h @@ -137,18 +137,18 @@ public: // Returns a Packed type of the given width static const DfgDataType& packed(uint32_t width) { // Find or create the right sized packed type - const auto pair = s_packedTypes.emplace(width, nullptr); - if (pair.second) pair.first->second = new DfgDataType{width}; - return *pair.first->second; + const DfgDataType*& entryr = s_packedTypes[width]; + if (!entryr) entryr = new DfgDataType{width}; + return *entryr; } // Returns an Array type of the given size with the given elements static const DfgDataType& array(const DfgDataType& elemType, uint32_t size) { UASSERT(elemType.isPacked(), "Cannot create multi-dimensional arrays yet"); // Find or create the right sized array type with this as elements - const auto pair = elemType.m_arrayTypes.emplace(size, nullptr); - if (pair.second) pair.first->second = new DfgDataType{size, elemType}; - return *pair.first->second; + const DfgDataType*& entryr = elemType.m_arrayTypes[size]; + if (!entryr) entryr = new DfgDataType{size, elemType}; + return *entryr; } // Returns the singleton Null type diff --git a/src/V3File.cpp b/src/V3File.cpp index 464bd0e86..177912568 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -651,10 +651,18 @@ V3OutFormatter::V3OutFormatter(V3OutFormatter::Language lang) //---------------------------------------------------------------------- -string V3OutFormatter::indentSpaces(int num) { +static constexpr int MAXSPACE = 80; // After this indent, stop indenting more + +// Table of spaces, so we don't have to alloc/free them all the time +static const std::array s_indentSpaces = []() { + std::array table; + for (int i = 0; i <= MAXSPACE; ++i) table[i] = std::string(static_cast(i), ' '); + return table; +}(); + +const std::string& V3OutFormatter::indentSpaces(int num) { // Indent the specified number of spaces. - if (num <= 0) return std::string{}; - return std::string(std::min(num, MAXSPACE), ' '); + return s_indentSpaces[std::max(0, std::min(num, MAXSPACE))]; } bool V3OutFormatter::tokenMatch(const char* cp, const char* cmp) { diff --git a/src/V3File.h b/src/V3File.h index 54f532611..56d808efb 100644 --- a/src/V3File.h +++ b/src/V3File.h @@ -106,7 +106,6 @@ public: class V3OutFormatter VL_NOT_FINAL { // TYPES - static constexpr int MAXSPACE = 80; // After this indent, stop indenting more public: enum AlignClass : uint8_t { AL_AUTO = 0, AL_STATIC = 1 }; enum Language : uint8_t { LA_C, LA_JSON, LA_MK, LA_VERILOG, LA_XML }; @@ -173,7 +172,7 @@ public: if (!m_nobreak) puts("\n"); } // STATIC METHODS - static string indentSpaces(int num); + static const std::string& indentSpaces(int num); // Add escaped characters to strings static string quoteNameControls(const string& namein, Language lang = LA_C) VL_PURE; static bool tokenMatch(const char* cp, const char* cmp); diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 27ad85ef7..d6cf9c1ea 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -66,7 +66,9 @@ class FileLineSingleton final { ~FileLineSingleton() = default; fileNameIdx_t nameToNumber(const string& filename); - string numberToName(fileNameIdx_t filenameno) const VL_MT_SAFE { return m_names[filenameno]; } + const std::string& numberToName(fileNameIdx_t filenameno) const VL_MT_SAFE { + return m_names[filenameno]; + } V3LangCode numberToLang(fileNameIdx_t filenameno) const { return m_languages[filenameno]; } void numberToLang(fileNameIdx_t filenameno, const V3LangCode& l) { m_languages[filenameno] = l; @@ -291,7 +293,9 @@ public: string ascii() const VL_MT_SAFE; string asciiLineCol() const; int filenameno() const VL_MT_SAFE { return m_filenameno; } - string filename() const VL_MT_SAFE { return singleton().numberToName(filenameno()); } + const std::string& filename() const VL_MT_SAFE { + return singleton().numberToName(filenameno()); + } // Filename with C string escapes string filenameEsc() const VL_MT_SAFE { return VString::quoteBackslash(filename()); } bool filenameIsGlobal() const VL_MT_SAFE { diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp index 7270451d0..2c24c1b24 100644 --- a/src/V3GraphAlg.cpp +++ b/src/V3GraphAlg.cpp @@ -465,22 +465,14 @@ void V3Graph::subtreeLoops(V3EdgeFuncP edgeFuncp, V3GraphVertex* vertexp, V3Grap //###################################################################### // Algorithms - sorting -struct GraphSortVertexCmp final { - bool operator()(const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) const { - return lhsp->sortCmp(rhsp) < 0; - } -}; -struct GraphSortEdgeCmp final { - bool operator()(const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) const { - return lhsp->sortCmp(rhsp) < 0; - } -}; - void V3Graph::sortVertices() { // Sort list of vertices by rank, then fanout std::vector vertexps; for (V3GraphVertex& vertex : m_vertices) vertexps.push_back(&vertex); - std::stable_sort(vertexps.begin(), vertexps.end(), GraphSortVertexCmp()); + std::stable_sort(vertexps.begin(), vertexps.end(), + [](const V3GraphVertex* lhsp, const V3GraphVertex* rhsp) { // + return lhsp->sortCmp(rhsp) < 0; + }); // Re-insert in sorted order for (V3GraphVertex* const vertexp : vertexps) { m_vertices.unlink(vertexp); @@ -495,7 +487,10 @@ void V3Graph::sortEdges() { // Make a vector for (V3GraphEdge& edge : vertex.outEdges()) edges.push_back(&edge); // Sort - std::stable_sort(edges.begin(), edges.end(), GraphSortEdgeCmp()); + std::stable_sort(edges.begin(), edges.end(), + [](const V3GraphEdge* lhsp, const V3GraphEdge* rhsp) { // + return lhsp->sortCmp(rhsp) < 0; + }); // Relink edges in specified order for (V3GraphEdge* const edgep : edges) edgep->relinkFromp(&vertex); // Prep for next diff --git a/src/V3Hash.h b/src/V3Hash.h index 088de7b88..ee51ad208 100644 --- a/src/V3Hash.h +++ b/src/V3Hash.h @@ -64,12 +64,16 @@ public: V3Hash operator+(T that) const { return V3Hash{combine(m_value, V3Hash{that}.m_value)}; } + V3Hash operator+(const std::string& that) const { + return V3Hash{combine(m_value, V3Hash{that}.m_value)}; + } // '+=' combines in place template V3Hash& operator+=(T that) { return *this = *this + that; } + V3Hash& operator+=(const std::string& that) { return *this = *this + that; } }; std::ostream& operator<<(std::ostream& os, const V3Hash& rhs) VL_MT_SAFE; diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 276c0104a..de012e08d 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -64,55 +64,48 @@ class LifeVarEntry final { // Last assignment to this varscope, nullptr if no longer relevant AstNodeStmt* m_assignp = nullptr; AstConst* m_constp = nullptr; // Known constant value + bool m_isNew = true; // Is just created // First access was a set (and thus block above may have a set that can be deleted - bool m_setBeforeUse; + bool m_setBeforeUse = false; // Was ever assigned (and thus above block may not preserve constant propagation) bool m_everSet = false; public: - class CRESET {}; - class SIMPLEASSIGN {}; - class COMPLEXASSIGN {}; - class CONSUMED {}; - - LifeVarEntry(CRESET, AstCReset* nodep) - : m_setBeforeUse{true} { - resetStatement(nodep); - } - LifeVarEntry(SIMPLEASSIGN, AstNodeAssign* nodep) - : m_setBeforeUse{true} { - simpleAssign(nodep); - } - explicit LifeVarEntry(COMPLEXASSIGN) - : m_setBeforeUse{false} { - complexAssign(); - } - explicit LifeVarEntry(CONSUMED) - : m_setBeforeUse{false} { - consumed(); - } + LifeVarEntry() = default; ~LifeVarEntry() = default; + + void init(bool setBeforeUse) { + UASSERT(m_isNew, "Not a new entry"); + m_isNew = false; + m_setBeforeUse = setBeforeUse; + } + void simpleAssign(AstNodeAssign* nodep) { // New simple A=.... assignment + UASSERT_OBJ(!m_isNew, nodep, "Uninitialzized new entry"); m_assignp = nodep; m_constp = nullptr; m_everSet = true; if (VN_IS(nodep->rhsp(), Const)) m_constp = VN_AS(nodep->rhsp(), Const); } void resetStatement(AstCReset* nodep) { // New CReset(A) assignment + UASSERT_OBJ(!m_isNew, nodep, "Uninitialzized new entry"); m_assignp = nodep; m_constp = nullptr; m_everSet = true; } void complexAssign() { // A[x]=... or some complicated assignment + UASSERT(!m_isNew, "Uninitialzized new entry"); m_assignp = nullptr; m_constp = nullptr; m_everSet = true; } void consumed() { // Rvalue read of A + UASSERT(!m_isNew, "Uninitialzized new entry"); m_assignp = nullptr; } AstNodeStmt* assignp() const { return m_assignp; } AstConst* constNodep() const { return m_constp; } + bool isNew() const { return m_isNew; } bool setBeforeUse() const { return m_setBeforeUse; } bool everSet() const { return m_everSet; } }; @@ -126,9 +119,9 @@ class LifeBlock final { // 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 - using LifeMap = std::unordered_map; - LifeMap m_map; // Current active lifetime map for current scope + // For each basic block, we'll make a new map of what variables that if/else is changing + // Current active lifetime map for current scope + std::unordered_map m_map; LifeBlock* const m_aboveLifep; // Upper life, or nullptr LifeState* const m_statep; // Current global state bool m_replacedVref = false; // Replaced a variable reference since last clearing @@ -140,20 +133,19 @@ public: , m_statep{statep} {} ~LifeBlock() = default; // METHODS - void checkRemoveAssign(const LifeMap::iterator& it) { - const AstVar* const varp = it->first->varp(); - LifeVarEntry* const entp = &(it->second); + void checkRemoveAssign(AstVarScope* vscp, LifeVarEntry& entr) { + const AstVar* const varp = vscp->varp(); if (!varp->isSigPublic() && !varp->sensIfacep()) { // 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 (AstNodeStmt* const oldassp = entp->assignp()) { + if (AstNodeStmt* const oldassp = entr.assignp()) { UINFO(7, " PREV: " << oldassp); // Redundant assignment, in same level block // Don't delete it now as it will confuse iteration since it maybe WAY // above our current iteration point. UINFOTREE(7, oldassp, "", "REMOVE/SAMEBLK"); - entp->complexAssign(); + entr.complexAssign(); oldassp->unlinkFrBack(); if (VN_IS(oldassp, CReset)) { ++m_statep->m_statCResetDel; @@ -168,40 +160,43 @@ public: // Do we have a old assignment we can nuke? UINFO(4, " CRESETof: " << nodep); UINFO(7, " new: " << rstp); - const auto pair = m_map.emplace(std::piecewise_construct, // - std::forward_as_tuple(nodep), - std::forward_as_tuple(LifeVarEntry::CRESET{}, rstp)); - if (!pair.second) { - checkRemoveAssign(pair.first); - pair.first->second.resetStatement(rstp); + LifeVarEntry& entr = m_map[nodep]; + if (entr.isNew()) { + entr.init(true); + } else { + checkRemoveAssign(nodep, entr); } + entr.resetStatement(rstp); // lifeDump(); } void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) { // Do we have a old assignment we can nuke? UINFO(4, " ASSIGNof: " << nodep); UINFO(7, " new: " << assp); - const auto pair = m_map.emplace(std::piecewise_construct, // - std::forward_as_tuple(nodep), - std::forward_as_tuple(LifeVarEntry::SIMPLEASSIGN{}, assp)); - if (!pair.second) { - checkRemoveAssign(pair.first); - pair.first->second.simpleAssign(assp); + LifeVarEntry& entr = m_map[nodep]; + if (entr.isNew()) { + entr.init(true); + } else { + checkRemoveAssign(nodep, entr); } + entr.simpleAssign(assp); // lifeDump(); } void complexAssign(AstVarScope* nodep) { UINFO(4, " clearof: " << nodep); - const auto pair = m_map.emplace(nodep, LifeVarEntry::COMPLEXASSIGN{}); - if (!pair.second) pair.first->second.complexAssign(); + LifeVarEntry& entr = m_map[nodep]; + if (entr.isNew()) entr.init(false); + entr.complexAssign(); } void clearReplaced() { m_replacedVref = false; } bool replaced() const { return m_replacedVref; } void varUsageReplace(AstVarScope* nodep, AstVarRef* varrefp) { // Variable rvalue. If it references a constant, we can replace it - const auto pair = m_map.emplace(nodep, LifeVarEntry::CONSUMED{}); - if (!pair.second) { - if (AstConst* const constp = pair.first->second.constNodep()) { + LifeVarEntry& entr = m_map[nodep]; + if (entr.isNew()) { + entr.init(false); + } else { + if (AstConst* const constp = entr.constNodep()) { if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->sensIfacep()) { // Aha, variable is constant; substitute in. // We'll later constant propagate @@ -214,19 +209,19 @@ public: } } UINFO(4, " usage: " << nodep); - pair.first->second.consumed(); } + entr.consumed(); } void complexAssignFind(AstVarScope* nodep) { - const auto pair = m_map.emplace(nodep, LifeVarEntry::COMPLEXASSIGN{}); - if (!pair.second) { - UINFO(4, " casfind: " << pair.first->first); - pair.first->second.complexAssign(); - } + UINFO(4, " casfind: " << nodep); + LifeVarEntry& entr = m_map[nodep]; + if (entr.isNew()) entr.init(false); + entr.complexAssign(); } void consumedFind(AstVarScope* nodep) { - const auto pair = m_map.emplace(nodep, LifeVarEntry::CONSUMED{}); - if (!pair.second) pair.first->second.consumed(); + LifeVarEntry& entr = m_map[nodep]; + if (entr.isNew()) entr.init(false); + entr.consumed(); } void lifeToAbove() { // Any varrefs under a if/else branch affect statements outside and after the if/else @@ -259,7 +254,7 @@ public: // Both branches set the var, we can remove the assignment before the IF. UINFO(4, "DUALBRANCH " << nodep); const auto itab = m_map.find(nodep); - if (itab != m_map.end()) checkRemoveAssign(itab); + if (itab != m_map.end()) checkRemoveAssign(nodep, itab->second); } } // this->lifeDump(); @@ -287,12 +282,7 @@ class LifeVisitor final : public VNVisitor { bool m_sideEffect = false; // Side effects discovered in assign RHS bool m_noopt = false; // Disable optimization of variables in this block bool m_tracingCall = false; // Iterating into a CCall to a CFunc - - // LIFE MAP - // For each basic block, we'll make a new map of what variables that if/else is changing - using LifeMap = std::unordered_map; - // cppcheck-suppress memleak // cppcheck bug - it is deleted - LifeBlock* m_lifep; // Current active lifetime map for current scope + LifeBlock* m_lifep = nullptr; // Current active lifetime map for current scope // METHODS void setNoopt() { diff --git a/src/V3Localize.cpp b/src/V3Localize.cpp index 43cc60555..423a3e2c6 100644 --- a/src/V3Localize.cpp +++ b/src/V3Localize.cpp @@ -206,7 +206,7 @@ class LocalizeVisitor final : public VNVisitor { AstVarScope* const varScopep = nodep->varScopep(); // Remember this function accesses this VarScope (we always need this as we might optimize // this VarScope into a local, even if it's not assigned. See 'isOptimizable') - m_accessors(varScopep).emplace(m_cfuncp); + m_accessors(varScopep).insert(m_cfuncp); // emplace performs a temporary malloc // Remember the reference so we can fix it up later (we always need this as well) m_references(m_cfuncp).emplace(varScopep, nodep); diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 671a542c3..443003fcb 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -195,6 +195,8 @@ public: // For getline() string m_lineChars; ///< Characters left for next line + string m_tokenBuf; // Token buffer, to avoid repeated alloc/free + void v3errorEnd(std::ostringstream& str) VL_RELEASE(V3Error::s().m_mutex) { fileline()->v3errorEnd(str); } @@ -1687,7 +1689,7 @@ int V3PreProcImp::getFinalToken(string& buf) { if (!m_finAhead) { m_finAhead = true; m_finToken = getStateToken(); - m_finBuf = string{yyourtext(), yyourleng()}; + m_finBuf.assign(yyourtext(), yyourleng()); } const int tok = m_finToken; buf = m_finBuf; @@ -1748,10 +1750,10 @@ string V3PreProcImp::getline() { const char* rtnp; bool gotEof = false; while (nullptr == (rtnp = std::strchr(m_lineChars.c_str(), '\n')) && !gotEof) { - string buf; - const int tok = getFinalToken(buf /*ref*/); + m_tokenBuf.clear(); + const int tok = getFinalToken(m_tokenBuf /*ref*/); if (debug() >= 5) { - const string bufcln = V3PreLex::cleanDbgStrg(buf); + const string bufcln = V3PreLex::cleanDbgStrg(m_tokenBuf); const string flcol = m_lexp->m_tokFilelinep->asciiLineCol(); UINFO(0, flcol << ": GETFETC: " << tokenName(tok) << ": " << bufcln); } @@ -1762,7 +1764,7 @@ string V3PreProcImp::getline() { } gotEof = true; } else { - m_lineChars.append(buf); + m_lineChars.append(m_tokenBuf); } } diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index 5d5c3e20f..4ac334e2d 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -169,11 +169,6 @@ public: //###################################################################### // Top Stats class -void V3Stats::addStatSum(const string& name, double count) VL_MT_SAFE_EXCLUDES(s_mutex) { - V3LockGuard lock{s_mutex}; - addStat(V3Statistic{"*", name, count, 0, true}); -} - void V3Stats::statsStageAll(AstNetlist* nodep, const std::string& stage, bool fastOnly) { StatsVisitor{nodep, stage, fastOnly}; } diff --git a/src/V3Stats.h b/src/V3Stats.h index 6cf5eddb9..8f8e8c690 100644 --- a/src/V3Stats.h +++ b/src/V3Stats.h @@ -90,7 +90,7 @@ public: } // CONSTRUCTORS V3Statistic(const string& stage, const string& name, double value, unsigned precision, - bool sumit = false, bool perf = false) + bool sumit, bool perf) : m_name{name} , m_value{value} , m_precision{precision} @@ -121,13 +121,22 @@ public: static void addStat(const V3Statistic&); static void addStat(const string& stage, const string& name, double value, unsigned precision = 0) { - addStat(V3Statistic{stage, name, value, precision}); + addStat(V3Statistic{stage, name, value, precision, false, false}); } static void addStat(const string& name, double value, unsigned precision = 0) { - addStat(V3Statistic{"*", name, value, precision}); + addStat(V3Statistic{"*", name, value, precision, false, false}); } // Add summary statistic - Threadsafe _unlike most other functions here_ - static void addStatSum(const string& name, double count) VL_MT_SAFE_EXCLUDES(s_mutex); + static void addStatSum(const char* name, double count) VL_MT_SAFE_EXCLUDES(s_mutex) { + // Avoid memory blow-up when called frequently with zero adds, + // e.g. from V3Const invoked on individual expressions. + if (count == 0.0) return; + V3LockGuard lock{s_mutex}; + addStat(V3Statistic{"*", name, count, 0, true, false}); + } + static void addStatSum(const std::string& name, double count) { + addStatSum(name.c_str(), count); + } static void addStatPerf(const string& name, double value) { addStat(V3Statistic{"*", name, value, 6, true, true}); } diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 0bf1bdd92..32350cae6 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -160,12 +160,7 @@ class StatsReport final { public: // METHODS - static void addStat(const V3Statistic& stat) { - // Avoid memory blow-up when called frequently with zero adds, - // e.g. from V3Const invoked on individual expressions. - if (stat.sumit() && stat.value() == 0.0) return; - s_allStats.push_back(stat); - } + static void addStat(const V3Statistic& stat) { s_allStats.push_back(stat); } static double getStatSum(const string& name) { // O(n^2) if called a lot; present assumption is only a small call count diff --git a/src/V3String.cpp b/src/V3String.cpp index ad040081b..8fdb580cf 100644 --- a/src/V3String.cpp +++ b/src/V3String.cpp @@ -327,10 +327,6 @@ string VString::replaceWord(const string& str, const string& from, const string& return result; } -bool VString::startsWith(const string& str, const string& prefix) { - return str.rfind(prefix, 0) == 0; // Faster than .find(_) == 0 -} - bool VString::endsWith(const string& str, const string& suffix) { if (str.length() < suffix.length()) return false; return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0; diff --git a/src/V3String.h b/src/V3String.h index 874e732e8..435a7d506 100644 --- a/src/V3String.h +++ b/src/V3String.h @@ -139,7 +139,12 @@ public: // e.g.: replaceWords("one apple bad_apple", "apple", "banana") -> "one banana bad_apple" static string replaceWord(const string& str, const string& from, const string& to); // Predicate to check if 'str' starts with 'prefix' - static bool startsWith(const string& str, const string& prefix); + static bool startsWith(const string& str, const char* prefixp) { + return !str.rfind(prefixp, 0); // Faster than .find(_) == 0 + } + static bool startsWith(const string& str, const string& prefix) { + return !str.rfind(prefix, 0); // Faster than .find(_) == 0 + } // Predicate to check if 'str' ends with 'suffix' static bool endsWith(const string& str, const string& suffix); // Return proper article (a/an) for a word. May be inaccurate for some special words