diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 18c9fea8c..6c6070574 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -97,8 +97,8 @@ protected: result = vertexp->user(); break; case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings) - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (latchCheckInternal(castVertexp(edgep->top()))) { + for (V3GraphEdge& edge : vertexp->outEdges()) { + if (latchCheckInternal(castVertexp(edge.top()))) { result = true; break; } @@ -106,9 +106,8 @@ protected: break; case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling) // A BRANCH vertex always has exactly 2 siblings - LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top()); - LatchDetectGraphVertex* const elsp - = castVertexp(vertexp->outBeginp()->outNextp()->top()); + LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outEdges().frontp()->top()); + LatchDetectGraphVertex* const elsp = castVertexp(vertexp->outEdges().backp()->top()); result = latchCheckInternal(ifp) && latchCheckInternal(elsp); break; } @@ -170,7 +169,7 @@ public: for (const auto& vrp : m_outputs) { LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p()); vertp->user(true); // Identify the output vertex we are checking paths _to_ - if (!latchCheckInternal(castVertexp(verticesBeginp()))) latch_detected = true; + if (!latchCheckInternal(castVertexp(vertices().frontp()))) latch_detected = true; if (latch_detected && !latch_expected) { nodep->v3warn( LATCH, diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 83feb35ff..a54120e7b 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -38,20 +38,21 @@ void DfgGraph::addGraph(DfgGraph& other) { m_size += other.m_size; other.m_size = 0; - const auto moveVertexList = [this](V3List& src, V3List& dst) { - if (DfgVertex* vtxp = src.begin()) { - vtxp->m_verticesEnt.moveAppend(src, dst, vtxp); - do { - vtxp->m_userCnt = 0; - vtxp->m_graphp = this; - vtxp = vtxp->verticesNext(); - } while (vtxp); - } - }; - - moveVertexList(other.m_varVertices, m_varVertices); - moveVertexList(other.m_constVertices, m_constVertices); - moveVertexList(other.m_opVertices, m_opVertices); + for (DfgVertexVar& vtx : other.m_varVertices) { + vtx.m_userCnt = 0; + vtx.m_graphp = this; + } + m_varVertices.splice(m_varVertices.end(), other.m_varVertices); + for (DfgConst& vtx : other.m_constVertices) { + vtx.m_userCnt = 0; + vtx.m_graphp = this; + } + m_constVertices.splice(m_constVertices.end(), other.m_constVertices); + for (DfgVertex& vtx : other.m_opVertices) { + vtx.m_userCnt = 0; + vtx.m_graphp = this; + } + m_opVertices.splice(m_opVertices.end(), other.m_opVertices); } static const string toDotId(const DfgVertex& vtx) { return '"' + cvtToHex(&vtx) + '"'; } diff --git a/src/V3Dfg.h b/src/V3Dfg.h index 859854604..0fe403686 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -55,6 +55,8 @@ #endif class DfgEdge; +class DfgVertex; +class DfgGraph; class DfgVisitor; //------------------------------------------------------------------------------ @@ -89,140 +91,6 @@ constexpr bool operator==(VDfgType lhs, VDfgType::en rhs) { return lhs.m_e == rh constexpr bool operator==(VDfgType::en lhs, VDfgType rhs) { return lhs == rhs.m_e; } inline std::ostream& operator<<(std::ostream& os, const VDfgType& t) { return os << t.ascii(); } -//------------------------------------------------------------------------------ -// Dataflow graph -//------------------------------------------------------------------------------ - -class DfgGraph final { - friend class DfgVertex; - - // TYPES - - // RAII handle for DfgVertex user data - class UserDataInUse final { - DfgGraph* m_graphp; // The referenced graph - - public: - // cppcheck-suppress noExplicitConstructor - UserDataInUse(DfgGraph* graphp) - : m_graphp{graphp} {} - // cppcheck-suppress noExplicitConstructor - UserDataInUse(UserDataInUse&& that) { - UASSERT(that.m_graphp, "Moving from empty"); - m_graphp = std::exchange(that.m_graphp, nullptr); - } - VL_UNCOPYABLE(UserDataInUse); - UserDataInUse& operator=(UserDataInUse&& that) { - UASSERT(that.m_graphp, "Moving from empty"); - m_graphp = std::exchange(that.m_graphp, nullptr); - return *this; - } - - ~UserDataInUse() { - if (m_graphp) m_graphp->m_userCurrent = 0; - } - }; - - // MEMBERS - - // Variables and constants make up a significant proportion of vertices (40-50% was observed - // in large designs), and they can often be treated specially in algorithms, which in turn - // enables significant Verilation performance gains, so we keep these in separate lists for - // direct access. - V3List m_varVertices; // The variable vertices in the graph - V3List m_constVertices; // The constant vertices in the graph - V3List m_opVertices; // The operation vertices in the graph - - size_t m_size = 0; // Number of vertices in the graph - uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use - uint32_t m_userCnt = 0; // Vertex user data generation counter - // Parent of the graph (i.e.: the module containing the logic represented by this graph). - AstModule* const m_modulep; - const string m_name; // Name of graph (for debugging) - -public: - // CONSTRUCTOR - explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED; - ~DfgGraph() VL_MT_DISABLED; - VL_UNCOPYABLE(DfgGraph); - - // METHODS -public: - // Add DfgVertex to this graph (assumes not yet contained). - inline void addVertex(DfgVertex& vtx); - // Remove DfgVertex form this graph (assumes it is contained). - inline void removeVertex(DfgVertex& vtx); - // Number of vertices in this graph - size_t size() const { return m_size; } - // Parent module - AstModule* modulep() const { return m_modulep; } - // Name of this graph - const string& name() const { return m_name; } - - // Reset Vertex user data - UserDataInUse userDataInUse() { - UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data"); - ++m_userCnt; - UASSERT(m_userCnt, "'m_userCnt' overflow"); - m_userCurrent = m_userCnt; - return UserDataInUse{this}; - } - - // Access to vertex lists for faster iteration in important contexts - inline DfgVertexVar* varVerticesBeginp() const; - inline DfgVertexVar* varVerticesRbeginp() const; - inline DfgConst* constVerticesBeginp() const; - inline DfgConst* constVerticesRbeginp() const; - inline DfgVertex* opVerticesBeginp() const; - inline DfgVertex* opVerticesRbeginp() const; - - // Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices - // in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however - // not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'. - inline void forEachVertex(std::function f); - - // 'const' variant of 'forEachVertex'. No mutation allowed. - inline void forEachVertex(std::function f) const; - - // Add contents of other graph to this graph. Leaves other graph empty. - void addGraph(DfgGraph& other) VL_MT_DISABLED; - - // Split this graph into individual components (unique sub-graphs with no edges between them). - // Also removes any vertices that are not weakly connected to any variable. - // Leaves 'this' graph empty. - std::vector> splitIntoComponents(std::string label) VL_MT_DISABLED; - - // Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at - // least one strongly connected component (SCC) plus any other vertices that feed or sink from - // the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to - // be cyclic, but they are not guaranteed to be strongly connected (however, they are always - // at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part - // of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed - // to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if - // it was originally connected. - std::vector> - extractCyclicComponents(std::string label) VL_MT_DISABLED; - - // Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of - // the graph which is included in the output. - void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED; - // Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to - // the name of the graph which is included in the output. - void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED; - // Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is - // added to the name of the graph, which is included in the file name and the output. - void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED; - // Dump upstream (source) logic cone starting from given vertex into a file with the given - // 'fileName'. 'name' is the name of the graph, which is included in the output. - void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, - const string& name = "") const VL_MT_DISABLED; - // Dump all individual logic cones driving external variables in Graphviz format into separate - // new automatically numbered debug files. 'label' is added to the name of the graph, which is - // included in the file names and the output. This is useful for very large graphs that are - // otherwise difficult to browse visually due to their size. - void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED; -}; - //------------------------------------------------------------------------------ // Dataflow graph edge //------------------------------------------------------------------------------ @@ -264,7 +132,8 @@ class DfgVertex VL_NOT_FINAL { using UserDataStorage = void*; // Storage allocated for user data // STATE - V3ListEnt m_verticesEnt; // V3List handle of this vertex, kept under the DfgGraph + V3ListLinks m_links; // V3List links + protected: DfgEdge* m_sinksp = nullptr; // List of sinks of this vertex FileLine* const m_filelinep; // Source location @@ -280,6 +149,14 @@ protected: public: virtual ~DfgVertex() VL_MT_DISABLED; +private: + V3ListLinks& links() { return m_links; } + +public: + // List type that can store Vertex (which must be a DfgVertex) instances via m_links + template + using List = V3List; + // METHODS private: // Visitor accept method @@ -349,54 +226,15 @@ public: // Retrieve user data, constructing it fresh on first try. template - T& user() { - static_assert(sizeof(T) <= sizeof(UserDataStorage), - "Size of user data type 'T' is too large for allocated storage"); - static_assert(alignof(T) <= alignof(UserDataStorage), - "Alignment of user data type 'T' is larger than allocated storage"); - T* const storagep = reinterpret_cast(&m_userDataStorage); - const uint32_t userCurrent = m_graphp->m_userCurrent; - UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");); - if (m_userCnt != userCurrent) { - m_userCnt = userCurrent; - // cppcheck-has-bug-suppress uninitvar - VL_ATTR_UNUSED T* const resultp = new (storagep) T{}; - UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd");); - } - return *storagep; - } + inline T& user(); // Retrieve user data, must be current. template - T& getUser() { - static_assert(sizeof(T) <= sizeof(UserDataStorage), - "Size of user data type 'T' is too large for allocated storage"); - static_assert(alignof(T) <= alignof(UserDataStorage), - "Alignment of user data type 'T' is larger than allocated storage"); - T* const storagep = reinterpret_cast(&m_userDataStorage); -#if VL_DEBUG - const uint32_t userCurrent = m_graphp->m_userCurrent; - UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"); - UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale"); -#endif - return *storagep; - } + inline T& getUser(); // Set user data, becomes current. template - typename std::enable_if::type setUser(T value) { - static_assert(sizeof(T) <= sizeof(UserDataStorage), - "Size of user data type 'T' is too large for allocated storage"); - static_assert(alignof(T) <= alignof(UserDataStorage), - "Alignment of user data type 'T' is larger than allocated storage"); - T* const storagep = reinterpret_cast(&m_userDataStorage); - const uint32_t userCurrent = m_graphp->m_userCurrent; -#if VL_DEBUG - UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"); -#endif - m_userCnt = userCurrent; - *storagep = value; - } + inline void setUser(T value); // Width of result uint32_t width() const { @@ -449,10 +287,6 @@ public: // Relink all sinks to be driven from the given new source void replaceWith(DfgVertex* newSourcep) VL_MT_DISABLED; - // Access to vertex list for faster iteration in important contexts - DfgVertex* verticesNext() const { return m_verticesEnt.nextp(); } - DfgVertex* verticesPrev() const { return m_verticesEnt.prevp(); } - // Calls given function 'f' for each source vertex of this vertex // Unconnected source edges are not iterated. inline void forEachSource(std::function f) VL_MT_DISABLED; @@ -567,9 +401,6 @@ public: virtual const string srcName(size_t idx) const = 0; }; -// Specializations of privateTypeTest -#include "V3Dfg__gen_type_tests.h" // From ./astgen - //------------------------------------------------------------------------------ // Dfg vertex visitor //------------------------------------------------------------------------------ @@ -583,152 +414,6 @@ public: #include "V3Dfg__gen_visitor_decls.h" // From ./astgen }; -//------------------------------------------------------------------------------ -// Inline method definitions -//------------------------------------------------------------------------------ - -void DfgGraph::addVertex(DfgVertex& vtx) { - // Note: changes here need to be replicated in DfgGraph::addGraph - ++m_size; - if (vtx.is()) { - vtx.m_verticesEnt.pushBack(m_constVertices, &vtx); - } else if (vtx.is()) { - vtx.m_verticesEnt.pushBack(m_varVertices, &vtx); - } else { - vtx.m_verticesEnt.pushBack(m_opVertices, &vtx); - } - vtx.m_userCnt = 0; - vtx.m_graphp = this; -} - -void DfgGraph::removeVertex(DfgVertex& vtx) { - // Note: changes here need to be replicated in DfgGraph::addGraph - --m_size; - if (vtx.is()) { - vtx.m_verticesEnt.unlink(m_constVertices, &vtx); - } else if (vtx.is()) { - vtx.m_verticesEnt.unlink(m_varVertices, &vtx); - } else { - vtx.m_verticesEnt.unlink(m_opVertices, &vtx); - } - vtx.m_userCnt = 0; - vtx.m_graphp = nullptr; -} - -void DfgGraph::forEachVertex(std::function f) { - for (DfgVertex *vtxp = m_varVertices.begin(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - f(*vtxp); - } - for (DfgVertex *vtxp = m_constVertices.begin(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - f(*vtxp); - } - for (DfgVertex *vtxp = m_opVertices.begin(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - f(*vtxp); - } -} - -void DfgGraph::forEachVertex(std::function f) const { - for (const DfgVertex* vtxp = m_varVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) { - f(*vtxp); - } - for (const DfgVertex* vtxp = m_constVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) { - f(*vtxp); - } - for (const DfgVertex* vtxp = m_opVertices.begin(); vtxp; vtxp = vtxp->verticesNext()) { - f(*vtxp); - } -} - -void DfgVertex::forEachSource(std::function f) { - const auto pair = sourceEdges(); - const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) { - if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep); - } -} - -void DfgVertex::forEachSource(std::function f) const { - const auto pair = sourceEdges(); - const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) { - if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep); - } -} - -void DfgVertex::forEachSink(std::function f) { - for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) { - nextp = edgep->m_nextp; - f(*edgep->m_sinkp); - } -} - -void DfgVertex::forEachSink(std::function f) const { - for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp); -} - -void DfgVertex::forEachSourceEdge(std::function f) { - const auto pair = sourceEdges(); - DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) f(edgesp[i], i); -} - -void DfgVertex::forEachSourceEdge(std::function f) const { - const auto pair = sourceEdges(); - const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) f(edgesp[i], i); -} - -void DfgVertex::forEachSinkEdge(std::function f) { - for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) { - nextp = edgep->m_nextp; - f(*edgep); - } -} - -void DfgVertex::forEachSinkEdge(std::function f) const { - for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) { - nextp = edgep->m_nextp; - f(*edgep); - } -} - -const DfgEdge* DfgVertex::findSourceEdge(std::function p) const { - const auto pair = sourceEdges(); - const DfgEdge* const edgesp = pair.first; - const size_t arity = pair.second; - for (size_t i = 0; i < arity; ++i) { - const DfgEdge& edge = edgesp[i]; - if (p(edge, i)) return &edge; - } - return nullptr; -} - -template -Vertex* DfgVertex::findSink(std::function p) const { - static_assert(std::is_base_of::value, - "'Vertex' must be subclass of 'DfgVertex'"); - for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) { - if (Vertex* const sinkp = edgep->m_sinkp->cast()) { - if (p(*sinkp)) return sinkp; - } - } - return nullptr; -} - -template -Vertex* DfgVertex::findSink() const { - static_assert(!std::is_same::value, - "'Vertex' must be proper subclass of 'DfgVertex'"); - return findSink([](const Vertex&) { return true; }); -} - //------------------------------------------------------------------------------ // DfgVertex sub-types follow //------------------------------------------------------------------------------ @@ -903,20 +588,281 @@ public: // The rest of the DfgVertex subclasses are generated by 'astgen' from AstNodeExpr nodes #include "V3Dfg__gen_auto_classes.h" -DfgVertexVar* DfgGraph::varVerticesBeginp() const { - return static_cast(m_varVertices.begin()); +//------------------------------------------------------------------------------ +// Dataflow graph +//------------------------------------------------------------------------------ + +class DfgGraph final { + friend class DfgVertex; + + // TYPES + + // RAII handle for DfgVertex user data + class UserDataInUse final { + DfgGraph* m_graphp; // The referenced graph + + public: + // cppcheck-suppress noExplicitConstructor + UserDataInUse(DfgGraph* graphp) + : m_graphp{graphp} {} + // cppcheck-suppress noExplicitConstructor + UserDataInUse(UserDataInUse&& that) { + UASSERT(that.m_graphp, "Moving from empty"); + m_graphp = std::exchange(that.m_graphp, nullptr); + } + VL_UNCOPYABLE(UserDataInUse); + UserDataInUse& operator=(UserDataInUse&& that) { + UASSERT(that.m_graphp, "Moving from empty"); + m_graphp = std::exchange(that.m_graphp, nullptr); + return *this; + } + + ~UserDataInUse() { + if (m_graphp) m_graphp->m_userCurrent = 0; + } + }; + + // MEMBERS + + // Variables and constants make up a significant proportion of vertices (40-50% was observed + // in large designs), and they can often be treated specially in algorithms, which in turn + // enables significant Verilation performance gains, so we keep these in separate lists for + // direct access. + DfgVertex::List m_varVertices; // The variable vertices in the graph + DfgVertex::List m_constVertices; // The constant vertices in the graph + DfgVertex::List m_opVertices; // The operation vertices in the graph + + size_t m_size = 0; // Number of vertices in the graph + uint32_t m_userCurrent = 0; // Vertex user data generation number currently in use + uint32_t m_userCnt = 0; // Vertex user data generation counter + // Parent of the graph (i.e.: the module containing the logic represented by this graph). + AstModule* const m_modulep; + const string m_name; // Name of graph (for debugging) + +public: + // CONSTRUCTOR + explicit DfgGraph(AstModule& module, const string& name = "") VL_MT_DISABLED; + ~DfgGraph() VL_MT_DISABLED; + VL_UNCOPYABLE(DfgGraph); + + // METHODS +public: + // Add DfgVertex to this graph (assumes not yet contained). + inline void addVertex(DfgVertex& vtx); + // Remove DfgVertex form this graph (assumes it is contained). + inline void removeVertex(DfgVertex& vtx); + // Number of vertices in this graph + size_t size() const { return m_size; } + // Parent module + AstModule* modulep() const { return m_modulep; } + // Name of this graph + const string& name() const { return m_name; } + + // Reset Vertex user data + UserDataInUse userDataInUse() { + UASSERT(!m_userCurrent, "Conflicting use of DfgVertex user data"); + ++m_userCnt; + UASSERT(m_userCnt, "'m_userCnt' overflow"); + m_userCurrent = m_userCnt; + return UserDataInUse{this}; + } + + // Access to vertex lists + DfgVertex::List& varVertices() { return m_varVertices; } + const DfgVertex::List& varVertices() const { return m_varVertices; } + DfgVertex::List& constVertices() { return m_constVertices; } + const DfgVertex::List& constVertices() const { return m_constVertices; } + DfgVertex::List& opVertices() { return m_opVertices; } + const DfgVertex::List& opVertices() const { return m_opVertices; } + + // Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices + // in the graph, or to delete/unlink the vertex passed to 'f' during iteration. It is however + // not safe to delete/unlink any vertex in the same graph other than the one passed to 'f'. + inline void forEachVertex(std::function f); + + // 'const' variant of 'forEachVertex'. No mutation allowed. + inline void forEachVertex(std::function f) const; + + // Add contents of other graph to this graph. Leaves other graph empty. + void addGraph(DfgGraph& other) VL_MT_DISABLED; + + // Split this graph into individual components (unique sub-graphs with no edges between them). + // Also removes any vertices that are not weakly connected to any variable. + // Leaves 'this' graph empty. + std::vector> splitIntoComponents(std::string label) VL_MT_DISABLED; + + // Extract cyclic sub-graphs from 'this' graph. Cyclic sub-graphs are those that contain at + // least one strongly connected component (SCC) plus any other vertices that feed or sink from + // the SCCs, up to a variable boundary. This means that the returned graphs are guaranteed to + // be cyclic, but they are not guaranteed to be strongly connected (however, they are always + // at least weakly connected). Trivial SCCs that are acyclic (i.e.: vertices that are not part + // of a cycle) are left in 'this' graph. This means that at the end 'this' graph is guaranteed + // to be a DAG (acyclic). 'this' will not necessarily be a connected graph at the end, even if + // it was originally connected. + std::vector> + extractCyclicComponents(std::string label) VL_MT_DISABLED; + + // Dump graph in Graphviz format into the given stream 'os'. 'label' is added to the name of + // the graph which is included in the output. + void dumpDot(std::ostream& os, const string& label = "") const VL_MT_DISABLED; + // Dump graph in Graphviz format into a new file with the given 'fileName'. 'label' is added to + // the name of the graph which is included in the output. + void dumpDotFile(const string& fileName, const string& label = "") const VL_MT_DISABLED; + // Dump graph in Graphviz format into a new automatically numbered debug file. 'label' is + // added to the name of the graph, which is included in the file name and the output. + void dumpDotFilePrefixed(const string& label = "") const VL_MT_DISABLED; + // Dump upstream (source) logic cone starting from given vertex into a file with the given + // 'fileName'. 'name' is the name of the graph, which is included in the output. + void dumpDotUpstreamCone(const string& fileName, const DfgVertex& vtx, + const string& name = "") const VL_MT_DISABLED; + // Dump all individual logic cones driving external variables in Graphviz format into separate + // new automatically numbered debug files. 'label' is added to the name of the graph, which is + // included in the file names and the output. This is useful for very large graphs that are + // otherwise difficult to browse visually due to their size. + void dumpDotAllVarConesPrefixed(const string& label = "") const VL_MT_DISABLED; +}; + +// Specializations of privateTypeTest +#include "V3Dfg__gen_type_tests.h" // From ./astgen + +//------------------------------------------------------------------------------ +// Inline method definitions - for DfgVertex +//------------------------------------------------------------------------------ + +template +T& DfgVertex::user() { + static_assert(sizeof(T) <= sizeof(UserDataStorage), + "Size of user data type 'T' is too large for allocated storage"); + static_assert(alignof(T) <= alignof(UserDataStorage), + "Alignment of user data type 'T' is larger than allocated storage"); + T* const storagep = reinterpret_cast(&m_userDataStorage); + const uint32_t userCurrent = m_graphp->m_userCurrent; + UDEBUGONLY(UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving");); + if (m_userCnt != userCurrent) { + m_userCnt = userCurrent; + // cppcheck-has-bug-suppress uninitvar + VL_ATTR_UNUSED T* const resultp = new (storagep) T{}; + UDEBUGONLY(UASSERT_OBJ(resultp == storagep, this, "Something is odd");); + } + return *storagep; } -DfgVertexVar* DfgGraph::varVerticesRbeginp() const { - return static_cast(m_varVertices.rbegin()); + +template +T& DfgVertex::getUser() { + static_assert(sizeof(T) <= sizeof(UserDataStorage), + "Size of user data type 'T' is too large for allocated storage"); + static_assert(alignof(T) <= alignof(UserDataStorage), + "Alignment of user data type 'T' is larger than allocated storage"); + T* const storagep = reinterpret_cast(&m_userDataStorage); +#if VL_DEBUG + const uint32_t userCurrent = m_graphp->m_userCurrent; + UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"); + UASSERT_OBJ(m_userCnt == userCurrent, this, "DfgVertex user data is stale"); +#endif + return *storagep; } -DfgConst* DfgGraph::constVerticesBeginp() const { - return static_cast(m_constVertices.begin()); + +template +void DfgVertex::setUser(T value) { + static_assert(sizeof(T) <= sizeof(UserDataStorage), + "Size of user data type 'T' is too large for allocated storage"); + static_assert(alignof(T) <= alignof(UserDataStorage), + "Alignment of user data type 'T' is larger than allocated storage"); + T* const storagep = reinterpret_cast(&m_userDataStorage); + const uint32_t userCurrent = m_graphp->m_userCurrent; +#if VL_DEBUG + UASSERT_OBJ(userCurrent, this, "DfgVertex user data used without reserving"); +#endif + m_userCnt = userCurrent; + *storagep = value; } -DfgConst* DfgGraph::constVerticesRbeginp() const { - return static_cast(m_constVertices.rbegin()); + +void DfgVertex::forEachSource(std::function f) { + const auto pair = sourceEdges(); + const DfgEdge* const edgesp = pair.first; + const size_t arity = pair.second; + for (size_t i = 0; i < arity; ++i) { + if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep); + } +} + +void DfgVertex::forEachSource(std::function f) const { + const auto pair = sourceEdges(); + const DfgEdge* const edgesp = pair.first; + const size_t arity = pair.second; + for (size_t i = 0; i < arity; ++i) { + if (DfgVertex* const sourcep = edgesp[i].m_sourcep) f(*sourcep); + } +} + +void DfgVertex::forEachSink(std::function f) { + for (const DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) { + nextp = edgep->m_nextp; + f(*edgep->m_sinkp); + } +} + +void DfgVertex::forEachSink(std::function f) const { + for (const DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) f(*edgep->m_sinkp); +} + +void DfgVertex::forEachSourceEdge(std::function f) { + const auto pair = sourceEdges(); + DfgEdge* const edgesp = pair.first; + const size_t arity = pair.second; + for (size_t i = 0; i < arity; ++i) f(edgesp[i], i); +} + +void DfgVertex::forEachSourceEdge(std::function f) const { + const auto pair = sourceEdges(); + const DfgEdge* const edgesp = pair.first; + const size_t arity = pair.second; + for (size_t i = 0; i < arity; ++i) f(edgesp[i], i); +} + +void DfgVertex::forEachSinkEdge(std::function f) { + for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) { + nextp = edgep->m_nextp; + f(*edgep); + } +} + +void DfgVertex::forEachSinkEdge(std::function f) const { + for (DfgEdge *edgep = m_sinksp, *nextp; edgep; edgep = nextp) { + nextp = edgep->m_nextp; + f(*edgep); + } +} + +const DfgEdge* DfgVertex::findSourceEdge(std::function p) const { + const auto pair = sourceEdges(); + const DfgEdge* const edgesp = pair.first; + const size_t arity = pair.second; + for (size_t i = 0; i < arity; ++i) { + const DfgEdge& edge = edgesp[i]; + if (p(edge, i)) return &edge; + } + return nullptr; +} + +template +Vertex* DfgVertex::findSink(std::function p) const { + static_assert(std::is_base_of::value, + "'Vertex' must be subclass of 'DfgVertex'"); + for (DfgEdge* edgep = m_sinksp; edgep; edgep = edgep->m_nextp) { + if (Vertex* const sinkp = edgep->m_sinkp->cast()) { + if (p(*sinkp)) return sinkp; + } + } + return nullptr; +} + +template +Vertex* DfgVertex::findSink() const { + static_assert(!std::is_same::value, + "'Vertex' must be proper subclass of 'DfgVertex'"); + return findSink([](const Vertex&) { return true; }); } -DfgVertex* DfgGraph::opVerticesBeginp() const { return m_opVertices.begin(); } -DfgVertex* DfgGraph::opVerticesRbeginp() const { return m_opVertices.rbegin(); } bool DfgVertex::isZero() const { if (const DfgConst* const constp = cast()) return constp->isZero(); @@ -928,4 +874,48 @@ bool DfgVertex::isOnes() const { return false; } +//------------------------------------------------------------------------------ +// Inline method definitions - for DfgGraph +//------------------------------------------------------------------------------ + +void DfgGraph::addVertex(DfgVertex& vtx) { + // Note: changes here need to be replicated in DfgGraph::addGraph + ++m_size; + if (DfgConst* const cVtxp = vtx.cast()) { + m_constVertices.linkBack(cVtxp); + } else if (DfgVertexVar* const vVtxp = vtx.cast()) { + m_varVertices.linkBack(vVtxp); + } else { + m_opVertices.linkBack(&vtx); + } + vtx.m_userCnt = 0; + vtx.m_graphp = this; +} + +void DfgGraph::removeVertex(DfgVertex& vtx) { + // Note: changes here need to be replicated in DfgGraph::addGraph + --m_size; + if (DfgConst* const cVtxp = vtx.cast()) { + m_constVertices.unlink(cVtxp); + } else if (DfgVertexVar* const vVtxp = vtx.cast()) { + m_varVertices.unlink(vVtxp); + } else { + m_opVertices.unlink(&vtx); + } + vtx.m_userCnt = 0; + vtx.m_graphp = nullptr; +} + +void DfgGraph::forEachVertex(std::function f) { + for (DfgVertexVar* const vtxp : m_varVertices.unlinkable()) f(*vtxp); + for (DfgConst* const vtxp : m_constVertices.unlinkable()) f(*vtxp); + for (DfgVertex* const vtxp : m_opVertices.unlinkable()) f(*vtxp); +} + +void DfgGraph::forEachVertex(std::function f) const { + for (const DfgVertexVar& vtx : m_varVertices) f(vtx); + for (const DfgConst& vtx : m_constVertices) f(vtx); + for (const DfgVertex& vtx : m_opVertices) f(vtx); +} + #endif diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index 446b0b7d0..815b960c5 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -44,13 +44,12 @@ class SplitIntoComponents final { queue.reserve(m_dfg.size()); // any sort of interesting logic must involve a variable, so we only need to iterate them - for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); + for (DfgVertexVar& vtx : m_dfg.varVertices()) { // If already assigned this vertex to a component, then continue - if (vtxp->user()) continue; + if (vtx.user()) continue; // Start depth first traversal at this vertex - queue.push_back(vtxp); + queue.push_back(&vtx); // Depth first traversal do { @@ -74,16 +73,15 @@ class SplitIntoComponents final { } } - void moveVertices(DfgVertex* headp) { - for (DfgVertex *vtxp = headp, *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - DfgVertex& vtx = *vtxp; - if (const size_t component = vtx.user()) { - m_dfg.removeVertex(vtx); - m_components[component - 1]->addVertex(vtx); + template + void moveVertices(DfgVertex::List& list) { + for (DfgVertex* const vtxp : list.unlinkable()) { + if (const size_t component = vtxp->user()) { + m_dfg.removeVertex(*vtxp); + m_components[component - 1]->addVertex(*vtxp); } else { // This vertex is not connected to a variable and is hence unused, remove here - vtx.unlinkDelete(m_dfg); + VL_DO_DANGLING(vtxp->unlinkDelete(m_dfg), vtxp); } } } @@ -101,9 +99,9 @@ class SplitIntoComponents final { m_components[i - 1].reset(new DfgGraph{*m_dfg.modulep(), m_prefix + cvtToStr(i - 1)}); } // Move the vertices to the component graphs - moveVertices(m_dfg.varVerticesBeginp()); - moveVertices(m_dfg.constVerticesBeginp()); - moveVertices(m_dfg.opVerticesBeginp()); + moveVertices(m_dfg.varVertices()); + moveVertices(m_dfg.constVertices()); + moveVertices(m_dfg.opVertices()); // UASSERT(m_dfg.size() == 0, "'this' DfgGraph should have been emptied"); } @@ -238,9 +236,8 @@ class ExtractCyclicComponents final { // We can leverage some properties of the input graph to gain a bit of speed. Firstly, we // know constant nodes have no in edges, so they cannot be part of a non-trivial SCC. Mark // them as such without starting a whole traversal. - for (DfgConst *vtxp = m_dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - VertexState& vtxState = allocState(*vtxp); + for (DfgConst& vtx : m_dfg.constVertices()) { + VertexState& vtxState = allocState(vtx); vtxState.index = 0; vtxState.component = 0; } @@ -248,16 +245,15 @@ class ExtractCyclicComponents final { // Next, we know that all SCCs must include a variable (as the input graph was converted // from an AST, we can only have a cycle by going through a variable), so we only start // traversals through them, and only if we know they have both in and out edges. - for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (vtxp->arity() > 0 && vtxp->hasSinks()) { - VertexState& vtxState = getOrAllocState(*vtxp); + for (DfgVertexVar& vtx : m_dfg.varVertices()) { + if (vtx.arity() > 0 && vtx.hasSinks()) { + VertexState& vtxState = getOrAllocState(vtx); // If not yet visited, start a traversal - if (vtxState.index == UNASSIGNED) visitColorSCCs(*vtxp, vtxState); + if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); } else { - VertexState& vtxState = getOrAllocState(*vtxp); + VertexState& vtxState = getOrAllocState(vtx); UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0, - vtxp, "Non circular variable must be in a trivial SCC");); + &vtx, "Non circular variable must be in a trivial SCC");); vtxState.index = 0; vtxState.component = 0; } @@ -265,9 +261,8 @@ class ExtractCyclicComponents final { // Finally, everything we did not visit through the traversal of a variable cannot be in an // SCC, (otherwise we would have found it from a variable). - for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - VertexState& vtxState = getOrAllocState(*vtxp); + for (DfgVertex& vtx : m_dfg.opVertices()) { + VertexState& vtxState = getOrAllocState(vtx); if (vtxState.index == UNASSIGNED) { vtxState.index = 0; vtxState.component = 0; @@ -306,9 +301,7 @@ class ExtractCyclicComponents final { // Ensure that component boundaries are always at variables, by merging SCCs. Merging stops // at variable boundaries, so we don't need to iterate variables. Constants are reachable // from their sinks, or are unused, so we don't need to iterate them either. - for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - DfgVertex& vtx = *vtxp; + for (DfgVertex& vtx : m_dfg.opVertices()) { // Start DFS from each vertex that is in a non-trivial SCC, and merge everything // that is reachable from it into this component. if (const size_t target = state(vtx).component) visitMergeSCCs(vtx, target); @@ -400,8 +393,7 @@ class ExtractCyclicComponents final { static void packSources(DfgGraph& dfg) { // Remove undriven variable sources - for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); + for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) { if (DfgVarPacked* const varp = vtxp->cast()) { varp->packSources(); if (!varp->hasSinks() && varp->arity() == 0) { @@ -419,9 +411,9 @@ class ExtractCyclicComponents final { } } - void moveVertices(DfgVertex* headp) { - for (DfgVertex *vtxp = headp, *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); + template + void moveVertices(DfgVertex::List& list) { + for (DfgVertex* const vtxp : list.unlinkable()) { DfgVertex& vtx = *vtxp; if (const size_t component = state(vtx).component) { m_dfg.removeVertex(vtx); @@ -481,17 +473,12 @@ class ExtractCyclicComponents final { // earlier merging of components ensured crossing in fact only happen at variable // boundaries). Note that fixing up the edges can create clones of variables. Clones do // not need fixing up, so we do not need to iterate them. - DfgVertex* const lastp = m_dfg.varVerticesRbeginp(); - for (DfgVertexVar *vtxp = m_dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - // It is possible the last vertex (with a nullptr for 'nextp') gets cloned, and hence - // it's 'nextp' would become none nullptr as the clone is added. However, we don't need - // to iterate clones anyway, so it's ok to get the 'nextp' early in the loop. - nextp = vtxp->verticesNext(); - DfgVertexVar& vtx = *vtxp; + DfgVertex* const lastp = m_dfg.varVertices().backp(); + for (DfgVertexVar& vtx : m_dfg.varVertices()) { // Fix up the edges crossing components fixEdges(vtx); // Don't iterate clones added during this loop - if (vtxp == lastp) break; + if (&vtx == lastp) break; } // Pack sources of variables to remove the now undriven inputs @@ -507,9 +494,9 @@ class ExtractCyclicComponents final { // Move other vertices to their component graphs // After this, vertex states are invalid as we moved the vertices - moveVertices(m_dfg.varVerticesBeginp()); - moveVertices(m_dfg.constVerticesBeginp()); - moveVertices(m_dfg.opVerticesBeginp()); + moveVertices(m_dfg.varVertices()); + moveVertices(m_dfg.constVertices()); + moveVertices(m_dfg.opVertices()); // Check results for consistency if (VL_UNLIKELY(m_doExpensiveChecks)) { diff --git a/src/V3DfgDfgToAst.cpp b/src/V3DfgDfgToAst.cpp index a854bac66..ee7b0c078 100644 --- a/src/V3DfgDfgToAst.cpp +++ b/src/V3DfgDfgToAst.cpp @@ -240,20 +240,18 @@ class DfgToAstVisitor final : DfgVisitor { // Convert the graph back to combinational assignments // The graph must have been regularized, so we only need to render assignments - for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - + for (DfgVertexVar& vtx : dfg.varVertices()) { // If there is no driver (this vertex is an input to the graph), then nothing to do. - if (!vtxp->isDrivenByDfg()) continue; + if (!vtx.isDrivenByDfg()) continue; // Render packed variable assignments - if (const DfgVarPacked* const dfgVarp = vtxp->cast()) { + if (const DfgVarPacked* const dfgVarp = vtx.cast()) { convertVarDriver(dfgVarp); continue; } // Render array variable assignments - convertArrayDiver(vtxp->as()); + convertArrayDiver(vtx.as()); } } diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index c582a7221..b5ef851ee 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -109,29 +109,22 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { // Pre-hash variables, these are all unique, so just set their hash to a unique value uint32_t varHash = 0; - for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - vtxp->user() = V3Hash{++varHash}; - } + for (DfgVertexVar& vtx : dfg.varVertices()) vtx.user() = V3Hash{++varHash}; // Similarly pre-hash constants for speed. While we don't combine constants, we do want // expressions using the same constants to be combined, so we do need to hash equal // constants to equal values. - for (DfgConst *vtxp = dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp); + for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) { // Delete unused constants while we are at it. if (!vtxp->hasSinks()) { - vtxp->unlinkDelete(dfg); + VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); continue; } vtxp->user() = vtxp->num().toHash() + varHash; } // Combine operation vertices - for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp); + for (DfgVertex* const vtxp : dfg.opVertices().unlinkable()) { // Delete unused nodes while we are at it. if (!vtxp->hasSinks()) { vtxp->unlinkDelete(dfg); @@ -144,7 +137,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { if (candidatep->equals(*vtxp, equalsCache)) { ++ctx.m_eliminated; vtxp->replaceWith(candidatep); - vtxp->unlinkDelete(dfg); + VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); replaced = true; break; } @@ -158,10 +151,9 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { removeUnused(dfg); } -void V3DfgPasses::inlineVars(const DfgGraph& dfg) { - for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (DfgVarPacked* const varp = vtxp->cast()) { +void V3DfgPasses::inlineVars(DfgGraph& dfg) { + for (DfgVertexVar& vtx : dfg.varVertices()) { + if (DfgVarPacked* const varp = vtx.cast()) { // Don't inline SystemC variables, as SystemC types are not interchangeable with // internal types, and hence the variables are not interchangeable either. if (varp->hasSinks() && varp->isDrivenFullyByDfg() && !varp->varp()->isSc()) { @@ -170,11 +162,11 @@ void V3DfgPasses::inlineVars(const DfgGraph& dfg) { // We must keep the original driver in certain cases, when swapping them would // not be functionally or technically (implementation reasons) equivalent if (DfgVertexVar* const driverVarp = driverp->cast()) { - const AstVar* const varp = driverVarp->varp(); + const AstVar* const astVarp = driverVarp->varp(); // If driven from a SystemC variable - if (varp->isSc()) continue; + if (astVarp->isSc()) continue; // If the variable is forceable - if (varp->isForceable()) continue; + if (astVarp->isForceable()) continue; } varp->forEachSinkEdge([=](DfgEdge& edge) { @@ -202,16 +194,14 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { DfgVertex* workListp = sentinelp; // Add all unused vertices to the work list. This also allocates all DfgVertex::user. - for (DfgVertex *vtxp = dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp); - if (vtxp->hasSinks()) { + for (DfgVertex& vtx : dfg.opVertices()) { + if (vtx.hasSinks()) { // This vertex is used. Allocate user, but don't add to work list. - vtxp->setUser(nullptr); + vtx.setUser(nullptr); } else { // This vertex is unused. Add to work list. - vtxp->setUser(workListp); - workListp = vtxp; + vtx.setUser(workListp); + workListp = &vtx; } } @@ -240,9 +230,8 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { } // Finally remove unused constants - for (DfgConst *vtxp = dfg.constVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (!vtxp->hasSinks()) vtxp->unlinkDelete(dfg); + for (DfgConst* const vtxp : dfg.constVertices().unlinkable()) { + if (!vtxp->hasSinks()) VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); } } @@ -258,11 +247,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { DfgVertex* workListp = sentinelp; // Add all variables to the initial work list - for (DfgVertexVar *vtxp = dfg.varVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp); - vtxp->setUser(workListp); - workListp = vtxp; + for (DfgVertexVar& vtx : dfg.varVertices()) { + vtx.setUser(workListp); + workListp = &vtx; } const auto addToWorkList = [&](DfgVertex& vtx) { diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index 21d34602c..6fe0d7d54 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -125,7 +125,7 @@ AstModule* dfgToAst(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED; // Common subexpression elimination void cse(DfgGraph&, V3DfgCseContext&) VL_MT_DISABLED; // Inline fully driven variables -void inlineVars(const DfgGraph&) VL_MT_DISABLED; +void inlineVars(DfgGraph&) VL_MT_DISABLED; // Peephole optimizations void peephole(DfgGraph&, V3DfgPeepholeContext&) VL_MT_DISABLED; // Regularize graph. This must be run before converting back to Ast. diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 7b9ae8fbd..649545c5e 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1533,12 +1533,10 @@ class V3DfgPeephole final : public DfgVisitor { // Add all vertices to the work list, and to the vertex cache. // This also allocates all DfgVertex::user. - for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - if (VL_LIKELY(nextp)) VL_PREFETCH_RW(nextp); - vtxp->setUser(m_workListp); - m_workListp = vtxp; - m_cache.cache(vtxp); + for (DfgVertex& vtx : m_dfg.opVertices()) { + vtx.setUser(m_workListp); + m_workListp = &vtx; + m_cache.cache(&vtx); } // Process the work list diff --git a/src/V3DfgRegularize.cpp b/src/V3DfgRegularize.cpp index e6f7c08de..a4875ef5c 100644 --- a/src/V3DfgRegularize.cpp +++ b/src/V3DfgRegularize.cpp @@ -44,11 +44,11 @@ class DfgRegularize final { size_t m_nTmps = 0; // Number of temporaries added to this graph - for variable names only // Return canonical variable that can be used to hold the value of this vertex - DfgVarPacked* getCanonicalVariable(DfgVertex* vtxp) { + DfgVarPacked* getCanonicalVariable(DfgVertex& vtx) { // First gather all existing variables fully written by this vertex std::vector varVtxps; - vtxp->forEachSink([&](DfgVertex& vtx) { - if (DfgVarPacked* const varVtxp = vtx.cast()) { + vtx.forEachSink([&](DfgVertex& sink) { + if (DfgVarPacked* const varVtxp = sink.cast()) { if (varVtxp->isDrivenFullyByDfg()) varVtxps.push_back(varVtxp); } }); @@ -79,9 +79,9 @@ class DfgRegularize final { ++m_ctx.m_temporariesIntroduced; // Add temporary AstVar to containing module - FileLine* const flp = vtxp->fileline(); + FileLine* const flp = vtx.fileline(); const std::string name = m_tmpNamePrefix + std::to_string(m_nTmps++); - AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, vtxp->dtypep()}; + AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, vtx.dtypep()}; m_dfg.modulep()->addStmtsp(varp); // Create and return a variable vertex for the temporary @@ -94,27 +94,25 @@ class DfgRegularize final { , m_ctx{ctx} { // Ensure intermediate values used multiple times are written to variables - for (DfgVertex *vtxp = m_dfg.opVerticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNext(); - + for (DfgVertex& vtx : m_dfg.opVertices()) { // Operations without multiple sinks need no variables - if (!vtxp->hasMultipleSinks()) continue; + if (!vtx.hasMultipleSinks()) continue; // Array selects need no variables, they are just memory references - if (vtxp->is()) continue; + if (vtx.is()) continue; // This is an op which has multiple sinks. Ensure it is assigned to a variable. - DfgVarPacked* const varp = getCanonicalVariable(vtxp); + DfgVarPacked* const varp = getCanonicalVariable(vtx); if (varp->arity()) { // Existing variable FileLine* const flp = varp->driverFileLine(0); varp->sourceEdge(0)->unlinkSource(); varp->resetSources(); - vtxp->replaceWith(varp); - varp->addDriver(flp, 0, vtxp); + vtx.replaceWith(varp); + varp->addDriver(flp, 0, &vtx); } else { // Temporary variable - vtxp->replaceWith(varp); - varp->addDriver(vtxp->fileline(), 0, vtxp); + vtx.replaceWith(varp); + varp->addDriver(vtx.fileline(), 0, &vtx); } } } diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index f65a3f283..d33eb76d9 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -53,13 +53,6 @@ public: , m_varp{varp} {} ASTGEN_MEMBERS_DfgVertexVar; - DfgVertexVar* verticesNext() const { - return static_cast(DfgVertex::verticesNext()); - } - DfgVertexVar* verticesPrev() const { - return static_cast(DfgVertex::verticesPrev()); - } - bool isDrivenByDfg() const { return arity() > 0; } AstVar* varp() const { return m_varp; } @@ -107,9 +100,6 @@ public: , m_num{flp, static_cast(width), value} {} ASTGEN_MEMBERS_DfgConst; - DfgConst* verticesNext() const { return static_cast(DfgVertex::verticesNext()); } - DfgConst* verticesPrev() const { return static_cast(DfgVertex::verticesPrev()); } - V3Number& num() { return m_num; } const V3Number& num() const { return m_num; } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 47f45e24b..366cf79ab 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -847,11 +847,10 @@ void EmitCSyms::emitSymImp() { puts("// Configure profiling for PGO\n"); if (v3Global.opt.mtasks()) { v3Global.rootp()->topModulep()->foreach([&](const AstExecGraph* execGraphp) { - for (const V3GraphVertex* vxp = execGraphp->depGraphp()->verticesBeginp(); vxp; - vxp = vxp->verticesNextp()) { - const ExecMTask* const mtp = static_cast(vxp); - puts("_vm_pgoProfiler.addCounter(" + cvtToStr(mtp->id()) + ", \"" - + mtp->hashName() + "\");\n"); + for (const V3GraphVertex& vtx : execGraphp->depGraphp()->vertices()) { + const ExecMTask& mt = static_cast(vtx); + puts("_vm_pgoProfiler.addCounter(" + cvtToStr(mt.id()) + ", \"" + mt.hashName() + + "\");\n"); } }); } diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index 52fa970ef..f42a4e87d 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -119,8 +119,8 @@ private: // Find minimum cost MTask for scaling MTask node widths uint32_t minCost = UINT32_MAX; - for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - if (const ExecMTask* const mtaskp = vxp->cast()) { + for (const V3GraphVertex& vtx : graph.vertices()) { + if (const ExecMTask* const mtaskp = vtx.cast()) { minCost = minCost > mtaskp->cost() ? mtaskp->cost() : minCost; } } @@ -142,17 +142,17 @@ private: }; // Emit MTasks - for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - if (const ExecMTask* const mtaskp = vxp->cast()) emitMTask(mtaskp); + for (const V3GraphVertex& vtx : graph.vertices()) { + if (const ExecMTask* const mtaskp = vtx.cast()) emitMTask(mtaskp); } // Emit MTask dependency edges *logp << "\n // MTask dependencies\n"; - for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - if (const ExecMTask* const mtaskp = vxp->cast()) { - for (V3GraphEdge* edgep = mtaskp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const V3GraphVertex* const top = edgep->top(); - *logp << " " << vxp->name() << " -> " << top->name() << "\n"; + for (const V3GraphVertex& vtx : graph.vertices()) { + if (const ExecMTask* const mtaskp = vtx.cast()) { + for (const V3GraphEdge& edge : mtaskp->outEdges()) { + const V3GraphVertex* const top = edge.top(); + *logp << " " << vtx.name() << " -> " << top->name() << "\n"; } } } @@ -173,8 +173,8 @@ public: uint32_t crossThreadDependencies(const ExecMTask* mtaskp) const { const uint32_t thisThreadId = threadId(mtaskp); uint32_t result = 0; - for (V3GraphEdge* edgep = mtaskp->inBeginp(); edgep; edgep = edgep->inNextp()) { - const ExecMTask* const prevp = edgep->fromp()->as(); + for (const V3GraphEdge& edge : mtaskp->inEdges()) { + const ExecMTask* const prevp = edge.fromp()->as(); if (threadId(prevp) != thisThreadId) ++result; } return result; @@ -261,8 +261,8 @@ class PackThreads final { } bool isReady(ThreadSchedule& schedule, const ExecMTask* mtaskp) { - for (V3GraphEdge* edgeInp = mtaskp->inBeginp(); edgeInp; edgeInp = edgeInp->inNextp()) { - const ExecMTask* const prevp = edgeInp->fromp()->as(); + for (const V3GraphEdge& edgeIn : mtaskp->inEdges()) { + const ExecMTask* const prevp = edgeIn.fromp()->as(); if (schedule.threadId(prevp) == ThreadSchedule::UNASSIGNED) { // This predecessor is not assigned yet return false; @@ -272,7 +272,7 @@ class PackThreads final { } // Pack an MTasks from given graph into m_nThreads threads, return the schedule. - ThreadSchedule pack(const V3Graph& mtaskGraph) { + ThreadSchedule pack(V3Graph& mtaskGraph) { // The result ThreadSchedule schedule{m_nThreads}; @@ -283,8 +283,8 @@ class PackThreads final { std::set readyMTasks; // Build initial ready list - for (V3GraphVertex* vxp = mtaskGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - ExecMTask* const mtaskp = vxp->as(); + for (V3GraphVertex& vtx : mtaskGraph.vertices()) { + ExecMTask* const mtaskp = vtx.as(); if (isReady(schedule, mtaskp)) readyMTasks.insert(mtaskp); } @@ -303,9 +303,8 @@ class PackThreads final { << ", skipping thread.\n"); break; } - for (V3GraphEdge* edgep = mtaskp->inBeginp(); edgep; - edgep = edgep->inNextp()) { - const ExecMTask* const priorp = edgep->fromp()->as(); + for (const V3GraphEdge& edge : mtaskp->inEdges()) { + const ExecMTask* const priorp = edge.fromp()->as(); const uint32_t priorEndTime = completionTime(schedule, priorp, threadId); if (priorEndTime > timeBegin) timeBegin = priorEndTime; } @@ -343,9 +342,8 @@ class PackThreads final { // Update the ready list const size_t erased = readyMTasks.erase(bestMtaskp); UASSERT_OBJ(erased > 0, bestMtaskp, "Should have erased something?"); - for (V3GraphEdge* edgeOutp = bestMtaskp->outBeginp(); edgeOutp; - edgeOutp = edgeOutp->outNextp()) { - ExecMTask* const nextp = edgeOutp->top()->as(); + for (V3GraphEdge& edgeOut : bestMtaskp->outEdges()) { + ExecMTask* const nextp = edgeOut.top()->as(); // Dependent MTask should not yet be assigned to a thread UASSERT(schedule.threadId(nextp) == ThreadSchedule::UNASSIGNED, "Tasks after one being assigned should not be assigned yet"); @@ -427,7 +425,7 @@ public: for (AstNode* const nodep : mTaskBodyps) nodep->deleteTree(); } - static const ThreadSchedule apply(const V3Graph& mtaskGraph) { + static const ThreadSchedule apply(V3Graph& mtaskGraph) { return PackThreads{}.pack(mtaskGraph); } }; @@ -493,11 +491,8 @@ void fillinCosts(V3Graph* execMTaskGraphp) { // Pass 1: See what profiling data applies Costs costs; // For each mtask, costs - for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp; - vxp = vxp->verticesNextp()) { - ExecMTask* const mtp = const_cast(vxp)->as(); - // Compute name of mtask, for hash lookup - + for (V3GraphVertex& vtx : execMTaskGraphp->vertices()) { + ExecMTask* const mtp = vtx.as(); // This estimate is 64 bits, but the final mtask graph algorithm needs 32 bits const uint64_t costEstimate = V3InstrCount::count(mtp->bodyp(), false); const uint64_t costProfiled @@ -513,9 +508,8 @@ void fillinCosts(V3Graph* execMTaskGraphp) { int totalEstimates = 0; int missingProfiles = 0; - for (const V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp; - vxp = vxp->verticesNextp()) { - ExecMTask* const mtp = const_cast(vxp)->as(); + for (V3GraphVertex& vtx : execMTaskGraphp->vertices()) { + ExecMTask* const mtp = vtx.as(); const uint32_t costEstimate = costs[mtp->id()].first; const uint64_t costProfiled = costs[mtp->id()].second; UINFO(9, "ce = " << costEstimate << " cp=" << costProfiled << endl); @@ -550,8 +544,8 @@ void finalizeCosts(V3Graph* execMTaskGraphp) { // choice among several ready mtasks, we'll want to start the // highest priority one first, so we're always working on the "long // pole" - for (V3GraphEdge* edgep = mtp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const ExecMTask* const followp = edgep->top()->as(); + for (V3GraphEdge& edge : mtp->outEdges()) { + const ExecMTask* const followp = edge.top()->as(); if ((followp->priority() + mtp->cost()) > mtp->priority()) { mtp->priority(followp->priority() + mtp->cost()); } @@ -561,9 +555,8 @@ void finalizeCosts(V3Graph* execMTaskGraphp) { // Some MTasks may now have zero cost, eliminate those. // (It's common for tasks to shrink to nothing when V3LifePost // removes dly assignments.) - for (V3GraphVertex* vxp = execMTaskGraphp->verticesBeginp(); vxp;) { - ExecMTask* const mtp = vxp->as(); - vxp = vxp->verticesNextp(); // Advance before delete + for (V3GraphVertex* const vtxp : execMTaskGraphp->vertices().unlinkable()) { + ExecMTask* const mtp = vtxp->as(); // Don't rely on checking mtp->cost() == 0 to detect an empty task. // Our cost-estimating logic is just an estimate. Instead, check @@ -571,9 +564,9 @@ void finalizeCosts(V3Graph* execMTaskGraphp) { AstMTaskBody* const bodyp = mtp->bodyp(); if (!bodyp->stmtsp()) { // Kill this empty mtask UINFO(6, "Removing zero-cost " << mtp->name() << endl); - for (V3GraphEdge* inp = mtp->inBeginp(); inp; inp = inp->inNextp()) { - for (V3GraphEdge* outp = mtp->outBeginp(); outp; outp = outp->outNextp()) { - new V3GraphEdge{execMTaskGraphp, inp->fromp(), outp->top(), 1}; + for (V3GraphEdge& in : mtp->inEdges()) { + for (V3GraphEdge& out : mtp->outEdges()) { + new V3GraphEdge{execMTaskGraphp, in.fromp(), out.top(), 1}; } } VL_DO_DANGLING(mtp->unlinkDelete(execMTaskGraphp), mtp); @@ -647,8 +640,8 @@ void addMTaskToFunction(const ThreadSchedule& schedule, const uint32_t threadId, } // For any dependent mtask that's on another thread, signal one dependency completion. - for (V3GraphEdge* edgep = mtaskp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const ExecMTask* const nextp = edgep->top()->as(); + for (const V3GraphEdge& edge : mtaskp->outEdges()) { + const ExecMTask* const nextp = edge.top()->as(); if (schedule.threadId(nextp) != threadId) { addStrStmt("vlSelf->__Vm_mtaskstate_" + cvtToStr(nextp->id()) + ".signalUpstreamDone(even_cycle);\n"); diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index a6b481218..4efdbdb35 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -335,8 +335,8 @@ public: static void v3GateWarnSyncAsync(GateGraph& graph) { // AstVar::user2 -> bool: Warned about SYNCASYNCNET const VNUser2InUse user2InUse; - for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (const GateVarVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex& vtx : graph.vertices()) { + if (const GateVarVertex* const vvertexp = vtx.cast()) { const AstVarScope* const vscp = vvertexp->varScp(); const AstNode* const sp = vvertexp->rstSyncNodep(); const AstNode* const ap = vvertexp->rstAsyncNodep(); @@ -438,9 +438,8 @@ class GateClkDecomp final { ++m_seenClkVectors; } - for (V3GraphEdge *edgep = vVtxp->outBeginp(), *nextp; edgep; edgep = nextp) { - // Need to find the next edge before visiting in case the edge is deleted - nextp = edgep->outNextp(); + // Edge might be deleted, so need unlinkable iteration + for (V3GraphEdge* const edgep : vVtxp->outEdges().unlinkable()) { visit(edgep->top()->as(), offset, vscp); } @@ -485,7 +484,7 @@ class GateClkDecomp final { "Should only make it here with clkOffset == 0"); rhsp->replaceWith( new AstVarRef{rhsp->fileline(), m_clkVtxp->varScp(), VAccess::READ}); - while (V3GraphEdge* const edgep = lVtxp->inBeginp()) { + while (V3GraphEdge* const edgep = lVtxp->inEdges().frontp()) { VL_DO_DANGLING(edgep->unlinkDelete(), edgep); } m_graph.addEdge(m_clkVtxp, lVtxp, 1); @@ -495,14 +494,14 @@ class GateClkDecomp final { return; } - visit(lVtxp->outBeginp()->top()->as(), clkOffset); + visit(lVtxp->outEdges().frontp()->top()->as(), clkOffset); } explicit GateClkDecomp(GateGraph& graph) : m_graph{graph} { UINFO(9, "Starting clock decomposition" << endl); - for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - GateVarVertex* const vVtxp = itp->cast(); + for (V3GraphVertex& vtx : graph.vertices()) { + GateVarVertex* const vVtxp = vtx.cast(); if (!vVtxp) continue; const AstVarScope* const vscp = vVtxp->varScp(); @@ -740,17 +739,18 @@ class GateInline final { substitutions.clear(); } - static GateVarVertex* ffToVarVtx(V3GraphVertex* vtxp) { - while (vtxp && !vtxp->is()) vtxp = vtxp->verticesNextp(); - return static_cast(vtxp); - } - void optimizeSignals(bool allowMultiIn) { // Consider "inlining" variables - GateVarVertex* nextp = ffToVarVtx(m_graph.verticesBeginp()); - while (GateVarVertex* const vVtxp = nextp) { + auto& vertices = m_graph.vertices(); + const auto ffToVarVtx = [&](V3GraphVertex::List::iterator it) { + while (it != vertices.end() && !(*it).is()) ++it; + return it; + }; + V3GraphVertex::List::iterator vIt = ffToVarVtx(vertices.begin()); + while (vIt != vertices.end()) { + GateVarVertex* const vVtxp = (*vIt).as(); // vVtxp and it's driving logic might be deleted, so grab next up front - nextp = ffToVarVtx(vVtxp->verticesNextp()); + vIt = ffToVarVtx(++vIt); // Nothing to inline if no driver, or multiple drivers ... if (!vVtxp->inSize1()) continue; @@ -759,7 +759,8 @@ class GateInline final { if (!vVtxp->reducible()) continue; // Grab the driving logic - GateLogicVertex* const lVtxp = vVtxp->inBeginp()->fromp()->as(); + GateLogicVertex* const lVtxp + = vVtxp->inEdges().frontp()->fromp()->as(); if (!lVtxp->reducible()) continue; AstNode* const logicp = lVtxp->nodep(); @@ -779,10 +780,10 @@ class GateInline final { } else { // Do it if not used, or used only once, ignoring slow code int n = 0; - for (V3GraphEdge* ep = vVtxp->outBeginp(); ep; ep = ep->outNextp()) { - const GateLogicVertex* const dstVtxp = ep->top()->as(); + for (V3GraphEdge& edge : vVtxp->outEdges()) { + const GateLogicVertex* const dstVtxp = edge.top()->as(); // Ignore slow code, or if the destination is not used - if (!dstVtxp->slow() && dstVtxp->outBeginp()) n += ep->weight(); + if (!dstVtxp->slow() && !dstVtxp->outEmpty()) n += edge.weight(); if (n > 1) break; } if (n > 1) continue; @@ -803,18 +804,14 @@ class GateInline final { const std::unordered_set readVscps{readVscpsVec.begin(), readVscpsVec.end()}; - for (V3GraphEdge *edgep = vVtxp->outBeginp(), *nextp; edgep; edgep = nextp) { - // Edge might be deleted, so grab next - nextp = edgep->outNextp(); - + for (V3GraphEdge* const edgep : vVtxp->outEdges().unlinkable()) { GateLogicVertex* const dstVtxp = edgep->top()->as(); // If the consumer logic writes one of the variables that the substitution // is reading, then we would get a cycles, so we cannot do that. bool canInline = true; - for (V3GraphEdge* dedgep = dstVtxp->outBeginp(); dedgep; - dedgep = dedgep->outNextp()) { - const GateVarVertex* const consVVertexp = dedgep->top()->as(); + for (V3GraphEdge& dedge : dstVtxp->outEdges()) { + const GateVarVertex* const consVVertexp = dedge.top()->as(); if (readVscps.count(consVVertexp->varScp())) { canInline = false; break; @@ -1079,8 +1076,8 @@ class GateDedupe final { if (!vVtxp->inSize1()) return; AstNodeVarRef* dupRefp = nullptr; - for (V3GraphEdge* edgep = vVtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - dupRefp = visit(edgep->fromp()->as(), vVtxp); + for (V3GraphEdge& edge : vVtxp->inEdges()) { + dupRefp = visit(edge.fromp()->as(), vVtxp); } if (!dupRefp) return; @@ -1088,7 +1085,7 @@ class GateDedupe final { "GateLogicVertex* visit should have returned nullptr " "if consumer var vertex is not dedupable."); - GateLogicVertex* const lVtxp = vVtxp->inBeginp()->fromp()->as(); + GateLogicVertex* const lVtxp = vVtxp->inEdges().frontp()->fromp()->as(); const GateOkVisitor okVisitor{lVtxp->nodep(), false, true}; if (!okVisitor.isSimple()) return; @@ -1097,8 +1094,7 @@ class GateDedupe final { UINFO(4, "replacing " << vVtxp << " with " << dupVVtxp << endl); // Replace all of this varvertex's consumers with dupRefp - for (V3GraphEdge *edgep = vVtxp->outBeginp(), *nextp; edgep; edgep = nextp) { - nextp = edgep->outNextp(); + for (V3GraphEdge* const edgep : vVtxp->outEdges().unlinkable()) { const GateLogicVertex* const consumerVtxp = edgep->top()->as(); AstNode* const consumerp = consumerVtxp->nodep(); UINFO(9, "elim src vtx" << lVtxp << " node " << lVtxp->nodep() << endl); @@ -1130,7 +1126,7 @@ class GateDedupe final { } // Remove inputs links - while (V3GraphEdge* const edgep = vVtxp->inBeginp()) { + while (V3GraphEdge* const edgep = vVtxp->inEdges().frontp()) { VL_DO_DANGLING(edgep->unlinkDelete(), edgep); } @@ -1141,9 +1137,7 @@ class GateDedupe final { // Given iterated logic, starting at consumerVtxp, returns a varref that // has the same logic input, or nullptr if none AstNodeVarRef* visit(GateLogicVertex* lVtxp, const GateVarVertex* consumerVtxp) { - for (V3GraphEdge* edgep = lVtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - visit(edgep->fromp()->as()); - } + for (V3GraphEdge& edge : lVtxp->inEdges()) visit(edge.fromp()->as()); if (lVtxp->dedupable() && consumerVtxp->dedupable()) { // TODO: Doing a simple pointer comparison of activep won't work @@ -1158,15 +1152,15 @@ class GateDedupe final { explicit GateDedupe(GateGraph& graph) { // Traverse starting from each of the clocks UINFO(9, "Gate dedupe() clocks:\n"); - for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (GateVarVertex* const vVtxp = itp->cast()) { + for (V3GraphVertex& vtx : graph.vertices()) { + if (GateVarVertex* const vVtxp = vtx.cast()) { if (vVtxp->isClock()) visit(vVtxp); } } // Traverse starting from each of the outputs UINFO(9, "Gate dedupe() outputs:\n"); - for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (GateVarVertex* const vVtxp = itp->cast()) { + for (V3GraphVertex& vtx : graph.vertices()) { + if (GateVarVertex* const vVtxp = vtx.cast()) { if (vVtxp->isTop() && vVtxp->varScp()->varp()->isWritable()) visit(vVtxp); } } @@ -1209,9 +1203,7 @@ class GateMergeAssignments final { GateLogicVertex* prevLVtxp = nullptr; AstNodeAssign* prevAssignp = nullptr; - for (V3GraphEdge *edgep = vVtxp->inBeginp(), *nextp; edgep; edgep = nextp) { - nextp = edgep->inNextp(); // fThe edge could be deleted - + for (V3GraphEdge* const edgep : vVtxp->inEdges().unlinkable()) { GateLogicVertex* const lVtxp = edgep->fromp()->as(); if (!lVtxp->outSize1()) continue; @@ -1251,7 +1243,7 @@ class GateMergeAssignments final { // Don't need to delete assignp, will be handled // Update the graph - while (V3GraphEdge* const iedgep = lVtxp->inBeginp()) { + while (V3GraphEdge* const iedgep = lVtxp->inEdges().frontp()) { GateVarVertex* const fromVtxp = iedgep->fromp()->as(); m_graph.addEdge(fromVtxp, prevLVtxp, 1); VL_DO_DANGLING(iedgep->unlinkDelete(), iedgep); @@ -1259,7 +1251,6 @@ class GateMergeAssignments final { // Delete the out-edges of lVtxp (there is only one, we checked earlier) VL_DO_DANGLING(edgep->unlinkDelete(), edgep); - ++m_statAssignMerged; } else { prevLVtxp = lVtxp; @@ -1271,8 +1262,8 @@ class GateMergeAssignments final { explicit GateMergeAssignments(GateGraph& graph) : m_graph{graph} { UINFO(6, "mergeAssigns\n"); - for (V3GraphVertex* itp = graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (GateVarVertex* const vVtxp = itp->cast()) process(vVtxp); + for (V3GraphVertex& vtx : graph.vertices()) { + if (GateVarVertex* const vVtxp = vtx.cast()) process(vVtxp); } } @@ -1298,8 +1289,8 @@ class GateUnused final { vtxp->user(true); vtxp->setConsumed("propagated"); // Walk sources and mark them too - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - GateEitherVertex* const fromVtxp = static_cast(edgep->fromp()); + for (V3GraphEdge& edge : vtxp->inEdges()) { + GateEitherVertex* const fromVtxp = static_cast(edge.fromp()); markRecurse(fromVtxp); } } @@ -1307,9 +1298,9 @@ class GateUnused final { // Mark all vertices that feed a consumed vertex void mark() { m_graph.userClearVertices(); - for (V3GraphVertex* vtxp = m_graph.verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - GateEitherVertex* const eVtxp = static_cast(vtxp); - if (eVtxp->consumed()) markRecurse(eVtxp); + for (V3GraphVertex& vtx : m_graph.vertices()) { + GateEitherVertex& eVtx = static_cast(vtx); + if (eVtx.consumed()) markRecurse(&eVtx); } } @@ -1327,8 +1318,7 @@ class GateUnused final { // Remove unused logic void remove() { - for (V3GraphVertex *vtxp = m_graph.verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); + for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) { if (GateLogicVertex* const lVtxp = vtxp->cast()) { if (!lVtxp->consumed() && lVtxp->activep()) { // activep is nullptr under cfunc AstNode* const nodep = lVtxp->nodep(); @@ -1337,7 +1327,7 @@ class GateUnused final { UINFO(8, " Remove unconsumed " << nodep << endl); nodep->unlinkFrBack(); VL_DO_DANGLING(nodep->deleteTree(), nodep); - lVtxp->unlinkDelete(&m_graph); + VL_DO_DANGLING(lVtxp->unlinkDelete(&m_graph), lVtxp); } } } diff --git a/src/V3Graph.cpp b/src/V3Graph.cpp index fe425151b..968e4c261 100644 --- a/src/V3Graph.cpp +++ b/src/V3Graph.cpp @@ -41,7 +41,7 @@ V3GraphVertex::V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old) , m_color{old.m_color} , m_rank{old.m_rank} { m_userp = nullptr; - verticesPushBack(graphp); + graphp->vertices().linkBack(this); } V3GraphVertex::V3GraphVertex(V3Graph* graphp) @@ -49,67 +49,61 @@ V3GraphVertex::V3GraphVertex(V3Graph* graphp) , m_color{0} , m_rank{0} { m_userp = nullptr; - verticesPushBack(graphp); -} - -void V3GraphVertex::verticesPushBack(V3Graph* graphp) { - m_vertices.pushBack(graphp->m_vertices, this); + graphp->vertices().linkBack(this); } void V3GraphVertex::unlinkEdges(V3Graph*) { - for (V3GraphEdge* edgep = outBeginp(); edgep; /*BELOW*/) { - V3GraphEdge* nextp = edgep->outNextp(); - edgep->unlinkDelete(); - edgep = nextp; - } - for (V3GraphEdge* edgep = inBeginp(); edgep; /*BELOW*/) { - V3GraphEdge* nextp = edgep->inNextp(); - edgep->unlinkDelete(); - edgep = nextp; - } + while (V3GraphEdge* const ep = m_outs.frontp()) VL_DO_DANGLING(ep->unlinkDelete(), ep); + while (V3GraphEdge* const ep = m_ins.frontp()) VL_DO_DANGLING(ep->unlinkDelete(), ep); } void V3GraphVertex::unlinkDelete(V3Graph* graphp) { // Delete edges unlinkEdges(graphp); // Unlink from vertex list - m_vertices.unlink(graphp->m_vertices, this); + graphp->m_vertices.unlink(this); // Delete delete this; // this=nullptr; } 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()) { - new V3GraphEdge{graphp, iedgep->fromp(), oedgep->top(), - std::min(iedgep->weight(), oedgep->weight()), - iedgep->cutable() && oedgep->cutable()}; + for (V3GraphEdge& iedge : inEdges()) { + for (V3GraphEdge& oedge : outEdges()) { + new V3GraphEdge{graphp, iedge.fromp(), oedge.top(), + std::min(iedge.weight(), oedge.weight()), + iedge.cutable() && oedge.cutable()}; } } // Remove old edges unlinkEdges(graphp); } -bool V3GraphVertex::inSize1() const { return !inEmpty() && !inBeginp()->inNextp(); } -bool V3GraphVertex::outSize1() const { return !outEmpty() && !outBeginp()->outNextp(); } - -V3GraphEdge* V3GraphVertex::findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp) { +template +V3GraphEdge* V3GraphVertex::findConnectingEdgep(V3GraphVertex* waywardp) { // O(edges) linear search. Searches search both nodes' edge lists in // parallel. The lists probably aren't _both_ huge, so this is // unlikely to blow up even on fairly nasty graphs. - const GraphWay inv = way.invert(); - V3GraphEdge* aedgep = this->beginp(way); - V3GraphEdge* bedgep = waywardp->beginp(inv); - while (aedgep && bedgep) { - if (aedgep->furtherp(way) == waywardp) return aedgep; - if (bedgep->furtherp(inv) == this) return bedgep; - aedgep = aedgep->nextp(way); - bedgep = bedgep->nextp(inv); + constexpr GraphWay way{T_Way}; + constexpr GraphWay inv = way.invert(); + auto& aEdges = this->edges(); + auto aIt = aEdges.begin(); + auto aEnd = aEdges.end(); + auto& bEdges = waywardp->edges(); + auto bIt = bEdges.begin(); + auto bEnd = bEdges.end(); + while (aIt != aEnd && bIt != bEnd) { + V3GraphEdge& aedge = *aIt++; + if (aedge.furtherp() == waywardp) return &aedge; + V3GraphEdge& bedge = *bIt++; + if (bedge.furtherp() == this) return &bedge; } return nullptr; } +template V3GraphEdge* V3GraphVertex::findConnectingEdgep(V3GraphVertex*); +template V3GraphEdge* V3GraphVertex::findConnectingEdgep(V3GraphVertex*); + // cppcheck-has-bug-suppress constParameter void V3GraphVertex::v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) { std::ostringstream nsstr; @@ -153,43 +147,36 @@ void V3GraphEdge::init(V3Graph* /*graphp*/, V3GraphVertex* fromp, V3GraphVertex* m_cutable = cutable; m_userp = nullptr; // Link vertices to this edge - outPushBack(); - inPushBack(); + m_fromp->outEdges().linkBack(this); + m_top->inEdges().linkBack(this); } -V3GraphEdge* V3GraphEdge::relinkFromp(V3GraphVertex* newFromp) { - V3GraphEdge* oldNxt = outNextp(); - m_outs.unlink(m_fromp->m_outs, this); +void V3GraphEdge::relinkFromp(V3GraphVertex* newFromp) { + m_fromp->outEdges().unlink(this); m_fromp = newFromp; - outPushBack(); - return oldNxt; + m_fromp->outEdges().linkBack(this); } -V3GraphEdge* V3GraphEdge::relinkTop(V3GraphVertex* newTop) { - V3GraphEdge* oldNxt = inNextp(); - m_ins.unlink(m_top->m_ins, this); +void V3GraphEdge::relinkTop(V3GraphVertex* newTop) { + m_top->inEdges().unlink(this); m_top = newTop; - inPushBack(); - return oldNxt; + m_top->inEdges().linkBack(this); } void V3GraphEdge::unlinkDelete() { // Unlink from side - m_outs.unlink(m_fromp->m_outs, this); + m_fromp->outEdges().unlink(this); // Unlink to side - m_ins.unlink(m_top->m_ins, this); + m_top->inEdges().unlink(this); // Delete - delete this; // this=nullptr; + delete this; } -void V3GraphEdge::outPushBack() { - // m_fromp->m_outsp.push_back(this); - m_outs.pushBack(m_fromp->m_outs, this); -} +std::string V3GraphEdge::name() const { return m_fromp->name() + "->" + m_top->name(); } -void V3GraphEdge::inPushBack() { - // m_top->m_insp.push_back(this); - m_ins.pushBack(m_top->m_ins, this); +int V3GraphEdge::sortCmp(const V3GraphEdge* rhsp) const { + if (!m_weight || !rhsp->m_weight) return 0; + return top()->sortCmp(rhsp->top()); } //###################################################################### @@ -197,61 +184,50 @@ void V3GraphEdge::inPushBack() { // Graph top level // Constructors -V3Graph::V3Graph() { - // Anything here is probably needed in clear() also - verticesUnlink(); -} +V3Graph::V3Graph() {} V3Graph::~V3Graph() { clear(); } 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(); + for (V3GraphVertex& vertex : vertices()) { + while (V3GraphEdge* const edgep = vertex.outEdges().unlinkFront()) { VL_DO_DANGLING(delete edgep, edgep); - edgep = nextp; } - vertexp->outUnlink(); } // Delete the old vertices - for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; /*BELOW*/) { - V3GraphVertex* nextp = vertexp->verticesNextp(); + while (V3GraphVertex* const vertexp = vertices().unlinkFront()) { VL_DO_DANGLING(delete vertexp, vertexp); - vertexp = nextp; } - verticesUnlink(); } void V3Graph::userClearVertices() { // Clear user() in all of tree // We may use the userCnt trick in V3Ast later... (but gblCnt would be // in V3Graph instead of static - which has the complication of finding - // the graph pointer given a vertex.) For now we don't call this often, and + // the graph pointer given a vertex.) For now, we don't call this often, and // 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(nullptr); // Its a union, but might be different size than user() + for (V3GraphVertex& vertex : vertices()) { + vertex.user(0); + vertex.userp(nullptr); // It's 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(nullptr); // Its a union, but might be different size than user() + for (V3GraphVertex& vertex : vertices()) { + for (V3GraphEdge& edge : vertex.outEdges()) { + edge.user(0); + edge.userp(nullptr); // It's 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; - } + for (V3GraphVertex& vertex : vertices()) vertex.color(0); } //====================================================================== @@ -270,35 +246,29 @@ void V3Graph::dump(std::ostream& os) const { // This generates a file used by graphviz, https://www.graphviz.org os << " Graph:\n"; // Print vertices - for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) { - os << "\tNode: " << vertexp->name(); - if (vertexp->color()) os << " color=" << vertexp->color(); + for (const V3GraphVertex& vertex : vertices()) { + os << "\tNode: " << vertex.name(); + if (vertex.color()) os << " color=" << vertex.color(); os << '\n'; // Print edges - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - dumpEdge(os, vertexp, edgep); - } - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - dumpEdge(os, vertexp, edgep); - } + for (const V3GraphEdge& edge : vertex.inEdges()) dumpEdge(os, vertex, edge); + for (const V3GraphEdge& edge : vertex.outEdges()) dumpEdge(os, vertex, edge); } } -void V3Graph::dumpEdge(std::ostream& os, const V3GraphVertex* vertexp, - const V3GraphEdge* edgep) const { - if (edgep->weight() && (edgep->fromp() == vertexp || edgep->top() == vertexp)) { +void V3Graph::dumpEdge(std::ostream& os, const V3GraphVertex& vertex, + const V3GraphEdge& edge) const { + if (edge.weight() && (edge.fromp() == &vertex || edge.top() == &vertex)) { os << "\t\t"; - if (edgep->fromp() == vertexp) os << "-> " << edgep->top()->name(); - if (edgep->top() == vertexp) os << "<- " << edgep->fromp()->name(); - if (edgep->cutable()) os << " [CUTABLE]"; + if (edge.fromp() == &vertex) os << "-> " << edge.top()->name(); + if (edge.top() == &vertex) os << "<- " << edge.fromp()->name(); + if (edge.cutable()) os << " [CUTABLE]"; os << '\n'; } } -void V3Graph::dumpEdges(std::ostream& os, const V3GraphVertex* vertexp) const { - for (const V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) - dumpEdge(os, vertexp, edgep); - for (const V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) - dumpEdge(os, vertexp, edgep); +void V3Graph::dumpEdges(std::ostream& os, const V3GraphVertex& vertex) const { + for (const V3GraphEdge& edge : vertex.inEdges()) dumpEdge(os, vertex, edge); + for (const V3GraphEdge& edge : vertex.outEdges()) dumpEdge(os, vertex, edge); } void V3Graph::dumpDotFilePrefixed(const string& nameComment, bool colorAsSubgraph) const { @@ -326,16 +296,16 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { // List of all possible subgraphs // Collections of explicit ranks std::unordered_set ranks; - std::unordered_multimap rankSets; - std::multimap subgraphs; - for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) { + std::unordered_multimap rankSets; + std::multimap subgraphs; + for (const V3GraphVertex& vertex : vertices()) { const string vertexSubgraph - = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : ""; - subgraphs.emplace(vertexSubgraph, vertexp); - const string& dotRank = vertexp->dotRank(); + = (colorAsSubgraph && vertex.color()) ? cvtToStr(vertex.color()) : ""; + subgraphs.emplace(vertexSubgraph, &vertex); + const string& dotRank = vertex.dotRank(); if (!dotRank.empty()) { ranks.emplace(dotRank); - rankSets.emplace(dotRank, vertexp); + rankSets.emplace(dotRank, &vertex); } } @@ -373,20 +343,20 @@ void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) const { 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()) { - const int fromVnum = numMap[edgep->fromp()]; - const int toVnum = numMap[edgep->top()]; - *logp << "\tn" << edgep->fromp()->dotName() << fromVnum << " -> n" - << edgep->top()->dotName() << toVnum + for (const V3GraphVertex& vertex : vertices()) { + for (const V3GraphEdge& edge : vertex.outEdges()) { + if (edge.weight()) { + const int fromVnum = numMap[edge.fromp()]; + const int toVnum = numMap[edge.top()]; + *logp << "\tn" << edge.fromp()->dotName() << fromVnum << " -> n" + << edge.top()->dotName() << toVnum << " [" - // <<"fontsize=8 label=\""<<(edgep->name()!="" ? edgep->name() : "\\E")<<"\"" - << "fontsize=8 label=\"" - << (edgep->dotLabel() != "" ? edgep->dotLabel() : "") << "\"" - << " weight=" << edgep->weight() << " color=" << edgep->dotColor(); - if (edgep->dotStyle() != "") *logp << " style=" << edgep->dotStyle(); - // if (edgep->cutable()) *logp << ",constraint=false"; // to rank without + // <<"fontsize=8 label=\""<<(edge.name()!="" ? edge.name() : "\\E")<<"\"" + << "fontsize=8 label=\"" << (edge.dotLabel() != "" ? edge.dotLabel() : "") + << "\"" + << " weight=" << edge.weight() << " color=" << edge.dotColor(); + if (edge.dotStyle() != "") *logp << " style=" << edge.dotStyle(); + // if (edge.cutable()) *logp << ",constraint=false"; // to rank without // following edges *logp << "];\n"; } diff --git a/src/V3Graph.h b/src/V3Graph.h index 52b445742..47515b540 100644 --- a/src/V3Graph.h +++ b/src/V3Graph.h @@ -78,11 +78,280 @@ constexpr bool operator==(const GraphWay& lhs, const GraphWay& rhs) { return lhs constexpr bool operator==(const GraphWay& lhs, GraphWay::en rhs) { return lhs.m_e == rhs; } constexpr bool operator==(GraphWay::en lhs, const GraphWay& rhs) { return lhs == rhs.m_e; } +//============================================================================ +// TODO should we have a smaller edge structure when we don't need weight etc? + +class V3GraphEdge VL_NOT_FINAL { + VL_RTTI_IMPL_BASE(V3GraphEdge) + // Wires/variables aren't edges. Edges have only a single to/from vertex +public: + // ENUMS + enum Cutable : uint8_t { NOT_CUTABLE = false, CUTABLE = true }; // For passing to V3GraphEdge +protected: + friend class V3Graph; + friend class V3GraphVertex; + friend class GraphAcyc; + friend class GraphAcycEdge; + + V3ListLinks m_oLinks; // List links to store instances of this class (out edges) + V3ListLinks m_iLinks; // List links to store instances of this class (in edges) + // + 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 + uint64_t m_user; // Marker for some algorithms + }; + // METHODS + void init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, + bool cutable = false) VL_MT_DISABLED; + void cut() { m_weight = 0; } // 0 weight is same as disconnected + // CONSTRUCTORS +protected: + V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, + const V3GraphEdge& old) VL_MT_DISABLED { + init(graphp, fromp, top, old.m_weight, old.m_cutable); + } + +private: + V3ListLinks& oLinks() { return m_oLinks; } + V3ListLinks& iLinks() { return m_iLinks; } + +public: + // List types to store instances of this class + using OList = V3List; + using IList = V3List; + + //! Add DAG from one node to the specified node + V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, + bool cutable = false) VL_MT_DISABLED { + 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 VL_MT_DISABLED { + return new V3GraphEdge{graphp, fromp, top, *this}; + } + virtual ~V3GraphEdge() = default; + // METHODS + // Return true iff of type T + template + bool is() const { + static_assert(std::is_base_of::value, + "'T' must be a subtype of V3GraphEdge"); + static_assert(std::is_same::type, + VTypeListFront>::value, + "Missing VL_RTTI_IMPL(...) call in 'T'"); + return this->isInstanceOfClassWithId(T::rttiClassId()); + } + + // Return cast to subtype T and assert of that type + template + T* as() { + UASSERT(is(), "V3GraphEdge is not of expected type"); + return static_cast(this); + } + template + const T* as() const { + UASSERT(is(), "V3GraphEdge is not of expected type"); + return static_cast(this); + } + + // Return cast to subtype T, else nullptr if different type + template + T* cast() { + return is() ? static_cast(this) : nullptr; + } + template + const T* cast() const { + return is() ? static_cast(this) : nullptr; + } + + virtual string name() const VL_MT_DISABLED; + virtual string dotLabel() const { return ""; } + virtual string dotColor() const { return cutable() ? "yellowGreen" : "red"; } + virtual string dotStyle() const { return cutable() ? "dashed" : ""; } + virtual int sortCmp(const V3GraphEdge* rhsp) const VL_MT_DISABLED; + void unlinkDelete() VL_MT_DISABLED; + void relinkFromp(V3GraphVertex* newFromp) VL_MT_DISABLED; + void relinkTop(V3GraphVertex* newTop) VL_MT_DISABLED; + // 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(uint64_t user) { m_user = user; } + uint64_t user() const { return m_user; } + V3GraphVertex* fromp() const { return m_fromp; } + V3GraphVertex* top() const { return m_top; } + template + V3GraphVertex* furtherp() const { + return T_Way == GraphWay::FORWARD ? top() : fromp(); + } + // STATIC ACCESSORS + static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; } + static bool followAlwaysTrue(const V3GraphEdge*) { return true; } +}; + +//============================================================================ + +class V3GraphVertex VL_NOT_FINAL { + VL_RTTI_IMPL_BASE(V3GraphVertex) + // Vertices may be a 'gate'/wire statement OR a variable +protected: + friend class V3Graph; + friend class V3GraphEdge; + friend class GraphAcyc; + friend class GraphAlgRank; + V3ListLinks m_links; // List links to store instances of this class + V3GraphEdge::OList m_outs; // List of outbound edges + V3GraphEdge::IList m_ins; // List of inbound edges + 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 + }; + // ACCESSORS + void fanout(double fanout) { m_fanout = fanout; } + +protected: + // CONSTRUCTORS + V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old) VL_MT_DISABLED; + +private: + V3ListLinks& links() { return m_links; } + +public: + // List types to store instances of this class + using List = V3List; + + explicit V3GraphVertex(V3Graph* graphp) VL_MT_DISABLED; + //! Clone copy constructor. Doesn't copy edges or user/userp. + virtual V3GraphVertex* clone(V3Graph* graphp) const VL_MT_DISABLED { + return new V3GraphVertex{graphp, *this}; + } + virtual ~V3GraphVertex() = default; + void unlinkEdges(V3Graph* graphp) VL_MT_DISABLED; + void unlinkDelete(V3Graph* graphp) VL_MT_DISABLED; + + // METHODS + // Return true iff of type T + template + bool is() const { + static_assert(std::is_base_of::value, + "'T' must be a subtype of V3GraphVertex"); + static_assert(std::is_same::type, + VTypeListFront>::value, + "Missing VL_RTTI_IMPL(...) call in 'T'"); + return this->isInstanceOfClassWithId(T::rttiClassId()); + } + + // Return cast to subtype T and assert of that type + template + T* as() { + UASSERT_OBJ(is(), this, "V3GraphVertex is not of expected type"); + return static_cast(this); + } + template + const T* as() const { + UASSERT_OBJ(is(), this, "V3GraphVertex is not of expected type"); + return static_cast(this); + } + + // Return cast to subtype T, else nullptr if different type + template + T* cast() { + return is() ? static_cast(this) : nullptr; + } + template + const T* cast() const { + return is() ? static_cast(this) : nullptr; + } + + // ACCESSORS + virtual string name() const { return ""; } + virtual string dotColor() const { return "black"; } + virtual string dotShape() const { return ""; } + virtual string dotStyle() const { return ""; } + virtual string dotName() const { return ""; } + virtual string dotRank() const { return ""; } + virtual uint32_t rankAdder() const { return 1; } + virtual FileLine* fileline() const { return nullptr; } // nullptr 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; + } + 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 VL_MT_STABLE { return m_user; } + void userp(void* userp) { m_userp = userp; } + void* userp() const VL_MT_STABLE { return m_userp; } + V3GraphEdge::IList& inEdges() { return m_ins; } + const V3GraphEdge::IList& inEdges() const { return m_ins; } + template + inline auto& edges(); + template + inline const auto& edges() const; + bool inEmpty() const { return m_ins.empty(); } + bool inSize1() const { return m_ins.hasSingleElement(); } + V3GraphEdge::OList& outEdges() { return m_outs; } + const V3GraphEdge::OList& outEdges() const { return m_outs; } + bool outEmpty() const { return m_outs.empty(); } + bool outSize1() const { return m_outs.hasSingleElement(); } + // METHODS + /// Error reporting + void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED; + void v3errorEndFatal(std::ostringstream& str) const + VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED; + /// Edges are routed around this vertex to point from "from" directly to "to" + void rerouteEdges(V3Graph* graphp) VL_MT_DISABLED; + // Find the edge connecting this vertex to the given vertex. + // If edge is not found returns nullptr. O(edges) performance. + template + V3GraphEdge* findConnectingEdgep(V3GraphVertex* otherp) VL_MT_DISABLED; +}; + +std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) VL_MT_DISABLED; + +template <> +inline auto& V3GraphVertex::edges() { + return m_outs; +} + +template <> +inline auto& V3GraphVertex::edges() { + return m_ins; +} + +template <> +inline const auto& V3GraphVertex::edges() const { + return m_outs; +} + +template <> +inline const auto& V3GraphVertex::edges() const { + return m_ins; +} + //============================================================================ class V3Graph VL_NOT_FINAL { // MEMBERS - V3List m_vertices; // All vertices + V3GraphVertex::List m_vertices; // All vertices protected: friend class V3GraphVertex; @@ -90,9 +359,8 @@ protected: friend class GraphAcyc; // METHODS double orderDFSIterate(V3GraphVertex* vertexp) VL_MT_DISABLED; - void dumpEdge(std::ostream& os, const V3GraphVertex* vertexp, - const V3GraphEdge* edgep) const VL_MT_DISABLED; - void verticesUnlink() { m_vertices.reset(); } + void dumpEdge(std::ostream& os, const V3GraphVertex& vertex, + const V3GraphEdge& edge) const VL_MT_DISABLED; // ACCESSORS public: @@ -103,7 +371,8 @@ public: // METHODS void clear() VL_MT_DISABLED; // Empty it of all vertices/edges, as if making a new object bool empty() const { return m_vertices.empty(); } - V3GraphVertex* verticesBeginp() const { return m_vertices.begin(); } + V3GraphVertex::List& vertices() { return m_vertices; } + const V3GraphVertex::List& vertices() const { return m_vertices; } // METHODS - ALGORITHMS @@ -180,7 +449,7 @@ public: bool colorAsSubgraph = false) const VL_MT_DISABLED; void dumpDotFilePrefixedAlways(const string& nameComment, bool colorAsSubgraph = false) const VL_MT_DISABLED; - void dumpEdges(std::ostream& os, const V3GraphVertex* vertexp) const VL_MT_DISABLED; + void dumpEdges(std::ostream& os, const V3GraphVertex& vertex) const VL_MT_DISABLED; static void selfTest() VL_MT_DISABLED; class ParallelismReport final { @@ -212,8 +481,8 @@ public: } }; - ParallelismReport parallelismReport( - std::function vertexCost) const VL_MT_DISABLED; + ParallelismReport + parallelismReport(std::function vertexCost) VL_MT_DISABLED; // CALLBACKS virtual void loopsMessageCb(V3GraphVertex* vertexp) VL_MT_DISABLED; @@ -222,245 +491,4 @@ public: //============================================================================ -class V3GraphVertex VL_NOT_FINAL { - VL_RTTI_IMPL_BASE(V3GraphVertex) - // Vertices may be a 'gate'/wire statement OR a variable -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 - union { - void* m_userp; // Marker for some algorithms - uint32_t m_user; // Marker for some algorithms - }; - // METHODS - void verticesPushBack(V3Graph* graphp) VL_MT_DISABLED; - // 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 -protected: - // CONSTRUCTORS - V3GraphVertex(V3Graph* graphp, const V3GraphVertex& old) VL_MT_DISABLED; - -public: - explicit V3GraphVertex(V3Graph* graphp) VL_MT_DISABLED; - //! Clone copy constructor. Doesn't copy edges or user/userp. - virtual V3GraphVertex* clone(V3Graph* graphp) const VL_MT_DISABLED { - return new V3GraphVertex{graphp, *this}; - } - virtual ~V3GraphVertex() = default; - void unlinkEdges(V3Graph* graphp) VL_MT_DISABLED; - void unlinkDelete(V3Graph* graphp) VL_MT_DISABLED; - - // METHODS - // Return true iff of type T - template - bool is() const { - static_assert(std::is_base_of::value, - "'T' must be a subtype of V3GraphVertex"); - static_assert(std::is_same::type, - VTypeListFront>::value, - "Missing VL_RTTI_IMPL(...) call in 'T'"); - return this->isInstanceOfClassWithId(T::rttiClassId()); - } - - // Return cast to subtype T and assert of that type - template - T* as() { - UASSERT_OBJ(is(), this, "V3GraphVertex is not of expected type"); - return static_cast(this); - } - template - const T* as() const { - UASSERT_OBJ(is(), this, "V3GraphVertex is not of expected type"); - return static_cast(this); - } - - // Return cast to subtype T, else nullptr if different type - template - T* cast() { - return is() ? static_cast(this) : nullptr; - } - template - const T* cast() const { - return is() ? static_cast(this) : nullptr; - } - - // ACCESSORS - virtual string name() const { return ""; } - virtual string dotColor() const { return "black"; } - virtual string dotShape() const { return ""; } - virtual string dotStyle() const { return ""; } - virtual string dotName() const { return ""; } - virtual string dotRank() const { return ""; } - virtual uint32_t rankAdder() const { return 1; } - virtual FileLine* fileline() const { return nullptr; } // nullptr 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; - } - 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 VL_MT_STABLE { return m_user; } - void userp(void* userp) { m_userp = userp; } - void* userp() const VL_MT_STABLE { return m_userp; } - // ITERATORS - V3GraphVertex* verticesNextp() const { return m_vertices.nextp(); } - V3GraphEdge* inBeginp() const { return m_ins.begin(); } - bool inEmpty() const { return inBeginp() == nullptr; } - bool inSize1() const VL_MT_DISABLED; - V3GraphEdge* outBeginp() const { return m_outs.begin(); } - bool outEmpty() const { return outBeginp() == nullptr; } - bool outSize1() const VL_MT_DISABLED; - V3GraphEdge* beginp(GraphWay way) const { return way.forward() ? outBeginp() : inBeginp(); } - // METHODS - /// Error reporting - void v3errorEnd(std::ostringstream& str) const VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED; - void v3errorEndFatal(std::ostringstream& str) const - VL_RELEASE(V3Error::s().m_mutex) VL_MT_DISABLED; - /// Edges are routed around this vertex to point from "from" directly to "to" - void rerouteEdges(V3Graph* graphp) VL_MT_DISABLED; - /// Find the edge connecting ap and bp, where bp is wayward from ap. - /// If edge is not found returns nullptr. O(edges) performance. - V3GraphEdge* findConnectingEdgep(GraphWay way, const V3GraphVertex* waywardp) VL_MT_DISABLED; -}; - -std::ostream& operator<<(std::ostream& os, V3GraphVertex* vertexp) VL_MT_DISABLED; - -//============================================================================ -// TODO should we have a smaller edge structure when we don't need weight etc? - -class V3GraphEdge VL_NOT_FINAL { - VL_RTTI_IMPL_BASE(V3GraphEdge) - // Wires/variables aren't edges. Edges have only a single to/from vertex -public: - // ENUMS - enum Cutable : uint8_t { 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) - // - 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 - uint64_t m_user; // Marker for some algorithms - }; - // METHODS - void init(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, - bool cutable = false) VL_MT_DISABLED; - void cut() { m_weight = 0; } // 0 weight is same as disconnected - void outPushBack() VL_MT_DISABLED; - void inPushBack() VL_MT_DISABLED; - // CONSTRUCTORS -protected: - V3GraphEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, - const V3GraphEdge& old) VL_MT_DISABLED { - 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) VL_MT_DISABLED { - 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 VL_MT_DISABLED { - return new V3GraphEdge{graphp, fromp, top, *this}; - } - virtual ~V3GraphEdge() = default; - // METHODS - // Return true iff of type T - template - bool is() const { - static_assert(std::is_base_of::value, - "'T' must be a subtype of V3GraphEdge"); - static_assert(std::is_same::type, - VTypeListFront>::value, - "Missing VL_RTTI_IMPL(...) call in 'T'"); - return this->isInstanceOfClassWithId(T::rttiClassId()); - } - - // Return cast to subtype T and assert of that type - template - T* as() { - UASSERT(is(), "V3GraphEdge is not of expected type"); - return static_cast(this); - } - template - const T* as() const { - UASSERT(is(), "V3GraphEdge is not of expected type"); - return static_cast(this); - } - - // Return cast to subtype T, else nullptr if different type - template - T* cast() { - return is() ? static_cast(this) : nullptr; - } - template - const T* cast() const { - return is() ? static_cast(this) : nullptr; - } - - virtual string name() const { return m_fromp->name() + "->" + m_top->name(); } - virtual string dotLabel() const { return ""; } - 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()); - } - void unlinkDelete() VL_MT_DISABLED; - V3GraphEdge* relinkFromp(V3GraphVertex* newFromp) VL_MT_DISABLED; - V3GraphEdge* relinkTop(V3GraphVertex* newTop) VL_MT_DISABLED; - // 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(uint64_t user) { m_user = user; } - uint64_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(); } - V3GraphVertex* furtherp(GraphWay way) const { return way.forward() ? top() : fromp(); } - // STATIC ACCESSORS - static bool followNotCutable(const V3GraphEdge* edgep) { return !edgep->m_cutable; } - static bool followAlwaysTrue(const V3GraphEdge*) { return true; } - // ITERATORS - V3GraphEdge* outNextp() const { return m_outs.nextp(); } - V3GraphEdge* inNextp() const { return m_ins.nextp(); } - V3GraphEdge* nextp(GraphWay way) const { return way.forward() ? outNextp() : inNextp(); } -}; - -//============================================================================ - #endif // Guard diff --git a/src/V3GraphAcyc.cpp b/src/V3GraphAcyc.cpp index e6dc74446..7397f1d3b 100644 --- a/src/V3GraphAcyc.cpp +++ b/src/V3GraphAcyc.cpp @@ -39,11 +39,18 @@ class GraphAcycVertex final : public V3GraphVertex { V3GraphVertex* const m_origVertexp; // Pointer to first vertex this represents protected: friend class GraphAcyc; - V3ListEnt m_work; // List of vertices with optimization work left + V3ListLinks m_links; // List links to store instances of this class uint32_t m_storedRank = 0; // Rank held until commit to edge placement bool m_onWorkList = false; // True if already on list of work to do bool m_deleted = false; // True if deleted + +private: + V3ListLinks& links() { return m_links; } + public: + // List type to store instances of this class + using List = V3List; + GraphAcycVertex(V3Graph* graphp, V3GraphVertex* origVertexp) : V3GraphVertex{graphp} , m_origVertexp{origVertexp} {} @@ -103,7 +110,7 @@ class GraphAcyc final { // MEMBERS V3Graph* const m_origGraphp; // Original graph V3Graph m_breakGraph; // Graph with only breakable edges represented - V3List m_work; // List of vertices with optimization work left + GraphAcycVertex::List m_work; // List of vertices with optimization work left std::vector m_origEdgeDelp; // List of deletions to do when done const V3EdgeFuncP m_origEdgeFuncp; // Function that says we follow this edge (in original graph) @@ -127,12 +134,11 @@ class GraphAcyc final { bool origFollowEdge(V3GraphEdge* edgep) { return (edgep->weight() && (m_origEdgeFuncp)(edgep)); } - V3GraphEdge* edgeFromEdge(V3GraphEdge* oldedgep, V3GraphVertex* fromp, V3GraphVertex* top) { + void edgeFromEdge(V3GraphEdge* oldedgep, V3GraphVertex* fromp, V3GraphVertex* top) { // Make new breakGraph edge, with old edge as a template GraphAcycEdge* const newEdgep = new GraphAcycEdge{&m_breakGraph, fromp, top, oldedgep->weight(), oldedgep->cutable()}; 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 @@ -172,14 +178,14 @@ class GraphAcyc final { // 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); + m_work.linkBack(avertexp); } } - GraphAcycVertex* workBeginp() { return m_work.begin(); } + GraphAcycVertex* workBeginp() { return m_work.frontp(); } void workPop() { GraphAcycVertex* const avertexp = workBeginp(); avertexp->m_onWorkList = false; - avertexp->m_work.unlink(m_work, avertexp); + m_work.unlink(avertexp); } public: @@ -203,37 +209,35 @@ 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* const avertexp = new GraphAcycVertex{&m_breakGraph, overtexp}; - overtexp->userp(avertexp); // Stash so can look up later + for (V3GraphVertex& overtex : origGraphp->vertices()) { + if (overtex.color()) { + GraphAcycVertex* const avertexp = new GraphAcycVertex{&m_breakGraph, &overtex}; + overtex.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()) { - GraphAcycVertex* const avertexp = static_cast(overtexp->userp()); - buildGraphIterate(overtexp, avertexp); + for (V3GraphVertex& overtex : origGraphp->vertices()) { + if (overtex.color()) { + GraphAcycVertex* const avertexp = static_cast(overtex.userp()); + buildGraphIterate(&overtex, 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 - const V3GraphVertex* toVertexp = edgep->top(); + for (V3GraphEdge& edge : overtexp->outEdges()) { + if (origFollowEdge(&edge)) { // not cut + const V3GraphVertex* toVertexp = edge.top(); if (toVertexp->color()) { GraphAcycVertex* const 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()}; - addOrigEdgep(breakEdgep, edgep); // So can find original edge + edge.weight(), edge.cutable()}; + addOrigEdgep(breakEdgep, &edge); // So can find original edge } } } @@ -241,10 +245,7 @@ void GraphAcyc::buildGraphIterate(V3GraphVertex* overtexp, GraphAcycVertex* aver 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& vertex : m_breakGraph.vertices()) workPush(&vertex); // Optimize till everything finished while (GraphAcycVertex* vertexp = workBeginp()) { workPop(); @@ -266,10 +267,8 @@ 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(); - GraphAcycVertex* const avertexp = static_cast(vertexp); + for (V3GraphVertex* const vtxp : m_breakGraph.vertices().unlinkable()) { + GraphAcycVertex* const avertexp = static_cast(vtxp); if (avertexp->isDelete()) { VL_DO_DANGLING(avertexp->unlinkDelete(&m_breakGraph), avertexp); } @@ -284,17 +283,13 @@ void GraphAcyc::simplifyNone(GraphAcycVertex* avertexp) { UINFO(9, " SimplifyNoneRemove " << avertexp << endl); avertexp->setDelete(); // Mark so we won't delete it twice // Remove edges - while (V3GraphEdge* const edgep = avertexp->outBeginp()) { - V3GraphVertex* otherVertexp = edgep->top(); - // UINFO(9, " out " << otherVertexp << endl); + while (V3GraphEdge* const edgep = avertexp->outEdges().frontp()) { + workPush(edgep->top()); VL_DO_DANGLING(edgep->unlinkDelete(), edgep); - workPush(otherVertexp); } - while (V3GraphEdge* const edgep = avertexp->inBeginp()) { - V3GraphVertex* otherVertexp = edgep->fromp(); - // UINFO(9, " in " << otherVertexp << endl); + while (V3GraphEdge* const edgep = avertexp->inEdges().frontp()) { + workPush(edgep->fromp()); VL_DO_DANGLING(edgep->unlinkDelete(), edgep); - workPush(otherVertexp); } } } @@ -303,10 +298,10 @@ 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(); + V3GraphEdge* const inEdgep = avertexp->inEdges().frontp(); + V3GraphEdge* const outEdgep = avertexp->outEdges().frontp(); + V3GraphVertex* const inVertexp = inEdgep->fromp(); + V3GraphVertex* const 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) { @@ -317,7 +312,7 @@ void GraphAcyc::simplifyOne(GraphAcycVertex* avertexp) { // 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 + V3GraphEdge* const templateEdgep = ((inEdgep->cutable() && (!outEdgep->cutable() || inEdgep->weight() < outEdgep->weight())) ? inEdgep @@ -339,13 +334,12 @@ void GraphAcyc::simplifyOut(GraphAcycVertex* avertexp) { // to the next node in the list if (avertexp->isDelete()) return; if (avertexp->outSize1()) { - V3GraphEdge* outEdgep = avertexp->outBeginp(); + V3GraphEdge* const outEdgep = avertexp->outEdges().frontp(); if (!outEdgep->cutable()) { V3GraphVertex* outVertexp = outEdgep->top(); UINFO(9, " SimplifyOutRemove " << avertexp << endl); avertexp->setDelete(); // Mark so we won't delete it twice - for (V3GraphEdge *nextp, *inEdgep = avertexp->inBeginp(); inEdgep; inEdgep = nextp) { - nextp = inEdgep->inNextp(); + for (V3GraphEdge* const inEdgep : avertexp->inEdges().unlinkable()) { V3GraphVertex* inVertexp = inEdgep->fromp(); if (inVertexp == avertexp) { if (debug()) v3error("Non-cutable vertex=" << avertexp); // LCOV_EXCL_LINE @@ -375,12 +369,9 @@ void GraphAcyc::simplifyDup(GraphAcycVertex* avertexp) { // Remove redundant edges if (avertexp->isDelete()) return; // Clear marks - for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - edgep->top()->userp(nullptr); - } + for (V3GraphEdge& edge : avertexp->outEdges()) edge.top()->userp(nullptr); // Mark edges and detect duplications - for (V3GraphEdge *nextp, *edgep = avertexp->outBeginp(); edgep; edgep = nextp) { - nextp = edgep->outNextp(); + for (V3GraphEdge* const edgep : avertexp->outEdges().unlinkable()) { V3GraphVertex* outVertexp = edgep->top(); V3GraphEdge* prevEdgep = static_cast(outVertexp->userp()); if (prevEdgep) { @@ -413,8 +404,7 @@ void GraphAcyc::simplifyDup(GraphAcycVertex* avertexp) { 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(); + for (V3GraphEdge* const edgep : avertexp->outEdges().unlinkable()) { if (edgep->cutable() && edgep->top() == avertexp) { cutOrigEdge(edgep, " Cut Basic"); VL_DO_DANGLING(edgep->unlinkDelete(), edgep); @@ -427,15 +417,12 @@ void GraphAcyc::cutBackward(GraphAcycVertex* avertexp) { // If a cutable edge is from A->B, and there's a non-cutable edge B->A, then must cut! if (avertexp->isDelete()) return; // Clear marks - for (V3GraphEdge* edgep = avertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - edgep->top()->user(false); - } - for (V3GraphEdge* edgep = avertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - if (!edgep->cutable()) edgep->fromp()->user(true); + for (V3GraphEdge& edge : avertexp->outEdges()) edge.top()->user(false); + for (V3GraphEdge& edge : avertexp->inEdges()) { + if (!edge.cutable()) edge.fromp()->user(true); } // Detect duplications - for (V3GraphEdge *nextp, *edgep = avertexp->outBeginp(); edgep; edgep = nextp) { - nextp = edgep->outNextp(); + for (V3GraphEdge* const edgep : avertexp->outEdges().unlinkable()) { if (edgep->cutable() && edgep->top()->user()) { cutOrigEdge(edgep, " Cut A->B->A"); VL_DO_DANGLING(edgep->unlinkDelete(), edgep); @@ -449,10 +436,9 @@ 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& vertex : m_breakGraph.vertices()) { + for (V3GraphEdge& edge : vertex.outEdges()) { + if (edge.weight() && edge.cutable()) ++numEdges; } } UINFO(4, " Cutable edges = " << numEdges << endl); @@ -460,11 +446,10 @@ void GraphAcyc::place() { std::vector edges; // List of all edges to be processed // Make the vector properly sized right off the bat -- faster than reallocating edges.reserve(numEdges + 1); - 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); + for (V3GraphVertex& vertex : m_breakGraph.vertices()) { + vertex.user(0); // Clear in prep of next step + for (V3GraphEdge& edge : vertex.outEdges()) { + if (edge.weight() && edge.cutable()) edges.push_back(&edge); } } @@ -522,9 +507,9 @@ bool GraphAcyc::placeIterate(GraphAcycVertex* vertexp, uint32_t currentRank) { } 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 (placeIterate(static_cast(edgep->top()), currentRank + 1)) { + for (V3GraphEdge& edge : vertexp->outEdges()) { + if (edge.weight() && !edge.cutable()) { + if (placeIterate(static_cast(edge.top()), currentRank + 1)) { // We don't need to reset user(); we'll use a different placeStep for the next edge return true; // Loop detected } diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp index d1aa032fb..7eec461e1 100644 --- a/src/V3GraphAlg.cpp +++ b/src/V3GraphAlg.cpp @@ -42,19 +42,15 @@ class GraphRemoveRedundant final : GraphAlg<> { const 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& vertex : m_graphp->vertices()) vertexIterate(&vertex); } void vertexIterate(V3GraphVertex* vertexp) { // Clear marks - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - edgep->top()->userp(nullptr); - } + for (V3GraphEdge& edge : vertexp->outEdges()) edge.top()->userp(nullptr); + // Mark edges and detect duplications - for (V3GraphEdge *nextp, *edgep = vertexp->outBeginp(); edgep; edgep = nextp) { - nextp = edgep->outNextp(); + + for (V3GraphEdge* const edgep : vertexp->outEdges().unlinkable()) { if (followEdge(edgep)) { V3GraphVertex* outVertexp = edgep->top(); V3GraphEdge* prevEdgep = static_cast(outVertexp->userp()); @@ -112,15 +108,15 @@ public: : GraphAlg<>(graphp, nullptr) {} void go() { GraphPathChecker checker{m_graphp}; - for (V3GraphVertex* vxp = m_graphp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { + for (V3GraphVertex& vtx : m_graphp->vertices()) { V3GraphEdge* deletep = nullptr; - for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { + for (V3GraphEdge& edge : vtx.outEdges()) { if (deletep) VL_DO_CLEAR(deletep->unlinkDelete(), deletep = nullptr); // It should be safe to modify the graph, despite using // the GraphPathChecker, as none of the modifications will // change what can be reached from what, nor should they // change the rank or CP of any node. - if (checker.isTransitiveEdge(edgep)) deletep = edgep; + if (checker.isTransitiveEdge(&edge)) deletep = &edge; } if (deletep) VL_DO_DANGLING(deletep->unlinkDelete(), deletep); } @@ -143,10 +139,9 @@ class GraphAlgWeakly final : GraphAlg<> { m_graphp->clearColors(); // Color graph uint32_t currentColor = 0; - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { + for (V3GraphVertex& vertex : m_graphp->vertices()) { currentColor++; - vertexIterate(vertexp, currentColor); + vertexIterate(&vertex, currentColor); } } @@ -155,11 +150,11 @@ class GraphAlgWeakly final : GraphAlg<> { // 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& edge : vertexp->outEdges()) { + if (followEdge(&edge)) vertexIterate(edge.top(), currentColor); } - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - if (followEdge(edgep)) vertexIterate(edgep->fromp(), currentColor); + for (V3GraphEdge& edge : vertexp->inEdges()) { + if (followEdge(&edge)) vertexIterate(edge.fromp(), currentColor); } } @@ -192,33 +187,30 @@ class GraphAlgStrongly final : GraphAlg<> { // 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); + for (V3GraphVertex& vertex : m_graphp->vertices()) { + vertex.color(0); + vertex.user(0); } // Color graph - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - if (!vertexp->user()) { + for (V3GraphVertex& vertex : m_graphp->vertices()) { + if (!vertex.user()) { m_currentDfs++; - vertexIterate(vertexp); + vertexIterate(&vertex); } } // 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()) { + for (V3GraphVertex& vertex : m_graphp->vertices()) { bool onecolor = true; - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (followEdge(edgep)) { - if (vertexp->color() == edgep->top()->color()) { + for (V3GraphEdge& edge : vertex.outEdges()) { + if (followEdge(&edge)) { + if (vertex.color() == edge.top()->color()) { onecolor = false; break; } } } - if (onecolor) vertexp->color(0); + if (onecolor) vertex.color(0); } } @@ -226,9 +218,9 @@ class GraphAlgStrongly final : GraphAlg<> { const 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(); + for (V3GraphEdge& edge : vertexp->outEdges()) { + if (followEdge(&edge)) { + V3GraphVertex* top = edge.top(); if (!top->user()) { // Dest not computed yet vertexIterate(top); } @@ -273,15 +265,13 @@ class GraphAlgRank final : GraphAlg<> { // 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& vertex : m_graphp->vertices()) { + vertex.rank(0); + vertex.user(0); } - for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - if (!vertexp->user()) { // - vertexIterate(vertexp, 1); + for (V3GraphVertex& vertex : m_graphp->vertices()) { + if (!vertex.user()) { // + vertexIterate(&vertex, 1); } } } @@ -298,10 +288,8 @@ class GraphAlgRank final : GraphAlg<> { 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()); - } + for (V3GraphEdge& edge : vertexp->outEdges()) { + if (followEdge(&edge)) vertexIterate(edge.top(), currentRank + vertexp->rankAdder()); } vertexp->user(2); } @@ -353,8 +341,8 @@ class GraphAlgRLoops final : GraphAlg<> { } 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); + for (V3GraphEdge& edge : vertexp->outEdges()) { + if (followEdge(&edge)) vertexIterate(edge.top(), currentRank); } vertexp->user(2); } @@ -387,13 +375,13 @@ class GraphAlgSubtrees final : GraphAlg<> { newVertexp = vertexp->clone(m_loopGraphp); vertexp->userp(newVertexp); - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (followEdge(edgep)) { - V3GraphEdge* newEdgep = static_cast(edgep->userp()); + for (V3GraphEdge& edge : vertexp->outEdges()) { + if (followEdge(&edge)) { + V3GraphEdge* newEdgep = static_cast(edge.userp()); if (!newEdgep) { - V3GraphVertex* newTop = vertexIterateAll(edgep->top()); - newEdgep = edgep->clone(m_loopGraphp, newVertexp, newTop); - edgep->userp(newEdgep); + V3GraphVertex* newTop = vertexIterateAll(edge.top()); + newEdgep = edge.clone(m_loopGraphp, newVertexp, newTop); + edge.userp(newEdgep); } } } @@ -438,30 +426,25 @@ struct GraphSortEdgeCmp final { 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); - } + for (V3GraphVertex& vertex : m_vertices) vertices.push_back(&vertex); std::stable_sort(vertices.begin(), vertices.end(), GraphSortVertexCmp()); - this->verticesUnlink(); - for (V3GraphVertex* ip : vertices) ip->verticesPushBack(this); + // Re-insert in sorted order + for (V3GraphVertex* const ip : vertices) { + m_vertices.unlink(ip); + m_vertices.linkBack(ip); + } } 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()) { + for (V3GraphVertex& vertex : vertices()) { // Make a vector - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - edges.push_back(edgep); - } + for (V3GraphEdge& edge : vertex.outEdges()) edges.push_back(&edge); // 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(); - for (V3GraphEdge* edgep : edges) edgep->outPushBack(); + for (V3GraphEdge* const edgep : edges) edgep->relinkFromp(&vertex); // Prep for next edges.clear(); } @@ -487,8 +470,8 @@ void V3Graph::orderPreRanked() { // Compute fanouts // Vertex::m_user begin: 1 indicates processing, 2 indicates completed userClearVertices(); - for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp = vertexp->verticesNextp()) { - if (!vertexp->user()) orderDFSIterate(vertexp); + for (V3GraphVertex& vertex : vertices()) { + if (!vertex.user()) orderDFSIterate(&vertex); } // Sort list of vertices by rank, then fanout. Fanout is a bit of a @@ -506,12 +489,12 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) { UASSERT_OBJ(vertexp->user() != 1, vertexp, "Loop found, backward edges should be dead"); vertexp->user(1); double fanout = 0; - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (edgep->weight()) fanout += orderDFSIterate(edgep->m_top); + for (V3GraphEdge& edge : vertexp->outEdges()) { + if (edge.weight()) fanout += orderDFSIterate(edge.top()); } // Just count inbound edges - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - if (edgep->weight()) ++fanout; + for (V3GraphEdge& edge : vertexp->inEdges()) { + if (edge.weight()) ++fanout; } vertexp->fanout(fanout); vertexp->user(2); @@ -524,12 +507,12 @@ double V3Graph::orderDFSIterate(V3GraphVertex* vertexp) { class GraphAlgParallelismReport final { // MEMBERS - const V3Graph& m_graph; // The graph + V3Graph& m_graph; // The graph const std::function m_vertexCost; // vertex cost function V3Graph::ParallelismReport m_report; // The result report // CONSTRUCTORS - explicit GraphAlgParallelismReport(const V3Graph& graph, + explicit GraphAlgParallelismReport(V3Graph& graph, std::function vertexCost) : m_graph{graph} , m_vertexCost{vertexCost} { @@ -540,13 +523,13 @@ class GraphAlgParallelismReport final { for (const V3GraphVertex* vertexp; (vertexp = serialize.nextp());) { ++m_report.m_vertexCount; uint64_t cpCostToHere = 0; - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { + for (const V3GraphEdge& edge : vertexp->inEdges()) { ++m_report.m_edgeCount; // For each upstream item, add its critical path cost to // the cost of this edge, to form a new candidate critical // path cost to the current node. Whichever is largest is // the critical path to reach the start of this node. - cpCostToHere = std::max(cpCostToHere, critPaths[edgep->fromp()]); + cpCostToHere = std::max(cpCostToHere, critPaths[edge.fromp()]); } // Include the cost of the current vertex in the critical // path, so it represents the critical path to the end of @@ -564,12 +547,12 @@ class GraphAlgParallelismReport final { public: static V3Graph::ParallelismReport - apply(const V3Graph& graph, std::function vertexCost) { + apply(V3Graph& graph, std::function vertexCost) { return GraphAlgParallelismReport(graph, vertexCost).m_report; } }; V3Graph::ParallelismReport -V3Graph::parallelismReport(std::function vertexCost) const { +V3Graph::parallelismReport(std::function vertexCost) { return GraphAlgParallelismReport::apply(*this, vertexCost); } diff --git a/src/V3GraphPathChecker.cpp b/src/V3GraphPathChecker.cpp index b528714d9..f7f72cdfb 100644 --- a/src/V3GraphPathChecker.cpp +++ b/src/V3GraphPathChecker.cpp @@ -53,36 +53,17 @@ struct GraphPCNode final { //###################################################################### // GraphPathChecker implementation -GraphPathChecker::GraphPathChecker(const V3Graph* graphp, V3EdgeFuncP edgeFuncp) - : GraphAlg{graphp, edgeFuncp} { - for (V3GraphVertex* vxp = graphp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - // Setup tracking structure for each node. If delete a vertex - // there would be a leak, but ok as accept only const V3Graph*'s. - vxp->userp(new GraphPCNode); - } - // Init critical paths in userp() for each vertex - initHalfCriticalPaths(GraphWay::FORWARD, false); - initHalfCriticalPaths(GraphWay::REVERSE, false); -} - -GraphPathChecker::~GraphPathChecker() { - // Free every GraphPCNode - for (V3GraphVertex* vxp = m_graphp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - const GraphPCNode* const nodep = static_cast(vxp->userp()); - VL_DO_DANGLING(delete nodep, nodep); - vxp->userp(nullptr); - } -} - -void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) { +template +void GraphPathChecker::initHalfCriticalPaths(bool checkOnly) { + constexpr GraphWay way{T_Way}; + constexpr GraphWay rev = way.invert(); GraphStreamUnordered order(m_graphp, way); - const GraphWay rev = way.invert(); while (const V3GraphVertex* const vertexp = order.nextp()) { unsigned critPathCost = 0; - for (V3GraphEdge* edgep = vertexp->beginp(rev); edgep; edgep = edgep->nextp(rev)) { - if (!m_edgeFuncp(edgep)) continue; + for (const V3GraphEdge& edge : vertexp->edges()) { + if (!m_edgeFuncp(&edge)) continue; - const V3GraphVertex* wrelativep = edgep->furtherp(rev); + const V3GraphVertex* wrelativep = edge.furtherp(); const GraphPCNode* const wrelUserp = static_cast(wrelativep->userp()); critPathCost = std::max(critPathCost, wrelUserp->m_cp[way] + 1); } @@ -97,6 +78,30 @@ void GraphPathChecker::initHalfCriticalPaths(GraphWay way, bool checkOnly) { } } +template void GraphPathChecker::initHalfCriticalPaths(bool); +template void GraphPathChecker::initHalfCriticalPaths(bool); + +GraphPathChecker::GraphPathChecker(V3Graph* graphp, V3EdgeFuncP edgeFuncp) + : GraphAlg{graphp, edgeFuncp} { + for (V3GraphVertex& vtx : graphp->vertices()) { + // Setup tracking structure for each node. If delete a vertex + // there would be a leak, but ok as accept only const V3Graph*'s. + vtx.userp(new GraphPCNode); + } + // Init critical paths in userp() for each vertex + initHalfCriticalPaths(false); + initHalfCriticalPaths(false); +} + +GraphPathChecker::~GraphPathChecker() { + // Free every GraphPCNode + for (V3GraphVertex& vtx : m_graphp->vertices()) { + const GraphPCNode* const nodep = static_cast(vtx.userp()); + VL_DO_DANGLING(delete nodep, nodep); + vtx.userp(nullptr); + } +} + bool GraphPathChecker::pathExistsInternal(const V3GraphVertex* ap, const V3GraphVertex* bp, unsigned* costp) { GraphPCNode* const auserp = static_cast(ap->userp()); @@ -121,11 +126,11 @@ bool GraphPathChecker::pathExistsInternal(const V3GraphVertex* ap, const V3Graph // Slow path; visit some extended family bool foundPath = false; - for (V3GraphEdge* edgep = ap->outBeginp(); edgep && !foundPath; edgep = edgep->outNextp()) { - if (!m_edgeFuncp(edgep)) continue; + for (const V3GraphEdge& edge : ap->outEdges()) { + if (!m_edgeFuncp(&edge)) continue; unsigned childCost; - if (pathExistsInternal(edgep->top(), bp, &childCost)) foundPath = true; + if (pathExistsInternal(edge.top(), bp, &childCost)) foundPath = true; if (costp) *costp += childCost; } @@ -141,10 +146,9 @@ bool GraphPathChecker::isTransitiveEdge(const V3GraphEdge* edgep) { const V3GraphVertex* fromp = edgep->fromp(); const V3GraphVertex* top = edgep->top(); incGeneration(); - for (const V3GraphEdge* fromOutp = fromp->outBeginp(); fromOutp; - fromOutp = fromOutp->outNextp()) { - if (fromOutp == edgep) continue; - if (pathExistsInternal(fromOutp->top(), top)) return true; + for (const V3GraphEdge& fromOut : fromp->outEdges()) { + if (&fromOut == edgep) continue; + if (pathExistsInternal(fromOut.top(), top)) return true; } return false; } diff --git a/src/V3GraphPathChecker.h b/src/V3GraphPathChecker.h index 124feedc8..dfa043f46 100644 --- a/src/V3GraphPathChecker.h +++ b/src/V3GraphPathChecker.h @@ -30,7 +30,7 @@ /// /// The graph (or at least, the subset the algorithm sees through /// edgeFuncp) must not change during the lifetime of the checker. -class GraphPathChecker final : GraphAlg { +class GraphPathChecker final : GraphAlg { // Count "generations" which increases on operations that scan through // the graph. Each node is marked with the last generation that scanned // it, to enable asserting there are no cycles, and to avoid recursing @@ -39,9 +39,8 @@ class GraphPathChecker final : GraphAlg { public: // CONSTRUCTORS - explicit GraphPathChecker(const V3Graph* graphp, - V3EdgeFuncP edgeFuncp - = V3GraphEdge::followAlwaysTrue) VL_MT_DISABLED; + explicit GraphPathChecker(V3Graph* graphp, V3EdgeFuncP edgeFuncp + = V3GraphEdge::followAlwaysTrue) VL_MT_DISABLED; ~GraphPathChecker() VL_MT_DISABLED; // METHODS @@ -55,7 +54,8 @@ public: private: bool pathExistsInternal(const V3GraphVertex* ap, const V3GraphVertex* bp, unsigned* costp = nullptr) VL_MT_DISABLED; - void initHalfCriticalPaths(GraphWay way, bool checkOnly) VL_MT_DISABLED; + template + void initHalfCriticalPaths(bool checkOnly) VL_MT_DISABLED; void incGeneration() { ++m_generation; } VL_UNCOPYABLE(GraphPathChecker); diff --git a/src/V3GraphStream.h b/src/V3GraphStream.h index 0e6391216..7741938b0 100644 --- a/src/V3GraphStream.h +++ b/src/V3GraphStream.h @@ -104,32 +104,25 @@ public: , m_last{m_readyVertices.end()} , m_way{way} { uint32_t pos = 0; - for (const V3GraphVertex* vxp = graphp->verticesBeginp(); vxp; - vxp = vxp->verticesNextp()) { + for (const V3GraphVertex& vtx : graphp->vertices()) { // Every vertex initially is waiting, or ready. if (way == GraphWay::FORWARD) { - if (vxp->inEmpty()) { - const VxHolder newVx{vxp, pos++, 0}; + if (vtx.inEmpty()) { + const VxHolder newVx{&vtx, pos++, 0}; m_readyVertices.insert(newVx); } else { - uint32_t depCount = 0; - for (V3GraphEdge* depp = vxp->inBeginp(); depp; depp = depp->inNextp()) { - ++depCount; - } - const VxHolder newVx{vxp, pos++, depCount}; - m_waitingVertices.emplace(vxp, newVx); + const uint32_t depCount = vtx.inEdges().size(); + const VxHolder newVx{&vtx, pos++, depCount}; + m_waitingVertices.emplace(&vtx, newVx); } } else { // REVERSE - if (vxp->outEmpty()) { - const VxHolder newVx{vxp, pos++, 0}; + if (vtx.outEmpty()) { + const VxHolder newVx{&vtx, pos++, 0}; m_readyVertices.insert(newVx); } else { - uint32_t depCount = 0; - for (V3GraphEdge* depp = vxp->outBeginp(); depp; depp = depp->outNextp()) { - ++depCount; - } - const VxHolder newVx{vxp, pos++, depCount}; - m_waitingVertices.emplace(vxp, newVx); + const uint32_t depCount = vtx.outEdges().size(); + const VxHolder newVx{&vtx, pos++, depCount}; + m_waitingVertices.emplace(&vtx, newVx); } } } @@ -196,8 +189,8 @@ public: private: void unblockDeps(const V3GraphVertex* vertexp) { if (m_way == GraphWay::FORWARD) { - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const V3GraphVertex* const toVertexp = edgep->top(); + for (const V3GraphEdge& edgep : vertexp->outEdges()) { + const V3GraphVertex* const toVertexp = edgep.top(); const auto it = m_waitingVertices.find(toVertexp); UASSERT_OBJ(it != m_waitingVertices.end(), toVertexp, @@ -208,8 +201,8 @@ private: } } } else { - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - const V3GraphVertex* const fromVertexp = edgep->fromp(); + for (const V3GraphEdge& edge : vertexp->inEdges()) { + const V3GraphVertex* const fromVertexp = edge.fromp(); const auto it = m_waitingVertices.find(fromVertexp); UASSERT_OBJ(it != m_waitingVertices.end(), fromVertexp, @@ -239,7 +232,7 @@ class GraphStreamUnordered final { public: // CONSTRUCTORS VL_UNCOPYABLE(GraphStreamUnordered); - explicit GraphStreamUnordered(const V3Graph* graphp, GraphWay way = GraphWay::FORWARD) + explicit GraphStreamUnordered(V3Graph* graphp, GraphWay way = GraphWay::FORWARD) : m_way{way} { if (m_way == GraphWay::FORWARD) { init(graphp); @@ -272,27 +265,21 @@ public: private: template // - VL_ATTR_NOINLINE void init(const V3Graph* graphp) { + VL_ATTR_NOINLINE void init(V3Graph* graphp) { constexpr GraphWay way{T_Way}; - constexpr GraphWay inv = way.invert(); // Assign every vertex without an incoming edge to ready, others to waiting - for (V3GraphVertex *vertexp = graphp->verticesBeginp(), *nextp; vertexp; vertexp = nextp) { - nextp = vertexp->verticesNextp(); - uint32_t nDeps = 0; - for (V3GraphEdge* edgep = vertexp->beginp(inv); edgep; edgep = edgep->nextp(inv)) { - ++nDeps; - } - vertexp->color(nDeps); // Using color instead of user, as user might be used by client - if (VL_UNLIKELY(nDeps == 0)) m_nextVertices.push_back(vertexp); + for (V3GraphVertex& vertex : graphp->vertices()) { + const uint32_t nDeps = vertex.edges().size(); + vertex.color(nDeps); // Using color instead of user, as user might be used by client + if (VL_UNLIKELY(nDeps == 0)) m_nextVertices.push_back(&vertex); } } template // VL_ATTR_NOINLINE const V3GraphVertex* unblock(const V3GraphVertex* resultp) { constexpr GraphWay way{T_Way}; - for (V3GraphEdge *edgep = resultp->beginp(way), *nextp; edgep; edgep = nextp) { - nextp = edgep->nextp(way); - V3GraphVertex* const vertexp = edgep->furtherp(way); + for (const V3GraphEdge& edge : resultp->edges()) { + V3GraphVertex* const vertexp = edge.furtherp(); #if VL_DEBUG UASSERT_OBJ(vertexp->color() != 0, vertexp, "Should not be on waiting list"); #endif diff --git a/src/V3GraphTest.cpp b/src/V3GraphTest.cpp index 293de138e..75760d694 100644 --- a/src/V3GraphTest.cpp +++ b/src/V3GraphTest.cpp @@ -259,7 +259,7 @@ public: gp->order(); // Test debug function - gp->dumpEdges(std::cout, ap); + gp->dumpEdges(std::cout, *ap); dumpSelf(); } diff --git a/src/V3LifePost.cpp b/src/V3LifePost.cpp index f6d0541ad..44a63688d 100644 --- a/src/V3LifePost.cpp +++ b/src/V3LifePost.cpp @@ -149,7 +149,7 @@ class LifePostDlyVisitor final : public VNVisitor { // Map each dly var to its AstAssignPost* node and the location thereof std::unordered_map m_assignposts; - const V3Graph* m_mtasksGraphp = nullptr; // Mtask tracking graph + V3Graph* m_mtasksGraphp = nullptr; // Mtask tracking graph std::unique_ptr m_checker; const AstCFunc* const m_evalNbap; // The _eval__nba function @@ -325,9 +325,8 @@ class LifePostDlyVisitor final : public VNVisitor { UASSERT_OBJ(!m_mtasksGraphp, nodep, "Cannot handle more than one AstExecGraph"); m_mtasksGraphp = nodep->depGraphp(); } - for (V3GraphVertex* mtaskVxp = nodep->depGraphp()->verticesBeginp(); mtaskVxp; - mtaskVxp = mtaskVxp->verticesNextp()) { - const ExecMTask* const mtaskp = mtaskVxp->as(); + for (V3GraphVertex& mtaskVtx : nodep->depGraphp()->vertices()) { + const ExecMTask* const mtaskp = mtaskVtx.as(); m_execMTaskp = mtaskp; m_sequence = 0; iterate(mtaskp->bodyp()); diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 4c4e50bac..8bf4e41ee 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -165,8 +165,8 @@ class LinkCellsVisitor final : public VNVisitor { m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells"); m_graph.rank(); - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (const LinkCellsVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex& vtx : m_graph.vertices()) { + if (const LinkCellsVertex* const vvertexp = vtx.cast()) { // +1 so we leave level 1 for the new wrapper we'll make in a moment AstNodeModule* const modp = vvertexp->modp(); modp->level(vvertexp->rank() + 1); diff --git a/src/V3List.h b/src/V3List.h index 6b6305929..4abedc532 100644 --- a/src/V3List.h +++ b/src/V3List.h @@ -1,6 +1,6 @@ // -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* -// DESCRIPTION: Verilator: List class with storage in existing classes +// DESCRIPTION: Verilator: Doubly linked endogenous (intrusive) list // // Code available from: https://verilator.org // @@ -20,124 +20,324 @@ #include "config_build.h" #include "verilatedos.h" -#include +#include "V3Error.h" + +#include +#include //============================================================================ +// The list links (just 2 pointers), to be instantiated as a member in the +// list element base class 'T_Base'. They are considered mutable, even if the +// list element is 'const', as they are only used for storing the elements in +// a V3List. That is, you can store const elements in a V3List. +template +class V3ListLinks final { + // The V3List itself, but nothing else can access the link pointers + template & (B::*)(), typename> + friend class V3List; -template -class V3List; -template -class V3ListEnt; - -template -class V3List final { - // List container for linked list of elements of type *T (T is a pointer type) -private: - // MEMBERS - T m_headp = nullptr; // First element - T m_tailp = nullptr; // Last element - friend class V3ListEnt; + T_Base* m_nextp = nullptr; // Next element in list + T_Base* m_prevp = nullptr; // Previous element in list public: - V3List() = default; - ~V3List() = default; - // METHODS - T begin() const { return m_headp; } - T end() const { return nullptr; } - T rbegin() const { return m_tailp; } - T rend() const { return nullptr; } - bool empty() const { return m_headp == nullptr; } - void reset() { // clear() without walking the list - m_headp = nullptr; - m_tailp = nullptr; - } -}; - -//============================================================================ - -template -class V3ListEnt final { - // List entry for linked list of elements of type *T (T is a pointer type) -private: - // MEMBERS - T m_nextp = nullptr; // Pointer to next element, nullptr=end - T m_prevp = nullptr; // Pointer to previous element, nullptr=beginning - friend class V3List; - static V3ListEnt* baseToListEnt(void* newbasep, size_t offset) { - // "this" must be an 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*)(((uint8_t*)newbasep) + offset); - } - -public: - V3ListEnt() = default; - ~V3ListEnt() { + V3ListLinks() = default; + ~V3ListLinks() { #ifdef VL_DEBUG - // Load bogus pointers so we can catch deletion bugs - m_nextp = reinterpret_cast(1); - m_prevp = reinterpret_cast(1); + m_nextp = reinterpret_cast(1); + m_prevp = reinterpret_cast(1); #endif } - T nextp() const { return m_nextp; } - T prevp() const { return m_prevp; } - // METHODS - void pushBack(V3List& listr, T newp) { - // "this" must be an element inside of *newp - // cppcheck-suppress thisSubtraction - const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp); - m_nextp = nullptr; - 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 an element inside of *newp - // cppcheck-suppress thisSubtraction - const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(newp); - m_nextp = listr.m_headp; - if (m_nextp) baseToListEnt(m_nextp, offset)->m_prevp = newp; - listr.m_headp = newp; - m_prevp = nullptr; - if (!listr.m_tailp) listr.m_tailp = newp; - } - // Unlink from side - void unlink(V3List& listr, T oldp) { - // "this" must be an element inside of *oldp - // cppcheck-suppress thisSubtraction - const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_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 = nullptr; - } - // Remove all nodes from 'oldListr', append them to 'newListr'. 'this' must be a member of the - // object at 'selfp', and 'selfp' must be the head of the list in 'oldListr'. - void moveAppend(V3List& oldListr, V3List& newListr, T selfp) { - UASSERT(selfp == oldListr.m_headp, "Must be head of list to use 'moveAppend'"); - const size_t offset = (size_t)(uint8_t*)(this) - (size_t)(uint8_t*)(selfp); - const T headp = selfp; - const T tailp = oldListr.m_tailp; - oldListr.reset(); - if (newListr.empty()) { - newListr.m_headp = headp; - newListr.m_tailp = tailp; - } else { - baseToListEnt(newListr.m_tailp, offset)->m_nextp = headp; - m_prevp = newListr.m_tailp; - newListr.m_tailp = tailp; - } - } + VL_UNCOPYABLE(V3ListLinks); + VL_UNMOVABLE(V3ListLinks); }; //============================================================================ +// Generic endogenous (or intrusive) doubly linked list, with links stored +// inside the elements. The template parameters are: +// - 'T_Base' is the base type of elements that contains the V3ListLinks +// instance as a data member. +// - 'LinksGetter' is a member function pointer that returns a reference to +// the links within 'T_Base'. Note that you should be able to use a data +// member pointer, instead of a function pointer, but that is buggy in MSVC. +// - 'T_Element' is the actual type of elements, which must be the same, +// or a subtype of 'T_Base'. +template & (T_Base::*LinksGetter)(), // + typename T_Element = T_Base> +class V3List final { + static_assert(std::is_base_of::value, + "'T_Element' must be a subtype of 'T_Base"); + + using ListType = V3List; + + // MEMBERS + T_Base* m_headp = nullptr; + T_Base* m_lastp = nullptr; + + // Given the T_Base, return the Links. The links are always mutable, even in const elements. + VL_ATTR_ALWINLINE + static V3ListLinks& toLinks(const T_Base* elementp) { + return (const_cast(elementp)->*LinksGetter)(); + } + + VL_ATTR_ALWINLINE + static void prefetch(T_Base* elementp, T_Base* fallbackp) { + UDEBUGONLY(UASSERT(fallbackp, "Prefetch fallback pointer must be non nullptr");); + // This compiles to a branchless prefetch with cmove, with the address always valid + VL_PREFETCH_RW(elementp ? elementp : fallbackp); + } + + // Iterator class template for V3List. This is just enough to support range based for loops + // and basic usage. Feel free to extend as required. + template + class SimpleItertatorImpl final { + static_assert(std::is_same::value + || std::is_same::value, + "'SimpleItertatorImpl' must be used with element type only"); + + // The List itself, but nothing else can construct iterators + template & (B::*)(), typename> + friend class V3List; + + using IteratorType = SimpleItertatorImpl; + + T_Base* m_currp; // Currently iterated element, or 'nullptr' for 'end()' iterator + + VL_ATTR_ALWINLINE + SimpleItertatorImpl(T_Base* elementp) + : m_currp{elementp} {} + + VL_ATTR_ALWINLINE + static T_Base* step(T_Base* currp) { + if VL_CONSTEXPR_CXX17 (T_Reverse) { + return toLinks(currp).m_prevp; + } else { + return toLinks(currp).m_nextp; + } + } + + public: + // Dereference + VL_ATTR_ALWINLINE + T_IteratorElement& operator*() const { + UDEBUGONLY(UASSERT(m_currp, "Dereferencing end of list iterator");); + prefetch(step(m_currp), m_currp); + return *static_cast(m_currp); + } + // Pre increment + VL_ATTR_ALWINLINE + IteratorType& operator++() { + UDEBUGONLY(UASSERT(m_currp, "Pre-incrementing end of list iterator");); + m_currp = step(m_currp); + return *this; + } + // Post increment + VL_ATTR_ALWINLINE + IteratorType operator++(int) { + UDEBUGONLY(UASSERT(m_currp, "Post-incrementing end of list iterator");); + T_Base* const elementp = m_currp; + m_currp = step(m_currp); + return IteratorType{elementp}; + } + VL_ATTR_ALWINLINE + bool operator==(const IteratorType& other) const { return m_currp == other.m_currp; } + VL_ATTR_ALWINLINE + bool operator!=(const IteratorType& other) const { return m_currp != other.m_currp; } + // Convert to const iterator + VL_ATTR_ALWINLINE + operator SimpleItertatorImpl() const { + return SimpleItertatorImpl{m_currp}; + } + }; + + // Proxy class for creating unlinkable iterators, so we can use + // 'for (T_Element* const ptr : list.unlinkable()) list.unlink(ptr);' + class UnlinkableProxy final { + // The List itself, but nothing else can construct UnlinkableProxy + template & (B::*)(), typename> + friend class V3List; + + ListType& m_list; // The proxied list + + UnlinkableProxy(ListType& list) + : m_list{list} {} + + // Unlinkable iterator class template. This only supports enough for range based for loops. + // If you want something fancier, use and manage the direct iterator manually. + template + class UnlinkableItertatorImpl final { + static_assert(std::is_same::value + || std::is_same::value, + "'UnlinkableItertatorImpl' must be used with element type only"); + + // The UnlinkableProxy, but nothing else can construct unlinkable iterators + friend class UnlinkableProxy; + + using IteratorType = UnlinkableItertatorImpl; + + T_Base* m_currp; // Currently iterated element, or 'nullptr' for 'end()' iterator + T_Base* m_nextp; // Next element after current, or 'nullptr' for 'end()' iterator + + VL_ATTR_ALWINLINE + UnlinkableItertatorImpl(T_Base* elementp) + : m_currp{elementp} + , m_nextp{toLinks(m_currp).m_nextp} {} + VL_ATTR_ALWINLINE + UnlinkableItertatorImpl(std::nullptr_t) + : m_currp{nullptr} + , m_nextp{nullptr} {} + + public: + // Dereference - Note this returns a pointer. + VL_ATTR_ALWINLINE + T_IteratorElement* operator*() const { + UDEBUGONLY(UASSERT(m_currp, "Dereferencing end of list iterator");); + prefetch(m_nextp, m_currp); + return static_cast(m_currp); + } + // Pre increment - Keeps hold of current next pointer. + VL_ATTR_ALWINLINE + IteratorType& operator++() { + UDEBUGONLY(UASSERT(m_currp, "Pre-incrementing end of list iterator");); + m_currp = m_nextp; + m_nextp = m_currp ? toLinks(m_currp).m_nextp : nullptr; + return *this; + } + VL_ATTR_ALWINLINE + bool operator!=(const IteratorType& other) const { return m_currp != other.m_currp; } + }; + + public: + using iterator = UnlinkableItertatorImpl; + using const_iterator = UnlinkableItertatorImpl; + iterator begin() { // + return m_list.m_headp ? iterator{m_list.m_headp} : end(); + } + const_iterator begin() const { + return m_list.m_headp ? const_iterator{m_list.m_headp} : end(); + } + iterator end() { return iterator{nullptr}; } + const_iterator end() const { return const_iterator{nullptr}; } + }; + +public: + using iterator = SimpleItertatorImpl; + using const_iterator = SimpleItertatorImpl; + using reverse_iterator = SimpleItertatorImpl; + using const_reverse_iterator = SimpleItertatorImpl; + + // CONSTRUCTOR + V3List() = default; + ~V3List() { +#ifdef VL_DEBUG + m_headp = reinterpret_cast(1); + m_lastp = reinterpret_cast(1); +#endif + } + VL_UNCOPYABLE(V3List); + VL_UNMOVABLE(V3List); + + // METHDOS + bool empty() const { return !m_headp; } + bool hasSingleElement() const { return m_headp && m_headp == m_lastp; } + bool hasMultipleElements() const { return m_headp && m_headp != m_lastp; } + + // These return pointers, as we often want to unlink/delete them, and can also signal empty + T_Element* frontp() { return static_cast(m_headp); } + const T_Element* frontp() const { return static_cast(m_headp); } + T_Element* backp() { return static_cast(m_lastp); } + const T_Element* backp() const { return static_cast(m_lastp); } + + // Standard iterators. The iterator is only invalidated if the element it points to is + // unlinked. Other list operations do not invalidate the itartor. If you want to be able to + // unlink the currently iterated element, use 'unlinkable()' below. + iterator begin() { return iterator{m_headp}; } + const_iterator begin() const { return const_iterator{m_headp}; } + iterator end() { return iterator{nullptr}; } + const_iterator end() const { return const_iterator{nullptr}; } + reverse_iterator rbegin() { return reverse_iterator{m_lastp}; } + const_reverse_iterator rbegin() const { return const_reverse_iterator{m_lastp}; } + reverse_iterator rend() { return reverse_iterator{nullptr}; } + const_reverse_iterator rend() const { return const_reverse_iterator{nullptr}; } + + // Handle to create unlinkable iterators, which allows unlinking the currently iterated + // element without invalidating the iterator. However, every other operation that mutates + // the list invalidates the unlinkable iterator! + UnlinkableProxy unlinkable() { return UnlinkableProxy{*this}; } + + // Link (insert) existing element at front + void linkFront(const T_Element* elementp) { + auto& links = toLinks(elementp); + links.m_nextp = m_headp; + links.m_prevp = nullptr; + if (m_headp) toLinks(m_headp).m_prevp = const_cast(elementp); + m_headp = const_cast(elementp); + if (!m_lastp) m_lastp = m_headp; + } + + // Link (insert) existing element at back + void linkBack(const T_Element* elementp) { + auto& links = toLinks(elementp); + links.m_nextp = nullptr; + links.m_prevp = m_lastp; + if (m_lastp) toLinks(m_lastp).m_nextp = const_cast(elementp); + m_lastp = const_cast(elementp); + if (!m_headp) m_headp = m_lastp; + } + + // Unlink (remove) and return element at the front. Returns 'nullptr' if the list is empty. + T_Element* unlinkFront() { + T_Element* const headp = m_headp; + if (headp) unlink(headp); + return headp; + } + + // Unlink (remove) and return element at the back. Returns 'nullptr' if the list is empty. + T_Element* unlinkBack() { + T_Element* const lastp = m_lastp; + if (lastp) unlink(lastp); + return lastp; + } + + // Unlink (remove) the given element. + void unlink(const T_Element* elementp) { + auto& links = toLinks(elementp); + if (links.m_nextp) toLinks(links.m_nextp).m_prevp = links.m_prevp; + if (links.m_prevp) toLinks(links.m_prevp).m_nextp = links.m_nextp; + if (m_headp == elementp) m_headp = links.m_nextp; + if (m_lastp == elementp) m_lastp = links.m_prevp; + links.m_prevp = nullptr; + links.m_nextp = nullptr; + } + + // Swap elements of 2 lists + void swap(ListType& other) { + std::swap(m_headp, other.m_headp); + std::swap(m_lastp, other.m_lastp); + } + + // Take elements from 'other' and link (insert) them all before the given position. + void splice(const_iterator pos, ListType& other) { + if (empty()) { + swap(other); + } else if (other.empty()) { + return; + } else { + UASSERT(pos == end(), "Sorry, only splicing at the end is implemented at the moment"); + toLinks(m_lastp).m_nextp = other.m_headp; + toLinks(other.m_headp).m_prevp = m_lastp; + m_lastp = other.m_lastp; + other.m_headp = nullptr; + other.m_lastp = nullptr; + } + } + + // This is O(n)! + size_t size() const { + size_t result = 0; + for (auto it = begin(); it != end(); ++it) ++result; + return result; + } +}; #endif // Guard diff --git a/src/V3OrderInternal.h b/src/V3OrderInternal.h index a35a4199e..0c5b4cfbe 100644 --- a/src/V3OrderInternal.h +++ b/src/V3OrderInternal.h @@ -52,12 +52,12 @@ void processDomains(AstNetlist* netlistp, // const TrigToSenMap& trigToSen, // const ExternalDomainsProvider& externalDomains); -std::vector createSerial(const OrderGraph& orderGraph, // +std::vector createSerial(OrderGraph& orderGraph, // const std::string& tag, // const TrigToSenMap& trigToSenMap, // bool slow); -AstExecGraph* createParallel(const OrderGraph& orderGraph, // +AstExecGraph* createParallel(OrderGraph& orderGraph, // const std::string& tag, // const TrigToSenMap& trigToSenMap, // bool slow); diff --git a/src/V3OrderMoveGraph.cpp b/src/V3OrderMoveGraph.cpp index b2727ade8..bebcd319c 100644 --- a/src/V3OrderMoveGraph.cpp +++ b/src/V3OrderMoveGraph.cpp @@ -54,7 +54,7 @@ class OrderMoveGraphBuilder final { using DomainMap = std::map; // MEMBERS - const OrderGraph& m_orderGraph; // Input OrderGraph + OrderGraph& m_orderGraph; // Input OrderGraph std::unique_ptr m_moveGraphp{new OrderMoveGraph}; // Output OrderMoveGraph // Map from Trigger reference AstSenItem to the original AstSenTree const V3Order::TrigToSenMap& m_trigToSen; @@ -62,7 +62,7 @@ class OrderMoveGraphBuilder final { std::deque m_domainMaps; // CONSTRUCTORS - OrderMoveGraphBuilder(const OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen) + OrderMoveGraphBuilder(OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen) : m_orderGraph{orderGraph} , m_trigToSen{trigToSen} { // How this works: @@ -77,18 +77,18 @@ class OrderMoveGraphBuilder final { // For each logic vertex, make a OrderMoveVertex, for each variable vertex, allocate // storage - for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (OrderLogicVertex* const lvtxp = itp->cast()) { + for (V3GraphVertex& vtx : m_orderGraph.vertices()) { + if (OrderLogicVertex* const lvtxp = vtx.cast()) { lvtxp->userp(new OrderMoveVertex{*m_moveGraphp, lvtxp, lvtxp->domainp()}); } else { // This is an OrderVarVertex m_domainMaps.emplace_back(); - itp->userp(&m_domainMaps.back()); + vtx.userp(&m_domainMaps.back()); } } // Build edges between logic vertices - for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (OrderLogicVertex* const lvtxp = itp->cast()) { + for (V3GraphVertex& vtx : m_orderGraph.vertices()) { + if (OrderLogicVertex* const lvtxp = vtx.cast()) { iterateLogicVertex(lvtxp); } } @@ -167,11 +167,11 @@ class OrderMoveGraphBuilder final { AstSenTree* const domainp = lvtxp->domainp(); OrderMoveVertex* const lMoveVtxp = static_cast(lvtxp->userp()); // Search forward from lvtxp, making new edges from lMoveVtxp forward - for (V3GraphEdge* edgep = lvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (edgep->weight() == 0) continue; // Was cut + for (const V3GraphEdge& edge : lvtxp->outEdges()) { + if (edge.weight() == 0) continue; // Was cut // OrderGraph is a bipartite graph, so we know it's an OrderVarVertex - const OrderVarVertex* const vvtxp = static_cast(edgep->top()); + const OrderVarVertex* const vvtxp = static_cast(edge.top()); // Look up OrderMoveVertex for this domain on this variable DomainMap& mapp = *static_cast(vvtxp->userp()); @@ -196,11 +196,11 @@ class OrderMoveGraphBuilder final { OrderMoveVertex* iterateVarVertex(const OrderVarVertex* vvtxp, AstSenTree* domainp) { OrderMoveVertex* vMoveVtxp = nullptr; // Search forward from vvtxp, making new edges from vMoveVtxp forward - for (V3GraphEdge* edgep = vvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (edgep->weight() == 0) continue; // Was cut + for (const V3GraphEdge& edge : vvtxp->outEdges()) { + if (edge.weight() == 0) continue; // Was cut // OrderGraph is a bipartite graph, so we know it's an OrderLogicVertex - const OrderLogicVertex* const lVtxp = edgep->top()->as(); + const OrderLogicVertex* const lVtxp = edge.top()->as(); // Do not construct dependencies across exclusive domains. if (domainsExclusive(domainp, lVtxp->domainp())) continue; @@ -214,7 +214,7 @@ class OrderMoveGraphBuilder final { } public: - static std::unique_ptr apply(const OrderGraph& orderGraph, + static std::unique_ptr apply(OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen) { return std::move(OrderMoveGraphBuilder{orderGraph, trigToSen}.m_moveGraphp); } @@ -223,7 +223,7 @@ public: //====================================================================== // OrderMoveGraph implementation -std::unique_ptr OrderMoveGraph::build(const OrderGraph& orderGraph, +std::unique_ptr OrderMoveGraph::build(OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen) { return OrderMoveGraphBuilder::apply(orderGraph, trigToSen); } diff --git a/src/V3OrderMoveGraph.h b/src/V3OrderMoveGraph.h index 3b5ff52f2..e401c229a 100644 --- a/src/V3OrderMoveGraph.h +++ b/src/V3OrderMoveGraph.h @@ -29,15 +29,71 @@ #include "V3Order.h" #include "V3OrderGraph.h" -class OrderMoveVertex; +class OrderMoveDomScope; +class OrderMoveGraph; + +//====================================================================== +// Vertex types + +class OrderMoveVertex final : public V3GraphVertex { + VL_RTTI_IMPL(OrderMoveVertex, V3GraphVertex) + + // The corresponding logic vertex, or nullptr if this MoveVertex stands for a variable vertex. + OrderLogicVertex* const m_logicp; + OrderMoveDomScope& m_domScope; // DomScope this vertex is under + V3ListLinks m_links; // List links to store instances of this class + + // METHODS + std::string dotColor() const override { return logicp() ? logicp()->dotColor() : "yellow"; } + + std::string name() const override VL_MT_STABLE { + std::string nm; + if (!logicp()) { + nm = "var"; + } else { + nm = logicp()->name() + "\\n"; + nm += "MV:"; + nm += +" d=" + cvtToHex(logicp()->domainp()); + nm += +" s=" + cvtToHex(logicp()->scopep()); + } + if (userp()) nm += "\nu=" + cvtToHex(userp()); + return nm; + } + + V3ListLinks& links() { return m_links; } + +public: + // List type to store instances of this class + using List = V3List; + + // CONSTRUCTORS + OrderMoveVertex(OrderMoveGraph& graph, OrderLogicVertex* lVtxp, + const AstSenTree* domainp) VL_MT_DISABLED; + ~OrderMoveVertex() override = default; + VL_UNCOPYABLE(OrderMoveVertex); + + OrderLogicVertex* logicp() const VL_MT_STABLE { return m_logicp; } + OrderMoveDomScope& domScope() const { return m_domScope; } +}; + +//====================================================================== +// Graph type + +// OrderMoveGraph is constructed from the fine-grained OrderGraph. +// It is a slightly coarsened representation of dependencies used to drive serialization. +class OrderMoveGraph final : public V3Graph { +public: + // Build an OrderMoveGraph from an OrderGraph + static std::unique_ptr build(OrderGraph&, const V3Order::TrigToSenMap&); +}; // Information stored for each unique (domain, scope) pair. Mainly a list of ready vertices under // that (domain, scope). OrderMoveDomScope instances are themselves organized into a global ready // list if they have ready vertices. class OrderMoveDomScope final { // STATE - V3List m_readyVertices; // Ready vertices in this domain/scope - V3ListEnt m_listEnt; // List entry to store this instance + OrderMoveVertex::List m_readyVertices; // Ready vertices in this domain/scope + V3ListLinks m_links; // List links to store instances of this class bool m_isOnList = false; // True if DomScope is already on a list through m_listEnt const AstSenTree* const m_domainp; // Domain the vertices belong to const AstScope* const m_scopep; // Scope the vertices belong to @@ -72,7 +128,12 @@ class OrderMoveDomScope final { // Map from Domain/Scope to the corresponding DomScope instance static DomScopeMap s_dsMap; + V3ListLinks& links() { return m_links; } + public: + // List type to store instances of this class + using List = V3List; + // STATIC MEMBERS static OrderMoveDomScope& getOrCreate(const AstSenTree* domainp, const AstScope* scopep) { return s_dsMap @@ -92,84 +153,12 @@ public: VL_UNMOVABLE(OrderMoveDomScope); // MEMBERS - V3List& readyVertices() { return m_readyVertices; } + OrderMoveVertex::List& readyVertices() { return m_readyVertices; } const AstSenTree* domainp() const { return m_domainp; } const AstScope* scopep() const { return m_scopep; } bool isOnList() const { return m_isOnList; } - void unlinkFrom(V3List& list) { - UASSERT_OBJ(m_isOnList, m_domainp, "unlinkFrom, but DomScope is not on a list"); - m_isOnList = false; - m_listEnt.unlink(list, this); - } - void appendTo(V3List& list) { - UASSERT_OBJ(!m_isOnList, m_domainp, "appendTo, but DomScope is already on a list"); - m_isOnList = true; - m_listEnt.pushBack(list, this); - } - void prependTo(V3List& list) { - UASSERT_OBJ(!m_isOnList, m_domainp, "prependTo, but DomScope is already on a list"); - m_isOnList = true; - m_listEnt.pushFront(list, this); - } - OrderMoveDomScope* nextp() const { return m_listEnt.nextp(); } -}; - -//====================================================================== -// Graph type - -// OrderMoveGraph is constructed from the fine-grained OrderGraph. -// It is a slightly coarsened representation of dependencies used to drive serialization. -class OrderMoveGraph final : public V3Graph { -public: - // Build an OrderMoveGraph from an OrderGraph - static std::unique_ptr build(const OrderGraph&, const V3Order::TrigToSenMap&); -}; - -//====================================================================== -// Vertex types - -class OrderMoveVertex final : public V3GraphVertex { - VL_RTTI_IMPL(OrderMoveVertex, V3GraphVertex) - - // The corresponding logic vertex, or nullptr if this MoveVertex stands for a variable vertex. - OrderLogicVertex* const m_logicp; - OrderMoveDomScope& m_domScope; // DomScope this vertex is under - V3ListEnt m_listEnt; // List entry to store this Vertex - - // METHODS - std::string dotColor() const override { return logicp() ? logicp()->dotColor() : "yellow"; } - - std::string name() const override VL_MT_STABLE { - std::string nm; - if (!logicp()) { - nm = "var"; - } else { - nm = logicp()->name() + "\\n"; - nm += "MV:"; - nm += +" d=" + cvtToHex(logicp()->domainp()); - nm += +" s=" + cvtToHex(logicp()->scopep()); - } - if (userp()) nm += "\nu=" + cvtToHex(userp()); - return nm; - } - -public: - // CONSTRUCTORS - OrderMoveVertex(OrderMoveGraph& graph, OrderLogicVertex* lVtxp, - const AstSenTree* domainp) VL_MT_DISABLED; - ~OrderMoveVertex() override = default; - VL_UNCOPYABLE(OrderMoveVertex); - - OrderLogicVertex* logicp() const VL_MT_STABLE { return m_logicp; } - OrderMoveDomScope& domScope() const { return m_domScope; } - - void unlinkFrom(V3List& list) { m_listEnt.unlink(list, this); } - void appendTo(V3List& list) { m_listEnt.pushBack(list, this); } - void moveAppend(V3List& src, V3List& dst) { - m_listEnt.moveAppend(src, dst, this); - } - OrderMoveVertex* nextp() const { return m_listEnt.nextp(); } + void isOnList(bool value) { m_isOnList = value; } }; //====================================================================== @@ -177,7 +166,7 @@ public: class OrderMoveGraphSerializer final { // STATE - V3List m_readyDomScopeps; // List of DomScopes which have ready vertices + OrderMoveDomScope::List m_readyDomScopeps; // List of DomScopes which have ready vertices OrderMoveDomScope* m_nextDomScopep = nullptr; // Next DomScope to yield from // METHODS @@ -187,16 +176,18 @@ class OrderMoveGraphSerializer final { if (vtxp->logicp()) { // Add this vertex to the ready list of its DomScope OrderMoveDomScope& domScope = vtxp->domScope(); - vtxp->appendTo(domScope.readyVertices()); + domScope.readyVertices().linkBack(vtxp); // Add the DomScope to the global ready list if not there yet - if (!domScope.isOnList()) domScope.appendTo(m_readyDomScopeps); + if (!domScope.isOnList()) { + domScope.isOnList(true); + m_readyDomScopeps.linkBack(&domScope); + } } else { // This is a bit nonsense at this point, but equivalent to the old version // Remove dependency on vertex we are returning. This might add vertices to // currReadyList. - for (V3GraphEdge *edgep = vtxp->outBeginp(), *nextp; edgep; edgep = nextp) { - nextp = edgep->outNextp(); + for (V3GraphEdge& edge : vtxp->outEdges()) { // The dependent variable - OrderMoveVertex* const dVtxp = edgep->top()->as(); + OrderMoveVertex* const dVtxp = edge.top()->as(); // Update number of dependencies const uint32_t nDeps = dVtxp->user() - 1; dVtxp->user(nDeps); @@ -210,11 +201,9 @@ public: // CONSTRUCTOR OrderMoveGraphSerializer(OrderMoveGraph& moveGraph) { // Set V3GraphVertex::user() to the number of incoming edges (upstream dependencies) - for (V3GraphVertex *vtxp = moveGraph.verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); - uint32_t nDeps = 0; - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) ++nDeps; - vtxp->user(nDeps); + for (V3GraphVertex& vtx : moveGraph.vertices()) { + const uint32_t nDeps = vtx.inEdges().size(); + vtx.user(nDeps); } } ~OrderMoveGraphSerializer() = default; @@ -224,26 +213,27 @@ public: void addSeed(OrderMoveVertex* vtxp) { ready(vtxp); } OrderMoveVertex* getNext() { - if (!m_nextDomScopep) m_nextDomScopep = m_readyDomScopeps.begin(); - OrderMoveDomScope* const currDomScopep = m_nextDomScopep; + if (!m_nextDomScopep) m_nextDomScopep = m_readyDomScopeps.frontp(); // If nothing is ready, we are done - if (!currDomScopep) return nullptr; + if (!m_nextDomScopep) return nullptr; + OrderMoveDomScope& currDomScope = *m_nextDomScopep; - V3List& currReadyList = currDomScopep->readyVertices(); - // This is the vertex we are returning now - OrderMoveVertex* const mVtxp = currReadyList.begin(); - UASSERT(mVtxp, "DomScope on ready list, but has no ready vertices"); - // Unlink vertex from ready list under the DomScope - mVtxp->unlinkFrom(currReadyList); + OrderMoveVertex::List& currReadyList = currDomScope.readyVertices(); + UASSERT(!currReadyList.empty(), "DomScope on ready list, but has no ready vertices"); + + // Remove vertex from ready list under the DomScope. This is the vertex we are returning. + OrderMoveVertex* mVtxp = currReadyList.unlinkFront(); // Nonsesne, but what we used to do - if (currReadyList.empty()) currDomScopep->unlinkFrom(m_readyDomScopeps); + if (currReadyList.empty()) { + currDomScope.isOnList(false); + m_readyDomScopeps.unlink(&currDomScope); + } // Remove dependency on vertex we are returning. This might add vertices to currReadyList. - for (V3GraphEdge *edgep = mVtxp->outBeginp(), *nextp; edgep; edgep = nextp) { - nextp = edgep->outNextp(); + for (V3GraphEdge& edge : mVtxp->outEdges()) { // The dependent variable - OrderMoveVertex* const dVtxp = edgep->top()->as(); + OrderMoveVertex* const dVtxp = edge.top()->as(); // Update number of dependencies const uint32_t nDeps = dVtxp->user() - 1; dVtxp->user(nDeps); @@ -255,9 +245,9 @@ public: // under the same domain. if (currReadyList.empty()) { m_nextDomScopep = nullptr; - for (OrderMoveDomScope* dsp = m_readyDomScopeps.begin(); dsp; dsp = dsp->nextp()) { - if (dsp->domainp() == currDomScopep->domainp()) { - m_nextDomScopep = dsp; + for (OrderMoveDomScope& domScope : m_readyDomScopeps) { + if (domScope.domainp() == currDomScope.domainp()) { + m_nextDomScopep = &domScope; break; } } diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index 3453916c9..cc614b211 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -44,6 +44,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; +class LogicMTask; class MTaskEdge; class MergeCandidate; class SiblingMC; @@ -172,220 +173,6 @@ struct EdgeKey final { using EdgeHeap = PairingHeap; -//============================================================================= -// LogicMTask - -class LogicMTask final : public V3GraphVertex { - VL_RTTI_IMPL(LogicMTask, V3GraphVertex) - template - friend class PropagateCp; - -public: - // TYPES - struct CmpLogicMTask final { - bool operator()(const LogicMTask* ap, const LogicMTask* bp) const { - return ap->id() < bp->id(); - } - }; - -private: - // MEMBERS - - // List of OrderMoveVertex's assigned to this mtask. LogicMTask does not - // own the OrderMoveVertex objects, we merely keep them in a list here. - V3List m_mVertices; - - // Cost estimate for this LogicMTask, derived from V3InstrCount. - // In abstract time units. - uint32_t m_cost = 0; - - // Cost of critical paths going FORWARD from graph-start to the start - // of this vertex, and also going REVERSE from the end of the graph to - // the end of the vertex. Same units as m_cost. - std::array m_critPathCost; - - const uint32_t m_id; // Unique LogicMTask ID number - static uint32_t s_nextId; // Next ID number to use - - // Count "generations" which are just operations that scan through the - // graph. We'll mark each node with the last generation that scanned - // it. We can use this to avoid recursing through the same node twice - // while searching for a path. - uint64_t m_generation = 0; - - // Store a set of forward relatives so we can quickly check if we have a given child - std::unordered_set m_edgeSet; - // Store the outgoing and incoming edges in a heap sorted by the critical path length - std::array m_edgeHeap; - - // MTasks for which a SiblingMC exists with 'this' as the higher ID MTask (m_ap in SiblingMC) - std::set m_siblings; - // List of SiblingMCs for which this is the higher ID MTask (m_ap in SiblingMC) - V3List m_aSiblingMCs; - // List of SiblingMCs for which this is the lower ID MTask (m_bp in SiblingMC) - V3List m_bSiblingMCs; - -public: - // CONSTRUCTORS - LogicMTask(V3Graph* graphp, OrderMoveVertex* mVtxp) - : V3GraphVertex{graphp} - , m_id{s_nextId++} { - UASSERT(s_nextId < 0xFFFFFFFFUL, "Too many mTaskGraphp"); - for (uint32_t& item : m_critPathCost) item = 0; - if (mVtxp) { - mVtxp->appendTo(m_mVertices); - if (const OrderLogicVertex* const olvp = mVtxp->logicp()) { - m_cost += V3InstrCount::count(olvp->nodep(), true); - } - } - } - - // METHODS - std::set& siblings() { return m_siblings; }; - V3List& aSiblingMCs() { return m_aSiblingMCs; }; - V3List& bSiblingMCs() { return m_bSiblingMCs; }; - - V3List& vertexList() { return m_mVertices; } - const V3List& vertexList() const { return m_mVertices; } - void moveAllVerticesFrom(LogicMTask* otherp) { - otherp->m_mVertices.begin()->moveAppend(otherp->m_mVertices, m_mVertices); - m_cost += otherp->m_cost; - } - static uint64_t incGeneration() { - static uint64_t s_generation = 0; - ++s_generation; - return s_generation; - } - - // Use this instead of pointer-compares to compare LogicMTasks. Avoids - // nondeterministic output. Also name mTaskGraphp based on this number in - // the final C++ output. - uint32_t id() const { return m_id; } - // Abstract cost of every logic mtask - uint32_t cost() const VL_MT_SAFE { return m_cost; } - void setCost(uint32_t cost) { m_cost = cost; } // For tests only - uint32_t stepCost() const { return stepCost(m_cost); } - static uint32_t stepCost(uint32_t cost) { -#if PART_STEPPED_COST - // Round cost up to the nearest 5%. Use this when computing all - // critical paths. The idea is that critical path changes don't - // need to propagate when they don't exceed the next step, saving a - // lot of recursion. - if (cost == 0) return 0; - - double logcost = log(cost); - // log(1.05) is about 0.05 - // So, round logcost up to the next 0.05 boundary - logcost *= 20.0; - logcost = ceil(logcost); - logcost = logcost / 20.0; - - const uint32_t stepCost = static_cast(exp(logcost)); - UDEBUGONLY(UASSERT_STATIC(stepCost >= cost, "stepped cost error exceeded");); - UDEBUGONLY(UASSERT_STATIC(stepCost <= ((cost * 11 / 10)), "stepped cost error exceeded");); - return stepCost; -#else - return cost; -#endif - } - - template - void addRelativeEdge(MTaskEdge* edgep); - template - void stealRelativeEdge(MTaskEdge* edgep); - template - void removeRelativeEdge(MTaskEdge* edgep); - - void addRelativeMTask(LogicMTask* relativep) { - // Add the relative to connecting edge map - const bool exits = !m_edgeSet.emplace(relativep).second; - UDEBUGONLY(UASSERT(!exits, "Adding existing relative");); - } - void removeRelativeMTask(LogicMTask* relativep) { - const size_t removed = m_edgeSet.erase(relativep); - UDEBUGONLY(UASSERT(removed, "Relative should have been in set");); - } - bool hasRelativeMTask(LogicMTask* relativep) const { return m_edgeSet.count(relativep); } - - void checkRelativesCp(GraphWay way) const; - - string name() const override VL_MT_STABLE { - // Display forward and reverse critical path costs. This gives a quick - // read on whether graph partitioning looks reasonable or bad. - std::ostringstream out; - out << "mt" << m_id << "." << this << " [b" << m_critPathCost[GraphWay::FORWARD] << " a" - << m_critPathCost[GraphWay::REVERSE] << " c" << cost(); - return out.str(); - } - - void setCritPathCost(GraphWay way, uint32_t cost) { m_critPathCost[way] = cost; } - uint32_t critPathCost(GraphWay way) const { return m_critPathCost[way]; } - uint32_t critPathCostWithout(GraphWay way, const V3GraphEdge* withoutp) const; - -private: - static bool pathExistsFromInternal(LogicMTask* fromp, LogicMTask* top, - const V3GraphEdge* excludedEdgep, uint64_t generation) { - // Q) Why does this take LogicMTask instead of generic V3GraphVertex? - // A) We'll use the critical paths known to LogicMTask to prune the - // recursion for speed. Also store 'generation' in - // LogicMTask::m_generation so we can prune the search and avoid - // recursing through the same node more than once in a single - // search. - - if (fromp->m_generation == generation) { - // Already looked at this node in the current search. - // Since we're back again, we must not have found a path on the - // first go. - return false; - } - fromp->m_generation = generation; - - // Base case: we found a path. - if (fromp == top) return true; - - // Base case: fromp is too late, cannot possibly be a prereq for top. - if (fromp->critPathCost(GraphWay::REVERSE) - < (top->critPathCost(GraphWay::REVERSE) + top->stepCost())) { - return false; - } - if ((fromp->critPathCost(GraphWay::FORWARD) + fromp->stepCost()) - > top->critPathCost(GraphWay::FORWARD)) { - return false; - } - - // Recursively look for a path - for (const V3GraphEdge* followp = fromp->outBeginp(); followp; - followp = followp->outNextp()) { - if (followp == excludedEdgep) continue; - LogicMTask* const nextp = static_cast(followp->top()); - if (pathExistsFromInternal(nextp, top, nullptr, generation)) return true; - } - return false; - } - - // True if there's a path from 'fromp' to 'top' excluding - // 'excludedEdgep', false otherwise. - // - // 'excludedEdgep' may be nullptr in which case no edge is excluded. If - // 'excludedEdgep' is non-nullptr it must connect fromp and top. - // - // TODO: consider changing this API to the 'isTransitiveEdge' API - // used by GraphPathChecker -public: - static bool pathExistsFrom(LogicMTask* fromp, LogicMTask* top, - const V3GraphEdge* excludedEdgep) { - return pathExistsFromInternal(fromp, top, excludedEdgep, incGeneration()); - } - - static void dumpCpFilePrefixed(const V3Graph& graph, const string& nameComment); - -private: - VL_UNCOPYABLE(LogicMTask); -}; - -// Start at 1, so that 0 indicates no mtask. -uint32_t LogicMTask::s_nextId = 1; - //###################################################################### // MTask utility classes @@ -451,40 +238,28 @@ class SiblingMC final : public MergeCandidate { LogicMTask* const m_ap; LogicMTask* const m_bp; - V3ListEnt m_aEnt; // List entry for m_ap->aSiblingMCs() - V3ListEnt m_bEnt; // List entry for m_bp->bSiblingMCs() + V3ListLinks m_aLinks; // List links to store instances of this class + V3ListLinks m_bLinks; // List links to store instances of this class + + V3ListLinks& aLinks() { return m_aLinks; } + V3ListLinks& bLinks() { return m_bLinks; } public: + // List type to store instances of this class + using AList = V3List; + using BList = V3List; + // CONSTRUCTORS - SiblingMC() = delete; - SiblingMC(LogicMTask* ap, LogicMTask* bp) - : MergeCandidate{/* isSiblingMC: */ true} - , m_ap{ap} - , m_bp{bp} { - // Storage management depends on this - UASSERT(ap->id() > bp->id(), "Should be ordered"); - UDEBUGONLY(UASSERT(ap->siblings().count(bp), "Should be in sibling map");); - m_aEnt.pushBack(m_ap->aSiblingMCs(), this); - m_bEnt.pushBack(m_bp->bSiblingMCs(), this); - } + SiblingMC(LogicMTask* ap, LogicMTask* bp); ~SiblingMC() = default; // METHODS - SiblingMC* aNextp() const { return m_aEnt.nextp(); } - SiblingMC* bNextp() const { return m_bEnt.nextp(); } - void unlinkA() { - VL_ATTR_UNUSED const size_t removed = m_ap->siblings().erase(m_bp); - UDEBUGONLY(UASSERT(removed == 1, "Should have been in sibling set");); - m_aEnt.unlink(m_ap->aSiblingMCs(), this); - } - void unlinkB() { m_bEnt.unlink(m_bp->bSiblingMCs(), this); } + void unlinkA(); + void unlinkB(); LogicMTask* ap() const { return m_ap; } LogicMTask* bp() const { return m_bp; } - bool mergeWouldCreateCycle() const { - return (LogicMTask::pathExistsFrom(m_ap, m_bp, nullptr) - || LogicMTask::pathExistsFrom(m_bp, m_ap, nullptr)); - } + bool mergeWouldCreateCycle() const; }; static_assert(!std::is_polymorphic::value, "Should not have a vtable"); @@ -503,33 +278,17 @@ class MTaskEdge final : public V3GraphEdge, public MergeCandidate { public: // CONSTRUCTORS - MTaskEdge(V3Graph* graphp, LogicMTask* fromp, LogicMTask* top, int weight) - : V3GraphEdge{graphp, fromp, top, weight} - , MergeCandidate{/* isSiblingMC: */ false} { - fromp->addRelativeMTask(top); - fromp->addRelativeEdge(this); - top->addRelativeEdge(this); - } + MTaskEdge(V3Graph* graphp, LogicMTask* fromp, LogicMTask* top, int weight); // METHODS - LogicMTask* furtherMTaskp(GraphWay way) const { - return static_cast(this->furtherp(way)); - } - LogicMTask* fromMTaskp() const { return static_cast(fromp()); } - LogicMTask* toMTaskp() const { return static_cast(top()); } - bool mergeWouldCreateCycle() const { - return LogicMTask::pathExistsFrom(fromMTaskp(), toMTaskp(), this); - } + template + inline LogicMTask* furtherMTaskp() const; + inline LogicMTask* fromMTaskp() const; + inline LogicMTask* toMTaskp() const; + bool mergeWouldCreateCycle() const; // Following initial assignment of critical paths, clear this MTaskEdge // out of the edge-map for each node and reinsert at a new location // with updated critical path. - void resetCriticalPaths() { - LogicMTask* const fromp = fromMTaskp(); - LogicMTask* const top = toMTaskp(); - fromp->removeRelativeEdge(this); - top->removeRelativeEdge(this); - fromp->addRelativeEdge(this); - top->addRelativeEdge(this); - } + void resetCriticalPaths(); uint32_t cachedCp(GraphWay way) const { return m_edgeHeapNode[way].key().m_score; } @@ -543,110 +302,311 @@ private: VL_UNCOPYABLE(MTaskEdge); }; -template -void LogicMTask::addRelativeEdge(MTaskEdge* edgep) { - constexpr GraphWay way{T_Way}; - constexpr GraphWay inv = way.invert(); - // Add to the edge heap - LogicMTask* const relativep = edgep->furtherMTaskp(way); - // Value is !way cp to this edge - const uint32_t cp = relativep->stepCost() + relativep->critPathCost(inv); +//============================================================================= +// LogicMTask + +class LogicMTask final : public V3GraphVertex { + VL_RTTI_IMPL(LogicMTask, V3GraphVertex) + template + friend class PropagateCp; + +public: + // TYPES + struct CmpLogicMTask final { + bool operator()(const LogicMTask* ap, const LogicMTask* bp) const { + return ap->id() < bp->id(); + } + }; + +private: + // MEMBERS + + // List of OrderMoveVertex's assigned to this mtask. LogicMTask does not + // own the OrderMoveVertex objects, we merely keep them in a list here. + OrderMoveVertex::List m_mVertices; + + // Cost estimate for this LogicMTask, derived from V3InstrCount. + // In abstract time units. + uint32_t m_cost = 0; + + // Cost of critical paths going FORWARD from graph-start to the start + // of this vertex, and also going REVERSE from the end of the graph to + // the end of the vertex. Same units as m_cost. + std::array m_critPathCost; + + const uint32_t m_id; // Unique LogicMTask ID number + static uint32_t s_nextId; // Next ID number to use + + // Count "generations" which are just operations that scan through the + // graph. We'll mark each node with the last generation that scanned + // it. We can use this to avoid recursing through the same node twice + // while searching for a path. + uint64_t m_generation = 0; + + // Store a set of forward relatives so we can quickly check if we have a given child + std::unordered_set m_edgeSet; + // Store the outgoing and incoming edges in a heap sorted by the critical path length + std::array m_edgeHeap; + + // MTasks for which a SiblingMC exists with 'this' as the higher ID MTask (m_ap in SiblingMC) + std::set m_siblings; + // List of SiblingMCs for which this is the higher ID MTask (m_ap in SiblingMC) + SiblingMC::AList m_aSiblingMCs; + // List of SiblingMCs for which this is the lower ID MTask (m_bp in SiblingMC) + SiblingMC::BList m_bSiblingMCs; + +public: + // CONSTRUCTORS + LogicMTask(V3Graph* graphp, OrderMoveVertex* mVtxp) + : V3GraphVertex{graphp} + , m_id{s_nextId++} { + UASSERT(s_nextId < 0xFFFFFFFFUL, "Too many mTaskGraphp"); + for (uint32_t& item : m_critPathCost) item = 0; + if (mVtxp) { + m_mVertices.linkBack(mVtxp); + if (const OrderLogicVertex* const olvp = mVtxp->logicp()) { + m_cost += V3InstrCount::count(olvp->nodep(), true); + } + } + } + + // METHODS + std::set& siblings() { return m_siblings; }; + SiblingMC::AList& aSiblingMCs() { return m_aSiblingMCs; }; + SiblingMC::BList& bSiblingMCs() { return m_bSiblingMCs; }; + + OrderMoveVertex::List& vertexList() { return m_mVertices; } + const OrderMoveVertex::List& vertexList() const { return m_mVertices; } + void moveAllVerticesFrom(LogicMTask* otherp) { + m_mVertices.splice(m_mVertices.end(), otherp->vertexList()); + m_cost += otherp->m_cost; + } + static uint64_t incGeneration() { + static uint64_t s_generation = 0; + ++s_generation; + return s_generation; + } + + // Use this instead of pointer-compares to compare LogicMTasks. Avoids + // nondeterministic output. Also name mTaskGraphp based on this number in + // the final C++ output. + uint32_t id() const { return m_id; } + // Abstract cost of every logic mtask + uint32_t cost() const VL_MT_SAFE { return m_cost; } + void setCost(uint32_t cost) { m_cost = cost; } // For tests only + uint32_t stepCost() const { return stepCost(m_cost); } + static uint32_t stepCost(uint32_t cost) { +#if PART_STEPPED_COST + // Round cost up to the nearest 5%. Use this when computing all + // critical paths. The idea is that critical path changes don't + // need to propagate when they don't exceed the next step, saving a + // lot of recursion. + if (cost == 0) return 0; + + double logcost = log(cost); + // log(1.05) is about 0.05 + // So, round logcost up to the next 0.05 boundary + logcost *= 20.0; + logcost = ceil(logcost); + logcost = logcost / 20.0; + + const uint32_t stepCost = static_cast(exp(logcost)); + UDEBUGONLY(UASSERT_STATIC(stepCost >= cost, "stepped cost error exceeded");); + UDEBUGONLY(UASSERT_STATIC(stepCost <= ((cost * 11 / 10)), "stepped cost error exceeded");); + return stepCost; +#else + return cost; +#endif + } + + template + void addRelativeEdge(MTaskEdge* edgep) { + constexpr GraphWay way{T_Way}; + constexpr GraphWay inv = way.invert(); + // Add to the edge heap + LogicMTask* const relativep = edgep->furtherMTaskp(); + // Value is !way cp to this edge + const uint32_t cp = relativep->stepCost() + relativep->critPathCost(inv); + // + m_edgeHeap[way].insert(&edgep->m_edgeHeapNode[way], {relativep->id(), cp}); + } + template + void stealRelativeEdge(MTaskEdge* edgep) { + constexpr GraphWay way{T_Way}; + // Make heap node insertable, ruining the heap it is currently in. + edgep->m_edgeHeapNode[way].yank(); + // Add the edge as new + addRelativeEdge(edgep); + } + template + void removeRelativeEdge(MTaskEdge* edgep) { + constexpr GraphWay way{T_Way}; + // Remove from the edge heap + m_edgeHeap[way].remove(&edgep->m_edgeHeapNode[way]); + } + + void addRelativeMTask(LogicMTask* relativep) { + // Add the relative to connecting edge map + const bool exits = !m_edgeSet.emplace(relativep).second; + UDEBUGONLY(UASSERT(!exits, "Adding existing relative");); + } + void removeRelativeMTask(LogicMTask* relativep) { + const size_t removed = m_edgeSet.erase(relativep); + UDEBUGONLY(UASSERT(removed, "Relative should have been in set");); + } + bool hasRelativeMTask(LogicMTask* relativep) const { return m_edgeSet.count(relativep); } + + template + void checkRelativesCp() const { + constexpr GraphWay way{T_Way}; + for (const V3GraphEdge& edge : edges()) { + const LogicMTask* const relativep + = static_cast(edge.furtherp()); + const uint32_t cachedCp = static_cast(edge).cachedCp(way); + const uint32_t cp = relativep->critPathCost(way.invert()) + relativep->stepCost(); + partCheckCachedScoreVsActual(cachedCp, cp); + } + } + + string name() const override VL_MT_STABLE { + // Display forward and reverse critical path costs. This gives a quick + // read on whether graph partitioning looks reasonable or bad. + std::ostringstream out; + out << "mt" << m_id << "." << this << " [b" << m_critPathCost[GraphWay::FORWARD] << " a" + << m_critPathCost[GraphWay::REVERSE] << " c" << cost(); + return out.str(); + } + + void setCritPathCost(GraphWay way, uint32_t cost) { m_critPathCost[way] = cost; } + uint32_t critPathCost(GraphWay way) const { return m_critPathCost[way]; } + template + uint32_t critPathCostWithout(const V3GraphEdge* withoutp) const { + const GraphWay way{T_Way}; + const GraphWay inv = way.invert(); + // Compute the critical path cost wayward to this node, without considering edge + // 'withoutp'. We need to look at two edges at most, the critical path if that is not via + // 'withoutp', or the second-worst path, if the critical path is via 'withoutp'. + UDEBUGONLY(UASSERT(withoutp->furtherp() == this, + "In critPathCostWithout(), edge 'withoutp' must further to 'this'");); + const EdgeHeap& edgeHeap = m_edgeHeap[inv]; + const EdgeHeap::Node* const maxp = edgeHeap.max(); + if (!maxp) return 0; + if (MTaskEdge::toMTaskEdge(inv, maxp) != withoutp) return maxp->key().m_score; + const EdgeHeap::Node* const secp = edgeHeap.secondMax(); + if (!secp) return 0; + return secp->key().m_score; + } + +private: + static bool pathExistsFromInternal(LogicMTask* fromp, LogicMTask* top, + const V3GraphEdge* excludedEdgep, uint64_t generation) { + // Q) Why does this take LogicMTask instead of generic V3GraphVertex? + // A) We'll use the critical paths known to LogicMTask to prune the + // recursion for speed. Also store 'generation' in + // LogicMTask::m_generation so we can prune the search and avoid + // recursing through the same node more than once in a single + // search. + + if (fromp->m_generation == generation) { + // Already looked at this node in the current search. + // Since we're back again, we must not have found a path on the + // first go. + return false; + } + fromp->m_generation = generation; + + // Base case: we found a path. + if (fromp == top) return true; + + // Base case: fromp is too late, cannot possibly be a prereq for top. + if (fromp->critPathCost(GraphWay::REVERSE) + < (top->critPathCost(GraphWay::REVERSE) + top->stepCost())) { + return false; + } + if ((fromp->critPathCost(GraphWay::FORWARD) + fromp->stepCost()) + > top->critPathCost(GraphWay::FORWARD)) { + return false; + } + + // Recursively look for a path + for (const V3GraphEdge& follow : fromp->outEdges()) { + if (&follow == excludedEdgep) continue; + LogicMTask* const nextp = static_cast(follow.top()); + if (pathExistsFromInternal(nextp, top, nullptr, generation)) return true; + } + return false; + } + + // True if there's a path from 'fromp' to 'top' excluding + // 'excludedEdgep', false otherwise. // - m_edgeHeap[way].insert(&edgep->m_edgeHeapNode[way], {relativep->id(), cp}); -} - -template -void LogicMTask::stealRelativeEdge(MTaskEdge* edgep) { - constexpr GraphWay way{T_Way}; - // Make heap node insertable, ruining the heap it is currently in. - edgep->m_edgeHeapNode[way].yank(); - // Add the edge as new - addRelativeEdge(edgep); -} - -template -void LogicMTask::removeRelativeEdge(MTaskEdge* edgep) { - constexpr GraphWay way{T_Way}; - // Remove from the edge heap - m_edgeHeap[way].remove(&edgep->m_edgeHeapNode[way]); -} - -void LogicMTask::checkRelativesCp(GraphWay way) const { - for (V3GraphEdge* edgep = beginp(way); edgep; edgep = edgep->nextp(way)) { - const LogicMTask* const relativep = static_cast(edgep->furtherp(way)); - const uint32_t cachedCp = static_cast(edgep)->cachedCp(way); - const uint32_t cp = relativep->critPathCost(way.invert()) + relativep->stepCost(); - partCheckCachedScoreVsActual(cachedCp, cp); + // 'excludedEdgep' may be nullptr in which case no edge is excluded. If + // 'excludedEdgep' is non-nullptr it must connect fromp and top. + // + // TODO: consider changing this API to the 'isTransitiveEdge' API + // used by GraphPathChecker +public: + static bool pathExistsFrom(LogicMTask* fromp, LogicMTask* top, + const V3GraphEdge* excludedEdgep) { + return pathExistsFromInternal(fromp, top, excludedEdgep, incGeneration()); } -} -uint32_t LogicMTask::critPathCostWithout(GraphWay way, const V3GraphEdge* withoutp) const { - // Compute the critical path cost wayward to this node, without considering edge 'withoutp'. - // We need to look at two edges at most, the critical path if that is not via 'withoutp', - // or the second-worst path, if the critical path is via 'withoutp'. - UDEBUGONLY(UASSERT(withoutp->furtherp(way) == this, - "In critPathCostWithout(), edge 'withoutp' must further to 'this'");); - const GraphWay inv = way.invert(); - const EdgeHeap& edgeHeap = m_edgeHeap[inv]; - const EdgeHeap::Node* const maxp = edgeHeap.max(); - if (!maxp) return 0; - if (MTaskEdge::toMTaskEdge(inv, maxp) != withoutp) return maxp->key().m_score; - const EdgeHeap::Node* const secp = edgeHeap.secondMax(); - if (!secp) return 0; - return secp->key().m_score; -} + static void dumpCpFilePrefixed(const V3Graph& graph, const string& nameComment) { + const string filename = v3Global.debugFilename(nameComment) + ".txt"; + UINFO(1, "Writing " << filename << endl); + const std::unique_ptr ofp{V3File::new_ofstream(filename)}; + std::ostream* const osp = &(*ofp); // &* needed to deref unique_ptr + if (osp->fail()) v3fatalStatic("Can't write " << filename); -void LogicMTask::dumpCpFilePrefixed(const V3Graph& graph, const string& nameComment) { - const string filename = v3Global.debugFilename(nameComment) + ".txt"; - UINFO(1, "Writing " << filename << endl); - const std::unique_ptr ofp{V3File::new_ofstream(filename)}; - std::ostream* const osp = &(*ofp); // &* needed to deref unique_ptr - if (osp->fail()) v3fatalStatic("Can't write " << filename); - - // Find start vertex with longest CP - LogicMTask* startp = nullptr; - for (V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - LogicMTask* const mtaskp = static_cast(vxp); - if (!startp) { - startp = mtaskp; - continue; + // Find start vertex with longest CP + const LogicMTask* startp = nullptr; + for (const V3GraphVertex& vtx : graph.vertices()) { + const LogicMTask& mtask = static_cast(vtx); + if (!startp) { + startp = &mtask; + continue; + } + if (mtask.cost() + mtask.critPathCost(GraphWay::REVERSE) + > startp->cost() + startp->critPathCost(GraphWay::REVERSE)) { + startp = &mtask; + } } - if (mtaskp->cost() + mtaskp->critPathCost(GraphWay::REVERSE) - > startp->cost() + startp->critPathCost(GraphWay::REVERSE)) { - startp = mtaskp; + + // Follow the entire critical path + std::vector path; + uint32_t totalCost = 0; + for (const LogicMTask* nextp = startp; nextp;) { + path.push_back(nextp); + totalCost += nextp->cost(); + + if (EdgeHeap::Node* const maxp = nextp->m_edgeHeap[GraphWay::FORWARD].max()) { + nextp = MTaskEdge::toMTaskEdge(GraphWay::FORWARD, maxp)->toMTaskp(); + } else { + nextp = nullptr; + } + } + + *osp << "totalCost = " << totalCost + << " (should match the computed critical path cost (CP) for the graph)\n"; + + // Dump + for (const LogicMTask* mtaskp : path) { + *osp << "begin mtask with cost " << mtaskp->cost() << '\n'; + for (const OrderMoveVertex& mVtx : mtaskp->vertexList()) { + const OrderLogicVertex* const logicp = mVtx.logicp(); + if (!logicp) continue; + // Show nodes with hierarchical costs + V3InstrCount::count(logicp->nodep(), false, osp); + } } } - // Follow the entire critical path - std::vector path; - uint32_t totalCost = 0; - for (LogicMTask* nextp = startp; nextp;) { - path.push_back(nextp); - totalCost += nextp->cost(); +private: + VL_UNCOPYABLE(LogicMTask); +}; - if (EdgeHeap::Node* const maxp = nextp->m_edgeHeap[GraphWay::FORWARD].max()) { - nextp = MTaskEdge::toMTaskEdge(GraphWay::FORWARD, maxp)->toMTaskp(); - } else { - nextp = nullptr; - } - } - - *osp << "totalCost = " << totalCost - << " (should match the computed critical path cost (CP) for the graph)\n"; - - // Dump - for (const LogicMTask* mtaskp : path) { - *osp << "begin mtask with cost " << mtaskp->cost() << '\n'; - const V3List& vertexList = mtaskp->vertexList(); - for (OrderMoveVertex *mVtxp = vertexList.begin(), *nextp; mVtxp; mVtxp = nextp) { - nextp = mVtxp->nextp(); - const OrderLogicVertex* const logicp = mVtxp->logicp(); - if (!logicp) continue; - // Show nodes with hierarchical costs - V3InstrCount::count(logicp->nodep(), false, osp); - } - } -} +// Start at 1, so that 0 indicates no mtask. +uint32_t LogicMTask::s_nextId = 1; // Instead of dynamic cast SiblingMC* MergeCandidate::toSiblingMC() { @@ -681,8 +641,8 @@ static uint32_t edgeScore(const MTaskEdge* edgep) { const LogicMTask* const top = edgep->toMTaskp(); const LogicMTask* const fromp = edgep->fromMTaskp(); const uint32_t mergedCpCostFwd = std::max(fromp->critPathCost(GraphWay::FORWARD), - top->critPathCostWithout(GraphWay::FORWARD, edgep)); - const uint32_t mergedCpCostRev = std::max(fromp->critPathCostWithout(GraphWay::REVERSE, edgep), + top->critPathCostWithout(edgep)); + const uint32_t mergedCpCostRev = std::max(fromp->critPathCostWithout(edgep), top->critPathCost(GraphWay::REVERSE)); return mergedCpCostRev + mergedCpCostFwd + LogicMTask::stepCost(fromp->cost() + top->cost()); } @@ -698,13 +658,69 @@ void MergeCandidate::rescore() { } } +SiblingMC::SiblingMC(LogicMTask* ap, LogicMTask* bp) + : MergeCandidate{/* isSiblingMC: */ true} + , m_ap{ap} + , m_bp{bp} { + // Storage management depends on this + UASSERT(ap->id() > bp->id(), "Should be ordered"); + UDEBUGONLY(UASSERT(ap->siblings().count(bp), "Should be in sibling map");); + m_ap->aSiblingMCs().linkBack(this); + m_bp->bSiblingMCs().linkBack(this); +} + +void SiblingMC::unlinkA() { + VL_ATTR_UNUSED const size_t removed = m_ap->siblings().erase(m_bp); + UDEBUGONLY(UASSERT(removed == 1, "Should have been in sibling set");); + m_ap->aSiblingMCs().unlink(this); +} + +void SiblingMC::unlinkB() { m_bp->bSiblingMCs().unlink(this); } + +bool SiblingMC::mergeWouldCreateCycle() const { + return (LogicMTask::pathExistsFrom(m_ap, m_bp, nullptr) + || LogicMTask::pathExistsFrom(m_bp, m_ap, nullptr)); +} + +MTaskEdge::MTaskEdge(V3Graph* graphp, LogicMTask* fromp, LogicMTask* top, int weight) + : V3GraphEdge{graphp, fromp, top, weight} + , MergeCandidate{/* isSiblingMC: */ false} { + fromp->addRelativeMTask(top); + fromp->addRelativeEdge(this); + top->addRelativeEdge(this); +} + +template +LogicMTask* MTaskEdge::furtherMTaskp() const { + return static_cast(this->furtherp()); +} +LogicMTask* MTaskEdge::fromMTaskp() const { return static_cast(fromp()); } +LogicMTask* MTaskEdge::toMTaskp() const { return static_cast(top()); } + +bool MTaskEdge::mergeWouldCreateCycle() const { + return LogicMTask::pathExistsFrom(fromMTaskp(), toMTaskp(), this); +} +// Following initial assignment of critical paths, clear this MTaskEdge +// out of the edge-map for each node and reinsert at a new location +// with updated critical path. +void MTaskEdge::resetCriticalPaths() { + LogicMTask* const fromp = fromMTaskp(); + LogicMTask* const top = toMTaskp(); + fromp->removeRelativeEdge(this); + top->removeRelativeEdge(this); + fromp->addRelativeEdge(this); + top->addRelativeEdge(this); +} + //###################################################################### // Look at vertex costs (in one way) to form critical paths for each // vertex. -static void partInitHalfCriticalPaths(GraphWay way, V3Graph& mTaskGraph, bool checkOnly) { +template +static void partInitHalfCriticalPaths(V3Graph& mTaskGraph, bool checkOnly) { + constexpr GraphWay way{T_Way}; + constexpr GraphWay rev = way.invert(); GraphStreamUnordered order{&mTaskGraph, way}; - const GraphWay rev = way.invert(); for (const V3GraphVertex* vertexp; (vertexp = order.nextp());) { const LogicMTask* const mtaskcp = static_cast(vertexp); LogicMTask* const mtaskp = const_cast(mtaskcp); @@ -712,17 +728,16 @@ static void partInitHalfCriticalPaths(GraphWay way, V3Graph& mTaskGraph, bool ch #if VL_DEBUG std::unordered_set relatives; #endif - for (V3GraphEdge* edgep = vertexp->beginp(rev); edgep; edgep = edgep->nextp(rev)) { + for (const V3GraphEdge& edge : vertexp->edges()) { #if VL_DEBUG // Run a few asserts on the initial mtask graph, // while we're iterating through... - UASSERT_OBJ(edgep->weight() != 0, mtaskp, - "Should be no cut edges in mTaskGraphp graph"); - UASSERT_OBJ(relatives.find(edgep->furtherp(rev)) == relatives.end(), mtaskp, + UASSERT_OBJ(edge.weight() != 0, mtaskp, "Should be no cut edges in mTaskGraphp graph"); + UASSERT_OBJ(relatives.find(edge.furtherp()) == relatives.end(), mtaskp, "Should be no redundant edges in mTaskGraphp graph"); - relatives.insert(edgep->furtherp(rev)); + relatives.insert(edge.furtherp()); #endif - const LogicMTask* const relativep = static_cast(edgep->furtherp(rev)); + const LogicMTask* const relativep = static_cast(edge.furtherp()); cpCost = std::max(cpCost, (relativep->critPathCost(way) + static_cast(relativep->stepCost()))); } @@ -736,28 +751,25 @@ static void partInitHalfCriticalPaths(GraphWay way, V3Graph& mTaskGraph, bool ch // Look at vertex costs to form critical paths for each vertex. static void partInitCriticalPaths(V3Graph& mTaskGraph) { - partInitHalfCriticalPaths(GraphWay::FORWARD, mTaskGraph, false); - partInitHalfCriticalPaths(GraphWay::REVERSE, mTaskGraph, false); + partInitHalfCriticalPaths(mTaskGraph, false); + partInitHalfCriticalPaths(mTaskGraph, false); // Reset all MTaskEdges so that 'm_edges' will show correct CP numbers. // They would have been all zeroes on initial creation of the MTaskEdges. - for (V3GraphVertex* vxp = mTaskGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - MTaskEdge* const mtedgep = edgep->as(); - mtedgep->resetCriticalPaths(); - } + for (V3GraphVertex& vtx : mTaskGraph.vertices()) { + for (V3GraphEdge& edge : vtx.outEdges()) edge.as()->resetCriticalPaths(); } } // Do an EXPENSIVE check to make sure that all incremental CP updates have // gone correctly. static void partCheckCriticalPaths(V3Graph& mTaskGraph) { - partInitHalfCriticalPaths(GraphWay::FORWARD, mTaskGraph, true); - partInitHalfCriticalPaths(GraphWay::REVERSE, mTaskGraph, true); - for (V3GraphVertex* vxp = mTaskGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - const LogicMTask* const mtaskp = static_cast(vxp); - mtaskp->checkRelativesCp(GraphWay::FORWARD); - mtaskp->checkRelativesCp(GraphWay::REVERSE); + partInitHalfCriticalPaths(mTaskGraph, true); + partInitHalfCriticalPaths(mTaskGraph, true); + for (const V3GraphVertex& vtx : mTaskGraph.vertices()) { + const LogicMTask& mtask = static_cast(vtx); + mtask.checkRelativesCp(); + mtask.checkRelativesCp(); } } @@ -856,13 +868,11 @@ public: // For *vxp, whose CP-inclusive has just increased to // newInclusiveCp, iterate to all wayward nodes, update the edges // of each, and add each to m_pending if its overall CP has grown. - for (MTaskEdge *edgep = static_cast(vxp->beginp(way)), *nextp; edgep; - edgep = nextp) { - // Fetch early as likely cache miss - nextp = static_cast(edgep->nextp(way)); + for (V3GraphEdge& graphEdge : vxp->edges()) { + MTaskEdge& edge = static_cast(graphEdge); - LogicMTask* const relativep = edgep->furtherMTaskp(way); - EdgeHeap::Node& edgeHeapNode = edgep->m_edgeHeapNode[inv]; + LogicMTask* const relativep = edge.furtherMTaskp(); + EdgeHeap::Node& edgeHeapNode = edge.m_edgeHeapNode[inv]; if (newInclusiveCp > edgeHeapNode.key().m_score) { relativep->m_edgeHeap[inv].increaseKey(&edgeHeapNode, newInclusiveCp); } @@ -977,7 +987,7 @@ public: // Seed the propagator with every input node; // This should result in the complete graph getting all CP's assigned. for (const auto& i : vx) { - if (!i->inBeginp()) prop.cpHasIncreased(i, 1 /* inclusive CP starts at 1 */); + if (i->inEmpty()) prop.cpHasIncreased(i, 1 /* inclusive CP starts at 1 */); } // Run the propagator. @@ -1024,11 +1034,8 @@ static void partRedirectEdgesFrom(V3Graph& graph, LogicMTask* recipientp, LogicM // non-transitive edges only ever increase. // Process outgoing edges - MTaskEdge* outNextp = static_cast(donorp->outBeginp()); - while (outNextp) { - MTaskEdge* const edgep = outNextp; - LogicMTask* const relativep = outNextp->toMTaskp(); - outNextp = static_cast(outNextp->outNextp()); + while (MTaskEdge* const edgep = static_cast(donorp->outEdges().frontp())) { + LogicMTask* const relativep = edgep->toMTaskp(); relativep->removeRelativeEdge(edgep); @@ -1038,7 +1045,7 @@ static void partRedirectEdgesFrom(V3Graph& graph, LogicMTask* recipientp, LogicM if (sbp) { if (sbp->contains(edgep)) sbp->remove(edgep); MTaskEdge* const existMTaskEdgep = static_cast( - recipientp->findConnectingEdgep(GraphWay::FORWARD, relativep)); + recipientp->findConnectingEdgep(relativep)); UDEBUGONLY(UASSERT(existMTaskEdgep, "findConnectingEdge didn't find edge");); if (sbp->contains(existMTaskEdgep)) sbp->hintScoreChanged(existMTaskEdgep); } @@ -1061,11 +1068,8 @@ static void partRedirectEdgesFrom(V3Graph& graph, LogicMTask* recipientp, LogicM } // Process incoming edges - MTaskEdge* inNextp = static_cast(donorp->inBeginp()); - while (inNextp) { - MTaskEdge* const edgep = inNextp; - LogicMTask* const relativep = inNextp->fromMTaskp(); - inNextp = static_cast(inNextp->inNextp()); + while (MTaskEdge* const edgep = static_cast(donorp->inEdges().frontp())) { + LogicMTask* const relativep = edgep->fromMTaskp(); relativep->removeRelativeMTask(donorp); relativep->removeRelativeEdge(edgep); @@ -1076,7 +1080,7 @@ static void partRedirectEdgesFrom(V3Graph& graph, LogicMTask* recipientp, LogicM if (sbp) { if (sbp->contains(edgep)) sbp->remove(edgep); MTaskEdge* const existMTaskEdgep = static_cast( - recipientp->findConnectingEdgep(GraphWay::REVERSE, relativep)); + recipientp->findConnectingEdgep(relativep)); UDEBUGONLY(UASSERT(existMTaskEdgep, "findConnectingEdge didn't find edge");); if (sbp->contains(existMTaskEdgep)) sbp->hintScoreChanged(existMTaskEdgep); } @@ -1140,12 +1144,11 @@ public: , m_exitMTaskp{exitMTaskp} { if (m_slowAsserts) { // Check there are no redundant edges - for (V3GraphVertex* itp = m_mTaskGraph.verticesBeginp(); itp; - itp = itp->verticesNextp()) { + for (V3GraphVertex& vtx : m_mTaskGraph.vertices()) { std::unordered_set neighbors; - for (V3GraphEdge* edgep = itp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const bool first = neighbors.insert(edgep->top()).second; - UASSERT_OBJ(first, itp, "Redundant edge found in input to Contraction()"); + for (V3GraphEdge& edge : vtx.outEdges()) { + const bool first = neighbors.insert(edge.top()).second; + UASSERT_OBJ(first, &vtx, "Redundant edge found in input to Contraction()"); } } } @@ -1169,13 +1172,11 @@ public: // - Merge the best pair. // - Incrementally recompute critical paths near the merged mtask. - for (V3GraphVertex* itp = m_mTaskGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - itp->userp(nullptr); // Reset user value while we are here. Used by PropagateCp. - for (V3GraphEdge* edgep = itp->outBeginp(); edgep; edgep = edgep->outNextp()) { - m_sb.add(static_cast(edgep)); - } - siblingPairFromRelatives(itp); - siblingPairFromRelatives(itp); + for (V3GraphVertex& vtx : m_mTaskGraph.vertices()) { + vtx.userp(nullptr); // Reset user value while we are here. Used by PropagateCp. + for (V3GraphEdge& edge : vtx.outEdges()) m_sb.add(static_cast(&edge)); + siblingPairFromRelatives(&vtx); + siblingPairFromRelatives(&vtx); } doRescore(); // Set initial scores in scoreboard @@ -1223,11 +1224,7 @@ public: // Except, if we have too many mTaskGraphp, raise the score // limit and keep going... - unsigned mtaskCount = 0; - for (V3GraphVertex* vxp = m_mTaskGraph.verticesBeginp(); vxp; - vxp = vxp->verticesNextp()) { - ++mtaskCount; - } + const unsigned mtaskCount = m_mTaskGraph.vertices().size(); if (mtaskCount > maxMTasks) { const uint32_t oldLimit = m_scoreLimit; m_scoreLimit = (m_scoreLimit * 120) / 100; @@ -1327,12 +1324,12 @@ private: // relatives will see a new wayward CP from this merge. uint32_t newCp; if (mergeEdgep) { - if (mtaskp == mergeEdgep->furtherp(way)) { + if (mtaskp == mergeEdgep->furtherp()) { newCp = std::max(otherp->critPathCost(way), - mtaskp->critPathCostWithout(way, mergeEdgep)); + mtaskp->critPathCostWithout(mergeEdgep)); } else { newCp = std::max(mtaskp->critPathCost(way), - otherp->critPathCostWithout(way, mergeEdgep)); + otherp->critPathCostWithout(mergeEdgep)); } } else { newCp = std::max(otherp->critPathCost(way), mtaskp->critPathCost(way)); @@ -1350,19 +1347,15 @@ private: } void removeSiblingMCsWith(LogicMTask* mtaskp) { - for (SiblingMC *smcp = mtaskp->aSiblingMCs().begin(), *nextp; // lintok-begin-on-ref - smcp; smcp = nextp) { - nextp = smcp->aNextp(); + while (SiblingMC* const smcp = mtaskp->aSiblingMCs().unlinkFront()) { m_sb.remove(smcp); smcp->unlinkB(); - delete smcp; + VL_DO_DANGLING(delete smcp, smcp); } - for (SiblingMC *smcp = mtaskp->bSiblingMCs().begin(), *nextp; // lintok-begin-on-ref - smcp; smcp = nextp) { - nextp = smcp->bNextp(); + while (SiblingMC* const smcp = mtaskp->bSiblingMCs().unlinkFront()) { m_sb.remove(smcp); smcp->unlinkA(); - delete smcp; + VL_DO_DANGLING(delete smcp, smcp); } } @@ -1376,8 +1369,6 @@ private: // Clear the sibling map of the recipient. The donor will be deleted anyway, so we can // leave that in a corrupt for efficiency. recipientp->siblings().clear(); - recipientp->aSiblingMCs().reset(); - recipientp->bSiblingMCs().reset(); } void contract(MergeCandidate* mergeCanp) { @@ -1490,15 +1481,15 @@ private: siblingPairFromRelatives(recipientp); siblingPairFromRelatives(recipientp); unsigned edges = 0; - for (V3GraphEdge* edgep = recipientp->outBeginp(); edgep; edgep = edgep->outNextp()) { - LogicMTask* const postreqp = static_cast(edgep->top()); + for (V3GraphEdge& edge : recipientp->outEdges()) { + LogicMTask* const postreqp = static_cast(edge.top()); siblingPairFromRelatives(postreqp); ++edges; if (edges >= PART_SIBLING_EDGE_LIMIT) break; } edges = 0; - for (V3GraphEdge* edgep = recipientp->inBeginp(); edgep; edgep = edgep->inNextp()) { - LogicMTask* const prereqp = static_cast(edgep->fromp()); + for (V3GraphEdge& edge : recipientp->inEdges()) { + LogicMTask* const prereqp = static_cast(edge.fromp()); siblingPairFromRelatives(prereqp); ++edges; if (edges >= PART_SIBLING_EDGE_LIMIT) break; @@ -1528,11 +1519,10 @@ private: // It's fine if we already have this SiblingMC, we may have // created it earlier. Just confirm that we have associated data. bool found = false; - for (const SiblingMC* smcp = ap->aSiblingMCs().begin(); // lintok-begin-on-ref - smcp; smcp = smcp->aNextp()) { - UASSERT_OBJ(smcp->ap() == ap, ap, "Inconsistent SiblingMC"); - UASSERT_OBJ(m_sb.contains(smcp), ap, "Must be on the scoreboard"); - if (smcp->bp() == bp) found = true; + for (const SiblingMC& smc : ap->aSiblingMCs()) { + UASSERT_OBJ(smc.ap() == ap, ap, "Inconsistent SiblingMC"); + UASSERT_OBJ(m_sb.contains(&smc), ap, "Must be on the scoreboard"); + if (smc.bp() == bp) found = true; } UASSERT_OBJ(found, ap, "Sibling not found"); } @@ -1542,7 +1532,8 @@ private: void siblingPairFromRelatives(V3GraphVertex* mtaskp) { constexpr GraphWay way{T_Way}; // Need at least 2 edges - if (!mtaskp->beginp(way) || !mtaskp->beginp(way)->nextp(way)) return; + auto& edges = mtaskp->edges(); + if (!edges.hasMultipleElements()) return; std::array neighbors; @@ -1569,9 +1560,8 @@ private: size_t n = 0; // Populate the buffers - for (V3GraphEdge *edgep = mtaskp->beginp(way), *nextp; edgep; edgep = nextp) { - nextp = edgep->nextp(way); // Fetch next first as likely cache miss - LogicMTask* const otherp = static_cast(edgep->furtherp(way)); + for (V3GraphEdge& edge : mtaskp->edges()) { + LogicMTask* const otherp = static_cast(edge.furtherp()); neighbors[n] = otherp; sortRecs[n].m_id = otherp->id(); sortRecs[n].m_cp = otherp->critPathCost(way) + otherp->cost(); @@ -1638,9 +1628,7 @@ private: chain_len * 2, nullptr, nullptr, /* slowAsserts: */ false); // All vertices should merge into one - UASSERT_SELFTEST( - bool, mTaskGraph.verticesBeginp() && !mTaskGraph.verticesBeginp()->verticesNextp(), - true); + UASSERT_SELFTEST(bool, mTaskGraph.vertices().hasSingleElement(), true); const uint64_t endUsecs = V3Os::timeUsecs(); const uint64_t elapsedUsecs = endUsecs - startUsecs; @@ -1856,8 +1844,8 @@ class FixDataHazards final { = const_cast(static_cast(serialize.nextp()))) { // Compute and assign rank uint32_t rank = 0; - for (V3GraphEdge* edgep = mtaskp->inBeginp(); edgep; edgep = edgep->inNextp()) { - rank = std::max(edgep->fromp()->rank() + 1, rank); + for (V3GraphEdge& edge : mtaskp->inEdges()) { + rank = std::max(edge.fromp()->rank() + 1, rank); } mtaskp->rank(rank); @@ -1865,22 +1853,21 @@ class FixDataHazards final { // Entry and exit MTasks have no MTaskMoveVertices under them, so move on if (mtaskp->vertexList().empty()) continue; // Otherwise there should be only one OrderMoveVertex in each MTask at this stage - const V3List& vertexList = mtaskp->vertexList(); - UASSERT_OBJ(!vertexList.begin()->nextp(), mtaskp, "Multiple OrderMoveVertex"); - const OrderMoveVertex* const moveVtxp = vertexList.begin(); + const OrderMoveVertex::List& vertexList = mtaskp->vertexList(); + UASSERT_OBJ(vertexList.hasSingleElement(), mtaskp, "Multiple OrderMoveVertex"); + const OrderMoveVertex* const mVtxp = vertexList.frontp(); // Set up mapping back to the MTask from the OrderLogicVertex - if (OrderLogicVertex* const lvtxp = moveVtxp->logicp()) lvtxp->userp(mtaskp); + if (OrderLogicVertex* const lvtxp = mVtxp->logicp()) lvtxp->userp(mtaskp); } } // Gather all variables. SystemC vars will be handled slightly specially, so keep separate. std::vector regularVars; std::vector systemCVars; - for (V3GraphVertex *vtxp = orderGraph.verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); + for (const V3GraphVertex& vtx : orderGraph.vertices()) { // Only consider OrderVarStdVertex which reflects // an actual lvalue assignment; the others do not. - if (const OrderVarStdVertex* const vvtxp = vtxp->cast()) { + if (const OrderVarStdVertex* const vvtxp = vtx.cast()) { if (vvtxp->vscp()->varp()->isSc()) { systemCVars.push_back(vvtxp); } else { @@ -1953,10 +1940,9 @@ class FixDataHazards final { // Same basic strategy as above to serialize access to SC vars. if (!v3Global.opt.threadsDpiPure() || !v3Global.opt.threadsDpiUnpure()) { TasksByRank tasksByRank; - for (V3GraphVertex *vtxp = m_mTaskGraph.verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); - LogicMTask* const mtaskp = static_cast(vtxp); - if (hasDpiHazard(mtaskp)) tasksByRank[mtaskp->rank()].insert(mtaskp); + for (V3GraphVertex& vtx : m_mTaskGraph.vertices()) { + LogicMTask& mtask = static_cast(vtx); + if (hasDpiHazard(&mtask)) tasksByRank[mtask.rank()].insert(&mtask); } mergeSameRankTasks(tasksByRank); } @@ -1965,8 +1951,8 @@ class FixDataHazards final { // METHODS void findAdjacentTasks(const OrderVarStdVertex* varVtxp, TasksByRank& tasksByRank) { // Find all writer tasks for this variable, group by rank. - for (V3GraphEdge* edgep = varVtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - if (const auto* const logicVtxp = edgep->fromp()->cast()) { + for (const V3GraphEdge& edge : varVtxp->inEdges()) { + if (const auto* const logicVtxp = edge.fromp()->cast()) { LogicMTask* const writerMtaskp = static_cast(logicVtxp->userp()); tasksByRank[writerMtaskp->rank()].insert(writerMtaskp); } @@ -1992,10 +1978,8 @@ class FixDataHazards final { // Merge donor into recipient. if (donorp == recipientp) continue; // Fix up the map, so donor's OLVs map to recipientp - const V3List& vtxList = donorp->vertexList(); - for (const OrderMoveVertex *vtxp = vtxList.begin(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->nextp(); - vtxp->logicp()->userp(recipientp); + for (const OrderMoveVertex& vtx : donorp->vertexList()) { + vtx.logicp()->userp(recipientp); } // Move all vertices from donorp to recipientp recipientp->moveAllVerticesFrom(donorp); @@ -2010,9 +1994,8 @@ class FixDataHazards final { } } bool hasDpiHazard(LogicMTask* mtaskp) { - const V3List& vertexList = mtaskp->vertexList(); - for (const OrderMoveVertex* mVtxp = vertexList.begin(); mVtxp; mVtxp = mVtxp->nextp()) { - if (OrderLogicVertex* const lvtxp = mVtxp->logicp()) { + for (const OrderMoveVertex& mVtx : mtaskp->vertexList()) { + if (OrderLogicVertex* const lvtxp = mVtx.logicp()) { // NOTE: We don't handle DPI exports. If testbench code calls a // DPI-exported function at any time during eval() we may have // a data hazard. (Likewise in non-threaded mode if an export @@ -2039,7 +2022,7 @@ public: // Partitioner implementation // Print debug stats about graphp whose nodes must be LogicMTask's. -static void debugMTaskGraphStats(const V3Graph& graph, const string& stage) { +static void debugMTaskGraphStats(V3Graph& graph, const string& stage) { if (!debug() && !dumpLevel() && !dumpGraphLevel()) return; UINFO(4, "\n"); @@ -2048,11 +2031,9 @@ static void debugMTaskGraphStats(const V3Graph& graph, const string& stage) { uint32_t totalCost = 0; std::array mtaskCostHist; mtaskCostHist.fill(0); - - for (const V3GraphVertex* mtaskp = graph.verticesBeginp(); mtaskp; - mtaskp = mtaskp->verticesNextp()) { + for (const V3GraphVertex& mtask : graph.vertices()) { ++mtaskCount; - uint32_t mtaskCost = mtaskp->as()->cost(); + uint32_t mtaskCost = mtask.as()->cost(); totalCost += mtaskCost; unsigned log2Cost = 0; @@ -2110,14 +2091,11 @@ static void hashGraphDebug(const V3Graph& graph, const char* debugName) { std::unordered_map vx2Id; unsigned id = 0; - for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - vx2Id[vxp] = id++; - } + for (const V3GraphVertex& vtx : graph.vertices()) vx2Id[&vtx] = id++; unsigned hash = 0; - for (const V3GraphVertex* vxp = graph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - for (const V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const V3GraphVertex* const top = edgep->top(); - hash = vx2Id[top] + 31U * hash; // The K&R hash function + for (const V3GraphVertex& vtx : graph.vertices()) { + for (const V3GraphEdge& edge : vtx.outEdges()) { + hash = vx2Id[edge.top()] + 31U * hash; // The K&R hash function } } UINFO(0, "Hash of shape (not contents) of " << debugName << " = " << cvtToStr(hash) << endl); @@ -2158,7 +2136,8 @@ class Partitioner final { if (mvtxp->logicp()) return false; // Count fan-in, up to 3 unsigned fanIn = 0; - for (V3GraphEdge* edgep = mvtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { + auto& inEdges = mvtxp->inEdges(); + for (auto it = inEdges.begin(); it != inEdges.end(); ++it) { if (++fanIn == 3) break; } UDEBUGONLY(UASSERT_OBJ(fanIn <= 3, mvtxp, "Should have stopped counting fanIn");); @@ -2166,7 +2145,8 @@ class Partitioner final { if (fanIn <= 1) return true; // Count fan-out, up to 3 unsigned fanOut = 0; - for (V3GraphEdge* edgep = mvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { + auto& outEdges = mvtxp->outEdges(); + for (auto it = outEdges.begin(); it != outEdges.end(); ++it) { if (++fanOut == 3) break; } UDEBUGONLY(UASSERT_OBJ(fanOut <= 3, mvtxp, "Should have stopped counting fanOut");); @@ -2189,14 +2169,13 @@ class Partitioner final { const VNUser1InUse user1inUse; // Create the LogicMTasks for each OrderMoveVertex - for (V3GraphVertex *vtxp = m_moveGraph.verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); - OrderMoveVertex* const mVtxp = static_cast(vtxp); - if (bypassOk(mVtxp)) { - mVtxp->userp(nullptr); // Set to nullptr to mark as bypassed + for (V3GraphVertex& vtx : m_moveGraph.vertices()) { + OrderMoveVertex& mVtx = static_cast(vtx); + if (bypassOk(&mVtx)) { + mVtx.userp(nullptr); // Set to nullptr to mark as bypassed } else { - LogicMTask* const mtaskp = new LogicMTask{m_mTaskGraphp.get(), mVtxp}; - mVtxp->userp(mtaskp); + LogicMTask* const mtaskp = new LogicMTask{m_mTaskGraphp.get(), &mVtx}; + mVtx.userp(mtaskp); totalGraphCost += mtaskp->cost(); } } @@ -2207,44 +2186,39 @@ class Partitioner final { // Create the mtask->mtask dependency edges based on the dependencies between // OrderMoveVertex vertices. - for (V3GraphVertex *vtxp = m_mTaskGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); - LogicMTask* const mtaskp = static_cast(vtxp); + for (V3GraphVertex& vtx : m_mTaskGraphp->vertices()) { + LogicMTask& mtask = static_cast(vtx); // Entry and exit vertices handled separately - if (VL_UNLIKELY((mtaskp == m_entryMTaskp) || (mtaskp == m_exitMTaskp))) continue; + if (VL_UNLIKELY((&mtask == m_entryMTaskp) || (&mtask == m_exitMTaskp))) continue; - const V3List& vertexList = mtaskp->vertexList(); - OrderMoveVertex* const mvtxp = vertexList.begin(); + OrderMoveVertex::List& vertexList = mtask.vertexList(); // At this point, there should only be one OrderMoveVertex per LogicMTask - UASSERT_OBJ(!mvtxp->nextp(), mtaskp, "Multiple OrderMoveVertex"); - UASSERT_OBJ(mvtxp->userp(), mtaskp, "Bypassed OrderMoveVertex should not have MTask"); + UASSERT_OBJ(vertexList.hasSingleElement(), &mtask, "Multiple OrderMoveVertex"); + OrderMoveVertex* const mVtxp = vertexList.frontp(); + UASSERT_OBJ(mVtxp->userp(), &mtask, "Bypassed OrderMoveVertex should not have MTask"); // Function to add a edge to a dependent from 'mtaskp' - const auto addEdge = [this, mtaskp](LogicMTask* otherp) { - UASSERT_OBJ(otherp != mtaskp, mtaskp, "Would create a cycle edge"); - if (mtaskp->hasRelativeMTask(otherp)) return; // Don't create redundant edges. - new MTaskEdge{m_mTaskGraphp.get(), mtaskp, otherp, 1}; + const auto addEdge = [this, &mtask](LogicMTask* otherp) { + UASSERT_OBJ(otherp != &mtask, &mtask, "Would create a cycle edge"); + if (mtask.hasRelativeMTask(otherp)) return; // Don't create redundant edges. + new MTaskEdge{m_mTaskGraphp.get(), &mtask, otherp, 1}; }; // Iterate downstream direct dependents - for (V3GraphEdge *dEdgep = mvtxp->outBeginp(), *dNextp; dEdgep; dEdgep = dNextp) { - dNextp = dEdgep->outNextp(); - V3GraphVertex* const top = dEdgep->top(); + for (V3GraphEdge& dEdge : mVtxp->outEdges()) { + V3GraphVertex* const top = dEdge.top(); if (LogicMTask* const otherp = static_cast(top->userp())) { // The opposite end of the edge is not a bypassed vertex, add as direct // dependent addEdge(otherp); } else { // The opposite end of the edge is a bypassed vertex, add transitive dependents - for (V3GraphEdge *tEdgep = top->outBeginp(), *tNextp; tEdgep; - tEdgep = tNextp) { - tNextp = tEdgep->outNextp(); - LogicMTask* const transp - = static_cast(tEdgep->top()->userp()); + for (V3GraphEdge& tEdge : top->outEdges()) { + LogicMTask* const transp = static_cast(tEdge.top()->userp()); // The Move graph is bipartite (logic <-> var), and logic is never // bypassed, hence 'transp' must be non nullptr. - UASSERT_OBJ(transp, mvtxp, "This cannot be a bypassed vertex"); + UASSERT_OBJ(transp, mVtxp, "This cannot be a bypassed vertex"); addEdge(transp); } } @@ -2252,15 +2226,14 @@ class Partitioner final { } // Create Dependencies to/from the entry/exit vertices. - for (V3GraphVertex *vtxp = m_mTaskGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); - LogicMTask* const mtaskp = static_cast(vtxp); + for (V3GraphVertex& vtx : m_mTaskGraphp->vertices()) { + LogicMTask& mtask = static_cast(vtx); - if (VL_UNLIKELY((mtaskp == m_entryMTaskp) || (mtaskp == m_exitMTaskp))) continue; + if (VL_UNLIKELY((&mtask == m_entryMTaskp) || (&mtask == m_exitMTaskp))) continue; // Add the entry/exit edges - if (mtaskp->inEmpty()) new MTaskEdge{m_mTaskGraphp.get(), m_entryMTaskp, mtaskp, 1}; - if (mtaskp->outEmpty()) new MTaskEdge{m_mTaskGraphp.get(), mtaskp, m_exitMTaskp, 1}; + if (mtask.inEmpty()) new MTaskEdge{m_mTaskGraphp.get(), m_entryMTaskp, &mtask, 1}; + if (mtask.outEmpty()) new MTaskEdge{m_mTaskGraphp.get(), &mtask, m_exitMTaskp, 1}; } return totalGraphCost; @@ -2343,15 +2316,13 @@ class Partitioner final { // Remove MTasks that have no logic in it rerouting the edges. Set user to indicate the // mtask on every underlying OrderMoveVertex. Clear vertex lists (used later). m_moveGraph.userClearVertices(); - for (V3GraphVertex *vtxp = m_mTaskGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); + for (V3GraphVertex* const vtxp : m_mTaskGraphp->vertices().unlinkable()) { LogicMTask* const mtaskp = vtxp->as(); - V3List& vertexList = mtaskp->vertexList(); + OrderMoveVertex::List& vertexList = mtaskp->vertexList(); // Check if MTask is empty bool empty = true; - for (OrderMoveVertex *mVtxp = vertexList.begin(), *nextp; mVtxp; mVtxp = nextp) { - nextp = mVtxp->nextp(); - if (mVtxp->logicp()) { + for (OrderMoveVertex& mVtx : vertexList) { + if (mVtx.logicp()) { empty = false; break; } @@ -2363,10 +2334,7 @@ class Partitioner final { continue; } // Annotate the underlying OrderMoveVertex vertices and unlink them - while (OrderMoveVertex* mVtxp = vertexList.begin()) { - mVtxp->userp(mtaskp); - mVtxp->unlinkFrom(vertexList); - } + while (OrderMoveVertex* const mVtxp = vertexList.unlinkFront()) mVtxp->userp(mtaskp); } m_mTaskGraphp->removeRedundantEdgesSum(&V3GraphEdge::followAlwaysTrue); } @@ -2388,7 +2356,7 @@ struct MTaskVxIdLessThan final { } }; -AstExecGraph* V3Order::createParallel(const OrderGraph& orderGraph, const std::string& tag, +AstExecGraph* V3Order::createParallel(OrderGraph& orderGraph, const std::string& tag, const TrigToSenMap& trigToSen, bool slow) { UINFO(2, " Constructing parallel code for '" + tag + "'"); @@ -2407,26 +2375,23 @@ AstExecGraph* V3Order::createParallel(const OrderGraph& orderGraph, const std::s if (dumpGraphLevel() >= 9) moveGraphp->dumpDotFilePrefixed(tag + "_ordermv_mtasks"); // Some variable OrderMoveVertices are not assigned to an MTask. Reroute and delete these. - for (V3GraphVertex *vtxp = moveGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); + for (V3GraphVertex* const vtxp : moveGraphp->vertices().unlinkable()) { OrderMoveVertex* const mVtxp = vtxp->as(); if (!mVtxp->userp()) { UASSERT_OBJ(!mVtxp->logicp(), mVtxp, "Logic OrderMoveVertex not assigned to mtask"); mVtxp->rerouteEdges(moveGraphp.get()); - VL_DO_DANGLING(vtxp->unlinkDelete(moveGraphp.get()), vtxp); + VL_DO_DANGLING(mVtxp->unlinkDelete(moveGraphp.get()), mVtxp); } } // Remove all edges from the move graph that cross between MTasks. Add logic to MTask lists. - for (V3GraphVertex *vtxp = moveGraphp->verticesBeginp(), *nextpVtxp; vtxp; vtxp = nextpVtxp) { - nextpVtxp = vtxp->verticesNextp(); - OrderMoveVertex* const mVtxp = vtxp->as(); + for (V3GraphVertex& vtx : moveGraphp->vertices()) { + OrderMoveVertex* const mVtxp = vtx.as(); LogicMTask* const mtaskp = static_cast(mVtxp->userp()); // Add to list in MTask, in MoveGraph order. This should not be necessary, but see #4993. - mVtxp->appendTo(mtaskp->vertexList()); + mtaskp->vertexList().linkBack(mVtxp); // Remove edges crossing between MTasks - for (V3GraphEdge *edgep = mVtxp->outBeginp(), *nextEdgep; edgep; edgep = nextEdgep) { - nextEdgep = edgep->outNextp(); + for (V3GraphEdge* const edgep : mVtxp->outEdges().unlinkable()) { const OrderMoveVertex* const toMVtxp = edgep->top()->as(); if (mtaskp != toMVtxp->userp()) VL_DO_DANGLING(edgep->unlinkDelete(), edgep); } @@ -2449,12 +2414,10 @@ AstExecGraph* V3Order::createParallel(const OrderGraph& orderGraph, const std::s LogicMTask* const mTaskp = const_cast(cMTaskp); // Add initially ready vertices within this MTask to the serializer as seeds, - // and unlink them from the vertex list in the MTask as we go. - V3List& vertexList = mTaskp->vertexList(); - while (OrderMoveVertex* vtxp = vertexList.begin()) { - // The serializer uses the list node in the vertex, so must unlink here - vtxp->unlinkFrom(vertexList); - if (vtxp->inEmpty()) serializer.addSeed(vtxp); + // and unlink them from the vertex list in the MTask as we go. (The serializer + // uses the list links in the vertex, so must unlink it here.) + while (OrderMoveVertex* const mVtxp = mTaskp->vertexList().unlinkFront()) { + if (mVtxp->inEmpty()) serializer.addSeed(mVtxp); } // Emit all logic within the MTask as they become ready @@ -2490,16 +2453,15 @@ AstExecGraph* V3Order::createParallel(const OrderGraph& orderGraph, const std::s << execMTaskp->id() << std::endl); // Add the dependency edges between ExecMTasks - for (const V3GraphEdge* inp = mTaskp->inBeginp(); inp; inp = inp->inNextp()) { - const V3GraphVertex* fromVxp = inp->fromp(); + for (const V3GraphEdge& edge : mTaskp->inEdges()) { + const V3GraphVertex* fromVxp = edge.fromp(); const LogicMTask* const fromp = fromVxp->as(); new V3GraphEdge{depGraphp, logicMTaskToExecMTask.at(fromp), execMTaskp, 1}; } } // Delete the remaining variable vertices - for (V3GraphVertex *vtxp = moveGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); + for (V3GraphVertex* const vtxp : moveGraphp->vertices().unlinkable()) { if (!vtxp->as()->logicp()) { VL_DO_DANGLING(vtxp->unlinkDelete(moveGraphp.get()), vtxp); } diff --git a/src/V3OrderProcessDomains.cpp b/src/V3OrderProcessDomains.cpp index 458244a60..9e4186e23 100644 --- a/src/V3OrderProcessDomains.cpp +++ b/src/V3OrderProcessDomains.cpp @@ -83,8 +83,8 @@ class V3OrderProcessDomains final { // Buffer to hold external sensitivities std::vector externalDomainps; // For each vertex - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - OrderEitherVertex* const vtxp = itp->as(); + for (V3GraphVertex& it : m_graph.vertices()) { + OrderEitherVertex* const vtxp = it.as(); UINFO(5, " pdi: " << vtxp << endl); // Sequential logic already has its domain set if (vtxp->domainp()) continue; @@ -95,10 +95,10 @@ class V3OrderProcessDomains final { if (lvtxp) domainp = lvtxp->hybridp(); // For each incoming edge, examine the source vertex - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - OrderEitherVertex* const fromVtxp = edgep->fromp()->as(); + for (V3GraphEdge& edge : vtxp->inEdges()) { + OrderEitherVertex* const fromVtxp = edge.fromp()->as(); // Cut edge - if (!edgep->weight()) continue; + if (!edge.weight()) continue; // if (!fromVtxp->domainMatters()) continue; @@ -162,14 +162,14 @@ class V3OrderProcessDomains final { std::unordered_map, const AstSenTree*> trigToSen; for (const auto& pair : m_trigToSen) trigToSen.emplace(*pair.first, pair.second); - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (OrderVarVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex& vtx : m_graph.vertices()) { + if (OrderVarVertex* const vvertexp = vtx.cast()) { string name(vvertexp->vscp()->prettyName()); - if (itp->is()) { + if (vtx.is()) { name += " {PRE}"; - } else if (itp->is()) { + } else if (vtx.is()) { name += " {POST}"; - } else if (itp->is()) { + } else if (vtx.is()) { name += " {PORD}"; } std::ostringstream os; diff --git a/src/V3OrderSerial.cpp b/src/V3OrderSerial.cpp index e8198211a..f556e51f5 100644 --- a/src/V3OrderSerial.cpp +++ b/src/V3OrderSerial.cpp @@ -31,7 +31,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //###################################################################### // OrderSerial class -std::vector V3Order::createSerial(const OrderGraph& graph, const std::string& tag, +std::vector V3Order::createSerial(OrderGraph& graph, const std::string& tag, const TrigToSenMap& trigToSen, bool slow) { UINFO(2, " Constructing serial code for '" + tag + "'"); @@ -45,9 +45,8 @@ std::vector V3Order::createSerial(const OrderGraph& graph, const std OrderMoveGraphSerializer serializer{*moveGraphp}; // Add initially ready vertices (those with no dependencies) to the serializer as seeds - for (V3GraphVertex *vtxp = moveGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); - if (vtxp->inEmpty()) serializer.addSeed(vtxp->as()); + for (V3GraphVertex& vtx : moveGraphp->vertices()) { + if (vtx.inEmpty()) serializer.addSeed(vtx.as()); } // Emit all logic as they become ready @@ -68,8 +67,7 @@ std::vector V3Order::createSerial(const OrderGraph& graph, const std } // Delete the remaining variable vertices - for (V3GraphVertex *vtxp = moveGraphp->verticesBeginp(), *nextp; vtxp; vtxp = nextp) { - nextp = vtxp->verticesNextp(); + for (V3GraphVertex* const vtxp : moveGraphp->vertices().unlinkable()) { if (!vtxp->as()->logicp()) { VL_DO_DANGLING(vtxp->unlinkDelete(moveGraphp.get()), vtxp); } diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index b968d5b91..05ef424a5 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -176,8 +176,8 @@ void removeNonCyclic(Graph* graphp) { }; // Start with vertices with no inputs or outputs - for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - if (vtxp->inEmpty() || vtxp->outEmpty()) enqueue(vtxp); + for (V3GraphVertex& vtx : graphp->vertices()) { + if (vtx.inEmpty() || vtx.outEmpty()) enqueue(&vtx); } // Iterate while we still have candidates @@ -189,16 +189,14 @@ void removeNonCyclic(Graph* graphp) { if (vtxp->inEmpty()) { // Enqueue children for consideration, remove out edges, and delete this vertex - for (V3GraphEdge *edgep = vtxp->outBeginp(), *nextp; edgep; edgep = nextp) { - nextp = edgep->outNextp(); + for (V3GraphEdge* const edgep : vtxp->outEdges().unlinkable()) { enqueue(edgep->top()); VL_DO_DANGLING(edgep->unlinkDelete(), edgep); } VL_DO_DANGLING(vtxp->unlinkDelete(graphp), vtxp); } else if (vtxp->outEmpty()) { // Enqueue parents for consideration, remove in edges, and delete this vertex - for (V3GraphEdge *edgep = vtxp->inBeginp(), *nextp; edgep; edgep = nextp) { - nextp = edgep->inNextp(); + for (V3GraphEdge* const edgep : vtxp->inEdges().unlinkable()) { enqueue(edgep->fromp()); VL_DO_DANGLING(edgep->unlinkDelete(), edgep); } @@ -209,11 +207,11 @@ void removeNonCyclic(Graph* graphp) { // Has this VarVertex been cut? (any edges in or out has been cut) bool isCut(const SchedAcyclicVarVertex* vtxp) { - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - if (edgep->weight() == 0) return true; + for (const V3GraphEdge& edge : vtxp->inEdges()) { + if (edge.weight() == 0) return true; } - for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - if (edgep->weight() == 0) return true; + for (const V3GraphEdge& edge : vtxp->outEdges()) { + if (edge.weight() == 0) return true; } return false; } @@ -221,8 +219,8 @@ bool isCut(const SchedAcyclicVarVertex* vtxp) { std::vector findCutVertices(Graph* graphp) { std::vector result; const VNUser1InUse user1InUse; // bool: already added to result - for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - if (SchedAcyclicVarVertex* const vvtxp = vtxp->cast()) { + for (V3GraphVertex& vtx : graphp->vertices()) { + if (SchedAcyclicVarVertex* const vvtxp = vtx.cast()) { if (!vvtxp->vscp()->user1SetOnce() && isCut(vvtxp)) result.push_back(vvtxp); } } @@ -231,8 +229,8 @@ std::vector findCutVertices(Graph* graphp) { void resetEdgeWeights(const std::vector& cutVertices) { for (SchedAcyclicVarVertex* const vvtxp : cutVertices) { - for (V3GraphEdge* ep = vvtxp->inBeginp(); ep; ep = ep->inNextp()) ep->weight(1); - for (V3GraphEdge* ep = vvtxp->outBeginp(); ep; ep = ep->outNextp()) ep->weight(1); + for (V3GraphEdge& e : vvtxp->inEdges()) e.weight(1); + for (V3GraphEdge& e : vvtxp->outEdges()) e.weight(1); } } @@ -252,19 +250,18 @@ void gatherSCCCandidates(V3GraphVertex* vtxp, std::vector& candidates && name.find("__Vdly") == string::npos // Ignore internal signals && name.find("__Vcell") == string::npos) { // Also compute the fanout of this vertex - unsigned fanout = 0; - for (V3GraphEdge* ep = vtxp->outBeginp(); ep; ep = ep->outNextp()) ++fanout; + const unsigned fanout = vtxp->outEdges().size(); candidates.emplace_back(vvtxp, fanout); } } // Iterate through all the vertices within the same strongly connected component (same color) - for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - V3GraphVertex* const top = edgep->top(); + for (V3GraphEdge& edge : vtxp->outEdges()) { + V3GraphVertex* const top = edge.top(); if (top->color() == vtxp->color()) gatherSCCCandidates(top, candidates); } - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - V3GraphVertex* const fromp = edgep->fromp(); + for (V3GraphEdge& edge : vtxp->inEdges()) { + V3GraphVertex* const fromp = edge.fromp(); if (fromp->color() == vtxp->color()) gatherSCCCandidates(fromp, candidates); } } @@ -359,9 +356,9 @@ LogicByScope fixCuts(AstNetlist* netlistp, { const VNUser1InUse user1InUse; // bool: already added to 'lvtxps' for (SchedAcyclicVarVertex* const vvtxp : cutVertices) { - for (V3GraphEdge* edgep = vvtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { + for (V3GraphEdge& edge : vvtxp->outEdges()) { SchedAcyclicLogicVertex* const lvtxp - = static_cast(edgep->top()); + = static_cast(edge.top()); if (!lvtxp->logicp()->user1SetOnce()) lvtxps.push_back(lvtxp); lvtx2Cuts[lvtxp].push_back(vvtxp->vscp()); } diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index 33d77bb3f..caf64561e 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -290,13 +290,13 @@ public: } }; -void colorActiveRegion(const V3Graph& graph) { +void colorActiveRegion(V3Graph& graph) { // Work queue for depth first traversal std::vector queue{}; // Trace from all SchedSenVertex - for (V3GraphVertex* vtxp = graph.verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - if (const auto activeEventVtxp = vtxp->cast()) { + for (V3GraphVertex& vtx : graph.vertices()) { + if (const auto activeEventVtxp = vtx.cast()) { queue.push_back(activeEventVtxp); } } @@ -313,17 +313,15 @@ void colorActiveRegion(const V3Graph& graph) { vtx.color(1); // Enqueue all parent vertices that feed this vertex. - for (V3GraphEdge* edgep = vtx.inBeginp(); edgep; edgep = edgep->inNextp()) { - queue.push_back(edgep->fromp()); - } + for (V3GraphEdge& edge : vtx.inEdges()) queue.push_back(edge.fromp()); // If this is a logic vertex, also enqueue all variable vertices that are driven from this // logic. This will ensure that if a variable is set in the active region, then all // settings of that variable will be in the active region. if (vtx.is()) { - for (V3GraphEdge* edgep = vtx.outBeginp(); edgep; edgep = edgep->outNextp()) { - UASSERT(edgep->top()->is(), "Should be var vertex"); - queue.push_back(edgep->top()); + for (V3GraphEdge& edge : vtx.outEdges()) { + UASSERT(edge.top()->is(), "Should be var vertex"); + queue.push_back(edge.top()); } } } @@ -346,8 +344,8 @@ LogicRegions partition(LogicByScope& clockedLogic, LogicByScope& combinationalLo LogicRegions result; - for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - if (const auto lvtxp = vtxp->cast()) { + for (V3GraphVertex& vtx : graphp->vertices()) { + if (const auto lvtxp = vtx.cast()) { LogicByScope& lbs = lvtxp->color() ? result.m_act : result.m_nba; AstNode* const logicp = lvtxp->logicp(); logicp->unlinkFrBack(); diff --git a/src/V3SchedReplicate.cpp b/src/V3SchedReplicate.cpp index 5b0256210..eb5194692 100644 --- a/src/V3SchedReplicate.cpp +++ b/src/V3SchedReplicate.cpp @@ -225,8 +225,8 @@ void propagateDrivingRegions(SchedReplicateVertex* vtxp) { // Compute union of driving regions of all inputs uint8_t drivingRegions = 0; - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - SchedReplicateVertex* const srcp = edgep->fromp()->as(); + for (V3GraphEdge& edge : vtxp->inEdges()) { + SchedReplicateVertex* const srcp = edge.fromp()->as(); propagateDrivingRegions(srcp); drivingRegions |= srcp->drivingRegions(); } @@ -240,8 +240,8 @@ void propagateDrivingRegions(SchedReplicateVertex* vtxp) { LogicReplicas replicate(Graph* graphp) { LogicReplicas result; - for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - if (SchedReplicateLogicVertex* const lvtxp = vtxp->cast()) { + for (V3GraphVertex& vtx : graphp->vertices()) { + if (SchedReplicateLogicVertex* const lvtxp = vtx.cast()) { const auto replicateTo = [&](LogicByScope& lbs) { lbs.add(lvtxp->scopep(), lvtxp->senTreep(), lvtxp->logicp()->cloneTree(false)); }; @@ -264,8 +264,8 @@ LogicReplicas replicateLogic(LogicRegions& logicRegionsRegions) { // Dump for debug if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate"); // Propagate driving region flags - for (V3GraphVertex* vtxp = graphp->verticesBeginp(); vtxp; vtxp = vtxp->verticesNextp()) { - propagateDrivingRegions(vtxp->as()); + for (V3GraphVertex& vtx : graphp->vertices()) { + propagateDrivingRegions(vtx.as()); } // Dump for debug if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("sched-replicate-propagated"); diff --git a/src/V3Split.cpp b/src/V3Split.cpp index e2eb6c757..f64c4cf9e 100644 --- a/src/V3Split.cpp +++ b/src/V3Split.cpp @@ -317,17 +317,16 @@ protected: } void pruneDepsOnInputs() { - for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - if (!vertexp->outBeginp() && vertexp->is()) { + for (V3GraphVertex& vertex : m_graph.vertices()) { + if (vertex.outEmpty() && vertex.is()) { if (debug() >= 9) { - const SplitVarStdVertex* const stdp = static_cast(vertexp); - UINFO(0, "Will prune deps on var " << stdp->nodep() << endl); - stdp->nodep()->dumpTree("- "); + const SplitVarStdVertex& sVtx = static_cast(vertex); + UINFO(0, "Will prune deps on var " << sVtx.nodep() << endl); + sVtx.nodep()->dumpTree("- "); } - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - SplitEdge* const oedgep = static_cast(edgep); - oedgep->setIgnoreThisStep(); + for (V3GraphEdge& edge : vertex.inEdges()) { + SplitEdge& oedge = static_cast(edge); + oedge.setIgnoreThisStep(); } } } @@ -475,19 +474,16 @@ protected: // 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()) { - if (!vertexp->user()) { - if (const SplitLogicVertex* const vvertexp = vertexp->cast()) { - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; - edgep = edgep->inNextp()) { - SplitEdge* const oedgep = static_cast(edgep); - oedgep->setIgnoreThisStep(); + for (V3GraphVertex& vertex : m_graph.vertices()) { + if (!vertex.user()) { + if (const SplitLogicVertex* const vvertexp = vertex.cast()) { + for (V3GraphEdge& edge : vertex.inEdges()) { + SplitEdge& oedge = static_cast(edge); + oedge.setIgnoreThisStep(); } - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; - edgep = edgep->outNextp()) { - SplitEdge* const oedgep = static_cast(edgep); - oedgep->setIgnoreThisStep(); + for (V3GraphEdge& edge : vertex.outEdges()) { + SplitEdge& oedge = static_cast(edge); + oedge.setIgnoreThisStep(); } } } @@ -904,18 +900,17 @@ protected: // For any 'if' node whose deps have all been pruned // (meaning, its conditional expression only looks at primary // inputs) prune all edges that depend on the 'if'. - for (V3GraphVertex* vertexp = m_graph.verticesBeginp(); vertexp; - vertexp = vertexp->verticesNextp()) { - const SplitLogicVertex* const logicp = vertexp->cast(); + for (V3GraphVertex& vertex : m_graph.vertices()) { + SplitLogicVertex* const logicp = vertex.cast(); if (!logicp) continue; const AstNodeIf* const ifNodep = VN_CAST(logicp->nodep(), NodeIf); if (!ifNodep) continue; bool pruneMe = true; - for (V3GraphEdge* edgep = logicp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const SplitEdge* const oedgep = static_cast(edgep); - if (!oedgep->ignoreThisStep()) { + for (const V3GraphEdge& edge : logicp->outEdges()) { + const SplitEdge& oedge = static_cast(edge); + if (!oedge.ignoreThisStep()) { // This if conditional depends on something we can't // prune -- a variable generated in the current block. pruneMe = false; @@ -923,11 +918,11 @@ protected: // When we can't prune dependencies on the conditional, // give a hint about why... if (debug() >= 9) { - V3GraphVertex* vxp = oedgep->top(); + V3GraphVertex* vxp = oedge.top(); const SplitNodeVertex* const nvxp = static_cast(vxp); UINFO(0, "Cannot prune if-node due to edge " - << oedgep << " pointing to node " << nvxp->nodep() << endl); + << &oedge << " pointing to node " << nvxp->nodep() << endl); nvxp->nodep()->dumpTree("- "); } @@ -938,9 +933,9 @@ protected: if (!pruneMe) continue; // This if can be split; prune dependencies on it. - for (V3GraphEdge* edgep = logicp->inBeginp(); edgep; edgep = edgep->inNextp()) { - SplitEdge* const oedgep = static_cast(edgep); - oedgep->setIgnoreThisStep(); + for (V3GraphEdge& edge : logicp->inEdges()) { + SplitEdge& oedge = static_cast(edge); + oedge.setIgnoreThisStep(); } } diff --git a/src/V3TSP.cpp b/src/V3TSP.cpp index c94784db2..63312a401 100644 --- a/src/V3TSP.cpp +++ b/src/V3TSP.cpp @@ -146,6 +146,9 @@ private: }; static Vertex* castVertexp(V3GraphVertex* vxp) { return static_cast(vxp); } + static const Vertex* castVertexp(const V3GraphVertex* vxp) { + return static_cast(vxp); + } public: // From *this, populate *mstp with the minimum spanning tree. @@ -156,8 +159,8 @@ public: // Use Prim's algorithm to efficiently construct the MST. uint32_t vertCount = 0; - for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - mstp->addVertex(castVertexp(vxp)->key()); + for (V3GraphVertex& vtx : vertices()) { + mstp->addVertex(castVertexp(&vtx)->key()); vertCount++; } @@ -181,12 +184,12 @@ public: // Allocate new edge list EdgeList* const newEdgesp = &allocatedEdgeLists[vertIdx++]; // Gather out edges of this vertex - for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { + for (V3GraphEdge& edge : vtxp->outEdges()) { // Don't add edges leading to vertices we already visited. This is a highly // connected graph, so this greatly reduces the cost of maintaining the pending // set. - if (edgep->top()->user() == VertexState::MST_VISITED) continue; - newEdgesp->push_back(edgep); + if (edge.top()->user() == VertexState::MST_VISITED) continue; + newEdgesp->push_back(&edge); } // If no relevant out edges, then we are done if (newEdgesp->empty()) return; @@ -197,7 +200,7 @@ public: }; // To start, choose an arbitrary vertex and visit it. - visit(verticesBeginp()); + visit(vertices().frontp()); // Repeatedly find the least costly edge in the pending set. // If it connects to an unvisited node, visit that node and update @@ -282,14 +285,14 @@ public: std::vector pendingEdges; for (Vertex* const fromp : odds) { - for (V3GraphEdge* edgep = fromp->outBeginp(); edgep; edgep = edgep->outNextp()) { - Vertex* const top = castVertexp(edgep->top()); + for (V3GraphEdge& edge : fromp->outEdges()) { + Vertex* const top = castVertexp(edge.top()); // There are two edges (in both directions) between these two vertices. Keep one. if (fromp > top) continue; // We only care about edges between the odd-order vertices if (top->user() != VertexState::UNMATCHED_ODD) continue; // Add to candidate list - pendingEdges.push_back(edgep); + pendingEdges.push_back(&edge); } } @@ -313,12 +316,12 @@ public: void combineGraph(const TspGraphTmpl& g) { std::unordered_set edges_done; - for (V3GraphVertex* vxp = g.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - const Vertex* const fromp = castVertexp(vxp); - for (V3GraphEdge* edgep = fromp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const Vertex* const top = castVertexp(edgep->top()); - if (edges_done.insert(getEdgeId(edgep)).second) { - addEdge(fromp->key(), top->key(), edgep->weight()); + for (const V3GraphVertex& vtx : g.vertices()) { + const Vertex* const fromp = castVertexp(&vtx); + for (const V3GraphEdge& edge : fromp->outEdges()) { + const Vertex* const top = castVertexp(edge.top()); + if (edges_done.insert(getEdgeId(&edge)).second) { + addEdge(fromp->key(), top->key(), edge.weight()); } } } @@ -335,12 +338,12 @@ public: tour.push_back(cur_vertexp); // Look for an arbitrary edge we've not yet marked - for (V3GraphEdge* edgep = cur_vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const uint32_t edgeId = getEdgeId(edgep); + for (V3GraphEdge& edge : cur_vertexp->outEdges()) { + const uint32_t edgeId = getEdgeId(&edge); if (markedEdgesp->end() == markedEdgesp->find(edgeId)) { // This edge is not yet marked, so follow it. markedEdgesp->insert(edgeId); - Vertex* const neighborp = castVertexp(edgep->top()); + Vertex* const neighborp = castVertexp(edge.top()); UINFO(6, "following edge " << edgeId << " from " << cur_vertexp->key() << " to " << neighborp->key() << endl); cur_vertexp = neighborp; @@ -359,8 +362,8 @@ public: do { recursed = false; // Look for an arbitrary edge at vxp we've not yet marked - for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const uint32_t edgeId = getEdgeId(edgep); + for (V3GraphEdge& edge : vxp->outEdges()) { + const uint32_t edgeId = getEdgeId(&edge); if (markedEdgesp->end() == markedEdgesp->find(edgeId)) { UINFO(6, "Recursing.\n"); findEulerTourRecurse(markedEdgesp, vxp, sortedOutp); @@ -381,12 +384,12 @@ public: void dumpGraph(std::ostream& os, const string& nameComment) const { // UINFO(0) as controlled by caller os << "At " << nameComment << ", dumping graph. Keys:\n"; - for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - const Vertex* const tspvp = castVertexp(vxp); + for (const V3GraphVertex& vtx : vertices()) { + const Vertex* const tspvp = castVertexp(&vtx); os << " " << tspvp->key() << '\n'; - for (V3GraphEdge* edgep = tspvp->outBeginp(); edgep; edgep = edgep->outNextp()) { - const Vertex* const neighborp = castVertexp(edgep->top()); - os << " has edge " << getEdgeId(edgep) << " to " << neighborp->key() << '\n'; + for (const V3GraphEdge& edge : tspvp->outEdges()) { + const Vertex* const neighborp = castVertexp(edge.top()); + os << " has edge " << getEdgeId(&edge) << " to " << neighborp->key() << '\n'; } } } @@ -404,18 +407,15 @@ public: if (::dumpGraphLevel() >= 6) dumpDotFilePrefixed("findEulerTour"); std::unordered_set markedEdges; // Pick a start node - Vertex* const start_vertexp = castVertexp(verticesBeginp()); + Vertex* const start_vertexp = castVertexp(vertices().frontp()); findEulerTourRecurse(&markedEdges, start_vertexp, sortedOutp); } std::vector getOddDegreeKeys() const { std::vector result; - for (V3GraphVertex* vxp = verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - const Vertex* const tspvp = castVertexp(vxp); - uint32_t degree = 0; - for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - degree++; - } + for (const V3GraphVertex& vtx : vertices()) { + const Vertex* const tspvp = castVertexp(&vtx); + const uint32_t degree = vtx.outEdges().size(); if (degree & 1) result.push_back(tspvp->key()); } return result; diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 644ea9266..969559ba9 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -153,8 +153,8 @@ private: << vxp->impureNode()->warnContextSecondary()); } // 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())); + for (V3GraphEdge& edge : vxp->outEdges()) { + checkPurity(nodep, static_cast(edge.top())); } } TaskFTaskVertex* getFTaskVertex(AstNodeFTask* nodep) { diff --git a/src/V3Timing.cpp b/src/V3Timing.cpp index 48aa5c017..b656a05c3 100644 --- a/src/V3Timing.cpp +++ b/src/V3Timing.cpp @@ -227,8 +227,8 @@ class TimingSuspendableVisitor final : public VNVisitor { // Propagate flag to all nodes that depend on the given one void propagateFlags(DepVtx* const vxp, NodeFlag flag) { auto* const parentp = vxp->nodep(); - for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - auto* const depVxp = static_cast(edgep->top()); + for (V3GraphEdge& edge : vxp->outEdges()) { + auto* const depVxp = static_cast(edge.top()); AstNode* const depp = depVxp->nodep(); if (passFlag(parentp, depp, flag)) propagateFlags(depVxp, flag); } @@ -236,19 +236,19 @@ class TimingSuspendableVisitor final : public VNVisitor { template void propagateFlagsIf(DepVtx* const vxp, NodeFlag flag, Predicate p) { auto* const parentp = vxp->nodep(); - for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - auto* const depVxp = static_cast(edgep->top()); + for (V3GraphEdge& edge : vxp->outEdges()) { + auto* const depVxp = static_cast(edge.top()); AstNode* const depp = depVxp->nodep(); - if (p(edgep) && passFlag(parentp, depp, flag)) propagateFlagsIf(depVxp, flag, p); + if (p(&edge) && passFlag(parentp, depp, flag)) propagateFlagsIf(depVxp, flag, p); } } template void propagateFlagsReversedIf(DepVtx* const vxp, NodeFlag flag, Predicate p) { auto* const parentp = vxp->nodep(); - for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - auto* const depVxp = static_cast(edgep->fromp()); + for (V3GraphEdge& edge : vxp->inEdges()) { + auto* const depVxp = static_cast(edge.fromp()); AstNode* const depp = depVxp->nodep(); - if (p(edgep) && passFlag(parentp, depp, flag)) + if (p(&edge) && passFlag(parentp, depp, flag)) propagateFlagsReversedIf(depVxp, flag, p); } } @@ -408,34 +408,34 @@ public: m_suspGraph.removeTransitiveEdges(); m_procGraph.removeTransitiveEdges(); // Propagate suspendability - for (V3GraphVertex* vxp = m_suspGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - DepVtx* const depVxp = static_cast(vxp); - if (hasFlags(depVxp->nodep(), T_SUSPENDEE)) propagateFlags(depVxp, T_SUSPENDEE); + for (V3GraphVertex& vtx : m_suspGraph.vertices()) { + DepVtx& depVtx = static_cast(vtx); + if (hasFlags(depVtx.nodep(), T_SUSPENDEE)) propagateFlags(&depVtx, T_SUSPENDEE); } if (dumpGraphLevel() >= 6) m_suspGraph.dumpDotFilePrefixed("timing_deps"); // Propagate T_HAS_PROCESS - for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - DepVtx* const depVxp = static_cast(vxp); + for (V3GraphVertex& vtx : m_procGraph.vertices()) { + DepVtx& depVtx = static_cast(vtx); // Find processes that'll allocate VlProcess - if (hasFlags(depVxp->nodep(), T_FORCES_PROC)) { - propagateFlagsIf(depVxp, T_FORCES_PROC, [&](const V3GraphEdge* e) -> bool { + if (hasFlags(depVtx.nodep(), T_FORCES_PROC)) { + propagateFlagsIf(&depVtx, T_FORCES_PROC, [&](const V3GraphEdge* e) -> bool { return !hasFlags(static_cast(e->fromp())->nodep(), T_ALLOCS_PROC); }); } // Mark nodes on paths between processes and statements that use VlProcess - if (hasFlags(depVxp->nodep(), T_NEEDS_PROC)) { - propagateFlagsIf(depVxp, T_NEEDS_PROC, [&](const V3GraphEdge* e) -> bool { + if (hasFlags(depVtx.nodep(), T_NEEDS_PROC)) { + propagateFlagsIf(&depVtx, T_NEEDS_PROC, [&](const V3GraphEdge* e) -> bool { return !hasFlags(static_cast(e->top())->nodep(), T_ALLOCS_PROC); }); } } - for (V3GraphVertex* vxp = m_procGraph.verticesBeginp(); vxp; vxp = vxp->verticesNextp()) { - DepVtx* const depVxp = static_cast(vxp); + for (V3GraphVertex& vtx : m_procGraph.vertices()) { + DepVtx& depVtx = static_cast(vtx); // Mark nodes that will be emitted with a VlProcess argument - if (hasFlags(depVxp->nodep(), T_ALLOCS_PROC | T_FORCES_PROC)) { - addFlags(depVxp->nodep(), T_HAS_PROC); - propagateFlagsReversedIf(depVxp, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool { + if (hasFlags(depVtx.nodep(), T_ALLOCS_PROC | T_FORCES_PROC)) { + addFlags(depVtx.nodep(), T_HAS_PROC); + propagateFlagsReversedIf(&depVtx, T_HAS_PROC, [&](const V3GraphEdge* e) -> bool { return hasFlags(static_cast(e->fromp())->nodep(), T_NEEDS_PROC); }); } diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 5fc03e40a..9bd2e21ad 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -202,8 +202,8 @@ class TraceVisitor final : public VNVisitor { // Note uses user4 V3DupFinder dupFinder; // Duplicate code detection // Hash all of the traced values and find if there are any duplicates - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (TraceTraceVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex& vtx : m_graph.vertices()) { + if (TraceTraceVertex* const vvertexp = vtx.cast()) { const AstTraceDecl* const nodep = vvertexp->nodep(); UASSERT_OBJ(!vvertexp->duplicatep(), nodep, "Should not be a duplicate"); const auto dupit = dupFinder.findDuplicate(nodep->valuep()); @@ -227,9 +227,8 @@ class TraceVisitor final : public VNVisitor { void graphSimplify(bool initial) { if (initial) { // Remove all variable nodes - for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { - nextp = itp->verticesNextp(); - if (TraceVarVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) { + if (TraceVarVertex* const vvertexp = vtxp->cast()) { vvertexp->rerouteEdges(&m_graph); vvertexp->unlinkDelete(&m_graph); } @@ -239,9 +238,8 @@ class TraceVisitor final : public VNVisitor { // expansion. m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); // Remove all Cfunc nodes - for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { - nextp = itp->verticesNextp(); - if (TraceCFuncVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) { + if (TraceCFuncVertex* const vvertexp = vtxp->cast()) { vvertexp->rerouteEdges(&m_graph); vvertexp->unlinkDelete(&m_graph); } @@ -252,44 +250,42 @@ class TraceVisitor final : public VNVisitor { m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); // If there are any edges from a always, keep only the always - for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp; - itp = itp->verticesNextp()) { - if (const TraceTraceVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex& vtx : m_graph.vertices()) { + if (TraceTraceVertex* const vvertexp = vtx.cast()) { // Search for the incoming always edge const V3GraphEdge* alwaysEdgep = nullptr; - for (const V3GraphEdge* edgep = vvertexp->inBeginp(); edgep; - edgep = edgep->inNextp()) { + for (const V3GraphEdge& edge : vvertexp->inEdges()) { const TraceActivityVertex* const actVtxp - = edgep->fromp()->as(); + = edge.fromp()->as(); if (actVtxp->activityAlways()) { - alwaysEdgep = edgep; + alwaysEdgep = &edge; break; } } // If always edge exists, remove all other edges if (alwaysEdgep) { - for (V3GraphEdge *nextp, *edgep = vvertexp->inBeginp(); edgep; edgep = nextp) { - nextp = edgep->inNextp(); - if (edgep != alwaysEdgep) edgep->unlinkDelete(); + for (V3GraphEdge* const edgep : vvertexp->inEdges().unlinkable()) { + if (edgep != alwaysEdgep) VL_DO_DANGLING(edgep->unlinkDelete(), edgep); } } } } // Activity points with no outputs can be removed - for (V3GraphVertex *nextp, *itp = m_graph.verticesBeginp(); itp; itp = nextp) { - nextp = itp->verticesNextp(); - if (TraceActivityVertex* const vtxp = itp->cast()) { + for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) { + if (TraceActivityVertex* const aVtxp = vtxp->cast()) { // Leave in the always vertex for later use. - if (vtxp != m_alwaysVtxp && !vtxp->outBeginp()) vtxp->unlinkDelete(&m_graph); + if (aVtxp != m_alwaysVtxp && aVtxp->outEmpty()) { + VL_DO_DANGLING(aVtxp->unlinkDelete(&m_graph), aVtxp); + } } } } uint32_t assignactivityNumbers() { uint32_t activityNumber = 1; // Note 0 indicates "slow" only - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (TraceActivityVertex* const vvertexp = itp->cast()) { + for (V3GraphVertex& vtx : m_graph.vertices()) { + if (TraceActivityVertex* const vvertexp = vtx.cast()) { if (vvertexp != m_alwaysVtxp) { if (vvertexp->slow()) { vvertexp->activityCode(TraceActivityVertex::ACTIVITY_SLOW); @@ -306,15 +302,14 @@ class TraceVisitor final : public VNVisitor { // Populate sort structure traces.clear(); nNonConstCodes = 0; - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (TraceTraceVertex* const vtxp = itp->cast()) { + for (V3GraphVertex& vtx : m_graph.vertices()) { + if (TraceTraceVertex* const vtxp = vtx.cast()) { ActCodeSet actSet; UINFO(9, " Add to sort: " << vtxp << endl); if (debug() >= 9) vtxp->nodep()->dumpTree("- trnode: "); - for (const V3GraphEdge* edgep = vtxp->inBeginp(); edgep; - edgep = edgep->inNextp()) { + for (const V3GraphEdge& edge : vtxp->inEdges()) { const TraceActivityVertex* const cfvertexp - = edgep->fromp()->cast(); + = edge.fromp()->cast(); UASSERT_OBJ(cfvertexp, vtxp->nodep(), "Should have been function pointing to this trace"); UINFO(9, " Activity: " << cfvertexp << endl); @@ -444,9 +439,8 @@ class TraceVisitor final : public VNVisitor { m_activityVscp = newvscp; // Insert activity setters - for (const V3GraphVertex* itp = m_graph.verticesBeginp(); itp; - itp = itp->verticesNextp()) { - if (const TraceActivityVertex* const vtxp = itp->cast()) { + for (const V3GraphVertex& vtx : m_graph.vertices()) { + if (const TraceActivityVertex* const vtxp = vtx.cast()) { if (vtxp->activitySlow()) { // Just set all flags in slow code as it should be rare. // This will be rolled up into a loop by V3Reloop. diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 455de671f..faacf9a3b 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -217,8 +217,8 @@ private: vtxp->user(1); // Recursed UINFO(9, " Mark tri " << level << " " << vtxp << endl); if (!vtxp->varp()) { // not a var where we stop the recursion - for (V3GraphEdge* edgep = vtxp->outBeginp(); edgep; edgep = edgep->outNextp()) { - TristateVertex* const vvertexp = static_cast(edgep->top()); + for (V3GraphEdge& edge : vtxp->outEdges()) { + TristateVertex* const vvertexp = static_cast(edge.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()) { @@ -229,8 +229,8 @@ private: } 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* const vvertexp = static_cast(edgep->fromp()); + for (V3GraphEdge& edge : vtxp->inEdges()) { + TristateVertex* const vvertexp = static_cast(edge.fromp()); if (const AstVarRef* const refp = VN_CAST(vvertexp->nodep(), VarRef)) { if (refp->access().isWriteOrRW() // Doesn't hurt to not check if already set, but by doing so when we @@ -254,8 +254,8 @@ private: vtxp->user(3); // Recursed UINFO(9, " Mark feedstri " << level << " " << vtxp << endl); if (!vtxp->varp()) { // not a var where we stop the recursion - for (V3GraphEdge* edgep = vtxp->inBeginp(); edgep; edgep = edgep->inNextp()) { - TristateVertex* const vvertexp = static_cast(edgep->fromp()); + for (V3GraphEdge& edge : vtxp->inEdges()) { + TristateVertex* const vvertexp = static_cast(edge.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()) { @@ -270,13 +270,13 @@ public: // METHODS bool empty() const { return m_graph.empty(); } void clear() { - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - const TristateVertex* const vvertexp = static_cast(itp); - if (vvertexp->isTristate() && !vvertexp->processed()) { + for (V3GraphVertex& vtx : m_graph.vertices()) { + const TristateVertex& vvertex = static_cast(vtx); + if (vvertex.isTristate() && !vvertex.processed()) { // Not v3errorSrc as no reason to stop the world - vvertexp->nodep()->v3error("Unsupported tristate construct" - " (in graph; not converted): " - << vvertexp->nodep()->prettyTypeName()); + vvertex.nodep()->v3error("Unsupported tristate construct" + " (in graph; not converted): " + << vvertex.nodep()->prettyTypeName()); } } m_graph.clear(); @@ -284,11 +284,11 @@ public: } void graphWalk(AstNodeModule* nodep) { UINFO(9, " Walking " << nodep << endl); - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - graphWalkRecurseFwd(static_cast(itp), 0); + for (V3GraphVertex& vtx : m_graph.vertices()) { + graphWalkRecurseFwd(static_cast(&vtx), 0); } - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - graphWalkRecurseBack(static_cast(itp), 0); + for (V3GraphVertex& vtx : m_graph.vertices()) { + graphWalkRecurseBack(static_cast(&vtx), 0); } if (dumpGraphLevel() >= 9) m_graph.dumpDotFilePrefixed("tri_pos__" + nodep->name()); } @@ -332,10 +332,10 @@ public: VarVec tristateVars() { // Return all tristate variables VarVec v; - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - const TristateVertex* const vvertexp = static_cast(itp); - if (vvertexp->isTristate()) { - if (AstVar* const nodep = VN_CAST(vvertexp->nodep(), Var)) v.push_back(nodep); + for (const V3GraphVertex& vtx : m_graph.vertices()) { + const TristateVertex& vvertex = static_cast(vtx); + if (vvertex.isTristate()) { + if (AstVar* const nodep = VN_CAST(vvertex.nodep(), Var)) v.push_back(nodep); } } return v; diff --git a/src/V3VariableOrder.cpp b/src/V3VariableOrder.cpp index 84671eb28..822ea86cb 100644 --- a/src/V3VariableOrder.cpp +++ b/src/V3VariableOrder.cpp @@ -275,9 +275,8 @@ void V3VariableOrder::orderAll(AstNetlist* netlistp) { // Gather MTask affinities if (v3Global.opt.mtasks()) { netlistp->topModulep()->foreach([&](AstExecGraph* execGraphp) { - for (const V3GraphVertex* vtxp = execGraphp->depGraphp()->verticesBeginp(); vtxp; - vtxp = vtxp->verticesNextp()) { - GatherMTaskAffinity::apply(vtxp->as(), mTaskAffinity); + for (const V3GraphVertex& vtx : execGraphp->depGraphp()->vertices()) { + GatherMTaskAffinity::apply(vtx.as(), mTaskAffinity); } }); }