From e508d2a6d752059f33521aa5530ca973d5b153db Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 23 Mar 2026 14:41:36 +0000 Subject: [PATCH] Internals: Use a custom worklist in DfgPeephole --- src/V3DfgPeephole.cpp | 71 ++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 5e80290c5..cec50f516 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -128,11 +128,18 @@ template <> void foldOp (V3Number& out, const V3Number& lhs, cons // clang-format on class V3DfgPeephole final : public DfgVisitor { + // TYPES + struct VertexInfo final { + // Position of this vertx in the work list. Zero means not in the list. + size_t m_workListIndex = 0; + }; + // STATE DfgGraph& m_dfg; // The DfgGraph being visited V3DfgPeepholeContext& m_ctx; // The config structure const DfgDataType& m_bitDType = DfgDataType::packed(1); // Common, so grab it up front - DfgWorklist m_workList{m_dfg}; // This is a worklist based algorithm + std::vector m_wlist; // Using a manual work list as need special operations + DfgUserMap m_vInfo = m_dfg.makeUserMap(); // Map to VertexInfo V3DfgCache m_cache{m_dfg}; // Vertex cache to avoid creating redundant vertices DfgVertex* m_vtxp = nullptr; // Currently considered vertex @@ -149,7 +156,14 @@ class V3DfgPeephole final : public DfgVisitor { return true; } - void addToWorkList(DfgVertex* vtxp) { m_workList.push_front(*vtxp); } + void addToWorkList(DfgVertex* vtxp) { + VertexInfo& vInfo = m_vInfo[*vtxp]; + // If already on work list, ignore + if (vInfo.m_workListIndex) return; + // Add to work list + vInfo.m_workListIndex = m_wlist.size(); + m_wlist.push_back(vtxp); + } void addSourcesToWorkList(DfgVertex* vtxp) { vtxp->foreachSource([&](DfgVertex& src) { @@ -166,15 +180,12 @@ class V3DfgPeephole final : public DfgVisitor { } void deleteVertex(DfgVertex* vtxp) { - UASSERT_OBJ(!m_workList.contains(*vtxp), vtxp, "Deleted Vertex is in work list"); + UASSERT_OBJ(!m_vInfo[vtxp].m_workListIndex, vtxp, "Deleted Vertex is in work list"); // Inputs should have been reset UASSERT_OBJ(vtxp->is() || !vtxp->nInputs(), vtxp, "Operation Vertx should not have sources when being deleted"); // Should not have sinks UASSERT_OBJ(!vtxp->hasSinks(), vtxp, "Should not delete used vertex"); - // 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 (m_workList.contains(*vtxp)) return; // Otherwise we can delete it now. // This pass only removes variables that are either not driven in this graph, // or are not observable outside the graph. If there is also no external write @@ -190,7 +201,7 @@ class V3DfgPeephole final : public DfgVisitor { // Replace 'm_vtxp' with the given vertex void replace(DfgVertex* resp) { // Should not be in the work list - UASSERT_OBJ(!m_workList.contains(*m_vtxp), m_vtxp, "Replaced Vertex is in work list"); + UASSERT_OBJ(!m_vInfo[m_vtxp].m_workListIndex, m_vtxp, "Replaced Vertex is in work list"); // Debug bisect check const auto debugCallback = [&]() -> void { @@ -1804,44 +1815,54 @@ class V3DfgPeephole final : public DfgVisitor { : m_dfg{dfg} , m_ctx{ctx} { + // Initialize the work list + m_wlist.reserve(m_dfg.size() * 4); + // Need a nullptr at index 0 so VertexInfo::m_workListIndex can be used to check membership + m_wlist.push_back(nullptr); + // Add all variable vertices to the work list. Do this first so they are processed last. // This order has a better chance of preserving original variables in case they are needed. for (DfgVertexVar& vtx : m_dfg.varVertices()) addToWorkList(&vtx); - // Add all operation vertices to the work list and cache + // Add all operation vertices to the work list for (DfgVertex& vtx : m_dfg.opVertices()) addToWorkList(&vtx); // Process the work list - m_workList.foreach([&](DfgVertex& vtx) { + while (!m_wlist.empty()) { VL_RESTORER(m_vtxp); - m_vtxp = &vtx; - // Variables are special, just iterate them, the visit might delete them - if (vtx.is()) { - iterate(&vtx); - return; + // Pop up head of work list + m_vtxp = m_wlist.back(); + m_wlist.pop_back(); + if (!m_vtxp) continue; // If removed from worklist, move on + m_vInfo[m_vtxp].m_workListIndex = 0; // No longer on work list + + // Variables are special, just visit them, the visit might delete them + if (DfgVertexVar* const varp = m_vtxp->cast()) { + visit(varp); + continue; } // Remove unused operations as we go - if (!vtx.hasSinks()) { - if (vtx.nInputs()) { - addSourcesToWorkList(&vtx); - m_cache.invalidate(&vtx); - vtx.resetInputs(); + if (!m_vtxp->hasSinks()) { + if (m_vtxp->nInputs()) { + addSourcesToWorkList(m_vtxp); + m_cache.invalidate(m_vtxp); + m_vtxp->resetInputs(); } - deleteVertex(&vtx); - return; + deleteVertex(m_vtxp); + continue; } // Check if an equivalent vertex exists, if so replace this vertex with it - if (DfgVertex* const sampep = m_cache.cache(&vtx)) { + if (DfgVertex* const sampep = m_cache.cache(m_vtxp)) { replace(sampep); - return; + continue; } // Visit vertex, might get deleted in the process - iterate(&vtx); - }); + iterate(m_vtxp); + } } public: