Internals: Simplify/cleanup V3OrderSerial (#4955). No functional change.
Remove redundant data structures and simplify/cleanup implementation. No functional change. Output is identical.
This commit is contained in:
parent
5a69321be3
commit
9a3aed57b1
|
|
@ -52,38 +52,24 @@ class V3OrderMoveGraphBuilder final {
|
|||
// TYPES
|
||||
using DomainMap = std::map<const AstSenTree*, T_MoveVertex*>;
|
||||
|
||||
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<V3Graph> 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<DomainMap> 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<OrderLogicVertex>()) {
|
||||
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<OrderLogicVertex>()) {
|
||||
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<const OrderLogicVertex*>(edgep->top());
|
||||
const OrderLogicVertex* const lVtxp = edgep->top()->as<OrderLogicVertex>();
|
||||
|
||||
// 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<T_MoveVertex*>(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<T_MoveVertex*>(lVtxp->userp());
|
||||
new V3GraphEdge{m_outGraphp.get(), vMoveVtxp, lMoveVxp, 1};
|
||||
}
|
||||
return vMoveVtxp;
|
||||
}
|
||||
|
||||
VL_UNCOPYABLE(V3OrderMoveGraphBuilder);
|
||||
public:
|
||||
static std::unique_ptr<V3Graph> apply(const OrderGraph& orderGraph,
|
||||
const V3Order::TrigToSenMap& trigToSen) {
|
||||
return std::move(V3OrderMoveGraphBuilder{orderGraph, trigToSen}.m_outGraphp);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
|
|||
|
|
@ -34,22 +34,6 @@
|
|||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
class OrderMTaskMoveVertexMaker final
|
||||
: public V3OrderMoveGraphBuilder<MTaskMoveVertex>::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<MTaskMoveVertex> mtask_pmbg(&graph, &logicGraph, trigToSen,
|
||||
&create_mtask_vertex);
|
||||
mtask_pmbg.build();
|
||||
}
|
||||
const std::unique_ptr<V3Graph> logicGraphp
|
||||
= V3OrderMoveGraphBuilder<MTaskMoveVertex>::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<OrderVerticesByDomainThenScope> logicStream{&logicGraph};
|
||||
GraphStream<OrderVerticesByDomainThenScope> logicStream{logicGraphp.get()};
|
||||
while (const V3GraphVertex* const vtxp = logicStream.nextp()) {
|
||||
const MTaskMoveVertex* const movep = vtxp->as<MTaskMoveVertex>();
|
||||
// Only care about logic vertices
|
||||
|
|
|
|||
|
|
@ -26,360 +26,277 @@
|
|||
#include "V3OrderInternal.h"
|
||||
#include "V3OrderMoveGraphBuilder.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
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<OrderMoveVertex*> m_pomWaitingE; // List of nodes needing inputs to become ready
|
||||
V3ListEnt<OrderMoveVertex*> 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<OrderMoveDomScope*> m_readyDomScopeE; // List of next ready dom scope
|
||||
V3List<OrderMoveVertex*> 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<OrderMoveVertex*> m_readyVertices; // Ready vertices in this domain/scope
|
||||
V3ListEnt<OrderMoveDomScope*> 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<const AstSenTree*, const AstScope*>;
|
||||
using DomScopeMap = std::map<DomScopeKey, OrderMoveDomScope*>;
|
||||
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<uintptr_t>(key.m_domainp)};
|
||||
hash += reinterpret_cast<uintptr_t>(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<DomScopeMapKey, OrderMoveDomScope, DomScopeMapKey::Hash,
|
||||
DomScopeMapKey::Equal>;
|
||||
// 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<OrderMoveVertex*>& 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<OrderMoveVertex*>& 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<OrderMoveVertex>::MoveVertexMaker {
|
||||
// MEMBERS
|
||||
V3Graph* m_pomGraphp;
|
||||
V3List<OrderMoveVertex*>* m_pomWaitingp;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
OrderMoveVertexMaker(V3Graph* pomGraphp, V3List<OrderMoveVertex*>* 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<OrderMoveDomScope*>& 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<OrderMoveDomScope*>& 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<OrderMoveVertex*> m_pomWaiting; // List of nodes needing inputs to become ready
|
||||
friend class OrderMoveDomScope;
|
||||
V3List<OrderMoveDomScope*> 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<OrderMoveVertex*> 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<AstActive*> 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<OrderMoveVertex*>& list) { m_listEnt.unlink(list, this); }
|
||||
void appendTo(V3List<OrderMoveVertex*>& 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<V3Graph> m_moveGraphp; // Graph of logic elements to move
|
||||
V3List<OrderMoveDomScope*> 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<OrderMoveVertex>();
|
||||
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<OrderMoveVertex>::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<OrderMoveVertex> 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<OrderMoveVertex*>& 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<OrderMoveVertex>();
|
||||
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<OrderMoveVertex*>& 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<OrderMoveVertex>();
|
||||
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<AstActive*> 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<OrderMoveVertex*>(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<AstActive*> 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue