diff --git a/src/V3OrderMoveGraphBuilder.h b/src/V3OrderMoveGraphBuilder.h index 5f1626bed..c5838bbdf 100644 --- a/src/V3OrderMoveGraphBuilder.h +++ b/src/V3OrderMoveGraphBuilder.h @@ -52,38 +52,24 @@ class V3OrderMoveGraphBuilder final { // TYPES using DomainMap = std::map; -public: - class MoveVertexMaker VL_NOT_FINAL { - public: - // Clients of ProcessMoveBuildGraph must supply MoveVertexMaker - // which creates new T_MoveVertex's. Each new vertex wraps lvertexp - // (which may be nullptr.) - virtual T_MoveVertex* makeVertexp(OrderLogicVertex* lvertexp, - const OrderEitherVertex* varVertexp, - const AstSenTree* domainp) - = 0; - }; - -private: // MEMBERS - const OrderGraph* const m_graphp; // Input OrderGraph - V3Graph* const m_outGraphp; // Output graph of T_MoveVertex vertices + const OrderGraph& m_orderGraph; // Input OrderGraph + // Output graph of T_MoveVertex vertices + std::unique_ptr m_outGraphp{new V3Graph}; // Map from Trigger reference AstSenItem to the original AstSenTree const V3Order::TrigToSenMap& m_trigToSen; - MoveVertexMaker* const m_vxMakerp; // Factory class for T_MoveVertex's // Storage for domain -> T_MoveVertex, maps held in OrderVarVertex::userp() std::deque m_domainMaps; -public: // CONSTRUCTORS - V3OrderMoveGraphBuilder(const OrderGraph* logicGraphp, // Input graph of OrderLogicVertex etc. - V3Graph* outGraphp, // Output graph of T_MoveVertex's - const V3Order::TrigToSenMap& trigToSen, MoveVertexMaker* vxMakerp) - : m_graphp{logicGraphp} - , m_outGraphp{outGraphp} - , m_trigToSen{trigToSen} - , m_vxMakerp{vxMakerp} {} + V3OrderMoveGraphBuilder(const OrderGraph& orderGraph, const V3Order::TrigToSenMap& trigToSen) + : m_orderGraph{orderGraph} + , m_trigToSen{trigToSen} { + build(); + } virtual ~V3OrderMoveGraphBuilder() = default; + VL_UNCOPYABLE(V3OrderMoveGraphBuilder); + VL_UNMOVABLE(V3OrderMoveGraphBuilder); // METHODS void build() { @@ -100,9 +86,9 @@ public: // done the forward search, so stop. // For each logic vertex, make a T_MoveVertex, for each variable vertex, allocate storage - for (V3GraphVertex* itp = m_graphp->verticesBeginp(); itp; itp = itp->verticesNextp()) { + for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (OrderLogicVertex* const lvtxp = itp->cast()) { - lvtxp->userp(m_vxMakerp->makeVertexp(lvtxp, nullptr, lvtxp->domainp())); + lvtxp->userp(new T_MoveVertex{*m_outGraphp, lvtxp, lvtxp->domainp()}); } else { // This is an OrderVarVertex m_domainMaps.emplace_back(); @@ -110,14 +96,13 @@ public: } } // Build edges between logic vertices - for (V3GraphVertex* itp = m_graphp->verticesBeginp(); itp; itp = itp->verticesNextp()) { + for (V3GraphVertex* itp = m_orderGraph.verticesBeginp(); itp; itp = itp->verticesNextp()) { if (OrderLogicVertex* const lvtxp = itp->cast()) { iterateLogicVertex(lvtxp); } } } -private: // Returns the AstSenItem that originally corresponds to this AstSenTree, or nullptr if no // original AstSenTree, or if the original AstSenTree had multiple AstSenItems. const AstSenItem* getOrigSenItem(AstSenTree* senTreep) { @@ -200,7 +185,7 @@ private: if (!vMoveVtxp) continue; // Add this (variable, domain) as dependent of the logic that writes it. - new V3GraphEdge{m_outGraphp, lMoveVtxp, vMoveVtxp, 1}; + new V3GraphEdge{m_outGraphp.get(), lMoveVtxp, vMoveVtxp, 1}; } } @@ -213,21 +198,24 @@ private: if (edgep->weight() == 0) continue; // Was cut // OrderGraph is a bipartite graph, so we know it's an OrderLogicVertex - const OrderLogicVertex* const lvtxp - = static_cast(edgep->top()); + const OrderLogicVertex* const lVtxp = edgep->top()->as(); // Do not construct dependencies across exclusive domains. - if (domainsExclusive(domainp, lvtxp->domainp())) continue; + if (domainsExclusive(domainp, lVtxp->domainp())) continue; // there is a path from this vvtx to a logic vertex. Add the new edge. - if (!vMoveVtxp) vMoveVtxp = m_vxMakerp->makeVertexp(nullptr, vvtxp, domainp); - T_MoveVertex* const lMoveVxp = static_cast(lvtxp->userp()); - new V3GraphEdge{m_outGraphp, vMoveVtxp, lMoveVxp, 1}; + if (!vMoveVtxp) vMoveVtxp = new T_MoveVertex{*m_outGraphp, nullptr, domainp}; + T_MoveVertex* const lMoveVxp = static_cast(lVtxp->userp()); + new V3GraphEdge{m_outGraphp.get(), vMoveVtxp, lMoveVxp, 1}; } return vMoveVtxp; } - VL_UNCOPYABLE(V3OrderMoveGraphBuilder); +public: + static std::unique_ptr apply(const OrderGraph& orderGraph, + const V3Order::TrigToSenMap& trigToSen) { + return std::move(V3OrderMoveGraphBuilder{orderGraph, trigToSen}.m_outGraphp); + } }; #endif // Guard diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index 6b247fadd..49aa08f27 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -34,22 +34,6 @@ VL_DEFINE_DEBUG_FUNCTIONS; -class OrderMTaskMoveVertexMaker final - : public V3OrderMoveGraphBuilder::MoveVertexMaker { - V3Graph* m_pomGraphp; - -public: - explicit OrderMTaskMoveVertexMaker(V3Graph* pomGraphp) - : m_pomGraphp{pomGraphp} {} - MTaskMoveVertex* makeVertexp(OrderLogicVertex* lvertexp, const OrderEitherVertex* varVertexp, - const AstSenTree* domainp) override { - return new MTaskMoveVertex{m_pomGraphp, lvertexp, varVertexp, domainp}; - } - -private: - VL_UNCOPYABLE(OrderMTaskMoveVertexMaker); -}; - // Sort MTaskMoveVertex vertices by domain, then by scope, based on teh order they are encountered class OrderVerticesByDomainThenScope final { mutable uint64_t m_nextId = 0; // Next id to use @@ -82,6 +66,8 @@ struct MTaskVxIdLessThan final { AstExecGraph* V3Order::createParallel(const OrderGraph& graph, const std::string& tag, const TrigToSenMap& trigToSen, bool slow) { + UINFO(2, " Constructing parallel code for '" + tag + "'"); + // For nondeterminism debug: V3Partition::hashGraphDebug(&graph, "V3Order's m_graph"); @@ -91,21 +77,16 @@ AstExecGraph* V3Order::createParallel(const OrderGraph& graph, const std::string // Now, starting from m_graph, make a slightly-coarsened graph representing // only logic, and discarding edges we know we can ignore. // This is quite similar to the 'm_pomGraph' of the serial code gen: - V3Graph logicGraph; - { - OrderMTaskMoveVertexMaker create_mtask_vertex(&logicGraph); - V3OrderMoveGraphBuilder mtask_pmbg(&graph, &logicGraph, trigToSen, - &create_mtask_vertex); - mtask_pmbg.build(); - } + const std::unique_ptr logicGraphp + = V3OrderMoveGraphBuilder::apply(graph, trigToSen); // Needed? We do this for m_pomGraph in serial mode, so do it here too: - logicGraph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); + logicGraphp->removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); // Partition logicGraph into LogicMTask's. The partitioner will annotate // each vertex in logicGraph with a 'color' which is really an mtask ID // in this context. - V3Partition partitioner{&graph, &logicGraph}; + V3Partition partitioner{&graph, logicGraphp.get()}; V3Graph mtasks; partitioner.go(&mtasks); @@ -124,7 +105,7 @@ AstExecGraph* V3Order::createParallel(const OrderGraph& graph, const std::string // This is the order we'll execute logic nodes within the MTask. // // MTasks may span scopes and domains, so sort by both here: - GraphStream logicStream{&logicGraph}; + GraphStream logicStream{logicGraphp.get()}; while (const V3GraphVertex* const vtxp = logicStream.nextp()) { const MTaskMoveVertex* const movep = vtxp->as(); // Only care about logic vertices diff --git a/src/V3OrderSerial.cpp b/src/V3OrderSerial.cpp index c0397c691..95ccb5811 100644 --- a/src/V3OrderSerial.cpp +++ b/src/V3OrderSerial.cpp @@ -26,360 +26,277 @@ #include "V3OrderInternal.h" #include "V3OrderMoveGraphBuilder.h" -#include +#include VL_DEFINE_DEBUG_FUNCTIONS; -class OrderMoveDomScope; - -class OrderMoveVertex final : public V3GraphVertex { - VL_RTTI_IMPL(OrderMoveVertex, V3GraphVertex) - enum OrderMState : uint8_t { POM_WAIT, POM_READY, POM_MOVED }; - - OrderLogicVertex* const m_logicp; - OrderMState m_state; // Movement state - OrderMoveDomScope* m_domScopep; // Domain/scope list information - -protected: - friend class OrderProcess; - friend class OrderMoveVertexMaker; - // These only contain the "next" item, - // for the head of the list, see the same var name under OrderProcess - V3ListEnt m_pomWaitingE; // List of nodes needing inputs to become ready - V3ListEnt m_readyVerticesE; // List of ready under domain/scope -public: - // CONSTRUCTORS - OrderMoveVertex(V3Graph* graphp, OrderLogicVertex* logicp) VL_MT_DISABLED - : V3GraphVertex{graphp}, - m_logicp{logicp}, - m_state{POM_WAIT}, - m_domScopep{nullptr} {} - ~OrderMoveVertex() override = default; - - // METHODS - string dotColor() const override { - if (logicp()) { - return logicp()->dotColor(); - } else { - return ""; - } - } - - string name() const override VL_MT_STABLE { - string nm; - if (VL_UNCOVERABLE(!logicp())) { // Avoid crash when debugging - nm = "nul"; // LCOV_EXCL_LINE - } else { - nm = logicp()->name(); - nm += (string{"\\nMV:"} + " d=" + cvtToHex(logicp()->domainp()) - + " s=" + cvtToHex(logicp()->scopep())); - } - return nm; - } - OrderLogicVertex* logicp() const VL_MT_STABLE { return m_logicp; } - bool isWait() const { return m_state == POM_WAIT; } - void setReady() VL_MT_DISABLED { - UASSERT(m_state == POM_WAIT, "Wait->Ready on node not in proper state"); - m_state = POM_READY; - } - void setMoved() VL_MT_DISABLED { - UASSERT(m_state == POM_READY, "Ready->Moved on node not in proper state"); - m_state = POM_MOVED; - } - OrderMoveDomScope* domScopep() const { return m_domScopep; } - OrderMoveVertex* pomWaitingNextp() const { return m_pomWaitingE.nextp(); } - void domScopep(OrderMoveDomScope* ds) { m_domScopep = ds; } -}; - //###################################################################### -class OrderProcess; +class OrderMoveVertex; +// 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 { - // Information stored for each unique loop, domain & scope trifecta -public: - V3ListEnt m_readyDomScopeE; // List of next ready dom scope - V3List m_readyVertices; // Ready vertices with same domain & scope -private: - bool m_onReadyList = false; // True if DomScope is already on list of ready dom/scopes - const AstSenTree* const m_domainp; // Domain all vertices belong to - const AstScope* const m_scopep; // Scope all vertices belong to + // STATE + V3List m_readyVertices; // Ready vertices in this domain/scope + V3ListEnt m_listEnt; // List entry to store this instance + 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 - using DomScopeKey = std::pair; - using DomScopeMap = std::map; - static DomScopeMap s_dsMap; // Structure registered for each dom/scope pairing + // Key type for map below + class DomScopeMapKey final { + const AstSenTree* const m_domainp; + const AstScope* const m_scopep; + + public: + DomScopeMapKey(const AstSenTree* domainp, const AstScope* scopep) + : m_domainp{domainp} + , m_scopep{scopep} {} + + struct Hash final { + size_t operator()(const DomScopeMapKey& key) const { + V3Hash hash{reinterpret_cast(key.m_domainp)}; + hash += reinterpret_cast(key.m_scopep); + return hash.value(); + } + }; + + struct Equal final { + bool operator()(const DomScopeMapKey& a, const DomScopeMapKey& b) const { + return a.m_domainp == b.m_domainp && a.m_scopep == b.m_scopep; + } + }; + }; + + using DomScopeMap = std::unordered_map; + // Map from Domain/Scope to the corresponding DomScope instance + static DomScopeMap s_dsMap; public: + // STATIC MEMBERS + static OrderMoveDomScope& getOrCreate(const AstSenTree* domainp, const AstScope* scopep) { + return s_dsMap + .emplace(std::piecewise_construct, // + std::forward_as_tuple(domainp, scopep), // + std::forward_as_tuple(domainp, scopep)) + .first->second; + } + + static void clear() { s_dsMap.clear(); } + + // CONSTRUCTOR OrderMoveDomScope(const AstSenTree* domainp, const AstScope* scopep) : m_domainp{domainp} , m_scopep{scopep} {} - OrderMoveDomScope* readyDomScopeNextp() const { return m_readyDomScopeE.nextp(); } + ~OrderMoveDomScope() = default; + VL_UNCOPYABLE(OrderMoveDomScope); + VL_UNMOVABLE(OrderMoveDomScope); + + // MEMBERS + V3List& readyVertices() { return m_readyVertices; } const AstSenTree* domainp() const { return m_domainp; } const AstScope* scopep() const { return m_scopep; } - // Check the domScope is on ready list, add if not - void ready(OrderProcess* opp); - // Mark one vertex as finished, remove from ready list if done - void movedVertex(OrderProcess* opp, OrderMoveVertex* vertexp); - // STATIC MEMBERS (for lookup) - static void clear() { - for (const auto& itr : s_dsMap) delete itr.second; - s_dsMap.clear(); - } - V3List& readyVertices() { return m_readyVertices; } - static OrderMoveDomScope* findCreate(const AstSenTree* domainp, const AstScope* scopep) { - const DomScopeKey key = std::make_pair(domainp, scopep); - const auto pair = s_dsMap.emplace(key, nullptr); - if (pair.second) pair.first->second = new OrderMoveDomScope{domainp, scopep}; - return pair.first->second; - } - string name() const { - return string{"MDS:"} + " d=" + cvtToHex(domainp()) + " s=" + cvtToHex(scopep()); - } -}; -// ###################################################################### -// OrderMoveVertexMaker and related - -class OrderMoveVertexMaker final - : public V3OrderMoveGraphBuilder::MoveVertexMaker { - // MEMBERS - V3Graph* m_pomGraphp; - V3List* m_pomWaitingp; - -public: - // CONSTRUCTORS - OrderMoveVertexMaker(V3Graph* pomGraphp, V3List* pomWaitingp) - : m_pomGraphp{pomGraphp} - , m_pomWaitingp{pomWaitingp} {} - // METHODS - OrderMoveVertex* makeVertexp(OrderLogicVertex* lvertexp, const OrderEitherVertex*, - const AstSenTree* domainp) override { - OrderMoveVertex* const resultp = new OrderMoveVertex{m_pomGraphp, lvertexp}; - AstScope* const scopep = lvertexp ? lvertexp->scopep() : nullptr; - resultp->domScopep(OrderMoveDomScope::findCreate(domainp, scopep)); - resultp->m_pomWaitingE.pushBack(*m_pomWaitingp, resultp); - return resultp; + 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); } - -private: - VL_UNCOPYABLE(OrderMoveVertexMaker); + 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); + } + OrderMoveDomScope* nextp() const { return m_listEnt.nextp(); } }; OrderMoveDomScope::DomScopeMap OrderMoveDomScope::s_dsMap; -std::ostream& operator<<(std::ostream& lhs, const OrderMoveDomScope& rhs) { - lhs << rhs.name(); - return lhs; -} +// ###################################################################### +// OrderMoveVertex constructor -//###################################################################### -// OrderProcess class +class OrderMoveVertex final : public V3GraphVertex { + VL_RTTI_IMPL(OrderMoveVertex, V3GraphVertex) -class OrderProcess final { - // STATE - const OrderGraph& m_graph; // The ordering graph - - // Map from Trigger reference AstSenItem to the original AstSenTree - const V3Order::TrigToSenMap& m_trigToSen; - - const string m_tag; // Substring to add to generated names - - V3Graph m_pomGraph; // Graph of logic elements to move - V3List m_pomWaiting; // List of nodes needing inputs to become ready - friend class OrderMoveDomScope; - V3List m_pomReadyDomScope; // List of ready domain/scope pairs, by loopId - - V3OrderCFuncEmitter m_emitter; + // 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 for ready list under DomScope // METHODS - // processMove* routines schedule serial execution - void processMove(); - void processMoveClear(); - void processMoveBuildGraph(); - void processMovePrepReady(); - void processMoveReadyOne(OrderMoveVertex* vertexp); - void processMoveDoneOne(OrderMoveVertex* vertexp); + std::string dotColor() const override { return logicp() ? logicp()->dotColor() : ""; } - // CONSTRUCTOR - OrderProcess(const OrderGraph& graph, const string& tag, - const V3Order::TrigToSenMap& trigToSen, bool slow) - : m_graph{graph} - , m_trigToSen{trigToSen} - , m_tag{tag} - , m_emitter{tag, slow} { - UINFO(2, " Construct Move Graph...\n"); - processMoveBuildGraph(); - // Different prefix (ordermv) as it's not the same graph - if (dumpGraphLevel() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_start"); - m_pomGraph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); - if (dumpGraphLevel() >= 4) m_pomGraph.dumpDotFilePrefixed(m_tag + "_ordermv_simpl"); - - UINFO(2, " Move...\n"); - processMove(); + std::string name() const override VL_MT_STABLE { + if (!logicp()) { + return "var"; + } else { + std::string nm = logicp()->name() + "\\n"; + nm += "MV:"; + nm += +" d=" + cvtToHex(logicp()->domainp()); + nm += +" s=" + cvtToHex(logicp()->scopep()); + return nm; + } } - ~OrderProcess() = default; - public: - // Order the logic - static std::vector main(const OrderGraph& graph, const string& tag, - const V3Order::TrigToSenMap& trigToSen, bool slow) { - return OrderProcess{graph, tag, trigToSen, slow}.m_emitter.getAndClearActiveps(); + // CONSTRUCTORS + OrderMoveVertex(V3Graph& graph, OrderLogicVertex* lVtxp, + const AstSenTree* domainp) VL_MT_DISABLED + : V3GraphVertex{&graph}, + m_logicp{lVtxp}, + m_domScope{OrderMoveDomScope::getOrCreate(domainp, lVtxp ? lVtxp->scopep() : nullptr)} { + UASSERT_OBJ(!lVtxp || lVtxp->domainp() == domainp, lVtxp, "Wrong domain for Move vertex"); } + ~OrderMoveVertex() override = default; + + 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); } }; //###################################################################### -// OrderMoveDomScope methods +// OrderSerial class -// Check the domScope is on ready list, add if not -void OrderMoveDomScope::ready(OrderProcess* opp) { - if (!m_onReadyList) { - m_onReadyList = true; - m_readyDomScopeE.pushBack(opp->m_pomReadyDomScope, this); +class OrderSerial final { + // STATE + std::unique_ptr m_moveGraphp; // Graph of logic elements to move + V3List m_readyDomScopeps; // List of DomScopes which have ready vertices + V3OrderCFuncEmitter m_emitter; // Code emitter to construct the result + + // METHODS + + // Take the given waiting logic vertex, and move it to the ready list its DomScope + void logicReady(OrderMoveVertex* lVtxp) { + UASSERT_OBJ(lVtxp->logicp(), lVtxp, "logicReady called on variable vertex"); + UASSERT_OBJ(lVtxp->inEmpty(), lVtxp, "logicReady called on vertex with incoming edge"); + // Add this logic vertex to the ready list of its DomScope + OrderMoveDomScope& domScope = lVtxp->domScope(); + lVtxp->appendTo(domScope.readyVertices()); + // Add the DomScope to the global ready list if not there yet + if (!domScope.isOnList()) domScope.appendTo(m_readyDomScopeps); } -} -// Mark one vertex as finished, remove from ready list if done -void OrderMoveDomScope::movedVertex(OrderProcess* opp, OrderMoveVertex* vertexp) { - UASSERT_OBJ(m_onReadyList, vertexp, - "Moving vertex from ready when nothing was on que as ready."); - if (m_readyVertices.empty()) { // Else more work to get to later - m_onReadyList = false; - m_readyDomScopeE.unlink(opp->m_pomReadyDomScope, this); + // Remove the given variable vertex, and check if any of its dependents are ready + void varReady(OrderMoveVertex* vVtxp) { + UASSERT_OBJ(!vVtxp->logicp(), vVtxp, "varReady called on logic vertex"); + UASSERT_OBJ(vVtxp->inEmpty(), vVtxp, "varReady called on vertex with incoming edge"); + // Remove dependency of consumer logic on this variable, and mark them ready if this is + // the last dependency. + for (V3GraphEdge *edgep = vVtxp->outBeginp(), *nextp; edgep; edgep = nextp) { + // Pick up next as we are deleting it + nextp = edgep->outNextp(); + // The dependent logic + OrderMoveVertex* const lVtxp = edgep->top()->as(); + UASSERT_OBJ(lVtxp->logicp(), lVtxp, "The move graph should be bipartite"); + // Delete this edge + VL_DO_DANGLING(edgep->unlinkDelete(), edgep); + // If this was the last dependency, the consumer logic is ready + if (lVtxp->inEmpty()) logicReady(lVtxp); + } + + // Can delete the vertex now + VL_DO_DANGLING(vVtxp->unlinkDelete(m_moveGraphp.get()), vVtxp); } -} -void OrderProcess::processMoveClear() { - OrderMoveDomScope::clear(); - m_pomWaiting.reset(); - m_pomReadyDomScope.reset(); - m_pomGraph.clear(); -} + void process(const OrderGraph& orderGraph, const std::string& tag, + const V3Order::TrigToSenMap& trigToSen) { + // Build the move graph + m_moveGraphp = V3OrderMoveGraphBuilder::apply(orderGraph, trigToSen); + if (dumpGraphLevel() >= 9) m_moveGraphp->dumpDotFilePrefixed(tag + "_ordermv_start"); + m_moveGraphp->removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue); + if (dumpGraphLevel() >= 4) m_moveGraphp->dumpDotFilePrefixed(tag + "_ordermv_simpl"); -void OrderProcess::processMoveBuildGraph() { - // Build graph of only vertices - UINFO(5, " MoveBuildGraph\n"); - processMoveClear(); - // Vertex::user->OrderMoveVertex*, last edge added or nullptr=none - m_pomGraph.userClearVertices(); - - OrderMoveVertexMaker createOrderMoveVertex(&m_pomGraph, &m_pomWaiting); - V3OrderMoveGraphBuilder serialPMBG(&m_graph, &m_pomGraph, m_trigToSen, - &createOrderMoveVertex); - serialPMBG.build(); -} - -//###################################################################### -// OrderVisitor - Moving - -void OrderProcess::processMove() { - // The graph routines have already sorted the vertexes and edges into best->worst order - // Make a new waiting graph with only OrderLogicVertex's - // (Order is preserved in the recreation so the sorting is preserved) - // Move any node with all inputs ready to a "ready" graph mapped by domain and then scope - // While waiting graph ! empty (and also known: something in ready graph) - // For all scopes in domain of top ready vertex - // For all vertexes in domain&scope of top ready vertex - // Make ordered activation block for this module - // Add that new activation to the list of calls to make. - // Move logic to ordered active - // Any children that have all inputs now ready move from waiting->ready graph - // (This may add nodes the for loop directly above needs to detext) - processMovePrepReady(); - - // New domain... another loop - UINFO(5, " MoveIterate\n"); - while (!m_pomReadyDomScope.empty()) { - // Start with top node on ready list's domain & scope - OrderMoveDomScope* domScopep = m_pomReadyDomScope.begin(); - OrderMoveVertex* const topVertexp - = domScopep->readyVertices().begin(); // lintok-begin-on-ref - UASSERT(topVertexp, "domScope on ready list without any nodes ready under it"); - // Work on all scopes ready inside this domain - while (domScopep) { - UINFO(6, " MoveDomain l=" << domScopep->domainp() << endl); - // Process all nodes ready under same domain & scope - m_emitter.forceNewFunction(); - V3List& readyVertices = domScopep->readyVertices(); - while (OrderMoveVertex* vertexp = readyVertices.begin()) { - UASSERT_OBJ(vertexp->domScopep() == domScopep, vertexp, "Domain mismatch"); - m_emitter.emitLogic(vertexp->logicp()); - processMoveDoneOne(vertexp); + // Mark initially ready vertices (those with no dependencies) + for (V3GraphVertex* vtxp = m_moveGraphp->verticesBeginp(); vtxp; + vtxp = vtxp->verticesNextp()) { + if (!vtxp->inEmpty()) continue; + OrderMoveVertex* const mVtxp = vtxp->as(); + if (mVtxp->logicp()) { + logicReady(mVtxp); + } else { + varReady(mVtxp); } - // Done with scope/domain pair, pick new scope under same domain, or nullptr if none - // left - OrderMoveDomScope* domScopeNextp = nullptr; - for (OrderMoveDomScope* huntp = m_pomReadyDomScope.begin(); huntp; - huntp = huntp->readyDomScopeNextp()) { - if (huntp->domainp() == domScopep->domainp()) { - domScopeNextp = huntp; + } + + // Emit all logic as they become ready + for (OrderMoveDomScope *currDomScopep = m_readyDomScopeps.begin(), *nextDomScopep; + currDomScopep; currDomScopep = nextDomScopep) { + m_emitter.forceNewFunction(); + + // Emit all logic ready under the current DomScope + V3List& currReadyList = currDomScopep->readyVertices(); + UASSERT(!currReadyList.empty(), "DomScope on ready list, not has no ready vertices"); + while (OrderMoveVertex* const lVtxp = currReadyList.begin()) { + UASSERT_OBJ(&lVtxp->domScope() == currDomScopep, lVtxp, "DomScope mismatch"); + // Unlink vertex from ready list under the DomScope + lVtxp->unlinkFrom(currReadyList); + // Unlink DomScope from the global ready list if this is the last vertex + // TODO: should do this later + if (currReadyList.empty()) currDomScopep->unlinkFrom(m_readyDomScopeps); + + // Actually emit the logic under this vertex + m_emitter.emitLogic(lVtxp->logicp()); + + // Remove dependency of produced variables on this logic, and mark them ready if + // this is the last producer. + for (V3GraphEdge *edgep = lVtxp->outBeginp(), *nextp; edgep; edgep = nextp) { + // Pick up next as we are deleting it + nextp = edgep->outNextp(); + // The dependent variable + OrderMoveVertex* const vVtxp = edgep->top()->as(); + UASSERT_OBJ(!vVtxp->logicp(), vVtxp, "The move graph should be bipartite"); + // Delete this edge + VL_DO_DANGLING(edgep->unlinkDelete(), edgep); + // If this was the last producer, the produced variable is ready + if (vVtxp->inEmpty()) varReady(vVtxp); + } + + // Can delete the vertex now + VL_DO_DANGLING(lVtxp->unlinkDelete(m_moveGraphp.get()), lVtxp); + } + + // Done with this DomScope, pick a new one to emit. Prefer a new scope under the + // same domain. If there isn't one, just pick teh head of the global ready list + nextDomScopep = m_readyDomScopeps.begin(); + for (OrderMoveDomScope* huntp = nextDomScopep; huntp; huntp = huntp->nextp()) { + if (huntp->domainp() == currDomScopep->domainp()) { + nextDomScopep = huntp; break; } } - domScopep = domScopeNextp; } - } - UASSERT(m_pomWaiting.empty(), - "Didn't converge; nodes waiting, none ready, perhaps some input activations lost."); - // Cleanup memory - processMoveClear(); -} -void OrderProcess::processMovePrepReady() { - // Make list of ready nodes - UINFO(5, " MovePrepReady\n"); - for (OrderMoveVertex* vertexp = m_pomWaiting.begin(); vertexp;) { - OrderMoveVertex* const nextp = vertexp->pomWaitingNextp(); - if (vertexp->isWait() && vertexp->inEmpty()) processMoveReadyOne(vertexp); - vertexp = nextp; + UASSERT(m_moveGraphp->empty(), "Waiting vertices remain, but none are ready"); } -} -void OrderProcess::processMoveReadyOne(OrderMoveVertex* vertexp) { - // Recursive! - // Move one node from waiting to ready list - vertexp->setReady(); - // Remove node from waiting list - vertexp->m_pomWaitingE.unlink(m_pomWaiting, vertexp); - if (vertexp->logicp()) { - // Add to ready list (indexed by domain and scope) - vertexp->m_readyVerticesE.pushBack(vertexp->domScopep()->m_readyVertices, vertexp); - vertexp->domScopep()->ready(this); - } else { - // vertexp represents a non-logic vertex. - // Recurse to mark its following neighbors ready. - processMoveDoneOne(vertexp); + // CONSTRUCTOR + OrderSerial(const OrderGraph& orderGraph, const std::string& tag, + const V3Order::TrigToSenMap& trigToSen, bool slow) + : m_emitter{tag, slow} { + OrderMoveDomScope::clear(); + process(orderGraph, tag, trigToSen); + OrderMoveDomScope::clear(); } -} -void OrderProcess::processMoveDoneOne(OrderMoveVertex* vertexp) { - // Move one node from ready to completion - vertexp->setMoved(); - // Unlink from ready lists - if (vertexp->logicp()) { - vertexp->m_readyVerticesE.unlink(vertexp->domScopep()->m_readyVertices, vertexp); - vertexp->domScopep()->movedVertex(this, vertexp); + ~OrderSerial() = default; + +public: + // Order the logic + static std::vector apply(const OrderGraph& graph, const std::string& tag, + const V3Order::TrigToSenMap& trigToSen, bool slow) { + return OrderSerial{graph, tag, trigToSen, slow}.m_emitter.getAndClearActiveps(); } - // Don't need to add it to another list, as we're done with it - // Mark our outputs as one closer to ready - for (V3GraphEdge *edgep = vertexp->outBeginp(), *nextp; edgep; edgep = nextp) { - nextp = edgep->outNextp(); - OrderMoveVertex* const toVertexp = static_cast(edgep->top()); - UINFO(9, " Clear to " << (toVertexp->inEmpty() ? "[EMP] " : " ") << toVertexp - << endl); - // Delete this edge - VL_DO_DANGLING(edgep->unlinkDelete(), edgep); - if (toVertexp->inEmpty()) { - // If destination node now has all inputs resolved; recurse to move that vertex - // This is thus depth first (before width) which keeps the - // resulting executable's d-cache happy. - processMoveReadyOne(toVertexp); - } - } -} +}; std::vector V3Order::createSerial(const OrderGraph& graph, const std::string& tag, const TrigToSenMap& trigToSen, bool slow) { - return OrderProcess::main(graph, tag, trigToSen, slow); + UINFO(2, " Constructing serial code for '" + tag + "'"); + return OrderSerial::apply(graph, tag, trigToSen, slow); } diff --git a/src/V3PartitionGraph.h b/src/V3PartitionGraph.h index ba0c60a98..915787a63 100644 --- a/src/V3PartitionGraph.h +++ b/src/V3PartitionGraph.h @@ -32,22 +32,17 @@ class MTaskMoveVertex final : public V3GraphVertex { // cannot both be set. Each MTaskMoveVertex represents a logic node // or a var node, it can't be both. OrderLogicVertex* const m_logicp; // Logic represented by this vertex - const OrderEitherVertex* const m_varp; // Var represented by this vertex const AstSenTree* const m_domainp; public: - MTaskMoveVertex(V3Graph* graphp, OrderLogicVertex* logicp, const OrderEitherVertex* varp, - const AstSenTree* domainp) VL_MT_DISABLED : V3GraphVertex{graphp}, + MTaskMoveVertex(V3Graph& graph, OrderLogicVertex* logicp, + const AstSenTree* domainp) VL_MT_DISABLED : V3GraphVertex{&graph}, m_logicp{logicp}, - m_varp{varp}, - m_domainp{domainp} { - UASSERT(!(logicp && varp), "MTaskMoveVertex: logicp and varp may not both be set!\n"); - } + m_domainp{domainp} {} ~MTaskMoveVertex() override = default; // ACCESSORS OrderLogicVertex* logicp() const { return m_logicp; } - const OrderEitherVertex* varp() const { return m_varp; } const AstScope* scopep() const { return m_logicp ? m_logicp->scopep() : nullptr; } const AstSenTree* domainp() const { return m_domainp; }