diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index 4d2a8e342..af50685a2 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -223,20 +223,26 @@ void DfgGraph::mergeGraphs(std::vector>&& otherps) { } // Otherwise they will be moved vtxp->nodep()->user2p(vtxp); - vtxp->m_userCnt = 0; - vtxp->m_graphp = this; + vtxp->m_userGeneration = 0; +#ifdef VL_DEBUG + vtxp->m_dfgp = this; +#endif } m_varVertices.splice(m_varVertices.end(), otherp->m_varVertices); // Process constants for (DfgConst& vtx : otherp->m_constVertices) { - vtx.m_userCnt = 0; - vtx.m_graphp = this; + vtx.m_userGeneration = 0; +#ifdef VL_DEBUG + vtx.m_dfgp = this; +#endif } m_constVertices.splice(m_constVertices.end(), otherp->m_constVertices); // Process operations for (DfgVertex& vtx : otherp->m_opVertices) { - vtx.m_userCnt = 0; - vtx.m_graphp = this; + vtx.m_userGeneration = 0; +#ifdef VL_DEBUG + vtx.m_dfgp = this; +#endif } m_opVertices.splice(m_opVertices.end(), otherp->m_opVertices); // Update graph sizes @@ -586,8 +592,8 @@ bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const { return result >> 1; } -V3Hash DfgVertex::hash() { - V3Hash& result = user(); +V3Hash DfgVertex::hash(DfgUserMap& cache) { + V3Hash& result = cache[this]; if (!result.value()) { V3Hash hash{selfHash()}; // Variables are defined by themselves, so there is no need to hash them further @@ -597,7 +603,7 @@ V3Hash DfgVertex::hash() { hash += m_type; hash += size(); foreachSource([&](DfgVertex& vtx) { - hash += vtx.hash(); + hash += vtx.hash(cache); return false; }); } diff --git a/src/V3Dfg.h b/src/V3Dfg.h index bab4a0498..62fc47494 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -54,10 +54,18 @@ #define VL_NOT_FINAL // This #define fixes broken code folding in the CLion IDE #endif +// Can T be stored in the memory allocted for U? +template +inline constexpr bool fitsSpaceAllocatedFor() { + return sizeof(T) <= sizeof(U) && alignof(T) <= alignof(U); +} + class DfgEdge; class DfgVertex; class DfgGraph; class DfgVisitor; +template ()> +class DfgUserMap; // Specialization of std::hash for a std::pair for use below template <> @@ -191,8 +199,8 @@ class DfgVertex VL_NOT_FINAL { friend class DfgGraph; friend class DfgEdge; friend class DfgVisitor; - - using UserDataStorage = void*; // Storage allocated for user data + template + friend class DfgUserMap; // STATE V3ListLinks m_links; // V3List links in the DfgGraph @@ -201,10 +209,16 @@ class DfgVertex VL_NOT_FINAL { FileLine* const m_filelinep; // Source location AstNodeDType* m_dtypep; // Data type of the result of this vertex - mutable for efficiency - DfgGraph* m_graphp; // The containing DfgGraph const VDfgType m_type; // Vertex type tag - uint32_t m_userCnt = 0; // User data generation number - UserDataStorage m_userDataStorage = nullptr; // User data storage + + // The only way to access thes is via DfgUserMap, so mutable is appropriate, + // the map can change while the keys (DfgVertex) are const. + mutable uint32_t m_userGeneration = 0; // User data generation number + mutable void* m_userStorage = nullptr; // User data storage - one pointer worth + +#ifdef VL_DEBUG + DfgGraph* m_dfgp = nullptr; // Graph this vertex belongs to +#endif // METHODS // Visitor accept method @@ -252,8 +266,10 @@ public: FileLine* fileline() const { return m_filelinep; } // The data type of the result of the vertex AstNodeDType* dtypep() const { return m_dtypep; } - // Is it a packed type (instead of an array) + // Is it a packed type bool isPacked() const { return VN_IS(dtypep(), BasicDType); } + // Is it an array type + bool isArray() const { return !isPacked(); } // Width of result uint32_t width() const { UASSERT_OBJ(isPacked(), this, "non-packed has no 'width()'"); @@ -265,19 +281,6 @@ public: return VN_AS(dtypep(), UnpackArrayDType)->elementsConst(); } - // Retrieve user data, constructing it fresh on first try. - template - T_User& user(); - // Retrieve user data, must be current. - template - const T_User& getUser() const; - // Retrieve user data, must be current. - template - T_User& getUser(); - // Set user data, becomes current. - template - void setUser(T_User value); - // Cache type for 'equals' below using EqualsCache = std::unordered_map, uint8_t>; @@ -295,8 +298,8 @@ public: } // Hash of vertex (depends on this vertex and all upstream vertices feeding into this vertex). - // Uses user data for caching hashes - V3Hash hash() VL_MT_DISABLED; + // Uses the given DfgUserMap for caching hashes + V3Hash hash(DfgUserMap& cache) VL_MT_DISABLED; // Predicate: has 1 or more sinks bool hasSinks() const { return !m_sinks.empty(); } @@ -465,34 +468,7 @@ public: //------------------------------------------------------------------------------ // 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; - } - }; + friend class DfgUserMapBase; // MEMBERS @@ -503,16 +479,18 @@ class DfgGraph final { 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), // or nullptr when run after V3Scope AstModule* const m_modulep; const std::string m_name; // Name of graph - need not be unique std::string m_tmpNameStub{""}; // Name stub for temporary variables - computed lazy + // The only way to access thes is via DfgUserMap, so mutable is appropriate, + // the map can change while the graph is const. + mutable bool m_vertexUserInUse = false; // Vertex user data currently in use + mutable uint32_t m_vertexUserGeneration = 0; // Vertex user data generation counter + public: // CONSTRUCTOR explicit DfgGraph(AstModule* modulep, const string& name = "") VL_MT_DISABLED; @@ -528,14 +506,9 @@ public: // 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}; - } + // Create a new DfgUserMap + template + inline DfgUserMap makeUserMap() const; // Access to vertex lists DfgVertex::List& varVertices() { return m_varVertices; } @@ -547,6 +520,9 @@ public: // Add DfgVertex to this graph (assumes not yet contained). void addVertex(DfgVertex& vtx) { +#ifdef VL_DEBUG + UASSERT_OBJ(!vtx.m_dfgp, &vtx, "Vertex already in a graph"); +#endif // Note: changes here need to be replicated in DfgGraph::mergeGraphs ++m_size; if (DfgConst* const cVtxp = vtx.cast()) { @@ -556,12 +532,17 @@ public: } else { m_opVertices.linkBack(&vtx); } - vtx.m_userCnt = 0; - vtx.m_graphp = this; + vtx.m_userGeneration = 0; +#ifdef VL_DEBUG + vtx.m_dfgp = this; +#endif } // Remove DfgVertex form this graph (assumes it is contained). void removeVertex(DfgVertex& vtx) { +#ifdef VL_DEBUG + UASSERT_OBJ(vtx.m_dfgp == this, &vtx, "Vertex not in this graph"); +#endif // Note: changes here need to be replicated in DfgGraph::mergeGraphs --m_size; if (DfgConst* const cVtxp = vtx.cast()) { @@ -571,8 +552,10 @@ public: } else { m_opVertices.unlink(&vtx); } - vtx.m_userCnt = 0; - vtx.m_graphp = nullptr; + vtx.m_userGeneration = 0; +#ifdef VL_DEBUG + vtx.m_dfgp = nullptr; +#endif } // Calls given function 'f' for each vertex in the graph. It is safe to manipulate any vertices @@ -655,6 +638,137 @@ public: sinkCone(const std::vector&) const VL_MT_DISABLED; }; +//------------------------------------------------------------------------------ +// Map from DfgVertices to T_Value implemeneted via DfgVertex::m_userStorage + +// Base class with common behavour +class DfgUserMapBase VL_NOT_FINAL { + template + friend class DfgUserMap; + +protected: + // STATE + const DfgGraph* m_dfgp; // The graph this map is for + // The current generation number + const uint32_t m_currentGeneration; + + // CONSTRUCTOR + explicit DfgUserMapBase(const DfgGraph* dfgp) + : m_dfgp{dfgp} + , m_currentGeneration{++m_dfgp->m_vertexUserGeneration} { + UASSERT(m_currentGeneration, "DfgGraph user data genartion number overflow"); + UASSERT(!m_dfgp->m_vertexUserInUse, "DfgUserMap already in use for this DfgGraph"); + m_dfgp->m_vertexUserInUse = true; + } + VL_UNCOPYABLE(DfgUserMapBase); + DfgUserMapBase(DfgUserMapBase&& that) + : m_dfgp{that.m_dfgp} + , m_currentGeneration{that.m_currentGeneration} { + that.m_dfgp = nullptr; + } + DfgUserMapBase& operator=(DfgUserMapBase&&) = delete; + +public: + ~DfgUserMapBase() { + if (m_dfgp) m_dfgp->m_vertexUserInUse = false; + } +}; + +// Specialization where T_Value fits in DfgVertex::m_userStorage directly +template +class DfgUserMap final : public DfgUserMapBase { + static_assert(fitsSpaceAllocatedFor(), + "'T_Value' does not fit 'DfgVertex::m_userStorage'"); + friend class DfgGraph; + + // CONSTRUCTOR + explicit DfgUserMap(const DfgGraph* dfgp) + : DfgUserMapBase{dfgp} {} + VL_UNCOPYABLE(DfgUserMap); + DfgUserMap& operator=(DfgUserMap&&) = delete; + +public: + DfgUserMap(DfgUserMap&&) = default; + ~DfgUserMap() = default; + + // METHODS + // Retrieve mapped value for 'vtx', value initializing it on first access + T_Value& operator[](const DfgVertex& vtx) { +#ifdef VL_DEBUG + UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph"); +#endif + T_Value* const storagep = reinterpret_cast(&vtx.m_userStorage); + if (vtx.m_userGeneration != m_currentGeneration) { + new (storagep) T_Value{}; + vtx.m_userGeneration = m_currentGeneration; + } + return *storagep; + } + // Same as above with pointer as key + T_Value& operator[](const DfgVertex* vtxp) { return (*this)[*vtxp]; } + + // Retrieve mapped value of 'vtx', must be alerady present + T_Value& at(const DfgVertex& vtx) const { +#ifdef VL_DEBUG + UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph"); +#endif + UASSERT_OBJ(vtx.m_userGeneration == m_currentGeneration, &vtx, "Vertex not in map"); + T_Value* const storagep = reinterpret_cast(&vtx.m_userStorage); + return *storagep; + } + // Same as above with pointer as key + T_Value& at(const DfgVertex* vtxp) const { return (*this).at(*vtxp); } +}; + +// Specialization where T_Value does not fit in DfgVertex::m_userStorage directly +template +class DfgUserMap final : public DfgUserMapBase { + static_assert(fitsSpaceAllocatedFor(), + "'T_Value*' does not fit 'DfgVertex::m_userStorage'"); + friend class DfgGraph; + + // STATE + std::deque m_storage; // Storage for T_Value instances + + // CONSTRUCTOR + explicit DfgUserMap(const DfgGraph* dfgp) + : DfgUserMapBase{dfgp} {} + VL_UNCOPYABLE(DfgUserMap); + DfgUserMap& operator=(DfgUserMap&&) = delete; + +public: + DfgUserMap(DfgUserMap&&) = default; + ~DfgUserMap() = default; + + // METHODS + // Retrieve mapped value for 'vtx', value initializing it on first access + T_Value& operator[](const DfgVertex& vtx) { +#ifdef VL_DEBUG + UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph"); +#endif + T_Value*& storagepr = reinterpret_cast(vtx.m_userStorage); + if (vtx.m_userGeneration != m_currentGeneration) { + m_storage.emplace_back(); + storagepr = &m_storage.back(); + vtx.m_userGeneration = m_currentGeneration; + } + return *storagepr; + } + // Same as above with pointer as key + T_Value& operator[](const DfgVertex* vtxp) { return (*this)[*vtxp]; } + + // Retrieve mapped value of 'vtx', must be alerady present + T_Value& at(const DfgVertex& vtx) const { +#ifdef VL_DEBUG + UASSERT_OBJ(vtx.m_dfgp == m_dfgp, &vtx, "Vertex not in this graph"); +#endif + UASSERT_OBJ(vtx.m_userGeneration == m_currentGeneration, &vtx, "Vertex not in map"); + return *reinterpret_cast(vtx.m_userStorage); + } + // Same as above with pointer as key + T_Value& at(const DfgVertex* vtxp) const { return (*this).at(*vtxp); } +}; + //------------------------------------------------------------------------------ // Inline method definitions @@ -684,57 +798,11 @@ void DfgEdge::relinkSrcp(DfgVertex* srcp) { // }}} -// DfgVertex {{{ +// DfgGraph {{{ template -T_User& DfgVertex::user() { - static_assert(sizeof(T_User) <= sizeof(UserDataStorage), - "Size of user data type 'T_User' is too large for allocated storage"); - static_assert(alignof(T_User) <= alignof(UserDataStorage), - "Alignment of user data type 'T_User' is larger than allocated storage"); - T_User* 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; - new (storagep) T_User{}; - } - return *storagep; -} - -template -const T_User& DfgVertex::getUser() const { - static_assert(sizeof(T_User) <= sizeof(UserDataStorage), - "Size of user data type 'T_User' is too large for allocated storage"); - static_assert(alignof(T_User) <= alignof(UserDataStorage), - "Alignment of user data type 'T_User' is larger than allocated storage"); - const T_User* 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; -} - -template -T_User& DfgVertex::getUser() { - return const_cast(const_cast(this)->getUser()); -} - -template -void DfgVertex::setUser(T_User value) { - static_assert(sizeof(T_User) <= sizeof(UserDataStorage), - "Size of user data type 'T_User' is too large for allocated storage"); - static_assert(alignof(T_User) <= alignof(UserDataStorage), - "Alignment of user data type 'T_User' is larger than allocated storage"); - T_User* 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; +DfgUserMap DfgGraph::makeUserMap() const { + return DfgUserMap{this}; } // }}} diff --git a/src/V3DfgBreakCycles.cpp b/src/V3DfgBreakCycles.cpp index c2b35a92b..4a0ff6526 100644 --- a/src/V3DfgBreakCycles.cpp +++ b/src/V3DfgBreakCycles.cpp @@ -29,6 +29,9 @@ VL_DEFINE_DEBUG_FUNCTIONS; +// Throughout these algorithm, we use the DfgUserMap as a map to the SCC number +using Vtx2Scc = DfgUserMap; + class TraceDriver final : public DfgVisitor { // TYPES @@ -63,6 +66,7 @@ class TraceDriver final : public DfgVisitor { // STATE DfgGraph& m_dfg; // The graph being processed + Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map // The strongly connected component we are trying to escape const uint64_t m_component; const bool m_aggressive; // Trace aggressively, creating intermediate ops @@ -110,7 +114,7 @@ class TraceDriver final : public DfgVisitor { if VL_CONSTEXPR_CXX17 (std::is_same::value) { DfgConst* const vtxp = new DfgConst{m_dfg, refp->fileline(), width, 0}; - vtxp->template setUser(0); + m_vtx2Scc[vtxp] = 0; m_newVtxps.emplace_back(vtxp); return reinterpret_cast(vtxp); } else { @@ -121,7 +125,7 @@ class TraceDriver final : public DfgVisitor { Vertex>::type; AstNodeDType* const dtypep = V3Dfg::dtypePacked(width); Vtx* const vtxp = new Vtx{m_dfg, refp->fileline(), dtypep}; - vtxp->template setUser(0); + m_vtx2Scc[vtxp] = 0; m_newVtxps.emplace_back(vtxp); return reinterpret_cast(vtxp); } @@ -163,7 +167,7 @@ class TraceDriver final : public DfgVisitor { // found what we were looking for. However, keep going past a splice, // or a unit as they cannot be use directly (they must always feed into // a variable, so we can't make them drive arbitrary logic) - if (vtxp->getUser() != m_component // + if (m_vtx2Scc[vtxp] != m_component // && !vtxp->is() // && !vtxp->is()) { if (msb != vtxp->width() - 1 || lsb != 0) { @@ -614,8 +618,9 @@ class TraceDriver final : public DfgVisitor { #undef SET_RESULT // CONSTRUCTOR - TraceDriver(DfgGraph& dfg, uint64_t component, bool aggressive) + TraceDriver(DfgGraph& dfg, Vtx2Scc& vtx2Scc, uint64_t component, bool aggressive) : m_dfg{dfg} + , m_vtx2Scc{vtx2Scc} , m_component{component} , m_aggressive{aggressive} { #ifdef VL_DEBUG @@ -629,18 +634,18 @@ class TraceDriver final : public DfgVisitor { } public: - // Given a Vertex that is part of an SCC denoted by vtxp->user(), + // Given a Vertex that is part of an SCC denoted by vtx2Scc, // return a vertex that is equivalent to 'vtxp[lsb +: width]', but is not // part of the same SCC. Returns nullptr if such a vertex cannot be // computed. This can add new vertices to the graph. The 'aggressive' flag // enables creating many intermediate operations. This should only be set // if it is reasonably certain the tracing will succeed, otherwise we can // waste a lot of compute. - static DfgVertex* apply(DfgGraph& dfg, DfgVertex* vtxp, uint32_t lsb, uint32_t width, - bool aggressive) { - TraceDriver traceDriver{dfg, vtxp->getUser(), aggressive}; + static DfgVertex* apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertex& vtx, uint32_t lsb, + uint32_t width, bool aggressive) { + TraceDriver traceDriver{dfg, vtx2Scc, vtx2Scc[vtx], aggressive}; // Find the out-of-component driver of the given vertex - DfgVertex* const resultp = traceDriver.trace(vtxp, lsb + width - 1, lsb); + DfgVertex* const resultp = traceDriver.trace(&vtx, lsb + width - 1, lsb); // Delete unused newly created vertices (these can be created if a // partial trace succeded, but an eventual one falied). Because new // vertices should be created depth first, it is enough to do a single @@ -660,6 +665,7 @@ public: class IndependentBits final : public DfgVisitor { // STATE + const Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map const uint64_t m_component; // The component the start vertex is part of // Vertex to current bit mask map. The mask is set for the bits that **depend** on 'm_varp'. std::unordered_map m_vtxp2Mask; @@ -675,9 +681,7 @@ class IndependentBits final : public DfgVisitor { std::forward_as_tuple(vtxp), // std::forward_as_tuple(vtxp->fileline(), static_cast(vtxp->width()), 0)); // Initialize to all ones if the vertex is part of the same component, otherwise zeroes - if (pair.second && vtxp->getUser() == m_component) { - pair.first->second.setAllBits1(); - } + if (pair.second && m_vtx2Scc.at(vtxp) == m_component) { pair.first->second.setAllBits1(); } return pair.first->second; } @@ -910,8 +914,9 @@ class IndependentBits final : public DfgVisitor { #undef MASK // CONSTRUCTOR - IndependentBits(DfgGraph& dfg, DfgVertex* vtxp) - : m_component{vtxp->getUser()} { + IndependentBits(DfgGraph& dfg, const Vtx2Scc& vtx2Scc, DfgVertex& vertex) + : m_vtx2Scc{vtx2Scc} + , m_component{m_vtx2Scc.at(vertex)} { #ifdef VL_DEBUG if (v3Global.opt.debugCheck()) { @@ -927,7 +932,7 @@ class IndependentBits final : public DfgVisitor { // Enqueue every operation vertex in the analysed component for (DfgVertex& vtx : dfg.opVertices()) { - if (vtx.getUser() == m_component) workList.emplace_back(&vtx); + if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx); } // While there is an item on the worklist ... @@ -940,7 +945,7 @@ class IndependentBits final : public DfgVisitor { // For an unpacked array vertex, just enque it's sinks. // (There can be no loops through arrays directly) currp->foreachSink([&](DfgVertex& vtx) { - if (vtx.getUser() == m_component) workList.emplace_back(&vtx); + if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx); return false; }); continue; @@ -957,7 +962,7 @@ class IndependentBits final : public DfgVisitor { // If mask changed, enqueue sinks if (!prevMask.isCaseEq(maskCurr)) { currp->foreachSink([&](DfgVertex& vtx) { - if (vtx.getUser() == m_component) workList.emplace_back(&vtx); + if (m_vtx2Scc.at(vtx) == m_component) workList.emplace_back(&vtx); return false; }); @@ -981,62 +986,78 @@ public: // independent of the vertex itself (logic is not circular). The result is // a conservative estimate, so bits reported dependent might not actually // be, but all bits reported independent are known to be so. - static V3Number apply(DfgGraph& dfg, DfgVertex* vtxp) { - IndependentBits independentBits{dfg, vtxp}; + static V3Number apply(DfgGraph& dfg, const Vtx2Scc& vtx2Scc, DfgVertex& vtx) { + IndependentBits independentBits{dfg, vtx2Scc, vtx}; // The mask represents the dependent bits, so invert it - V3Number result{vtxp->fileline(), static_cast(vtxp->width()), 0}; - result.opNot(independentBits.mask(vtxp)); + V3Number result{vtx.fileline(), static_cast(vtx.width()), 0}; + result.opNot(independentBits.mask(&vtx)); return result; } }; class FixUpSelDrivers final { - static size_t fixUpSelSinks(DfgGraph& dfg, DfgVertex* vtxp) { - size_t nImprovements = 0; - const uint64_t component = vtxp->getUser(); - vtxp->foreachSink([&](DfgVertex& sink) { + DfgGraph& m_dfg; // The graph being processed + Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map + size_t m_nImprovements = 0; // Number of improvements mde + + void fixUpSelSinks(DfgVertex& vtx) { + const uint64_t component = m_vtx2Scc[vtx]; + vtx.foreachSink([&](DfgVertex& sink) { // Ignore if sink is not part of same cycle - if (sink.getUser() != component) return false; + if (m_vtx2Scc[sink] != component) return false; // Only handle Sel DfgSel* const selp = sink.cast(); if (!selp) return false; // Try to find the driver of the selected bits outside the cycle DfgVertex* const fixp - = TraceDriver::apply(dfg, vtxp, selp->lsb(), selp->width(), false); + = TraceDriver::apply(m_dfg, m_vtx2Scc, vtx, selp->lsb(), selp->width(), false); if (!fixp) return false; // Found an out-of-cycle driver. We can replace this sel with that. selp->replaceWith(fixp); - selp->unlinkDelete(dfg); - ++nImprovements; + selp->unlinkDelete(m_dfg); + ++m_nImprovements; return false; }); - return nImprovements; } - static size_t fixUpArraySelSinks(DfgGraph& dfg, DfgVertex* vtxp) { - size_t nImprovements = 0; - const uint64_t component = vtxp->getUser(); - vtxp->foreachSink([&](DfgVertex& sink) { + void fixUpArraySelSinks(DfgVertex& vtx) { + const uint64_t component = m_vtx2Scc[vtx]; + vtx.foreachSink([&](DfgVertex& sink) { // Ignore if sink is not part of same cycle - if (sink.getUser() != component) return false; + if (m_vtx2Scc[sink] != component) return false; // Only handle ArraySels - DfgArraySel* const aselp = sink.cast(); - if (!aselp) return false; + DfgArraySel* const asp = sink.cast(); + if (!asp) return false; // First, try to fix up the whole word - if (DfgVertex* const fixp = TraceDriver::apply(dfg, aselp, 0, aselp->width(), false)) { + DfgVertex* const fixp + = TraceDriver::apply(m_dfg, m_vtx2Scc, *asp, 0, asp->width(), false); + if (fixp) { // Found an out-of-cycle driver for the whole ArraySel - aselp->replaceWith(fixp); - aselp->unlinkDelete(dfg); - ++nImprovements; + asp->replaceWith(fixp); + asp->unlinkDelete(m_dfg); + ++m_nImprovements; return false; } // Attempt to fix up piece-wise at Sels applied to the ArraySel - nImprovements += fixUpSelSinks(dfg, aselp); + fixUpSelSinks(*asp); // Remove if became unused - if (!aselp->hasSinks()) aselp->unlinkDelete(dfg); + if (!asp->hasSinks()) asp->unlinkDelete(m_dfg); return false; }); - return nImprovements; + } + + FixUpSelDrivers(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) + : m_dfg{dfg} + , m_vtx2Scc{vtx2Scc} { + UINFO(9, "FixUpSelDrivers of " << var.nodep()->name()); + if (var.isPacked()) { + // For Packed variables, fix up all Sels applied to it + fixUpSelSinks(var); + } else if (var.isArray()) { + // For Array variables, fix up each ArraySel applied to it + fixUpArraySelSinks(var); + } + UINFO(9, "FixUpSelDrivers made " << m_nImprovements << " improvements"); } public: @@ -1044,26 +1065,20 @@ public: // expression of the selected bits. This can fix things like // 'a[1:0] = foo', 'a[2] = a[1]', which are somewhat common. // Returns the number of drivers fixed up. - static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) { - UINFO(9, "FixUpSelDrivers of " << varp->nodep()->name()); - size_t nImprovements = 0; - if (varp->is()) { - // For Packed variables, fix up all Sels applied to it - nImprovements += fixUpSelSinks(dfg, varp); - } else if (varp->is()) { - // For Array variables, fix up each ArraySel applied to it - nImprovements += fixUpArraySelSinks(dfg, varp); - } - UINFO(9, "FixUpSelDrivers made " << nImprovements << " improvements"); - return nImprovements; + static size_t apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) { + return FixUpSelDrivers{dfg, vtx2Scc, var}.m_nImprovements; } }; class FixUpIndependentRanges final { - // Returns a bitmask set if that bit of 'vtxp' is used (has a sink) - static V3Number computeUsedBits(DfgVertex* vtxp) { - V3Number result{vtxp->fileline(), static_cast(vtxp->width()), 0}; - vtxp->foreachSink([&result](DfgVertex& sink) { + DfgGraph& m_dfg; // The graph being processed + Vtx2Scc& m_vtx2Scc; // The Vertex to SCC map + size_t m_nImprovements = 0; // Number of improvements mde + + // Returns a bitmask set if that bit of 'vtx' is used (has a sink) + static V3Number computeUsedBits(DfgVertex& vtx) { + V3Number result{vtx.fileline(), static_cast(vtx.width()), 0}; + vtx.foreachSink([&result](DfgVertex& sink) { // If used via a Sel, mark the selected bits used if (const DfgSel* const selp = sink.cast()) { uint32_t lsb = selp->lsb(); @@ -1078,24 +1093,22 @@ class FixUpIndependentRanges final { return result; } - static std::string debugStr(const DfgVertex* vtxp) { - if (const DfgArraySel* const aselp = vtxp->cast()) { + static std::string debugStr(const DfgVertex& vtx) { + if (const DfgArraySel* const aselp = vtx.cast()) { const size_t i = aselp->bitp()->as()->toSizeT(); - return debugStr(aselp->fromp()) + "[" + std::to_string(i) + "]"; + return debugStr(*aselp->fromp()) + "[" + std::to_string(i) + "]"; } - if (const DfgVertexVar* const varp = vtxp->cast()) { + if (const DfgVertexVar* const varp = vtx.cast()) { return varp->nodep()->name(); } - vtxp->v3fatalSrc("Unhandled node type"); // LCOV_EXCL_LINE + vtx.v3fatalSrc("Unhandled node type"); // LCOV_EXCL_LINE } // Trace drivers of independent bits of 'vtxp' in the range '[hi:lo]' // append replacement terms to 'termps'. Returns number of successful // replacements. - static size_t gatherTerms(std::vector& termps, DfgGraph& dfg, - DfgVertex* const vtxp, const V3Number& indpBits, const uint32_t hi, - const uint32_t lo) { - size_t nImprovements = 0; + void gatherTerms(std::vector& termps, DfgVertex& vtx, const V3Number& indpBits, + const uint32_t hi, const uint32_t lo) { // Iterate through consecutive dependent/non-dependet ranges within [hi:lo] bool isIndependent = indpBits.bitIs1(lo); uint32_t lsb = lo; @@ -1106,20 +1119,20 @@ class FixUpIndependentRanges final { DfgVertex* termp = nullptr; // If the range is not self-dependent, attempt to trace its driver if (isIndependent) { - termp = TraceDriver::apply(dfg, vtxp, lsb, width, true); + termp = TraceDriver::apply(m_dfg, m_vtx2Scc, vtx, lsb, width, true); if (termp) { - ++nImprovements; + ++m_nImprovements; } else { UINFO(5, "TraceDriver of independent range failed for " - << debugStr(vtxp) << "[" << msb << ":" << lsb << "]"); + << debugStr(vtx) << "[" << msb << ":" << lsb << "]"); } } // Fall back on using the part of the variable (if dependent, or trace failed) if (!termp) { AstNodeDType* const dtypep = V3Dfg::dtypePacked(width); - DfgSel* const selp = new DfgSel{dfg, vtxp->fileline(), dtypep}; + DfgSel* const selp = new DfgSel{m_dfg, vtx.fileline(), dtypep}; // Same component as 'vtxp', as reads 'vtxp' and will replace 'vtxp' - selp->setUser(vtxp->getUser()); + m_vtx2Scc[selp] = m_vtx2Scc.at(vtx); // Do not connect selp->fromp yet, need to do afer replacing 'vtxp' selp->lsb(lsb); termp = selp; @@ -1130,40 +1143,38 @@ class FixUpIndependentRanges final { isIndependent = !isIndependent; lsb = msb + 1; } - return nImprovements; } - static size_t fixUpPacked(DfgGraph& dfg, DfgVertex* vtxp) { - UASSERT_OBJ(VN_IS(vtxp->dtypep(), BasicDType), vtxp, "Should be a packed BasicDType"); - size_t nImprovements = 0; + void fixUpPacked(DfgVertex& vtx) { + UASSERT_OBJ(vtx.isPacked(), &vtx, "Should be a packed type"); // Figure out which bits of 'vtxp' are used - const V3Number usedBits = computeUsedBits(vtxp); - UINFO(9, "Used bits of '" << debugStr(vtxp) << "' are " - << usedBits.displayed(vtxp->fileline(), "%b")); + const V3Number usedBits = computeUsedBits(vtx); + UINFO(9, "Used bits of '" << debugStr(vtx) << "' are " + << usedBits.displayed(vtx.fileline(), "%b")); // Nothing to do if no bits are used - if (usedBits.isEqZero()) return 0; + if (usedBits.isEqZero()) return; // Figure out which bits of 'vtxp' are dependent of themselves - const V3Number indpBits = IndependentBits::apply(dfg, vtxp); - UINFO(9, "Independent bits of '" << debugStr(vtxp) << "' are " - << indpBits.displayed(vtxp->fileline(), "%b")); + const V3Number indpBits = IndependentBits::apply(m_dfg, m_vtx2Scc, vtx); + UINFO(9, "Independent bits of '" << debugStr(vtx) << "' are " + << indpBits.displayed(vtx.fileline(), "%b")); // Can't do anything if all bits are dependent - if (indpBits.isEqZero()) return 0; + if (indpBits.isEqZero()) return; { // Nothing to do if no used bits are independen (all used bits are dependent) - V3Number usedAndIndependent{vtxp->fileline(), static_cast(vtxp->width()), 0}; + V3Number usedAndIndependent{vtx.fileline(), static_cast(vtx.width()), 0}; usedAndIndependent.opAnd(usedBits, indpBits); - if (usedAndIndependent.isEqZero()) return 0; + if (usedAndIndependent.isEqZero()) return; } // We are computing the terms to concatenate and replace 'vtxp' with std::vector termps; // Iterate through consecutive used/unused ranges - FileLine* const flp = vtxp->fileline(); - const uint32_t width = vtxp->width(); + FileLine* const flp = vtx.fileline(); + const uint32_t width = vtx.width(); bool isUsed = usedBits.bitIs1(0); // Is current range used uint32_t lsb = 0; // LSB of current range for (uint32_t msb = 0; msb < width; ++msb) { @@ -1171,11 +1182,11 @@ class FixUpIndependentRanges final { if (!endRange) continue; if (isUsed) { // The range is used, compute the replacement terms - nImprovements += gatherTerms(termps, dfg, vtxp, indpBits, msb, lsb); + gatherTerms(termps, vtx, indpBits, msb, lsb); } else { // The range is not used, just use constant 0 as a placeholder - DfgConst* const constp = new DfgConst{dfg, flp, msb - lsb + 1, 0}; - constp->setUser(0); + DfgConst* const constp = new DfgConst{m_dfg, flp, msb - lsb + 1, 0}; + m_vtx2Scc[constp] = 0; termps.emplace_back(constp); } // Next iteration @@ -1184,21 +1195,21 @@ class FixUpIndependentRanges final { } // If no imporovement was possible, delete the terms we just created - if (!nImprovements) { - for (DfgVertex* const termp : termps) VL_DO_DANGLING(termp->unlinkDelete(dfg), termp); + if (!m_nImprovements) { + for (DfgVertex* const tp : termps) VL_DO_DANGLING(tp->unlinkDelete(m_dfg), tp); termps.clear(); - return 0; + return; } // We managed to imporove something, apply the replacement // Concatenate all the terms to create the replacement DfgVertex* replacementp = termps.front(); - const uint64_t vComp = vtxp->getUser(); + const uint64_t vComp = m_vtx2Scc.at(vtx); for (size_t i = 1; i < termps.size(); ++i) { DfgVertex* const termp = termps[i]; const uint32_t catWidth = replacementp->width() + termp->width(); AstNodeDType* const dtypep = V3Dfg::dtypePacked(catWidth); - DfgConcat* const catp = new DfgConcat{dfg, flp, dtypep}; + DfgConcat* const catp = new DfgConcat{m_dfg, flp, dtypep}; catp->rhsp(replacementp); catp->lhsp(termp); // Need to figure out which component the replacement vertex @@ -1206,56 +1217,61 @@ class FixUpIndependentRanges final { // component, it's part of that component, otherwise its not // cyclic (all terms are from outside the original component, // and feed into the original component). - const uint64_t tComp = termp->getUser(); - const uint64_t rComp = replacementp->getUser(); - catp->setUser(tComp == vComp || rComp == vComp ? vComp : 0); + const uint64_t tComp = m_vtx2Scc.at(termp); + const uint64_t rComp = m_vtx2Scc.at(replacementp); + m_vtx2Scc[catp] = tComp == vComp || rComp == vComp ? vComp : 0; replacementp = catp; } // Replace the vertex - vtxp->replaceWith(replacementp); + vtx.replaceWith(replacementp); // Connect the Sel nodes in the replacement for (DfgVertex* const termp : termps) { if (DfgSel* const selp = termp->cast()) { - if (!selp->fromp()) selp->fromp(vtxp); + if (!selp->fromp()) selp->fromp(&vtx); } } + } - // Done - return nImprovements; + void main(DfgVertexVar& var) { + UINFO(9, "FixUpIndependentRanges of " << var.nodep()->name()); + + if (var.is()) { + // For Packed variables, fix up as whole + fixUpPacked(var); + } else if (var.is()) { + // For array variables, fix up element-wise + const uint64_t component = m_vtx2Scc.at(var); + var.foreachSink([&](DfgVertex& sink) { + // Ignore if sink is not part of same cycle + if (m_vtx2Scc.at(sink) != component) return false; + // Only handle ArraySels with constant index + DfgArraySel* const aselp = sink.cast(); + if (!aselp) return false; + if (!aselp->bitp()->is()) return false; + // Fix up the word + fixUpPacked(*aselp); + // Remove if became unused + if (!aselp->hasSinks()) aselp->unlinkDelete(m_dfg); + return false; + }); + } + + UINFO(9, "FixUpIndependentRanges made " << m_nImprovements << " improvements"); + } + + FixUpIndependentRanges(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) + : m_dfg{dfg} + , m_vtx2Scc{vtx2Scc} { + main(var); } public: // Similar to FixUpSelDrivers, but first comptute which bits of the // variable are self dependent, and fix up those that are independent // but used. - static size_t apply(DfgGraph& dfg, DfgVertexVar* varp) { - UINFO(9, "FixUpIndependentRanges of " << varp->nodep()->name()); - size_t nImprovements = 0; - - if (varp->is()) { - // For Packed variables, fix up as whole - nImprovements += fixUpPacked(dfg, varp); - } else if (varp->is()) { - // For array variables, fix up element-wise - const uint64_t component = varp->getUser(); - varp->foreachSink([&](DfgVertex& sink) { - // Ignore if sink is not part of same cycle - if (sink.getUser() != component) return false; - // Only handle ArraySels with constant index - DfgArraySel* const aselp = sink.cast(); - if (!aselp) return false; - if (!aselp->bitp()->is()) return false; - // Fix up the word - nImprovements += fixUpPacked(dfg, aselp); - // Remove if became unused - if (!aselp->hasSinks()) aselp->unlinkDelete(dfg); - return false; - }); - } - - UINFO(9, "FixUpIndependentRanges made " << nImprovements << " improvements"); - return nImprovements; + static size_t apply(DfgGraph& dfg, Vtx2Scc& vtx2Scc, DfgVertexVar& var) { + return FixUpIndependentRanges{dfg, vtx2Scc, var}.m_nImprovements; } }; @@ -1292,8 +1308,9 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { // Iterate while an improvement can be made and the graph is still cyclic do { // Color SCCs (populates DfgVertex::user()) - const auto userDataInUse = res.userDataInUse(); - const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(res); + Vtx2Scc vtx2Scc = res.makeUserMap(); + const uint32_t numNonTrivialSCCs + = V3DfgPasses::colorStronglyConnectedComponents(res, vtx2Scc); // Congrats if it has become acyclic if (!numNonTrivialSCCs) { @@ -1310,10 +1327,9 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { // Method 1: FixUpSelDrivers for (DfgVertexVar& vtx : res.varVertices()) { // If Variable is not part of a cycle, move on - const uint64_t component = vtx.getUser(); - if (!component) continue; + if (!vtx2Scc[vtx]) continue; - const size_t nFixed = FixUpSelDrivers::apply(res, &vtx); + const size_t nFixed = FixUpSelDrivers::apply(res, vtx2Scc, vtx); if (nFixed) { nImprovements += nFixed; ctx.m_breakCyclesContext.m_nImprovements += nFixed; @@ -1327,10 +1343,9 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { // Method 2. FixUpIndependentRanges for (DfgVertexVar& vtx : res.varVertices()) { // If Variable is not part of a cycle, move on - const uint64_t component = vtx.getUser(); - if (!component) continue; + if (!vtx2Scc[vtx]) continue; - const size_t nFixed = FixUpIndependentRanges::apply(res, &vtx); + const size_t nFixed = FixUpIndependentRanges::apply(res, vtx2Scc, vtx); if (nFixed) { nImprovements += nFixed; ctx.m_breakCyclesContext.m_nImprovements += nFixed; @@ -1340,10 +1355,11 @@ V3DfgPasses::breakCycles(const DfgGraph& dfg, V3DfgContext& ctx) { } while (nImprovements != prevNImprovements); if (dumpDfgLevel() >= 9) { - const auto userDataInUse = res.userDataInUse(); - V3DfgPasses::colorStronglyConnectedComponents(res); - res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining", - [](const DfgVertex& vtx) { return vtx.getUser(); }); + Vtx2Scc vtx2Scc = res.makeUserMap(); + V3DfgPasses::colorStronglyConnectedComponents(res, vtx2Scc); + res.dumpDotFilePrefixed(ctx.prefix() + "breakCycles-remaining", [&](const DfgVertex& vtx) { + return vtx2Scc[vtx]; // + }); } // If an improvement was made, return the still cyclic improved graph diff --git a/src/V3DfgColorSCCs.cpp b/src/V3DfgColorSCCs.cpp index 6caa0b957..3d02e1bd3 100644 --- a/src/V3DfgColorSCCs.cpp +++ b/src/V3DfgColorSCCs.cpp @@ -26,52 +26,51 @@ #include #include -// Similar algorithm used in ExtractCyclicComponents. -// This one sets DfgVertex::user(). See the static 'apply' method below. class ColorStronglyConnectedComponents final { + static_assert(sizeof(uint32_t[2]) == sizeof(uint64_t), "Incorrect ovverlay size"); + + // CONSTANTS static constexpr uint32_t UNASSIGNED = std::numeric_limits::max(); - // TYPES - struct VertexState final { - uint32_t component = UNASSIGNED; // Result component number (0 means not in SCC) - uint32_t index = UNASSIGNED; // Used by Pearce's algorithm for detecting SCCs - VertexState() = default; - VertexState(uint32_t i, uint32_t n) - : component{n} - , index{i} {} - }; - // STATE - DfgGraph& m_dfg; // The input graph + const DfgGraph& m_dfg; // The input graph + DfgUserMap& m_map; // The result map we are computing - also used for traversal uint32_t m_nonTrivialSCCs = 0; // Number of non-trivial SCCs in the graph uint32_t m_index = 0; // Visitation index counter - std::vector m_stack; // The stack used by the algorithm + std::vector m_stack; // The stack used by the algorithm // METHODS - void visitColorSCCs(DfgVertex& vtx, VertexState& vtxState) { - UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED, &vtx, "Already visited vertex");); + // Use the bottom 32-bit word as the component number + uint32_t& component(const DfgVertex& vtx) { // + return reinterpret_cast(m_map[vtx])[0]; + } + // Use the top 32-bit word as the visitation index + uint32_t& index(const DfgVertex& vtx) { // + return reinterpret_cast(m_map[vtx])[1]; + } + + void visitColorSCCs(const DfgVertex& vtx) { + UDEBUGONLY(UASSERT_OBJ(index(vtx) == UNASSIGNED, &vtx, "Already visited vertex");); // Visiting vertex - const size_t rootIndex = vtxState.index = ++m_index; + const size_t rootIndex = index(vtx) = ++m_index; // Visit children - vtx.foreachSink([&](DfgVertex& child) { - VertexState& childSatate = child.user(); + vtx.foreachSink([&](const DfgVertex& child) { // If the child has not yet been visited, then continue traversal - if (childSatate.index == UNASSIGNED) visitColorSCCs(child, childSatate); + if (index(child) == UNASSIGNED) visitColorSCCs(child); // If the child is not in an SCC - if (childSatate.component == UNASSIGNED) { - if (vtxState.index > childSatate.index) vtxState.index = childSatate.index; + if (component(child) == UNASSIGNED) { + if (index(vtx) > index(child)) index(vtx) = index(child); } return false; }); - if (vtxState.index == rootIndex) { + if (index(vtx) == rootIndex) { // This is the 'root' of an SCC // A trivial SCC contains only a single vertex - const bool isTrivial = m_stack.empty() // - || m_stack.back()->getUser().index < rootIndex; + const bool isTrivial = m_stack.empty() || index(*m_stack.back()) < rootIndex; // We also need a separate component for vertices that drive themselves (which can // happen for input like 'assign a = a'), as we want to extract them (they are cyclic). const bool drivesSelf = vtx.foreachSink([&](const DfgVertex& sink) { // @@ -81,17 +80,16 @@ class ColorStronglyConnectedComponents final { if (!isTrivial || drivesSelf) { // Allocate new component ++m_nonTrivialSCCs; - vtxState.component = m_nonTrivialSCCs; + component(vtx) = m_nonTrivialSCCs; while (!m_stack.empty()) { - VertexState& topState = m_stack.back()->getUser(); // Only higher nodes belong to the same SCC - if (topState.index < rootIndex) break; + if (index(*m_stack.back()) < rootIndex) break; + component(*m_stack.back()) = m_nonTrivialSCCs; m_stack.pop_back(); - topState.component = m_nonTrivialSCCs; } } else { // Trivial SCC (and does not drive itself), so acyclic. Keep it in original graph. - vtxState.component = 0; + component(vtx) = 0; } } else { // Not the root of an SCC @@ -102,56 +100,61 @@ class ColorStronglyConnectedComponents final { void colorSCCs() { // We know constant nodes have no input edges, so they cannot be part // of a non-trivial SCC. Mark them as such without any real traversals. - for (DfgConst& vtx : m_dfg.constVertices()) vtx.setUser(VertexState{0, 0}); - - // Start traversals through variables - for (DfgVertexVar& vtx : m_dfg.varVertices()) { - VertexState& vtxState = vtx.user(); - // If it has no input or no outputs, it cannot be part of a non-trivial SCC. - if ((!vtx.srcp() && !vtx.defaultp()) || !vtx.hasSinks()) { - UDEBUGONLY(UASSERT_OBJ(vtxState.index == UNASSIGNED || vtxState.component == 0, - &vtx, "Non circular variable must be in a trivial SCC");); - vtxState.index = 0; - vtxState.component = 0; - continue; - } - // If not yet visited, start a traversal - if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); + for (const DfgConst& vtx : m_dfg.constVertices()) { + index(vtx) = 0; + component(vtx) = 0; } - // Start traversals through operations - for (DfgVertex& vtx : m_dfg.opVertices()) { - VertexState& vtxState = vtx.user(); - // If not yet visited, start a traversal - if (vtxState.index == UNASSIGNED) visitColorSCCs(vtx, vtxState); + // Initialize state of variable vertices + for (const DfgVertexVar& vtx : m_dfg.varVertices()) { + // If it has no inputs or no outputs, it cannot be part of a non-trivial SCC. + if ((!vtx.srcp() && !vtx.defaultp()) || !vtx.hasSinks()) { + index(vtx) = 0; + component(vtx) = 0; + continue; + } + index(vtx) = UNASSIGNED; + component(vtx) = UNASSIGNED; + } + + // Initialize state of operation vertices + for (const DfgVertex& vtx : m_dfg.opVertices()) { + index(vtx) = UNASSIGNED; + component(vtx) = UNASSIGNED; + } + + // Start traversals through not yet visited variables + for (const DfgVertexVar& vtx : m_dfg.varVertices()) { + if (index(vtx) == UNASSIGNED) visitColorSCCs(vtx); + } + + // Start traversals through not yet visited operations + for (const DfgVertex& vtx : m_dfg.opVertices()) { + if (index(vtx) == UNASSIGNED) visitColorSCCs(vtx); } } - explicit ColorStronglyConnectedComponents(DfgGraph& dfg) - : m_dfg{dfg} { + explicit ColorStronglyConnectedComponents(const DfgGraph& dfg, DfgUserMap& map) + : m_dfg{dfg} + , m_map{map} { UASSERT(dfg.size() < UNASSIGNED, "Graph too big " << dfg.name()); // Yet another implementation of Pearce's algorithm. colorSCCs(); - // Re-assign user values - m_dfg.forEachVertex([](DfgVertex& vtx) { - const uint64_t component = vtx.getUser().component; - vtx.setUser(component); + // Re-assign mapped values + m_dfg.forEachVertex([&](const DfgVertex& vtx) { + const uint64_t c = component(vtx); + map[vtx] = c; }); } public: - // Sets DfgVertex::user() for all vertext to: - // - 0, if the vertex is not part of a non-trivial strongly connected component - // and is not part of a self-loop. That is: the Vertex is not part of any cycle. - // - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N. - // That is: each set of vertices that are reachable from each other will have the same - // non-zero value assigned. - // Returns the number of non-trivial SCCs (~distinct cycles) - static uint32_t apply(DfgGraph& dfg) { - return ColorStronglyConnectedComponents{dfg}.m_nonTrivialSCCs; + // See declaration of V3DfgPasses::colorStronglyConnectedComponents + static uint32_t apply(const DfgGraph& dfg, DfgUserMap& map) { + return ColorStronglyConnectedComponents{dfg, map}.m_nonTrivialSCCs; } }; -uint32_t V3DfgPasses::colorStronglyConnectedComponents(DfgGraph& dfg) { - return ColorStronglyConnectedComponents::apply(dfg); +uint32_t V3DfgPasses::colorStronglyConnectedComponents(const DfgGraph& dfg, + DfgUserMap& map) { + return ColorStronglyConnectedComponents::apply(dfg, map); } diff --git a/src/V3DfgDecomposition.cpp b/src/V3DfgDecomposition.cpp index c73d1b979..531762f8c 100644 --- a/src/V3DfgDecomposition.cpp +++ b/src/V3DfgDecomposition.cpp @@ -34,6 +34,8 @@ class SplitIntoComponents final { // STATE DfgGraph& m_dfg; // The input graph + // Map from vertices to the weekly connected component they belong to + DfgUserMap m_component = m_dfg.makeUserMap(); const std::string m_prefix; // Component name prefix std::vector> m_components; // The extracted components // Component counter - starting from 1 as 0 is the default value used as a marker @@ -44,10 +46,10 @@ class SplitIntoComponents final { std::vector queue; queue.reserve(m_dfg.size()); - // any sort of interesting logic must involve a variable, so we only need to iterate them + // Any sort of interesting logic must involve a variable, so we only need to iterate them for (DfgVertexVar& vtx : m_dfg.varVertices()) { // If already assigned this vertex to a component, then continue - if (vtx.user()) continue; + if (m_component[vtx]) continue; // Start depth first traversal at this vertex queue.push_back(&vtx); @@ -59,10 +61,10 @@ class SplitIntoComponents final { queue.pop_back(); // Move on if already visited - if (item.user()) continue; + if (m_component[item]) continue; // Assign to current component - item.user() = m_componentCounter; + m_component[item] = m_componentCounter; // Enqueue all sources and sinks of this vertex. item.foreachSource([&](DfgVertex& src) { @@ -83,7 +85,7 @@ class SplitIntoComponents final { template void moveVertices(DfgVertex::List& list) { for (DfgVertex* const vtxp : list.unlinkable()) { - if (const size_t component = vtxp->user()) { + if (const size_t component = m_component[vtxp]) { m_dfg.removeVertex(*vtxp); m_components[component - 1]->addVertex(*vtxp); } else { @@ -96,8 +98,6 @@ class SplitIntoComponents final { SplitIntoComponents(DfgGraph& dfg, const std::string& label) : m_dfg{dfg} , m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} { - // Component number is stored as DfgVertex::user() - const auto userDataInUse = m_dfg.userDataInUse(); // Color each component of the graph colorComponents(); // Allocate the component graphs @@ -126,6 +126,8 @@ std::vector> DfgGraph::splitIntoComponents(const std:: class ExtractCyclicComponents final { // STATE DfgGraph& m_dfg; // The input graph + // Map from vertices to the component they belong to + DfgUserMap m_component = m_dfg.makeUserMap(); const std::string m_prefix; // Component name prefix const bool m_doExpensiveChecks = v3Global.opt.debugCheck(); // The extracted cyclic components @@ -137,19 +139,21 @@ class ExtractCyclicComponents final { void addVertexAndExpandSiblings(DfgVertex& vtx, uint64_t component) { // Do not go past a variable, we will partition the graph there if (vtx.is()) return; + // Pick up component value reference + uint64_t& vtxComponentr = m_component.at(vtx); // Don't need to recurse if the vertex is already in the same component, // it was either marked through an earlier traversal, in which case it // was processed recursively, or it will be processed later. - if (vtx.getUser() == component) return; + if (vtxComponentr == component) return; // Because all cycles are through a variable, we can't reach another SCC. - UASSERT_OBJ(!vtx.getUser(), &vtx, "Cycle without variable involvement"); + UASSERT_OBJ(!vtxComponentr, &vtx, "Cycle without variable involvement"); // Put this vertex in the component, and continue recursively - vtx.setUser(component); + vtxComponentr = component; expandSiblings(vtx, component); } void expandSiblings(DfgVertex& vtx, uint64_t component) { - UASSERT_OBJ(vtx.getUser() == component, &vtx, "Traversal didn't stop"); + UASSERT_OBJ(m_component.at(vtx) == component, &vtx, "Traversal didn't stop"); vtx.foreachSink([&](DfgVertex& v) { addVertexAndExpandSiblings(v, component); return false; @@ -179,22 +183,22 @@ class ExtractCyclicComponents final { // component if they are not variables themselves. The assertions below // must hold because of the assumption above. for (DfgVertexVar& vtx : m_dfg.varVertices()) { - const uint64_t varComponent = vtx.getUser(); + const uint64_t varComponent = m_component.at(vtx); if (!varComponent) continue; if (DfgVertex* const srcp = vtx.srcp()) { if (!srcp->is()) { - const uint64_t srcComponent = srcp->getUser(); + uint64_t& srcComponent = m_component.at(srcp); UASSERT_OBJ(!srcComponent || srcComponent == varComponent, srcp, "Cycle through 'srcp' that does not go through variable."); - srcp->setUser(varComponent); + srcComponent = varComponent; } } if (DfgVertex* const defp = vtx.defaultp()) { if (!defp->is()) { - const uint64_t defComponent = defp->getUser(); + uint64_t& defComponent = m_component.at(defp); UASSERT_OBJ(!defComponent || defComponent == varComponent, defp, "Cycle through 'defaultp' that does not go through variable"); - defp->setUser(varComponent); + defComponent = varComponent; } } } @@ -205,7 +209,7 @@ class ExtractCyclicComponents final { // We do this by staring a DFS from each vertex that is part of an // component and add all reachable non-variable vertices to the same. for (DfgVertex& vtx : m_dfg.opVertices()) { - if (const uint64_t targetComponent = vtx.getUser()) { + if (const uint64_t targetComponent = m_component.at(vtx)) { expandSiblings(vtx, targetComponent); } } @@ -213,7 +217,7 @@ class ExtractCyclicComponents final { // Retrieve clone of vertex in the given component DfgVertexVar* getClone(DfgVertexVar& vtx, uint64_t component) { - UASSERT_OBJ(vtx.getUser() != component, &vtx, "Vertex is in that component"); + UASSERT_OBJ(m_component.at(vtx) != component, &vtx, "Vertex is in that component"); DfgVertexVar*& clonep = m_clones[&vtx][component]; if (!clonep) { if (DfgVarPacked* const pVtxp = vtx.cast()) { @@ -230,7 +234,7 @@ class ExtractCyclicComponents final { } } UASSERT_OBJ(clonep, &vtx, "Unhandled 'DfgVertexVar' sub-type"); - clonep->setUser(component); + m_component[clonep] = component; clonep->tmpForp(vtx.tmpForp()); } return clonep; @@ -238,11 +242,11 @@ class ExtractCyclicComponents final { // Fix edges that cross components void fixEdges(DfgVertexVar& vtx) { - const uint64_t component = vtx.getUser(); + const uint64_t component = m_component.at(vtx); // Fix up srcp and dstp (they must be the same component, or variable) if (DfgVertex* const sp = vtx.srcp()) { - const uint64_t srcComponent = sp->getUser(); + const uint64_t srcComponent = m_component.at(sp); if (srcComponent != component) { UASSERT_OBJ(sp->is(), &vtx, "'srcp' in different component"); getClone(vtx, srcComponent)->srcp(sp); @@ -250,7 +254,7 @@ class ExtractCyclicComponents final { } } if (DfgVertex* const dp = vtx.defaultp()) { - const uint64_t defaultComponent = dp->getUser(); + const uint64_t defaultComponent = m_component.at(dp); if (defaultComponent != component) { UASSERT_OBJ(dp->is(), &vtx, "'defaultp' in different component"); getClone(vtx, defaultComponent)->defaultp(dp); @@ -264,7 +268,7 @@ class ExtractCyclicComponents final { return false; }); for (DfgVertex* const sinkp : sinkps) { - const uint64_t sinkComponent = sinkp->getUser(); + const uint64_t sinkComponent = m_component.at(sinkp); // Same component is OK if (sinkComponent == component) continue; DfgVertex* const clonep = getClone(vtx, sinkComponent); @@ -278,7 +282,7 @@ class ExtractCyclicComponents final { void moveVertices(DfgVertex::List& list) { for (DfgVertex* const vtxp : list.unlinkable()) { DfgVertex& vtx = *vtxp; - if (const uint64_t component = vtx.getUser()) { + if (const uint64_t component = m_component.at(vtx)) { m_dfg.removeVertex(vtx); m_components[component - 1]->addVertex(vtx); } @@ -289,10 +293,10 @@ class ExtractCyclicComponents final { // Check that edges only cross components at variable boundaries dfg.forEachVertex([&](DfgVertex& vtx) { if (vtx.is()) return; - const uint64_t component = vtx.getUser(); + const uint64_t component = m_component.at(vtx); vtx.foreachSink([&](DfgVertex& snk) { if (snk.is()) return false; // OK to cross at variables - UASSERT_OBJ(component == snk.getUser(), &vtx, + UASSERT_OBJ(component == m_component.at(snk), &vtx, "Edge crossing components without variable involvement"); return false; }); @@ -317,10 +321,10 @@ class ExtractCyclicComponents final { }); } - void extractComponents(uint32_t numNonTrivialSCCs) { + void extractComponents(uint32_t nComponents) { // Allocate result graphs - m_components.resize(numNonTrivialSCCs); - for (uint32_t i = 0; i < numNonTrivialSCCs; ++i) { + m_components.resize(nComponents); + for (uint32_t i = 0; i < nComponents; ++i) { m_components[i].reset(new DfgGraph{m_dfg.modulep(), m_prefix + cvtToStr(i)}); } @@ -359,16 +363,14 @@ class ExtractCyclicComponents final { explicit ExtractCyclicComponents(DfgGraph& dfg, const std::string& label) : m_dfg{dfg} , m_prefix{dfg.name() + (label.empty() ? "" : "-") + label + "-component-"} { - // DfgVertex::user is set to the SCC number by colorStronglyConnectedComponents, - const auto userDataInUse = dfg.userDataInUse(); // Find all the non-trivial SCCs (and trivial cycles) in the graph - const uint32_t numNonTrivialSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg); + const uint32_t nSCCs = V3DfgPasses::colorStronglyConnectedComponents(dfg, m_component); // If the graph was acyclic (which should be the common case), then we are done. - if (!numNonTrivialSCCs) return; + if (!nSCCs) return; // Ensure that component boundaries are always at variables, by expanding SCCs expandComponents(); // Extract the components - extractComponents(numNonTrivialSCCs); + extractComponents(nSCCs); } public: diff --git a/src/V3DfgPasses.cpp b/src/V3DfgPasses.cpp index 72f882ad3..f5f64e9ba 100644 --- a/src/V3DfgPasses.cpp +++ b/src/V3DfgPasses.cpp @@ -30,7 +30,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { // Remove common sub-expressions { // Used by DfgVertex::hash - const auto userDataInUse = dfg.userDataInUse(); + DfgUserMap hashCache = dfg.makeUserMap(); DfgVertex::EqualsCache equalsCache; std::unordered_map> verticesWithEqualHashes; @@ -38,7 +38,7 @@ 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& vtx : dfg.varVertices()) vtx.user() = V3Hash{++varHash}; + for (const DfgVertexVar& vtx : dfg.varVertices()) hashCache[vtx] = 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 @@ -49,7 +49,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { VL_DO_DANGLING(vtxp->unlinkDelete(dfg), vtxp); continue; } - vtxp->user() = vtxp->num().toHash() + varHash; + hashCache[vtxp] = vtxp->num().toHash() + varHash; } // Combine operation vertices @@ -59,7 +59,7 @@ void V3DfgPasses::cse(DfgGraph& dfg, V3DfgCseContext& ctx) { vtxp->unlinkDelete(dfg); continue; } - const V3Hash hash = vtxp->hash(); + const V3Hash hash = vtxp->hash(hashCache); std::vector& vec = verticesWithEqualHashes[hash]; bool replaced = false; for (DfgVertex* const candidatep : vec) { @@ -91,8 +91,8 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) { } void V3DfgPasses::removeUnused(DfgGraph& dfg) { - // DfgVertex::user is the next pointer of the work list elements - const auto userDataInUse = dfg.userDataInUse(); + // Map from vertex to next vertex in the work list + DfgUserMap nextp = dfg.makeUserMap(); // Head of work list. Note that we want all next pointers in the list to be non-zero (including // that of the last element). This allows as to do two important things: detect if an element @@ -102,29 +102,22 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { DfgVertex* const sentinelp = reinterpret_cast(&dfg); DfgVertex* workListp = sentinelp; - // Add all unused operation vertices to the work list. This also allocates all DfgVertex::user. + // Add all unused operation vertices to the work list for (DfgVertex& vtx : dfg.opVertices()) { - if (vtx.hasSinks()) { - // This vertex is used. Allocate user, but don't add to work list. - vtx.setUser(nullptr); - } else { - // This vertex is unused. Add to work list. - vtx.setUser(workListp); - workListp = &vtx; - } + if (vtx.hasSinks()) continue; + // This vertex is unused. Add to work list. + nextp[vtx] = workListp; + workListp = &vtx; } // Also add all unused temporaries created during synthesis for (DfgVertexVar& vtx : dfg.varVertices()) { if (!vtx.tmpForp()) continue; - if (vtx.hasSinks() || vtx.hasDfgRefs()) { - // This vertex is used. Allocate user, but don't add to work list. - vtx.setUser(nullptr); - } else { - // This vertex is unused. Add to work list. - vtx.setUser(workListp); - workListp = &vtx; - } + if (vtx.hasSinks()) continue; + if (vtx.hasDfgRefs()) continue; + // This vertex is unused. Add to work list. + nextp[vtx] = workListp; + workListp = &vtx; } // Process the work list @@ -132,11 +125,11 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { // Pick up the head DfgVertex* const vtxp = workListp; // Detach the head - workListp = vtxp->getUser(); + workListp = nextp.at(vtxp); // Prefetch next item VL_PREFETCH_RW(workListp); // This item is now off the work list - vtxp->setUser(nullptr); + nextp.at(vtxp) = nullptr; // DfgLogic should have been synthesized or removed UASSERT_OBJ(!vtxp->is(), vtxp, "Should not be DfgLogic"); // If used, then nothing to do, so move on @@ -153,9 +146,9 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { const DfgVertexVar* const varp = src.cast(); if (varp && !varp->tmpForp()) return false; // If already in work list then nothing to do - if (src.getUser()) return false; + if (nextp[src]) return false; // Actually add to work list. - src.setUser(workListp); + nextp[src] = workListp; workListp = &src; return false; }); @@ -177,8 +170,6 @@ void V3DfgPasses::removeUnused(DfgGraph& dfg) { void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { UASSERT(dfg.modulep(), "binToOneHot only works with unscoped DfgGraphs for now"); - const auto userDataInUse = dfg.userDataInUse(); - // Structure to keep track of comparison details struct Term final { DfgVertex* m_vtxp = nullptr; // Vertex to replace @@ -189,13 +180,13 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { , m_inv{inv} {} }; - // Map from 'value beign compared' -> 'terms', stored in DfgVertex::user() - using Val2Terms = std::map>; - // Allocator for Val2Terms, so it's cleaned up on return - std::deque val2TermsAllocator; // List of vertices that are used as sources std::vector srcps; + // Map from 'vertices' -> 'value beign compared' -> 'terms' + using Val2Terms = std::map>; + DfgUserMap vtx2Val2Terms = dfg.makeUserMap(); + // Only consider input variables from a reasonable range: // - not too big to avoid huge tables, you are doomed anyway at that point.. // - not too small, as it's probably not worth it @@ -255,16 +246,12 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { // Not a comparison-like vertex continue; } - // Grab the Val2Terms entry - Val2Terms*& val2Termspr = srcp->user(); - if (!val2Termspr) { - // Remeber and allocate on first encounter - srcps.emplace_back(srcp); - val2TermsAllocator.emplace_back(); - val2Termspr = &val2TermsAllocator.back(); - } + // Grab Val2Terms for this vertex + Val2Terms& val2Terms = vtx2Val2Terms[srcp]; + // Remeber and on first encounter + if (val2Terms.empty()) srcps.emplace_back(srcp); // Record term - (*val2Termspr)[val].emplace_back(&vtx, inv); + val2Terms[val].emplace_back(&vtx, inv); ++nTerms; } @@ -281,7 +268,7 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { // Create decoders for each srcp for (DfgVertex* const srcp : srcps) { - const Val2Terms& val2Terms = *srcp->getUser(); + const Val2Terms& val2Terms = vtx2Val2Terms[srcp]; // If not enough terms in this vertex, ignore if (val2Terms.size() < TERM_LIMIT) continue; @@ -422,7 +409,8 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) { } void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { - const auto userDataInUse = dfg.userDataInUse(); + // Map from vertex to next vertex in the work list + DfgUserMap nextp = dfg.makeUserMap(); // Head of work list. Note that we want all next pointers in the list to be non-zero // (including that of the last element). This allows us to do two important things: detect @@ -434,16 +422,16 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { // Add all variables to the initial work list for (DfgVertexVar& vtx : dfg.varVertices()) { - vtx.setUser(workListp); + nextp[vtx] = workListp; workListp = &vtx; } const auto addToWorkList = [&](DfgVertex& vtx) { // If already in work list then nothing to do - DfgVertex*& nextInWorklistp = vtx.user(); - if (nextInWorklistp) return false; + DfgVertex*& nextpr = nextp[vtx]; + if (nextpr) return false; // Actually add to work list. - nextInWorklistp = workListp; + nextpr = workListp; workListp = &vtx; return false; }; @@ -462,9 +450,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) { // Pick up the head of the work list DfgVertex* const vtxp = workListp; // Detach the head - workListp = vtxp->getUser(); + workListp = nextp.at(vtxp); // Reset user pointer so it can be added back to the work list later - vtxp->setUser(nullptr); + nextp.at(vtxp) = nullptr; // Prefetch next item VL_PREFETCH_RW(workListp); diff --git a/src/V3DfgPasses.h b/src/V3DfgPasses.h index 93febfa68..e6c211b5e 100644 --- a/src/V3DfgPasses.h +++ b/src/V3DfgPasses.h @@ -64,14 +64,14 @@ void dfgToAst(DfgGraph&, V3DfgContext&) VL_MT_DISABLED; // Construct binary to oneHot decoders void binToOneHot(DfgGraph&, V3DfgBinToOneHotContext&) VL_MT_DISABLED; -// Sets DfgVertex::user() for all vertext to: +// Populates the given DfgUserMap for all vertext to: // - 0, if the vertex is not part of a non-trivial strongly connected component // and is not part of a self-loop. That is: the Vertex is not part of any cycle. // - N, if the vertex is part of a non-trivial strongly conneced component or self-loop N. // That is: each set of vertices that are reachable from each other will have the same // non-zero value assigned. // Returns the number of non-trivial SCCs (distinct cycles) -uint32_t colorStronglyConnectedComponents(DfgGraph&) VL_MT_DISABLED; +uint32_t colorStronglyConnectedComponents(const DfgGraph&, DfgUserMap&) VL_MT_DISABLED; // Common subexpression elimination void cse(DfgGraph&, V3DfgCseContext&) VL_MT_DISABLED; // Inline fully driven variables diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 4af6f06c8..4c3f0316c 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -130,6 +130,8 @@ class V3DfgPeephole final : public DfgVisitor { // STATE DfgGraph& m_dfg; // The DfgGraph being visited + // Map from vertex to next vertex on work list + DfgUserMap m_nextp = m_dfg.makeUserMap(); V3DfgPeepholeContext& m_ctx; // The config structure AstNodeDType* const m_bitDType = V3Dfg::dtypePacked(1); // Common, so grab it up front // Head of work list. Note that we want all next pointers in the list to be non-zero (including @@ -156,9 +158,9 @@ class V3DfgPeephole final : public DfgVisitor { // We only process actual operation vertices if (vtxp->is() || vtxp->is()) return; // If already in work list then nothing to do - if (vtxp->getUser()) return; + if (m_nextp[vtxp]) return; // Actually add to work list. - vtxp->setUser(m_workListp); + m_nextp[vtxp] = m_workListp; m_workListp = vtxp; } @@ -181,7 +183,7 @@ class V3DfgPeephole final : public DfgVisitor { addSourcesToWorkList(vtxp); // If in work list then we can't delete it just yet (as we can't remove from the middle of // the work list), but it will be deleted when the work list is processed. - if (vtxp->getUser()) return; + if (m_nextp[vtxp]) return; // Otherwise we can delete it now. // Remove from cache m_cache.invalidateByValue(vtxp); @@ -230,14 +232,8 @@ class V3DfgPeephole final : public DfgVisitor { Vertex* make(FileLine* flp, AstNodeDType* dtypep, Operands... operands) { // Find or create an equivalent vertex Vertex* const vtxp = m_cache.getOrCreate(flp, dtypep, operands...); - - // Add to work list. - DfgVertex*& workListNextp = vtxp->template user(); - if (!workListNextp) { - workListNextp = m_workListp; - m_workListp = vtxp; - } - + // Add to work list + addToWorkList(vtxp); // Return new node return vtxp; } @@ -1755,13 +1751,9 @@ class V3DfgPeephole final : public DfgVisitor { : m_dfg{dfg} , m_ctx{ctx} { - // DfgVertex::user is the next pointer of the work list elements - const auto userDataInUse = m_dfg.userDataInUse(); - - // Add all vertices to the work list, and to the vertex cache. - // This also allocates all DfgVertex::user. + // Add all operation vertices to the work list and cache for (DfgVertex& vtx : m_dfg.opVertices()) { - vtx.setUser(m_workListp); + m_nextp[vtx] = m_workListp; m_workListp = &vtx; m_cache.cache(&vtx); } @@ -1771,9 +1763,9 @@ class V3DfgPeephole final : public DfgVisitor { // Pick up the head DfgVertex* const vtxp = m_workListp; // Detach the head and prefetch next - m_workListp = vtxp->getUser(); + m_workListp = m_nextp[vtxp]; VL_PREFETCH_RW(m_workListp); - vtxp->setUser(nullptr); + m_nextp[vtxp] = nullptr; // Remove unused vertices as we gp if (!vtxp->hasSinks()) { deleteVertex(vtxp); diff --git a/src/V3DfgSynthesize.cpp b/src/V3DfgSynthesize.cpp index 16d3badad..9840f1583 100644 --- a/src/V3DfgSynthesize.cpp +++ b/src/V3DfgSynthesize.cpp @@ -1822,8 +1822,8 @@ void V3DfgPasses::synthesize(DfgGraph& dfg, V3DfgContext& ctx) { // Otherwise figure out which vertices are worth synthesizing. // Find cycles - const auto userDataInUse = dfg.userDataInUse(); - V3DfgPasses::colorStronglyConnectedComponents(dfg); + DfgUserMap scc = dfg.makeUserMap(); + V3DfgPasses::colorStronglyConnectedComponents(dfg, scc); // First, gather variables, we will then attempt to synthesize all their drivers std::vector varps; @@ -1832,7 +1832,7 @@ void V3DfgPasses::synthesize(DfgGraph& dfg, V3DfgContext& ctx) { if (!var.srcp()) continue; // Circular variable - synthesize - if (var.getUser()) { + if (scc.at(var)) { varps.emplace_back(&var); continue; }